Resolver端
APP调用栈
(1) 用户调用代码
这里是一个简单的读取文本文件的代码,通过openFileDescriptor()开始访问目标FileProvider。
Uri uri = Uri.parse("content://com.demoapp.filedemo.fileprovider/share_name/myfile.txt");
ParcelFileDescriptor parcelFileDescriptor =
getContentResolver().openFileDescriptor(uri, "r");
FileReader reader = new FileReader(parcelFileDescriptor.getFileDescriptor());
BufferedReader bufferedReader = new BufferedReader(reader);
// 解析传来的数据,\A表示从字符串开头进行匹配
String res = new Scanner(bufferedReader).useDelimiter("\\A").next();
Log.d(TAG, res);
(2) APP端调用堆栈
ContentResolver.openFileDescriptor(uri, mode) ->
ContentResolver.openFileDescriptor(uri, mode, null) ->
ContentResolver.openAssetFileDescriptor() ->
ContentResolver.openTypedAssetFileDescriptor() ->
ContentResolver.acquireUnstableProvider(uri) ->
ContextImpl.ApplicationContentResolver.acquireUnstableProvider() ->
AT.acquireProvider()
CR.openFileDescriptor()
CR=ContentResolver
由用户调用CR.openFileDescriptor()请求文件并返回文件的ParcelFileDescriptor对象,ParcelFileDescriptor是一个可以跨进程程度的文件对象。
public final @Nullable ParcelFileDescriptor openFileDescriptor(@NonNull Uri uri,
@NonNull String mode) throws FileNotFoundException {
return openFileDescriptor(uri, mode, null);
}
public final @Nullable ParcelFileDescriptor openFileDescriptor(@NonNull Uri uri,
@NonNull String mode, @Nullable CancellationSignal cancellationSignal)
throws FileNotFoundException {
try {
if (mWrapped != null) return mWrapped.openFile(uri, mode, cancellationSignal);
} catch (RemoteException e) {
return null;
}
AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode, cancellationSignal);
if (afd == null) {
return null;
}
if (afd.getDeclaredLength() < 0) {
// This is a full file!
return afd.getParcelFileDescriptor();
}
// Client can't handle a sub-section of a file, so close what
// we got and bail with an exception.
try {
afd.close();
} catch (IOException e) {
}
throw new FileNotFoundException("Not a whole file");
}
CR.openAssetFileDescriptor()
在这里会通过传入的Uri获取到scheme,根据scheme的不同执行不同的流程,只有scheme为”android.resource”和”file”走特有的流程,其它scheme如”content”都走else流程。
对FileProvider:
- 如果mode=r,即只读,调用openTypedAssetFileDescriptor()
- 其它mode,调用IContentProvider.openAssetFile(),通过Binder直接调用到Provider端。
接下来只介绍一下只读情况。
CR.openTypedAssetFileDescriptor()
CR=ContentResolver
在ContentResolver.openTypedAssetFileDescriptor()中通过auth获取IContentProvider引用,通过该引用调用到Provider端,调用IContentProvider.openTypedAssetFile()
system_server端中的作用就是获取IContentProvider引用。
此步骤中主要进行:
- 首先获取不稳定的IContentProvider,即IContentProvider unstableProvider,它的作用主要是将远程Provider添加到本地,并判断目标对象是否存在;
- 如果通过unstableProvider判断目标对象存在,则使用stableProvider实际访问目标,获取到ParcelFileDescriptor pfd对象;
- 通过ParcelFileDescriptor pfd构造AssetFileDescriptor对象并返回给用户;
- 最后释放连接。
public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
@NonNull String mimeType, @Nullable Bundle opts,
@Nullable CancellationSignal cancellationSignal) throws FileNotFoundException {
Objects.requireNonNull(uri, "uri");
Objects.requireNonNull(mimeType, "mimeType");
try {
if (mWrapped != null) return mWrapped.openTypedAssetFile(uri, mimeType, opts, cancellationSignal);
} catch (RemoteException e) {
return null;
}
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
IContentProvider stableProvider = null;
AssetFileDescriptor fd = null;
try {
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
remoteCancellationSignal = unstableProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
fd = unstableProvider.openTypedAssetFile(
mContext.getAttributionSource(), uri, mimeType, opts,
remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
}
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
fd = stableProvider.openTypedAssetFile(
mContext.getAttributionSource(), uri, mimeType, opts,
remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
}
}
if (stableProvider == null) {
stableProvider = acquireProvider(uri);
}
releaseUnstableProvider(unstableProvider);
unstableProvider = null;
ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
fd.getParcelFileDescriptor(), stableProvider);
// Success! Don't release the provider when exiting, let
// ParcelFileDescriptorInner do that when it is closed.
stableProvider = null;
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
fd.getDeclaredLength(), fd.getExtras());
} catch (RemoteException e) {
// Whatever, whatever, we'll go away.
throw new FileNotFoundException(
"Failed opening content provider: " + uri);
} catch (FileNotFoundException e) {
throw e;
} finally {
if (cancellationSignal != null) {
cancellationSignal.setRemote(null);
}
if (stableProvider != null) {
releaseProvider(stableProvider);
}
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
}
}
获取Provider的Binder引用
ACR=ApplicationContentResolver,是ContextImpl的内部类
也就是获取到IContentProvider对象。
调用栈:
CR.acquireUnstableProvider(uri) ->
ACR.acquireUnstableProvider() ->
ActivityThread.acquireProvider() ->
AMS.getContentProvider()
这里比较主要的是ActivityThread.acquireProvider(),详情参考“ContentProvider发布与访问过程”。
system_server端
调用栈
AMS.getContentProvider() ->
ContentProviderHelper.getContentProvider() ->
ContentProviderHelper.getContentProviderImpl()
AMS.getContentProvider()
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String callingPackage, String name, int userId,
boolean stable) {
traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "getContentProvider: ", name);
try {
return mCpHelper.getContentProvider(caller, callingPackage, name, userId, stable);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
CPH.getContentProvider()
ContentProviderHolder getContentProvider(IApplicationThread caller, String callingPackage,
String name, int userId, boolean stable) {
mService.enforceNotIsolatedCaller("getContentProvider");
if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
Slog.w(TAG, "Sdk sandbox process " + Binder.getCallingUid()
+ " is accessing content provider " + name
+ ". This access will most likely be blocked in the future");
}
if (caller == null) {
String msg = "null IApplicationThread when getting content provider " + name;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
// The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
// with cross-user grant.
final int callingUid = Binder.getCallingUid();
if (callingPackage != null && mService.mAppOpsService.checkPackage(
callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
throw new SecurityException("Given calling package " + callingPackage
+ " does not match caller's uid " + callingUid);
}
return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
null, stable, userId);
}
CPH.getContentProviderImpl()
getContentProviderImpl()中代码很多,有很多判断流程,这里暂不详细说明。详情参考“ContentProvider发布与访问过程”。
权限检查
调用栈:
CPH.getContentProviderImpl() ->
CPH.checkAssociationAndPermissionLocked() ->
CPH.checkContentProviderPermission() ->
AMS.checkComponentPermission() ->
ActivityManager.checkComponentPermission()
详情参考:ContentProvider权限检查过程
Provider端
调用栈
通过Binder调用到FileProvider.openFile(uri, mode),调用栈如下:
Native ->
Binder.execTransact() ->
Binder.execTransactInternal()
ContentProviderNative.onTransact(){
//...
case OPEN_TYPED_ASSET_FILE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
AttributionSource attributionSource = AttributionSource.CREATOR
.createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
String mimeType = data.readString();
Bundle opts = data.readBundle();
ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
AssetFileDescriptor fd;
fd = openTypedAssetFile(attributionSource, url, mimeType, opts, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
fd.writeToParcel(reply,
Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
//...
} ->
ContentProvider.Transport.openTypedAssetFile() ->
ContentProvider.openTypedAssetFile(4 args) ->
ContentProvider.openTypedAssetFile(3 args) ->
ContentProvider.openAssetFile() ->
FileProvider.openFile()
接下来主要看一些关键流程。
ContentProvider.openTypedAssetFile()
ContentProvider.Transport类:
public AssetFileDescriptor openTypedAssetFile(
@NonNull AttributionSource attributionSource, Uri uri, String mimeType,
Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
Bundle.setDefusable(opts, true);
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(attributionSource, uri, "r");
traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
return mInterface.openTypedAssetFile(
uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
最后调用到ContentProvider类:
public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
@NonNull String mimeTypeFilter, @Nullable Bundle opts,
@Nullable CancellationSignal signal) throws FileNotFoundException {
return openTypedAssetFile(uri, mimeTypeFilter, opts);
}
public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
@NonNull String mimeTypeFilter, @Nullable Bundle opts) throws FileNotFoundException {
if ("*/*".equals(mimeTypeFilter)) {
// If they can take anything, the untyped open call is good enough.
return openAssetFile(uri, "r");
}
String baseType = getType(uri);
if (baseType != null && ClipDescription.compareMimeTypes(baseType, mimeTypeFilter)) {
// Use old untyped open call if this provider has a type for this
// URI and it matches the request.
return openAssetFile(uri, "r");
}
throw new FileNotFoundException("Can't open " + uri + " as type " + mimeTypeFilter);
}
ContentProvider.openAssetFile()
public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode)
throws FileNotFoundException {
ParcelFileDescriptor fd = openFile(uri, mode);
return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null;
}
FileProvider.openFile()
最终调用到这里打开文件。
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
throws FileNotFoundException {
// ContentProvider has already checked granted permissions
final File file = mStrategy.getFileForUri(uri);
final int fileMode = modeToMode(mode);
return ParcelFileDescriptor.open(file, fileMode);
}
注:FileProvider并不是AOSP中的类,而是来自androidx.core.content.FileProvider。
权限检查
Native ->
Binder.execTransact() ->
Binder.execTransactInternal()
ContentProviderNative.onTransact() ->
ContentProvider.openTypedAssetFile() ->
ContentProvider.enforceFilePermission() ->
ContentProvider.enforceReadPermission() ->
ContentProvider.enforceReadPermissionInner()
详情参考:ContentProvider权限检查过程