ContentProvider--stable參數

先總結一下,在ActivityThread#acquireProvider(...)獲取IContentProvider時,stable參數不影響是否能取得這個IContentProvider。首先這個stable reCountf和unstable refCount在本地會保存一份,而且會保存在一個IBinder對象中,類型是ContentProviderConnection,在AMS中,ProcessRecord會保存該進程的所有ContentProviderConnection的Bp端。但是一般都是用到他們的字段成員,很少用到方法成員,而字段成員應該是Bp端和Bn端各保存一份的。

無論是使用ContentResolver的insert,delete,update,query,首先都會去acquireProvider(...)或者acquireUnstableProvider(...),而選擇Unstable還是Stable,取決於你是做什麼操作,就是選擇insert還是delete或者update和query。其中query首先是去acquireUnstableProvider,在向AMS獲取IContentProvider之前,會先在本地查詢是否有保存這個authority對應的IContentProvider,如果有則不用向AMS獲取。

insert,delete,update操作都是首先使用acquireProvider,就增加的是stable refCount。而只有query操作是例外,先是使用acquireProvider,如果使用獲取到的IContentProvider去做query操作時,發現拋DeadObjectException,即目標CP所在進程已死。那麼就使用acquireProvider重新獲取IContentProvider,並增加stable refCount。

在完成insert,query等方法時,都會去調用releaseProvider方法,而這個方法就涉及到refCount的操作,refCount又分爲stable和unstable。而releaseProvider並不一定會導致緩存的IContentProvider被remove和AMS中的ProcessRecord(客戶端進程)中和ContentProviderRecord(目標ContentProvider在AMS中的代表)中的ContentProviderConnection會被移除。主要要看stable refCount和unstable refCount是否都爲0,如果是則調用ActivityThread#completeRemoveProvider(...)

在本地沒有緩存IContentProvider時,從AMS中獲取到IContentProvider後,會進行installProvider操作。installProvider 中會增加refCount,操作refCount的方法是ActivityThread#incProviderRefLocked()。

在目標CP進程死後,AMS會去調用removeDyingProviderLocked,其中會根據ContentProviderConnection的stableCount和unstableCount處理引用該CP的進程。如果stableCount不爲0,則殺死該客戶進程。如果stableCount爲0,而unstableCount不爲0,則通過ApplicationThread#unstableProviderDied通知客戶進程,並從ContentProviderRecord中移除該ContentProviderConnection。

 

下面開始分析源碼:

ContentResolver#query(...):

public final @Nullable Cursor query(final @NonNull Uri uri, @Nullable String[] projection,
            @Nullable String selection, @Nullable String[] selectionArgs,
            @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
        Preconditions.checkNotNull(uri, "uri");
//首先去使用unstable的方式獲取IContentProvider。即向對應Connection的unstableRefCount加1
//如果有緩存就直接在緩存拿,如果是首次獲取,則向AMS獲取。
        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 {
//通過binder通信直接與目標CP通信並獲取Cursor
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        selection, selectionArgs, sortOrder, 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
//如果目標CP所在進程已死,則拋這異常,此時將通過該方法處理這個緩存的Bp
                unstableProviderDied(unstableProvider);
//通過stable的方式獲取IContentProvider,即向對應的connection的stableRefCount加1
                stableProvider = acquireProvider(uri);
                if (stableProvider == null) {
                    return null;
                }
                qCursor = stableProvider.query(mPackageName, uri, projection,
                        selection, selectionArgs, sortOrder, 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, selection, sortOrder);

            // Wrap the cursor object into CursorWrapperInner object.
//最終還是要使用stable的方式去獲取一次,即必須會對stableRefCount進行加1
            CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
                    stableProvider != null ? stableProvider : acquireProvider(uri));
//注意這裏,那麼下面則不會release這個stable的ref了,應該會在Cursor關閉之後才釋放
            stableProvider = null;
            qCursor = null;//注意這裏,提前置空,下面就不用close cursor了
            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);
            }
//每次對ContentProvider操作完都會馬上release,即對refCount減1
            if (unstableProvider != null) {//會被執行
                releaseUnstableProvider(unstableProvider);
            }
            if (stableProvider != null) {//上面已置空,不會執行
                releaseProvider(stableProvider);
            }
        }
    }

