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!