ContentProvider-访问过程

Post on Oct 08, 2022 by Wei Lin

query()调用过程

Content Provider之间互相通信的源码浅析—CSDN

总过程

APP端:

Context.getContentResolver().query(uri, null, null, null, null) ->
ContentResolver.query(6 args) ->
ContentResolver.query(4 args) ->
ContentResolver.acquireUnstableProvider() ->
ApplicationContentResolver.acquireUnstableProvider() ->
ActivityThread.acquireProvider()

AMS端:

AMS.getContentProvider() ->
ContentProviderHelper.getContentProvider() ->
ContentProviderHelper.getContentProviderImpl() ->
ContentProviderRecord.newHolder()

APP端在ActivityThread.acquireProvider()中获取到IContentProvider对象,返回到ContentResolver.query(4 args)中,之后便可以通过该对象实现对数据的增删改查。

IContentProvider.query() ->
ContentProvider.Transport.query() ->
ContentInterface.query() ->
MyContentProvider.query()

通过ContentInterface.query()调用到目标ContentProvider自定义的query方法。

getContentResolver().query()

用户APP端调用接口查询:

Uri uri = Uri.parse("content://com.demoapp.contentproviderdemo.provider/book");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);

getContentResolver()表示从当前context中获取一个ContentResolver对象,实际上是Context.getContentResolver(),通过该对象提供的接口进行查询。

Context.getContentResolver()的调用过程如下:

ContextWrapper.getContentResolver() ->
ContextImpl.getContentResolver()

返回一个ApplicationContentResolver对象:

private final ApplicationContentResolver mContentResolver;
mContentResolver = new ApplicationContentResolver(this, mainThread);
public ContentResolver getContentResolver() {
    return mContentResolver;
}

而ApplicationContentResolver继承于ContentResolver。

private static final class ApplicationContentResolver extends ContentResolver {
    //......
    public ApplicationContentResolver(Context context, ActivityThread mainThread) {
        super(context);
        mMainThread = Objects.requireNonNull(mainThread);
    }
    //......
}

ContentResolver.query(4 args)

ContentResolver.query(5 args) ->
ContentResolver.query(6 args) ->
ContentResolver.query(4 args)

在ContentResolver.query(4 args)中,会依次请求不稳定的Provider连接与稳定的Provider连接。其中涉及到了一个unstableProvider和两个stableProvider,过程如下:

  • 首先定义IContentProvider unstableProvider和IContentProvider stableProvide在query()的函数内引用;
  • 获取到unstableProvider,并通过unstableProvider获取到qCursor;
  • 如果获取qCursor失败(发生异常),则通过IContentProvider stableProvide应用获取稳定连接(在catch块内);
  • 如果未获取到stableProvide,则最后也要通过acquireProvider(uri)获取,由try{}内的局部变量IContentProvider provider持有引用;
  • 最后在finally{}中稳定连接和不稳定连接的计数均减1(注意函数内执行的不是释放连接,而是相关连接数=0后才释放,实际上是将连接数-1)。
@Override
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
        @Nullable String[] projection, @Nullable Bundle queryArgs,
        @Nullable CancellationSignal cancellationSignal) {
    Objects.requireNonNull(uri, "uri");

    //...

    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return null;
    }
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
        long startTime = SystemClock.uptimeMillis();

        ICancellationSignal remoteCancellationSignal = null;
        if (cancellationSignal != null) {
            cancellationSignal.throwIfCanceled();
            remoteCancellationSignal = unstableProvider.createCancellationSignal();
            cancellationSignal.setRemote(remoteCancellationSignal);
        }
        try {
            qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection,
                    queryArgs, remoteCancellationSignal);
        } 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) {
                return null;
            }
            qCursor = stableProvider.query(mContext.getAttributionSource(), uri, projection,
                    queryArgs, remoteCancellationSignal);
        }
        if (qCursor == null) {
            return null;
        }

        // Force query execution.  Might fail and throw a runtime exception here.
        qCursor.getCount();
        long durationMillis = SystemClock.uptimeMillis() - startTime;
        maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);

        // Wrap the cursor object into CursorWrapperInner object.
        final IContentProvider provider = (stableProvider != null) ? stableProvider
                : acquireProvider(uri);
        final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
        stableProvider = null;
        qCursor = null;
        return wrapper;
    } catch (RemoteException e) {
        // Arbitrary and not worth documenting, as Activity
        // Manager will kill this process shortly anyway.
        return null;
    } finally {
        if (qCursor != null) {
            qCursor.close();
        }
        if (cancellationSignal != null) {
            cancellationSignal.setRemote(null);
        }
        if (unstableProvider != null) {
            releaseUnstableProvider(unstableProvider);
        }
        if (stableProvider != null) {
            releaseProvider(stableProvider);
        }
    }
}

