android activity grantUriPermission()方法执行流程

/ android / 没有评论 / 1826浏览

用到的代码文件

frameworks/base/core/java/:
android/content/ContextWrapper.java
android/content/Intent.java
android/app/ContextImpl.java

frameworks/base/services/core/java/:
com/android/server/am/ActivityManagerService.java
com/android/server/am/UriPermission.java

代码执行流程图

grantUriPermission() 查看原图

grantUriPermission()方法执行流程

下面的代码基于android O(api 27)。

本文基于MTK(MediaTek)平台基线。

本文主要是记录在Activity中调用grantUriPermission()方法的执行流程。
因为Activity继承自ContextWrapper,所以其实质为调用ContextWrapper中的grantUriPermission()方法。

下面进入ContextWrapper类中的grantUriPermission()方法。

@Override
public void grantUriPermission(String toPackage, 
    Uri uri, int modeFlags) {
    /*
    这里是我调用这个方法时传递的参数
    pkg: com.android.bluetooth
    uri: content://com.***.filemanager.fileProvider/
            external_storage/mtklog/file_tree.txt
    flags: 1 -> Intent.FLAG_GRANT_READ_URI_PERMISSION
    */
    log("method grantUriPermission() pkg: " + toPackage 
        + " uri: " + uri + " flags: " + modeFlags);
    /*
    mBase: android.app.ContextImpl
    */
    log("mBase: " + mBase.getClass().getName());
    mBase.grantUriPermission(toPackage, uri, modeFlags);
}

下面进入ContextImpl类中的grantUriPermission()方法。

