The Client process in Replugin provides Binder through the Server resident process ContentProvider to complete the communication between the Client and the resident process.

Replugin source code read — Frame initialization

IPC learning — Simple concepts of AIDL

In pmBase. init, if the Client process is Replugin initialized, the initForClient method is called to initialize the non-resident process

In the above flowchart, the Client process uses getContentResolver.query to get the CURSOR and then the Binder of the resident process

ContentProvider Fetch process
  • Get the ContentResolver from getContentResolver
  • 2, getContentResolver return ApplicationContentResolver ContextImpl inner class object
  • 3. The IContentResolver interface object is obtained by AMS and ActivityThread in ContentResolver
  • 3.1. First determine whether the URI meets the standard
  • 3.2, through inner class ContextImpl ApplicationContentResolver sends a message to the caller ActivityThread see if any is called the ContentProvider cache, if there is a direct return, If AMS’s getContentProvider method is not called
  • The AMS getContentProvider method calls the getContentProviderImpl method, which checks whether the provider has a cache in AMS, and then whether the multiprocess property is true. True means that the provider allows instance creation in the caller’s process, but the default value is false, and then determines whether the provider’s process has been created. If not, call the startProcessLocked method in AMS to create the Process to which the provider belongs through the process. start method
  • 3.4. After the process is created, execute the main method in the ActivityThread of the process. In the main method, the message loop is started and the ATTACH method is invoked. Go to AMS attachApplicationLocked, where you can call back to ActivityThread using the bindApplication method for ActivityThread, After initializing some information here, a message is sent to the Handler in the ActivityThread, which then calls the handleBindApplication method, where the Application method is created, Then call the installContentProviders method
  • 3.5. There are two steps in the installContentProvider method:
  • 3.5.1 Start the for loop to load all providers that need to be created in this process. The for loop calls the installProviders method and loads the ContentProvider class in the installProvider method. By loading the ContentProvider. GetIContentProvider IContentProvider interface object, the interface is a Binder object, returns an object called Transport type, This object inherits from ContentProviderNative, which inherits from Binder and implements the IContentProvider interface, That is, the loaded ContentProvider returns a Binder IContentProvider interface object, so it can be called across processes
  • 3.5.2 After loading the ContentProvider class, call AMS publishContentProviders method to notify AMS that the ContentProvider has been loaded. And returns the Binder IContentProvider interface object, which is then ready to operate on the Provider in the process

<provider
	android:name=".MyContentProvider"
	android:authorities="com.example.content.provider"
    android:multiprocess="false"
    android:process=":remote"
    android:exported="true"/>
Copy the code

Multiprocess: ** true means that each caller process creates a ContentProvider. The default is false

**android:process=”:remote”, android:multiprocess=”true”: ** The ContentProvider is not loaded when the application is started, it is loaded when the ContentProvider is called, and it is initialized in the caller’s process. The remote process that defines the ContentProvider may not have started at this point

**android:process=”:remote”, android:multiprocess=”false”: ** The ContentProvider is not loaded with application startup, it is loaded when the ContentProvider is called, and the ContentProvider is initialized in the “remote” process

1. Context.getContentResolver
private ContextImpl(ContextImpl container, ActivityThread mainThread,
            		LoadedApk packageInfo, String splitName,
            		IBinder activityToken, UserHandle user, int flags,
            		ClassLoader classLoader) {
	mContentResolver = new ApplicationContentResolver(this, mainThread);   
}

