Merge "Pull more data from binder calls."
diff --git a/Android.bp b/Android.bp
index a44834b..1e12dac 100644
--- a/Android.bp
+++ b/Android.bp
@@ -85,6 +85,7 @@
         "core/java/android/app/IUidObserver.aidl",
         "core/java/android/app/IUiAutomationConnection.aidl",
         "core/java/android/app/IUiModeManager.aidl",
+        "core/java/android/app/IUriGrantsManager.aidl",
         "core/java/android/app/IUserSwitchObserver.aidl",
         "core/java/android/app/IWallpaperManager.aidl",
         "core/java/android/app/IWallpaperManagerCallback.aidl",
@@ -653,8 +654,6 @@
             "system/bt/binder",
             "system/security/keystore/binder",
         ],
-
-        generate_get_transaction_name: true
     },
 
     exclude_srcs: [
diff --git a/Android.mk b/Android.mk
index 7890983..d7be055 100644
--- a/Android.mk
+++ b/Android.mk
@@ -772,7 +772,7 @@
     "writeObject\(Ljava/io/ObjectOutputStream;\)V" \
     "writeReplace\(\)Ljava/lang/Object;"
 $(LOCAL_LIGHT_GREYLIST): $(LOCAL_SRC_ALL)
-	sort $(LOCAL_SRC_GREYLIST) $(LOCAL_SRC_VENDOR_LIST) \
+	sort $(LOCAL_SRC_GREYLIST) $(LOCAL_SRC_VENDOR_LIST) $(PRIVATE_GREYLIST_INPUTS) \
 	     <(grep -E "\->("$(subst $(space),"|",$(REGEX_SERIALIZATION))")$$" \
 	               $(LOCAL_SRC_PRIVATE_API)) \
 	     <(comm -12 <(sort $(LOCAL_SRC_REMOVED_API)) <(sort $(LOCAL_SRC_PRIVATE_API))) \
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3f579bc..90fa8ca 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2559,7 +2559,6 @@
         return clearApplicationUserData(mContext.getPackageName(), null);
     }
 
-
     /**
      * Permits an application to get the persistent URI permissions granted to another.
      *
@@ -2571,17 +2570,13 @@
      * @return list of granted URI permissions
      *
      * @hide
+     * @deprecated use {@link UriGrantsManager#getGrantedUriPermissions(String)} instead.
      */
+    @Deprecated
     public ParceledListSlice<GrantedUriPermission> getGrantedUriPermissions(
             @Nullable String packageName) {
-        try {
-            @SuppressWarnings("unchecked")
-            final ParceledListSlice<GrantedUriPermission> castedList = getService()
-                    .getGrantedUriPermissions(packageName, mContext.getUserId());
-            return castedList;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return ((UriGrantsManager) mContext.getSystemService(Context.URI_GRANTS_SERVICE))
+                .getGrantedUriPermissions(packageName);
     }
 
     /**
@@ -2592,14 +2587,12 @@
      * @param packageName application to clear its granted permissions
      *
      * @hide
+     * @deprecated use {@link UriGrantsManager#clearGrantedUriPermissions(String)} instead.
      */
+    @Deprecated
     public void clearGrantedUriPermissions(String packageName) {
-        try {
-            getService().clearGrantedUriPermissions(packageName,
-                    mContext.getUserId());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        ((UriGrantsManager) mContext.getSystemService(Context.URI_GRANTS_SERVICE))
+                .clearGrantedUriPermissions(packageName);
     }
 
     /**
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 2e4404c..c895978 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -50,13 +50,6 @@
     public static final int ALLOW_FULL_ONLY = 2;
 
     /**
-     * Grant Uri permissions from one app to another. This method only extends
-     * permission grants if {@code callingUid} has permission to them.
-     */
-    public abstract void grantUriPermissionFromIntent(int callingUid, String targetPkg,
-            Intent intent, int targetUserId);
-
-    /**
      * Verify that calling app has access to the given provider.
      */
     public abstract String checkContentProviderAccess(String authority, int userId);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 19d7c83..285f83b 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -243,12 +243,6 @@
     boolean isTopActivityImmersive();
     void crashApplication(int uid, int initialPid, in String packageName, int userId, in String message);
     String getProviderMimeType(in Uri uri, int userId);
-    IBinder newUriPermissionOwner(in String name);
-    void grantUriPermissionFromOwner(in IBinder owner, int fromUid, in String targetPkg,
-            in Uri uri, int mode, int sourceUserId, int targetUserId);
-    void revokeUriPermissionFromOwner(in IBinder owner, in Uri uri, int mode, int userId);
-    int checkGrantUriPermission(int callingUid, in String targetPkg, in Uri uri,
-            int modeFlags, int userId);
     // Cause the specified process to dump the specified heap.
     boolean dumpHeap(in String process, int userId, boolean managed, boolean mallocInfo,
             boolean runGc, in String path, in ParcelFileDescriptor fd,
@@ -363,9 +357,6 @@
     ActivityManager.StackInfo getFocusedStackInfo();
     void restart();
     void performIdleMaintenance();
-    void takePersistableUriPermission(in Uri uri, int modeFlags, String toPackage, int userId);
-    void releasePersistableUriPermission(in Uri uri, int modeFlags, String toPackage, int userId);
-    ParceledListSlice getPersistedUriPermissions(in String packageName, boolean incoming);
     void appNotRespondingViaProvider(in IBinder connection);
     Rect getTaskBounds(int taskId);
     boolean setProcessMemoryTrimLevel(in String process, int uid, int level);
@@ -441,12 +432,6 @@
     void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
             in Rect tempDockedTaskInsetBounds,
             in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
-    // Gets the URI permissions granted to an arbitrary package (or all packages if null)
-    // NOTE: this is different from getPersistedUriPermissions(), which returns the URIs the package
-    // granted to another packages (instead of those granted to it).
-    ParceledListSlice getGrantedUriPermissions(in String packageName, int userId);
-    // Clears the URI permissions granted to an arbitrary package.
-    void clearGrantedUriPermissions(in String packageName, int userId);
     boolean isAppForeground(int uid);
     void removeStack(int stackId);
     void makePackageIdle(String packageName, int userId);
diff --git a/core/java/android/app/IUriGrantsManager.aidl b/core/java/android/app/IUriGrantsManager.aidl
new file mode 100644
index 0000000..928c627
--- /dev/null
+++ b/core/java/android/app/IUriGrantsManager.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.pm.ParceledListSlice;
+import android.net.Uri;
+import android.os.IBinder;
+
+/**
+ * Interface for managing an app's permission to access a particular URI.
+ * {@hide}
+ */
+interface IUriGrantsManager {
+    void takePersistableUriPermission(in Uri uri, int modeFlags, String toPackage, int userId);
+    void releasePersistableUriPermission(in Uri uri, int modeFlags, String toPackage, int userId);
+    void grantUriPermissionFromOwner(in IBinder owner, int fromUid, in String targetPkg,
+            in Uri uri, int mode, int sourceUserId, int targetUserId);
+    /**
+     * Gets the URI permissions granted to an arbitrary package (or all packages if null)
+     * NOTE: this is different from getPersistedUriPermissions(), which returns the URIs the package
+     * granted to another packages (instead of those granted to it).
+     */
+    ParceledListSlice getGrantedUriPermissions(in String packageName, int userId);
+    /** Clears the URI permissions granted to an arbitrary package. */
+    void clearGrantedUriPermissions(in String packageName, int userId);
+    ParceledListSlice getPersistedUriPermissions(in String packageName, boolean incoming);
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index b432baa..003f364 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -226,6 +226,14 @@
                         ctx.getOuterContext(), ctx.mMainThread.getHandler());
             }});
 
