Merge "WindowInsetsAnimation: Clean up API"
diff --git a/Android.bp b/Android.bp
index dce8bf2..3fff2d4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -471,7 +471,6 @@
         "framework-platform-compat-config",
         "libcore-platform-compat-config",
         "services-platform-compat-config",
-        "media-provider-platform-compat-config",
     ],
     static_libs: [
         // If MimeMap ever becomes its own APEX, then this dependency would need to be removed
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 40b81dd..d9b9c56 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8497,6 +8497,11 @@
      * From {@link android.os.Build.VERSION_CODES#N} the profile or device owner can still use
      * {@link android.accounts.AccountManager} APIs to add or remove accounts when account
      * management for a specific type is disabled.
+     * <p>
+     * This method may be called on the {@code DevicePolicyManager} instance returned from
+     * {@link #getParentProfileInstance(ComponentName)} by the profile owner on an
+     * organization-owned device, to restrict accounts that may not be managed on the primary
+     * profile.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param accountType For which account management is disabled or enabled.
@@ -8506,10 +8511,10 @@
      */
     public void setAccountManagementDisabled(@NonNull ComponentName admin, String accountType,
             boolean disabled) {
-        throwIfParentInstance("setAccountManagementDisabled");
         if (mService != null) {
             try {
-                mService.setAccountManagementDisabled(admin, accountType, disabled);
+                mService.setAccountManagementDisabled(admin, accountType, disabled,
+                        mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -8517,28 +8522,43 @@
     }
 
     /**
-     * Gets the array of accounts for which account management is disabled by the profile owner.
+     * Gets the array of accounts for which account management is disabled by the profile owner
+     * or device owner.
      *
      * <p> Account management can be disabled/enabled by calling
      * {@link #setAccountManagementDisabled}.
+     * <p>
+     * This method may be called on the {@code DevicePolicyManager} instance returned from
+     * {@link #getParentProfileInstance(ComponentName)}. Note that only a profile owner on
+     * an organization-deviced can affect account types on the parent profile instance.
      *
      * @return a list of account types for which account management has been disabled.
      *
      * @see #setAccountManagementDisabled
      */
     public @Nullable String[] getAccountTypesWithManagementDisabled() {
-        throwIfParentInstance("getAccountTypesWithManagementDisabled");
-        return getAccountTypesWithManagementDisabledAsUser(myUserId());
+        return getAccountTypesWithManagementDisabledAsUser(myUserId(), mParentInstance);
+    }
+
+    /**
+     * @see #getAccountTypesWithManagementDisabled()
+     * Note that calling this method on the parent profile instance will return the same
+     * value as calling it on the main {@code DevicePolicyManager} instance.
+     * @hide
+     */
+    public @Nullable String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
+        return getAccountTypesWithManagementDisabledAsUser(userId, false);
     }
 
     /**
      * @see #getAccountTypesWithManagementDisabled()
      * @hide
      */
-    public @Nullable String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
+    public @Nullable String[] getAccountTypesWithManagementDisabledAsUser(
+            int userId, boolean parentInstance) {
         if (mService != null) {
             try {
-                return mService.getAccountTypesWithManagementDisabledAsUser(userId);
+                return mService.getAccountTypesWithManagementDisabledAsUser(userId, parentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -9876,6 +9896,7 @@
      * <li>{@link #setTrustAgentConfiguration}</li>
      * <li>{@link #getRequiredStrongAuthTimeout}</li>
      * <li>{@link #setRequiredStrongAuthTimeout}</li>
+     * <li>{@link #getAccountTypesWithManagementDisabled}</li>
      * </ul>
      * <p>
      * The following methods are supported for the parent instance but can only be called by the
@@ -9884,6 +9905,7 @@
      * <li>{@link #getPasswordComplexity}</li>
      * <li>{@link #setCameraDisabled}</li>
      * <li>{@link #getCameraDisabled}</li>
+     * <li>{@link #setAccountManagementDisabled(ComponentName, String, boolean)}</li>
      * </ul>
      *
      * <p>The following methods can be called by the profile owner of a managed profile
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0aed39c..84332ca 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -248,9 +248,9 @@
     int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent);
     boolean installExistingPackage(in ComponentName admin, in String callerPackage, in String packageName);
 
-    void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
+    void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled, in boolean parent);
     String[] getAccountTypesWithManagementDisabled();
-    String[] getAccountTypesWithManagementDisabledAsUser(int userId);
+    String[] getAccountTypesWithManagementDisabledAsUser(int userId, in boolean parent);
 
     void setSecondaryLockscreenEnabled(in ComponentName who, boolean enabled);
     boolean isSecondaryLockscreenEnabled(int userId);
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 9c4a8f4..5b98188 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -526,15 +526,17 @@
     }
 
     /**
-     * Registers a custom provider of {@link android.net.NetworkStats} to combine the network
-     * statistics that cannot be seen by the kernel to system. To unregister, invoke
-     * {@link NetworkStatsProviderCallback#unregister()}.
+     * Registers a custom provider of {@link android.net.NetworkStats} to provide network statistics
+     * to the system. To unregister, invoke {@link NetworkStatsProviderCallback#unregister()}.
+     * Note that no de-duplication of statistics between providers is performed, so each provider
+     * must only report network traffic that is not being reported by any other provider.
      *
-     * @param tag a human readable identifier of the custom network stats provider.
-     * @param provider a custom implementation of {@link AbstractNetworkStatsProvider} that needs to
-     *                 be registered to the system.
+     * @param tag a human readable identifier of the custom network stats provider. This is only
+     *            used for debugging.
+     * @param provider the subclass of {@link AbstractNetworkStatsProvider} that needs to be
+     *                 registered to the system.
      * @return a {@link NetworkStatsProviderCallback}, which can be used to report events to the
-     *         system.
+     *         system or unregister the provider.
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 059cd94..5ab0354 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.shared.InetAddressUtils;
 import android.os.Parcel;
@@ -66,6 +67,9 @@
 
     public String serverHostName;
 
+    @Nullable
+    public String captivePortalApiUrl;
+
     public DhcpResults() {
         super();
     }
@@ -100,6 +104,7 @@
             leaseDuration = source.leaseDuration;
             mtu = source.mtu;
             serverHostName = source.serverHostName;
+            captivePortalApiUrl = source.captivePortalApiUrl;
         }
     }
 
@@ -133,6 +138,7 @@
         leaseDuration = 0;
         mtu = 0;
         serverHostName = null;
+        captivePortalApiUrl = null;
     }
 
     @Override
@@ -144,6 +150,9 @@
         str.append(" lease ").append(leaseDuration).append(" seconds");
         if (mtu != 0) str.append(" MTU ").append(mtu);
         str.append(" Servername ").append(serverHostName);
+        if (captivePortalApiUrl != null) {
+            str.append(" CaptivePortalApiUrl ").append(captivePortalApiUrl);
+        }
 
         return str.toString();
     }
@@ -161,7 +170,8 @@
                 && Objects.equals(vendorInfo, target.vendorInfo)
                 && Objects.equals(serverHostName, target.serverHostName)
                 && leaseDuration == target.leaseDuration
-                && mtu == target.mtu;
+                && mtu == target.mtu
+                && Objects.equals(captivePortalApiUrl, target.captivePortalApiUrl);
     }
 
     /**
@@ -186,6 +196,7 @@
         InetAddressUtils.parcelInetAddress(dest, serverAddress, flags);
         dest.writeString(vendorInfo);
         dest.writeString(serverHostName);
+        dest.writeString(captivePortalApiUrl);
     }
 
     @Override
@@ -201,6 +212,7 @@
         dhcpResults.serverAddress = (Inet4Address) InetAddressUtils.unparcelInetAddress(in);
         dhcpResults.vendorInfo = in.readString();
         dhcpResults.serverHostName = in.readString();
+        dhcpResults.captivePortalApiUrl = in.readString();
         return dhcpResults;
     }
 
@@ -305,4 +317,12 @@
     public void setMtu(int mtu) {
         this.mtu = mtu;
     }
+
+    public String getCaptivePortalApiUrl() {
+        return captivePortalApiUrl;
+    }
+
+    public void setCaptivePortalApiUrl(String url) {
+        captivePortalApiUrl = url;
+    }
 }
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index 57269c4..ec485d3 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -22,6 +22,7 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.os.AsyncTask;
+import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.util.Size;
@@ -32,6 +33,7 @@
 import android.view.inline.InlinePresentationSpec;
 
 import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
 import com.android.internal.view.inline.IInlineContentCallback;
 import com.android.internal.view.inline.IInlineContentProvider;
 
@@ -62,6 +64,14 @@
     private final @Nullable IInlineContentProvider mContentProvider;
 
     /**
+     * Used to keep a strong reference to the callback so it doesn't get garbage collected.
+     *
+     * @hide
+     */
+    @DataClass.ParcelWith(InlineContentCallbackImplParceling.class)
+    private @Nullable InlineContentCallbackImpl mInlineContentCallback;
+
+    /**
      * Creates a new {@link InlineSuggestion}, for testing purpose.
      *
      * @hide
@@ -69,9 +79,19 @@
     @TestApi
     @NonNull
     public static InlineSuggestion newInlineSuggestion(@NonNull InlineSuggestionInfo info) {
-        return new InlineSuggestion(info, null);
+        return new InlineSuggestion(info, null, /* inlineContentCallback */ null);
     }
 
+    /**
+     * Creates a new {@link InlineSuggestion}.
+     *
+     * @hide
+     */
+    public InlineSuggestion(
+            @NonNull InlineSuggestionInfo info,
+            @Nullable IInlineContentProvider contentProvider) {
+        this(info, contentProvider, /* inlineContentCallback */ null);
+    }
 
 
 
@@ -79,12 +99,14 @@
      * Inflates a view with the content of this suggestion at a specific size.
      * The size must be between the {@link InlinePresentationSpec#getMinSize() min size}
      * and the {@link InlinePresentationSpec#getMaxSize() max size} of the presentation
-     * spec returned by {@link InlineSuggestionInfo#getPresentationSpec()}. If an invalid
-     * argument is passed an exception is thrown.
+     * spec returned by {@link InlineSuggestionInfo#getPresentationSpec()}.
      *
      * @param context Context in which to inflate the view.
      * @param size The size at which to inflate the suggestion.
      * @param callback Callback for receiving the inflated view.
+     *
+     * @throws IllegalArgumentException If an invalid argument is passed.
+     * @throws IllegalStateException if this method is already called.
      */
     public void inflate(@NonNull Context context, @NonNull Size size,
             @NonNull @CallbackExecutor Executor callbackExecutor,
@@ -96,19 +118,15 @@
             throw new IllegalArgumentException("size not between min:"
                     + minSize + " and max:" + maxSize);
         }
+        mInlineContentCallback = getInlineContentCallback(context, callbackExecutor, callback);
         AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
             if (mContentProvider == null) {
                 callback.accept(/* view */ null);
                 return;
             }
-            // TODO(b/137800469): keep a strong reference to the contentCallback so it doesn't
-            //  get GC'd. Also add a isInflated() method and make sure the view can only be
-            //  inflated once.
             try {
-                InlineContentCallbackImpl contentCallback = new InlineContentCallbackImpl(context,
-                        callbackExecutor, callback);
                 mContentProvider.provideContent(size.getWidth(), size.getHeight(),
-                        new InlineContentCallbackWrapper(contentCallback));
+                        new InlineContentCallbackWrapper(mInlineContentCallback));
             } catch (RemoteException e) {
                 Slog.w(TAG, "Error creating suggestion content surface: " + e);
                 callback.accept(/* view */ null);
@@ -116,6 +134,14 @@
         });
     }
 
+    private synchronized InlineContentCallbackImpl getInlineContentCallback(Context context,
+            Executor callbackExecutor, Consumer<View> callback) {
+        if (mInlineContentCallback != null) {
+            throw new IllegalStateException("Already called #inflate()");
+        }
+        return new InlineContentCallbackImpl(context, callbackExecutor, callback);
+    }
+
     private static final class InlineContentCallbackWrapper extends IInlineContentCallback.Stub {
 
         private final WeakReference<InlineContentCallbackImpl> mCallbackImpl;
@@ -157,6 +183,22 @@
         }
     }
 
+    /**
+     * This class used to provide parcelling logic for InlineContentCallbackImpl. It's intended to
+     * make this parcelling a no-op, since it can't be parceled and we don't need to parcel it.
+     */
+    private static class InlineContentCallbackImplParceling implements
+            Parcelling<InlineContentCallbackImpl> {
+        @Override
+        public void parcel(InlineContentCallbackImpl item, Parcel dest, int parcelFlags) {
+        }
+
+        @Override
+        public InlineContentCallbackImpl unparcel(Parcel source) {
+            return null;
+        }
+    }
+
 
 
     // Code below generated by codegen v1.0.14.
@@ -175,16 +217,20 @@
     /**
      * Creates a new InlineSuggestion.
      *
+     * @param inlineContentCallback
+     *   Used to keep a strong reference to the callback so it doesn't get garbage collected.
      * @hide
      */
     @DataClass.Generated.Member
     public InlineSuggestion(
             @NonNull InlineSuggestionInfo info,
-            @Nullable IInlineContentProvider contentProvider) {
+            @Nullable IInlineContentProvider contentProvider,
+            @Nullable InlineContentCallbackImpl inlineContentCallback) {
         this.mInfo = info;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mInfo);
         this.mContentProvider = contentProvider;
+        this.mInlineContentCallback = inlineContentCallback;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -194,6 +240,16 @@
         return mInfo;
     }
 
+    /**
+     * Used to keep a strong reference to the callback so it doesn't get garbage collected.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public @Nullable InlineContentCallbackImpl getInlineContentCallback() {
+        return mInlineContentCallback;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -202,7 +258,8 @@
 
         return "InlineSuggestion { " +
                 "info = " + mInfo + ", " +
-                "contentProvider = " + mContentProvider +
+                "contentProvider = " + mContentProvider + ", " +
+                "inlineContentCallback = " + mInlineContentCallback +
         " }";
     }
 
@@ -220,7 +277,8 @@
         //noinspection PointlessBooleanExpression
         return true
                 && java.util.Objects.equals(mInfo, that.mInfo)
-                && java.util.Objects.equals(mContentProvider, that.mContentProvider);
+                && java.util.Objects.equals(mContentProvider, that.mContentProvider)
+                && java.util.Objects.equals(mInlineContentCallback, that.mInlineContentCallback);
     }
 
     @Override
@@ -232,20 +290,34 @@
         int _hash = 1;
         _hash = 31 * _hash + java.util.Objects.hashCode(mInfo);
         _hash = 31 * _hash + java.util.Objects.hashCode(mContentProvider);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mInlineContentCallback);
         return _hash;
     }
 
+    @DataClass.Generated.Member
+    static Parcelling<InlineContentCallbackImpl> sParcellingForInlineContentCallback =
+            Parcelling.Cache.get(
+                    InlineContentCallbackImplParceling.class);
+    static {
+        if (sParcellingForInlineContentCallback == null) {
+            sParcellingForInlineContentCallback = Parcelling.Cache.put(
+                    new InlineContentCallbackImplParceling());
+        }
+    }
+
     @Override
     @DataClass.Generated.Member
-    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
         if (mContentProvider != null) flg |= 0x2;
+        if (mInlineContentCallback != null) flg |= 0x4;
         dest.writeByte(flg);
         dest.writeTypedObject(mInfo, flags);
         if (mContentProvider != null) dest.writeStrongInterface(mContentProvider);
+        sParcellingForInlineContentCallback.parcel(mInlineContentCallback, dest, flags);
     }
 
     @Override
@@ -255,18 +327,20 @@
     /** @hide */
     @SuppressWarnings({"unchecked", "RedundantCast"})
     @DataClass.Generated.Member
-    /* package-private */ InlineSuggestion(@NonNull android.os.Parcel in) {
+    /* package-private */ InlineSuggestion(@NonNull Parcel in) {
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         byte flg = in.readByte();
         InlineSuggestionInfo info = (InlineSuggestionInfo) in.readTypedObject(InlineSuggestionInfo.CREATOR);
         IInlineContentProvider contentProvider = (flg & 0x2) == 0 ? null : IInlineContentProvider.Stub.asInterface(in.readStrongBinder());
+        InlineContentCallbackImpl inlineContentCallback = sParcellingForInlineContentCallback.unparcel(in);
 
         this.mInfo = info;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mInfo);
         this.mContentProvider = contentProvider;
+        this.mInlineContentCallback = inlineContentCallback;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -280,16 +354,16 @@
         }
 
         @Override
-        public InlineSuggestion createFromParcel(@NonNull android.os.Parcel in) {
+        public InlineSuggestion createFromParcel(@NonNull Parcel in) {
             return new InlineSuggestion(in);
         }
     };
 
     @DataClass.Generated(
-            time = 1581377984320L,
+            time = 1581929285156L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
-            inputSignatures = "private static final  java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic  void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+            inputSignatures = "private static final  java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic  void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nprivate synchronized  android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index e34aa97..a47bd17 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -653,17 +653,18 @@
         String classPathForElement = "";
         boolean compiledSomething = false;
         for (String classPathElement : classPathElements) {
-            // System server is fully AOTed and never profiled
-            // for profile guided compilation.
+            // We default to the verify filter because the compilation will happen on /data and
+            // system server cannot load executable code outside /system.
             String systemServerFilter = SystemProperties.get(
-                    "dalvik.vm.systemservercompilerfilter", "speed");
+                    "dalvik.vm.systemservercompilerfilter", "verify");
 
+            String classLoaderContext =
+                        getSystemServerClassLoaderContext(classPathForElement);
             int dexoptNeeded;
             try {
                 dexoptNeeded = DexFile.getDexOptNeeded(
                         classPathElement, instructionSet, systemServerFilter,
-                        null /* classLoaderContext */, false /* newProfile */,
-                        false /* downgrade */);
+                        classLoaderContext, false /* newProfile */, false /* downgrade */);
             } catch (FileNotFoundException ignored) {
                 // Do not add to the classpath.
                 Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
@@ -684,8 +685,6 @@
                 final String compilerFilter = systemServerFilter;
                 final String uuid = StorageManager.UUID_PRIVATE_INTERNAL;
                 final String seInfo = null;
-                final String classLoaderContext =
-                        getSystemServerClassLoaderContext(classPathForElement);
                 final int targetSdkVersion = 0;  // SystemServer targets the system's SDK version
                 try {
                     installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 4b95e4d..08b30f7 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -219,6 +219,9 @@
         <permission name="android.permission.WATCH_APPOPS"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
         <permission name="android.permission.UPDATE_DEVICE_STATS"/>
+        <!-- Permissions required for reading and logging compat changes -->
+        <permission name="android.permission.LOG_COMPAT_CHANGE" />
+        <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.providers.telephony">
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 6689080..70d79378 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -26,6 +26,7 @@
 import libcore.content.type.MimeMap;
 
 import java.util.HashMap;
+import java.util.Locale;
 
 /**
  * MediaScanner helper class.
@@ -215,23 +216,23 @@
             return true;
         }
 
-        switch (normalizedMimeType) {
+        switch (normalizedMimeType.toLowerCase(Locale.ROOT)) {
             case "application/epub+zip":
             case "application/msword":
             case "application/pdf":
             case "application/rtf":
             case "application/vnd.ms-excel":
-            case "application/vnd.ms-excel.addin.macroEnabled.12":
-            case "application/vnd.ms-excel.sheet.binary.macroEnabled.12":
-            case "application/vnd.ms-excel.sheet.macroEnabled.12":
-            case "application/vnd.ms-excel.template.macroEnabled.12":
+            case "application/vnd.ms-excel.addin.macroenabled.12":
+            case "application/vnd.ms-excel.sheet.binary.macroenabled.12":
+            case "application/vnd.ms-excel.sheet.macroenabled.12":
+            case "application/vnd.ms-excel.template.macroenabled.12":
             case "application/vnd.ms-powerpoint":
-            case "application/vnd.ms-powerpoint.addin.macroEnabled.12":
-            case "application/vnd.ms-powerpoint.presentation.macroEnabled.12":
-            case "application/vnd.ms-powerpoint.slideshow.macroEnabled.12":
-            case "application/vnd.ms-powerpoint.template.macroEnabled.12":
-            case "application/vnd.ms-word.document.macroEnabled.12":
-            case "application/vnd.ms-word.template.macroEnabled.12":
+            case "application/vnd.ms-powerpoint.addin.macroenabled.12":
+            case "application/vnd.ms-powerpoint.presentation.macroenabled.12":
+            case "application/vnd.ms-powerpoint.slideshow.macroenabled.12":
+            case "application/vnd.ms-powerpoint.template.macroenabled.12":
+            case "application/vnd.ms-word.document.macroenabled.12":
+            case "application/vnd.ms-word.template.macroenabled.12":
             case "application/vnd.oasis.opendocument.chart":
             case "application/vnd.oasis.opendocument.database":
             case "application/vnd.oasis.opendocument.formula":
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java
index 507dd4a..15a501d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java
@@ -97,6 +97,9 @@
         assertTrue(isDocumentMimeType("text/plain"));
         assertTrue(isDocumentMimeType("application/pdf"));
         assertTrue(isDocumentMimeType("application/msword"));
+        assertTrue(isDocumentMimeType("application/vnd.ms-excel.addin.macroEnabled.12"));
+        assertTrue(isDocumentMimeType("application/vnd.ms-powerpoint.addin.macroEnabled.12"));
+        assertTrue(isDocumentMimeType("application/vnd.ms-word.document.macroEnabled.12"));
         assertFalse(isDocumentMimeType("audio/mpeg"));
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
index f341bf1..2821af9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
@@ -27,6 +27,11 @@
     public static final String KEY_MEDIA_OUTPUT = "media_output";
 
     /**
+     * Key for the Media output group setting.
+     */
+    public static final String KEY_MEDIA_OUTPUT_GROUP = "media_output_group";
+
+    /**
      * Key for the Remote Media slice.
      */
     public static final String KEY_REMOTE_MEDIA = "remote_media";
@@ -48,6 +53,13 @@
             "com.android.settings.panel.action.MEDIA_OUTPUT";
 
     /**
+     * Activity Action: Show a settings dialog containing {@link MediaDevice} to handle media group
+     * operation.
+     */
+    public static final String ACTION_MEDIA_OUTPUT_GROUP =
+            "com.android.settings.panel.action.MEDIA_OUTPUT_GROUP";
+
+    /**
      * An string extra specifying a media package name.
      */
     public static final String EXTRA_PACKAGE_NAME =
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 530823a..d126ee0 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -126,8 +126,6 @@
  * <li>This service calls startBugreport() and passes in local file descriptors to receive
  * bugreport artifacts.
  * </ol>
- *
- * TODO: There are multiple threads involved.  Add synchronization accordingly.
  */
 public class BugreportProgressService extends Service {
     private static final String TAG = "BugreportProgressService";
@@ -219,6 +217,7 @@
     private final Object mLock = new Object();
 
     /** Managed bugreport info (keyed by id) */
+    @GuardedBy("mLock")
     private final SparseArray<BugreportInfo> mBugreportInfos = new SparseArray<>();
 
     private Context mContext;
@@ -317,23 +316,26 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-        final int size = mBugreportInfos.size();
-        if (size == 0) {
-            writer.println("No monitored processes");
-            return;
-        }
-        writer.print("Foreground id: "); writer.println(mForegroundId);
-        writer.println("\n");
-        writer.println("Monitored dumpstate processes");
-        writer.println("-----------------------------");
-        for (int i = 0; i < size; i++) {
-            writer.print("#"); writer.println(i + 1);
-            writer.println(getInfo(mBugreportInfos.keyAt(i)));
+        synchronized (mLock) {
+            final int size = mBugreportInfos.size();
+            if (size == 0) {
+                writer.println("No monitored processes");
+                return;
+            }
+            writer.print("Foreground id: "); writer.println(mForegroundId);
+            writer.println("\n");
+            writer.println("Monitored dumpstate processes");
+            writer.println("-----------------------------");
+            for (int i = 0; i < size; i++) {
+                writer.print("#");
+                writer.println(i + 1);
+                writer.println(getInfoLocked(mBugreportInfos.keyAt(i)));
+            }
         }
     }
 
     private static String getFileName(BugreportInfo info, String suffix) {
-        return String.format("%s-%s%s", info.baseName, info.name, suffix);
+        return String.format("%s-%s%s", info.baseName, info.getName(), suffix);
     }
 
     private final class BugreportCallbackImpl extends BugreportCallback {
@@ -573,7 +575,8 @@
         }
     }
 
-    private BugreportInfo getInfo(int id) {
+    @GuardedBy("mLock")
+    private BugreportInfo getInfoLocked(int id) {
         final BugreportInfo bugreportInfo = mBugreportInfos.get(id);
         if (bugreportInfo == null) {
             Log.w(TAG, "Not monitoring bugreports with ID " + id);
@@ -668,12 +671,12 @@
      * Updates the system notification for a given bugreport.
      */
     private void updateProgress(BugreportInfo info) {
-        if (info.progress < 0) {
-            Log.e(TAG, "Invalid progress value for " + info);
+        if (info.getProgress() < 0) {
+            Log.e(TAG, "Invalid progress values for " + info);
             return;
         }
 
-        if (info.finished) {
+        if (info.isFinished()) {
             Log.w(TAG, "Not sending progress notification because bugreport has finished already ("
                     + info + ")");
             return;
@@ -682,7 +685,7 @@
         final NumberFormat nf = NumberFormat.getPercentInstance();
         nf.setMinimumFractionDigits(2);
         nf.setMaximumFractionDigits(2);
-        final String percentageText = nf.format((double) info.progress / 100);
+        final String percentageText = nf.format((double) info.getProgress() / 100);
 
         String title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
 
@@ -690,18 +693,19 @@
         if (mIsWatch) {
             nf.setMinimumFractionDigits(0);
             nf.setMaximumFractionDigits(0);
-            final String watchPercentageText = nf.format((double) info.progress / 100);
+            final String watchPercentageText = nf.format((double) info.getProgress() / 100);
             title = title + "\n" + watchPercentageText;
         }
 
         final String name =
-                info.name != null ? info.name : mContext.getString(R.string.bugreport_unnamed);
+                info.getName() != null ? info.getName()
+                        : mContext.getString(R.string.bugreport_unnamed);
 
         final Notification.Builder builder = newBaseNotification(mContext)
                 .setContentTitle(title)
                 .setTicker(title)
                 .setContentText(name)
-                .setProgress(100 /* max value of progress percentage */, info.progress, false)
+                .setProgress(100 /* max value of progress percentage */, info.getProgress(), false)
                 .setOngoing(true);
 
         // Wear and ATV bugreport doesn't need the bug info dialog, screenshot and cancel action.
@@ -730,10 +734,10 @@
                 .setActions(infoAction, screenshotAction, cancelAction);
         }
         // Show a debug log, every LOG_PROGRESS_STEP percent.
-        final int progress = info.progress;
+        final int progress = info.getProgress();
 
-        if ((info.progress == 0) || (info.progress >= 100) ||
-                ((progress / LOG_PROGRESS_STEP) != (mLastProgressPercent / LOG_PROGRESS_STEP))) {
+        if ((info.getProgress() == 0) || (info.getProgress() >= 100)
+                || ((progress / LOG_PROGRESS_STEP) != (mLastProgressPercent / LOG_PROGRESS_STEP))) {
             Log.d(TAG, "Progress #" + info.id + ": " + percentageText);
         }
         mLastProgressPercent = progress;
@@ -778,10 +782,10 @@
             mBugreportInfos.remove(id);
         }
         // Must stop foreground service first, otherwise notif.cancel() will fail below.
-        stopForegroundWhenDone(id);
+        stopForegroundWhenDoneLocked(id);
         Log.d(TAG, "stopProgress(" + id + "): cancel notification");
         NotificationManager.from(mContext).cancel(id);
-        stopSelfWhenDone();
+        stopSelfWhenDoneLocked();
     }
 
     /**
@@ -791,13 +795,13 @@
         MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_CANCEL);
         Log.v(TAG, "cancel: ID=" + id);
         mInfoDialog.cancel();
-        final BugreportInfo info = getInfo(id);
-        if (info != null && !info.finished) {
-            Log.i(TAG, "Cancelling bugreport service (ID=" + id + ") on user's request");
-            mBugreportManager.cancelBugreport();
-            deleteScreenshots(info);
-        }
         synchronized (mLock) {
+            final BugreportInfo info = getInfoLocked(id);
+            if (info != null && !info.isFinished()) {
+                Log.i(TAG, "Cancelling bugreport service (ID=" + id + ") on user's request");
+                mBugreportManager.cancelBugreport();
+                deleteScreenshots(info);
+            }
             stopProgressLocked(id);
         }
     }
@@ -808,7 +812,10 @@
      */
     private void launchBugreportInfoDialog(int id) {
         MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_DETAILS);
-        final BugreportInfo info = getInfo(id);
+        final BugreportInfo info;
+        synchronized (mLock) {
+            info = getInfoLocked(id);
+        }
         if (info == null) {
             // Most likely am killed Shell before user tapped the notification. Since system might
             // be too busy anwyays, it's better to ignore the notification and switch back to the
@@ -842,7 +849,11 @@
      */
     private void takeScreenshot(int id) {
         MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SCREENSHOT);
-        if (getInfo(id) == null) {
+        BugreportInfo info;
+        synchronized (mLock) {
+            info = getInfoLocked(id);
+        }
+        if (info == null) {
             // Most likely am killed Shell before user tapped the notification. Since system might
             // be too busy anwyays, it's better to ignore the notification and switch back to the
             // non-interactive mode (where the bugerport will be shared upon completion).
@@ -877,9 +888,11 @@
             mServiceHandler.sendMessageDelayed(msg, DateUtils.SECOND_IN_MILLIS);
             return;
         }
-
+        final BugreportInfo info;
         // It's time to take the screenshot: let the proper thread handle it
-        final BugreportInfo info = getInfo(id);
+        synchronized (mLock) {
+            info = getInfoLocked(id);
+        }
         if (info == null) {
             return;
         }
@@ -895,11 +908,11 @@
      * SCREENSHOT button is enabled or disabled accordingly.
      */
     private void setTakingScreenshot(boolean flag) {
-        synchronized (BugreportProgressService.this) {
+        synchronized (mLock) {
             mTakingScreenshot = flag;
             for (int i = 0; i < mBugreportInfos.size(); i++) {
-                final BugreportInfo info = getInfo(mBugreportInfos.keyAt(i));
-                if (info.finished) {
+                final BugreportInfo info = getInfoLocked(mBugreportInfos.keyAt(i));
+                if (info.isFinished()) {
                     Log.d(TAG, "Not updating progress for " + info.id + " while taking screenshot"
                             + " because share notification was already sent");
                     continue;
@@ -920,7 +933,10 @@
 
     private void handleScreenshotResponse(Message resultMsg) {
         final boolean taken = resultMsg.arg2 != 0;
-        final BugreportInfo info = getInfo(resultMsg.arg1);
+        final BugreportInfo info;
+        synchronized (mLock) {
+            info = getInfoLocked(resultMsg.arg1);
+        }
         if (info == null) {
             return;
         }
@@ -929,7 +945,7 @@
         final String msg;
         if (taken) {
             info.addScreenshot(screenshotFile);
-            if (info.finished) {
+            if (info.isFinished()) {
                 Log.d(TAG, "Screenshot finished after bugreport; updating share notification");
                 info.renameScreenshots();
                 sendBugreportNotification(info, mTakingScreenshot);
@@ -955,9 +971,10 @@
     /**
      * Stop running on foreground once there is no more active bugreports being watched.
      */
-    private void stopForegroundWhenDone(int id) {
+    @GuardedBy("mLock")
+    private void stopForegroundWhenDoneLocked(int id) {
         if (id != mForegroundId) {
-            Log.d(TAG, "stopForegroundWhenDone(" + id + "): ignoring since foreground id is "
+            Log.d(TAG, "stopForegroundWhenDoneLocked(" + id + "): ignoring since foreground id is "
                     + mForegroundId);
             return;
         }
@@ -970,8 +987,8 @@
         final int total = mBugreportInfos.size();
         if (total > 0) {
             for (int i = 0; i < total; i++) {
-                final BugreportInfo info = getInfo(mBugreportInfos.keyAt(i));
-                if (!info.finished) {
+                final BugreportInfo info = getInfoLocked(mBugreportInfos.keyAt(i));
+                if (!info.isFinished()) {
                     updateProgress(info);
                     break;
                 }
@@ -982,7 +999,8 @@
     /**
      * Finishes the service when it's not monitoring any more processes.
      */
-    private void stopSelfWhenDone() {
+    @GuardedBy("mLock")
+    private void stopSelfWhenDoneLocked() {
         if (mBugreportInfos.size() > 0) {
             if (DEBUG) Log.d(TAG, "Staying alive, waiting for IDs " + mBugreportInfos);
             return;
@@ -995,12 +1013,14 @@
      * Wraps up bugreport generation and triggers a notification to share the bugreport.
      */
     private void onBugreportFinished(BugreportInfo info) {
-        Log.d(TAG, "Bugreport finished with title: " + info.title
+        Log.d(TAG, "Bugreport finished with title: " + info.getTitle()
                 + " and shareDescription:  " + info.shareDescription);
-        info.finished = true;
+        info.setFinished(true);
 
-        // Stop running on foreground, otherwise share notification cannot be dismissed.
-        stopForegroundWhenDone(info.id);
+        synchronized (mLock) {
+            // Stop running on foreground, otherwise share notification cannot be dismissed.
+            stopForegroundWhenDoneLocked(info.id);
+        }
 
         triggerLocalNotification(mContext, info);
     }
@@ -1062,8 +1082,8 @@
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         intent.setType(mimeType);
 
-        final String subject = !TextUtils.isEmpty(info.title) ?
-                info.title : bugreportUri.getLastPathSegment();
+        final String subject = !TextUtils.isEmpty(info.getTitle())
+                ? info.getTitle() : bugreportUri.getLastPathSegment();
         intent.putExtra(Intent.EXTRA_SUBJECT, subject);
 
         // EXTRA_TEXT should be an ArrayList, but some clients are expecting a single String.
@@ -1074,9 +1094,9 @@
             .append("\nSerial number: ")
             .append(SystemProperties.get("ro.serialno"));
         int descriptionLength = 0;
-        if (!TextUtils.isEmpty(info.description)) {
-            messageBody.append("\nDescription: ").append(info.description);
-            descriptionLength = info.description.length();
+        if (!TextUtils.isEmpty(info.getDescription())) {
+            messageBody.append("\nDescription: ").append(info.getDescription());
+            descriptionLength = info.getDescription().length();
         }
         intent.putExtra(Intent.EXTRA_TEXT, messageBody.toString());
         final ClipData clipData = new ClipData(null, new String[] { mimeType },
@@ -1117,12 +1137,17 @@
      */
     private void shareBugreport(int id, BugreportInfo sharedInfo) {
         MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SHARE);
-        BugreportInfo info = getInfo(id);
+        BugreportInfo info;
+        synchronized (mLock) {
+            info = getInfoLocked(id);
+        }
         if (info == null) {
             // Service was terminated but notification persisted
             info = sharedInfo;
-            Log.d(TAG, "shareBugreport(): no info for ID " + id + " on managed processes ("
-                    + mBugreportInfos + "), using info from intent instead (" + info + ")");
+            synchronized (mLock) {
+                Log.d(TAG, "shareBugreport(): no info for ID " + id + " on managed processes ("
+                        + mBugreportInfos + "), using info from intent instead (" + info + ")");
+            }
         } else {
             Log.v(TAG, "shareBugReport(): id " + id + " info = " + info);
         }
@@ -1194,10 +1219,10 @@
                 mContext.getString(R.string.bugreport_finished_pending_screenshot_text)
                 : mContext.getString(R.string.bugreport_finished_text);
         final String title;
-        if (TextUtils.isEmpty(info.title)) {
+        if (TextUtils.isEmpty(info.getTitle())) {
             title = mContext.getString(R.string.bugreport_finished_title, info.id);
         } else {
-            title = info.title;
+            title = info.getTitle();
             if (!TextUtils.isEmpty(info.shareDescription)) {
                 if(!takingScreenshot) content = info.shareDescription;
             }
@@ -1211,8 +1236,8 @@
                         PendingIntent.FLAG_UPDATE_CURRENT))
                 .setDeleteIntent(newCancelIntent(mContext, info));
 
-        if (!TextUtils.isEmpty(info.name)) {
-            builder.setSubText(info.name);
+        if (!TextUtils.isEmpty(info.getName())) {
+            builder.setSubText(info.getName());
         }
 
         Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
@@ -1305,13 +1330,14 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void addDetailsToZipFileLocked(BugreportInfo info) {
         if (info.bugreportFile == null) {
             // One possible reason is a bug in the Parcelization code.
             Log.wtf(TAG, "addDetailsToZipFile(): no bugreportFile on " + info);
             return;
         }
-        if (TextUtils.isEmpty(info.title) && TextUtils.isEmpty(info.description)) {
+        if (TextUtils.isEmpty(info.getTitle()) && TextUtils.isEmpty(info.getDescription())) {
             Log.d(TAG, "Not touching zip file since neither title nor description are set");
             return;
         }
@@ -1344,8 +1370,8 @@
             }
 
             // Then add the user-provided info.
-            addEntry(zos, "title.txt", info.title);
-            addEntry(zos, "description.txt", info.description);
+            addEntry(zos, "title.txt", info.getTitle());
+            addEntry(zos, "description.txt", info.getDescription());
         } catch (IOException e) {
             Log.e(TAG, "exception zipping file " + tmpZip, e);
             Toast.makeText(mContext, R.string.bugreport_add_details_to_zip_failed,
@@ -1355,7 +1381,7 @@
             // Make sure it only tries to add details once, even it fails the first time.
             info.addedDetailsToZip = true;
             info.addingDetailsToZip = false;
-            stopForegroundWhenDone(info.id);
+            stopForegroundWhenDoneLocked(info.id);
         }
 
         if (!tmpZip.renameTo(info.bugreportFile)) {
@@ -1508,24 +1534,27 @@
      * Updates the user-provided details of a bugreport.
      */
     private void updateBugreportInfo(int id, String name, String title, String description) {
-        final BugreportInfo info = getInfo(id);
+        final BugreportInfo info;
+        synchronized (mLock) {
+            info = getInfoLocked(id);
+        }
         if (info == null) {
             return;
         }
-        if (title != null && !title.equals(info.title)) {
+        if (title != null && !title.equals(info.getTitle())) {
             Log.d(TAG, "updating bugreport title: " + title);
             MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_DETAILS_TITLE_CHANGED);
         }
-        info.title = title;
-        if (description != null && !description.equals(info.description)) {
+        info.setTitle(title);
+        if (description != null && !description.equals(info.getDescription())) {
             Log.d(TAG, "updating bugreport description: " + description.length() + " chars");
             MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_DETAILS_DESCRIPTION_CHANGED);
         }
-        info.description = description;
-        if (name != null && !name.equals(info.name)) {
+        info.setDescription(description);
+        if (name != null && !name.equals(info.getName())) {
             Log.d(TAG, "updating bugreport name: " + name);
             MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_DETAILS_NAME_CHANGED);
-            info.name = name;
+            info.setName(name);
             updateProgress(info);
         }
     }
@@ -1640,14 +1669,14 @@
 
             // Then set fields.
             mId = info.id;
-            if (!TextUtils.isEmpty(info.name)) {
-                mInfoName.setText(info.name);
+            if (!TextUtils.isEmpty(info.getName())) {
+                mInfoName.setText(info.getName());
             }
-            if (!TextUtils.isEmpty(info.title)) {
-                mInfoTitle.setText(info.title);
+            if (!TextUtils.isEmpty(info.getTitle())) {
+                mInfoTitle.setText(info.getTitle());
             }
-            if (!TextUtils.isEmpty(info.description)) {
-                mInfoDescription.setText(info.description);
+            if (!TextUtils.isEmpty(info.getDescription())) {
+                mInfoDescription.setText(info.getDescription());
             }
 
             // And finally display it.
@@ -1666,7 +1695,7 @@
                     @Override
                     public void onClick(View view) {
                         MetricsLogger.action(context, MetricsEvent.ACTION_BUGREPORT_DETAILS_SAVED);
-                        sanitizeName(info.name);
+                        sanitizeName(info.getName());
                         final String name = mInfoName.getText().toString();
                         final String title = mInfoTitle.getText().toString();
                         final String description = mInfoDescription.getText().toString();
@@ -1817,6 +1846,8 @@
          */
         int type;
 
+        private final Object mLock = new Object();
+
         /**
          * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED.
          */
@@ -1855,6 +1886,78 @@
             return getFd(screenshotFiles.get(0));
         }
 
+        void setFinished(boolean isFinished) {
+            synchronized (mLock) {
+                this.finished = isFinished;
+            }
+        }
+
+        boolean isFinished() {
+            synchronized (mLock) {
+                return finished;
+            }
+        }
+
+        void setTitle(String title) {
+            synchronized (mLock) {
+                this.title = title;
+            }
+        }
+
+        String getTitle() {
+            synchronized (mLock) {
+                return title;
+            }
+        }
+
+        void setName(String name) {
+            synchronized (mLock) {
+                this.name = name;
+            }
+        }
+
+        String getName() {
+            synchronized (mLock) {
+                return name;
+            }
+        }
+
+        void setDescription(String description) {
+            synchronized (mLock) {
+                this.description = description;
+            }
+        }
+
+        String getDescription() {
+            synchronized (mLock) {
+                return description;
+            }
+        }
+
+        void setProgress(int progress) {
+            synchronized (mLock) {
+                this.progress = progress;
+            }
+        }
+
+        int getProgress() {
+            synchronized (mLock) {
+                return progress;
+            }
+        }
+
+        void setLastUpdate(long lastUpdate) {
+            synchronized (mLock) {
+                this.lastUpdate = lastUpdate;
+            }
+        }
+
+        long getLastUpdate() {
+            synchronized (mLock) {
+                return lastUpdate;
+            }
+        }
+
         /**
          * Gets the name for next user triggered screenshot file.
          */
@@ -1924,9 +2027,9 @@
             if (context == null) {
                 // Restored from Parcel
                 return formattedLastUpdate == null ?
-                        Long.toString(lastUpdate) : formattedLastUpdate;
+                        Long.toString(getLastUpdate()) : formattedLastUpdate;
             }
-            return DateUtils.formatDateTime(context, lastUpdate,
+            return DateUtils.formatDateTime(context, getLastUpdate(),
                     DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME);
         }
 
@@ -2047,13 +2150,13 @@
             progress = CAPPED_PROGRESS;
         }
         if (DEBUG) {
-            if (progress != info.progress) {
-                Log.v(TAG, "Updating progress for name " + info.name + "(id: " + info.id
-                        + ") from " + info.progress + " to " + progress);
+            if (progress != info.getProgress()) {
+                Log.v(TAG, "Updating progress for name " + info.getName() + "(id: " + info.id
+                        + ") from " + info.getProgress() + " to " + progress);
             }
         }
-        info.progress = progress;
-        info.lastUpdate = System.currentTimeMillis();
+        info.setProgress(progress);
+        info.setLastUpdate(System.currentTimeMillis());
 
         updateProgress(info);
     }
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index f39e7af..2653b6d 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -756,7 +756,7 @@
                 final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(),
                         mIpv4Address.getPrefixLength());
                 NetdUtils.tetherInterface(mNetd, mIfaceName, ipv4Prefix);
-            } catch (RemoteException | ServiceSpecificException e) {
+            } catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
                 mLog.e("Error Tethering: " + e);
                 mLastError = TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
                 return;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b334b26..60a30d3 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -64,6 +64,7 @@
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.net.CaptivePortal;
+import android.net.CaptivePortalData;
 import android.net.ConnectionInfo;
 import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
 import android.net.ConnectivityDiagnosticsManager.DataStallReport;
@@ -546,6 +547,14 @@
     public static final int EVENT_PROBE_STATUS_CHANGED = 46;
 
     /**
+     * Event for NetworkMonitor to inform ConnectivityService that captive portal data has changed.
+     * arg1 = unused
+     * arg2 = netId
+     * obj = captive portal data
+     */
+    private static final int EVENT_CAPPORT_DATA_CHANGED = 47;
+
+    /**
      * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
      * should be shown.
      */
@@ -2824,6 +2833,12 @@
                     updatePrivateDns(nai, (PrivateDnsConfig) msg.obj);
                     break;
                 }
+                case EVENT_CAPPORT_DATA_CHANGED: {
+                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
+                    if (nai == null) break;
+                    handleCaptivePortalDataUpdate(nai, (CaptivePortalData) msg.obj);
+                    break;
+                }
             }
             return true;
         }
@@ -2991,6 +3006,13 @@
         }
 
         @Override
+        public void notifyCaptivePortalDataChanged(CaptivePortalData data) {
+            mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
+                    EVENT_CAPPORT_DATA_CHANGED,
+                    0, mNetId, data));
+        }
+
+        @Override
         public void showProvisioningNotification(String action, String packageName) {
             final Intent intent = new Intent(action);
             intent.setPackage(packageName);
@@ -3118,6 +3140,13 @@
         handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
     }
 
+    private void handleCaptivePortalDataUpdate(@NonNull final NetworkAgentInfo nai,
+            @Nullable final CaptivePortalData data) {
+        nai.captivePortalData = data;
+        // CaptivePortalData will be merged into LinkProperties from NetworkAgentInfo
+        handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
+    }
+
     /**
      * Updates the linger state from the network requests inside the NAI.
      * @param nai the agent info to update
@@ -5838,6 +5867,10 @@
 
         updateWakeOnLan(newLp);
 
+        // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo,
+        // it is not contained in LinkProperties sent from NetworkAgents so needs to be merged here.
+        newLp.setCaptivePortalData(networkAgent.captivePortalData);
+
         // TODO - move this check to cover the whole function
         if (!Objects.equals(newLp, oldLp)) {
             synchronized (networkAgent) {
@@ -6898,6 +6931,15 @@
             // worry about multiple different substates of CONNECTED.
             newInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, info.getReason(),
                     info.getExtraInfo());
+        } else if (!suspended && info.getDetailedState() == NetworkInfo.DetailedState.SUSPENDED) {
+            // SUSPENDED state is currently only overridden from CONNECTED state. In the case the
+            // network agent is created, then goes to suspended, then goes out of suspended without
+            // ever setting connected. Check if network agent is ever connected to update the state.
+            newInfo.setDetailedState(nai.everConnected
+                    ? NetworkInfo.DetailedState.CONNECTED
+                    : NetworkInfo.DetailedState.CONNECTING,
+                    info.getReason(),
+                    info.getExtraInfo());
         }
         newInfo.setRoaming(!nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         return newInfo;
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 592be2f..4612cfd 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.net.CaptivePortalData;
 import android.net.IDnsResolver;
 import android.net.INetd;
 import android.net.INetworkMonitor;
@@ -167,6 +168,10 @@
     // Set to true when partial connectivity was detected.
     public boolean partialConnectivity;
 
+    // Captive portal info of the network, if any.
+    // Obtained by ConnectivityService and merged into NetworkAgent-provided information.
+    public CaptivePortalData captivePortalData;
+
     // Networks are lingered when they become unneeded as a result of their NetworkRequests being
     // satisfied by a higher-scoring network. so as to allow communication to wrap up before the
     // network is taken down.  This usually only happens to the default network. Lingering ends with
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index b24a938..563dcf7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -130,7 +130,7 @@
             Set<String> packageNames, int userId);
 
     /**
-     *  Notifies that any of the {@link AbstractNetworkStatsProvider} has reached its quota
+     *  Notifies that the specified {@link AbstractNetworkStatsProvider} has reached its quota
      *  which was set through {@link AbstractNetworkStatsProvider#setLimit(String, long)}.
      *
      * @param tag the human readable identifier of the custom network stats provider.
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index bb954ab..aacb46e9 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -4588,13 +4588,13 @@
                     final long quota = ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL);
                     removeInterfaceQuota(iface);
                     setInterfaceQuota(iface, quota);
-                    mNetworkStats.setStatsProviderLimit(iface, quota);
+                    mNetworkStats.setStatsProviderLimitAsync(iface, quota);
                     return true;
                 }
                 case MSG_REMOVE_INTERFACE_QUOTA: {
                     final String iface = (String) msg.obj;
                     removeInterfaceQuota(iface);
-                    mNetworkStats.setStatsProviderLimit(iface, QUOTA_UNLIMITED);
+                    mNetworkStats.setStatsProviderLimitAsync(iface, QUOTA_UNLIMITED);
                     return true;
                 }
                 case MSG_RESET_FIREWALL_RULES_BY_UID: {
diff --git a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
index 6d72cb5..0cb0bc2c 100644
--- a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java
@@ -40,5 +40,5 @@
      * Set the quota limit to all registered custom network stats providers.
      * Note that invocation of any interface will be sent to all providers.
      */
-    public abstract void setStatsProviderLimit(@NonNull String iface, long quota);
+    public abstract void setStatsProviderLimitAsync(@NonNull String iface, long quota);
 }
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index bde9ee2..0662400 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -155,6 +155,8 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Collect and persist detailed network statistics, and provide this data to
@@ -255,7 +257,6 @@
     }
 
     private final Object mStatsLock = new Object();
-    private final Object mStatsProviderLock = new Object();
 
     /** Set of currently active ifaces. */
     @GuardedBy("mStatsLock")
@@ -280,8 +281,11 @@
     private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
             new DropBoxNonMonotonicObserver();
 
+    private static final int MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS = 100;
     private final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList =
             new RemoteCallbackList<>();
+    /** Semaphore used to wait for stats provider to respond to request stats update. */
+    private final Semaphore mStatsProviderSem = new Semaphore(0, true);
 
     @GuardedBy("mStatsLock")
     private NetworkStatsRecorder mDevRecorder;
@@ -1337,6 +1341,25 @@
         final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0;
         final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0;
 
+        // Request asynchronous stats update from all providers for next poll. And wait a bit of
+        // time to allow providers report-in given that normally binder call should be fast.
+        // TODO: request with a valid token.
+        Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate");
+        final int registeredCallbackCount = mStatsProviderCbList.getRegisteredCallbackCount();
+        mStatsProviderSem.drainPermits();
+        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */));
+        try {
+            mStatsProviderSem.tryAcquire(registeredCallbackCount,
+                    MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            // Strictly speaking it's possible a provider happened to deliver between the timeout
+            // and the log, and that doesn't matter too much as this is just a debug log.
+            Log.d(TAG, "requestStatsUpdate - providers responded "
+                    + mStatsProviderSem.availablePermits()
+                    + "/" + registeredCallbackCount + " : " + e);
+        }
+        Trace.traceEnd(TRACE_TAG_NETWORK);
+
         // TODO: consider marking "untrusted" times in historical stats
         final long currentTime = mClock.millis();
 
@@ -1374,10 +1397,6 @@
             performSampleLocked();
         }
 
-        // request asynchronous stats update from all providers for next poll.
-        // TODO: request with a valid token.
-        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */));
-
         // finally, dispatch updated event to any listeners
         final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
         updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -1501,8 +1520,8 @@
         }
 
         @Override
-        public void setStatsProviderLimit(@NonNull String iface, long quota) {
-            Slog.v(TAG, "setStatsProviderLimit(" + iface + "," + quota + ")");
+        public void setStatsProviderLimitAsync(@NonNull String iface, long quota) {
+            Slog.v(TAG, "setStatsProviderLimitAsync(" + iface + "," + quota + ")");
             invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setLimit(iface, quota));
         }
     }
@@ -1783,9 +1802,9 @@
      * {@code unregister()} of the returned callback.
      *
      * @param tag a human readable identifier of the custom network stats provider.
-     * @param provider the binder interface of
-     *                 {@link android.net.netstats.provider.AbstractNetworkStatsProvider} that
-     *                 needs to be registered to the system.
+     * @param provider the {@link INetworkStatsProvider} binder corresponding to the
+     *                 {@link android.net.netstats.provider.AbstractNetworkStatsProvider} to be
+     *                 registered.
      *
      * @return a binder interface of
      *         {@link android.net.netstats.provider.NetworkStatsProviderCallback}, which can be
@@ -1798,7 +1817,8 @@
         Objects.requireNonNull(tag, "tag is null");
         try {
             NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl(
-                            tag, provider, mAlertObserver, mStatsProviderCbList);
+                    tag, provider, mStatsProviderSem, mAlertObserver,
+                    mStatsProviderCbList);
             mStatsProviderCbList.register(callback);
             Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid="
                     + getCallingUid() + "/" + getCallingPid());
@@ -1823,7 +1843,7 @@
 
     private void invokeForAllStatsProviderCallbacks(
             @NonNull ThrowingConsumer<NetworkStatsProviderCallbackImpl, RemoteException> task) {
-        synchronized (mStatsProviderCbList) {
+        synchronized (mStatsLock) {
             final int length = mStatsProviderCbList.beginBroadcast();
             try {
                 for (int i = 0; i < length; i++) {
@@ -1844,25 +1864,30 @@
     private static class NetworkStatsProviderCallbackImpl extends INetworkStatsProviderCallback.Stub
             implements IBinder.DeathRecipient {
         @NonNull final String mTag;
-        @NonNull private final Object mProviderStatsLock = new Object();
+
         @NonNull final INetworkStatsProvider mProvider;
+        @NonNull private final Semaphore mSemaphore;
         @NonNull final INetworkManagementEventObserver mAlertObserver;
         @NonNull final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
 
+        @NonNull private final Object mProviderStatsLock = new Object();
+
         @GuardedBy("mProviderStatsLock")
-        // STATS_PER_IFACE and STATS_PER_UID
+        // Track STATS_PER_IFACE and STATS_PER_UID separately.
         private final NetworkStats mIfaceStats = new NetworkStats(0L, 0);
         @GuardedBy("mProviderStatsLock")
         private final NetworkStats mUidStats = new NetworkStats(0L, 0);
 
         NetworkStatsProviderCallbackImpl(
                 @NonNull String tag, @NonNull INetworkStatsProvider provider,
+                @NonNull Semaphore semaphore,
                 @NonNull INetworkManagementEventObserver alertObserver,
                 @NonNull RemoteCallbackList<NetworkStatsProviderCallbackImpl> cbList)
                 throws RemoteException {
             mTag = tag;
             mProvider = provider;
             mProvider.asBinder().linkToDeath(this, 0);
+            mSemaphore = semaphore;
             mAlertObserver = alertObserver;
             mStatsProviderCbList = cbList;
         }
@@ -1881,7 +1906,8 @@
                     default:
                         throw new IllegalArgumentException("Invalid type: " + how);
                 }
-                // Return a defensive copy instead of local reference.
+                // Callers might be able to mutate the returned object. Return a defensive copy
+                // instead of local reference.
                 return stats.clone();
             }
         }
@@ -1895,6 +1921,7 @@
                 if (ifaceStats != null) mIfaceStats.combineAllValues(ifaceStats);
                 if (uidStats != null) mUidStats.combineAllValues(uidStats);
             }
+            mSemaphore.release();
         }
 
         @Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 063ad0f..707e8ad 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11055,14 +11055,21 @@
 
     @Override
     public void setAccountManagementDisabled(ComponentName who, String accountType,
-            boolean disabled) {
+            boolean disabled, boolean parent) {
         if (!mHasFeature) {
             return;
         }
         Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
+            /*
+             * When called on the parent DPM instance (parent == true), affects active admin
+             * selection in two ways:
+             * * The ActiveAdmin must be of an org-owned profile owner.
+             * * The parent ActiveAdmin instance should be used for managing the restriction.
+             */
             ActiveAdmin ap = getActiveAdminForCallerLocked(who,
-                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+                    parent ? DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER
+                            : DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
             if (disabled) {
                 ap.accountTypesWithManagementDisabled.add(accountType);
             } else {
@@ -11074,22 +11081,34 @@
 
     @Override
     public String[] getAccountTypesWithManagementDisabled() {
-        return getAccountTypesWithManagementDisabledAsUser(UserHandle.getCallingUserId());
+        return getAccountTypesWithManagementDisabledAsUser(UserHandle.getCallingUserId(), false);
     }
 
     @Override
-    public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
+    public String[] getAccountTypesWithManagementDisabledAsUser(int userId, boolean parent) {
         enforceFullCrossUsersPermission(userId);
         if (!mHasFeature) {
             return null;
         }
         synchronized (getLockObject()) {
-            DevicePolicyData policy = getUserData(userId);
-            final int N = policy.mAdminList.size();
-            ArraySet<String> resultSet = new ArraySet<>();
-            for (int i = 0; i < N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                resultSet.addAll(admin.accountTypesWithManagementDisabled);
+            final ArraySet<String> resultSet = new ArraySet<>();
+
+            if (!parent) {
+                final DevicePolicyData policy = getUserData(userId);
+                for (ActiveAdmin admin : policy.mAdminList) {
+                    resultSet.addAll(admin.accountTypesWithManagementDisabled);
+                }
+            }
+
+            // Check if there's a profile owner of an org-owned device and the method is called for
+            // the parent user of this profile owner.
+            final ActiveAdmin orgOwnedAdmin =
+                    getProfileOwnerOfOrganizationOwnedDeviceLocked(userId);
+            final boolean shouldGetParentAccounts = orgOwnedAdmin != null && (parent
+                    || UserHandle.getUserId(orgOwnedAdmin.getUid()) != userId);
+            if (shouldGetParentAccounts) {
+                resultSet.addAll(
+                        orgOwnedAdmin.getParentActiveAdmin().accountTypesWithManagementDisabled);
             }
             return resultSet.toArray(new String[resultSet.size()]);
         }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 7b2b30b..37ce510 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -96,7 +96,6 @@
 import android.util.ArraySet;
 import android.util.Pair;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
@@ -6058,6 +6057,55 @@
                 .thenReturn(packages);
     }
 
+    public void testSetAccountTypesWithManagementDisabledOnManagedProfile() throws Exception {
+        setupProfileOwner();
+
+        final String accountType = "com.example.account.type";
+        int originalUid = mContext.binder.callingUid;
+        dpm.setAccountManagementDisabled(admin1, accountType, true);
+        assertThat(dpm.getAccountTypesWithManagementDisabled()).asList().containsExactly(
+                accountType);
+        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
+        assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
+
+        mContext.binder.callingUid = originalUid;
+        dpm.setAccountManagementDisabled(admin1, accountType, false);
+        assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
+    }
+
+    public void testSetAccountTypesWithManagementDisabledOnOrgOwnedManagedProfile()
+            throws Exception {
+        final int managedProfileUserId = 15;
+        final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);
+
+        addManagedProfile(admin1, managedProfileAdminUid, admin1);
+        mContext.binder.callingUid = managedProfileAdminUid;
+
+        configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
+
+        int originalUid = mContext.binder.callingUid;
+        final String accountType = "com.example.account.type";
+        dpm.getParentProfileInstance(admin1).setAccountManagementDisabled(admin1, accountType,
+                true);
+        assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
+        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
+        assertThat(dpm.getAccountTypesWithManagementDisabled()).asList().containsExactly(
+                accountType);
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        assertThat(dpm.getAccountTypesWithManagementDisabled()).asList().containsExactly(
+                accountType);
+
+        mContext.binder.callingUid = originalUid;
+        dpm.getParentProfileInstance(admin1).setAccountManagementDisabled(admin1, accountType,
+                false);
+        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
+        assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
+    }
+
     // admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one.
     private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception {
         writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM,
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index a1baf0e..7e3cfc8 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1779,7 +1779,7 @@
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
-        verify(mStatsService).setStatsProviderLimit(TEST_IFACE, Long.MAX_VALUE);
+        verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, Long.MAX_VALUE);
 
         // Set limit to 10KB.
         setNetworkPolicies(new NetworkPolicy(
@@ -1788,7 +1788,7 @@
         postMsgAndWaitForCompletion();
 
         // Verifies that remaining quota is set to providers.
-        verify(mStatsService).setStatsProviderLimit(TEST_IFACE, 10000L - 4999L);
+        verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, 10000L - 4999L);
 
         reset(mStatsService);
 
@@ -1810,7 +1810,7 @@
         postMsgAndWaitForCompletion();
         verify(mStatsService).forceUpdate();
         postMsgAndWaitForCompletion();
-        verify(mStatsService).setStatsProviderLimit(TEST_IFACE, 10000L - 4999L - 1999L);
+        verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, 10000L - 4999L - 1999L);
     }
 
     /**
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 968f552..2e59966 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -799,6 +799,14 @@
             mProbesSucceeded = probesSucceeded;
         }
 
+        void notifyCaptivePortalDataChanged(CaptivePortalData data) {
+            try {
+                mNmCallbacks.notifyCaptivePortalDataChanged(data);
+            } catch (RemoteException e) {
+                throw new AssertionError("This cannot happen", e);
+            }
+        }
+
         public String waitForRedirectUrl() {
             assertTrue(mNetworkStatusReceived.block(TIMEOUT_MS));
             return mRedirectUrl;
@@ -1845,18 +1853,21 @@
         final Uri capportUrl = Uri.parse("https://capport.example.com/api");
         final CaptivePortalData capportData = new CaptivePortalData.Builder()
                 .setCaptive(true).build();
-        newLp.setCaptivePortalApiUrl(capportUrl);
-        newLp.setCaptivePortalData(capportData);
-        mWiFiNetworkAgent.sendLinkProperties(newLp);
 
         final Uri expectedCapportUrl = sanitized ? null : capportUrl;
-        final CaptivePortalData expectedCapportData = sanitized ? null : capportData;
+        newLp.setCaptivePortalApiUrl(capportUrl);
+        mWiFiNetworkAgent.sendLinkProperties(newLp);
         callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
-                Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl())
-                && Objects.equals(expectedCapportData, lp.getCaptivePortalData()));
+                Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl()));
         defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
-                Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl())
-                && Objects.equals(expectedCapportData, lp.getCaptivePortalData()));
+                Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl()));
+
+        final CaptivePortalData expectedCapportData = sanitized ? null : capportData;
+        mWiFiNetworkAgent.notifyCaptivePortalDataChanged(capportData);
+        callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
+                Objects.equals(expectedCapportData, lp.getCaptivePortalData()));
+        defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
+                Objects.equals(expectedCapportData, lp.getCaptivePortalData()));
 
         final LinkProperties lp = mCm.getLinkProperties(mWiFiNetworkAgent.getNetwork());
         assertEquals(expectedCapportUrl, lp.getCaptivePortalApiUrl());
@@ -2810,6 +2821,40 @@
         assertNoCallbacks(captivePortalCallback, validatedCallback);
     }
 
+    @Test
+    public void testCaptivePortalApi() throws Exception {
+        mServiceContext.setPermission(
+                android.Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+
+        final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
+        final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
+                .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
+        mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback);
+
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+        final String redirectUrl = "http://example.com/firstPath";
+
+        mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl, false /* isStrictMode */);
+        captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+        final CaptivePortalData testData = new CaptivePortalData.Builder()
+                .setUserPortalUrl(Uri.parse(redirectUrl))
+                .setBytesRemaining(12345L)
+                .build();
+
+        mWiFiNetworkAgent.notifyCaptivePortalDataChanged(testData);
+
+        captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+                lp -> testData.equals(lp.getCaptivePortalData()));
+
+        final LinkProperties newLps = new LinkProperties();
+        newLps.setMtu(1234);
+        mWiFiNetworkAgent.sendLinkProperties(newLps);
+        // CaptivePortalData is not lost and unchanged when LPs are received from the NetworkAgent
+        captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+                lp -> testData.equals(lp.getCaptivePortalData()) && lp.getMtu() == 1234);
+    }
+
     private NetworkRequest.Builder newWifiRequestBuilder() {
         return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI);
     }
@@ -3154,6 +3199,7 @@
                 mCellNetworkAgent);
         cellNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
+        assertEquals(NetworkInfo.State.SUSPENDED, mCm.getActiveNetworkInfo().getState());
 
         // Register a garden variety default network request.
         TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback();
@@ -3169,6 +3215,7 @@
                 mCellNetworkAgent);
         cellNetworkCallback.expectCallback(CallbackEntry.RESUMED, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
+        assertEquals(NetworkInfo.State.CONNECTED, mCm.getActiveNetworkInfo().getState());
 
         dfltNetworkCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(dfltNetworkCallback);