Provider超时

Post on Jan 12, 2023 by Wei Lin

ContentProvider Timeout

Base on: Android 12


ContentProvider 超时指的是ContentProvider发布时的超时,超时时间为CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s。ContentProvider 超时与Service、BroadcastQueue完全不同, 因为ContentProvider 的发布是在所在进程启动时进行的。

ContentProvider 超时比较少见。

发送延时消息

进程创建后,从主线程开始调用:

ActivityThread.main() -> 
ActivityThread.attach() -> 
AMS.attachApplication() -> 
AMS.attachApplicationLocked()


ContentProvider 的发布在AMS.attachApplicationLocked()中进行的。

private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
        int pid, int callingUid, long startSeq) {

    ProcessRecord app;
    long startTime = SystemClock.uptimeMillis();
    long bindApplicationTimeMillis;
    //......

boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
    //通过PMS查找应用在Manifest中注册的ContentProvider
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
    //如果该进程有ContentProvider,并且也存在待发布的ContentProviderRecord
    if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
        Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
        msg.obj = app;
        mHandler.sendMessageDelayed(msg,
                ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS);
    }
    //......
}

移除延时消息

在进程启动后,进入到进程的main方法,通过调用:

ActivityThread.main() ->
ActivityThread.attach() ->
AMS.attachApplication() ->
AMS.attachApplicationLocked()
ApplicationThread.bindApplication(){
    sendMessage(H.BIND_APPLICATION, data)
} ->
ActivityThread.handleBindApplication() ->
ActivityThread.installContentProviders() ->
AMS.publishContentProviders(){
    removeMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG)
}

移除延时消息是在AMS.publishContentProviders()中进行的,首先查询对应的ContentProviderRecord在所在进程中是否已存在,若已存在,则表示启动成功,然后从mLaunchingProviders列表中移除,再移除之前的延时消息,removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r)。


代码如下:

public final void publishContentProviders(IApplicationThread caller,
        List<ContentProviderHolder> providers) {
    if (providers == null) {
        return;
    }
    synchronized (this) {
        final ProcessRecord r = getRecordForAppLocked(caller);
        //......

        final int N = providers.size();
        for (int i = 0; i < N; i++) {
            //......
            //
            ContentProviderHolder src = providers.get(i);
            if (src == null || src.info == null || src.provider == null) {
                continue;
            }
            //查询ContentProvider所在进程中是否有对应的ContentProviderRecord
            ContentProviderRecord dst = r.pubProviders.get(src.info.name);
            if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
            if (dst != null) {  //查询到了进程提供的ContentProvider
                //......
                int launchingCount = mLaunchingProviders.size();
                int j;
                boolean wasInLaunchingProviders = false;
                for (j = 0; j < launchingCount; j++) {
                    //从mLaunchingProviders中移除已启动的ContentProvider
                    if (mLaunchingProviders.get(j) == dst) {
                        mLaunchingProviders.remove(j);
                        wasInLaunchingProviders = true;
                        j--;
                        launchingCount--;
                    }
                }
                if (wasInLaunchingProviders) {
                    mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
                }
                // ......
                //通知ContentProvider已经启动完成,结束等待
                synchronized (dst) {
                    dst.provider = src.provider;
                    dst.setProcess(r);
                    dst.notifyAll();
                }
                //......
            }
        }
    }
}

执行ANR流程

通过handleMessage执行CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息,AMS会kill掉已经启动的App, 然后根据需要决定是否重启App。执行栈为:

AMS.processContentProviderPublishTimedOutLocked() ->
ProcessList.removeProcessLocked() ->
ProcessRecord.kill()


void kill(String reason, @Reason int reasonCode, @SubReason int subReason, boolean noisy) {
    if (!killedByAm) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
        if (mService != null && (noisy || info.uid == mService.mCurOomAdjUid)) {
            mService.reportUidInfoMessageLocked(TAG,
                    "Killing " + toShortString() + " (adj " + setAdj + "): " + reason,
                    info.uid);
        }
        if (pid > 0) {
            mService.mProcessList.noteAppKill(this, reasonCode, subReason, reason);
            EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
            Process.killProcessQuiet(pid);
            ProcessList.killProcessGroup(uid, pid);
        } else {
            pendingStart = false;
        }
        if (!mPersistent) {
            killed = true;
            killedByAm = true;
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    }
}

注意:ContentProvider Timeout不会像input、Service 和Broadcast那样调用appNotResponding(),所以不会有ANR弹窗以及am_anr日志。