ActivityManagerService: ContentProvider 答疑解惑

1,370 阅读5分钟

provider 实现多进程实例

前面我们总是隐隐约约地提到,provider 可以安装在客户端进程,那么什么样的条件下,provider 可以安装在客户端进程中? 前面一篇文章的分析中有提到过,现在展示出部分代码

// ContentProviderHelper.java

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, int callingUid, String callingPackage, String callingTag,
        boolean stable, int userId) {
    // ...
   
    synchronized (mService) {
        // 获取客户端的进程实例
        ProcessRecord r = null;
        if (caller != null) {
            r = mService.getRecordForAppLOSP(caller);
            if (r == null) {
                throw new SecurityException("Unable to find app for caller " + caller
                        + " (pid=" + Binder.getCallingPid() + ") when getting content provider "
                        + name);
            }
        }

        // ...

        // provider 正在运行
        if (providerRunning) {
            cpi = cpr.info;

            if (r != null && cpr.canRunHere(r)) {
                // This provider has been published or is in the process
                // of being published...  but it is also allowed to run
                // in the caller's process, so don't make a connection
                // and just let the caller instantiate its own instance.
                ContentProviderHolder holder = cpr.newHolder(null, true);
                // don't give caller the provider object, it needs to make its own.
                holder.provider = null;
                return holder;
            }

            // ...
        }

        // provider 没有运行
        if (!providerRunning) {
            // ...

            if (r != null && cpr.canRunHere(r)) {
                // If this is a multiprocess provider, then just return its
                // info and allow the caller to instantiate it.  Only do
                // this if the provider is the same user as the caller's
                // process, or can run as root (so can be in any process).
                return cpr.newHolder(null, true);
            }
            
            // ...
        }

        // ...
    }

    // ...
}

可以看到,无论 provider 是否已经运行,都有机会在客户端进程中创建 provider 实例,而这个机会就在 ContentProviderRecord#canRunHere()

provider 已经运行,居然还可以运行在客户端进程中,也就是在客户端进程中创建 ContentProvider 实例,这样的设计又是为了什么呢?

public boolean canRunHere(ProcessRecord app) {
    // info 为 provider 信息,也就是在 AndroidManifest 中声明的 provider 信息
    // provider 可以 运行在客户端进程中的条件
    // 1. provider 所在的 app 的 uid 与客户端 app 的 uid 相同
    // 2. provider 支持多进程 或者 provider 的进程名与客户端 app 的进程名相同
    return (info.multiprocess || info.processName.equals(app.processName))
            && uid == app.info.uid;
}

这里的条件可要看清楚了,首先 provider 所在 app 和 客户端 app 的 uid 相同,其实就是下面这个玩意要一样

<manifest 
    android:sharedUserId="">

然后,还需要 provider 支持多进程,其实就是下面这个玩意

<provider 
    android:multiprocess="true"/>

如果 provider 不支持多进程,只要 provider 的进程名与客户端 app 的进程名一样,provider 也是可以运行在客户端进程中。那么 provider 进程名是什么呢? provider 可以声明自己的进程名,如下

<provider
    android:process=""
   />

而如果 provider 没有声明自己的进程名,那么 provider 进程名取自 app 的进程名。

现在 provider 怎样运行在客户端进程中,大家会玩了吗?如果会玩了,那么继续看下客户端如何安装 provider,这一次可就是真的安装 provider 了

private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    // 此时 holder.provider == null 是成立的
    if (holder == null || holder.provider == null) {
        // ...

        try {
            final java.lang.ClassLoader cl = c.getClassLoader();
            LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
            if (packageInfo == null) {
                // System startup case.
                packageInfo = getSystemContext().mPackageInfo;
            }
            // 1. 通过反射创建 ContentProvider 对象
            localProvider = packageInfo.getAppFactory()
                    .instantiateProvider(cl, info.name);
            // 获取 provider 接口,其实就是获取 provider binder
            provider = localProvider.getIContentProvider();
            if (provider == null) {
                return null;
            }
            // 2. 为 ContentProvider 对象保存 provider 信息,并且调用 ContentProvider#onCreate()
            localProvider.attachInfo(c, info);
        } catch (java.lang.Exception e) {
            // ...
        }
    } else {
        // ...
    }

    ContentProviderHolder retHolder;

    synchronized (mProviderMap) {
        if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
                + " / " + info.name);
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
            ComponentName cname = new ComponentName(info.packageName, info.name);
            ProviderClientRecord pr = mLocalProvidersByName.get(cname);
            if (pr != null) {
                // ...
            } else {
                // 本地创建 ContentProviderHolder
                holder = new ContentProviderHolder(info);
                // 保存 provider binder
                holder.provider = provider;
                // 本地安装的 provider,不需要释放
                holder.noReleaseNeeded = true;
                // 3. 创建 provider 记录,并保存
                pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                mLocalProviders.put(jBinder, pr);
                mLocalProvidersByName.put(cname, pr);
            }
            retHolder = pr.mHolder;
        } else {
            // ...
        }
    }
    return retHolder;
}