ContextImpl$ApplicationContentResolver#acquireProvider,acquireUnstableProvider和acquireExistingProvider,都是直接調用ActivityThread的方法,其中acquireProvider,acquireUnstableProvider向AcitivityThread傳遞的參數的區別是stable參數分別是true和false:

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


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

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

接着分析AcitivityThread#acquireProvider():

    public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
//首先去找緩存中是否已經有這個IContentProvider了,一般存儲在ActivityThread 
//ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if (provider != null) {
            return provider;//如果有緩存在直接返回該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.
        IActivityManager.ContentProviderHolder holder = null;
        try {
//向AMS獲取,把stable參數一併傳過去
            holder = ActivityManagerNative.getDefault().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        } catch (RemoteException ex) {
        }
        if (holder == null) {
            Slog.e(TAG, "Failed to find provider info for " + auth);
            return null;
        }
        Slog.d(TAG, "holder:" + holder + ", holder.provider:" + holder.provider);

        // Install provider will increment the reference count for us, and break
        // any ties in the race.

        /// M: if provider is killed for some reasons when getContentProvider from AMS,
        /// M: restart the provider one time @{
        try {
//如果是客戶進程的話,install到本地,很明顯這是第一次獲取時纔會去install,如果不是第一次
//獲取,那麼必然會從緩存中獲取到並直接返回。
            holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        } catch (SecurityException sex) {
            try {
                 holder = ActivityManagerNative.getDefault().getContentProvider(
                         getApplicationThread(), auth, userId, stable);
            } catch (RemoteException ex) {
            }

            if (holder == null) {
                Slog.e(TAG, "Failed to find provider info for " + auth);
                return null;
            }

            holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        }
        /// M: @}
        return holder.provider;
    }

分析AMS#getContentProvider(...):

    private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId) {
        ContentProviderRecord cpr;
        ContentProviderConnection conn = null;
        ProviderInfo cpi = null;

        synchronized(this) {
            long startTime = SystemClock.elapsedRealtime();

            ProcessRecord r = null;
            if (caller != null) {
                r = getRecordForAppLocked(caller);
                if (r == null) {
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                          + " (pid=" + Binder.getCallingPid()
                          + ") when getting content provider " + name);
                }
            }

            boolean checkCrossUser = true;

            checkTime(startTime, "getContentProviderImpl: getProviderByName");

            // First check if this content provider has been published...
            cpr = mProviderMap.getProviderByName(name, userId);

            /// M: To debug for get content provider process info
            Slog.d(TAG, "getContentProviderImpl: from caller=" + caller + " (pid=" + Binder.getCallingPid() + ", userId=" + userId + ") to get content provider " + name + " cpr=" + cpr);

            // If that didn't work, check if it exists for user 0 and then
            // verify that it's a singleton provider before using it.
            if (cpr == null && userId != UserHandle.USER_OWNER) {
                cpr = mProviderMap.getProviderByName(name, UserHandle.USER_OWNER);
                if (cpr != null) {
                    cpi = cpr.info;
                    if (isSingleton(cpi.processName, cpi.applicationInfo,
                            cpi.name, cpi.flags)
                            && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
                        userId = UserHandle.USER_OWNER;
                        checkCrossUser = false;
                    } else {
                        cpr = null;
                        cpi = null;
                    }
                }
            }

            boolean providerRunning = cpr != null;
//如果目標CP進程已經在運行了,進入這裏
            if (providerRunning) {
                cpi = cpr.info;
                String msg;
                checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
                        != null) {
                    throw new SecurityException(msg);
                }
                checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");

                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);
                    // don't give caller the provider object, it needs
                    // to make its own.
                    Slog.d(TAG, "getContentProviderImpl: holder.provider = null");
                    holder.provider = null;
                    return holder;
                }

                final long origId = Binder.clearCallingIdentity();

                checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");

                // In this case the provider instance already exists, so we can
                // return it right away.