(1) 获取unstable连接

​ 首先通过acquireUnstableProvider(uri)获取一个IContentProvider(即对端APP的ContentProvider的Binder引用),然后再通过IContentProvider.query()返回Cursor,如果Cursor==null,则表示查询对象不存在,则return null。

(2) 获取stable连接

​ 如果unstable连接连接是正常的,则建立一个stable连接。由于不稳定连接第一次已与客户端建立连接,所以第二次访问的不稳定连接直接在本地查询即可。真正访问数据的其实是stable连接。

接下来主要看IContentProvider unstableProvider的获取过程。

获取UnstableProvider

ContentResolver.acquireUnstableProvider(uri)

public final IContentProvider acquireUnstableProvider(Uri uri) {
    if (!SCHEME_CONTENT.equals(uri.getScheme())) {
        return null;
    }
    String auth = uri.getAuthority();
    if (auth != null) {
        return acquireUnstableProvider(mContext, uri.getAuthority());
    }
    return null;
}

然后调用ContentResolver的子类ApplicationContentResolver的acquireUnstableProvider()。

ACR.acquireUnstableProvider(c,auth)

ACR=ApplicationContentResolver

protected IContentProvider acquireUnstableProvider(Context c, String auth) {
    return mMainThread.acquireProvider(c,
            ContentProvider.getAuthorityWithoutUserId(auth),
            resolveUserIdFromAuthority(auth), false);
}

AT.acquireProvider(c,auth,false)

  • AT.acquireExistingProvider()在mProviderMap中查询已存在的provider,如果存在,返回并增加引用计数。
  • AMS.getContentProvider()获取目标ContentProvider的ContentProviderHolder对象。
  • installProvider()将该ContentProvider与客户端进程关联起来,并添加引用计数。
public final IContentProvider acquireProvider(
        Context c, String auth, int userId, boolean stable) {
    final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
    if (provider != null) {
        return provider;
    }

    // There is a possible race here.  Another thread may try to acquire
    // the same provider at the same time.  When this happens, we want to ensure
    // that the first one wins.
    // Note that we cannot hold the lock while acquiring and installing the
    // provider since it might take a long time to run and it could also potentially
    // be re-entrant in the case where the provider is in the same process.
    ContentProviderHolder holder = null;
    try {
        synchronized (getGetProviderLock(auth, userId)) {
            holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
        }
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    if (holder == null) {
        if (UserManager.get(c).isUserUnlocked(userId)) {
            Slog.e(TAG, "Failed to find provider info for " + auth);
        } else {
            Slog.w(TAG, "Failed to find provider info for " + auth + " (user not unlocked)");
        }
        return null;
    }

    // Install provider will increment the reference count for us, and break
    // any ties in the race.
    holder = installProvider(c, holder, holder.info,
            true /*noisy*/, holder.noReleaseNeeded, stable);
    return holder.provider;
}

AMS.getContentProvider()

public final ContentProviderHolder getContentProvider(
        IApplicationThread caller, String callingPackage, String name, int userId,
        boolean stable) {
    enforceNotIsolatedCaller("getContentProvider");
    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 && 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);
}

ContentProviderHelper.getContentProvider()

ContentProviderHolder getContentProvider(IApplicationThread caller, String callingPackage,
        String name, int userId, boolean stable) {
    mService.enforceNotIsolatedCaller("getContentProvider");
    if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
        // TODO(b/226318628): for sdk sandbox processes only allow accessing CPs registered by
        //  the WebView apk.
        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);
}

