preface

We know that the general Android engineers are in the application layer development, will not involve system source code, but if you want to go to the bottom, or in-depth plug-in, Framework system layer development work, if you do not understand Android source code but not, So the next I based on their own understanding and learning to record with Android development is closely related to the source analysis, probably from the Android SystemServer start, four components start, AMS, PMS and other dimensions to introduce, the following is my plan, of course, may change in the future.

If you haven’t paid attention to it yet, you can pay attention to it first. The series of articles will be updated continuously.

Android 8.0 source code analysis (a) SystemServer process started

Android 8.0 source code analysis (ii) Launcher

Android 8.0 source code analysis (three) application process creation to the application startup process

Android 8.0 source code analysis (four) Activity start

Android 8.0 source code analysis (5) Service startup

Android 8.0 source code analysis (6) BroadcastReceiver launch

Android 8.0 source code analysis (seven) ContentProvider start

ActivityManagerService

Android 8.0 source code analysis (nine) WindowManager

Android 8.0 source code analysis (ten) WindowManagerService window management

introduce

ContentProvider (ContentProvider) is one of the four components, which is the least used by developers among the four components. Its function is data interaction between processes, with Binder mechanism at the bottom for inter-process communication. I will not specify the use here, want to understand the use of Android can refer to the knowledge about ContentProvider is here! Next, we will conduct a comprehensive analysis based on the analysis of ContentProvider workflow.

Source code analysis

Query to AMS call procedure

Here is a code example:

    fun getContactsLists(a): MutableList<String> {
        var contactsLists: MutableList<String> = mutableListOf<String>()
        lateinit var cursor: Cursor
        try {
        Use getContentResolver() to query the contact list
      	cursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null.null.null.null)
        // Iterate over the cursor
        if(cursor ! =null) {
            while (cursor.moveToNext()) {
        // Get the name
            val displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
        // Phone number
            val number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
            contactsLists.add("Name:$displayNameTelephone:$number")
            }
        }
	cursor.close()
        } catch (error: Exception) {
            logger.error("error:$error")}return contactsLists
    }

	/ / test
    val contactsLists = getContactsLists()
    contactsLists.forEach { it -> println("Get contacts from ContentResolver:$contactsLists")}Copy the code

The above code is to obtain the address list of the mobile phone through the content provider, the output is:

I/ system. out: fetch from ContentResolver110Name: Ming112, Name: Huawei Customer Service Tel:4008308300]
Copy the code

So how does this process work inside? This is the main analysis content of this article. Have you found that the above code example is written with Kotlin code? Open source will be launched around November 10th, you can first follow my GitHub, and I will update the address later)

As usual, take a look at the analysis flow in this section, which focuses on sequence diagrams

To query data, get the contentResolver object using the parent getContentResolver() method:

    @Override
    public ContentResolver getContentResolver(a) {
        return mBase.getContentResolver();
    }
Copy the code

MBase (ContextImpl); ContextImpl (ContextImpl); mBase (ContextImpl);

//ContextImpl.java
    @Override
    public ContentResolver getContentResolver(a) {
        return mContentResolver;
    }
Copy the code

GetContentResolver method returned in the ApplicationContentResolver object, here it is ContextImpl static inner class, Derived from ContentResolver, it is created in ContextImpl’s constructor, which means that when we call its query, insert, and update methods, The ContentProvider is used to query the ContentProvider. Query is used to query the ContentProvider.

//ContentResolver.java
    public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable CancellationSignal cancellationSignal) {
        Preconditions.checkNotNull(uri, "uri");
        /** * 1. Get the IContentProvider object, which is the local proxy for ContentProvider */
        IContentProvider unstableProvider = acquireUnstableProvider(uri);
        if (unstableProvider == null) {
            return null;
        }
        IContentProvider stableProvider = null;
        Cursor qCursor = null;
        try {
            
            try {
                /** * 2. Call IContentProvider's query function to query */
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        queryArgs, remoteCancellationSignal);
            } catch (DeadObjectException e) {
                

            / /...
            return wrapper;
        } catch (RemoteException e) {
            return null;
        } finally{... }}Copy the code

AcquireUnstableProvider gets the ContentProvider’s local proxy object in the comment, and then calls the local proxy object’s query method to return a Cursor. Let’s look at note 1 acquireUnstableProvider method how to get a ContentProvider local proxy objects, the code is as follows:

   //ContentResolver.java 
   public final IContentProvider acquireUnstableProvider(Uri uri) {
        if(! SCHEME_CONTENT.equals(uri.getScheme())) {return null;
        }
        String auth = uri.getAuthority();
        if(auth ! =null) {
          	// Call the inner abstract method
            return acquireUnstableProvider(mContext, uri.getAuthority());
        }
        return null;
    }

    / * *@hide* /
    protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
Copy the code

By the above code acquireUnstableProvider returns an abstract function, specific implementation to subclass implementation, the subclass is ApplicationContentResolver object, we look at the specific implementation of the it, the code is as follows:

//ContextImpl.java
        @Override
        protected IContentProvider acquireUnstableProvider(Context c, String auth) {
            return mMainThread.acquireProvider(c,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), false);
        }
Copy the code

AcquireProvider acquireProvider acquireProvider acquireProvider acquireProvider acquireProvider acquireProvider acquireProvider acquireProvider

//ActivityThread.java

    public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
        AcquireExistingProvider (); /** * acquireExistingProvider (); /** * getProvider (); * /
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if(provider ! =null) {
            return provider;
        }


        ContentProviderHolder holder = null;
        try {
            /** * 2. Call IAcitivityManager to get the ContentProviderHolder object */
            holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        } catch (RemoteException ex) {
            throwex.rethrowFromSystemServer(); }.../** * 3. Install ContentProvider */
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }

Copy the code

The acquireExistingProvider method at comment 1 checks internally for the presence of a ContentProvider in the mProviderMap global variable of ActivityThread, and returns if so, Without communicating with AMS to get the getContentProvider method that calls annotation 2’s IActivityManager, Note 3 is to install the ContentProvider and store the contentprovider-related data in the mProviderMap as a cache, so that AMS does not need to be called every time you use the same ContentProvider. AMS getContentProvider method concrete implementation, the code is as follows:

//AMS.java
    @Override
    public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String name, int userId, boolean stable) {
        enforceNotIsolatedCaller("getContentProvider"); .// Invoke the internal getContentProviderImpl method
        return getContentProviderImpl(caller, name, null, stable, userId);
    }
Copy the code
//AMS.java


    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId) {
        ContentProviderRecord cpr;
        ContentProviderConnection conn = null;
        ProviderInfo cpi = null; ./** * 1. Get information about the target ContentProvider application process, and call comment 2 if the process has started, or 3 */ otherwise
                        ProcessRecord proc = getProcessRecordLocked(
                                cpi.processName, cpr.appInfo.uid, false);
                        if(proc ! =null&& proc.thread ! =null && !proc.killed) {
                            if (DEBUG_PROVIDER) 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 {
                                    /** * 2. Call IApplicationThread scheduleInstallProvider */
                                    proc.thread.scheduleInstallProvider(cpi);
                                } catch (RemoteException e) {
                                }
                            }
                        } else {
                            checkTime(startTime, "getContentProviderImpl: before start process");
                            /** * 3. Start a new 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); }}... }Copy the code

The code workflow above is to determine whether the application process in which the ContentProvider is located is started, and to call comment 2 if it is started, and to call comment 3 if it is not started, because the first three components are directly started application process. So this last component we’re going to look at how to execute without the application process starting. Note 2 To outline the process, I first call the scheduleInstallProvider function of the IApplication inner class of ActivityThread. Then install the ContentProvider via the H sendMessage notification. Let’s go straight to comment 3, which looks like this:

//AMS.java


    final ProcessRecord startProcessLocked(String processName,
            ApplicationInfo info, boolean knownToBeDead, int intentFlags,
            String hostingType, ComponentName hostingName, boolean allowWhileBooting,
            boolean isolated, boolean keepIfLarge) {
        return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
                hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
                null /* ABI override */.null /* entryPoint */.null /* entryPointArgs */.null /* crashHandler */);
    }
Copy the code

ActivityThread () : ActivityThread () : ActivityThread () : ActivityThread (); Let’s look directly at the ActivityThread main function, which looks like this

//ActivityThread.java


    // is executed by reflection calls
    public static void main(String[] args) {...// Main thread message loop
        Looper.prepareMainLooper();
        // Create an ActivityThread object
        ActivityThread thread = new ActivityThread();
        / / the Application, the Activity entry
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
Copy the code

ActivityThread attach function

//ActivityThread.java
    private void attach(boolean system) {.../ / 1.
     final IActivityManager mgr = ActivityManager.getService();
     try {
          / / 2. The associated Application
          mgr.attachApplication(mAppThread);
     } catch (RemoteException ex) {
      throwex.rethrowFromSystemServer(); }... }Copy the code

AttachApplication (AMS) is invoked in comment 2 to obtain the proxy IActivityManager for AMS. We pass the IApplicationThread object to AMS to keep the AMS process communicating with the AMS process.

AMS starts the process of the ContentProvider

As usual, take a look at the sequence diagram:

As we learned in the previous section, the AMS process is notified to invoke attachApplication after the application process is successfully started, as follows:

//AMS.java

private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
  ...
  
   thread.bindApplication(processName, appInfo, providers,
                        app.instr.mClass,
                        profilerInfo, app.instr.mArguments,
                        app.instr.mWatcher,
                        app.instr.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.persistent,
                        newConfiguration(getGlobalConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial); . }Copy the code

The thread.bindApplication method is called in attachApplicationLocked. Threads are iApplicationThreads, As with IActivityManager, aiDL is used to transfer data between processes. Let’s go back to the bindApplication method of the ActivityThread inner class ApplicationThread.

//ActivityThread.java

        public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableBinderTracking, boolean trackAllocation,
                boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                String buildSerial) {

            if(services ! =null) {
                // Setup the service cache in the ServiceManager
                ServiceManager.initServiceCache(services);
            }

            setCoreSettings(coreSettings);

            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableBinderTracking = enableBinderTracking;
            data.trackAllocation = trackAllocation;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            data.buildSerial = buildSerial;
          	// Send a message to class H
            sendMessage(H.BIND_APPLICATION, data);
        }