//和refCount,stable參數相關的就只有這裏了。該方法主要是創建一個ContentProviderConnection
//存到客戶進程ProcessRecord和目標CP的ContentProviderRecord中,然後對該對象那個的refCount
//加1,其中也有stableCount和unstableCount。後面接着分析該方法
                conn = incProviderCountLocked(r, cpr, token, stable);
                if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
                    if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
                        // If this is a perceptible app accessing the provider,
                        // make sure to count it as being accessed and thus
                        // back up on the LRU list.  This is good because
                        // content providers are often expensive to start.
                        checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
                        updateLruProcessLocked(cpr.proc, false, null);
                        checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
                        Slog.d(TAG, "getContentProviderImpl: updateLruProcessLocked cpr.proc=" + cpr.proc);
                    }
                }

                if (cpr.proc != null) {
                    if (false) {
                        if (cpr.name.flattenToShortString().equals(
                                "com.android.providers.calendar/.CalendarProvider2")) {
                            Slog.v(TAG, "****************** KILLING "
                                + cpr.name.flattenToShortString());
                            Process.killProcess(cpr.proc.pid);
                        }
                    }
                    checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
                    boolean success = updateOomAdjLocked(cpr.proc);
                    maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
                    checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
                    if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);
                    // NOTE: there is still a race here where a signal could be
                    // pending on the process even though we managed to update its
                    // adj level.  Not sure what to do about this, but at least
                    // the race is now smaller.
                    if (!success) {
                        // Uh oh...  it looks like the provider's process
                        // has been killed on us.  We need to wait for a new
                        // process to be started, and make sure its death
                        // doesn't kill our process.
                        Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString()
                                + " is crashing; detaching " + r);
                        boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
                        checkTime(startTime, "getContentProviderImpl: before appDied");
                        appDiedLocked(cpr.proc);
                        checkTime(startTime, "getContentProviderImpl: after appDied");
                        if (!lastRef) {
                            // This wasn't the last ref our process had on
                            // the provider...  we have now been killed, bail.
                            return null;
                        }
                        providerRunning = false;
                        conn = null;
                    }

                    /// M: Update cpr.proc's connProvider's process adj since it may have relation with A->B->C . @{
                    if (SystemProperties.get("ro.mtk_gmo_ram_optimize").equals("1")) {
                        if (success) {
                            for (int provi = cpr.proc.conProviders.size() - 1; provi >= 0 ; provi--) {
                                ContentProviderConnection proviConn = cpr.proc.conProviders.get(provi);

                                if (proviConn.stableCount > 0) {
                                    if (proviConn.provider.proc != null  && !proviConn.provider.proc.processName.equals("system")) {
                                        Slog.e(TAG, "getContentProviderImpl: Update provider " + cpr.proc + " conProviers's adj. conProviders.provider.proc=" + proviConn.provider.proc + " stableCount=" + proviConn.stableCount);
                                        updateOomAdjLocked(proviConn.provider.proc);
                                    }
                                }
                            }
                        }
                    }
                    /// M: }@

                }
                /// M: cpr added to mProviderMap when start process, but cpr.proc will set until publish provider @{
                /// We need to update provider adj if a provider process is acquired during launching
                else {
                    if (SystemProperties.get("ro.mtk_gmo_ram_optimize").equals("1")) {
                        final int N = mLaunchingProviders.size();
                        int i;
                        ContentProviderRecord launchingCpr;
                        for (i = 0; i < N; i++) {
                            launchingCpr = mLaunchingProviders.get(i);

                            if (cpr == launchingCpr) {
                                Slog.i(TAG, "getContentProviderImpl: updateOomAdjLocked for cpr=" + cpr + " launchingApp=" + cpr.launchingApp);
                                updateOomAdjLocked(cpr.launchingApp);
                                break;
                            }
                        }
                    }
                }
                /// M: }@

                Binder.restoreCallingIdentity(origId);
            }

            boolean singleton;