ContentProviderHelper.getContentProviderImpl()

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, int callingUid, String callingPackage, String callingTag,
        boolean stable, int userId) {...}

getContentProviderImpl()中代码很多,有很多判断流程,这里暂不详细说明。

如果Provider所在进程未启动,则在此处启动进程。

proc = mService.startProcessLocked(
        cpi.processName, cpr.appInfo, false, 0,
        new HostingRecord(HostingRecord.HOSTING_TYPE_CONTENT_PROVIDER,
            new ComponentName(
                    cpi.applicationInfo.packageName, cpi.name)),
        Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false);
checkTime(startTime, "getContentProviderImpl: after start process");
if (proc == null) {
    Slog.w(TAG, "Unable to launch app "
            + cpi.applicationInfo.packageName + "/"
            + cpi.applicationInfo.uid + " for provider " + name
            + ": process is bad");
    return null;
}

ContentProviderRecord.newHolder()

返回一个ContentProviderHolder对象。

ContentProviderHolder中保存了ContentProvider的相关信息,它含有ProviderInfo(包含了contentProvider的授权URI,读写权限等信息),ContentProvider的引用,IBinder(这其实是一个服务端的ContentProviderConnection对象,其继承了BInder,用来作为客户端与服务端的链接)

获取stableProvider

ContentResolver.acquireProvider(uri)

public final IContentProvider acquireProvider(Uri uri) {
    if (!SCHEME_CONTENT.equals(uri.getScheme())) {
        return null;
    }
    final String auth = uri.getAuthority();
    if (auth != null) {
        return acquireProvider(mContext, auth);
    }
    return null;
}

ACR.acquireProvider(c,auth)

ACR=ApplicationContentResolver

protected IContentProvider acquireProvider(Context context, String auth) {
    return mMainThread.acquireProvider(context,
            ContentProvider.getAuthorityWithoutUserId(auth),
            resolveUserIdFromAuthority(auth), true);
}

AT.acquireProvider(c,auth,true)

​ 由于之前已通过UnstableProvider将Provider添加到本地,所以在本地记录中搜索并返回provider引用即可。

final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
    return provider;
}

AT.acquireExistingProvider()

在mProviderMap中搜索provider记录。

public final IContentProvider acquireExistingProvider(
        Context c, String auth, int userId, boolean stable) {
    synchronized (mProviderMap) {
        final ProviderKey key = new ProviderKey(auth, userId);
        final ProviderClientRecord pr = mProviderMap.get(key);
        if (pr == null) {
            return null;
        }

        IContentProvider provider = pr.mProvider;
        IBinder jBinder = provider.asBinder();
        if (!jBinder.isBinderAlive()) {
            // The hosting process of the provider has died; we can't
            // use this one.
            Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
                    + ": existing object's process dead");
            handleUnstableProviderDiedLocked(jBinder, true);
            return null;
        }

        // Only increment the ref count if we have one.  If we don't then the
        // provider is not reference counted and never needs to be released.
        ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
        if (prc != null) {
            incProviderRefLocked(prc, stable);
        }
        return provider;
    }
}

查询数据

IContentProvider.query()

函数返回到ContentResolver.query()中,继续向下执行IContentProvider.query().

IContentProvider的实现位于ContentProvider.java中的内部类Transport。在这里调用mInterface.query(),mInterface是一个ContentInterface接口,由ContentProvider实现。

class Transport extends ContentProviderNative {
    volatile AppOpsManager mAppOpsManager = null;
    volatile int mReadOp = AppOpsManager.OP_NONE;
    volatile int mWriteOp = AppOpsManager.OP_NONE;
    volatile ContentInterface mInterface = ContentProvider.this;

    ContentProvider getContentProvider() {
        return ContentProvider.this;
    }

    @Override
    public String getProviderName() {
        return getContentProvider().getClass().getName();
    }