@Override
public void grantUriPermission(String toPackage, 
        Uri uri, int modeFlags) {
     try {
          // 0
         log("resolveUserId(uri): " + resolveUserId(uri));
         // content://com.***.filemanager.fileProvider/
         //     external_storage/RobustTest/robust_log.txt
         log("ContentProvider.getUriWithoutUserId(uri): " 
            + ContentProvider.getUriWithoutUserId(uri).toString());
         // android.app.IActivityManager$Stub$Proxy
         log("ActivityManager.getService(): " 
            + ActivityManager.getService().getClass().getName());
        /*
          * 这里的实质就是跨进程通信了,ActivityManagerService (AMS)
          */
        ActivityManager.getService().grantUriPermission(
                mMainThread.getApplicationThread(), toPackage,
                ContentProvider.getUriWithoutUserId(uri), 
                modeFlags, resolveUserId(uri));
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

下面进入ActivityManagerService类中的grantUriPermission()方法。

/**
 * @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 grantUriPermission(IApplicationThread caller, 
        String targetPkg, Uri uri,final int modeFlags, 
        int userId) {
    log("method grantUriPermission()");
    /*
    caller: android.app.IApplicationThread$Stub$Proxy
    targetPkg: com.android.bluetooth
    uri: content://com.***.filemanager.fileProvider/
            external_storage/RobustTest/robust_log.txt
    modeFlags: 1
    userId: 0
    */
    log("caller: " + caller.getClass().getName());
    log("targetPkg: " + targetPkg);
    log("uri: " + uri);
    log("modeFlags: " + modeFlags);
    log("userId: " + userId);
    /*
      * 确保当前不是被孤立的进程来调用这个方法
      * 这个方法的实质就是通过uid来获取对应的app id并判断其是否合法
      */
    enforceNotIsolatedCaller("grantUriPermission");
    /*
      * GrantUri是定义在ActivityManagerService中的静态内部类
      */
    GrantUri grantUri = new GrantUri(userId, uri, false);
    /*
      * 确保uri permission相关逻辑线程安全
      * 因为这个操作任何应用都可以调用
      */
    synchronized(this) {
        //获取调用进程的ProcessRecord对象
        final ProcessRecord r = getRecordForAppLocked(caller);
        //ProcessRecord{4b47804 1458:com.android.bluetooth/1002}
        log("r: "+r);
        if (r == null) {
            throw new SecurityException("Unable to find app for caller "
                    + caller
                    + " when granting permission to uri " + grantUri);
        }
        if (targetPkg == null) {
            throw new IllegalArgumentException("null target");
        }
        if (grantUri == null) {
            throw new IllegalArgumentException("null uri");
        }
        /*
         检查权限标志位是否合法
         给uri设置的权限标志必须是这四种,不然抛出异常
         */
        Preconditions.checkFlagsArgument(modeFlags,
                Intent.FLAG_GRANT_READ_URI_PERMISSION
                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);

        grantUriPermissionLocked(r.uid, targetPkg, grantUri,
                modeFlags, null,
                UserHandle.getUserId(r.uid));
    }
}

下面进入ActivityManagerService类中的grantUriPermissionLocked()方法。

void grantUriPermissionLocked(int callingUid, String targetPkg, 
        GrantUri grantUri,final int modeFlags, 
        UriPermissionOwner owner, int targetUserId) {
    log("method grantUriPermissionLocked()");
    /*
    callingUid: 1002
    targetPkg: com.android.bluetooth
    grantUri: content://com.***.filemanager.fileProvider/
            external_storage/RobustTest/robust_log.txt [user 0]
    modeFlags: 1
    owner: null
    targetUserId: 0
    */
    log("callingUid: " + callingUid);
    log("targetPkg: " + targetPkg);
    log("grantUri: " + grantUri);
    log("modeFlags: " + modeFlags);
    log("owner: " + owner);
    log("targetUserId: " + targetUserId);
    if (targetPkg == null) {
        throw new NullPointerException("targetPkg");
    }
    int targetUid;
    final IPackageManager pm = AppGlobals.getPackageManager();
    //pm: com.android.server.pm.PackageManagerService
    log("pm: " + pm.getClass().getName());
    try {
        //使用PMS来查询当前包名对应的uid
        targetUid = pm.getPackageUid(targetPkg, 
              MATCH_DEBUG_TRIAGED_MISSING, targetUserId);
    } catch (RemoteException ex) {
        return;
    }
    log("targetUid1: " + targetUid);
    /*
      * 这个方法主要是检查当前uri要设置的权限是否已经被设置过了
      * 如果当前uri不允许设置这种权限那么就会抛出异常
      */
    targetUid = checkGrantUriPermissionLocked(callingUid, 
            targetPkg, grantUri, modeFlags,targetUid);
    log("targetUid2: " + targetUid);
    if (targetUid < 0) {
        return;
    }

    grantUriPermissionUncheckedLocked(targetUid, targetPkg, 
            grantUri, modeFlags,owner);
}

下面进入ActivityManagerService类中的grantUriPermissionUncheckedLocked()方法。

void grantUriPermissionUncheckedLocked(int targetUid, 
        String targetPkg, GrantUri grantUri,
        final int modeFlags, UriPermissionOwner owner) {
    log("method grantUriPermissionUncheckedLocked()");
    /*
      * 判断当前uri要设置的uri是否被允许使用
      * 检查read or write权限
      */
    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();
    //com.***.filemanager.fileProvider
    log("authority: " + authority);
    //获取uri对应的provider信息
    final ProviderInfo pi = getProviderInfoLocked(authority, 
            grantUri.sourceUserId,MATCH_DEBUG_TRIAGED_MISSING);
    //ContentProviderInfo{name=com.***.filemanager.fileProvider
    //className=android.support.v4.content.FileProvider}
    log("pi: " + pi);
    if (pi == null) {
        Slog.w(TAG, "No content provider found for grant: " 
        + grantUri.toSafeString());
        return;
    }
    // -> false
    if ((modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0) {
        log("set grant uri prefix true");
        grantUri.prefix = true;
    }
    /*
      * 进入这里
      */
    final UriPermission perm = findOrCreateUriPermissionLocked(
            pi.packageName, targetPkg, targetUid, grantUri);
    log("perm: " + perm);
    log("perm: " + perm.getClass().getName());
    /*
      * 再进入这里
      */
    perm.grantModes(modeFlags, owner);
}

下面进入ActivityManagerService类中的findOrCreateUriPermissionLocked()方法。

/**
 * 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}.
 *
 * 看这个注释就应该明白了。
 * 每个uri被封装为一个GrantUri对象,
 * 而每一个GrantUri对象对应一个UriPermission对象
 */
@GuardedBy("this")
private final SparseArray<ArrayMap<GrantUri, UriPermission>>
        mGrantedUriPermissions = 
            new SparseArray<ArrayMap<GrantUri, UriPermission>>();

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;
}

下面进入UriPermission类中的findOrCreateUriPermissionLocked()方法。

void grantModes(int modeFlags, UriPermissionOwner owner) {
    // modeFlags: 1 owner: null
    log("method grantModes() modeFlags: "+modeFlags+" owner: "+owner);
    final boolean persistable = (modeFlags &
            Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
    //persistable: false
    log("persistable: "+persistable);
    modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    //modeFlags: 1
    log("modeFlags: "+modeFlags);

    if (persistable) {
        persistableModeFlags |= modeFlags;
    }

    if (owner == null) {// -> true
        globalModeFlags |= modeFlags;
    } else {
        if ((modeFlags 
            & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
            addReadOwner(owner);
        }
        if ((modeFlags 
            & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
            addWriteOwner(owner);
        }
    }

    updateModeFlags();
}

下面进入UriPermission类中的updateModeFlags()方法。

private void updateModeFlags() {
    final int oldModeFlags = modeFlags;
    /*
      *更新下modeFlags的标志位就完了
      */
    modeFlags = ownedModeFlags | globalModeFlags 
            | persistableModeFlags | persistedModeFlags;
    if (Log.isLoggable(TAG, Log.VERBOSE) 
            && (modeFlags != oldModeFlags)) {
        Slog.d(TAG,
                "Permission for " + targetPkg + " to " + uri 
                + " is changing from 0x"
                + Integer.toHexString(oldModeFlags) + " to 0x"
                + Integer.toHexString(modeFlags) 
                + " via calling UID "
                + Binder.getCallingUid() + " PID " 
                + Binder.getCallingPid(),
                new Throwable());
    }
}

至此在Activity中调用grantUriPermission()方法的执行流程就执行完了。

执行流程总结

每个uri被封装为一个GrantUri对象,而每一个GrantUri对象对应一个UriPermission对象。
最终存储在ActivityManagerService中的mGrantedUriPermissions这个SparseArray对象中。

谁会检查?

1.ContentProvider.java中的enforceReadPermissionInner()方法。
2.ActivityManagerService.java中的checkContentProviderPermissionLocked()方法。
3.以后遇见了再补充......

后记

写这篇文章的原因是最近项目上遇见了一个Uri权限的问题,使用grantUriPermission()这个方法就能通过权限的检查。当时使用了就使用了并没有去深究其中的原因,但是不知道为什么不是我做事的态度,所以今天查看了源码,探究了其中的原因。

“知其然知其所以然”!