//如果目標CP進程還未運行,進入這裏。沒運行首先肯定是要先startProcessLocked
            if (!providerRunning) {
                try {
                    checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
                    cpi = AppGlobals.getPackageManager().
                        resolveContentProvider(name,
                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
                    checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
                } catch (RemoteException ex) {
                    /// M: Debug for get content provider problem from PMS
                    Log.v(TAG, "getContentProviderImpl: resolveContentProvider() exception " + ex.getMessage(), ex);
                }
                if (cpi == null) {
                    /// M: Debug for get content provider problem from PMS
                    Log.v(TAG, "getContentProviderImpl: can't get cpi from packagemanager");
                    return null;
                }
                // If the provider is a singleton AND
                // (it's a call within the same user || the provider is a
                // privileged app)
                // Then allow connecting to the singleton provider
                singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                        cpi.name, cpi.flags)
                        && isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
                if (singleton) {
                    userId = UserHandle.USER_OWNER;
                }
                cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
                checkTime(startTime, "getContentProviderImpl: got app info for user");

                String msg;
                checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
                if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
                        != null) {
                    throw new SecurityException(msg);
                }
                checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");

                if (!mProcessesReady && !mDidUpdate && !mWaitingUpdate
                        && !cpi.processName.equals("system")) {
                    // If this content provider does not run in the system
                    // process, and the system is not yet ready to run other
                    // processes, then fail fast instead of hanging.
                    throw new IllegalArgumentException(
                            "Attempt to launch content provider before system ready");
                }

                // Make sure that the user who owns this provider is running.  If not,
                // we don't want to allow it to run.
                if (!isUserRunningLocked(userId, false)) {
                    Slog.w(TAG, "Unable to launch app "
                            + cpi.applicationInfo.packageName + "/"
                            + cpi.applicationInfo.uid + " for provider "
                            + name + ": user " + userId + " is stopped");
                    return null;
                }

                ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
                checkTime(startTime, "getContentProviderImpl: before getProviderByClass");
                cpr = mProviderMap.getProviderByClass(comp, userId);
                checkTime(startTime, "getContentProviderImpl: after getProviderByClass");
                final boolean firstClass = cpr == null;
                if (firstClass) {
                    final long ident = Binder.clearCallingIdentity();
                    try {
                        checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");
                        ApplicationInfo ai =
                            AppGlobals.getPackageManager().
                                getApplicationInfo(
                                        cpi.applicationInfo.packageName,
                                        STOCK_PM_FLAGS, userId);
                        checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");
                        if (ai == null) {
                            Slog.w(TAG, "No package info for content provider "
                                    + cpi.name);
                            return null;
                        }
                        ai = getAppInfoForUser(ai, userId);
                        cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
                    } catch (RemoteException ex) {
                        // pm is in same process, this will never happen.
                    } finally {
                        Binder.restoreCallingIdentity(ident);
                    }
                }

                checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");

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

                if (DEBUG_PROVIDER) Slog.w(TAG_PROVIDER, "LAUNCHING REMOTE PROVIDER (myuid "
                            + (r != null ? r.uid : null) + " pruid " + cpr.appInfo.uid + "): "
                            + cpr.info.name + " callers=" + Debug.getCallers(6));

                // This is single process, and our app is now connecting to it.
                // See if we are already in the process of launching this
                // provider.
                final int N = mLaunchingProviders.size();
                int i;
                for (i = 0; i < N; i++) {
                    if (mLaunchingProviders.get(i) == cpr) {
                        break;
                    }
                }

                // If the provider is not already being launched, then get it
                // started.
                if (i >= N) {
                    final long origId = Binder.clearCallingIdentity();

                    try {
                        // Content provider is now in use, its package can't be stopped.
                        try {
                            checkTime(startTime, "getContentProviderImpl: before set stopped state");
                            AppGlobals.getPackageManager().setPackageStoppedState(
                                    cpr.appInfo.packageName, false, userId);
                            checkTime(startTime, "getContentProviderImpl: after set stopped state");
                        } catch (RemoteException e) {
                        } catch (IllegalArgumentException e) {
                            Slog.w(TAG, "Failed trying to unstop package "
                                    + cpr.appInfo.packageName + ": " + e);
                        }

                        // Use existing process if already started
                        checkTime(startTime, "getContentProviderImpl: looking for process record");
                        ProcessRecord proc = getProcessRecordLocked(
                                cpi.processName, cpr.appInfo.uid, false);
                        if (proc != null && proc.thread != null) {
                            /// M: Add pubProviders check for schedule install to avoid init
                            ///    content provider twice
                            ///    (It may run schedule install while process launching)
                            if (DEBUG_PROVIDER || true) Slog.d(TAG_PROVIDER,
                                    "Installing in existing process " + proc);
                            if (!proc.pubProviders.containsKey(cpi.name)) {
                                checkTime(startTime, "getContentProviderImpl: scheduling install");
                                proc.pubProviders.put(cpi.name, cpr);
                                try {
                                    proc.thread.scheduleInstallProvider(cpi);
                                } catch (RemoteException e) {
                                }
                            }
                        } else {
                            checkTime(startTime, "getContentProviderImpl: before start process");
                            proc = startProcessLocked(cpi.processName,
                                    cpr.appInfo, false, 0, "content provider",
                                    new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name), false, 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;
                            }
                        }
                        cpr.launchingApp = proc;
                        mLaunchingProviders.add(cpr);
                    } finally {
                        Binder.restoreCallingIdentity(origId);
                    }
                }

                checkTime(startTime, "getContentProviderImpl: updating data structures");

                // Make sure the provider is published (the same provider class
                // may be published under multiple names).
                if (firstClass) {
                    mProviderMap.putProviderByClass(comp, cpr);
                }

                mProviderMap.putProviderByName(name, cpr);
                conn = incProviderCountLocked(r, cpr, token, stable);
                if (conn != null) {
                    conn.waiting = true;
                }
            }
            checkTime(startTime, "getContentProviderImpl: done!");
        }

        // Wait for the provider to be published...
        synchronized (cpr) {
            while (cpr.provider == null) {
                if (cpr.launchingApp == null) {
                    Slog.w(TAG, "Unable to launch app "
                            + cpi.applicationInfo.packageName + "/"
                            + cpi.applicationInfo.uid + " for provider "
                            + name + ": launching app became null");
                    EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,
                            UserHandle.getUserId(cpi.applicationInfo.uid),
                            cpi.applicationInfo.packageName,
                            cpi.applicationInfo.uid, name);
                    return null;
                }
                try {
                    if (DEBUG_MU) Slog.v(TAG_MU,
                            "Waiting to start provider " + cpr
                            + " launchingApp=" + cpr.launchingApp);
                    if (conn != null) {
                        conn.waiting = true;
                    }
                    cpr.wait();
                } catch (InterruptedException ex) {
                } finally {
                    if (conn != null) {
                        conn.waiting = false;
                    }
                }
            }
        }
        return cpr != null ? cpr.newHolder(conn) : null;
    }