其实这一部分代码在前面文章中已经分析过,这里简单介绍下过程

  1. 客户端自己创建 ContentProvider 对象,然后保存 provider 信息,并调用 ContentProvider#onCreate() 方法。
  2. 创建 provider 记录,也就是 ContentProviderRecord 对象,然后用数据结构保存。

unstable provier 和 stable provider 区别

前面我们提到过 unstable provider 和 stable provider 的区别,现在我们用代码来解释下这两者的区别

假设我们通过 ActivityManager#forceStopPackage() 来杀掉 provider 进程,在 AMS 的调用如下

public void forceStopPackage(final String packageName, int userId) {
    // ...
    try {
        IPackageManager pm = AppGlobals.getPackageManager();
        synchronized(this) {
            int[] users = userId == UserHandle.USER_ALL
                    ? mUserController.getUsers() : new int[] { userId };
            for (int user : users) {
                // ...
                
                if (mUserController.isUserRunning(user, 0)) {
                    // 杀掉进程
                    forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
                    // 发送广播
                    finishForceStopPackageLocked(packageName, pkgUid);
                }
            }
        }
    } finally {
        Binder.restoreCallingIdentity(callingId);
    }
}

最终调用如下代码

final boolean forceStopPackageLocked(String packageName, int appId,
        boolean callerWillRestart, boolean purgeCache, boolean doit,
        boolean evenPersistent, boolean uninstalling, int userId, String reason) {
    // ...

    // 获取 app 的所有 provider
    ArrayList<ContentProviderRecord> providers = new ArrayList<>();
    if (mCpHelper.getProviderMap().collectPackageProvidersLocked(packageName, null, doit,
            evenPersistent, userId, providers)) {
        if (!doit) {
            return true;
        }
        didSomething = true;
    }
    
    // 移除 provider
    for (i = providers.size() - 1; i >= 0; i--) {
        mCpHelper.removeDyingProviderLocked(null, providers.get(i), true);
    }

    // ...
}

不出意外,最终由 ContentProviderHelper 来移除 provider

boolean removeDyingProviderLocked(ProcessRecord proc, ContentProviderRecord cpr,
        boolean always) {
    // ...

    for (int i = cpr.connections.size() - 1; i >= 0; i--) {
        ContentProviderConnection conn = cpr.connections.get(i);
        // ...
        ProcessRecord capp = conn.client;
        final IApplicationThread thread = capp.getThread();
        conn.dead = true;
        // 1. 如有 stable provider 的客户端
        if (conn.stableCount() > 0) {
            final int pid = capp.getPid();
            // 注意,要排除 persistent app 进程,以及 system_server 进程
            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);
            }
        } 
        // 2. 如果只有 unstable provider 客户端
        else if (thread != null && conn.provider.provider != null) {
            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 (conn.client.mProviders.removeProviderConnection(conn)) {
                mService.stopAssociationLocked(capp.uid, capp.processName,
                        cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
            }
        }
    }
    // ...
}

看到了吧,对于 stable provider,如果 provider 进程挂掉了,那么客户端也会受牵连被杀掉。

而对于 unstable provider,如果 provier 进程挂掉了,客户端只是移除了保存了的数据而已,并不会被杀掉。

最后,我们再来看看文章开头获取 provider 时关于两种 provider 代码

// ContentResolver.java

public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
        @Nullable String[] projection, @Nullable Bundle queryArgs,
        @Nullable CancellationSignal cancellationSignal) {
    // ...

    // 获取 unstable provider
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return null;
    }
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
        // ...

        // 注意,这里获取的 stable provider 并返回
        final IContentProvider provider = (stableProvider != null) ? stableProvider
                : acquireProvider(uri);
        final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
        stableProvider = null;
        qCursor = null;
        
        // 返回数据
        return wrapper;
    } catch (RemoteException e) {
        return null;
    } finally {
        // ...
    }
}

我们可以看到,查询的时候使用的是 unstable provier,但是返回的结果 Curosr 使用的是 stable provider。这说明了什么? 它说明了,在 Cursor 没有被 close 之前,只要 provider 进程挂掉了,那么客户端也会受牵连,会被杀掉。

TODO

provider 其实还有很多知识点没有解释,但是碍于时间的关系,我不可能把所有事情都解析出来,后续我会慢慢补充新的东西。