Copy the code

The ActivityThread inner class H receives the message and begins processing the BIND_APPLICSTION message with the following code:

//ActivityThread.java.public void handleMessage(Message msg) {...case BIND_APPLICATION:
    AppBindData data = (AppBindData)msg.obj;
    handleBindApplication(data);
                    
    break; . }...Copy the code

Call the internal handleBindApplication method with the following code:

//ActivityThread.java

private void handleBindApplication(AppBindData data) {...Create ContentImpl */
        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info); ./ * * * 2. * /
                final ClassLoader cl = instrContext.getClassLoader();
                mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
            } catch (Exception e) {
               ...
            }

            final ComponentName component = new ComponentName(ii.packageName, ii.name);
            / * * * 3. * /
            mInstrumentation.init(this, instrContext, appContext, component, data.instrumentationWatcher, data.instrumentationUiAutomationConnection); .try {

            4 / * * * * /
            Application app = data.info.makeApplication(data.restrictedBackupMode, null);
            mInitialApplication = app;


            if(! data.restrictedBackupMode) {if(! ArrayUtils.isEmpty(data.providers)) {/ * * * 5. * /
                    installContentProviders(app, data.providers);
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); }}try {
                mInstrumentation.onCreate(data.instrumentationArgs);
            }
            catch (Exception e) {
          ...
            }

            try {
                / 6. * * * * /
                mInstrumentation.callApplicationOnCreate(app);
            } catch(Exception e) { ... }}Copy the code

Note 1 creates the Context implementation class ContentImp. This class is familiar to all of you. It is the implementation class of Context, the parent class of the four components. Note 2 create Instrumentation through reflection and initialization Instrumentaion in note 3 place, in the annotations and create Application and call at 6 Application onCreate lifecycle methods, This shows that the ContentProvider is already started in the middle, so let’s look at comment 5 above to see how the ContentProvider is started:

//ActivityThread.java

    private void installContentProviders( Context context, List
       
         providers)
        {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        / * * * 1 * /
        for (ProviderInfo cpi : providers) {
            if (DEBUG_PROVIDER) {
                StringBuilder buf = new StringBuilder(128);
                buf.append("Pub ");
                buf.append(cpi.authority);
                buf.append(":");
                buf.append(cpi.name);
                Log.i(TAG, buf.toString());
            }
            / * * * 2. * /
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/.true /*noReleaseNeeded*/.true /*stable*/);
            if(cph ! =null) {
                cph.noReleaseNeeded = true; results.add(cph); }}try {
            / * * * 3. * /
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throwex.rethrowFromSystemServer(); }}Copy the code

Note 1 traverses the ProviderInfo list of the current application process to obtain the stored information for each ContentProvicer. Call the installProvider method in comment 2 to start these ContentProviders. Note 3 stores the started ContentProvider into the AMS mProviderMap. This global variable is used to cache the started ContentProvidec, as described in the previous section.

//ActivityThread.java

 private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {...try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                / * * * 1 * /localProvider = (ContentProvider)cl. loadClass(info.name).newInstance(); provider = localProvider.getIContentProvider(); ./ * * * 2. * /
                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; }... }Copy the code

Comment 1 instantiates the ContentProvider object via reflection and calls its attachInfo method at comment 2 as follows:

//ContentProvider.java
    public void attachInfo(Context context, ProviderInfo info) {
        attachInfo(context, info, false);
    }

    private void attachInfo(Context context, ProviderInfo info, boolean testing) {
        mNoPerms = testing;


        if (mContext == null) {
            mContext = context;
            if(context ! =null) {
                mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
                        Context.APP_OPS_SERVICE);
            }
            mMyUid = Process.myUid();
            if(info ! =null) { setReadPermission(info.readPermission); setWritePermission(info.writePermission); setPathPermissions(info.pathPermissions); mExported = info.exported; mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) ! =0;
                setAuthorities(info.authority);
            }
            /** * Install successfully, call the lifecycle function onCreate */
            ContentProvider.this.onCreate(); }}public abstract boolean onCreate(a);
Copy the code

As you can see, at the end of the ContentProvider attachInfo function, the abstract onCreate method is called, and its subclasses are notified that onCreate has been successfully started.

Here the four components have been explained, I hope these several articles can bring readers some harvest, thank you for reading.

Recommended reading

  • Android Advanced Decryption
  • Android: All about ContentProvider knowledge here!