分析AMS#incProviderCountLocked(...):

    ContentProviderConnection incProviderCountLocked(ProcessRecord r,
            final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
        if (r != null) {
            for (int i=0; i<r.conProviders.size(); i++) {//循環查看ProcessRecord
                ContentProviderConnection conn = r.conProviders.get(i);
                if (conn.provider == cpr) {//如果該進程有跟目標CP的connection,就直接加1
                    if (DEBUG_PROVIDER) Slog.v(TAG_PROVIDER,
                            "Adding provider requested by "
                            + r.processName + " from process "
                            + cpr.info.processName + ": " + cpr.name.flattenToShortString()
                            + " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
//增加引用
                    if (stable) {
                        conn.stableCount++;
                        conn.numStableIncs++;
                    } else {
                        conn.unstableCount++;
                        conn.numUnstableIncs++;
                    }
                    return conn;//如果該進程有跟目標CP的connection,直接返回
                }
            }
//如果客戶Process沒有與目標CP的connection,則創建一個
            ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
            if (stable) {//增加引用
                conn.stableCount = 1;
                conn.numStableIncs = 1;
            } else {
                conn.unstableCount = 1;
                conn.numUnstableIncs = 1;
            }
            cpr.connections.add(conn);//加到目標CP的ContentProviderRecord中
            r.conProviders.add(conn);//加到客戶進程的ProcessRecord中
            startAssociationLocked(r.uid, r.processName, cpr.uid, cpr.name, cpr.info.processName);//這個不清楚幹什麼用,暫時先不研究
            return conn;
        }
        cpr.addExternalProcessHandleLocked(externalProcessToken);
        return null;
    }

 

分析ActivityThread#installProvider(...):

    private IActivityManager.ContentProviderHolder installProvider(Context context,
            IActivityManager.ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
//這裏在目標CP進程中才會進入,因爲客戶進程中holder不爲空,目標CP進程在此創建CP實例的
        if (holder == null || holder.provider == null) {
            if (DEBUG_PROVIDER || noisy) {
                Slog.d(TAG, "Loading provider " + info.authority + ": "
                        + info.name);
            }
            Context c = null;
            ApplicationInfo ai = info.applicationInfo;
            Slog.d(TAG, "installProvider: context.getPackageName()=" + context.getPackageName());
            if (context.getPackageName().equals(ai.packageName)) {
                c = context;
            } else if (mInitialApplication != null &&
                    mInitialApplication.getPackageName().equals(ai.packageName)) {
                c = mInitialApplication;
            } else {
                try {
                    c = context.createPackageContext(ai.packageName,
                            Context.CONTEXT_INCLUDE_CODE);
                } catch (PackageManager.NameNotFoundException e) {
                    // Ignore
                }
            }
            if (c == null) {
                Slog.w(TAG, "Unable to get context for package " +
                      ai.packageName +
                      " while loading content provider " +
                      info.name);
                return null;
            }
            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                localProvider = (ContentProvider)cl.
                    loadClass(info.name).newInstance();
                provider = localProvider.getIContentProvider();
                if (provider == null) {
                    Slog.e(TAG, "Failed to instantiate class " +
                          info.name + " from sourceDir " +
                          info.applicationInfo.sourceDir);
                    return null;
                }
                if (DEBUG_PROVIDER) Slog.v(
                    TAG, "Instantiating local provider " + info.name);
                // XXX Need to create the correct context for this provider.
                localProvider.attachInfo(c, info);
            } catch (java.lang.Exception e) {
                if (!mInstrumentation.onException(null, e)) {
                    throw new RuntimeException(
                            "Unable to get provider " + info.name
                            + ": " + e.toString(), e);
                }
                return null;
            }
        } else {
            provider = holder.provider;
            if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
                    + info.name);
        }

        IActivityManager.ContentProviderHolder retHolder;

        synchronized (mProviderMap) {
            if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
                    + " / " + info.name);
            IBinder jBinder = provider.asBinder();