    @Override
    public Cursor query(String callingPkg, @Nullable String attributionTag, Uri uri,
                        @Nullable String[] projection, @Nullable Bundle queryArgs,
                        @Nullable ICancellationSignal cancellationSignal) {
        uri = validateIncomingUri(uri);
        uri = maybeGetUriWithoutUserId(uri);
        if (enforceReadPermission(callingPkg, attributionTag, uri, null)
            != AppOpsManager.MODE_ALLOWED) {
            // The caller has no access to the data, so return an empty cursor with
            // the columns in the requested order. The caller may ask for an invalid
            // column and we would not catch that but this is not a problem in practice.
            // We do not call ContentProvider#query with a modified where clause since
            // the implementation is not guaranteed to be backed by a SQL database, hence
            // it may not handle properly the tautology where clause we would have created.
            if (projection != null) {
                return new MatrixCursor(projection, 0);
            }

            // Null projection means all columns but we have no idea which they are.
            // However, the caller may be expecting to access them my index. Hence,
            // we have to execute the query as if allowed to get a cursor with the
            // columns. We then use the column names to return an empty cursor.
            Cursor cursor;
            final Pair<String, String> original = setCallingPackage(
                new Pair<>(callingPkg, attributionTag));
            try {
                cursor = mInterface.query(
                    uri, projection, queryArgs,
                    CancellationSignal.fromTransport(cancellationSignal));
            } catch (RemoteException e) {
                throw e.rethrowAsRuntimeException();
            } finally {
                setCallingPackage(original);
            }
            if (cursor == null) {
                return null;
            }

            // Return an empty cursor for all columns.
            return new MatrixCursor(cursor.getColumnNames(), 0);
        }
        Trace.traceBegin(TRACE_TAG_DATABASE, "query");
        final Pair<String, String> original = setCallingPackage(
            new Pair<>(callingPkg, attributionTag));
        try {
            return mInterface.query(
                uri, projection, queryArgs,
                CancellationSignal.fromTransport(cancellationSignal));
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        } finally {
            setCallingPackage(original);
            Trace.traceEnd(TRACE_TAG_DATABASE);
        }
    }
    //......
}

ContentInterface.query()

ContentInterface接口由ContentProvider实现。

public abstract class ContentProvider implements ContentInterface, ComponentCallbacks2 {...}

这里实际调用的是用户自定义的ContentProvider。

MyContentProvider.query()

自定义的ContentProvider。

@Override
public Cursor query(Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder) {
    Log.d(TAG, "query Excuted");

    SQLiteDatabase db = dbHelper.getReadableDatabase();
    Cursor cursor = null;
    switch (uriMatcher.match(uri)) {
        //...
        default:
            break;
    }
    return cursor;
}

其它调用过程

总结

  • ActivityThread.acquireProvider()

无论增删改查,都要获取到目标Provider,所以都要走到ActivityThread.acquireProvider(context, auth, userId, stable)这一步。

  • 抛出异常

查询是通过不稳定连接进行的,增删改获取的是稳定连接。稳定连接如果获取Provider对象失败,会抛出异常。

IContentProvider provider = acquireProvider(url);
if (provider == null) {
    throw new IllegalArgumentException("Unknown URL " + url);
}

添加数据insert()

APP端用户调用:

getContentResolver().insert(uri, values);

APP端-Framework过程:

ContentResolver.insert(2 args) ->
ContentResolver.insert(3 args) ->
ContentResolver.acquireProvider(auth) ->
ContextImpl.ApplicationContentResolver.acquireProvider(context,auth) ->
ActivityThread.acquireProvider()

AMS端:

AMS.getContentProvider() ->
ContentProviderHelper.getContentProvider() ->
ContentProviderHelper.getContentProviderImpl() ->
ContentProviderRecord.newHolder()

APP端在ActivityThread.acquireProvider()中获取到IContentProvider对象,返回到ContentResolver.insert(3 args)中,之后便可以通过该对象实现对数据的增删改查。

IContentProvider.insert() ->
ContentProvider.Transport.insert() ->
ContentInterface.insert() ->
MyContentProvide.insert()

通过ContentInterface.insert()回调到目标ContentProvider自定义的query方法。

删除数据delete()

APP端用户调用:

getContentResolver().delete(uri, where, selectionArgs);

APP端-Framework过程:

ContentResolver.delete(3 args) ->
ContentResolver.delete(2 args) ->
ContentResolver.acquireProvider(auth) ->
ContextImpl.ApplicationContentResolver.acquireProvider(context,auth) ->
ActivityThread.acquireProvider()

其余过程同上。

修改数据update()

APP端用户调用:

getContentResolver().update(uri, values, where, selectionArgs);

APP端-Framework过程:

ContentResolver.update(4 args) ->
ContentResolver.update(3 args) ->
ContentResolver.acquireProvider(auth) ->
ContextImpl.ApplicationContentResolver.acquireProvider(context,auth) ->
ActivityThread.acquireProvider()

其余过程同上。

call()调用

APP端用户调用:

getContentResolver().call(auth, method, arg, extras);

APP端-Framework过程:

ContentResolver.call() ->
ContentResolver.acquireProvider(auth) ->
ContextImpl.ApplicationContentResolver.acquireProvider(context,auth) ->
ActivityThread.acquireProvider()

其余过程同上。

其它

CPH.getContentProviderImpl()流程图

CPH=ContentProviderHelper

ContentProvider访问过程1.svg

unstableProvider与stableProvide的区别

具体在请求过程中的区别见getContentResolver().query(),另外Provider所在进程被杀后,对客户端进程的处理也有所区别。

通过forceStopPackage()来杀掉 Provider所在进程的流程如下:

AMS.forceStopPackage() ->
AMS.forceStopPackageLocked() ->
AMS.forceStopPackageLocked() ->
CPH.removeDyingProviderLocked()

在removeDyingProviderLocked()中:

ProcessRecord capp = conn.client;
final IApplicationThread thread = capp.getThread();
// 如果有通过stable Provider连接的客户端
if (conn.stableCount() > 0) {
    final int pid = capp.getPid();
    if (!capp.isPersistent() && thread != null
            && pid != 0 && pid != ActivityManagerService.MY_PID) {
        // 杀死客户端进程
        capp.killLocked(
                "depends on provider " + cpr.name.flattenToShortString()
                        + " in dying proc " + (proc != null ? proc.processName : "??")
                        + " (adj " + (proc != null ? proc.mState.getSetAdj() : "??") + ")",
                ApplicationExitInfo.REASON_DEPENDENCY_DIED,
                ApplicationExitInfo.SUBREASON_UNKNOWN,
                true);
    }
} else if (thread != null && conn.provider.provider != null) {
    // 如果只有通过unstable Provider连接的客户端
    try {
        // 则只是通知客户端,不会杀死客户端
        thread.unstableProviderDied(conn.provider.provider.asBinder());
    } catch (RemoteException e) {
    }
    // In the protocol here, we don't expect the client to correctly
    // clean up this connection, we'll just remove it.
    cpr.connections.remove(i);
    if (cpr.proc != null && !hasProviderConnectionLocked(cpr.proc)) {
        cpr.proc.mProfile.clearHostingComponentType(HOSTING_COMPONENT_TYPE_PROVIDER);
    }
    if (conn.client.mProviders.removeProviderConnection(conn)) {
        mService.stopAssociationLocked(capp.uid, capp.processName,
                cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
    }
}

所以区别就是:

  • 对于stableProvider,如果provider所在进程被杀,则客户端进程也会被杀;
  • 对于unstableProvider,如果provider所在进程被杀,则客户端只是移除Provider连接,并不会被杀掉。

​ 在执行query()的时候使用的是unstableProvider,但是返回给调用方的Curosr 使用的是stableProvider。这表示在 Cursor 没有被 close 之前,只要 provider所在进程死亡,则客户端进程也会被杀掉。

为什么要区分是否稳定连接?

ContentResolver只会在query()时使用unstableProvider,其它接口的调用inser()、delete()、update()、call()都只是使用stableProvider。

之所以只在query()中使用unstableProvider,是因为只是查询数据,并不涉及写入,而手机中query()的调用也是最多的,所以需要先通过unstableProvider查看一下目标数据是否存在,如果不存在客户端进程也不会crash,如果存在并能正常访问再建立stableProvider。

而如果像其它接口那样一开始就使用stableProvider,如果访问不到数据则会直接抛出异常,这对于使用较为频繁的query()来说是不可接受的。