This article is a reprint of the article, reading the original text will be better, at the end of the article there are links to the original text
PS: This article is based on Android API 26
1. Initialize the ContentProvider
In Part 4 of the last article, we learned how to communicate with IPC processes in Android using ContentProvider. In this article, we will analyze the source code of ContentProvider. When an application starts, the entry method is the main method of ActivityThread. The main method is a static method in which an instance of ActivityThread is created and a message queue is created for the main thread. The Attachapplication method of AMS is called remotely from the ActivityThread’s attach method and the ApplicationThread object is provided to AMS. public static void main(String[] args) {
. ActivityThread thread = new ActivityThread(); thread.attach(false); .
}
ActivityManager. GetService () this line of code by IBinder call AMS communication between processes, and invoke the AMS attachApplication method; private void attach(boolean system) {
. final IActivityManager mgr = ActivityManager.getService(); try { mgr.attachApplication(mAppThread); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); }...
}
We look at how ActivityManager for AMS, click ActivityManager. GetService () method to check, found that got a IActivityManager, Here, the implementation class of iActivityManager is ActivityManagerService, which is called AMS for short above. public static IActivityManager getService() {
return IActivityManagerSingleton.get(); } private static final Singleton<IActivityManager> IActivityManagerSingleton = new Singleton<IActivityManager>() { @Override protected IActivityManager create() { final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE); final IActivityManager am = IActivityManager.Stub.asInterface(b); return am; }};
Looking back at the Mgr. attachApplication(MAPPTHREAD) code, the attachApplication method in AMS, we see that the attachapplicationLocked method is called.
@Override public final void attachApplication(IApplicationThread thread) { synchronized (this) { int callingPid = Binder.getCallingPid(); final long origId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid); Binder.restoreCallingIdentity(origId); }}
ActivityManagerService attachapplicationLocked. Thread below is ActivityThread. BindApplication method of ActivityThread was called, and then returned to ActivityThread for processing. private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) { ...... if (app.instr ! = null) { 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, new Configuration(getGlobalConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial); } else { thread.bindApplication(processName, appInfo, providers, null, profilerInfo, null, null, null, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || ! normalMode, app.persistent, new Configuration(getGlobalConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial); }... return true;
}
ActivityThread bindApplication method, found that some information assigned to the AppBindData object property, and then through the Handler wrapped AppBindData to send a message out; 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) {
......
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;
sendMessage(H.BIND_APPLICATION, data);
}
Looking at the processing of Handler subclass H, we see that the handleBindApplication method of ActivityThread is called. private class H extends Handler {
. public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { ...... case BIND_APPLICATION: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication"); AppBindData data = (AppBindData) msg.obj; handleBindApplication(data); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; }}
}
Look at the handleBindApplication method of ActivityThread, We focus on the Application app = data. Info. MakeApplication (data) restrictedBackupMode, null), installContentProviders (app, Data. Will) and mInstrumentation. CallApplicationOnCreate (app) these three lines of code; private void handleBindApplication(AppBindData data) {
. try { // If the app is being launched for full backup or restore, bring it up in // a restricted environment with the base application class. Application app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; // don't bring up providers in restricted mode; they may depend on the // app's custom Application class if (! data.restrictedBackupMode) { if (! ArrayUtils.isEmpty(data.providers)) { installContentProviders(app, data.providers); // For process that contains content providers, we want to // ensure that the JIT is enabled "at some point". mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); } } // Do this after providers, since instrumentation tests generally start their // test thread at this point, and we don't want that racing. try { mInstrumentation.onCreate(data.instrumentationArgs); } catch (Exception e) { throw new RuntimeException( "Exception thrown in onCreate() of " + data.instrumentationName + ": " + e.toString(), e); }... } finally { StrictMode.setThreadPolicy(savedPolicy); }...
}
Let’s take a look at the data. The info. MakeApplication (data. RestrictedBackupMode, null) method, the data. The info is LoadedApk class object, In the makeApplication method of this LoadedApk class, get the ClassLoader, Then through mActivityThread. MInstrumentation. NewApplication (cl, appClass, appContext) to initialize the Application; public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) { ...... try { java.lang.ClassLoader cl = getClassLoader(); if (! mPackageName.equals("android")) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "initializeJavaContextClassLoader"); initializeJavaContextClassLoader(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); app = mActivityThread.mInstrumentation.newApplication( cl, appClass, appContext); appContext.setOuterContext(app); } catch (Exception e) { ...... }... return app;
}
The above mActivityThread mInstrumentation is Instrumentation, we click the newApplication Instrumentation contains three parameters method; public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return newApplication(cl.loadClass(className), context);
}
If we look at the Instrumentation newApplication method with two parameters, we see that this is where we initialize the Application implementation and associate the Context. static public Application newApplication(Class
clazz, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = (Application)clazz.newInstance();
app.attach(context);
return app;
}
Let’s go back to ActivityThread and call installContentProviders(app, data.providers) and see how it works. ActivityThread’s installProvider method is called. private void installContentProviders(
Context context, List<ProviderInfo> providers) { ...... for (ProviderInfo cpi : providers) { ...... ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); if (cph ! = null) { cph.noReleaseNeeded = true; results.add(cph); }}...
}
Looking down at ActivityThread’s installProvider method, we see that we initialize the ContentProvider and call ContentProvider’s attachInfo method, which takes two arguments. private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ...... 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); . return retHolder;
}
Looking down at the ContentProvider’s two-parameter attachInfo method, we see that we call its own three-parameter attachInfo method. public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
Click to see the ContentProvider’s attachInfo method, which takes three parameters, and find that the ContentProvider’s onCreate method is called. In the handleBindApplication method of ActivityThread above, three of the most critical methods are called, It is Application app = data. Info. MakeApplication (data) restrictedBackupMode, Null), installContentProviders (app, the data will) and mInstrumentation callApplicationOnCreate (app), InstallContentProviders (app,data.providers) calls the ContentProvider’s onCreate method. While mInstrumentation. CallApplicationOnCreate (app) to invoke the onCreate method of Application, So the ContentProvider’s onCreate method is executed before the Application’s onCreate method. private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing; /* * Only allow it to be set once, so after the content service gives * this to us clients can't change it. */ 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); } ContentProvider.this.onCreate(); }
}
2, ContentProvider source parse-query method
The ContentProvider implementation class implements onCreate, getType, Delete, Update, Query, and Insert methods. The ContentProvider implementation class implements onCreate, getType, Delete, Update, Query, and Insert methods. Those interested in the other five methods should read the source code for themselves; The first is to get a ContentResolver object, which is the getContentResolver method given to ContextRapper.
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
In this case, the ContextRapper’s getContentResolver method is transferred to the ContextImpl’s getContentResolver method for implementation. GetContentResolver ContextImpl class returns mContentResolver, it is a ApplicationContentResolver types of object;
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
This method calls a query method in a ContentResolver that has 6 parameters. This method calls a query method in a ContentResolver that has 6 parameters. public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
return query(uri, projection, selection, selectionArgs, sortOrder, null);
}
If we look at the query method with 6 parameters in ContentResolver, we find that this method calls the query method with 4 parameters in ContentResolver. public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder,
@Nullable CancellationSignal cancellationSignal) {
Bundle queryArgs = createSqlQueryBundle(selection, selectionArgs, sortOrder);
return query(uri, projection, queryArgs, cancellationSignal);
}
If we check the query method in ContentResolver with four parameters, we find that this method calls the AcquireUnStableProvider method in ContentResolver with one parameter. Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) { Preconditions.checkNotNull(uri, "uri"); IContentProvider unstableProvider = acquireUnstableProvider(uri); .
}
If you look at the one-parameter AcquireUnStableProvider method in ContentResolver, you’ll see that it calls the two-parameter AcquireUnStableProvider method in ContentResolver. public final IContentProvider acquireUnstableProvider(Uri uri) {
if (! SCHEME_CONTENT.equals(uri.getScheme())) { return null; } String auth = uri.getAuthority(); if (auth ! = null) { return acquireUnstableProvider(mContext, uri.getAuthority()); } return null;
}
See ApplicationContentResolver (is ContentResolver subclasses of a class, as well as ContextImpl inner classes) contains two parameters acquireUnstableProvider method, AcquireProvider calls ActivityThread. MMainThread is ActivityThread.
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
Click on ActivityThread’s AcquireProvider method. It will first check ActivityThread to see if the target ContentProvider already exists, and then return to ActivityThread if it does. If not, call AMS to get the ContentProvider object; public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) { final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable); if (provider ! = null) { return 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. ContentProviderHolder holder = null; try { holder = ActivityManager.getService().getContentProvider( getApplicationThread(), auth, userId, stable); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } if (holder == null) { Slog.e(TAG, "Failed to find provider info for " + auth); return null; } // Install provider will increment the reference count for us, and break // any ties in the race. holder = installProvider(c, holder, holder.info, true /*noisy*/, holder.noReleaseNeeded, stable); return holder.provider;
}
The ContentProvider we get from the client is not the original ContentProvider, but an object IContentProvider of type Binder of ContentProvider, IContentProvider concrete implementation is ContentProviderNative and ContentProvider. Transport, The ContentProvider. Transport inherited ContentProviderNative, here we see a ContentProvider, query methods of Transport, This method calls the ContentProvider’s four-parameter query method.
@Override public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) { ...... try { return ContentProvider.this.query( uri, projection, queryArgs, CancellationSignal.fromTransport(cancellationSignal)); } finally { setCallingPackage(original); }}
When we look at the ContentProvider’s four-parameter query method, we find that it calls the ContentProvider’s six-parameter query method. public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) { queryArgs = queryArgs ! = null ? queryArgs : Bundle.EMPTY; // if client doesn't supply an SQL sort order argument, attempt to build one from // QUERY_ARG_SORT* arguments. String sortClause = queryArgs.getString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER); if (sortClause == null && queryArgs.containsKey(ContentResolver.QUERY_ARG_SORT_COLUMNS)) { sortClause = ContentResolver.createSqlSortClause(queryArgs); } return query( uri, projection, queryArgs.getString(ContentResolver.QUERY_ARG_SQL_SELECTION), queryArgs.getStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS), sortClause, cancellationSignal);
}
The ContentProvider’s 6-parameter query method calls the ContentProvider’s 5-parameter query method. The ContentProvider’s 5-parameter query method is abstract. We end up calling our own query method, which extends from a subclass of the ContentProvider class; public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
@Nullable String selection, @Nullable String[] selectionArgs,
@Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
return query(uri, projection, selection, selectionArgs, sortOrder);
}