public ContentResolver getContentResolver(a) {
    return mContentResolver;
}
Copy the code
2.ApplicationContentResolver.query
public final Cursor query(final Uri uri, String[] projection, Bundle queryArgs,
            			  CancellationSignal cancellationSignal) {
	// 1. Obtain the interface object IContentProvider
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    // 2. Query data using the IContentProvider and return the Cursor object, where the thread blocks
	qCursor = unstableProvider.query(mPackageName, uri, projection,
                        queryArgs, remoteCancellationSignal);
    qCursor.getCount();
    long durationMillis = SystemClock.uptimeMillis() - startTime;
    maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);
    finalIContentProvider provider = (stableProvider ! =null)? stableProvider : acquireProvider(uri);// 5. Wrap the returned cursor as CursorWrapperInner
    final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
    stableProvider = null;
    qCursor = null;
    return wrapper;
}
Copy the code
3 ApplicationContentResolver.acquireUnstableProvider
ApplicationContentResolver.acquireUnstableProvider()
--->ActivityThread.acquireProvider()
Copy the code
4 ActivityThread.acquireProvider
public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
    // 1. Check whether there is a cached IContentProvider. If yes, return
    IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
    if(provider ! =null) {
        return provider;
	}
    ContentProviderHolder holder = null;
    // 2. Get the ActivityManagerService object, and then get the provider information through AMS
	holder = ActivityManager.getService().getContentProvider(
                        getApplicationThread(), auth, userId, stable);
    if (holder == null) {
        return null;
    }
    // 3. Get the target ContentProvider information and save it
    holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
    // 4. Return ContentProvider
    return holder.provider;
}
Copy the code
5.AMS.getContentProvider
@Override
public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String name, int userId, boolean stable) {
    // Enter the method
    return getContentProviderImpl(caller, name, null, stable, userId);
}
Copy the code
6.AMS.getContentProviderImpl
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId) {
    // 1. Save the provider information
    ContentProviderRecord cpr;
    ContentProviderConnection conn = null;
    ProviderInfo cpi = null;
    synchronized(this) {
        long startTime = SystemClock.uptimeMillis();
		// 2. Get the caller's process information
        ProcessRecord r = null;
        if(caller ! =null) {
            r = getRecordForAppLocked(caller);
        }
        boolean checkCrossUser = true;
        // 3. Check whether the provider information is loaded and cached
        cpr = mProviderMap.getProviderByName(name, userId);
        if (cpr == null&& userId ! = UserHandle.USER_SYSTEM) { cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);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_SYSTEM;
                    checkCrossUser = false;
                } else {
                    cpr = null;
                    cpi = null; }}}// 4. If this is the first call, providerRunning must be false
        booleanproviderRunning = cpr ! =null&& cpr.proc ! =null && !cpr.proc.killed;
        if (providerRunning) {
        	// 5. If there is cache
            cpi = cpr.info;
            String msg;
			// 6. Check whether the provider's multiprocess is true. If true, the provider will be sent to the caller
            If multiProcess is false, the default system has only one provider
            if(r ! =null && cpr.canRunHere(r)) {
                ContentProviderHolder holder = cpr.newHolder(null);
                holder.provider = null;
                returnholder; }}// 7. If there is no cache
        if(! providerRunning) {// 8. Obtain information about the application where the Provider resides through the PMS
            cpi = AppGlobals.getPackageManager().
                        resolveContentProvider(name,
                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
            if (cpi == null) {
                return null;
            }
            ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
            // 10. Check whether provider is being loaded for the first time
            cpr = mProviderMap.getProviderByClass(comp, userId);
            final boolean firstClass = cpr == null;
            if (firstClass) {
                final long ident = Binder.clearCallingIdentity();
                try {
                	Obtain the Application information of the provider
                    ApplicationInfo ai =
                            AppGlobals.getPackageManager().
                                getApplicationInfo(
                                        cpi.applicationInfo.packageName,
                                        STOCK_PM_FLAGS, userId);
                    ai = getAppInfoForUser(ai, userId);
                    cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
                } finally{ Binder.restoreCallingIdentity(ident); }}/** * 12. Check whether the provider's multiprocess is true. If it is true, the provider will initialize it in the caller's process
            if(r ! =null && cpr.canRunHere(r)) {
                return cpr.newHolder(null);
            }
			/** * all providers are stored in the ArrayList of mLaunchingProviders. By default, the system can only have one instance of this provider. If there are mLaunchingProviders, then I must be less than N */
            final int N = mLaunchingProviders.size();
            int i;
            for (i = 0; i < N; i++) {
                if (mLaunchingProviders.get(i) == cpr) {
                    break; }}// 14. No other application instances exist
            if (i >= N) {
                final long origId = Binder.clearCallingIdentity();
                try {
					//15. Check whether the provider process already exists
                    ProcessRecord proc = getProcessRecordLocked(
                                cpi.processName, cpr.appInfo.uid, false);
                    16. Check whether the process has been started
                    if(proc ! =null&& proc.thread ! =null && !proc.killed) {
                        if(! proc.pubProviders.containsKey(cpi.name)) { proc.pubProviders.put(cpi.name, cpr);//16. Use ApplicationThread to send the load Provider messageproc.thread.scheduleInstallProvider(cpi); }}else {
                    	//17 If the process is not created, create a new process
                        proc = startProcessLocked(cpi.processName,
                                    cpr.appInfo, false.0."content provider".new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name), false.false.false);
                    }
                    cpr.launchingApp = proc;
                    mLaunchingProviders.add(cpr);
                } finally{ Binder.restoreCallingIdentity(origId); }}//18. Store provider information
            if (firstClass) {
                mProviderMap.putProviderByClass(comp, cpr);
            }
            mProviderMap.putProviderByName(name, cpr);
            conn = incProviderCountLocked(r, cpr, token, stable);
            if(conn ! =null) {
                conn.waiting = true; }}}//19. Wrap the provider's information in ContentProviderHolder and return it
    returncpr ! =null ? cpr.newHolder(conn) : null;
}
Copy the code
7.ActivityThread.handleBindApplication