+        registerService(Context.URI_GRANTS_SERVICE, UriGrantsManager.class,
+                new CachedServiceFetcher<UriGrantsManager>() {
+            @Override
+            public UriGrantsManager createService(ContextImpl ctx) {
+                return new UriGrantsManager(
+                        ctx.getOuterContext(), ctx.mMainThread.getHandler());
+            }});
+
         registerService(Context.ALARM_SERVICE, AlarmManager.class,
                 new CachedServiceFetcher<AlarmManager>() {
             @Override
diff --git a/core/java/android/app/UriGrantsManager.java b/core/java/android/app/UriGrantsManager.java
new file mode 100644
index 0000000..5096f73
--- /dev/null
+++ b/core/java/android/app/UriGrantsManager.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app;
+
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Singleton;
+
+/**
+ * Class for managing an app's permission to access a particular {@link android.net.Uri}.
+ *
+ * @hide
+ */
+@SystemService(Context.URI_GRANTS_SERVICE)
+public class UriGrantsManager {
+
+    private final Context mContext;
+
+    UriGrantsManager(Context context, Handler handler) {
+        mContext = context;
+    }
+
+    /** @hide */
+    public static IUriGrantsManager getService() {
+        return IUriGrantsManagerSingleton.get();
+    }
+
+    private static final Singleton<IUriGrantsManager> IUriGrantsManagerSingleton =
+            new Singleton<IUriGrantsManager>() {
+                @Override
+                protected IUriGrantsManager create() {
+                    final IBinder b = ServiceManager.getService(Context.URI_GRANTS_SERVICE);
+                    return IUriGrantsManager.Stub.asInterface(b);
+                }
+            };
+
+    /**
+     * Permits an application to clear the persistent URI permissions granted to another.
+     *
+     * <p>Typically called by Settings, requires {@code CLEAR_APP_GRANTED_URI_PERMISSIONS}.
+     *
+     * @param packageName application to clear its granted permissions
+     *
+     * @hide
+     */
+    public void clearGrantedUriPermissions(String packageName) {
+        try {
+            getService().clearGrantedUriPermissions(packageName, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Permits an application to get the persistent URI permissions granted to another.
+     *
+     * <p>Typically called by Settings or DocumentsUI, requires
+     * {@code GET_APP_GRANTED_URI_PERMISSIONS}.
+     *
+     * @param packageName application to look for the granted permissions, or {@code null} to get
+     * granted permissions for all applications
+     * @return list of granted URI permissions
+     *
+     * @hide
+     */
+    public ParceledListSlice<GrantedUriPermission> getGrantedUriPermissions(
+            @Nullable String packageName) {
+        try {
+            @SuppressWarnings("unchecked")
+            final ParceledListSlice<GrantedUriPermission> castedList = getService()
+                    .getGrantedUriPermissions(packageName, mContext.getUserId());
+            return castedList;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 2099e75..51372c4 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -620,7 +620,7 @@
         String mIdType;
         String mIdEntry;
 
-        // TODO: once we have more flags, it might be better to store the individual
+        // TODO(b/37567426): once we have more flags, it might be better to store the individual
         // fields (viewId and childId) of the field.
         AutofillId mAutofillId;
         @View.AutofillType int mAutofillType = View.AUTOFILL_TYPE_NONE;
@@ -664,7 +664,7 @@
         static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000;
         static final int FLAGS_OPAQUE = 0x00008000;
 
-        // TODO: autofill data is made of many fields and ideally we should verify
+        // TODO(b/37567426): autofill data is made of many fields and ideally we should verify
         // one-by-one to optimize what's sent over, but there isn't enough flag bits for that, we'd
         // need to create a 'flags2' or 'autoFillFlags' field and add these flags there.
         // So, to keep thinkg simpler for now, let's just use on flag for all of them...
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index f923738..0ca7dae 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -26,6 +26,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
+import android.app.UriGrantsManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
@@ -2143,7 +2144,7 @@
             @Intent.AccessUriMode int modeFlags) {
         Preconditions.checkNotNull(uri, "uri");
         try {
-            ActivityManager.getService().takePersistableUriPermission(
+            UriGrantsManager.getService().takePersistableUriPermission(
                     ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
                     resolveUserId(uri));
         } catch (RemoteException e) {
@@ -2159,7 +2160,7 @@
         Preconditions.checkNotNull(toPackage, "toPackage");
         Preconditions.checkNotNull(uri, "uri");
         try {
-            ActivityManager.getService().takePersistableUriPermission(
+            UriGrantsManager.getService().takePersistableUriPermission(
                     ContentProvider.getUriWithoutUserId(uri), modeFlags, toPackage,
                     resolveUserId(uri));
         } catch (RemoteException e) {
@@ -2179,7 +2180,7 @@
             @Intent.AccessUriMode int modeFlags) {
         Preconditions.checkNotNull(uri, "uri");
         try {
-            ActivityManager.getService().releasePersistableUriPermission(
+            UriGrantsManager.getService().releasePersistableUriPermission(
                     ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
                     resolveUserId(uri));
         } catch (RemoteException e) {
@@ -2199,7 +2200,7 @@
      */
     public @NonNull List<UriPermission> getPersistedUriPermissions() {
         try {
-            return ActivityManager.getService()
+            return UriGrantsManager.getService()
                     .getPersistedUriPermissions(mPackageName, true).getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2215,7 +2216,7 @@
      */
     public @NonNull List<UriPermission> getOutgoingPersistedUriPermissions() {
         try {
-            return ActivityManager.getService()
+            return UriGrantsManager.getService()
                     .getPersistedUriPermissions(mPackageName, false).getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c515bce..6abe777 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3340,6 +3340,16 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.UriGrantsManager} for interacting with the global system state.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.UriGrantsManager
+     * @hide
+     */
+    public static final String URI_GRANTS_SERVICE = "uri_grants";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.app.AlarmManager} for receiving intents at a
      * time of your choosing.
      *
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index c6c676f..2d45b14 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -16,17 +16,26 @@
 
 package android.database.sqlite;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentValues;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
+import android.os.Build;
 import android.os.CancellationSignal;
 import android.os.OperationCanceledException;
 import android.provider.BaseColumns;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Set;
 import java.util.regex.Pattern;
 
@@ -95,9 +104,6 @@
         if (mWhereClause == null) {
             mWhereClause = new StringBuilder(inWhere.length() + 16);
         }
-        if (mWhereClause.length() == 0) {
-            mWhereClause.append('(');
-        }
         mWhereClause.append(inWhere);
     }
 
@@ -115,9 +121,6 @@
         if (mWhereClause == null) {
             mWhereClause = new StringBuilder(inWhere.length() + 16);
         }
-        if (mWhereClause.length() == 0) {
-            mWhereClause.append('(');
-        }
         DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
     }
 
@@ -376,6 +379,11 @@
             return null;
         }
 
+        final String sql;
+        final String unwrappedSql = buildQuery(
+                projectionIn, selection, groupBy, having,
+                sortOrder, limit);
+
         if (mStrict && selection != null && selection.length() > 0) {
             // Validate the user-supplied selection to detect syntactic anomalies
             // in the selection string that could indicate a SQL injection attempt.
@@ -384,25 +392,162 @@
             // originally specified. An attacker cannot create an expression that
             // would escape the SQL expression while maintaining balanced parentheses
             // in both the wrapped and original forms.
-            String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,
+
+            // NOTE: The ordering of the below operations is important; we must
+            // execute the wrapped query to ensure the untrusted clause has been
+            // fully isolated.
+
+            // Validate the unwrapped query
+            db.validateSql(unwrappedSql, cancellationSignal); // will throw if query is invalid
+
+            // Execute wrapped query for extra protection
+            final String wrappedSql = buildQuery(projectionIn, wrap(selection), groupBy,
                     having, sortOrder, limit);
-            db.validateSql(sqlForValidation, cancellationSignal); // will throw if query is invalid
+            sql = wrappedSql;
+        } else {
+            // Execute unwrapped query
+            sql = unwrappedSql;
         }
 
-        String sql = buildQuery(
-                projectionIn, selection, groupBy, having,
-                sortOrder, limit);
-
+        final String[] sqlArgs = selectionArgs;
         if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Performing query: " + sql);
+            if (Build.IS_DEBUGGABLE) {
+                Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+            } else {
+                Log.d(TAG, sql);
+            }
         }
         return db.rawQueryWithFactory(
-                mFactory, sql, selectionArgs,
+                mFactory, sql, sqlArgs,
                 SQLiteDatabase.findEditTable(mTables),
                 cancellationSignal); // will throw if query is invalid
     }
 
     /**
+     * Perform an update by combining all current settings and the
+     * information passed into this method.
+     *
+     * @param db the database to update on
+     * @param selection A filter declaring which rows to return,
+     *   formatted as an SQL WHERE clause (excluding the WHERE
+     *   itself). Passing null will return all rows for the given URL.
+     * @param selectionArgs You may include ?s in selection, which
+     *   will be replaced by the values from selectionArgs, in order
+     *   that they appear in the selection. The values will be bound
+     *   as Strings.
+     * @return the number of rows updated
+     * @hide
+     */
+    public int update(@NonNull SQLiteDatabase db, @NonNull ContentValues values,
+            @Nullable String selection, @Nullable String[] selectionArgs) {
+        Objects.requireNonNull(mTables, "No tables defined");
+        Objects.requireNonNull(db, "No database defined");
+        Objects.requireNonNull(values, "No values defined");
+
+        final String sql;
+        final String unwrappedSql = buildUpdate(values, selection);
+
+        if (mStrict) {
+            // Validate the user-supplied selection to detect syntactic anomalies
+            // in the selection string that could indicate a SQL injection attempt.
+            // The idea is to ensure that the selection clause is a valid SQL expression
+            // by compiling it twice: once wrapped in parentheses and once as
+            // originally specified. An attacker cannot create an expression that
+            // would escape the SQL expression while maintaining balanced parentheses
+            // in both the wrapped and original forms.
+
+            // NOTE: The ordering of the below operations is important; we must
+            // execute the wrapped query to ensure the untrusted clause has been
+            // fully isolated.
+
+            // Validate the unwrapped query
+            db.validateSql(unwrappedSql, null); // will throw if query is invalid
+
+            // Execute wrapped query for extra protection
+            final String wrappedSql = buildUpdate(values, wrap(selection));
+            sql = wrappedSql;
+        } else {
+            // Execute unwrapped query
+            sql = unwrappedSql;
+        }
+
+        final ArrayMap<String, Object> rawValues = values.getValues();
+        final String[] updateArgs = new String[rawValues.size()];
+        for (int i = 0; i < updateArgs.length; i++) {
+            final Object arg = rawValues.valueAt(i);
+            updateArgs[i] = (arg != null) ? arg.toString() : null;
+        }
+
+        final String[] sqlArgs = ArrayUtils.concat(String.class, updateArgs, selectionArgs);
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            if (Build.IS_DEBUGGABLE) {
+                Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+            } else {
+                Log.d(TAG, sql);
+            }
+        }
+        return db.executeSql(sql, sqlArgs);
+    }
+
+    /**
+     * Perform a delete by combining all current settings and the
+     * information passed into this method.
+     *
+     * @param db the database to delete on
+     * @param selection A filter declaring which rows to return,
+     *   formatted as an SQL WHERE clause (excluding the WHERE
+     *   itself). Passing null will return all rows for the given URL.
+     * @param selectionArgs You may include ?s in selection, which
+     *   will be replaced by the values from selectionArgs, in order
+     *   that they appear in the selection. The values will be bound
+     *   as Strings.
+     * @return the number of rows deleted
+     * @hide
+     */
+    public int delete(@NonNull SQLiteDatabase db, @Nullable String selection,
+            @Nullable String[] selectionArgs) {
+        Objects.requireNonNull(mTables, "No tables defined");
+        Objects.requireNonNull(db, "No database defined");
+
+        final String sql;
+        final String unwrappedSql = buildDelete(selection);
+
+        if (mStrict) {
+            // Validate the user-supplied selection to detect syntactic anomalies
+            // in the selection string that could indicate a SQL injection attempt.
+            // The idea is to ensure that the selection clause is a valid SQL expression
+            // by compiling it twice: once wrapped in parentheses and once as
+            // originally specified. An attacker cannot create an expression that
+            // would escape the SQL expression while maintaining balanced parentheses
+            // in both the wrapped and original forms.
+
+            // NOTE: The ordering of the below operations is important; we must
+            // execute the wrapped query to ensure the untrusted clause has been
+            // fully isolated.
+
+            // Validate the unwrapped query
+            db.validateSql(unwrappedSql, null); // will throw if query is invalid
+
+            // Execute wrapped query for extra protection
+            final String wrappedSql = buildDelete(wrap(selection));
+            sql = wrappedSql;
+        } else {
+            // Execute unwrapped query
+            sql = unwrappedSql;
+        }
+
+        final String[] sqlArgs = selectionArgs;
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            if (Build.IS_DEBUGGABLE) {
+                Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+            } else {
+                Log.d(TAG, sql);
+            }
+        }
+        return db.executeSql(sql, sqlArgs);
+    }
+
+    /**
      * Construct a SELECT statement suitable for use in a group of
      * SELECT statements that will be joined through UNION operators
      * in buildUnionQuery.
@@ -434,28 +579,10 @@
             String[] projectionIn, String selection, String groupBy,
             String having, String sortOrder, String limit) {
         String[] projection = computeProjection(projectionIn);
-
-        StringBuilder where = new StringBuilder();
-        boolean hasBaseWhereClause = mWhereClause != null && mWhereClause.length() > 0;
-
-        if (hasBaseWhereClause) {
-            where.append(mWhereClause.toString());
-            where.append(')');
-        }
-
-        // Tack on the user's selection, if present.
-        if (selection != null && selection.length() > 0) {
-            if (hasBaseWhereClause) {
-                where.append(" AND ");
-            }
-
-            where.append('(');
-            where.append(selection);
-            where.append(')');
-        }
+        String where = computeWhere(selection);
 
         return buildQueryString(
-                mDistinct, mTables, projection, where.toString(),
+                mDistinct, mTables, projection, where,
                 groupBy, having, sortOrder, limit);
     }
 
@@ -472,6 +599,42 @@
         return buildQuery(projectionIn, selection, groupBy, having, sortOrder, limit);
     }
 
+    /** {@hide} */
+    public String buildUpdate(ContentValues values, String selection) {
+        if (values == null || values.isEmpty()) {
+            throw new IllegalArgumentException("Empty values");
+        }
+
+        StringBuilder sql = new StringBuilder(120);
+        sql.append("UPDATE ");
+        sql.append(mTables);
+        sql.append(" SET ");
+
+        final ArrayMap<String, Object> rawValues = values.getValues();
+        for (int i = 0; i < rawValues.size(); i++) {
+            if (i > 0) {
+                sql.append(',');
+            }
+            sql.append(rawValues.keyAt(i));
+            sql.append("=?");
+        }
+
+        final String where = computeWhere(selection);
+        appendClause(sql, " WHERE ", where);
+        return sql.toString();
+    }
+
+    /** {@hide} */
+    public String buildDelete(String selection) {
+        StringBuilder sql = new StringBuilder(120);
+        sql.append("DELETE FROM ");
+        sql.append(mTables);
+
+        final String where = computeWhere(selection);
+        appendClause(sql, " WHERE ", where);
+        return sql.toString();
+    }
+
     /**
      * Construct a SELECT statement suitable for use in a group of
      * SELECT statements that will be joined through UNION operators
@@ -645,4 +808,37 @@
         }
         return null;
     }
+
+    private @Nullable String computeWhere(@Nullable String selection) {
+        final boolean hasInternal = !TextUtils.isEmpty(mWhereClause);
+        final boolean hasExternal = !TextUtils.isEmpty(selection);
+
+        if (hasInternal || hasExternal) {
+            final StringBuilder where = new StringBuilder();
+            if (hasInternal) {
+                where.append('(').append(mWhereClause).append(')');
+            }
+            if (hasInternal && hasExternal) {
+                where.append(" AND ");
+            }
+            if (hasExternal) {
+                where.append('(').append(selection).append(')');
+            }
+            return where.toString();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Wrap given argument in parenthesis, unless it's {@code null} or
+     * {@code ()}, in which case return it verbatim.
+     */
+    private @Nullable String wrap(@Nullable String arg) {
+        if (TextUtils.isEmpty(arg)) {
+            return arg;
+        } else {
+            return "(" + arg + ")";
+        }
+    }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index eee3630..f2429bd 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3922,7 +3922,8 @@
      * </li>
      * <li><strong>Overriden standard actions</strong> - These are actions that override
      * standard actions to customize them. For example, an app may add a label to the
-     * standard {@link #ACTION_CLICK} action to announce that this action clears browsing history.
+     * standard {@link #ACTION_CLICK} action to indicate to the user that this action clears
+     * browsing history.
      * </ul>
      * </p>
      * <p>
diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
index b6a37bc..bdb3e08 100644
--- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
+++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
@@ -81,6 +81,22 @@
     }
 
     @Test
+    public void testParcelizationForAutofill_oneSmallView() {
+        mActivity.addView(newSmallView());
+
+        waitUntilViewsAreLaidOff();
+
+        AssistStructure structure = new AssistStructure(mActivity, FOR_AUTOFILL, NO_FLAGS);
+
+        // Check properties on "original" structure
+        assertStructureWithManySmallViews(structure, 1);
+
+        // Check properties on "cloned" structure
+        AssistStructure clone = cloneThroughParcel(structure);
+        assertStructureWithManySmallViews(clone, 1);
+    }
+
+    @Test
     public void testParcelizationForAutofill_manySmallViews() {
         Log.d(TAG, "Adding " + NUMBER_SMALL_VIEWS + " small views");
 
@@ -93,14 +109,14 @@
         AssistStructure structure = new AssistStructure(mActivity, FOR_AUTOFILL, NO_FLAGS);
 
         // Check properties on "original" structure
-        assertStructureWithManySmallViews(structure);
+        assertStructureWithManySmallViews(structure, NUMBER_SMALL_VIEWS);
 
         // Check properties on "cloned" structure
         AssistStructure clone = cloneThroughParcel(structure);
-        assertStructureWithManySmallViews(clone);
+        assertStructureWithManySmallViews(clone, NUMBER_SMALL_VIEWS);
     }
 
-    private void assertStructureWithManySmallViews(AssistStructure structure) {
+    private void assertStructureWithManySmallViews(AssistStructure structure, int expectedSize) {
         int i = 0;
         try {
             assertPackageName(structure);
@@ -123,12 +139,12 @@
             // Parent
             ViewNode parent = rootView.getChildAt(1);
             assertThat(parent.getClassName()).isEqualTo(LinearLayout.class.getName());
-            assertThat(parent.getChildCount()).isEqualTo(NUMBER_SMALL_VIEWS);
+            assertThat(parent.getChildCount()).isEqualTo(expectedSize);
             assertThat(parent.getIdEntry()).isEqualTo("parent");
             assertThat(parent.getAutofillId()).isNotNull();
 
             // Children
-            for (i = 0; i < NUMBER_SMALL_VIEWS; i++) {
+            for (i = 0; i < expectedSize; i++) {
                 ViewNode smallView = parent.getChildAt(i);
                 assertSmallView(smallView);
             }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 6256922..f91dbcf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -51,8 +51,6 @@
 public class LocalBluetoothProfileManager {
     private static final String TAG = "LocalBluetoothProfileManager";
     private static final boolean DEBUG = BluetoothUtils.D;
-    /** Singleton instance. */
-    private static LocalBluetoothProfileManager sInstance;
 
     /**
      * An interface for notifying BluetoothHeadset IPC clients when they have
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
index 332a2a4..ba93970 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
@@ -88,7 +88,7 @@
     }
 
     private void updateTimes() {
-        mUptime.setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime()));
+        mUptime.setSummary(DateUtils.formatElapsedTime(SystemClock.elapsedRealtime() / 1000));
     }
 
     private static class MyHandler extends Handler {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
index 0c802af..47624ff 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
@@ -5,7 +5,7 @@
  * 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
+ * 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,
@@ -17,17 +17,19 @@
 package com.android.settingslib.drawer;
 
 import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
 import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
 import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
 
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.text.TextUtils;
-import android.widget.RemoteViews;
 
 import java.util.ArrayList;
 
@@ -36,25 +38,24 @@
  */
 public class Tile implements Parcelable {
 
+    private static final String TAG = "Tile";
+    private ActivityInfo mActivityInfo;
+
     /**
      * Title of the tile that is shown to the user.
+     *
      * @attr ref android.R.styleable#PreferenceHeader_title
      */
     public CharSequence title;
 
     /**
      * Optional summary describing what this tile controls.
+     *
      * @attr ref android.R.styleable#PreferenceHeader_summary
      */
     public CharSequence summary;
 
     /**
-     * Optional icon to show for this tile.
-     * @attr ref android.R.styleable#PreferenceHeader_icon
-     */
-    public Icon icon;
-
-    /**
      * Whether the icon can be tinted. This should be set to true for monochrome (single-color)
      * icons that can be tinted to match the design.
      */
@@ -95,8 +96,8 @@
      */
     public String key;
 
-    public Tile() {
-        // Empty
+    public Tile(ActivityInfo activityInfo) {
+        mActivityInfo = activityInfo;
     }
 
     @Override
@@ -106,14 +107,9 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mActivityInfo, flags);
         TextUtils.writeToParcel(title, dest, flags);
         TextUtils.writeToParcel(summary, dest, flags);
-        if (icon != null) {
-            dest.writeByte((byte) 1);
-            icon.writeToParcel(dest, flags);
-        } else {
-            dest.writeByte((byte) 0);
-        }
         if (intent != null) {
             dest.writeByte((byte) 1);
             intent.writeToParcel(dest, flags);
@@ -133,13 +129,37 @@
         dest.writeBoolean(isIconTintable);
     }
 
+    /**
+     * Optional icon to show for this tile.
+     *
+     * @attr ref android.R.styleable#PreferenceHeader_icon
+     */
+    public Icon getIcon() {
+        if (mActivityInfo == null || metaData == null) {
+            return null;
+        }
+        int iconResId = metaData.getInt(META_DATA_PREFERENCE_ICON);
+        // Set the icon
+        if (iconResId == 0) {
+            // Only fallback to activityinfo.icon if metadata does not contain ICON_URI.
+            // ICON_URI should be loaded in app UI when need the icon object. Handling IPC at this
+            // level is too complex because we don't have a strong threading contract for this class
+            if (!metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
+                iconResId = mActivityInfo.icon;
+            }
+        }
+        if (iconResId != 0) {
+            return Icon.createWithResource(mActivityInfo.packageName, iconResId);
+        } else {
+            return null;
+        }
+    }
+
     public void readFromParcel(Parcel in) {
+        mActivityInfo = ActivityInfo.CREATOR.createFromParcel(in);
         title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         if (in.readByte() != 0) {
-            icon = Icon.CREATOR.createFromParcel(in);
-        }
-        if (in.readByte() != 0) {
             intent = Intent.CREATOR.createFromParcel(in);
         }
         final int N = in.readInt();
@@ -162,6 +182,7 @@
         public Tile createFromParcel(Parcel source) {
             return new Tile(source);
         }
+
         public Tile[] newArray(int size) {
             return new Tile[size];
         }
@@ -169,7 +190,7 @@
 
     public boolean isPrimaryProfileOnly() {
         String profile = metaData != null ?
-            metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+                metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
         profile = (profile != null ? profile : PROFILE_ALL);
         return TextUtils.equals(profile, PROFILE_PRIMARY);
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 06f1456..11976d7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -24,7 +24,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
-import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -303,7 +302,7 @@
             Pair<String, String> key = new Pair<>(activityInfo.packageName, activityInfo.name);
             Tile tile = addedCache.get(key);
             if (tile == null) {
-                tile = new Tile();
+                tile = new Tile(activityInfo);
                 tile.intent = new Intent().setClassName(
                         activityInfo.packageName, activityInfo.name);
                 tile.category = categoryKey;
@@ -329,7 +328,6 @@
             boolean forceTintExternalIcon) {
         if (applicationInfo.isSystemApp()) {
             boolean forceTintIcon = false;
-            int icon = 0;
             CharSequence title = null;
             String summary = null;
             String keyHint = null;
@@ -347,9 +345,6 @@
                 }
 
                 if (res != null && metaData != null) {
-                    if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
-                        icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
-                    }
                     if (metaData.containsKey(META_DATA_PREFERENCE_ICON_TINTABLE)) {
                         if (forceTintIcon) {
                             Log.w(LOG_TAG, "Ignoring icon tintable for " + activityInfo);
@@ -390,18 +385,6 @@
                 title = activityInfo.loadLabel(pm).toString();
             }
 
-            // Set the icon
-            if (icon == 0) {
-                // Only fallback to activityinfo.icon if metadata does not contain ICON_URI.
-                // ICON_URI should be loaded in app UI when need the icon object.
-                if (!tile.metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
-                    icon = activityInfo.icon;
-                }
-            }
-            if (icon != 0) {
-                tile.icon = Icon.createWithResource(activityInfo.packageName, icon);
-            }
-
             // Set title and summary for the preference
             tile.title = title;
             tile.summary = summary;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/ProfileSelectDialogTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/ProfileSelectDialogTest.java
index ac2d759..63f462c 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/ProfileSelectDialogTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/ProfileSelectDialogTest.java
@@ -16,8 +16,16 @@
 
 package com.android.settingslib.drawer;
 
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.UserInfo;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -30,12 +38,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import static junit.framework.Assert.assertEquals;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ProfileSelectDialogTest {
@@ -58,7 +60,7 @@
 
     @Test
     public void testUpdateUserHandlesIfNeeded_Normal() {
-        final Tile tile = new Tile();
+        final Tile tile = new Tile(new ActivityInfo());
         tile.intent = new Intent();
         tile.userHandle.add(NORMAL_USER);
 
@@ -71,7 +73,7 @@
 
     @Test
     public void testUpdateUserHandlesIfNeeded_Remove() {
-        final Tile tile = new Tile();
+        final Tile tile = new Tile(new ActivityInfo());
         tile.intent = new Intent();
         tile.userHandle.add(REMOVED_USER);
         tile.userHandle.add(NORMAL_USER);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
index e153c3e..7553313 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
@@ -17,6 +17,7 @@
 package com.android.settingslib;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.mock;
@@ -43,13 +44,12 @@
 import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 /**
  * Tests for {@link HelpUtils}.
  */
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class HelpUtilsTest {
     private static final String TEST_HELP_URL = "intent:#Intent;action=com.android.test;end";
     private static final String PACKAGE_NAME_KEY = "package-name-key";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index ef13a5f..b2ab45c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -16,6 +16,7 @@
 package com.android.settingslib.bluetooth;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
@@ -31,23 +32,29 @@
 import android.content.res.Resources;
 
 import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class A2dpProfileTest {
 
-    @Mock Context mContext;
-    @Mock LocalBluetoothAdapter mAdapter;
-    @Mock CachedBluetoothDeviceManager mDeviceManager;
-    @Mock LocalBluetoothProfileManager mProfileManager;
-    @Mock BluetoothDevice mDevice;
-    @Mock BluetoothA2dp mBluetoothA2dp;
+    @Mock
+    Context mContext;
+    @Mock
+    LocalBluetoothAdapter mAdapter;
+    @Mock
+    CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    BluetoothDevice mDevice;
+    @Mock
+    BluetoothA2dp mBluetoothA2dp;
     BluetoothProfile.ServiceListener mServiceListener;
 
     A2dpProfile mProfile;
@@ -126,7 +133,7 @@
     private static String KNOWN_CODEC_LABEL = "Use high quality audio: %1$s";
     private static String UNKNOWN_CODEC_LABEL = "Use high quality audio";
     private static String[] CODEC_NAMES =
-            new String[] { "Default", "SBC", "AAC", "aptX", "aptX HD", "LDAC" };
+            new String[]{"Default", "SBC", "AAC", "aptX", "aptX HD", "LDAC"};
 
     /**
      * Helper for setting up several tests of getHighQualityAudioOptionLabel
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 1080690..5f42b66 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -15,26 +15,24 @@
  */
 package com.android.settingslib.bluetooth;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.verify;
 
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.Intent;
-
 import android.telephony.TelephonyManager;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class BluetoothEventManagerTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 16ed85c..c904384 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -17,12 +17,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
@@ -31,20 +27,18 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
-import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
 
 import java.util.Collection;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class CachedBluetoothDeviceManagerTest {
     private final static String DEVICE_NAME_1 = "TestName_1";
     private final static String DEVICE_NAME_2 = "TestName_2";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
index 03b023b..bc8be4d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
@@ -13,15 +13,16 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class HeadsetProfileTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index d342bc8..77fb272 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -20,7 +20,6 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -37,20 +36,19 @@
 import android.content.Intent;
 import android.os.ParcelUuid;
 
-import java.util.ArrayList;
-import java.util.List;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
 
-@RunWith(RobolectricTestRunner.class)
-@Config(resourceDir = "../../res")
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class LocalBluetoothProfileManagerTest {
     @Mock
     private CachedBluetoothDeviceManager mDeviceManager;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
index 5261ea0..4d7553c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
@@ -16,21 +16,24 @@
 package com.android.settingslib.core;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class AbstractPreferenceControllerTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
index ba955f9..20ce465 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
@@ -63,7 +63,8 @@
         uptimePreferenceController.displayPreference(mScreen);
 
         // SystemClock is shadowed so it shouldn't advance unexpectedly while the test is running
-        verify(mPreference).setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime()));
+        verify(mPreference).setSummary(
+                DateUtils.formatElapsedTime(SystemClock.elapsedRealtime() / 1000));
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
index 5d352f0..f03b334 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -1,28 +1,37 @@
 package com.android.settingslib.drawer;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
 import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
 import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.pm.ActivityInfo;
 import android.os.Bundle;
 
+import com.android.settingslib.R;
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
 public class TileTest {
 
+    private ActivityInfo mActivityInfo;
     private Tile mTile;
 
     @Before
     public void setUp() {
-        mTile = new Tile();
+        mActivityInfo = new ActivityInfo();
+        mActivityInfo.packageName = RuntimeEnvironment.application.getPackageName();
+        mActivityInfo.icon = R.drawable.ic_plus;
+        mTile = new Tile(mActivityInfo);
         mTile.metaData = new Bundle();
     }
 
@@ -48,4 +57,34 @@
         mTile.metaData = null;
         assertThat(mTile.isPrimaryProfileOnly()).isFalse();
     }
+
+    @Test
+    public void getIcon_noActivityOrMetadata_returnNull() {
+        final Tile tile1 = new Tile((ActivityInfo) null);
+        assertThat(tile1.getIcon()).isNull();
+
+        final Tile tile2 = new Tile(new ActivityInfo());
+        assertThat(tile2.getIcon()).isNull();
+    }
+
+    @Test
+    public void getIcon_providedByUri_returnNull() {
+        mTile.metaData.putString(META_DATA_PREFERENCE_ICON_URI, "content://foobar/icon");
+
+        assertThat(mTile.getIcon()).isNull();
+    }
+
+    @Test
+    public void getIcon_hasIconMetadata_returnIcon() {
+        mTile.metaData.putInt(META_DATA_PREFERENCE_ICON, R.drawable.ic_info);
+
+        assertThat(mTile.getIcon().getResId()).isEqualTo(R.drawable.ic_info);
+    }
+
+    @Test
+    public void getIcon_noIconMetadata_returnActivityIcon() {
+        mTile.metaData.putInt(META_DATA_PREFERENCE_ICON, 0);
+
+        assertThat(mTile.getIcon().getResId()).isEqualTo(mActivityInfo.icon);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index a1c48f0..e6c6335 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -314,7 +314,7 @@
                 false /* checkCategory */, true /* forceTintExternalIcon */);
 
         assertThat(outTiles.size()).isEqualTo(1);
-        assertThat(outTiles.get(0).icon.getResId()).isEqualTo(314159);
+        assertThat(outTiles.get(0).getIcon().getResId()).isEqualTo(314159);
         assertThat(outTiles.get(0).summary).isEqualTo("static-summary");
 
         // Case 2: Empty bundle.
@@ -332,7 +332,7 @@
                 false /* checkCategory */, true /* forceTintExternalIcon */);
 
         assertThat(outTiles.size()).isEqualTo(1);
-        assertThat(outTiles.get(0).icon.getResId()).isEqualTo(314159);
+        assertThat(outTiles.get(0).getIcon().getResId()).isEqualTo(314159);
         assertThat(outTiles.get(0).summary).isEqualTo("static-summary");
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
index ddadac1..fa64afe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
@@ -25,15 +25,16 @@
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
 
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class InputMethodAndSubtypeUtilCompatTest {
 
     private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
@@ -236,7 +237,7 @@
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
                 createDummyIme(false, false, createDummySubtype("keyboard", false, true))))
                 .isFalse();
-   }
+    }
 
     private static InputMethodInfo createDummyIme(boolean isSystem, boolean isAuxIme,
             InputMethodSubtype... subtypes) {
@@ -253,7 +254,7 @@
         si.exported = true;
         si.nonLocalizedLabel = "Dummy IME";
         ri.serviceInfo = si;
-        return new InputMethodInfo(ri, isAuxIme, "",  Arrays.asList(subtypes), 1, false);
+        return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false);
     }
 
     private static InputMethodSubtype createDummySubtype(
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
index d0a0686..03ab261 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
@@ -18,10 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
@@ -29,11 +25,16 @@
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class InputMethodAndSubtypeUtilTest {
 
     private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
index 026ad47..645dfa1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
@@ -30,13 +30,14 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class IconCacheTest {
     private Icon mIcon;
     private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
index 83a9d5b..1e066b1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
@@ -17,14 +17,16 @@
 
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.fail;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
 import org.robolectric.shadows.ShadowLooper;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class ThreadUtilsTest {
 
     @Test
@@ -56,7 +58,7 @@
     }
 
     @Test
-    public void testPostOnMainThread_shouldRunOnMainTread() throws Exception {
+    public void testPostOnMainThread_shouldRunOnMainTread() {
         TestRunnable cr = new TestRunnable();
         ShadowLooper.pauseMainLooper();
         ThreadUtils.postOnMainThread(cr);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
index 36abd20..a00f12d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
@@ -22,13 +22,14 @@
 import android.graphics.drawable.AnimatedRotateDrawable;
 import android.view.View;
 
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
 
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
 public class AnimatedImageViewTest {
     private AnimatedImageView mAnimatedImageView;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 22833b8..f3e100d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -22,7 +22,9 @@
 import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
 
 import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
 import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
 import static com.android.systemui.OverviewProxyService.OverviewProxyListener;
@@ -660,6 +662,10 @@
             nbModeChanged = nbMode != -1;
             if (nbModeChanged) {
                 if (mNavigationBarMode != nbMode) {
+                    if (mNavigationBarMode == MODE_TRANSPARENT
+                            || mNavigationBarMode == MODE_LIGHTS_OUT_TRANSPARENT) {
+                        mNavigationBarView.hideRecentsOnboarding();
+                    }
                     mNavigationBarMode = nbMode;
                     checkNavBarModes();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 6077e79..b7bc577 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -886,6 +886,10 @@
 
     public boolean isRotateButtonVisible() { return mShowRotateButton; }
 
+    void hideRecentsOnboarding() {
+        mRecentsOnboarding.hide(true);
+    }
+
     /**
      * @return the button at the given {@param x} and {@param y}.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 2516a9b..6cc88bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -510,6 +510,9 @@
     private void resetQuickScrub() {
         mQuickScrubActive = false;
         mAllowGestureDetection = false;
+        if (mCurrentNavigationBarView != null) {
+            mCurrentNavigationBarView.setAlpha(1f);
+        }
         mCurrentNavigationBarView = null;
         updateHighlight();
     }
diff --git a/services/core/java/com/android/server/InputContentUriTokenHandler.java b/services/core/java/com/android/server/InputContentUriTokenHandler.java
index 57cdc94..6338b2f 100644
--- a/services/core/java/com/android/server/InputContentUriTokenHandler.java
+++ b/services/core/java/com/android/server/InputContentUriTokenHandler.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.UriGrantsManager;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Binder;
@@ -27,6 +28,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 final class InputContentUriTokenHandler extends IInputContentUriToken.Stub {
 
@@ -63,12 +65,8 @@
                 return;
             }
 
-            try {
-                mPermissionOwnerToken = ActivityManager.getService()
-                        .newUriPermissionOwner("InputContentUriTokenHandler");
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
+            mPermissionOwnerToken = LocalServices.getService(UriGrantsManagerInternal.class)
+                    .newUriPermissionOwner("InputContentUriTokenHandler");
 
             doTakeLocked(mPermissionOwnerToken);
         }
@@ -78,7 +76,7 @@
         long origId = Binder.clearCallingIdentity();
         try {
             try {
-                ActivityManager.getService().grantUriPermissionFromOwner(
+                UriGrantsManager.getService().grantUriPermissionFromOwner(
                         permissionOwner, mSourceUid, mTargetPackage, mUri,
                         Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId, mTargetUserId);
             } catch (RemoteException e) {
@@ -96,11 +94,9 @@
                 return;
             }
             try {
-                ActivityManager.getService().revokeUriPermissionFromOwner(
-                        mPermissionOwnerToken, mUri,
-                        Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
+                LocalServices.getService(UriGrantsManagerInternal.class)
+                        .revokeUriPermissionFromOwner(mPermissionOwnerToken, mUri,
+                                Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId);
             } finally {
                 mPermissionOwnerToken = null;
             }
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 8402087..f15cd2a 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -42,6 +42,7 @@
 import android.util.Slog;
 
 import com.android.internal.telephony.IMms;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.util.List;
 
@@ -512,7 +513,7 @@
 
             long token = Binder.clearCallingIdentity();
             try {
-                LocalServices.getService(ActivityManagerInternal.class)
+                LocalServices.getService(UriGrantsManagerInternal.class)
                         .grantUriPermissionFromIntent(callingUid, PHONE_PACKAGE_NAME,
                                 grantIntent, UserHandle.USER_SYSTEM);
 
@@ -523,7 +524,7 @@
                 List<String> carrierPackages = telephonyManager.getCarrierPackageNamesForIntent(
                         intent);
                 if (carrierPackages != null && carrierPackages.size() == 1) {
-                    LocalServices.getService(ActivityManagerInternal.class)
+                    LocalServices.getService(UriGrantsManagerInternal.class)
                             .grantUriPermissionFromIntent(callingUid, carrierPackages.get(0),
                                     grantIntent, UserHandle.USER_SYSTEM);
                 }
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 63584d9..c5b4966 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -18,10 +18,12 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.os.Environment;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.util.Slog;
 
+import java.io.File;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
@@ -36,6 +38,7 @@
     private static final String TAG = "SystemServiceManager";
     private static final int SERVICE_CALL_WARN_TIME_MS = 50;
 
+    private static File sSystemDir;
     private final Context mContext;
     private boolean mSafeMode;
     private boolean mRuntimeRestarted;
@@ -318,6 +321,22 @@
     }
 
     /**
+     * Ensures that the system directory exist creating one if needed.
+     * @deprecated Use {@link Environment#getDataSystemCeDirectory()}
+     * or {@link Environment#getDataSystemDeDirectory()} instead.
+     * @return The system directory.
+     */
+    @Deprecated
+    public static File ensureSystemDir() {
+        if (sSystemDir == null) {
+            File dataDir = Environment.getDataDirectory();
+            sSystemDir = new File(dataDir, "system");
+            sSystemDir.mkdirs();
+        }
+        return sSystemDir;
+    }
+
+    /**
      * Outputs the state of this manager to the System log.
      */
     public void dump() {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 286e1f0..29937ab 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -65,7 +65,6 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
-import com.android.server.am.ActivityManagerService.NeededUriGrants;
 
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -96,6 +95,7 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.webkit.WebViewZygote;
+import com.android.server.uri.NeededUriGrants;
 
 public final class ActiveServices {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActiveServices" : TAG_AM;
@@ -512,7 +512,7 @@
             fgRequired = false;
         }
 
-        NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
+        NeededUriGrants neededGrants = mAm.mUgmInternal.checkGrantUriPermissionFromIntent(
                 callingUid, r.packageName, service, service.getFlags(), null, r.userId);
 
         // If permissions need a review before any of the app components can run,
@@ -2578,7 +2578,7 @@
             r.deliveredStarts.add(si);
             si.deliveryCount++;
             if (si.neededGrants != null) {
-                mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
+                mAm.mUgmInternal.grantUriPermissionUncheckedFromIntent(si.neededGrants,
                         si.getUriPermissionsLocked());
             }
             mAm.grantEphemeralAccessLocked(r.userId, si.intent,
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 552ed16..acdc738 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -421,11 +421,16 @@
         final int windowingMode = stack.getWindowingMode();
 
         if (activityType == ACTIVITY_TYPE_HOME) {
+            // TODO(b/111363427) Rollback to throws exceptions once we figure out how to properly
+            // deal with home type stack when external display removed
             if (mHomeStack != null && mHomeStack != stack) {
-                throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
+                // throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
+                //         + mHomeStack + " already exist on display=" + this + " stack=" + stack);
+                Slog.e(TAG, "addStackReferenceIfNeeded: home stack="
                         + mHomeStack + " already exist on display=" + this + " stack=" + stack);
+            } else {
+                mHomeStack = stack;
             }
-            mHomeStack = stack;
         } else if (activityType == ACTIVITY_TYPE_RECENTS) {
             if (mRecentsStack != null && mRecentsStack != stack) {
                 throw new IllegalArgumentException("addStackReferenceIfNeeded: recents stack="
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 58eb5c6..cb4b18d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -91,13 +91,9 @@
 import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
-import static android.view.Display.DEFAULT_DISPLAY;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
-import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
@@ -122,7 +118,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_URI_PERMISSION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
@@ -148,8 +143,6 @@
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.am.MemoryStatUtil.hasMemcg;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
-import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.Manifest;
 import android.Manifest.permission;
@@ -172,7 +165,6 @@
 import android.app.BroadcastOptions;
 import android.app.ContentProviderHolder;
 import android.app.Dialog;
-import android.app.GrantedUriPermission;
 import android.app.IActivityController;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
@@ -199,7 +191,6 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
 import android.content.BroadcastReceiver;
-import android.content.ClipData;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -248,7 +239,6 @@
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.DropBoxManager;
-import android.os.Environment;
 import android.os.FactoryTest;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -281,14 +271,12 @@
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
-import android.provider.Downloads;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.style.SuggestionSpan;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.AtomicFile;
 import android.util.DebugUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -301,7 +289,6 @@
 import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.TimingsTraceLog;
-import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
 import android.view.Gravity;
@@ -311,8 +298,8 @@
 import android.view.WindowManager;
 import android.view.autofill.AutofillManagerInternal;
 
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
+import com.android.server.uri.GrantUri;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -337,7 +324,6 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.MemInfoReader;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.function.QuadFunction;
@@ -371,14 +357,9 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerService;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.FileReader;
@@ -388,7 +369,6 @@
 import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
 import java.lang.ref.WeakReference;
-import java.nio.charset.StandardCharsets;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -411,7 +391,6 @@
 import java.util.function.BiFunction;
 
 import dalvik.system.VMRuntime;
-import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
 public class ActivityManagerService extends IActivityManager.Stub
@@ -494,9 +473,6 @@
     // Must be kept in sync with Am.
     private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
 
-    // Maximum number of persisted Uri grants a package is allowed
-    static final int MAX_PERSISTED_URI_GRANTS = 128;
-
     static final int MY_PID = myPid();
 
     static final String[] EMPTY_STRING_ARRAY = new String[0];
@@ -955,95 +931,7 @@
      * application is currently being launched and the provider will be
      * removed from this list once it is published.
      */
-    final ArrayList<ContentProviderRecord> mLaunchingProviders
-            = new ArrayList<ContentProviderRecord>();
-
-    /**
-     * File storing persisted {@link #mGrantedUriPermissions}.
-     */
-    private final AtomicFile mGrantFile;
-
-    /** XML constants used in {@link #mGrantFile} */
-    private static final String TAG_URI_GRANTS = "uri-grants";
-    private static final String TAG_URI_GRANT = "uri-grant";
-    private static final String ATTR_USER_HANDLE = "userHandle";
-    private static final String ATTR_SOURCE_USER_ID = "sourceUserId";
-    private static final String ATTR_TARGET_USER_ID = "targetUserId";
-    private static final String ATTR_SOURCE_PKG = "sourcePkg";
-    private static final String ATTR_TARGET_PKG = "targetPkg";
-    private static final String ATTR_URI = "uri";
-    private static final String ATTR_MODE_FLAGS = "modeFlags";
-    private static final String ATTR_CREATED_TIME = "createdTime";
-    private static final String ATTR_PREFIX = "prefix";
-
-    /**
-     * Global set of specific {@link Uri} permissions that have been granted.
-     * This optimized lookup structure maps from {@link UriPermission#targetUid}
-     * to {@link UriPermission#uri} to {@link UriPermission}.
-     */
-    @GuardedBy("this")
-    private final SparseArray<ArrayMap<GrantUri, UriPermission>>
-            mGrantedUriPermissions = new SparseArray<ArrayMap<GrantUri, UriPermission>>();
-
-    public static class GrantUri {
-        public final int sourceUserId;
-        public final Uri uri;
-        public boolean prefix;
-
-        public GrantUri(int sourceUserId, Uri uri, boolean prefix) {
-            this.sourceUserId = sourceUserId;
-            this.uri = uri;
-            this.prefix = prefix;
-        }
-
-        @Override
-        public int hashCode() {
-            int hashCode = 1;
-            hashCode = 31 * hashCode + sourceUserId;
-            hashCode = 31 * hashCode + uri.hashCode();
-            hashCode = 31 * hashCode + (prefix ? 1231 : 1237);
-            return hashCode;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o instanceof GrantUri) {
-                GrantUri other = (GrantUri) o;
-                return uri.equals(other.uri) && (sourceUserId == other.sourceUserId)
-                        && prefix == other.prefix;
-            }
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            String result = uri.toString() + " [user " + sourceUserId + "]";
-            if (prefix) result += " [prefix]";
-            return result;
-        }
-
-        public String toSafeString() {
-            String result = uri.toSafeString() + " [user " + sourceUserId + "]";
-            if (prefix) result += " [prefix]";
-            return result;
-        }
-
-        public void writeToProto(ProtoOutputStream proto, long fieldId) {
-            long token = proto.start(fieldId);
-            proto.write(GrantUriProto.URI, uri.toString());
-            proto.write(GrantUriProto.SOURCE_USER_ID, sourceUserId);
-            proto.end(token);
-        }
-
-        public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
-            if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
-                return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
-                        ContentProvider.getUriWithoutUserId(uri), false);
-            } else {
-                return new GrantUri(defaultSourceUserHandle, uri, false);
-            }
-        }
-    }
+    final ArrayList<ContentProviderRecord> mLaunchingProviders = new ArrayList<>();
 
     boolean mSystemProvidersInstalled;
 
@@ -1491,6 +1379,7 @@
     WindowManagerService mWindowManager;
     ActivityTaskManagerService mActivityTaskManager;
     ActivityTaskManagerInternal mAtmInternal;
+    UriGrantsManagerInternal mUgmInternal;
     final ActivityThread mSystemThread;
 
     private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -1540,7 +1429,6 @@
     static final int DISPATCH_PROCESSES_CHANGED_UI_MSG = 31;
     static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
     static final int REPORT_MEM_USAGE_MSG = 33;
-    static final int PERSIST_URI_GRANTS_MSG = 38;
     static final int UPDATE_TIME_PREFERENCE_MSG = 41;
     static final int FINISH_BOOTING_MSG = 45;
     static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
@@ -1952,10 +1840,6 @@
                 thread.start();
                 break;
             }
-            case PERSIST_URI_GRANTS_MSG: {
-                writeGrantedUriPermissions();
-                break;
-            }
             case UPDATE_TIME_PREFERENCE_MSG: {
                 // The user's time format preference might have changed.
                 // For convenience we re-use the Intent extra values.
@@ -2554,7 +2438,6 @@
         mBatteryStatsService = null;
         mCompatModePackages = null;
         mConstants = null;
-        mGrantFile = null;
         mHandler = null;
         mHandlerThread = null;
         mIntentFirewall = null;
@@ -2615,9 +2498,7 @@
         mProviderMap = new ProviderMap(this);
         mAppErrors = new AppErrors(mUiContext, this);
 
-        File dataDir = Environment.getDataDirectory();
-        File systemDir = new File(dataDir, "system");
-        systemDir.mkdirs();
+        final File systemDir = SystemServiceManager.ensureSystemDir();
 
         mAppWarnings = new AppWarnings(this, mUiContext, mHandler, mUiHandler, systemDir);
 
@@ -2634,7 +2515,7 @@
 
         mAppOpsService = mInjector.getAppOpsService(new File(systemDir, "appops.xml"), mHandler);
 
-        mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants");
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
 
         mUserController = new UserController(this);
 
@@ -2723,6 +2604,7 @@
         Slog.d("AppOps", "AppOpsService published");
         LocalServices.addService(ActivityManagerInternal.class, new LocalService());
         mActivityTaskManager.onActivityManagerInternalAdded();
+        mUgmInternal.onActivityManagerInternalAdded();
         // Wait for the synchronized block started in mProcessCpuThread,
         // so that any other access to mProcessCpuTracker from main thread
         // will be blocked during mProcessCpuTracker initialization.
@@ -5026,11 +4908,9 @@
                     // so it told us to keep those intact -- it's about to emplace app data
                     // that is appropriate for those bits of system state.
                     if (!keepState) {
-                        synchronized (this) {
-                            // Remove all permissions granted from/to this package
-                            removeUriPermissionsForPackageLocked(packageName, resolvedUserId, true,
-                                    false);
-                        }
+                        // Remove all permissions granted from/to this package
+                        mUgmInternal.removeUriPermissionsForPackage(packageName, resolvedUserId,
+                                true, false);
 
                         // Reset notification state
                         INotificationManager inm = NotificationManager.getService();
@@ -5724,7 +5604,7 @@
         }
 
         // Remove transient permissions granted from/to this package/user
-        removeUriPermissionsForPackageLocked(packageName, userId, false, false);
+        mUgmInternal.removeUriPermissionsForPackage(packageName, userId, false, false);
 
         if (doit) {
             for (i = mBroadcastQueues.length - 1; i >= 0; i--) {
@@ -7215,105 +7095,6 @@
         throw new SecurityException(msg);
     }
 
-    /**
-     * Determine if UID is holding permissions required to access {@link Uri} in
-     * the given {@link ProviderInfo}. Final permission checking is always done
-     * in {@link ContentProvider}.
-     */
-    private final boolean checkHoldingPermissionsLocked(
-            IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                "checkHoldingPermissionsLocked: uri=" + grantUri + " uid=" + uid);
-        if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
-            if (ActivityManager.checkComponentPermission(INTERACT_ACROSS_USERS, uid, -1, true)
-                    != PERMISSION_GRANTED) {
-                return false;
-            }
-        }
-        return checkHoldingPermissionsInternalLocked(pm, pi, grantUri, uid, modeFlags, true);
-    }
-
-    private final boolean checkHoldingPermissionsInternalLocked(IPackageManager pm, ProviderInfo pi,
-            GrantUri grantUri, int uid, final int modeFlags, boolean considerUidPermissions) {
-        if (pi.applicationInfo.uid == uid) {
-            return true;
-        } else if (!pi.exported) {
-            return false;
-        }
-
-        boolean readMet = (modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
-        boolean writeMet = (modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
-        try {
-            // check if target holds top-level <provider> permissions
-            if (!readMet && pi.readPermission != null && considerUidPermissions
-                    && (pm.checkUidPermission(pi.readPermission, uid) == PERMISSION_GRANTED)) {
-                readMet = true;
-            }
-            if (!writeMet && pi.writePermission != null && considerUidPermissions
-                    && (pm.checkUidPermission(pi.writePermission, uid) == PERMISSION_GRANTED)) {
-                writeMet = true;
-            }
-
-            // track if unprotected read/write is allowed; any denied
-            // <path-permission> below removes this ability
-            boolean allowDefaultRead = pi.readPermission == null;
-            boolean allowDefaultWrite = pi.writePermission == null;
-
-            // check if target holds any <path-permission> that match uri
-            final PathPermission[] pps = pi.pathPermissions;
-            if (pps != null) {
-                final String path = grantUri.uri.getPath();
-                int i = pps.length;
-                while (i > 0 && (!readMet || !writeMet)) {
-                    i--;
-                    PathPermission pp = pps[i];
-                    if (pp.match(path)) {
-                        if (!readMet) {
-                            final String pprperm = pp.getReadPermission();
-                            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                                    "Checking read perm for " + pprperm + " for " + pp.getPath()
-                                    + ": match=" + pp.match(path)
-                                    + " check=" + pm.checkUidPermission(pprperm, uid));
-                            if (pprperm != null) {
-                                if (considerUidPermissions && pm.checkUidPermission(pprperm, uid)
-                                        == PERMISSION_GRANTED) {
-                                    readMet = true;
-                                } else {
-                                    allowDefaultRead = false;
-                                }
-                            }
-                        }
-                        if (!writeMet) {
-                            final String ppwperm = pp.getWritePermission();
-                            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                                    "Checking write perm " + ppwperm + " for " + pp.getPath()
-                                    + ": match=" + pp.match(path)
-                                    + " check=" + pm.checkUidPermission(ppwperm, uid));
-                            if (ppwperm != null) {
-                                if (considerUidPermissions && pm.checkUidPermission(ppwperm, uid)
-                                        == PERMISSION_GRANTED) {
-                                    writeMet = true;
-                                } else {
-                                    allowDefaultWrite = false;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-
-            // grant unprotected <provider> read/write, if not blocked by
-            // <path-permission> above
-            if (allowDefaultRead) readMet = true;
-            if (allowDefaultWrite) writeMet = true;
-
-        } catch (RemoteException e) {
-            return false;
-        }
-
-        return readMet && writeMet;
-    }
-
     public boolean isAppStartModeDisabled(int uid, String packageName) {
         synchronized (this) {
             return getAppStartModeLocked(uid, packageName, 0, -1, false, true, false)
@@ -7487,67 +7268,6 @@
                 grantEphemeralAccess(userId, intent, targetAppId, ephemeralAppId);
     }
 
-    @GuardedBy("this")
-    private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
-        final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
-        if (targetUris != null) {
-            return targetUris.get(grantUri);
-        }
-        return null;
-    }
-
-    @GuardedBy("this")
-    private UriPermission findOrCreateUriPermissionLocked(String sourcePkg,
-            String targetPkg, int targetUid, GrantUri grantUri) {
-        ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
-        if (targetUris == null) {
-            targetUris = Maps.newArrayMap();
-            mGrantedUriPermissions.put(targetUid, targetUris);
-        }
-
-        UriPermission perm = targetUris.get(grantUri);
-        if (perm == null) {
-            perm = new UriPermission(sourcePkg, targetPkg, targetUid, grantUri);
-            targetUris.put(grantUri, perm);
-        }
-
-        return perm;
-    }
-
-    @GuardedBy("this")
-    private final boolean checkUriPermissionLocked(GrantUri grantUri, int uid,
-            final int modeFlags) {
-        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
-        final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE
-                : UriPermission.STRENGTH_OWNED;
-
-        // Root gets to do everything.
-        if (uid == 0) {
-            return true;
-        }
-
-        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid);
-        if (perms == null) return false;
-
-        // First look for exact match
-        final UriPermission exactPerm = perms.get(grantUri);
-        if (exactPerm != null && exactPerm.getStrength(modeFlags) >= minStrength) {
-            return true;
-        }
-
-        // No exact match, look for prefixes
-        final int N = perms.size();
-        for (int i = 0; i < N; i++) {
-            final UriPermission perm = perms.valueAt(i);
-            if (perm.uri.prefix && grantUri.uri.isPathPrefixMatch(perm.uri.uri)
-                    && perm.getStrength(modeFlags) >= minStrength) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
     /**
      * @param uri This uri must NOT contain an embedded userId.
      * @param userId The userId in which the uri is to be resolved.
@@ -7569,395 +7289,8 @@
         if (pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
         }
-        synchronized (this) {
-            return checkUriPermissionLocked(new GrantUri(userId, uri, false), uid, modeFlags)
-                    ? PackageManager.PERMISSION_GRANTED
-                    : PackageManager.PERMISSION_DENIED;
-        }
-    }
-
-    /**
-     * Check if the targetPkg can be granted permission to access uri by
-     * the callingUid using the given modeFlags.  Throws a security exception
-     * if callingUid is not allowed to do this.  Returns the uid of the target
-     * if the URI permission grant should be performed; returns -1 if it is not
-     * needed (for example targetPkg already has permission to access the URI).
-     * If you already know the uid of the target, you can supply it in
-     * lastTargetUid else set that to -1.
-     */
-    @GuardedBy("this")
-    int checkGrantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
-            final int modeFlags, int lastTargetUid) {
-        if (!Intent.isAccessUriMode(modeFlags)) {
-            return -1;
-        }
-
-        if (targetPkg != null) {
-            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                    "Checking grant " + targetPkg + " permission to " + grantUri);
-        }
-
-        final IPackageManager pm = AppGlobals.getPackageManager();
-
-        // If this is not a content: uri, we can't do anything with it.
-        if (!ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) {
-            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                    "Can't grant URI permission for non-content URI: " + grantUri);
-            return -1;
-        }
-
-        // Bail early if system is trying to hand out permissions directly; it
-        // must always grant permissions on behalf of someone explicit.
-        final int callingAppId = UserHandle.getAppId(callingUid);
-        if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) {
-            if ("com.android.settings.files".equals(grantUri.uri.getAuthority())) {
-                // Exempted authority for
-                // 1. cropping user photos and sharing a generated license html
-                //    file in Settings app
-                // 2. sharing a generated license html file in TvSettings app
-            } else {
-                Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
-                        + " grant to " + grantUri + "; use startActivityAsCaller() instead");
-                return -1;
-            }
-        }
-
-        final String authority = grantUri.uri.getAuthority();
-        final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId,
-                MATCH_DEBUG_TRIAGED_MISSING);
-        if (pi == null) {
-            Slog.w(TAG, "No content provider found for permission check: " +
-                    grantUri.uri.toSafeString());
-            return -1;
-        }
-
-        int targetUid = lastTargetUid;
-        if (targetUid < 0 && targetPkg != null) {
-            try {
-                targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
-                        UserHandle.getUserId(callingUid));
-                if (targetUid < 0) {
-                    if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                            "Can't grant URI permission no uid for: " + targetPkg);
-                    return -1;
-                }
-            } catch (RemoteException ex) {
-                return -1;
-            }
-        }
-
-        // If we're extending a persistable grant, then we always need to create
-        // the grant data structure so that take/release APIs work
-        if ((modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0) {
-            return targetUid;
-        }
-
-        if (targetUid >= 0) {
-            // First...  does the target actually need this permission?
-            if (checkHoldingPermissionsLocked(pm, pi, grantUri, targetUid, modeFlags)) {
-                // No need to grant the target this permission.
-                if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                        "Target " + targetPkg + " already has full permission to " + grantUri);
-                return -1;
-            }
-        } else {
-            // First...  there is no target package, so can anyone access it?
-            boolean allowed = pi.exported;
-            if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-                if (pi.readPermission != null) {
-                    allowed = false;
-                }
-            }
-            if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-                if (pi.writePermission != null) {
-                    allowed = false;
-                }
-            }
-            if (pi.pathPermissions != null) {
-                final int N = pi.pathPermissions.length;
-                for (int i=0; i<N; i++) {
-                    if (pi.pathPermissions[i] != null
-                            && pi.pathPermissions[i].match(grantUri.uri.getPath())) {
-                        if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
-                            if (pi.pathPermissions[i].getReadPermission() != null) {
-                                allowed = false;
-                            }
-                        }
-                        if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
-                            if (pi.pathPermissions[i].getWritePermission() != null) {
-                                allowed = false;
-                            }
-                        }
-                        break;
-                    }
-                }
-            }
-            if (allowed) {
-                return -1;
-            }
-        }
-
-        /* There is a special cross user grant if:
-         * - The target is on another user.
-         * - Apps on the current user can access the uri without any uid permissions.
-         * In this case, we grant a uri permission, even if the ContentProvider does not normally
-         * grant uri permissions.
-         */
-        boolean specialCrossUserGrant = UserHandle.getUserId(targetUid) != grantUri.sourceUserId
-                && checkHoldingPermissionsInternalLocked(pm, pi, grantUri, callingUid,
-                modeFlags, false /*without considering the uid permissions*/);
-
-        // Second...  is the provider allowing granting of URI permissions?
-        if (!specialCrossUserGrant) {
-            if (!pi.grantUriPermissions) {
-                throw new SecurityException("Provider " + pi.packageName
-                        + "/" + pi.name
-                        + " does not allow granting of Uri permissions (uri "
-                        + grantUri + ")");
-            }
-            if (pi.uriPermissionPatterns != null) {
-                final int N = pi.uriPermissionPatterns.length;
-                boolean allowed = false;
-                for (int i=0; i<N; i++) {
-                    if (pi.uriPermissionPatterns[i] != null
-                            && pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) {
-                        allowed = true;
-                        break;
-                    }
-                }
-                if (!allowed) {
-                    throw new SecurityException("Provider " + pi.packageName
-                            + "/" + pi.name
-                            + " does not allow granting of permission to path of Uri "
-                            + grantUri);
-                }
-            }
-        }
-
-        // Third...  does the caller itself have permission to access
-        // this uri?
-        if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
-            // Require they hold a strong enough Uri permission
-            if (!checkUriPermissionLocked(grantUri, callingUid, modeFlags)) {
-                if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(pi.readPermission)) {
-                    throw new SecurityException(
-                            "UID " + callingUid + " does not have permission to " + grantUri
-                                    + "; you could obtain access using ACTION_OPEN_DOCUMENT "
-                                    + "or related APIs");
-                } else {
-                    throw new SecurityException(
-                            "UID " + callingUid + " does not have permission to " + grantUri);
-                }
-            }
-        }
-        return targetUid;
-    }
-
-    /**
-     * @param uri This uri must NOT contain an embedded userId.
-     * @param userId The userId in which the uri is to be resolved.
-     */
-    @Override
-    public int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri,
-            final int modeFlags, int userId) {
-        enforceNotIsolatedCaller("checkGrantUriPermission");
-        synchronized(this) {
-            return checkGrantUriPermissionLocked(callingUid, targetPkg,
-                    new GrantUri(userId, uri, false), modeFlags, -1);
-        }
-    }
-
-    @GuardedBy("this")
-    void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg, GrantUri grantUri,
-            final int modeFlags, UriPermissionOwner owner) {
-        if (!Intent.isAccessUriMode(modeFlags)) {
-            return;
-        }
-
-        // So here we are: the caller has the assumed permission
-        // to the uri, and the target doesn't.  Let's now give this to
-        // the target.
-
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                "Granting " + targetPkg + "/" + targetUid + " permission to " + grantUri);
-
-        final String authority = grantUri.uri.getAuthority();
-        final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId,
-                MATCH_DEBUG_TRIAGED_MISSING);
-        if (pi == null) {
-            Slog.w(TAG, "No content provider found for grant: " + grantUri.toSafeString());
-            return;
-        }
-
-        if ((modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0) {
-            grantUri.prefix = true;
-        }
-        final UriPermission perm = findOrCreateUriPermissionLocked(
-                pi.packageName, targetPkg, targetUid, grantUri);
-        perm.grantModes(modeFlags, owner);
-    }
-
-    @GuardedBy("this")
-    void grantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
-            final int modeFlags, UriPermissionOwner owner, int targetUserId) {
-        if (targetPkg == null) {
-            throw new NullPointerException("targetPkg");
-        }
-        int targetUid;
-        final IPackageManager pm = AppGlobals.getPackageManager();
-        try {
-            targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, targetUserId);
-        } catch (RemoteException ex) {
-            return;
-        }
-
-        targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, modeFlags,
-                targetUid);
-        if (targetUid < 0) {
-            return;
-        }
-
-        grantUriPermissionUncheckedLocked(targetUid, targetPkg, grantUri, modeFlags,
-                owner);
-    }
-
-    static class NeededUriGrants extends ArrayList<GrantUri> {
-        final String targetPkg;
-        final int targetUid;
-        final int flags;
-
-        NeededUriGrants(String targetPkg, int targetUid, int flags) {
-            this.targetPkg = targetPkg;
-            this.targetUid = targetUid;
-            this.flags = flags;
-        }
-
-        void writeToProto(ProtoOutputStream proto, long fieldId) {
-            long token = proto.start(fieldId);
-            proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg);
-            proto.write(NeededUriGrantsProto.TARGET_UID, targetUid);
-            proto.write(NeededUriGrantsProto.FLAGS, flags);
-
-            final int N = this.size();
-            for (int i=0; i<N; i++) {
-                this.get(i).writeToProto(proto, NeededUriGrantsProto.GRANTS);
-            }
-            proto.end(token);
-        }
-    }
-
-    /**
-     * Like checkGrantUriPermissionLocked, but takes an Intent.
-     */
-    @GuardedBy("this")
-    NeededUriGrants checkGrantUriPermissionFromIntentLocked(int callingUid,
-            String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                "Checking URI perm to data=" + (intent != null ? intent.getData() : null)
-                + " clip=" + (intent != null ? intent.getClipData() : null)
-                + " from " + intent + "; flags=0x"
-                + Integer.toHexString(intent != null ? intent.getFlags() : 0));
-
-        if (targetPkg == null) {
-            throw new NullPointerException("targetPkg");
-        }
-
-        if (intent == null) {
-            return null;
-        }
-        Uri data = intent.getData();
-        ClipData clip = intent.getClipData();
-        if (data == null && clip == null) {
-            return null;
-        }
-        // Default userId for uris in the intent (if they don't specify it themselves)
-        int contentUserHint = intent.getContentUserHint();
-        if (contentUserHint == UserHandle.USER_CURRENT) {
-            contentUserHint = UserHandle.getUserId(callingUid);
-        }
-        final IPackageManager pm = AppGlobals.getPackageManager();
-        int targetUid;
-        if (needed != null) {
-            targetUid = needed.targetUid;
-        } else {
-            try {
-                targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
-                        targetUserId);
-            } catch (RemoteException ex) {
-                return null;
-            }
-            if (targetUid < 0) {
-                if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                        "Can't grant URI permission no uid for: " + targetPkg
-                        + " on user " + targetUserId);
-                return null;
-            }
-        }
-        if (data != null) {
-            GrantUri grantUri = GrantUri.resolve(contentUserHint, data);
-            targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
-                    targetUid);
-            if (targetUid > 0) {
-                if (needed == null) {
-                    needed = new NeededUriGrants(targetPkg, targetUid, mode);
-                }
-                needed.add(grantUri);
-            }
-        }
-        if (clip != null) {
-            for (int i=0; i<clip.getItemCount(); i++) {
-                Uri uri = clip.getItemAt(i).getUri();
-                if (uri != null) {
-                    GrantUri grantUri = GrantUri.resolve(contentUserHint, uri);
-                    targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode,
-                            targetUid);
-                    if (targetUid > 0) {
-                        if (needed == null) {
-                            needed = new NeededUriGrants(targetPkg, targetUid, mode);
-                        }
-                        needed.add(grantUri);
-                    }
-                } else {
-                    Intent clipIntent = clip.getItemAt(i).getIntent();
-                    if (clipIntent != null) {
-                        NeededUriGrants newNeeded = checkGrantUriPermissionFromIntentLocked(
-                                callingUid, targetPkg, clipIntent, mode, needed, targetUserId);
-                        if (newNeeded != null) {
-                            needed = newNeeded;
-                        }
-                    }
-                }
-            }
-        }
-
-        return needed;
-    }
-
-    /**
-     * Like grantUriPermissionUncheckedLocked, but takes an Intent.
-     */
-    @GuardedBy("this")
-    void grantUriPermissionUncheckedFromIntentLocked(NeededUriGrants needed,
-            UriPermissionOwner owner) {
-        if (needed != null) {
-            for (int i=0; i<needed.size(); i++) {
-                GrantUri grantUri = needed.get(i);
-                grantUriPermissionUncheckedLocked(needed.targetUid, needed.targetPkg,
-                        grantUri, needed.flags, owner);
-            }
-        }
-    }
-
-    @GuardedBy("this")
-    void grantUriPermissionFromIntentLocked(int callingUid,
-            String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId) {
-        NeededUriGrants needed = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg,
-                intent, intent != null ? intent.getFlags() : 0, null, targetUserId);
-        if (needed == null) {
-            return;
-        }
-
-        grantUriPermissionUncheckedFromIntentLocked(needed, owner);
+        return mUgmInternal.checkUriPermission(new GrantUri(userId, uri, false), uid, modeFlags)
+                ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
     }
 
     /**
@@ -7988,113 +7321,11 @@
                     | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                     | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
 
-            grantUriPermissionLocked(r.uid, targetPkg, grantUri, modeFlags, null,
+            mUgmInternal.grantUriPermission(r.uid, targetPkg, grantUri, modeFlags, null,
                     UserHandle.getUserId(r.uid));
         }
     }
 
-    @GuardedBy("this")
-    void removeUriPermissionIfNeededLocked(UriPermission perm) {
-        if (perm.modeFlags == 0) {
-            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(
-                    perm.targetUid);
-            if (perms != null) {
-                if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                        "Removing " + perm.targetUid + " permission to " + perm.uri);
-
-                perms.remove(perm.uri);
-                if (perms.isEmpty()) {
-                    mGrantedUriPermissions.remove(perm.targetUid);
-                }
-            }
-        }
-    }
-
-    @GuardedBy("this")
-    private void revokeUriPermissionLocked(String targetPackage, int callingUid, GrantUri grantUri,
-            final int modeFlags) {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                "Revoking all granted permissions to " + grantUri);
-
-        final IPackageManager pm = AppGlobals.getPackageManager();
-        final String authority = grantUri.uri.getAuthority();
-        final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId,
-                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
-        if (pi == null) {
-            Slog.w(TAG, "No content provider found for permission revoke: "
-                    + grantUri.toSafeString());
-            return;
-        }
-
-        // Does the caller have this permission on the URI?
-        if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
-            // If they don't have direct access to the URI, then revoke any
-            // ownerless URI permissions that have been granted to them.
-            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
-            if (perms != null) {
-                boolean persistChanged = false;
-                for (int i = perms.size()-1; i >= 0; i--) {
-                    final UriPermission perm = perms.valueAt(i);
-                    if (targetPackage != null && !targetPackage.equals(perm.targetPkg)) {
-                        continue;
-                    }
-                    if (perm.uri.sourceUserId == grantUri.sourceUserId
-                            && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
-                        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                                "Revoking non-owned " + perm.targetUid
-                                + " permission to " + perm.uri);
-                        persistChanged |= perm.revokeModes(
-                                modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
-                        if (perm.modeFlags == 0) {
-                            perms.removeAt(i);
-                        }
-                    }
-                }
-                if (perms.isEmpty()) {
-                    mGrantedUriPermissions.remove(callingUid);
-                }
-                if (persistChanged) {
-                    schedulePersistUriGrants();
-                }
-            }
-            return;
-        }
-
-        boolean persistChanged = false;
-
-        // Go through all of the permissions and remove any that match.
-        for (int i = mGrantedUriPermissions.size()-1; i >= 0; i--) {
-            final int targetUid = mGrantedUriPermissions.keyAt(i);
-            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
-
-            for (int j = perms.size()-1; j >= 0; j--) {
-                final UriPermission perm = perms.valueAt(j);
-                if (targetPackage != null && !targetPackage.equals(perm.targetPkg)) {
-                    continue;
-                }
-                if (perm.uri.sourceUserId == grantUri.sourceUserId
-                        && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
-                    if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                                "Revoking " + perm.targetUid + " permission to " + perm.uri);
-                    persistChanged |= perm.revokeModes(
-                            modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION,
-                            targetPackage == null);
-                    if (perm.modeFlags == 0) {
-                        perms.removeAt(j);
-                    }
-                }
-            }
-
-            if (perms.isEmpty()) {
-                mGrantedUriPermissions.removeAt(i);
-            }
-        }
-
-        if (persistChanged) {
-            schedulePersistUriGrants();
-        }
-    }
-
     /**
      * @param uri This uri must NOT contain an embedded userId.
      * @param userId The userId in which the uri is to be resolved.
@@ -8128,497 +7359,11 @@
                 return;
             }
 
-            revokeUriPermissionLocked(targetPackage, r.uid, new GrantUri(userId, uri, false),
+            mUgmInternal.revokeUriPermission(targetPackage, r.uid, new GrantUri(userId, uri, false),
                     modeFlags);
         }
     }
 
-    /**
-     * Remove any {@link UriPermission} granted <em>from</em> or <em>to</em> the
-     * given package.
-     *
-     * @param packageName Package name to match, or {@code null} to apply to all
-     *            packages.
-     * @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply
-     *            to all users.
-     * @param persistable If persistable grants should be removed.
-     * @param targetOnly When {@code true}, only remove permissions where the app is the target,
-     * not source.
-     */
-    @GuardedBy("this")
-    private void removeUriPermissionsForPackageLocked(
-            String packageName, int userHandle, boolean persistable, boolean targetOnly) {
-        if (userHandle == UserHandle.USER_ALL && packageName == null) {
-            throw new IllegalArgumentException("Must narrow by either package or user");
-        }
-
-        boolean persistChanged = false;
-
-        int N = mGrantedUriPermissions.size();
-        for (int i = 0; i < N; i++) {
-            final int targetUid = mGrantedUriPermissions.keyAt(i);
-            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
-
-            // Only inspect grants matching user
-            if (userHandle == UserHandle.USER_ALL
-                    || userHandle == UserHandle.getUserId(targetUid)) {
-                for (Iterator<UriPermission> it = perms.values().iterator(); it.hasNext();) {
-                    final UriPermission perm = it.next();
-
-                    // Only inspect grants matching package
-                    if (packageName == null || (!targetOnly && perm.sourcePkg.equals(packageName))
-                            || perm.targetPkg.equals(packageName)) {
-                        // Hacky solution as part of fixing a security bug; ignore
-                        // grants associated with DownloadManager so we don't have
-                        // to immediately launch it to regrant the permissions
-                        if (Downloads.Impl.AUTHORITY.equals(perm.uri.uri.getAuthority())
-                                && !persistable) continue;
-
-                        persistChanged |= perm.revokeModes(persistable
-                                ? ~0 : ~Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, true);
-
-                        // Only remove when no modes remain; any persisted grants
-                        // will keep this alive.
-                        if (perm.modeFlags == 0) {
-                            it.remove();
-                        }
-                    }
-                }
-
-                if (perms.isEmpty()) {
-                    mGrantedUriPermissions.remove(targetUid);
-                    N--;
-                    i--;
-                }
-            }
-        }
-
-        if (persistChanged) {
-            schedulePersistUriGrants();
-        }
-    }
-
-    @Override
-    public IBinder newUriPermissionOwner(String name) {
-        enforceNotIsolatedCaller("newUriPermissionOwner");
-        synchronized(this) {
-            UriPermissionOwner owner = new UriPermissionOwner(this, name);
-            return owner.getExternalTokenLocked();
-        }
-    }
-
-    /**
-     * @param uri This uri must NOT contain an embedded userId.
-     * @param sourceUserId The userId in which the uri is to be resolved.
-     * @param targetUserId The userId of the app that receives the grant.
-     */
-    @Override
-    public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri,
-            final int modeFlags, int sourceUserId, int targetUserId) {
-        targetUserId = mUserController.handleIncomingUser(Binder.getCallingPid(),
-                Binder.getCallingUid(), targetUserId, false, ALLOW_FULL_ONLY,
-                "grantUriPermissionFromOwner", null);
-        synchronized(this) {
-            UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
-            if (owner == null) {
-                throw new IllegalArgumentException("Unknown owner: " + token);
-            }
-            if (fromUid != Binder.getCallingUid()) {
-                if (Binder.getCallingUid() != myUid()) {
-                    // Only system code can grant URI permissions on behalf
-                    // of other users.
-                    throw new SecurityException("nice try");
-                }
-            }
-            if (targetPkg == null) {
-                throw new IllegalArgumentException("null target");
-            }
-            if (uri == null) {
-                throw new IllegalArgumentException("null uri");
-            }
-
-            grantUriPermissionLocked(fromUid, targetPkg, new GrantUri(sourceUserId, uri, false),
-                    modeFlags, owner, targetUserId);
-        }
-    }
-
-    /**
-     * @param uri This uri must NOT contain an embedded userId.
-     * @param userId The userId in which the uri is to be resolved.
-     */
-    @Override
-    public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId) {
-        synchronized(this) {
-            UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
-            if (owner == null) {
-                throw new IllegalArgumentException("Unknown owner: " + token);
-            }
-
-            if (uri == null) {
-                owner.removeUriPermissionsLocked(mode);
-            } else {
-                final boolean prefix = (mode & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0;
-                owner.removeUriPermissionLocked(new GrantUri(userId, uri, prefix), mode);
-            }
-        }
-    }
-
-    private void schedulePersistUriGrants() {
-        if (!mHandler.hasMessages(PERSIST_URI_GRANTS_MSG)) {
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG),
-                    10 * DateUtils.SECOND_IN_MILLIS);
-        }
-    }
-
-    private void writeGrantedUriPermissions() {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "writeGrantedUriPermissions()");
-
-        final long startTime = SystemClock.uptimeMillis();
-
-        // Snapshot permissions so we can persist without lock
-        ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList();
-        synchronized (this) {
-            final int size = mGrantedUriPermissions.size();
-            for (int i = 0; i < size; i++) {
-                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
-                for (UriPermission perm : perms.values()) {
-                    if (perm.persistedModeFlags != 0) {
-                        persist.add(perm.snapshot());
-                    }
-                }
-            }
-        }
-
-        FileOutputStream fos = null;
-        try {
-            fos = mGrantFile.startWrite(startTime);
-
-            XmlSerializer out = new FastXmlSerializer();
-            out.setOutput(fos, StandardCharsets.UTF_8.name());
-            out.startDocument(null, true);
-            out.startTag(null, TAG_URI_GRANTS);
-            for (UriPermission.Snapshot perm : persist) {
-                out.startTag(null, TAG_URI_GRANT);
-                writeIntAttribute(out, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
-                writeIntAttribute(out, ATTR_TARGET_USER_ID, perm.targetUserId);
-                out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
-                out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
-                out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri));
-                writeBooleanAttribute(out, ATTR_PREFIX, perm.uri.prefix);
-                writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags);
-                writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime);
-                out.endTag(null, TAG_URI_GRANT);
-            }
-            out.endTag(null, TAG_URI_GRANTS);
-            out.endDocument();
-
-            mGrantFile.finishWrite(fos);
-        } catch (IOException e) {
-            if (fos != null) {
-                mGrantFile.failWrite(fos);
-            }
-        }
-    }
-
-    @GuardedBy("this")
-    private void readGrantedUriPermissionsLocked() {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "readGrantedUriPermissions()");
-
-        final long now = System.currentTimeMillis();
-
-        FileInputStream fis = null;
-        try {
-            fis = mGrantFile.openRead();
-            final XmlPullParser in = Xml.newPullParser();
-            in.setInput(fis, StandardCharsets.UTF_8.name());
-
-            int type;
-            while ((type = in.next()) != END_DOCUMENT) {
-                final String tag = in.getName();
-                if (type == START_TAG) {
-                    if (TAG_URI_GRANT.equals(tag)) {
-                        final int sourceUserId;
-                        final int targetUserId;
-                        final int userHandle = readIntAttribute(in,
-                                ATTR_USER_HANDLE, UserHandle.USER_NULL);
-                        if (userHandle != UserHandle.USER_NULL) {
-                            // For backwards compatibility.
-                            sourceUserId = userHandle;
-                            targetUserId = userHandle;
-                        } else {
-                            sourceUserId = readIntAttribute(in, ATTR_SOURCE_USER_ID);
-                            targetUserId = readIntAttribute(in, ATTR_TARGET_USER_ID);
-                        }
-                        final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG);
-                        final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
-                        final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
-                        final boolean prefix = readBooleanAttribute(in, ATTR_PREFIX);
-                        final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS);
-                        final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now);
-
-                        // Sanity check that provider still belongs to source package
-                        // Both direct boot aware and unaware packages are fine as we
-                        // will do filtering at query time to avoid multiple parsing.
-                        final ProviderInfo pi = getProviderInfoLocked(
-                                uri.getAuthority(), sourceUserId, MATCH_DIRECT_BOOT_AWARE
-                                        | MATCH_DIRECT_BOOT_UNAWARE);
-                        if (pi != null && sourcePkg.equals(pi.packageName)) {
-                            int targetUid = -1;
-                            try {
-                                targetUid = AppGlobals.getPackageManager().getPackageUid(
-                                        targetPkg, MATCH_UNINSTALLED_PACKAGES, targetUserId);
-                            } catch (RemoteException e) {
-                            }
-                            if (targetUid != -1) {
-                                final UriPermission perm = findOrCreateUriPermissionLocked(
-                                        sourcePkg, targetPkg, targetUid,
-                                        new GrantUri(sourceUserId, uri, prefix));
-                                perm.initPersistedModes(modeFlags, createdTime);
-                            }
-                        } else {
-                            Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
-                                    + " but instead found " + pi);
-                        }
-                    }
-                }
-            }
-        } catch (FileNotFoundException e) {
-            // Missing grants is okay
-        } catch (IOException e) {
-            Slog.wtf(TAG, "Failed reading Uri grants", e);
-        } catch (XmlPullParserException e) {
-            Slog.wtf(TAG, "Failed reading Uri grants", e);
-        } finally {
-            IoUtils.closeQuietly(fis);
-        }
-    }
-
-    /**
-     * @param uri This uri must NOT contain an embedded userId.
-     * @param toPackage Name of package whose uri is being granted to (if {@code null}, uses
-     * calling uid)
-     * @param userId The userId in which the uri is to be resolved.
-     */
-    @Override
-    public void takePersistableUriPermission(Uri uri, final int modeFlags,
-            @Nullable String toPackage, int userId) {
-        final int uid;
-        if (toPackage != null) {
-            enforceCallingPermission(android.Manifest.permission.FORCE_PERSISTABLE_URI_PERMISSIONS,
-                    "takePersistableUriPermission");
-            uid = mPackageManagerInt.getPackageUid(toPackage, 0, userId);
-        } else {
-            enforceNotIsolatedCaller("takePersistableUriPermission");
-            uid = Binder.getCallingUid();
-        }
-
-        Preconditions.checkFlagsArgument(modeFlags,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        synchronized (this) {
-            boolean persistChanged = false;
-            GrantUri grantUri = new GrantUri(userId, uri, false);
-
-            UriPermission exactPerm = findUriPermissionLocked(uid, grantUri);
-            UriPermission prefixPerm = findUriPermissionLocked(uid,
-                    new GrantUri(userId, uri, true));
-
-            final boolean exactValid = (exactPerm != null)
-                    && ((modeFlags & exactPerm.persistableModeFlags) == modeFlags);
-            final boolean prefixValid = (prefixPerm != null)
-                    && ((modeFlags & prefixPerm.persistableModeFlags) == modeFlags);
-
-            if (!(exactValid || prefixValid)) {
-                throw new SecurityException("No persistable permission grants found for UID "
-                        + uid + " and Uri " + grantUri.toSafeString());
-            }
-
-            if (exactValid) {
-                persistChanged |= exactPerm.takePersistableModes(modeFlags);
-            }
-            if (prefixValid) {
-                persistChanged |= prefixPerm.takePersistableModes(modeFlags);
-            }
-
-            persistChanged |= maybePrunePersistedUriGrantsLocked(uid);
-
-            if (persistChanged) {
-                schedulePersistUriGrants();
-            }
-        }
-    }
-
-    /**
-     * @param uri This uri must NOT contain an embedded userId.
-     * @param toPackage Name of the target package whose uri is being released (if {@code null},
-     * uses calling uid)
-     * @param userId The userId in which the uri is to be resolved.
-     */
-    @Override
-    public void releasePersistableUriPermission(Uri uri, final int modeFlags,
-            @Nullable String toPackage, int userId) {
-
-        final int uid;
-        if (toPackage != null) {
-            enforceCallingPermission(android.Manifest.permission.FORCE_PERSISTABLE_URI_PERMISSIONS,
-                    "releasePersistableUriPermission");
-            uid = mPackageManagerInt.getPackageUid(toPackage, 0, userId);
-        } else {
-            enforceNotIsolatedCaller("releasePersistableUriPermission");
-            uid = Binder.getCallingUid();
-        }
-
-        Preconditions.checkFlagsArgument(modeFlags,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
-        synchronized (this) {
-            boolean persistChanged = false;
-
-            UriPermission exactPerm = findUriPermissionLocked(uid,
-                    new GrantUri(userId, uri, false));
-            UriPermission prefixPerm = findUriPermissionLocked(uid,
-                    new GrantUri(userId, uri, true));
-            if (exactPerm == null && prefixPerm == null && toPackage == null) {
-                throw new SecurityException("No permission grants found for UID " + uid
-                        + " and Uri " + uri.toSafeString());
-            }
-
-            if (exactPerm != null) {
-                persistChanged |= exactPerm.releasePersistableModes(modeFlags);
-                removeUriPermissionIfNeededLocked(exactPerm);
-            }
-            if (prefixPerm != null) {
-                persistChanged |= prefixPerm.releasePersistableModes(modeFlags);
-                removeUriPermissionIfNeededLocked(prefixPerm);
-            }
-
-            if (persistChanged) {
-                schedulePersistUriGrants();
-            }
-        }
-    }
-
-    /**
-     * Prune any older {@link UriPermission} for the given UID until outstanding
-     * persisted grants are below {@link #MAX_PERSISTED_URI_GRANTS}.
-     *
-     * @return if any mutations occured that require persisting.
-     */
-    @GuardedBy("this")
-    private boolean maybePrunePersistedUriGrantsLocked(int uid) {
-        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid);
-        if (perms == null) return false;
-        if (perms.size() < MAX_PERSISTED_URI_GRANTS) return false;
-
-        final ArrayList<UriPermission> persisted = Lists.newArrayList();
-        for (UriPermission perm : perms.values()) {
-            if (perm.persistedModeFlags != 0) {
-                persisted.add(perm);
-            }
-        }
-
-        final int trimCount = persisted.size() - MAX_PERSISTED_URI_GRANTS;
-        if (trimCount <= 0) return false;
-
-        Collections.sort(persisted, new UriPermission.PersistedTimeComparator());
-        for (int i = 0; i < trimCount; i++) {
-            final UriPermission perm = persisted.get(i);
-
-            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
-                    "Trimming grant created at " + perm.persistedCreateTime);
-
-            perm.releasePersistableModes(~0);
-            removeUriPermissionIfNeededLocked(perm);
-        }
-
-        return true;
-    }
-
-    @Override
-    public ParceledListSlice<android.content.UriPermission> getPersistedUriPermissions(
-            String packageName, boolean incoming) {
-        enforceNotIsolatedCaller("getPersistedUriPermissions");
-        Preconditions.checkNotNull(packageName, "packageName");
-
-        final int callingUid = Binder.getCallingUid();
-        final int callingUserId = UserHandle.getUserId(callingUid);
-        final IPackageManager pm = AppGlobals.getPackageManager();
-        try {
-            final int packageUid = pm.getPackageUid(packageName,
-                    MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, callingUserId);
-            if (packageUid != callingUid) {
-                throw new SecurityException(
-                        "Package " + packageName + " does not belong to calling UID " + callingUid);
-            }
-        } catch (RemoteException e) {
-            throw new SecurityException("Failed to verify package name ownership");
-        }
-
-        final ArrayList<android.content.UriPermission> result = Lists.newArrayList();
-        synchronized (this) {
-            if (incoming) {
-                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(
-                        callingUid);
-                if (perms == null) {
-                    Slog.w(TAG, "No permission grants found for " + packageName);
-                } else {
-                    for (int j = 0; j < perms.size(); j++) {
-                        final UriPermission perm = perms.valueAt(j);
-                        if (packageName.equals(perm.targetPkg) && perm.persistedModeFlags != 0) {
-                            result.add(perm.buildPersistedPublicApiObject());
-                        }
-                    }
-                }
-            } else {
-                final int size = mGrantedUriPermissions.size();
-                for (int i = 0; i < size; i++) {
-                    final ArrayMap<GrantUri, UriPermission> perms =
-                            mGrantedUriPermissions.valueAt(i);
-                    for (int j = 0; j < perms.size(); j++) {
-                        final UriPermission perm = perms.valueAt(j);
-                        if (packageName.equals(perm.sourcePkg) && perm.persistedModeFlags != 0) {
-                            result.add(perm.buildPersistedPublicApiObject());
-                        }
-                    }
-                }
-            }
-        }
-        return new ParceledListSlice<android.content.UriPermission>(result);
-    }
-
-    @Override
-    public ParceledListSlice<GrantedUriPermission> getGrantedUriPermissions(
-            @Nullable String packageName, int userId) {
-        enforceCallingPermission(android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS,
-                "getGrantedUriPermissions");
-
-        final List<GrantedUriPermission> result = new ArrayList<>();
-        synchronized (this) {
-            final int size = mGrantedUriPermissions.size();
-            for (int i = 0; i < size; i++) {
-                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
-                for (int j = 0; j < perms.size(); j++) {
-                    final UriPermission perm = perms.valueAt(j);
-                    if ((packageName == null || packageName.equals(perm.targetPkg))
-                            && perm.targetUserId == userId
-                            && perm.persistedModeFlags != 0) {
-                        result.add(perm.buildGrantedUriPermission());
-                    }
-                }
-            }
-        }
-        return new ParceledListSlice<>(result);
-    }
-
-    @Override
-    public void clearGrantedUriPermissions(String packageName, int userId) {
-        enforceCallingPermission(android.Manifest.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS,
-                "clearGrantedUriPermissions");
-        synchronized(this) {
-            removeUriPermissionsForPackageLocked(packageName, userId, true, true);
-        }
-    }
-
     @Override
     public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
         synchronized (this) {
@@ -8940,7 +7685,8 @@
             // Looking for cross-user grants before enforcing the typical cross-users permissions
             int tmpTargetUserId = mUserController.unsafeConvertIncomingUser(userId);
             if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
-                if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) {
+                if (mUgmInternal.checkAuthorityGrants(
+                        callingUid, cpi, tmpTargetUserId, checkUser)) {
                     return null;
                 }
                 checkedGrants = true;
@@ -8986,7 +7732,8 @@
                 }
             }
         }
