content: /*
   * 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 libcore.io.IoUtils;
  
  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;
  
  /** 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);
      }
  
      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> getUriPermissions(
              String packageName, boolean incoming, boolean persistedOnly) {
          enforceNotIsolatedCaller("getUriPermissions");
          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)
                                  && (!persistedOnly || 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)
                                  && (!persistedOnly || 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 = getPmInternal().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 = getPmInternal().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())
                      || "com.android.settings.module_licenses".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
                  // 3. Sharing module license files from Settings 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;
              }
          }
  
          // Figure out the value returned when access is allowed
          final int allowedResult;
          if ((modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0
                  || pi.forceUriPermissions) {
              // If we're extending a persistable grant or need to force, then we need to return
              // "targetUid" so that we always create a grant data structure to
              // support take/release APIs
              allowedResult = targetUid;
          } else {
              // Otherwise, we can return "-1" to indicate that no grant data
              // structures need to be created
              allowedResult = -1;
          }
  
          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 allowedResult;
              }
          } 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 allowedResult;
              }
          }
  
          /* 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 = targetUid >= 0
                  && 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);
              }
          }
      }
  
      private PackageManagerInternal getPmInternal() {
          // Don't need to synchonize; worst-case scenario LocalServices will be called twice.
          if (mPmInternal == null) {
              mPmInternal = LocalServices.getService(PackageManagerInternal.class);
          }
          return mPmInternal;
      }
  
      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)");
                  }
              }
          }
      }
  }
  