If the multiprocess to false, as the new process to create a ContentProvider, process creation called after ActivityThread. HandleBindApplication method

private void handleBindApplication(AppBindData data) {
	//1. Create context objects
    final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
	final InstrumentationInfo ii;
    //2. Prepare for initialization
    if(data.instrumentationName ! =null) {
        ii = new ApplicationPackageManager(null, getPackageManager())
                        .getInstrumentationInfo(data.instrumentationName, 0);
        mInstrumentationPackageName = ii.packageName;
        mInstrumentationAppDir = ii.sourceDir;
        mInstrumentationSplitAppDirs = ii.splitSourceDirs;
        mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
        mInstrumentedAppDir = data.info.getAppDir();
        mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
        mInstrumentedLibDir = data.info.getLibDir();
    } else {
        ii = null;
    }
    // Continue loading instrumentation.
    if(ii ! =null) {
        ApplicationInfo instrApp;
		//3. Create Application data
        instrApp = getPackageManager().getApplicationInfo(ii.packageName, 0,
                        UserHandle.myUserId());
        if (instrApp == null) {
            instrApp = new ApplicationInfo();
        }
        ii.copyTo(instrApp);
        instrApp.initForUser(UserHandle.myUserId());
        //4.LoadedApk object is an in-memory representation of APK files, such as APK file information, APK file code and resources,
        // Even the information about the four components of the code, such as Activity and Service, can be obtained through this object.
        final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                    appContext.getClassLoader(), false.true.false);
        final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

        final ClassLoader cl = instrContext.getClassLoader();
        mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
        final ComponentName component = new ComponentName(ii.packageName, ii.name);
        //5. Initialize Instrumentation
        mInstrumentation.init(this, instrContext, appContext, component,
                    data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
    } else {
        mInstrumentation = new Instrumentation();
        mInstrumentation.basicInit(this);
    }
    Application app;
    try {
		// 6. Create the Application and call back the Attach method of the Application
        app = data.info.makeApplication(data.restrictedBackupMode, null);
        app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled);
        mInitialApplication = app;
        if(! data.restrictedBackupMode) {if(! ArrayUtils.isEmpty(data.providers)) {/ / 7. Load the provider
            	installContentProviders(app, data.providers);
                mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
            }
        }
        mInstrumentation.onCreate(data.instrumentationArgs);
        mInstrumentation.callApplicationOnCreate(app);
    } finally{}}Copy the code