-        if (!checkedGrants && checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
+        if (!checkedGrants
+                && mUgmInternal.checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
             return null;
         }
 
@@ -9005,41 +7752,6 @@
         return msg;
     }
 
-    /**
-     * Returns if the ContentProvider has granted a uri to callingUid
-     */
-    boolean checkAuthorityGrants(int callingUid, ProviderInfo cpi, int userId, boolean checkUser) {
-        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
-        if (perms != null) {
-            for (int i=perms.size()-1; i>=0; i--) {
-                GrantUri grantUri = perms.keyAt(i);
-                if (grantUri.sourceUserId == userId || !checkUser) {
-                    if (matchesProvider(grantUri.uri, cpi)) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the uri authority is one of the authorities specified in the provider.
-     */
-    boolean matchesProvider(Uri uri, ProviderInfo cpi) {
-        String uriAuth = uri.getAuthority();
-        String cpiAuth = cpi.authority;
-        if (cpiAuth.indexOf(';') == -1) {
-            return cpiAuth.equals(uriAuth);
-        }
-        String[] cpiAuths = cpiAuth.split(";");
-        int length = cpiAuths.length;
-        for (int i = 0; i < length; i++) {
-            if (cpiAuths[i].equals(uriAuth)) return true;
-        }
-        return false;
-    }
-
     ContentProviderConnection incProviderCountLocked(ProcessRecord r,
             final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
             String callingTag, boolean stable) {
@@ -11521,9 +10233,7 @@
 
         retrieveSettings();
         final int currentUserId = mUserController.getCurrentUserId();
-        synchronized (this) {
-            readGrantedUriPermissionsLocked();
-        }
+        mUgmInternal.onSystemReady();
 
         final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
         if (pmi != null) {
@@ -14287,48 +12997,10 @@
     @GuardedBy("this")
     void dumpPermissionsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage) {
-        boolean needSep = false;
-        boolean printedAnything = false;
 
         pw.println("ACTIVITY MANAGER URI PERMISSIONS (dumpsys activity permissions)");
 
-        if (mGrantedUriPermissions.size() > 0) {
-            boolean printed = false;
-            int dumpUid = -2;
-            if (dumpPackage != null) {
-                try {
-                    dumpUid = mContext.getPackageManager().getPackageUidAsUser(dumpPackage,
-                            MATCH_ANY_USER, 0);
-                } catch (NameNotFoundException e) {
-                    dumpUid = -1;
-                }
-            }
-            for (int i=0; i<mGrantedUriPermissions.size(); i++) {
-                int uid = mGrantedUriPermissions.keyAt(i);
-                if (dumpUid >= -1 && UserHandle.getAppId(uid) != dumpUid) {
-                    continue;
-                }
-                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
-                if (!printed) {
-                    if (needSep) pw.println();
-                    needSep = true;
-                    pw.println("  Granted Uri Permissions:");
-                    printed = true;
-                    printedAnything = true;
-                }
-                pw.print("  * UID "); pw.print(uid); pw.println(" holds:");
-                for (UriPermission perm : perms.values()) {
-                    pw.print("    "); pw.println(perm);
-                    if (dumpAll) {
-                        perm.dump(pw, "      ");
-                    }
-                }
-            }
-        }
-
-        if (!printedAnything) {
-            pw.println("  (nothing)");
-        }
+        mUgmInternal.dump(pw, dumpAll, dumpPackage);
     }
 
     void dumpPendingIntentsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
@@ -17725,8 +16397,8 @@
                                                 intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);
 
                                         // Remove all permissions granted from/to this package
-                                        removeUriPermissionsForPackageLocked(ssp, userId, true,
-                                                false);
+                                        mUgmInternal.removeUriPermissionsForPackage(ssp, userId,
+                                                true, false);
 
                                         mActivityTaskManager.getRecentTasks().removeTasksByPackageName(ssp, userId);
 
@@ -22124,15 +20796,6 @@
     @VisibleForTesting
     final class LocalService extends ActivityManagerInternal {
         @Override
-        public void grantUriPermissionFromIntent(int callingUid, String targetPkg, Intent intent,
-                int targetUserId) {
-            synchronized (ActivityManagerService.this) {
-                ActivityManagerService.this.grantUriPermissionFromIntentLocked(callingUid,
-                        targetPkg, intent, null, targetUserId);
-            }
-        }
-
-        @Override
         public String checkContentProviderAccess(String authority, int userId) {
             return ActivityManagerService.this.checkContentProviderAccess(authority, userId);
         }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index b17aada..68e70a9 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -186,6 +186,7 @@
 import com.android.server.AttributeCache;
 import com.android.server.AttributeCache.Entry;
 import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.uri.UriPermissionOwner;
 import com.android.server.wm.AppWindowContainerController;
 import com.android.server.wm.AppWindowContainerListener;
 import com.android.server.wm.ConfigurationContainer;
@@ -1376,7 +1377,7 @@
 
     UriPermissionOwner getUriPermissionsLocked() {
         if (uriPermissions == null) {
-            uriPermissions = new UriPermissionOwner(service.mAm, this);
+            uriPermissions = new UriPermissionOwner(service.mUgmInternal, this);
         }
         return uriPermissions;
     }
@@ -1428,7 +1429,7 @@
      */
     final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
         // The activity now gets access to the data associated with this Intent.
-        service.mAm.grantUriPermissionFromIntentLocked(callingUid, packageName,
+        service.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
                 intent, getUriPermissionsLocked(), userId);
         final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
         boolean unsent = true;
@@ -1588,7 +1589,7 @@
 
     void removeUriPermissionsLocked() {
         if (uriPermissions != null) {
-            uriPermissions.removeUriPermissionsLocked();
+            uriPermissions.removeUriPermissions();
             uriPermissions = null;
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index d08784f..accb61f 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3368,7 +3368,7 @@
             String resultWho, int requestCode, int resultCode, Intent data) {
 
         if (callingUid > 0) {
-            mService.mAm.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
+            mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
                     data, r.getUriPermissionsLocked(), r.userId);
         }
 
@@ -3682,7 +3682,7 @@
                 }
             }
             if (r.info.applicationInfo.uid > 0) {
-                mService.mAm.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid,
+                mService.mUgmInternal.grantUriPermissionFromIntent(r.info.applicationInfo.uid,
                         resultTo.packageName, resultData,
                         resultTo.getUriPermissionsLocked(), resultTo.userId);
             }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4e39033..41a7e9e 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2502,8 +2502,7 @@
         }
         if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
             if (r != null) {
-                // TODO: This should also take in the windowing mode and activity type into account.
-                stack = (T) getValidLaunchStackOnDisplay(displayId, r);
+                stack = (T) getValidLaunchStackOnDisplay(displayId, r, options);
                 if (stack != null) {
                     return stack;
                 }
@@ -2548,12 +2547,7 @@
             }
         }
 
-        if (display == null
-                || !canLaunchOnDisplay(r, display.mDisplayId)
-                // TODO: Can be removed once we figure-out how non-standard types should launch
-                // outside the default display.
-                || (activityType != ACTIVITY_TYPE_STANDARD
-                && activityType != ACTIVITY_TYPE_UNDEFINED)) {
+        if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
             display = getDefaultDisplay();
         }
 
@@ -2575,7 +2569,8 @@
      * @param r Activity that should be launched there.
      * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
      */
-    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r) {
+    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+            @Nullable ActivityOptions options) {
         final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
         if (activityDisplay == null) {
             throw new IllegalArgumentException(
@@ -2597,7 +2592,9 @@
         // If there is no valid stack on the external display - check if new dynamic stack will do.
         if (displayId != DEFAULT_DISPLAY) {
             return activityDisplay.createStack(
-                    r.getWindowingMode(), r.getActivityType(), true /*onTop*/);
+                    options != null ? options.getLaunchWindowingMode() : r.getWindowingMode(),
+                    options != null ? options.getLaunchActivityType() : r.getActivityType(),
+                    true /*onTop*/);
         }
 
         Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
@@ -2698,7 +2695,8 @@
             if (displayId == currentFocus) {
                 continue;
             }
-            final ActivityStack stack = getValidLaunchStackOnDisplay(displayId, r);
+            final ActivityStack stack = getValidLaunchStackOnDisplay(displayId, r,
+                    null /* options */);
             if (stack != null) {
                 return stack;
             }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 4378ccb..8be5ada 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1409,7 +1409,7 @@
             return result;
         }
 
-        mService.mAm.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
+        mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
                 mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
         mService.mAm.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
                 mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
@@ -2103,7 +2103,7 @@
             if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) {
                 // Can't use target display, lets find a stack on the source display.
                 mTargetStack = mSupervisor.getValidLaunchStackOnDisplay(
-                        sourceStack.mDisplayId, mStartActivity);
+                        sourceStack.mDisplayId, mStartActivity, mOptions);
             }
             if (mTargetStack == null) {
                 // There are no suitable stacks on the target and source display(s). Look on all
@@ -2326,7 +2326,7 @@
 
         if (mPreferredDisplayId != DEFAULT_DISPLAY) {
             // Try to put the activity in a stack on a secondary display.
-            stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r);
+            stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r, aOptions);
             if (stack == null) {
                 // If source display is not suitable - look for topmost valid stack in the system.
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index de732c7..20f2ece 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -135,6 +135,7 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.server.AppOpsService;
 import com.android.server.pm.UserManagerService;
+import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import android.app.AppGlobals;
 import android.app.IActivityController;
@@ -259,6 +260,7 @@
     UiHandler mUiHandler;
     ActivityManagerService mAm;
     ActivityManagerInternal mAmInternal;
+    UriGrantsManagerInternal mUgmInternal;
     /* Global service lock used by the package the owns this service. */
     Object mGlobalLock;
     ActivityStackSupervisor mStackSupervisor;
@@ -612,6 +614,7 @@
 
     void onActivityManagerInternalAdded() {
         mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
     }
 
     protected ActivityStackSupervisor createStackSupervisor() {
@@ -3613,7 +3616,7 @@
                 throw new IllegalArgumentException("Activity does not exist; token="
                         + activityToken);
             }
-            return r.getUriPermissionsLocked().getExternalTokenLocked();
+            return r.getUriPermissionsLocked().getExternalToken();
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 8f43620..d8f94c9 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -44,6 +44,8 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
+import com.android.server.uri.NeededUriGrants;
+import com.android.server.uri.UriPermissionOwner;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -129,7 +131,7 @@
         final int id;
         final int callingId;
         final Intent intent;
-        final ActivityManagerService.NeededUriGrants neededGrants;
+        final NeededUriGrants neededGrants;
         long deliveredTime;
         int deliveryCount;
         int doneExecutingCount;
@@ -138,7 +140,7 @@
         String stringName;      // caching of toString
 
         StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent,
-                ActivityManagerService.NeededUriGrants _neededGrants, int _callingId) {
+                NeededUriGrants _neededGrants, int _callingId) {
             sr = _sr;
             taskRemoved = _taskRemoved;
             id = _id;
@@ -149,14 +151,14 @@
 
         UriPermissionOwner getUriPermissionsLocked() {
             if (uriPermissions == null) {
-                uriPermissions = new UriPermissionOwner(sr.ams, this);
+                uriPermissions = new UriPermissionOwner(sr.ams.mUgmInternal, this);
             }
             return uriPermissions;
         }
 
         void removeUriPermissionsLocked() {
             if (uriPermissions != null) {
-                uriPermissions.removeUriPermissionsLocked();
+                uriPermissions.removeUriPermissions();
                 uriPermissions = null;
             }
         }
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 5db20b0..e2ad5f5 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -21,7 +21,9 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
+import android.app.IUriGrantsManager;
 import android.app.KeyguardManager;
+import android.app.UriGrantsManager;
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.ContentProvider;
@@ -48,7 +50,9 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.io.IOException;
 import java.io.RandomAccessFile;
@@ -143,6 +147,8 @@
         SystemProperties.getBoolean("ro.kernel.qemu", false);
 
     private final IActivityManager mAm;
+    private final IUriGrantsManager mUgm;
+    private final UriGrantsManagerInternal mUgmInternal;
     private final IUserManager mUm;
     private final PackageManager mPm;
     private final AppOpsManager mAppOps;
@@ -159,15 +165,12 @@
         super(context);
 
         mAm = ActivityManager.getService();
+        mUgm = UriGrantsManager.getService();
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
         mPm = getContext().getPackageManager();
         mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
-        IBinder permOwner = null;
-        try {
-            permOwner = mAm.newUriPermissionOwner("clipboard");
-        } catch (RemoteException e) {
-            Slog.w("clipboard", "AM dead", e);
-        }
+        final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
         mPermissionOwner = permOwner;
         if (IS_EMULATOR) {
             mHostClipboardMonitor = new HostClipboardMonitor(
@@ -497,12 +500,10 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             // This will throw SecurityException if caller can't grant
-            mAm.checkGrantUriPermission(sourceUid, null,
+            mUgmInternal.checkGrantUriPermission(sourceUid, null,
                     ContentProvider.getUriWithoutUserId(uri),
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
-        } catch (RemoteException ignored) {
-            // Ignored because we're in same process
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -531,7 +532,7 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            mAm.grantUriPermissionFromOwner(mPermissionOwner, sourceUid, targetPkg,
+            mUgm.grantUriPermissionFromOwner(mPermissionOwner, sourceUid, targetPkg,
                     ContentProvider.getUriWithoutUserId(uri),
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)),
@@ -588,12 +589,10 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            mAm.revokeUriPermissionFromOwner(mPermissionOwner,
+            mUgmInternal.revokeUriPermissionFromOwner(mPermissionOwner,
                     ContentProvider.getUriWithoutUserId(uri),
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
-        } catch (RemoteException ignored) {
-            // Ignored because we're in same process
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index b796abd..e762d7f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -43,8 +43,6 @@
     @GuardedBy("mLock")
     private boolean mSystemAudioControlFeatureEnabled;
 
-    protected Integer mSystemAudioSource;
-
     private boolean mTvSystemAudioModeSupport;
 
     protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
@@ -232,7 +230,6 @@
             return true;
         }
 
-        mSystemAudioSource = systemAudioStatusOn ? message.getSource() : null;
         mService.sendCecCommand(
                 HdmiCecMessageBuilder.buildSetSystemAudioMode(
                         mAddress, Constants.ADDR_BROADCAST, systemAudioStatusOn));
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 71ba03a..a2eb1c1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1732,7 +1732,7 @@
                         Slog.w(TAG, "audio system is not available");
                         return;
                     }
-                    if (audioSystem().mSystemAudioSource == null) {
+                    if (!audioSystem().isSystemAudioActivated()) {
                         Slog.w(TAG, "audio system is not in system audio mode");
                         return;
                     }
@@ -1741,7 +1741,7 @@
                     sendCecCommand(HdmiCecMessageBuilder
                             .buildReportAudioStatus(
                                     device.getDeviceInfo().getLogicalAddress(),
-                                    audioSystem().mSystemAudioSource,
+                                    Constants.ADDR_TV,
                                     scaledVolume,
                                     isMute));
                 }
diff --git a/services/core/java/com/android/server/job/GrantedUriPermissions.java b/services/core/java/com/android/server/job/GrantedUriPermissions.java
index 8fecb8f..005b189 100644
--- a/services/core/java/com/android/server/job/GrantedUriPermissions.java
+++ b/services/core/java/com/android/server/job/GrantedUriPermissions.java
@@ -17,6 +17,7 @@
 package com.android.server.job;
 
 import android.app.IActivityManager;
+import android.app.UriGrantsManager;
 import android.content.ClipData;
 import android.content.ContentProvider;
 import android.content.Intent;
@@ -26,6 +27,8 @@
 import android.os.UserHandle;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
+import com.android.server.LocalServices;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -42,16 +45,14 @@
         mGrantFlags = grantFlags;
         mSourceUserId = UserHandle.getUserId(uid);
         mTag = tag;
-        mPermissionOwner = am.newUriPermissionOwner("job: " + tag);
+        mPermissionOwner = LocalServices
+                .getService(UriGrantsManagerInternal.class).newUriPermissionOwner("job: " + tag);
     }
 
     public void revoke(IActivityManager am) {
         for (int i = mUris.size()-1; i >= 0; i--) {
-            try {
-                am.revokeUriPermissionFromOwner(mPermissionOwner, mUris.get(i),
-                        mGrantFlags, mSourceUserId);
-            } catch (RemoteException e) {
-            }
+            LocalServices.getService(UriGrantsManagerInternal.class).revokeUriPermissionFromOwner(
+                    mPermissionOwner, mUris.get(i), mGrantFlags, mSourceUserId);
         }
         mUris.clear();
     }
@@ -119,8 +120,8 @@
             if (curPerms == null) {
                 curPerms = new GrantedUriPermissions(am, grantFlags, sourceUid, tag);
             }
-            am.grantUriPermissionFromOwner(curPerms.mPermissionOwner, sourceUid, targetPackage,
-                    uri, grantFlags, sourceUserId, targetUserId);
+            UriGrantsManager.getService().grantUriPermissionFromOwner(curPerms.mPermissionOwner,
+                    sourceUid, targetPackage, uri, grantFlags, sourceUserId, targetUserId);
             curPerms.mUris.add(uri);
         } catch (RemoteException e) {
             Slog.e("JobScheduler", "AM dead");
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 507d0c4..5a0578f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -91,6 +91,7 @@
 import android.app.IActivityManager;
 import android.app.INotificationManager;
 import android.app.ITransientNotification;
+import android.app.IUriGrantsManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
@@ -98,6 +99,7 @@
 import android.app.NotificationManager.Policy;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
+import android.app.UriGrantsManager;
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.backup.BackupManager;
@@ -204,6 +206,7 @@
 import com.android.server.notification.ManagedServices.UserProfiles;
 import com.android.server.policy.PhoneWindowManager;
 import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import libcore.io.IoUtils;
@@ -318,6 +321,8 @@
     private ICompanionDeviceManager mCompanionManager;
     private AccessibilityManager mAccessibilityManager;
     private IDeviceIdleController mDeviceIdleController;
+    private IUriGrantsManager mUgm;
+    private UriGrantsManagerInternal mUgmInternal;
 
     final IBinder mForegroundToken = new Binder();
     private WorkerHandler mHandler;
@@ -1365,7 +1370,8 @@
             ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
             NotificationUsageStats usageStats, AtomicFile policyFile,
             ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
-            UsageStatsManagerInternal appUsageStats, DevicePolicyManagerInternal dpm) {
+            UsageStatsManagerInternal appUsageStats, DevicePolicyManagerInternal dpm,
+            IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal) {
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
                 Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -1374,6 +1380,8 @@
         mAccessibilityManager =
                 (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
         mAm = am;
+        mUgm = ugm;
+        mUgmInternal = ugmInternal;
         mPackageManager = packageManager;
         mPackageManagerClient = packageManagerClient;
         mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
@@ -1528,7 +1536,9 @@
                 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
                 getGroupHelper(), ActivityManager.getService(),
                 LocalServices.getService(UsageStatsManagerInternal.class),
-                LocalServices.getService(DevicePolicyManagerInternal.class));
+                LocalServices.getService(DevicePolicyManagerInternal.class),
+                UriGrantsManager.getService(),
+                LocalServices.getService(UriGrantsManagerInternal.class));
 
         // register for various Intents
         IntentFilter filter = new IntentFilter();
@@ -5620,12 +5630,8 @@
 
         // If we have Uris to grant, but no owner yet, go create one
         if (newUris != null && permissionOwner == null) {
-            try {
-                if (DBG) Slog.d(TAG, key + ": creating owner");
-                permissionOwner = mAm.newUriPermissionOwner("NOTIF:" + key);
-            } catch (RemoteException ignored) {
-                // Ignored because we're in same process
-            }
+            if (DBG) Slog.d(TAG, key + ": creating owner");
+            permissionOwner = mUgmInternal.newUriPermissionOwner("NOTIF:" + key);
         }
 
         // If we have no Uris to grant, but an existing owner, go destroy it
@@ -5633,11 +5639,9 @@
             final long ident = Binder.clearCallingIdentity();
             try {
                 if (DBG) Slog.d(TAG, key + ": destroying owner");
-                mAm.revokeUriPermissionFromOwner(permissionOwner, null, ~0,
+                mUgmInternal.revokeUriPermissionFromOwner(permissionOwner, null, ~0,
                         UserHandle.getUserId(oldRecord.getUid()));
                 permissionOwner = null;
-            } catch (RemoteException ignored) {
-                // Ignored because we're in same process
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -5677,7 +5681,7 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            mAm.grantUriPermissionFromOwner(owner, sourceUid, targetPkg,
+            mUgm.grantUriPermissionFromOwner(owner, sourceUid, targetPkg,
                     ContentProvider.getUriWithoutUserId(uri),
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)),
@@ -5694,12 +5698,11 @@
 
         final long ident = Binder.clearCallingIdentity();
         try {
-            mAm.revokeUriPermissionFromOwner(owner,
+            mUgmInternal.revokeUriPermissionFromOwner(
+                    owner,
                     ContentProvider.getUriWithoutUserId(uri),
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
-        } catch (RemoteException ignored) {
-            // Ignored because we're in same process
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 0154c72..f32b338 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -70,6 +70,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.io.PrintWriter;
 import java.lang.reflect.Array;
@@ -96,6 +97,7 @@
     private static final int MAX_LOGTAG_LENGTH = 35;
     final StatusBarNotification sbn;
     IActivityManager mAm;
+    UriGrantsManagerInternal mUgmInternal;
     final int mTargetSdkVersion;
     final int mOriginalFlags;
     private final Context mContext;
@@ -182,6 +184,7 @@
         mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class)
                 .getPackageTargetSdkVersion(sbn.getPackageName());
         mAm = ActivityManager.getService();
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
         mOriginalFlags = sbn.getNotification().flags;
         mRankingTimeMs = calculateRankingTimeMs(0L);
         mCreationTimeMs = sbn.getPostTime();
@@ -1107,7 +1110,7 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             // This will throw SecurityException if caller can't grant
-            mAm.checkGrantUriPermission(sourceUid, null,
+            mUgmInternal.checkGrantUriPermission(sourceUid, null,
                     ContentProvider.getUriWithoutUserId(uri),
                     Intent.FLAG_GRANT_READ_URI_PERMISSION,
                     ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
@@ -1116,8 +1119,6 @@
                 mGrantableUris = new ArraySet<>();
             }
             mGrantableUris.add(uri);
-        } catch (RemoteException ignored) {
-            // Ignored because we're in same process
         } catch (SecurityException e) {
             if (!userOverriddenUri) {
                 if (mTargetSdkVersion >= Build.VERSION_CODES.P) {
diff --git a/services/core/java/com/android/server/uri/GrantUri.java b/services/core/java/com/android/server/uri/GrantUri.java
new file mode 100644
index 0000000..c694264
--- /dev/null
+++ b/services/core/java/com/android/server/uri/GrantUri.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.uri;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.GrantUriProto;
+
+/** A {@link Uri} that can be granted to app an to access with the right permission. */
+public class GrantUri {
+    public final int sourceUserId;
+    public final Uri uri;
+    public boolean prefix;
+
+    public GrantUri(int sourceUserId, Uri uri, boolean prefix) {
+        this.sourceUserId = sourceUserId;
+        this.uri = uri;
+        this.prefix = prefix;
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = 1;
+        hashCode = 31 * hashCode + sourceUserId;
+        hashCode = 31 * hashCode + uri.hashCode();
+        hashCode = 31 * hashCode + (prefix ? 1231 : 1237);
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof GrantUri) {
+            GrantUri other = (GrantUri) o;
+            return uri.equals(other.uri) && (sourceUserId == other.sourceUserId)
+                    && prefix == other.prefix;
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        String result = uri.toString() + " [user " + sourceUserId + "]";
+        if (prefix) result += " [prefix]";
+        return result;
+    }
+
+    public String toSafeString() {
+        String result = uri.toSafeString() + " [user " + sourceUserId + "]";
+        if (prefix) result += " [prefix]";
+        return result;
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(GrantUriProto.URI, uri.toString());
+        proto.write(GrantUriProto.SOURCE_USER_ID, sourceUserId);
+        proto.end(token);
+    }
+
+    public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
+        if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+            return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
+                    ContentProvider.getUriWithoutUserId(uri), false);
+        } else {
+            return new GrantUri(defaultSourceUserHandle, uri, false);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/uri/NeededUriGrants.java b/services/core/java/com/android/server/uri/NeededUriGrants.java
new file mode 100644
index 0000000..87a2641
--- /dev/null
+++ b/services/core/java/com/android/server/uri/NeededUriGrants.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.uri;
+
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.NeededUriGrantsProto;
+
+import java.util.ArrayList;
+
+/** List of {@link GrantUri} a process needs. */
+public class NeededUriGrants extends ArrayList<GrantUri> {
+    final String targetPkg;
+    final int targetUid;
+    final int flags;
+
+    public NeededUriGrants(String targetPkg, int targetUid, int flags) {
+        this.targetPkg = targetPkg;
+        this.targetUid = targetUid;
+        this.flags = flags;
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg);
+        proto.write(NeededUriGrantsProto.TARGET_UID, targetUid);
+        proto.write(NeededUriGrantsProto.FLAGS, flags);
+
+        final int N = this.size();
+        for (int i = 0; i < N; i++) {
+            this.get(i).writeToProto(proto, NeededUriGrantsProto.GRANTS);
+        }
+        proto.end(token);
+    }
+}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
new file mode 100644
index 0000000..2f50fcb
--- /dev/null
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.uri;
+
+import android.content.Intent;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+
+/**
+ * Uri Grants local system service interface.
+ * @hide Only for use within system server
+ */
+public interface UriGrantsManagerInternal {
+    void onSystemReady();
+    void onActivityManagerInternalAdded();
+    void removeUriPermissionIfNeeded(UriPermission perm);
+    void grantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
+            final int modeFlags, UriPermissionOwner owner, int targetUserId);
+    void revokeUriPermission(String targetPackage, int callingUid,
+            GrantUri grantUri, final int modeFlags);
+    boolean checkUriPermission(GrantUri grantUri, int uid, final int modeFlags);
+    int checkGrantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
+            final int modeFlags, int lastTargetUid);
+    int checkGrantUriPermission(
+            int callingUid, String targetPkg, Uri uri, int modeFlags, int userId);
+    NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid,
+            String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId);
+    /**
+     * Grant Uri permissions from one app to another. This method only extends
+     * permission grants if {@code callingUid} has permission to them.
+     */
+    void grantUriPermissionFromIntent(int callingUid,
+            String targetPkg, Intent intent, int targetUserId);
+    void grantUriPermissionFromIntent(int callingUid,
+            String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId);
+    void grantUriPermissionUncheckedFromIntent(
+            NeededUriGrants needed, UriPermissionOwner owner);
+    IBinder newUriPermissionOwner(String name);
+    /**
+     * Remove any {@link UriPermission} granted <em>from</em> or <em>to</em> the
+     * given package.
+     *
+     * @param packageName Package name to match, or {@code null} to apply to all
+     *            packages.
+     * @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply
+     *            to all users.
+     * @param persistable If persistable grants should be removed.
+     * @param targetOnly When {@code true}, only remove permissions where the app is the target,
+     * not source.
+     */
+    void removeUriPermissionsForPackage(
+            String packageName, int userHandle, boolean persistable, boolean targetOnly);
+    /**
+     * @param uri This uri must NOT contain an embedded userId.
+     * @param userId The userId in which the uri is to be resolved.
+     */
+    void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId);
+    boolean checkAuthorityGrants(
+            int callingUid, ProviderInfo cpi, int userId, boolean checkUser);
+    void dump(PrintWriter pw, boolean dumpAll, String dumpPackage);
+}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
new file mode 100644
index 0000000..1b5ffda
--- /dev/null
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -0,0 +1,1472 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.uri;
+
+import static android.Manifest.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS;
+import static android.Manifest.permission.FORCE_PERSISTABLE_URI_PERMISSIONS;
+import static android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.ROOT_UID;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.Process.myUid;
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readLongAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeLongAttribute;
+import static com.android.server.uri.UriGrantsManagerService.H.PERSIST_URI_GRANTS_MSG;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.AppGlobals;
+import android.app.GrantedUriPermission;
+import android.app.IUriGrantsManager;
+import android.content.ClipData;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PathPermission;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Downloads;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
+import com.android.server.IoThread;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import libcore.io.IoUtils;
+
+/** Manages uri grants. */
+public class UriGrantsManagerService extends IUriGrantsManager.Stub {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "UriGrantsManagerService";
+    // Maximum number of persisted Uri grants a package is allowed
+    private static final int MAX_PERSISTED_URI_GRANTS = 128;
+
+    private final Object mLock = new Object();
+    private final Context mContext;
+    private final H mH;
+    ActivityManagerInternal mAmInternal;
+    PackageManagerInternal mPmInternal;
+
+    /** File storing persisted {@link #mGrantedUriPermissions}. */
+    private final AtomicFile mGrantFile;
+
+    /** XML constants used in {@link #mGrantFile} */
+    private static final String TAG_URI_GRANTS = "uri-grants";
+    private static final String TAG_URI_GRANT = "uri-grant";
+    private static final String ATTR_USER_HANDLE = "userHandle";
+    private static final String ATTR_SOURCE_USER_ID = "sourceUserId";
+    private static final String ATTR_TARGET_USER_ID = "targetUserId";
+    private static final String ATTR_SOURCE_PKG = "sourcePkg";
+    private static final String ATTR_TARGET_PKG = "targetPkg";
+    private static final String ATTR_URI = "uri";
+    private static final String ATTR_MODE_FLAGS = "modeFlags";
+    private static final String ATTR_CREATED_TIME = "createdTime";
+    private static final String ATTR_PREFIX = "prefix";
+
+    /**
+     * Global set of specific {@link Uri} permissions that have been granted.
+     * This optimized lookup structure maps from {@link UriPermission#targetUid}
+     * to {@link UriPermission#uri} to {@link UriPermission}.
+     */
+    private final SparseArray<ArrayMap<GrantUri, UriPermission>>
+            mGrantedUriPermissions = new SparseArray<>();
+
+    private UriGrantsManagerService(Context context) {
+        mContext = context;
+        mH = new H(IoThread.get().getLooper());
+        final File systemDir = SystemServiceManager.ensureSystemDir();
+        mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants");
+    }
+
+    private void start() {
+        LocalServices.addService(UriGrantsManagerInternal.class, new LocalService());
+    }
+
+    void onActivityManagerInternalAdded() {
+        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+        mPmInternal = LocalServices.getService(PackageManagerInternal.class);
+    }
+
+    public static final class Lifecycle extends SystemService {
+        private final UriGrantsManagerService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+            mService = new UriGrantsManagerService(context);
+        }
+
+        @Override
+        public void onStart() {
+            publishBinderService(Context.URI_GRANTS_SERVICE, mService);
+            mService.start();
+        }
+
+        public UriGrantsManagerService getService() {
+            return mService;
+        }
+    }
+
+    /**
+     * @param uri This uri must NOT contain an embedded userId.
+     * @param sourceUserId The userId in which the uri is to be resolved.
+     * @param targetUserId The userId of the app that receives the grant.
+     */
+    @Override
+    public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri,
+            final int modeFlags, int sourceUserId, int targetUserId) {
+        targetUserId = mAmInternal.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), targetUserId, false, ALLOW_FULL_ONLY,
+                "grantUriPermissionFromOwner", null);
+        synchronized(mLock) {
+            UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+            if (owner == null) {
+                throw new IllegalArgumentException("Unknown owner: " + token);
+            }
+            if (fromUid != Binder.getCallingUid()) {
+                if (Binder.getCallingUid() != myUid()) {
+                    // Only system code can grant URI permissions on behalf
+                    // of other users.
+                    throw new SecurityException("nice try");
+                }
+            }
+            if (targetPkg == null) {
+                throw new IllegalArgumentException("null target");
+            }
+            if (uri == null) {
+                throw new IllegalArgumentException("null uri");
+            }
+
+            grantUriPermission(fromUid, targetPkg, new GrantUri(sourceUserId, uri, false),
+                    modeFlags, owner, targetUserId);
+        }
+    }
+
+    @Override
+    public ParceledListSlice<android.content.UriPermission> getPersistedUriPermissions(
+            String packageName, boolean incoming) {
+        enforceNotIsolatedCaller("getPersistedUriPermissions");
+        Preconditions.checkNotNull(packageName, "packageName");
+
+        final int callingUid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        final IPackageManager pm = AppGlobals.getPackageManager();
+        try {
+            final int packageUid = pm.getPackageUid(packageName,
+                    MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, callingUserId);
+            if (packageUid != callingUid) {
+                throw new SecurityException(
+                        "Package " + packageName + " does not belong to calling UID " + callingUid);
+            }
+        } catch (RemoteException e) {
+            throw new SecurityException("Failed to verify package name ownership");
+        }
+
+        final ArrayList<android.content.UriPermission> result = Lists.newArrayList();
+        synchronized (mLock) {
+            if (incoming) {
+                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(
+                        callingUid);
+                if (perms == null) {
+                    Slog.w(TAG, "No permission grants found for " + packageName);
+                } else {
+                    for (int j = 0; j < perms.size(); j++) {
+                        final UriPermission perm = perms.valueAt(j);
+                        if (packageName.equals(perm.targetPkg) && perm.persistedModeFlags != 0) {
+                            result.add(perm.buildPersistedPublicApiObject());
+                        }
+                    }
+                }
+            } else {
+                final int size = mGrantedUriPermissions.size();
+                for (int i = 0; i < size; i++) {
+                    final ArrayMap<GrantUri, UriPermission> perms =
+                            mGrantedUriPermissions.valueAt(i);
+                    for (int j = 0; j < perms.size(); j++) {
+                        final UriPermission perm = perms.valueAt(j);
+                        if (packageName.equals(perm.sourcePkg) && perm.persistedModeFlags != 0) {
+                            result.add(perm.buildPersistedPublicApiObject());
+                        }
+                    }
+                }
+            }
+        }
+        return new ParceledListSlice<>(result);
+    }
+
+    @Override
+    public ParceledListSlice<GrantedUriPermission> getGrantedUriPermissions(
+            @Nullable String packageName, int userId) {
+        mAmInternal.enforceCallingPermission(
+                GET_APP_GRANTED_URI_PERMISSIONS, "getGrantedUriPermissions");
+
+        final List<GrantedUriPermission> result = new ArrayList<>();
+        synchronized (mLock) {
+            final int size = mGrantedUriPermissions.size();
+            for (int i = 0; i < size; i++) {
+                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+                for (int j = 0; j < perms.size(); j++) {
+                    final UriPermission perm = perms.valueAt(j);
+                    if ((packageName == null || packageName.equals(perm.targetPkg))
+                            && perm.targetUserId == userId
+                            && perm.persistedModeFlags != 0) {
+                        result.add(perm.buildGrantedUriPermission());
+                    }
+                }
+            }
+        }
+        return new ParceledListSlice<>(result);
+    }
+
+    /**
+     * @param uri This uri must NOT contain an embedded userId.
+     * @param toPackage Name of package whose uri is being granted to (if {@code null}, uses
+     * calling uid)
+     * @param userId The userId in which the uri is to be resolved.
+     */
+    @Override
+    public void takePersistableUriPermission(Uri uri, final int modeFlags,
+            @Nullable String toPackage, int userId) {
+        final int uid;
+        if (toPackage != null) {
+            mAmInternal.enforceCallingPermission(FORCE_PERSISTABLE_URI_PERMISSIONS,
+                    "takePersistableUriPermission");
+            uid = mPmInternal.getPackageUid(toPackage, 0, userId);
+        } else {
+            enforceNotIsolatedCaller("takePersistableUriPermission");
+            uid = Binder.getCallingUid();
+        }
+
+        Preconditions.checkFlagsArgument(modeFlags,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        synchronized (mLock) {
+            boolean persistChanged = false;
+            GrantUri grantUri = new GrantUri(userId, uri, false);
+
+            UriPermission exactPerm = findUriPermissionLocked(uid, grantUri);
+            UriPermission prefixPerm = findUriPermissionLocked(uid,
+                    new GrantUri(userId, uri, true));
+
+            final boolean exactValid = (exactPerm != null)
+                    && ((modeFlags & exactPerm.persistableModeFlags) == modeFlags);
+            final boolean prefixValid = (prefixPerm != null)
+                    && ((modeFlags & prefixPerm.persistableModeFlags) == modeFlags);
+
+            if (!(exactValid || prefixValid)) {
+                throw new SecurityException("No persistable permission grants found for UID "
+                        + uid + " and Uri " + grantUri.toSafeString());
+            }
+
+            if (exactValid) {
+                persistChanged |= exactPerm.takePersistableModes(modeFlags);
+            }
+            if (prefixValid) {
+                persistChanged |= prefixPerm.takePersistableModes(modeFlags);
+            }
+
+            persistChanged |= maybePrunePersistedUriGrants(uid);
+
+            if (persistChanged) {
+                schedulePersistUriGrants();
+            }
+        }
+    }
+
+    @Override
+    public void clearGrantedUriPermissions(String packageName, int userId) {
+        mAmInternal.enforceCallingPermission(
+                CLEAR_APP_GRANTED_URI_PERMISSIONS, "clearGrantedUriPermissions");
+        synchronized(mLock) {
+            removeUriPermissionsForPackage(packageName, userId, true, true);
+        }
+    }
+
+    /**
+     * @param uri This uri must NOT contain an embedded userId.
+     * @param toPackage Name of the target package whose uri is being released (if {@code null},
+     * uses calling uid)
+     * @param userId The userId in which the uri is to be resolved.
+     */
+    @Override
+    public void releasePersistableUriPermission(Uri uri, final int modeFlags,
+            @Nullable String toPackage, int userId) {
+
+        final int uid;
+        if (toPackage != null) {
+            mAmInternal.enforceCallingPermission(FORCE_PERSISTABLE_URI_PERMISSIONS,
+                    "releasePersistableUriPermission");
+            uid = mPmInternal.getPackageUid(toPackage, 0, userId);
+        } else {
+            enforceNotIsolatedCaller("releasePersistableUriPermission");
+            uid = Binder.getCallingUid();
+        }
+
+        Preconditions.checkFlagsArgument(modeFlags,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+        synchronized (mLock) {
+            boolean persistChanged = false;
+
+            UriPermission exactPerm = findUriPermissionLocked(uid,
+                    new GrantUri(userId, uri, false));
+            UriPermission prefixPerm = findUriPermissionLocked(uid,
+                    new GrantUri(userId, uri, true));
+            if (exactPerm == null && prefixPerm == null && toPackage == null) {
+                throw new SecurityException("No permission grants found for UID " + uid
+                        + " and Uri " + uri.toSafeString());
+            }
+
+            if (exactPerm != null) {
+                persistChanged |= exactPerm.releasePersistableModes(modeFlags);
+                removeUriPermissionIfNeeded(exactPerm);
+            }
+            if (prefixPerm != null) {
+                persistChanged |= prefixPerm.releasePersistableModes(modeFlags);
+                removeUriPermissionIfNeeded(prefixPerm);
+            }
+
+            if (persistChanged) {
+                schedulePersistUriGrants();
+            }
+        }
+    }
+
+    /**
+     * Remove any {@link UriPermission} granted <em>from</em> or <em>to</em> the
+     * given package.
+     *
+     * @param packageName Package name to match, or {@code null} to apply to all
+     *            packages.
+     * @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply
+     *            to all users.
+     * @param persistable If persistable grants should be removed.
+     * @param targetOnly When {@code true}, only remove permissions where the app is the target,
+     * not source.
+     */
+    void removeUriPermissionsForPackage(
+            String packageName, int userHandle, boolean persistable, boolean targetOnly) {
+        if (userHandle == UserHandle.USER_ALL && packageName == null) {
+            throw new IllegalArgumentException("Must narrow by either package or user");
+        }
+
+        boolean persistChanged = false;
+
+        int N = mGrantedUriPermissions.size();
+        for (int i = 0; i < N; i++) {
+            final int targetUid = mGrantedUriPermissions.keyAt(i);
+            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+
+            // Only inspect grants matching user
+            if (userHandle == UserHandle.USER_ALL
+                    || userHandle == UserHandle.getUserId(targetUid)) {
+                for (Iterator<UriPermission> it = perms.values().iterator(); it.hasNext();) {
+                    final UriPermission perm = it.next();
+
+                    // Only inspect grants matching package
+                    if (packageName == null || (!targetOnly && perm.sourcePkg.equals(packageName))
+                            || perm.targetPkg.equals(packageName)) {
+                        // Hacky solution as part of fixing a security bug; ignore
+                        // grants associated with DownloadManager so we don't have
+                        // to immediately launch it to regrant the permissions
+                        if (Downloads.Impl.AUTHORITY.equals(perm.uri.uri.getAuthority())
+                                && !persistable) continue;
+
+                        persistChanged |= perm.revokeModes(persistable
+                                ? ~0 : ~Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, true);
+
+                        // Only remove when no modes remain; any persisted grants
+                        // will keep this alive.
+                        if (perm.modeFlags == 0) {
+                            it.remove();
+                        }
+                    }
+                }
+
+                if (perms.isEmpty()) {
+                    mGrantedUriPermissions.remove(targetUid);
+                    N--;
+                    i--;
+                }
+            }
+        }
+
+        if (persistChanged) {
+            schedulePersistUriGrants();
+        }
+    }
+
+    /** Returns if the ContentProvider has granted a uri to callingUid */
+    boolean checkAuthorityGrants(int callingUid, ProviderInfo cpi, int userId, boolean checkUser) {
+        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+        if (perms != null) {
+            for (int i = perms.size() - 1; i >= 0; i--) {
+                GrantUri grantUri = perms.keyAt(i);
+                if (grantUri.sourceUserId == userId || !checkUser) {
+                    if (matchesProvider(grantUri.uri, cpi)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /** Returns true if the uri authority is one of the authorities specified in the provider. */
+    private boolean matchesProvider(Uri uri, ProviderInfo cpi) {
+        String uriAuth = uri.getAuthority();
+        String cpiAuth = cpi.authority;
+        if (cpiAuth.indexOf(';') == -1) {
+            return cpiAuth.equals(uriAuth);
+        }
+        String[] cpiAuths = cpiAuth.split(";");
+        int length = cpiAuths.length;
+        for (int i = 0; i < length; i++) {
+            if (cpiAuths[i].equals(uriAuth)) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Prune any older {@link UriPermission} for the given UID until outstanding
+     * persisted grants are below {@link #MAX_PERSISTED_URI_GRANTS}.
+     *
+     * @return if any mutations occured that require persisting.
+     */
+    private boolean maybePrunePersistedUriGrants(int uid) {
+        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid);
+        if (perms == null) return false;
+        if (perms.size() < MAX_PERSISTED_URI_GRANTS) return false;
+
+        final ArrayList<UriPermission> persisted = Lists.newArrayList();
+        for (UriPermission perm : perms.values()) {
+            if (perm.persistedModeFlags != 0) {
+                persisted.add(perm);
+            }
+        }
+
+        final int trimCount = persisted.size() - MAX_PERSISTED_URI_GRANTS;
+        if (trimCount <= 0) return false;
+
+        Collections.sort(persisted, new UriPermission.PersistedTimeComparator());
+        for (int i = 0; i < trimCount; i++) {
+            final UriPermission perm = persisted.get(i);
+
+            if (DEBUG) Slog.v(TAG, "Trimming grant created at " + perm.persistedCreateTime);
+
+            perm.releasePersistableModes(~0);
+            removeUriPermissionIfNeeded(perm);
+        }
+
+        return true;
+    }
+
+    /** Like checkGrantUriPermission, but takes an Intent. */
+    NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid,
+            String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
+        if (DEBUG) Slog.v(TAG,
+                "Checking URI perm to data=" + (intent != null ? intent.getData() : null)
+                        + " clip=" + (intent != null ? intent.getClipData() : null)
+                        + " from " + intent + "; flags=0x"
+                        + Integer.toHexString(intent != null ? intent.getFlags() : 0));
+
+        if (targetPkg == null) {
+            throw new NullPointerException("targetPkg");
+        }
+
+        if (intent == null) {
+            return null;
+        }
+        Uri data = intent.getData();
+        ClipData clip = intent.getClipData();
+        if (data == null && clip == null) {
+            return null;
+        }
+        // Default userId for uris in the intent (if they don't specify it themselves)
+        int contentUserHint = intent.getContentUserHint();
+        if (contentUserHint == UserHandle.USER_CURRENT) {
+            contentUserHint = UserHandle.getUserId(callingUid);
+        }
+        final IPackageManager pm = AppGlobals.getPackageManager();
+        int targetUid;
+        if (needed != null) {
+            targetUid = needed.targetUid;
+        } else {
+            try {
+                targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, targetUserId);
+            } catch (RemoteException ex) {
+                return null;
+            }
+            if (targetUid < 0) {
+                if (DEBUG) Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg
+                        + " on user " + targetUserId);
+                return null;
+            }
+        }
+        if (data != null) {
+            GrantUri grantUri = GrantUri.resolve(contentUserHint, data);
+            targetUid = checkGrantUriPermission(callingUid, targetPkg, grantUri, mode, targetUid);
+            if (targetUid > 0) {
+                if (needed == null) {
+                    needed = new NeededUriGrants(targetPkg, targetUid, mode);
+                }
+                needed.add(grantUri);
+            }
+        }
+        if (clip != null) {
+            for (int i=0; i<clip.getItemCount(); i++) {
+                Uri uri = clip.getItemAt(i).getUri();
+                if (uri != null) {
+                    GrantUri grantUri = GrantUri.resolve(contentUserHint, uri);
+                    targetUid = checkGrantUriPermission(callingUid, targetPkg,
+                            grantUri, mode, targetUid);
+                    if (targetUid > 0) {
+                        if (needed == null) {
+                            needed = new NeededUriGrants(targetPkg, targetUid, mode);
+                        }
+                        needed.add(grantUri);
+                    }
+                } else {
+                    Intent clipIntent = clip.getItemAt(i).getIntent();
+                    if (clipIntent != null) {
+                        NeededUriGrants newNeeded = checkGrantUriPermissionFromIntent(
+                                callingUid, targetPkg, clipIntent, mode, needed, targetUserId);
+                        if (newNeeded != null) {
+                            needed = newNeeded;
+                        }
+                    }
+                }
+            }
+        }
+
+        return needed;
+    }
+
+    void grantUriPermissionFromIntent(int callingUid,
+            String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId) {
+        NeededUriGrants needed = checkGrantUriPermissionFromIntent(callingUid, targetPkg,
+                intent, intent != null ? intent.getFlags() : 0, null, targetUserId);
+        if (needed == null) {
+            return;
+        }
+
+        grantUriPermissionUncheckedFromIntent(needed, owner);
+    }
+
+    void readGrantedUriPermissions() {
+        if (DEBUG) Slog.v(TAG, "readGrantedUriPermissions()");
+
+        final long now = System.currentTimeMillis();
+
+        FileInputStream fis = null;
+        try {
+            fis = mGrantFile.openRead();
+            final XmlPullParser in = Xml.newPullParser();
+            in.setInput(fis, StandardCharsets.UTF_8.name());
+
+            int type;
+            while ((type = in.next()) != END_DOCUMENT) {
+                final String tag = in.getName();
+                if (type == START_TAG) {
+                    if (TAG_URI_GRANT.equals(tag)) {
+                        final int sourceUserId;
+                        final int targetUserId;
+                        final int userHandle = readIntAttribute(in,
+                                ATTR_USER_HANDLE, UserHandle.USER_NULL);
+                        if (userHandle != UserHandle.USER_NULL) {
+                            // For backwards compatibility.
+                            sourceUserId = userHandle;
+                            targetUserId = userHandle;
+                        } else {
+                            sourceUserId = readIntAttribute(in, ATTR_SOURCE_USER_ID);
+                            targetUserId = readIntAttribute(in, ATTR_TARGET_USER_ID);
+                        }
+                        final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG);
+                        final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
+                        final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
+                        final boolean prefix = readBooleanAttribute(in, ATTR_PREFIX);
+                        final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS);
+                        final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now);
+
+                        // Sanity check that provider still belongs to source package
+                        // Both direct boot aware and unaware packages are fine as we
+                        // will do filtering at query time to avoid multiple parsing.
+                        final ProviderInfo pi = getProviderInfo(uri.getAuthority(), sourceUserId,
+                                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
+                        if (pi != null && sourcePkg.equals(pi.packageName)) {
+                            int targetUid = -1;
+                            try {
+                                targetUid = AppGlobals.getPackageManager().getPackageUid(
+                                        targetPkg, MATCH_UNINSTALLED_PACKAGES, targetUserId);
+                            } catch (RemoteException e) {
+                            }
+                            if (targetUid != -1) {
+                                final UriPermission perm = findOrCreateUriPermission(
+                                        sourcePkg, targetPkg, targetUid,
+                                        new GrantUri(sourceUserId, uri, prefix));
+                                perm.initPersistedModes(modeFlags, createdTime);
+                            }
+                        } else {
+                            Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
+                                    + " but instead found " + pi);
+                        }
+                    }
+                }
+            }
+        } catch (FileNotFoundException e) {
+            // Missing grants is okay
+        } catch (IOException e) {
+            Slog.wtf(TAG, "Failed reading Uri grants", e);
+        } catch (XmlPullParserException e) {
+            Slog.wtf(TAG, "Failed reading Uri grants", e);
+        } finally {
+            IoUtils.closeQuietly(fis);
+        }
+    }
+
+    private UriPermission findOrCreateUriPermission(String sourcePkg,
+            String targetPkg, int targetUid, GrantUri grantUri) {
+        ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
+        if (targetUris == null) {
+            targetUris = Maps.newArrayMap();
+            mGrantedUriPermissions.put(targetUid, targetUris);
+        }
+
+        UriPermission perm = targetUris.get(grantUri);
+        if (perm == null) {
+            perm = new UriPermission(sourcePkg, targetPkg, targetUid, grantUri);
+            targetUris.put(grantUri, perm);
+        }
+
+        return perm;
+    }
+
+    private void grantUriPermissionUnchecked(int targetUid, String targetPkg, GrantUri grantUri,
+            final int modeFlags, UriPermissionOwner owner) {
+        if (!Intent.isAccessUriMode(modeFlags)) {
+            return;
+        }
+
+        // So here we are: the caller has the assumed permission to the uri, and the target doesn't.
+        // Let's now give this to the target.
+
+        if (DEBUG) Slog.v(TAG,
+                "Granting " + targetPkg + "/" + targetUid + " permission to " + grantUri);
+
+        final String authority = grantUri.uri.getAuthority();
+        final ProviderInfo pi = getProviderInfo(authority, grantUri.sourceUserId,
+                MATCH_DEBUG_TRIAGED_MISSING);
+        if (pi == null) {
+            Slog.w(TAG, "No content provider found for grant: " + grantUri.toSafeString());
+            return;
+        }
+
+        if ((modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0) {
+            grantUri.prefix = true;
+        }
+        final UriPermission perm = findOrCreateUriPermission(
+                pi.packageName, targetPkg, targetUid, grantUri);
+        perm.grantModes(modeFlags, owner);
+    }
+
+    /** Like grantUriPermissionUnchecked, but takes an Intent. */
+    void grantUriPermissionUncheckedFromIntent(NeededUriGrants needed, UriPermissionOwner owner) {
+        if (needed == null) {
+            return;
+        }
+        for (int i=0; i<needed.size(); i++) {
+            GrantUri grantUri = needed.get(i);
+            grantUriPermissionUnchecked(needed.targetUid, needed.targetPkg,
+                    grantUri, needed.flags, owner);
+        }
+    }
+
+    void grantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
+            final int modeFlags, UriPermissionOwner owner, int targetUserId) {
+        if (targetPkg == null) {
+            throw new NullPointerException("targetPkg");
+        }
+        int targetUid;
+        final IPackageManager pm = AppGlobals.getPackageManager();
+        try {
+            targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, targetUserId);
+        } catch (RemoteException ex) {
+            return;
+        }
+
+        targetUid = checkGrantUriPermission(callingUid, targetPkg, grantUri, modeFlags, targetUid);
+        if (targetUid < 0) {
+            return;
+        }
+
+        grantUriPermissionUnchecked(targetUid, targetPkg, grantUri, modeFlags, owner);
+    }
+
+    void revokeUriPermission(String targetPackage, int callingUid, GrantUri grantUri,
+            final int modeFlags) {
+        if (DEBUG) Slog.v(TAG, "Revoking all granted permissions to " + grantUri);
+
+        final IPackageManager pm = AppGlobals.getPackageManager();
+        final String authority = grantUri.uri.getAuthority();
+        final ProviderInfo pi = getProviderInfo(authority, grantUri.sourceUserId,
+                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
+        if (pi == null) {
+            Slog.w(TAG, "No content provider found for permission revoke: "
+                    + grantUri.toSafeString());
+            return;
+        }
+
+        // Does the caller have this permission on the URI?
+        if (!checkHoldingPermissions(pm, pi, grantUri, callingUid, modeFlags)) {
+            // If they don't have direct access to the URI, then revoke any
+            // ownerless URI permissions that have been granted to them.
+            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+            if (perms != null) {
+                boolean persistChanged = false;
+                for (int i = perms.size()-1; i >= 0; i--) {
+                    final UriPermission perm = perms.valueAt(i);
+                    if (targetPackage != null && !targetPackage.equals(perm.targetPkg)) {
+                        continue;
+                    }
+                    if (perm.uri.sourceUserId == grantUri.sourceUserId
+                            && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
+                        if (DEBUG) Slog.v(TAG, "Revoking non-owned "
+                                + perm.targetUid + " permission to " + perm.uri);
+                        persistChanged |= perm.revokeModes(
+                                modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
+                        if (perm.modeFlags == 0) {
+                            perms.removeAt(i);
+                        }
+                    }
+                }
+                if (perms.isEmpty()) {
+                    mGrantedUriPermissions.remove(callingUid);
+                }
+                if (persistChanged) {
+                    schedulePersistUriGrants();
+                }
+            }
+            return;
+        }
+
+        boolean persistChanged = false;
+
+        // Go through all of the permissions and remove any that match.
+        for (int i = mGrantedUriPermissions.size()-1; i >= 0; i--) {
+            final int targetUid = mGrantedUriPermissions.keyAt(i);
+            final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+
+            for (int j = perms.size()-1; j >= 0; j--) {
+                final UriPermission perm = perms.valueAt(j);
+                if (targetPackage != null && !targetPackage.equals(perm.targetPkg)) {
+                    continue;
+                }
+                if (perm.uri.sourceUserId == grantUri.sourceUserId
+                        && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
+                    if (DEBUG) Slog.v(TAG,
+                            "Revoking " + perm.targetUid + " permission to " + perm.uri);
+                    persistChanged |= perm.revokeModes(
+                            modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION,
+                            targetPackage == null);
+                    if (perm.modeFlags == 0) {
+                        perms.removeAt(j);
+                    }
+                }
+            }
+
+            if (perms.isEmpty()) {
+                mGrantedUriPermissions.removeAt(i);
+            }
+        }
+
+        if (persistChanged) {
+            schedulePersistUriGrants();
+        }
+    }
+
+    /**
+     * Determine if UID is holding permissions required to access {@link Uri} in
+     * the given {@link ProviderInfo}. Final permission checking is always done
+     * in {@link ContentProvider}.
+     */
+    private boolean checkHoldingPermissions(
+            IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
+        if (DEBUG) Slog.v(TAG, "checkHoldingPermissions: uri=" + grantUri + " uid=" + uid);
+        if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
+            if (ActivityManager.checkComponentPermission(INTERACT_ACROSS_USERS, uid, -1, true)
+                    != PERMISSION_GRANTED) {
+                return false;
+            }
+        }
+        return checkHoldingPermissionsInternal(pm, pi, grantUri, uid, modeFlags, true);
+    }
+
+    private boolean checkHoldingPermissionsInternal(IPackageManager pm, ProviderInfo pi,
+            GrantUri grantUri, int uid, final int modeFlags, boolean considerUidPermissions) {
+        if (pi.applicationInfo.uid == uid) {
+            return true;
+        } else if (!pi.exported) {
+            return false;
+        }
+
+        boolean readMet = (modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
+        boolean writeMet = (modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
+        try {
+            // check if target holds top-level <provider> permissions
+            if (!readMet && pi.readPermission != null && considerUidPermissions
+                    && (pm.checkUidPermission(pi.readPermission, uid) == PERMISSION_GRANTED)) {
+                readMet = true;
+            }
+            if (!writeMet && pi.writePermission != null && considerUidPermissions
+                    && (pm.checkUidPermission(pi.writePermission, uid) == PERMISSION_GRANTED)) {
+                writeMet = true;
+            }
+
+            // track if unprotected read/write is allowed; any denied
+            // <path-permission> below removes this ability
+            boolean allowDefaultRead = pi.readPermission == null;
+            boolean allowDefaultWrite = pi.writePermission == null;
+
+            // check if target holds any <path-permission> that match uri
+            final PathPermission[] pps = pi.pathPermissions;
+            if (pps != null) {
+                final String path = grantUri.uri.getPath();
+                int i = pps.length;
+                while (i > 0 && (!readMet || !writeMet)) {
+                    i--;
+                    PathPermission pp = pps[i];
+                    if (pp.match(path)) {
+                        if (!readMet) {
+                            final String pprperm = pp.getReadPermission();
+                            if (DEBUG) Slog.v(TAG,
+                                    "Checking read perm for " + pprperm + " for " + pp.getPath()
+                                            + ": match=" + pp.match(path)
+                                            + " check=" + pm.checkUidPermission(pprperm, uid));
+                            if (pprperm != null) {
+                                if (considerUidPermissions && pm.checkUidPermission(pprperm, uid)
+                                        == PERMISSION_GRANTED) {
+                                    readMet = true;
+                                } else {
+                                    allowDefaultRead = false;
+                                }
+                            }
+                        }
+                        if (!writeMet) {
+                            final String ppwperm = pp.getWritePermission();
+                            if (DEBUG) Slog.v(TAG,
+                                    "Checking write perm " + ppwperm + " for " + pp.getPath()
+                                            + ": match=" + pp.match(path)
+                                            + " check=" + pm.checkUidPermission(ppwperm, uid));
+                            if (ppwperm != null) {
+                                if (considerUidPermissions && pm.checkUidPermission(ppwperm, uid)
+                                        == PERMISSION_GRANTED) {
+                                    writeMet = true;
+                                } else {
+                                    allowDefaultWrite = false;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            // grant unprotected <provider> read/write, if not blocked by
+            // <path-permission> above
+            if (allowDefaultRead) readMet = true;
+            if (allowDefaultWrite) writeMet = true;
+
+        } catch (RemoteException e) {
+            return false;
+        }
+
+        return readMet && writeMet;
+    }
+
+    private void removeUriPermissionIfNeeded(UriPermission perm) {
+        if (perm.modeFlags != 0) {
+            return;
+        }
+        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(
+                perm.targetUid);
+        if (perms == null) {
+            return;
+        }
+        if (DEBUG) Slog.v(TAG, "Removing " + perm.targetUid + " permission to " + perm.uri);
+
+        perms.remove(perm.uri);
+        if (perms.isEmpty()) {
+            mGrantedUriPermissions.remove(perm.targetUid);
+        }
+    }
+
+    private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
+        final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
+        if (targetUris != null) {
+            return targetUris.get(grantUri);
+        }
+        return null;
+    }
+
+    private void schedulePersistUriGrants() {
+        if (!mH.hasMessages(PERSIST_URI_GRANTS_MSG)) {
+            mH.sendMessageDelayed(mH.obtainMessage(PERSIST_URI_GRANTS_MSG),
+                    10 * DateUtils.SECOND_IN_MILLIS);
+        }
+    }
+
+    private void enforceNotIsolatedCaller(String caller) {
+        if (UserHandle.isIsolated(Binder.getCallingUid())) {
+            throw new SecurityException("Isolated process not allowed to call " + caller);
+        }
+    }
+
+    private ProviderInfo getProviderInfo(String authority, int userHandle, int pmFlags) {
+        ProviderInfo pi = null;
+        try {
+            pi = AppGlobals.getPackageManager().resolveContentProvider(
+                    authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags,
+                    userHandle);
+        } catch (RemoteException ex) {
+        }
+        return pi;
+    }
+
+    /**
+     * Check if the targetPkg can be granted permission to access uri by
+     * the callingUid using the given modeFlags.  Throws a security exception
+     * if callingUid is not allowed to do this.  Returns the uid of the target
+     * if the URI permission grant should be performed; returns -1 if it is not
+     * needed (for example targetPkg already has permission to access the URI).
+     * If you already know the uid of the target, you can supply it in
+     * lastTargetUid else set that to -1.
+     */
+    int checkGrantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
+            final int modeFlags, int lastTargetUid) {
+        if (!Intent.isAccessUriMode(modeFlags)) {
+            return -1;
+        }
+
+        if (targetPkg != null) {
+            if (DEBUG) Slog.v(TAG, "Checking grant " + targetPkg + " permission to " + grantUri);
+        }
+
+        final IPackageManager pm = AppGlobals.getPackageManager();
+
+        // If this is not a content: uri, we can't do anything with it.
+        if (!ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) {
+            if (DEBUG) Slog.v(TAG, "Can't grant URI permission for non-content URI: " + grantUri);
+            return -1;
+        }
+
+        // Bail early if system is trying to hand out permissions directly; it
+        // must always grant permissions on behalf of someone explicit.
+        final int callingAppId = UserHandle.getAppId(callingUid);
+        if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) {
+            if ("com.android.settings.files".equals(grantUri.uri.getAuthority())) {
+                // Exempted authority for
+                // 1. cropping user photos and sharing a generated license html
+                //    file in Settings app
+                // 2. sharing a generated license html file in TvSettings app
+            } else {
+                Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
+                        + " grant to " + grantUri + "; use startActivityAsCaller() instead");
+                return -1;
+            }
+        }
+
+        final String authority = grantUri.uri.getAuthority();
+        final ProviderInfo pi = getProviderInfo(authority, grantUri.sourceUserId,
+                MATCH_DEBUG_TRIAGED_MISSING);
+        if (pi == null) {
+            Slog.w(TAG, "No content provider found for permission check: " +
+                    grantUri.uri.toSafeString());
+            return -1;
+        }
+
+        int targetUid = lastTargetUid;
+        if (targetUid < 0 && targetPkg != null) {
+            try {
+                targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
+                        UserHandle.getUserId(callingUid));
+                if (targetUid < 0) {
+                    if (DEBUG) Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg);
+                    return -1;
+                }
+            } catch (RemoteException ex) {
+                return -1;
+            }
+        }
+
+        // If we're extending a persistable grant, then we always need to create
+        // the grant data structure so that take/release APIs work
+        if ((modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0) {
+            return targetUid;
+        }
+
+        if (targetUid >= 0) {
+            // First...  does the target actually need this permission?
+            if (checkHoldingPermissions(pm, pi, grantUri, targetUid, modeFlags)) {
+                // No need to grant the target this permission.
+                if (DEBUG) Slog.v(TAG,
+                        "Target " + targetPkg + " already has full permission to " + grantUri);
+                return -1;
+            }
+        } else {
+            // First...  there is no target package, so can anyone access it?
+            boolean allowed = pi.exported;
+            if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+                if (pi.readPermission != null) {
+                    allowed = false;
+                }
+            }
+            if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+                if (pi.writePermission != null) {
+                    allowed = false;
+                }
+            }
+            if (pi.pathPermissions != null) {
+                final int N = pi.pathPermissions.length;
+                for (int i=0; i<N; i++) {
+                    if (pi.pathPermissions[i] != null
+                            && pi.pathPermissions[i].match(grantUri.uri.getPath())) {
+                        if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+                            if (pi.pathPermissions[i].getReadPermission() != null) {
+                                allowed = false;
+                            }
+                        }
+                        if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+                            if (pi.pathPermissions[i].getWritePermission() != null) {
+                                allowed = false;
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+            if (allowed) {
+                return -1;
+            }
+        }
+
+        /* There is a special cross user grant if:
+         * - The target is on another user.
+         * - Apps on the current user can access the uri without any uid permissions.
+         * In this case, we grant a uri permission, even if the ContentProvider does not normally
+         * grant uri permissions.
+         */
+        boolean specialCrossUserGrant = UserHandle.getUserId(targetUid) != grantUri.sourceUserId
+                && checkHoldingPermissionsInternal(pm, pi, grantUri, callingUid,
+                modeFlags, false /*without considering the uid permissions*/);
+
+        // Second...  is the provider allowing granting of URI permissions?
+        if (!specialCrossUserGrant) {
+            if (!pi.grantUriPermissions) {
+                throw new SecurityException("Provider " + pi.packageName
+                        + "/" + pi.name
+                        + " does not allow granting of Uri permissions (uri "
+                        + grantUri + ")");
+            }
+            if (pi.uriPermissionPatterns != null) {
+                final int N = pi.uriPermissionPatterns.length;
+                boolean allowed = false;
+                for (int i=0; i<N; i++) {
+                    if (pi.uriPermissionPatterns[i] != null
+                            && pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) {
+                        allowed = true;
+                        break;
+                    }
+                }
+                if (!allowed) {
+                    throw new SecurityException("Provider " + pi.packageName
+                            + "/" + pi.name
+                            + " does not allow granting of permission to path of Uri "
+                            + grantUri);
+                }
+            }
+        }
+
+        // Third...  does the caller itself have permission to access this uri?
+        if (!checkHoldingPermissions(pm, pi, grantUri, callingUid, modeFlags)) {
+            // Require they hold a strong enough Uri permission
+            if (!checkUriPermission(grantUri, callingUid, modeFlags)) {
+                if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(pi.readPermission)) {
+                    throw new SecurityException(
+                            "UID " + callingUid + " does not have permission to " + grantUri
+                                    + "; you could obtain access using ACTION_OPEN_DOCUMENT "
+                                    + "or related APIs");
+                } else {
+                    throw new SecurityException(
+                            "UID " + callingUid + " does not have permission to " + grantUri);
+                }
+            }
+        }
+        return targetUid;
+    }
+
+    /**
+     * @param userId The userId in which the uri is to be resolved.
+     */
+    int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri, int modeFlags,
+            int userId) {
+        return checkGrantUriPermission(callingUid, targetPkg,
+                new GrantUri(userId, uri, false), modeFlags, -1);
+    }
+
+    boolean checkUriPermission(GrantUri grantUri, int uid, final int modeFlags) {
+        final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
+        final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE
+                : UriPermission.STRENGTH_OWNED;
+
+        // Root gets to do everything.
+        if (uid == 0) {
+            return true;
+        }
+
+        final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid);
+        if (perms == null) return false;
+
+        // First look for exact match
+        final UriPermission exactPerm = perms.get(grantUri);
+        if (exactPerm != null && exactPerm.getStrength(modeFlags) >= minStrength) {
+            return true;
+        }
+
+        // No exact match, look for prefixes
+        final int N = perms.size();
+        for (int i = 0; i < N; i++) {
+            final UriPermission perm = perms.valueAt(i);
+            if (perm.uri.prefix && grantUri.uri.isPathPrefixMatch(perm.uri.uri)
+                    && perm.getStrength(modeFlags) >= minStrength) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private void writeGrantedUriPermissions() {
+        if (DEBUG) Slog.v(TAG, "writeGrantedUriPermissions()");
+
+        final long startTime = SystemClock.uptimeMillis();
+
+        // Snapshot permissions so we can persist without lock
+        ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList();
+        synchronized (this) {
+            final int size = mGrantedUriPermissions.size();
+            for (int i = 0; i < size; i++) {
+                final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
+                for (UriPermission perm : perms.values()) {
+                    if (perm.persistedModeFlags != 0) {
+                        persist.add(perm.snapshot());
+                    }
+                }
+            }
+        }
+
+        FileOutputStream fos = null;
+        try {
+            fos = mGrantFile.startWrite(startTime);
+
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(fos, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+            out.startTag(null, TAG_URI_GRANTS);
+            for (UriPermission.Snapshot perm : persist) {
+                out.startTag(null, TAG_URI_GRANT);
+                writeIntAttribute(out, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
+                writeIntAttribute(out, ATTR_TARGET_USER_ID, perm.targetUserId);
+                out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
+                out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
+                out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri));
+                writeBooleanAttribute(out, ATTR_PREFIX, perm.uri.prefix);
+                writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags);
+                writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime);
+                out.endTag(null, TAG_URI_GRANT);
+            }
+            out.endTag(null, TAG_URI_GRANTS);
+            out.endDocument();
+
+            mGrantFile.finishWrite(fos);
+        } catch (IOException e) {
+            if (fos != null) {
+                mGrantFile.failWrite(fos);
+            }
+        }
+    }
+
+    final class H extends Handler {
+        static final int PERSIST_URI_GRANTS_MSG = 1;
+
+        public H(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case PERSIST_URI_GRANTS_MSG: {
+                    writeGrantedUriPermissions();
+                    break;
+                }
+            }
+        }
+    }
+
+    final class LocalService implements UriGrantsManagerInternal {
+
+        @Override
+        public void removeUriPermissionIfNeeded(UriPermission perm) {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.removeUriPermissionIfNeeded(perm);
+            }
+        }
+
+        @Override
+        public void grantUriPermission(int callingUid, String targetPkg, GrantUri grantUri,
+                int modeFlags, UriPermissionOwner owner, int targetUserId) {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.grantUriPermission(
+                        callingUid, targetPkg, grantUri, modeFlags, owner, targetUserId);
+            }
+        }
+
+        @Override
+        public void revokeUriPermission(String targetPackage, int callingUid, GrantUri grantUri,
+                int modeFlags) {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.revokeUriPermission(
+                        targetPackage, callingUid, grantUri, modeFlags);
+            }
+        }
+
+        @Override
+        public boolean checkUriPermission(GrantUri grantUri, int uid, int modeFlags) {
+            synchronized (mLock) {
+                return UriGrantsManagerService.this.checkUriPermission(grantUri, uid, modeFlags);
+            }
+        }
+
+        @Override
+        public int checkGrantUriPermission(int callingUid, String targetPkg, GrantUri uri,
+                int modeFlags, int userId) {
+            synchronized (mLock) {
+                return UriGrantsManagerService.this.checkGrantUriPermission(
+                        callingUid, targetPkg, uri, modeFlags, userId);
+            }
+        }
+
+        @Override
+        public int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri, int modeFlags,
+                int userId) {
+            enforceNotIsolatedCaller("checkGrantUriPermission");
+            synchronized (mLock) {
+                return UriGrantsManagerService.this.checkGrantUriPermission(
+                        callingUid, targetPkg, uri, modeFlags, userId);
+            }
+        }
+
+        @Override
+        public NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid, String targetPkg,
+                Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
+            synchronized (mLock) {
+                return UriGrantsManagerService.this.checkGrantUriPermissionFromIntent(
+                        callingUid, targetPkg, intent, mode, needed, targetUserId);
+            }
+        }
+
+        @Override
+        public void grantUriPermissionFromIntent(int callingUid, String targetPkg, Intent intent,
+                int targetUserId) {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.grantUriPermissionFromIntent(
+                        callingUid, targetPkg, intent, null, targetUserId);
+            }
+        }
+
+        @Override
+        public void grantUriPermissionFromIntent(int callingUid, String targetPkg, Intent intent,
+                UriPermissionOwner owner, int targetUserId) {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.grantUriPermissionFromIntent(
+                        callingUid, targetPkg, intent, owner, targetUserId);
+            }
+        }
+
+        @Override
+        public void grantUriPermissionUncheckedFromIntent(NeededUriGrants needed,
+                UriPermissionOwner owner) {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.grantUriPermissionUncheckedFromIntent(needed, owner);
+            }
+        }
+
+        @Override
+        public void onSystemReady() {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.readGrantedUriPermissions();
+            }
+        }
+
+        @Override
+        public void onActivityManagerInternalAdded() {
+            synchronized (mLock) {
+                UriGrantsManagerService.this.onActivityManagerInternalAdded();
+            }
+        }
+
+        @Override
+        public IBinder newUriPermissionOwner(String name) {
+            enforceNotIsolatedCaller("newUriPermissionOwner");
+            synchronized(mLock) {
+                UriPermissionOwner owner = new UriPermissionOwner(this, name);
+                return owner.getExternalToken();
+            }
+        }
+
+        @Override
+        public void removeUriPermissionsForPackage(String packageName, int userHandle,
+                boolean persistable, boolean targetOnly) {
+            synchronized(mLock) {
+                UriGrantsManagerService.this.removeUriPermissionsForPackage(
+                        packageName, userHandle, persistable, targetOnly);
+            }
+        }
+
+        @Override
+        public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId) {
+            synchronized(mLock) {
+                final UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
+                if (owner == null) {
+                    throw new IllegalArgumentException("Unknown owner: " + token);
+                }
+
+                if (uri == null) {
+                    owner.removeUriPermissions(mode);
+                } else {
+                    final boolean prefix = (mode & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0;
+                    owner.removeUriPermission(new GrantUri(userId, uri, prefix), mode);
+                }
+            }
+        }
+
+        @Override
+        public boolean checkAuthorityGrants(int callingUid, ProviderInfo cpi, int userId,
+                boolean checkUser) {
+            synchronized(mLock) {
+                return UriGrantsManagerService.this.checkAuthorityGrants(
+                        callingUid, cpi, userId, checkUser);
+            }
+        }
+
+        @Override
+        public void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) {
+            synchronized(mLock) {
+                boolean needSep = false;
+                boolean printedAnything = false;
+                if (mGrantedUriPermissions.size() > 0) {
+                    boolean printed = false;
+                    int dumpUid = -2;
+                    if (dumpPackage != null) {
+                        try {
+                            dumpUid = mContext.getPackageManager().getPackageUidAsUser(dumpPackage,
+                                    MATCH_ANY_USER, 0);
+                        } catch (PackageManager.NameNotFoundException e) {
+                            dumpUid = -1;
+                        }
+                    }
+                    for (int i = 0; i < mGrantedUriPermissions.size(); i++) {
+                        int uid = mGrantedUriPermissions.keyAt(i);
+                        if (dumpUid >= -1 && UserHandle.getAppId(uid) != dumpUid) {
+                            continue;
+                        }
+                        final ArrayMap<GrantUri, UriPermission> perms =
+                                mGrantedUriPermissions.valueAt(i);
+                        if (!printed) {
+                            if (needSep) pw.println();
+                            needSep = true;
+                            pw.println("  Granted Uri Permissions:");
+                            printed = true;
+                            printedAnything = true;
+                        }
+                        pw.print("  * UID "); pw.print(uid); pw.println(" holds:");
+                        for (UriPermission perm : perms.values()) {
+                            pw.print("    "); pw.println(perm);
+                            if (dumpAll) {
+                                perm.dump(pw, "      ");
+                            }
+                        }
+                    }
+                }
+
+                if (!printedAnything) {
+                    pw.println("  (nothing)");
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/UriPermission.java b/services/core/java/com/android/server/uri/UriPermission.java
similarity index 98%
rename from services/core/java/com/android/server/am/UriPermission.java
rename to services/core/java/com/android/server/uri/UriPermission.java
index 1e071aa..bd6348a 100644
--- a/services/core/java/com/android/server/am/UriPermission.java
+++ b/services/core/java/com/android/server/uri/UriPermission.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -11,10 +11,10 @@
  * 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.am;
+package com.android.server.uri;
 
 import android.app.GrantedUriPermission;
 import android.content.Intent;
@@ -24,7 +24,6 @@
 import android.util.Log;
 import android.util.Slog;
 
-import com.android.server.am.ActivityManagerService.GrantUri;
 import com.google.android.collect.Sets;
 
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/am/UriPermissionOwner.java b/services/core/java/com/android/server/uri/UriPermissionOwner.java
similarity index 75%
rename from services/core/java/com/android/server/am/UriPermissionOwner.java
rename to services/core/java/com/android/server/uri/UriPermissionOwner.java
index 8eda38e..f814dc6 100644
--- a/services/core/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/core/java/com/android/server/uri/UriPermissionOwner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -11,27 +11,30 @@
  * 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.am;
+package com.android.server.uri;
 
-import android.content.Intent;
+import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
+import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+
 import android.os.Binder;
 import android.os.IBinder;
 import android.util.ArraySet;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.server.am.UriPermissionOwnerProto;
 import com.google.android.collect.Sets;
 
 import java.io.PrintWriter;
 import java.util.Iterator;
 
-final class UriPermissionOwner {
-    final ActivityManagerService service;
-    final Object owner;
+public class UriPermissionOwner {
+    private final UriGrantsManagerInternal mService;
+    private final Object mOwner;
 
-    Binder externalToken;
+    private Binder externalToken;
 
     private ArraySet<UriPermission> mReadPerms;
     private ArraySet<UriPermission> mWritePerms;
@@ -42,12 +45,12 @@
         }
     }
 
-    UriPermissionOwner(ActivityManagerService service, Object owner) {
-        this.service = service;
-        this.owner = owner;
+    public UriPermissionOwner(UriGrantsManagerInternal service, Object owner) {
+        mService = service;
+        mOwner = owner;
     }
 
-    Binder getExternalTokenLocked() {
+    public Binder getExternalToken() {
         if (externalToken == null) {
             externalToken = new ExternalToken();
         }
@@ -61,24 +64,22 @@
         return null;
     }
 
-    void removeUriPermissionsLocked() {
-        removeUriPermissionsLocked(Intent.FLAG_GRANT_READ_URI_PERMISSION
-                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    public void removeUriPermissions() {
+        removeUriPermissions(FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION);
     }
 
-    void removeUriPermissionsLocked(int mode) {
-        removeUriPermissionLocked(null, mode);
+    void removeUriPermissions(int mode) {
+        removeUriPermission(null, mode);
     }
 
-    void removeUriPermissionLocked(ActivityManagerService.GrantUri grantUri, int mode) {
-        if ((mode & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0
-                && mReadPerms != null) {
+    void removeUriPermission(GrantUri grantUri, int mode) {
+        if ((mode & FLAG_GRANT_READ_URI_PERMISSION) != 0 && mReadPerms != null) {
             Iterator<UriPermission> it = mReadPerms.iterator();
             while (it.hasNext()) {
                 UriPermission perm = it.next();
                 if (grantUri == null || grantUri.equals(perm.uri)) {
                     perm.removeReadOwner(this);
-                    service.removeUriPermissionIfNeededLocked(perm);
+                    mService.removeUriPermissionIfNeeded(perm);
                     it.remove();
                 }
             }
@@ -86,14 +87,14 @@
                 mReadPerms = null;
             }
         }
-        if ((mode & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0
+        if ((mode & FLAG_GRANT_WRITE_URI_PERMISSION) != 0
                 && mWritePerms != null) {
             Iterator<UriPermission> it = mWritePerms.iterator();
             while (it.hasNext()) {
                 UriPermission perm = it.next();
                 if (grantUri == null || grantUri.equals(perm.uri)) {
                     perm.removeWriteOwner(this);
-                    service.removeUriPermissionIfNeededLocked(perm);
+                    mService.removeUriPermissionIfNeeded(perm);
                     it.remove();
                 }
             }
@@ -142,7 +143,7 @@
 
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
-        proto.write(UriPermissionOwnerProto.OWNER, owner.toString());
+        proto.write(UriPermissionOwnerProto.OWNER, mOwner.toString());
         if (mReadPerms != null) {
             synchronized (mReadPerms) {
                 for (UriPermission p : mReadPerms) {
@@ -162,6 +163,6 @@
 
     @Override
     public String toString() {
-        return owner.toString();
+        return mOwner.toString();
     }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 31c0bdd..621b0c0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3387,11 +3387,16 @@
 
         private void addStackReferenceIfNeeded(TaskStack stack) {
             if (stack.isActivityTypeHome()) {
+                // TODO(b/111363427) Rollback to throws exceptions once we figure out how to
+                // properly deal with home type stack when external display removed
                 if (mHomeStack != null) {
-                    throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
+                    // throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
+                    //        + mHomeStack + " already exist on display=" + this + " stack=" + stack);
+                    Slog.e(TAG, "addStackReferenceIfNeeded: home stack="
                             + mHomeStack + " already exist on display=" + this + " stack=" + stack);
+                } else {
+                    mHomeStack = stack;
                 }
-                mHomeStack = stack;
             }
             final int windowingMode = stack.getWindowingMode();
             if (windowingMode == WINDOWING_MODE_PINNED) {
diff --git a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
index 51d5eea..20a1333 100644
--- a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
+++ b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
+import android.app.UriGrantsManager;
 import android.content.ClipData;
 import android.net.Uri;
 import android.os.Binder;
@@ -25,6 +26,8 @@
 import android.os.RemoteException;
 
 import com.android.internal.view.IDragAndDropPermissions;
+import com.android.server.LocalServices;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.util.ArrayList;
 
@@ -72,7 +75,7 @@
         long origId = Binder.clearCallingIdentity();
         try {
             for (int i = 0; i < mUris.size(); i++) {
-                ActivityManager.getService().grantUriPermissionFromOwner(
+                UriGrantsManager.getService().grantUriPermissionFromOwner(
                         permissionOwner, mSourceUid, mTargetPackage, mUris.get(i), mMode,
                         mSourceUserId, mTargetUserId);
             }
@@ -86,7 +89,8 @@
         if (mActivityToken != null || mPermissionOwnerToken != null) {
             return;
         }
-        mPermissionOwnerToken = ActivityManager.getService().newUriPermissionOwner("drop");
+        mPermissionOwnerToken = LocalServices.getService(UriGrantsManagerInternal.class)
+                .newUriPermissionOwner("drop");
         mTransientToken = transientToken;
         mTransientToken.linkToDeath(this, 0);
 
@@ -117,9 +121,9 @@
             mTransientToken = null;
         }
 
+        UriGrantsManagerInternal ugm = LocalServices.getService(UriGrantsManagerInternal.class);
         for (int i = 0; i < mUris.size(); ++i) {
-            ActivityManager.getService().revokeUriPermissionFromOwner(
-                    permissionOwner, mUris.get(i), mMode, mSourceUserId);
+            ugm.revokeUriPermissionFromOwner(permissionOwner, mUris.get(i), mMode, mSourceUserId);
         }
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ed4d4db..81ac6a4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -233,6 +233,7 @@
 import com.android.server.pm.UserRestrictionsUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 
+import com.android.server.uri.UriGrantsManagerInternal;
 import com.google.android.collect.Sets;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -6973,7 +6974,7 @@
                 intent.putExtra(DeviceAdminReceiver.EXTRA_BUGREPORT_HASH, bugreportHash);
                 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 
-                LocalServices.getService(ActivityManagerInternal.class)
+                LocalServices.getService(UriGrantsManagerInternal.class)
                         .grantUriPermissionFromIntent(Process.SHELL_UID,
                                 mOwners.getDeviceOwnerComponent().getPackageName(),
                                 intent, mOwners.getDeviceOwnerUserId());
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 86c5175..4cd7ccb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -118,6 +118,7 @@
 import com.android.server.tv.TvInputManagerService;
 import com.android.server.tv.TvRemoteService;
 import com.android.server.twilight.TwilightService;
+import com.android.server.uri.UriGrantsManagerService;
 import com.android.server.usage.UsageStatsService;
 import com.android.server.vr.VrManagerService;
 import com.android.server.webkit.WebViewUpdateService;
@@ -562,6 +563,11 @@
         mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class);
         traceEnd();
 
+        // Uri Grants Manager.
+        traceBeginAndSlog("UriGrantsManagerService");
+        mSystemServiceManager.startService(UriGrantsManagerService.Lifecycle.class);
+        traceEnd();
+
         // Activity manager runs the show.
         traceBeginAndSlog("StartActivityManager");
         // TODO: Might need to move after migration to WM.
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 677fd52..7e8697d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -36,8 +36,8 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
-import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
+import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wm.DisplayWindowController;
 
 import org.junit.Rule;
@@ -103,6 +103,7 @@
         if (!sOneTimeSetupDone) {
             sOneTimeSetupDone = true;
             MockitoAnnotations.initMocks(this);
+            AttributeCache.init(mContext);
         }
         mHandlerThread = new HandlerThread("ActivityTestsBaseThread");
         mHandlerThread.start();
@@ -127,7 +128,6 @@
     }
 
     ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
-        AttributeCache.init(mContext);
         final ActivityManagerService am = spy(new TestActivityManagerService(mContext, atm));
         setupActivityManagerService(am, atm);
         return am;
@@ -137,6 +137,8 @@
         atm.setActivityManagerService(am);
         atm.mAmInternal = am.new LocalService();
         am.mAtmInternal = atm.new LocalService();
+        am.mUgmInternal = mock(UriGrantsManagerInternal.class);
+        atm.mUgmInternal = mock(UriGrantsManagerInternal.class);
         // Makes sure the supervisor is using with the spy object.
         atm.mStackSupervisor.setService(atm);
         doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 715f8a6..b86f0db 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -19,6 +19,8 @@
 import android.hardware.tv.cec.V1_0.SendMessageResult;
 import android.os.MessageQueue;
 import com.android.server.hdmi.HdmiCecController.NativeWrapper;
+import java.util.ArrayList;
+import java.util.List;
 
 /** Fake {@link NativeWrapper} useful for testing. */
 final class FakeNativeWrapper implements NativeWrapper {
@@ -41,7 +43,7 @@
                 SendMessageResult.NACK,
             };
 
-    private HdmiCecMessage mResultMessage;
+    private final List<HdmiCecMessage> mResultMessages = new ArrayList<>();
 
     @Override
     public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
@@ -54,7 +56,7 @@
         if (body.length == 0) {
             return mPollAddressResponse[dstAddress];
         } else {
-            mResultMessage = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
+            mResultMessages.add(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
         }
         return SendMessageResult.SUCCESS;
     }
@@ -103,8 +105,15 @@
         return false;
     }
 
-    public HdmiCecMessage getResultMessage() {
-        return mResultMessage;
+    public List<HdmiCecMessage> getResultMessages() {
+        return new ArrayList<>(mResultMessages);
+    }
+
+    public HdmiCecMessage getOnlyResultMessage() throws Exception {
+        if (mResultMessages.size() != 1) {
+            throw new Exception("There is not exactly one message");
+        }
+        return mResultMessages.get(0);
     }
 
     public void setPollAddressResponse(int logicalAddress, int response) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index cde8e63..dbf7664 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -52,11 +52,9 @@
     private int mMusicVolume;
     private int mMusicMaxVolume;
     private boolean mMusicMute;
-    private boolean isAwake;
 
     @Before
     public void SetUp() {
-        isAwake = false;
         mHdmiControlService = new HdmiControlService(null) {
             @Override
             AudioManager getAudioManager() {
@@ -108,29 +106,24 @@
 
             @Override
             void wakeUp() {
-                isAwake = true;
             }
         };
         mMyLooper = mTestLooper.getLooper();
         mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
         mHdmiCecLocalDeviceAudioSystem.init();
         mHdmiControlService.setIoLooper(mMyLooper);
-
         mNativeWrapper = new FakeNativeWrapper();
         mHdmiCecController = HdmiCecController.createWithNativeWrapper(
-            mHdmiControlService, mNativeWrapper);
+                mHdmiControlService, mNativeWrapper);
         mHdmiControlService.setCecController(mHdmiCecController);
         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
         mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
-
         mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
         mHdmiControlService.initPortInfo();
         // No TV device interacts with AVR so system audio control won't be turned on here
         mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
-
         mTestLooper.dispatchAll();
     }
-
     @Test
     public void handleGiveAudioStatus_volume_10_mute_true() {
         mMusicVolume = 10;
@@ -138,215 +131,179 @@
         mMusicMaxVolume = 20;
         int scaledVolume = VolumeControlAction.scaleToCecVolume(10, mMusicMaxVolume);
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildReportAudioStatus(
-            ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
+                ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
         HdmiCecMessage messageGive = HdmiCecMessageBuilder.buildGiveAudioStatus(
-            ADDR_TV, ADDR_AUDIO_SYSTEM);
-
+                ADDR_TV, ADDR_AUDIO_SYSTEM);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
-
     @Test
     public void handleGiveSystemAudioModeStatus_originalOff() {
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+                .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
         HdmiCecMessage messageGive = HdmiCecMessageBuilder
-            .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
-
+                .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
-
     @Test
     public void handleRequestArcInitiate() {
         // TODO(b/80296911): Add tests when finishing handler impl.
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildInitiateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
+                .buildInitiateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
         HdmiCecMessage message = HdmiCecMessageBuilder
-            .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
-
+                .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
-
     @Test
     public void handleRequestArcTermination() {
         // TODO(b/80297105): Add tests when finishing handler impl.
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildTerminateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
+                .buildTerminateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
         HdmiCecMessage messageRequestOff = HdmiCecMessageBuilder
-            .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
-
+                .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(messageRequestOff))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
-
     @Test
     public void handleSetSystemAudioMode_setOn_orignalOff() {
         mMusicMute = true;
         HdmiCecMessage messageSet = HdmiCecMessageBuilder
-            .buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
+                .buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
         HdmiCecMessage messageGive = HdmiCecMessageBuilder
-            .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
-
+                .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
         // Check if originally off
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
-
+                .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
-
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
         // Check if correctly turned on
         expectedMessage = HdmiCecMessageBuilder
-            .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
-
+                .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
         assertThat(mMusicMute).isEqualTo(false);
     }
-
     @Test
     public void handleSystemAudioModeRequest_turnOffByTv() {
         assertThat(mMusicMute).isEqualTo(false);
-
         // Check if feature correctly turned off
         HdmiCecMessage messageGive = HdmiCecMessageBuilder
-            .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+                .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
         HdmiCecMessage messageRequestOff = HdmiCecMessageBuilder
-            .buildSystemAudioModeRequest(ADDR_TV, ADDR_AUDIO_SYSTEM, 2, false);
-
+                .buildSystemAudioModeRequest(ADDR_TV, ADDR_AUDIO_SYSTEM, 2, false);
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+                .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
-
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
         expectedMessage = HdmiCecMessageBuilder
-            .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+                .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
         assertThat(mMusicMute).isEqualTo(true);
     }
-
     @Test
     public void onStandbyAudioSystem_currentSystemAudioControlOn() {
         // Set system audio control on first
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
-
         // Check if standby correctly turns off the feature
         mHdmiCecLocalDeviceAudioSystem.onStandby(false, STANDBY_SCREEN_OFF);
         mTestLooper.dispatchAll();
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
-            .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+                .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
         assertThat(mMusicMute).isEqualTo(true);
     }
-
     @Test
     public void systemAudioControlOnPowerOn_alwaysOn() {
         mHdmiCecLocalDeviceAudioSystem.removeAction(
-            SystemAudioInitiationActionFromAvr.class);
+                SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
-            Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
-
+                Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
         assertThat(mHdmiCecLocalDeviceAudioSystem
-            .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
+                .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
     }
-
     @Test
     public void systemAudioControlOnPowerOn_neverOn() {
         mHdmiCecLocalDeviceAudioSystem.removeAction(
-            SystemAudioInitiationActionFromAvr.class);
+                SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
-            Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
-
+                Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem
-            .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
+                .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
     }
-
     @Test
     public void systemAudioControlOnPowerOn_useLastState_off() {
         mHdmiCecLocalDeviceAudioSystem.removeAction(
-            SystemAudioInitiationActionFromAvr.class);
+                SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
-            Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
-
+                Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
         assertThat(mHdmiCecLocalDeviceAudioSystem
-            .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
+                .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
     }
-
     @Test
     public void systemAudioControlOnPowerOn_useLastState_on() {
         mHdmiCecLocalDeviceAudioSystem.removeAction(
-            SystemAudioInitiationActionFromAvr.class);
+                SystemAudioInitiationActionFromAvr.class);
         mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
-            Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
-
+                Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
         assertThat(mHdmiCecLocalDeviceAudioSystem
-            .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
+                .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
     }
-
     @Test
     public void handleActiveSource_updateActiveSource() {
         HdmiCecMessage message = HdmiCecMessageBuilder
-            .buildActiveSource(ADDR_TV, 0x0000);
+                .buildActiveSource(ADDR_TV, 0x0000);
         ActiveSource expectedActiveSource = new ActiveSource(ADDR_TV, 0x0000);
-
         assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message))
                 .isEqualTo(true);
         mTestLooper.dispatchAll();
         assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().equals(expectedActiveSource))
                 .isEqualTo(true);
     }
-
     @Test
     public void terminateSystemAudioMode_systemAudioModeOff() {
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(false);
-
         mMusicMute = false;
         HdmiCecMessage message = HdmiCecMessageBuilder
                 .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
-
         mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
-
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(false);
         assertThat(mMusicMute).isEqualTo(false);
-        assertThat(mNativeWrapper.getResultMessage()).isNotEqualTo(message);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(message);
     }
-
     @Test
     public void terminateSystemAudioMode_systemAudioModeOn() {
         mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(true);
-
         mMusicMute = false;
         HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
                 .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
-
         mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
-
         assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(false);
         assertThat(mMusicMute).isEqualTo(true);
         mTestLooper.dispatchAll();
-        assertThat(mNativeWrapper.getResultMessage()).isEqualTo(expectedMessage);
+        assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index eec852b..56d3a20 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -13,14 +13,20 @@
  */
 package com.android.server;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.when;
 
 import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
 import android.os.Build;
 import android.support.test.InstrumentationRegistry;
 import android.testing.TestableContext;
 
+import com.android.server.uri.UriGrantsManagerInternal;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.mockito.Mock;
@@ -28,6 +34,7 @@
 
 public class UiServiceTestCase {
     @Mock protected PackageManagerInternal mPmi;
+    @Mock protected UriGrantsManagerInternal mUgmInternal;
 
     protected static final String PKG_N_MR1 = "com.example.n_mr1";
     protected static final String PKG_O = "com.example.o";
@@ -64,5 +71,10 @@
                             return Build.VERSION_CODES.CUR_DEVELOPMENT;
                     }
                 });
+
+        LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+        LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
+        when(mUgmInternal.checkGrantUriPermission(
+                anyInt(), anyString(), any(Uri.class), anyInt(), anyInt())).thenReturn(-1);
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e7a8b58..a0d145d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -69,6 +69,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
+import android.app.IUriGrantsManager;
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.UsageStatsManagerInternal;
@@ -112,11 +113,13 @@
 
 import com.android.internal.R;
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.server.LocalServices;
 import com.android.server.UiServiceTestCase;
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
 import com.android.server.notification.NotificationManagerService.NotificationAssistants;
 import com.android.server.notification.NotificationManagerService.NotificationListeners;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import org.junit.After;
 import org.junit.Before;
@@ -187,6 +190,10 @@
     IBinder mPermOwner;
     @Mock
     IActivityManager mAm;
+    @Mock
+    IUriGrantsManager mUgm;
+    @Mock
+    UriGrantsManagerInternal mUgmInternal;
 
     // Use a Testable subclass so we can simulate calls from the system without failing.
     private static class TestableNotificationManagerService extends NotificationManagerService {
@@ -233,6 +240,9 @@
                 Secure.NOTIFICATION_BADGING, 1,
                 UserHandle.getUserHandleForUid(mUid).getIdentifier());
 
+        LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+        LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
+
         mService = new TestableNotificationManagerService(mContext);
 
         // Use this testable looper.
@@ -251,7 +261,7 @@
         when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
-        when(mAm.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
+        when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
 
         // write to a test file; the system file isn't readable from tests
         mFile = new File(mContext.getCacheDir(), "test.xml");
@@ -283,7 +293,7 @@
                     mListeners, mAssistants, mConditionProviders,
                     mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
                     mGroupHelper, mAm, mAppUsageStats,
-                    mock(DevicePolicyManagerInternal.class));
+                    mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal);
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
@@ -2578,13 +2588,14 @@
                 PKG, PKG, 0, "tag", mUid, 0, nbA.build(), new UserHandle(mUid), null, 0), c);
 
         // First post means we grant access to both
-        reset(mAm);
-        when(mAm.newUriPermissionOwner(any())).thenReturn(new Binder());
+        reset(mUgm);
+        reset(mUgmInternal);
+        when(mUgmInternal.newUriPermissionOwner(any())).thenReturn(new Binder());
         mService.updateUriPermissions(recordA, null, mContext.getPackageName(),
                 UserHandle.USER_SYSTEM);
-        verify(mAm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+        verify(mUgm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
                 eq(message1.getDataUri()), anyInt(), anyInt(), anyInt());
-        verify(mAm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+        verify(mUgm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
                 eq(message2.getDataUri()), anyInt(), anyInt(), anyInt());
 
         Notification.Builder nbB = new Notification.Builder(mContext, c.getId())
@@ -2595,24 +2606,24 @@
                 PKG, 0, "tag", mUid, 0, nbB.build(), new UserHandle(mUid), null, 0), c);
 
         // Update means we drop access to first
-        reset(mAm);
+        reset(mUgmInternal);
         mService.updateUriPermissions(recordB, recordA, mContext.getPackageName(),
                 UserHandle.USER_SYSTEM);
-        verify(mAm, times(1)).revokeUriPermissionFromOwner(any(), eq(message1.getDataUri()),
-                anyInt(), anyInt());
+        verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(any(),
+                eq(message1.getDataUri()), anyInt(), anyInt());
 
         // Update back means we grant access to first again
-        reset(mAm);
+        reset(mUgm);
         mService.updateUriPermissions(recordA, recordB, mContext.getPackageName(),
                 UserHandle.USER_SYSTEM);
-        verify(mAm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+        verify(mUgm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
                 eq(message1.getDataUri()), anyInt(), anyInt(), anyInt());
 
         // And update to empty means we drop everything
-        reset(mAm);
+        reset(mUgmInternal);
         mService.updateUriPermissions(null, recordB, mContext.getPackageName(),
                 UserHandle.USER_SYSTEM);
-        verify(mAm, times(1)).revokeUriPermissionFromOwner(any(), eq(null),
+        verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(any(), eq(null),
                 anyInt(), anyInt());
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index bd6416d..6be633e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -62,6 +62,7 @@
 import com.android.internal.R;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.UiServiceTestCase;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -646,7 +647,8 @@
     @Test
     public void testCalculateGrantableUris_PappProvided() throws RemoteException {
         IActivityManager am = mock(IActivityManager.class);
-        when(am.checkGrantUriPermission(anyInt(), eq(null), any(),
+        UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
+        when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
                 anyInt(), anyInt())).thenThrow(new SecurityException());
 
         Notification n = mock(Notification.class);
@@ -655,6 +657,7 @@
                 new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid);
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
         record.mAm = am;
+        record.mUgmInternal = ugm;
 
         try {
             record.calculateGrantableUris();
@@ -667,8 +670,9 @@
     @Test
     public void testCalculateGrantableUris_PuserOverridden() throws RemoteException {
         IActivityManager am = mock(IActivityManager.class);
-        when(am.checkGrantUriPermission(anyInt(), eq(null), any(),
-                anyInt(), anyInt())).thenThrow(SecurityException.class);
+        UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
+        when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
+                anyInt(), anyInt())).thenThrow(new SecurityException());
 
         channel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
         Notification n = mock(Notification.class);
@@ -684,8 +688,9 @@
     @Test
     public void testCalculateGrantableUris_prePappProvided() throws RemoteException {
         IActivityManager am = mock(IActivityManager.class);
-        when(am.checkGrantUriPermission(anyInt(), eq(null), any(),
-                anyInt(), anyInt())).thenThrow(SecurityException.class);
+        UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class);
+        when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class),
+                anyInt(), anyInt())).thenThrow(new SecurityException());
 
         Notification n = mock(Notification.class);
         when(n.getChannelId()).thenReturn(channel.getId());
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 01f5d9c..57729b5 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -30,6 +30,7 @@
 import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
+import android.app.UriGrantsManager;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.content.ClipData;
@@ -62,6 +63,7 @@
 import com.android.server.am.AssistDataRequester;
 import com.android.server.am.AssistDataRequester.AssistDataRequesterCallbacks;
 import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -82,6 +84,7 @@
     final int mCallingUid;
     final Handler mHandler;
     final IActivityManager mAm;
+    final UriGrantsManagerInternal mUgmInternal;
     final IWindowManager mIWindowManager;
     final AppOpsManager mAppOps;
     final IBinder mPermissionOwner;
@@ -141,19 +144,15 @@
         mCallingUid = callingUid;
         mHandler = handler;
         mAm = ActivityManager.getService();
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         mAppOps = context.getSystemService(AppOpsManager.class);
         mAssistDataRequester = new AssistDataRequester(mContext, mIWindowManager,
                 (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE),
                 this, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
-        IBinder permOwner = null;
-        try {
-            permOwner = mAm.newUriPermissionOwner("voicesession:"
+        final IBinder permOwner = mUgmInternal.newUriPermissionOwner("voicesession:"
                     + component.flattenToShortString());
-        } catch (RemoteException e) {
-            Slog.w("voicesession", "AM dead", e);
-        }
         mPermissionOwner = permOwner;
         mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
         mBindIntent.setComponent(mSessionComponentName);
@@ -303,13 +302,14 @@
         long ident = Binder.clearCallingIdentity();
         try {
             // This will throw SecurityException for us.
-            mAm.checkGrantUriPermission(srcUid, null, ContentProvider.getUriWithoutUserId(uri),
-                    mode, ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid)));
+            mUgmInternal.checkGrantUriPermission(srcUid, null,
+                    ContentProvider.getUriWithoutUserId(uri), mode,
+                    ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid)));
             // No security exception, do the grant.
             int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser);
             uri = ContentProvider.getUriWithoutUserId(uri);
-            mAm.grantUriPermissionFromOwner(mPermissionOwner, srcUid, destPkg,
-                    uri, FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser);
+            UriGrantsManager.getService().grantUriPermissionFromOwner(mPermissionOwner, srcUid,
+                    destPkg, uri, FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser);
         } catch (RemoteException e) {
         } catch (SecurityException e) {
             Slog.w(TAG, "Can't propagate permission", e);
@@ -352,12 +352,8 @@
                     } catch (RemoteException e) {
                     }
                 }
-                try {
-                    mAm.revokeUriPermissionFromOwner(mPermissionOwner, null,
-                            FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION,
-                            mUser);
-                } catch (RemoteException e) {
-                }
+                mUgmInternal.revokeUriPermissionFromOwner(mPermissionOwner, null,
+                        FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION, mUser);
                 if (mSession != null) {
                     try {
                         ActivityTaskManager.getService().finishVoiceTask(mSession);