//如果是目標CP進程這裏就會成立
            if (localProvider != null) {
                ComponentName cname = new ComponentName(info.packageName, info.name);
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
                if (pr != null) {
                    if (DEBUG_PROVIDER) {
                        Slog.v(TAG, "installProvider: lost the race, "
                                + "using existing local provider");
                    }
                    provider = pr.mProvider;
                } else {
                    holder = new IActivityManager.ContentProviderHolder(info);
                    holder.provider = provider;
                    holder.noReleaseNeeded = true;
                    pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                    mLocalProviders.put(jBinder, pr);
                    mLocalProvidersByName.put(cname, pr);
                }
                retHolder = pr.mHolder;
            } else {//客戶進程進入這裏
                ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
                if (prc != null) {//因爲是首次查詢,一般都沒有緩存,所以直接跳到else
                    if (DEBUG_PROVIDER) {
                        Slog.v(TAG, "installProvider: lost the race, updating ref count");
                    }
                    // We need to transfer our new reference to the existing
                    // ref count, releasing the old one...  but only if
                    // release is needed (that is, it is not running in the
                    // system process).
                    if (!noReleaseNeeded) {
                        incProviderRefLocked(prc, stable);
                        try {
                            ActivityManagerNative.getDefault().removeContentProvider(
                                    holder.connection, stable);
                        } catch (RemoteException e) {
                            //do nothing content provider object is dead any way
                        }
                    }
                } else {//一般進入這裏
                    ProviderClientRecord client = installProviderAuthoritiesLocked(
                            provider, localProvider, holder);
                    if (noReleaseNeeded) {
                        prc = new ProviderRefCount(holder, client, 1000, 1000);
                    } else {
//創建時根據stable參數初始化。即分別對stableCount和unstableCount初始化
                        prc = stable
                                ? new ProviderRefCount(holder, client, 1, 0)
                                : new ProviderRefCount(holder, client, 0, 1);
                    }
//把一個跟ref相關的對象存起來,以Bp端作爲key。installProvider除了創建ref相關對象,還會把一個
//ProviderClientRecord放到ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
                    mProviderRefCountMap.put(jBinder, prc);
                }
                retHolder = prc.holder;
            }
        }

        return retHolder;
    }

分析ActivityThread#acquireExistingProvider:

    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.