8.ActivityThread.installContentProviders
private void installContentProviders( Context context, List
       
         providers)
        {
    final ArrayList<ContentProviderHolder> results = new ArrayList<>();

    for (ProviderInfo cpi : providers) {
    	// 1. Load ContentProvider
        ContentProviderHolder cph = installProvider(context, null, cpi,
                    false.true.true);
        if(cph ! =null) {
            cph.noReleaseNeeded = true;
            // 2.ContentProviderHolder stores resultsresults.add(cph); }}// 3. Notify AMS that the ContentProvider has been loaded and send results to AMS for management
    ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
}
Copy the code
9.ActivityThread.installProvider
private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    if (holder == null || holder.provider == null) {
		/ / 1. Create the Context
        Context c = null;
        ApplicationInfo ai = info.applicationInfo;
        if (context.getPackageName().equals(ai.packageName)) {
            c = context;
        } else if(mInitialApplication ! =null &&
                    mInitialApplication.getPackageName().equals(ai.packageName)) {
            c = mInitialApplication;
        } else {
            c = context.createPackageContext(ai.packageName,
                            Context.CONTEXT_INCLUDE_CODE);
        }
        try {
        	/ / 2. Create the provider
            final java.lang.ClassLoader cl = c.getClassLoader();
            LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
            if (packageInfo == null) {
                packageInfo = getSystemContext().mPackageInfo;
            }
            localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
            provider = localProvider.getIContentProvider();
            localProvider.attachInfo(c, info);
        } catch (java.lang.Exception e) {
        }
    } else {
        provider = holder.provider;
    }
    ContentProviderHolder retHolder;
    synchronized (mProviderMap) {
    IBinder jBinder = provider.asBinder();
    //3. Keep the Provider information
    if(localProvider ! =null) {
        ComponentName cname = new ComponentName(info.packageName, info.name);
        ProviderClientRecord pr = mLocalProvidersByName.get(cname);
        if(pr ! =null) {
            provider = pr.mProvider;
        } else {
            holder = new 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) {
            if (!noReleaseNeeded) {
                incProviderRefLocked(prc, stable);
                ActivityManager.getService().removeContentProvider(
                                    holder.connection, stable);
            }
        } else {
            ProviderClientRecord client = installProviderAuthoritiesLocked(
                            provider, localProvider, holder);
            if (noReleaseNeeded) {
                prc = new ProviderRefCount(holder, client, 1000.1000);
            } else {
                prc = stable
                                ? new ProviderRefCount(holder, client, 1.0)
                                : new ProviderRefCount(holder, client, 0.1);
            }
            mProviderRefCountMap.put(jBinder, prc);
        }
        retHolder = prc.holder;
    }
    return retHolder;
}
Copy the code
10.ContentProvider.getIContentProvider
public IContentProvider getIContentProvider(a) {
    return mTransport;
}
private Transport mTransport = new Transport();

class Transport extends ContentProviderNative{}

abstract public class ContentProviderNative extends Binder implements IContentProvider {}

public interface IContentProvider extends IInterface {}

public interface IInterface
{
    public IBinder asBinder(a);
}
Copy the code
11.AMS.publishContentProviders
public final void publishContentProviders(IApplicationThread caller, List
       
         providers)
        {
    synchronized (this) {
        final ProcessRecord r = getRecordForAppLocked(caller);
        final long origId = Binder.clearCallingIdentity();

        final int N = providers.size();
        for (int i = 0; i < N; i++) {
            ContentProviderHolder src = providers.get(i);
            / / 1. ContentProviderHolder with ContentPRoviderRecord mapping
            ContentProviderRecord dst = r.pubProviders.get(src.info.name);
            if(dst ! =null) {
                ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
                mProviderMap.putProviderByClass(comp, dst);
                String names[] = dst.info.authority.split(";");
                for (int j = 0; j < names.length; j++) {
                	/ / 1. ContentProviderRecord cache
                    mProviderMap.putProviderByName(names[j], dst);
                }
                int launchingCount = mLaunchingProviders.size();
                int j;
                boolean wasInLaunchingProviders = false;
                    for (j = 0; j < launchingCount; j++) {
                        if (mLaunchingProviders.get(j) == dst) {
                            mLaunchingProviders.remove(j);
                            wasInLaunchingProviders = true; j--; launchingCount--; }}if (wasInLaunchingProviders) {
                    mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
                }
                synchronized (dst) {
                    dst.provider = src.provider;
                    dst.proc = r;
                    dst.notifyAll();
                }
                updateOomAdjLocked(r, true); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } } Binder.restoreCallingIdentity(origId); }}Copy the code