//在installProvider方法中分析過,在向AMS獲取provider時,會創建一個ProviderRefCount
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
//對緩存中provider引用加1,那麼是對stableCount還是unstableCount加1,那就看acquire時傳入的參
//數了
                incProviderRefLocked(prc, stable);
            }
            return provider;
        }
    }

 分析ActivityThread#incProviderRefLocked:

    private final void incProviderRefLocked(ProviderRefCount prc, boolean stable) {
        if (stable) {//如果是stable方式獲取,則進入這裏
//每次都會對本地的refCount加1
            prc.stableCount += 1;
//在加1後等於1,即這是首次對該Provider做stable引用
            if (prc.stableCount == 1) {
                // We are acquiring a new stable reference on the provider.
                int unstableDelta;
                if (prc.removePending) {
                    // We have a pending remove operation, which is holding the
                    // last unstable reference.  At this point we are converting
                    // that unstable reference to our new stable reference.
                    unstableDelta = -1;
                    // Cancel the removal of the provider.
                    if (DEBUG_PROVIDER) {
                        Slog.v(TAG, "incProviderRef: stable "
                                + "snatched provider from the jaws of death");
                    }
                    prc.removePending = false;
                    // There is a race! It fails to remove the message, which
                    // will be handled in completeRemoveProvider().
                    mH.removeMessages(H.REMOVE_PROVIDER, prc);
                } else {
                    unstableDelta = 0;
                }
                try {
                    if (DEBUG_PROVIDER) {
                        Slog.v(TAG, "incProviderRef Now stable - "
                                + prc.holder.info.name + ": unstableDelta="
                                + unstableDelta);
                    }
//首次stable引用纔會觸發AMS#refContentProvider方法,第二次對同一provider做stable引用
//則只在本地增加引用數
                    ActivityManagerNative.getDefault().refContentProvider(
                            prc.holder.connection, 1, unstableDelta);
                } catch (RemoteException e) {
                    //do nothing content provider object is dead any way
                }
            }
        } else {//如果是unstable方式獲取則進入這裏
//每次都會對本地的refCount加1
            prc.unstableCount += 1;
//在加1後等於1,即這是首次對該Provider做unstable引用。和stable類似的屬性
            if (prc.unstableCount == 1) {
                // We are acquiring a new unstable reference on the provider.
                if (prc.removePending) {
                    // Oh look, we actually have a remove pending for the
                    // provider, which is still holding the last unstable
                    // reference.  We just need to cancel that to take new
                    // ownership of the reference.
                    if (DEBUG_PROVIDER) {
                        Slog.v(TAG, "incProviderRef: unstable "
                                + "snatched provider from the jaws of death");
                    }
                    prc.removePending = false;
                    mH.removeMessages(H.REMOVE_PROVIDER, prc);
                } else {
                    // First unstable ref, increment our count in the
                    // activity manager.
                    try {
                        if (DEBUG_PROVIDER) {
                            Slog.v(TAG, "incProviderRef: Now unstable - "
                                    + prc.holder.info.name);
                        }
//首次unstable引用纔會觸發AMS#refContentProvider方法,第二次對同一provider做unstable引用
//則只在本地增加引用數
                        ActivityManagerNative.getDefault().refContentProvider(
                                prc.holder.connection, 0, 1);
                    } catch (RemoteException e) {
                        //do nothing content provider object is dead any way
                    }
                }
            }
        }
    }

 由上面的分析可以得知,每次acquireProvider,無論是去本地緩存取,還是去AMS獲取,都會對本地的ProviderRefCount加1,而只有首次對Provider進行stable或者首次unstable引用纔會去觸發AMS中和refCount有關的方法。當然AMS#getContentProvider()中就有和refCount有關的重要邏輯。

 

分析AMS#refContentProvider(...):

    public boolean refContentProvider(IBinder connection, int stable, int unstable) {
        ContentProviderConnection conn;
        try {
            conn = (ContentProviderConnection)connection;
        } catch (ClassCastException e) {
            String msg ="refContentProvider: " + connection
                    + " not a ContentProviderConnection";
            Slog.w(TAG, msg);
            throw new IllegalArgumentException(msg);
        }
        if (conn == null) {
            throw new NullPointerException("connection is null");
        }

        synchronized (this) {
            if (stable > 0) {
                conn.numStableIncs += stable;
            }
            stable = conn.stableCount + stable;
            if (stable < 0) {
                throw new IllegalStateException("stableCount < 0: " + stable);
            }

            if (unstable > 0) {
                conn.numUnstableIncs += unstable;
            }
            unstable = conn.unstableCount + unstable;
            if (unstable < 0) {
                throw new IllegalStateException("unstableCount < 0: " + unstable);
            }

            if ((stable+unstable) <= 0) {
                throw new IllegalStateException("ref counts can't go to zero here: stable="
                        + stable + " unstable=" + unstable);
            }
            conn.stableCount = stable;
            conn.unstableCount = unstable;
            return !conn.dead;
        }
    }

看這方法也沒幹什麼,只是對傳過來的ContentProviderConnection的Bp端的字段變量進行賦值。而沒有將該Bp端存起來

 

下面分析releaseProvider相關和AMS#removeDyingProviderLocked,前面分析了創建一個對引用加1,下面的是對引用減一和銷燬,還有研究目標CP所在進程死後,stable參數對AMS處理和目標CP關聯的客戶進程的影響。

那麼這個stableCount

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章