Android AOSP 6.0.1 content provider acquireProvider process analysis

Android AOSP 6.0.1 content provider acquireProvider process analysis

The content provider is one of the main building blocks of the Android application, which can provide content for the application. They encapsulate data and provide it to the application through a single ContentResolver interface. You only need a content provider if you need to share data between multiple applications. For example, contact data is used by multiple applications and must be stored in the content provider. If you do not need to share data between multiple applications, you can use the database directly through SQLiteDatabase.

If you plan to share data, use a content provider. If you don't plan to share data, you might still use them because they provide a good abstraction, but you don't have to. This abstraction allows you to modify the application data storage implementation without affecting other existing applications that rely on data access. In this case, only the content provider is affected, and the application that accesses it is not affected. For example, you can replace the SQLite database with other storage, as shown in the figure below.

When a request is made through ContentResolver, the system will check the permission of the given URI and pass the request to the content provider registered under that permission. The content provider can interpret other parts of the URI as needed. The UriMatcher class helps to resolve URIs.

The main methods that need to be implemented are:

onCreate() is used to initialize the content provider

query(Uri, String[], Bundle, CancellationSignal) returns the data to the caller

insert(Uri, ContentValues) insert new data into the content provider

update(Uri, ContentValues, Bundle) Update the existing data in the content provider

delete(Uri, Bundle) Delete data from the content provider

getType(Uri) returns the MIME type of the data in the content provider

Data access methods (such as insert(Uri,ContentValues) and update(Uri,ContentValues,Bundle)) can be called from multiple threads at the same time and must be thread-safe. Other methods (such as onCreate()) can only be called from the main thread of the application, and lengthy operations must be avoided.

Requests for ContentResolver will be automatically forwarded to the appropriate ContentProvider instance, so subclasses do not have to worry about the details of cross-process calls.

The Android Framework includes content providers that manage data such as audio, video, images, and personal contact information. You can see some of them in the reference documentation of the android.provider package. Under certain restrictions, all Android applications can access these content providers.

Content providers can be used to manage access to various data storage sources, including structured data (such as SQLite relational databases) or unstructured data (such as image files).

Content providers provide fine-grained control over access to data. You can choose to restrict access to content providers only in the application, grant comprehensive permissions to access data from other applications, or configure different permissions to read and write data.

You can use content providers to abstract out the details of accessing different data sources in your application. For example, your application may store structured records in a SQLite database, as well as video and audio files. If you implement the corresponding content provider in your application, you can use the content provider to access all of this data.

1. use ContentProvider

First define a database auxiliary class, which is responsible for creating user tables.

class DBHelper (context: Context): SQLiteOpenHelper(context, DATABASE_NAME, null , DATABASE_VERSION) { override fun onCreate (db: SQLiteDatabase ) { db.execSQL( "CREATE TABLE IF NOT EXISTS $USER_TABLE_NAME (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)" ) } override fun onUpgrade (db: SQLiteDatabase , oldVersion: Int , newVersion: Int ) { } companion object { private const val DATABASE_NAME = "user.db" const val USER_TABLE_NAME = "user" private const val DATABASE_VERSION = 1 } } Copy code

Define the content provider, add two users to the table in the onCreate method, and implement the insert and query methods.

class DemoProvider : ContentProvider () { companion object { private const val TAG = "DemoProvider" private const val AUTHORITY = "com.demo.framework" private const val USER_CODE = 1 private val mMatcher: UriMatcher = UriMatcher(UriMatcher.NO_MATCH) init { mMatcher.addURI(AUTHORITY, "user" , USER_CODE) } } private var mContext: Context? = null private var mDbHelper: DBHelper? = null private var mDb: SQLiteDatabase? = null override fun onCreate () : Boolean { mContext = context!! mDbHelper = DBHelper(context!!) mDb = mDbHelper?.writableDatabase mDb?.execSQL( "delete from ${DBHelper.USER_TABLE_NAME} " ) mDb?.execSQL( "insert into ${DBHelper.USER_TABLE_NAME} values(0,'Peter');" ) mDb?.execSQL( "insert into ${DBHelper.USER_TABLE_NAME} values(1,'Justin');" ) Log.d(TAG, "------onCreate------" ) return true } override fun insert (uri: Uri , values: ContentValues ?) : Uri { Log.d(TAG, "------insert------" ) Log.d(TAG, "values = $values " ) val table = getTableName(uri) mDb?.insert(table, null , values) //When the ContentProvider data of the URI changes, notify the visitor who accesses the ContentProvider data mContext?.contentResolver?.notifyChange(uri, null ) return uri } override fun query ( uri: Uri , projection: Array < String >?, selection: String ?, selectionArgs: Array < String >?, sortOrder: String ? ) : Cursor { Log.d(TAG, "------query------" ) val table = getTableName(uri) return mDb!!.query(table, projection, selection, selectionArgs, null , null , sortOrder, null ) } override fun update ( uri: Uri , values: ContentValues ?, selection: String ?, selectionArgs: Array < String >? ) : Int { return 0 } override fun delete (uri: Uri , selection: String ?, selectionArgs: Array < String >?) : Int { return 0 } override fun getType (uri: Uri ) : String? { return null } private fun getTableName (uri: Uri ) : String? { var tableName: String? = null when (mMatcher.match(uri)) { USER_CODE -> tableName = DBHelper.USER_TABLE_NAME } return tableName } } Copy code

Finally, the DemoProvider is registered in AndroidManifest.xml.

< Provider Android: exported = "to true" Android: Authorities = "com.demo.framework" Android: name = ".DemoProvider"/> copy the code

Use our customized content provider in another APP.

class MainActivity : AppCompatActivity () { companion object { private const val TAG = "MainActivity" } override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) val uriUser = Uri.parse( "content://com.demo.framework/user" ) val values = ContentValues() values.put( "_id" , 2 ) values.put( "name" , "Bob" ) val resolver = contentResolver resolver.insert(uriUser, values) val cursor = resolver.query(uriUser, arrayOf( "_id" , "name" ), null , null , null ) var index = 0 while (cursor.moveToNext()) { index++ Log.d(TAG, "index = $index id = ${cursor.getInt( 0 )} name = ${cursor.getString( 1 )} " ) } cursor.close() } } Copy code

Run the above Demo code to print out all the contents of the user table.

2. getContentResolver process analysis

In Demo, we first get the ContentResolver object. Then call insert to insert the data, and then call the query method to query the data provided by DemoProvider.

frameworks/base/core/java/android/content/ContextWrapper.java

public class ContextWrapper extends Context { Context mBase; ...... @Override public ContentResolver getContentResolver () { return mBase.getContentResolver(); } ...... } Copy code

According to the analysis in the previous sections, mBase is an object of type ContextImpl.

frameworks/base/core/java/android/app/ContextImpl.java

class ContextImpl extends Context { ...... private final ApplicationContentResolver mContentResolver; ...... @Override public ContentResolver getContentResolver () { return mContentResolver; } ...... } Copy code

mContentResolver is an ApplicationContentResolver type, which inherits from ContentResolver.

frameworks/base/core/java/android/app/ContextImpl.java

private static final class ApplicationContentResolver extends ContentResolver { private final ActivityThread mMainThread; private final UserHandle mUser; public ApplicationContentResolver ( Context context, ActivityThread mainThread, UserHandle user) { super (context); mMainThread = Preconditions.checkNotNull(mainThread); mUser = Preconditions.checkNotNull(user); } ...... } Copy code

ContentResolver provides application access to the content model. First call acquireProvider to query the corresponding remote proxy class.

frameworks/base/core/java/android/content/ContentResolver.java

public abstract class ContentResolver { ...... public final @Nullable Uri insert ( @NonNull Uri url, @Nullable ContentValues values) { Preconditions.checkNotNull(url, "url" ); //1. Get the remote proxy class IContentProvider provider = acquireProvider(url); if (provider == null ) { throw new IllegalArgumentException( "Unknown URL " + url); } try { long startTime = SystemClock.uptimeMillis(); //2. Insert data Uri createdRow = provider.insert(mPackageName, url, values); long durationMillis = SystemClock.uptimeMillis()-startTime; maybeLogUpdateToEventLog(durationMillis, url, "insert" , null /* where */ ); return createdRow; } catch (RemoteException e) { //Arbitrary and not worth documenting, as Activity //Manager will kill this process shortly anyway. return null ; } finally { releaseProvider(provider); } } ...... } Copy code

Let's first look at the process of obtaining the remote proxy class. 1. determine whether the scheme is equal to content. If it is not equal to directly return null, then determine whether the Authority is empty. If it is not empty, call the overloaded version function whose input parameters are context and authority. .

frameworks/base/core/java/android/content/ContentResolver.java

public abstract class ContentResolver { ...... public static final String SCHEME_CONTENT = "content" ; ...... public final IContentProvider acquireProvider (Uri uri) { if (!SCHEME_CONTENT.equals(uri.getScheme())) { return null ; } final String auth = uri.getAuthority(); if (auth != null ) { return acquireProvider(mContext, auth); } return null ; } ...... } Copy code

The acquireProvider(Context c, String name) function is an abstract function, so it ultimately needs to be implemented by a subclass inherited from the abstract class ContentResolver.

frameworks/base/core/java/android/content/ContentResolver.java

public abstract class ContentResolver { ...... protected abstract IContentProvider acquireProvider (Context c, String name) ; ...... } Copy code

In the ApplicationContentResolver class, the acquireProvider function actually calls the acquireProvider function of ActivityThread.

frameworks/base/core/java/android/app/ContextImpl.java

private static final class ApplicationContentResolver extends ContentResolver { ...... private final ActivityThread mMainThread; ...... @Override protected IContentProvider acquireProvider (Context context, String auth) { return mMainThread.acquireProvider(context, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), true ); } ...... } Copy code

The input parameters are Context, Authority, userId and stable flag. First call acquireExistingProvider to query the existing provider, if it does not exist, get the IActivityManager.ContentProviderHolder object from the remote ActivityManagerService by calling the getContentProvider function of ActivityManagerProxy.

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread { ...... 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; } // provider // // provider // provider IActivityManager.ContentProviderHolder holder = null; 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; } // provider holder = installProvider(c, holder, holder.info, true /*noisy*/, holder.noReleaseNeeded, stable); return holder.provider; } ...... }

acquireExistingProvider :

1. ProviderKey

2. key ArrayMap<ProviderKey, ProviderClientRecord> ProviderClientRecord

mProviderMap ProviderClientRecord

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread { ...... private static final class ProviderKey { final String authority; final int userId; public ProviderKey(String authority, int userId) { this.authority = authority; this.userId = userId; } @Override public boolean equals(Object o) { if (o instanceof ProviderKey) { final ProviderKey other = (ProviderKey) o; return Objects.equals(authority, other.authority) && userId == other.userId; } return false; } @Override public int hashCode() { return ((authority != null) ? authority.hashCode() : 0) ^ userId; } } final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<ProviderKey, ProviderClientRecord>(); ...... final class ProviderClientRecord { final String[] mNames; final IContentProvider mProvider; final ContentProvider mLocalProvider; final IActivityManager.ContentProviderHolder mHolder; ProviderClientRecord(String[] names, IContentProvider provider, ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) { mNames = names; mProvider = provider; mLocalProvider = localProvider; mHolder = holder; } } ...... 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()) { //provider Log.i(TAG, "Acquiring provider " + auth + " for user " + userId + ": existing object's process dead"); handleUnstableProviderDiedLocked(jBinder, true); return null; } // provider ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc != null) { incProviderRefLocked(prc, stable); } return provider; } } ...... }

ActivityManagerProxy getContentProvider ActivityManagerService IActivityManager.ContentProviderHolder ActivityManagerProxy getContentProvider Binder mRemote ActivityManagerService GET_CONTENT_PROVIDER_TRANSACTION

frameworks/base/core/java/android/app/ActivityManagerNative.java

class ActivityManagerProxy implements IActivityManager { ...... public ContentProviderHolder getContentProvider(IApplicationThread caller, String name, int userId, boolean stable) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); data.writeString(name); data.writeInt(userId); data.writeInt(stable ? 1 : 0); mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0); reply.readException(); int res = reply.readInt(); ContentProviderHolder cph = null; if (res != 0) { cph = ContentProviderHolder.CREATOR.createFromParcel(reply); } data.recycle(); reply.recycle(); return cph; } ...... }

ActivityManagerService getContentProvider getContentProviderImpl

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... @Override public final ContentProviderHolder getContentProvider( IApplicationThread caller, String name, int userId, boolean stable) { enforceNotIsolatedCaller("getContentProvider"); if (caller == null) { String msg = "null IApplicationThread when getting content provider " + name; Slog.w(TAG, msg); throw new SecurityException(msg); } // checkContentProviderPermissionLocked() return getContentProviderImpl(caller, name, null, stable, userId); } ...... 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) { // ProcessRecord 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; // log checkTime(startTime, "getContentProviderImpl: getProviderByName"); // cpr = mProviderMap.getProviderByName(name, userId); // user 0 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; ...... boolean singleton; 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) { } if (cpi == null) { return null; } // 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")) { // throw new IllegalArgumentException( "Attempt to launch content provider before system ready"); } // 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 } finally { Binder.restoreCallingIdentity(ident); } } checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord"); if (r != null && cpr.canRunHere(r)) { // // root 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)); // final int N = mLaunchingProviders.size(); int i; for (i = 0; i < N; i++) { if (mLaunchingProviders.get(i) == cpr) { break; } } // if (i >= N) { final long origId = Binder.clearCallingIdentity(); try { // 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); } // checkTime(startTime, "getContentProviderImpl: looking for process record"); ProcessRecord proc = getProcessRecordLocked( cpi.processName, cpr.appInfo.uid, false); if (proc != null && proc.thread != null) { 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 { proc.thread.scheduleInstallProvider(cpi); } catch (RemoteException e) { } } } else { checkTime(startTime, "getContentProviderImpl: before start process"); //1. 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"); // if (firstClass) { mProviderMap.putProviderByClass(comp, cpr); } mProviderMap.putProviderByName(name, cpr); //2. conn = incProviderCountLocked(r, cpr, token, stable); if (conn != null) { conn.waiting = true; } } checkTime(startTime, "getContentProviderImpl: done!"); } // 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; } ...... }

Demo insert 1. startProcessLocked ContentProviderRecord provider null

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... 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 */); } final ProcessRecord startProcessLocked(String processName, ApplicationInfo info, boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName, boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge, String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) { long startTime = SystemClock.elapsedRealtime(); ProcessRecord app; if (!isolated) { app = getProcessRecordLocked(processName, info.uid, keepIfLarge); checkTime(startTime, "startProcess: after getProcessRecord"); if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) { // if (mBadProcesses.get(info.processName, info.uid) != null) { if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid + "/" + info.processName); return null; } } else { // // // if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid + "/" + info.processName); mProcessCrashTimes.remove(info.processName, info.uid); if (mBadProcesses.get(info.processName, info.uid) != null) { EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, UserHandle.getUserId(info.uid), info.uid, info.processName); mBadProcesses.remove(info.processName, info.uid); if (app != null) { app.bad = false; } } } } else { // app = null; } ...... String hostingNameStr = hostingName != null ? hostingName.flattenToShortString() : null; if (app == null) { checkTime(startTime, "startProcess: creating new process record"); //1 app = newProcessRecordLocked(info, processName, isolated, isolatedUid); if (app == null) { Slog.w(TAG, "Failed making new process record for " + processName + "/" + info.uid + " isolated=" + isolated); return null; } app.crashHandler = crashHandler; checkTime(startTime, "startProcess: done creating new process record"); } else { // app.addPackage(info.packageName, info.versionCode, mProcessStats); checkTime(startTime, "startProcess: added package to existing proc"); } // if (!mProcessesReady && !isAllowedWhileBooting(info) && !allowWhileBooting) { if (!mProcessesOnHold.contains(app)) { mProcessesOnHold.add(app); } if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "System not ready, putting on hold: " + app); checkTime(startTime, "startProcess: returning with proc on hold"); return app; } checkTime(startTime, "startProcess: stepping in to startProcess"); //2 startProcessLocked( app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs); checkTime(startTime, "startProcess: done starting proc!"); return (app.pid != 0) ? app : null; } ...... }

1 2 1 ProcessRecord 2

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess, boolean isolated, int isolatedUid) { String proc = customProcess != null ? customProcess : info.processName; BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); final int userId = UserHandle.getUserId(info.uid); int uid = info.uid; ...... final ProcessRecord r = new ProcessRecord(stats, info, proc, uid); if (!mBooted && !mBooting && userId == UserHandle.USER_OWNER && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) { r.persistent = true; } addProcessNameLocked(r); return r; } ...... private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) { long startTime = SystemClock.elapsedRealtime(); ...... try { ...... int uid = app.uid; int[] gids = null; int mountExternal = Zygote.MOUNT_EXTERNAL_NONE; if (!app.isolated) { int[] permGids = null; try { checkTime(startTime, "startProcess: getting gids from package manager"); final IPackageManager pm = AppGlobals.getPackageManager(); permGids = pm.getPackageGids(app.info.packageName, app.userId); MountServiceInternal mountServiceInternal = LocalServices.getService( MountServiceInternal.class); mountExternal = mountServiceInternal.getExternalStorageMountMode(uid, app.info.packageName); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } /* * GID */ if (ArrayUtils.isEmpty(permGids)) { gids = new int[2]; } else { gids = new int[permGids.length + 2]; System.arraycopy(permGids, 0, gids, 2, permGids.length); } gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid)); gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid)); } ...... app.gids = gids; app.requiredAbi = requiredAbi; app.instructionSet = instructionSet; // PID RuntimeException boolean isActivityProcess = (entryPoint == null); if (entryPoint == null) entryPoint = "android.app.ActivityThread"; Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName); checkTime(startTime, "startProcess: asking zygote to start proc"); Process.ProcessStartResult startResult = Process.start(entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet, app.info.dataDir, entryPointArgs); checkTime(startTime, "startProcess: returned from zygote!"); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); ...... } catch (RuntimeException e) { ...... } } ...... }

Process start start

processClass main()

processClass main()

niceName processClass processClass

frameworks/base/core/java/android/os/Process.java

public class Process { ...... public static final ProcessStartResult start(final String processClass, final String niceName, int uid, int gid, int[] gids, int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, String abi, String instructionSet, String appDataDir, String[] zygoteArgs) { try { return startViaZygote(processClass, niceName, uid, gid, gids, debugFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); throw new RuntimeException( "Starting VM process through Zygote failed", ex); } } ...... }

android Linux processClass android.app.ActivityThread ActivityThread main()

main() attach system false ActivityManagerProxy ActivityManagerService attachApplication ApplicationThread

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread { ...... private void attach(boolean system) { sCurrentActivityThread = this; mSystemThread = system; if (!system) { ...... final IActivityManager mgr = ActivityManagerNative.getDefault(); try { mgr.attachApplication(mAppThread); } catch (RemoteException ex) { //Ignore } ...... } else { ...... } ...... } ...... public static void main(String[] args) { ...... ActivityThread thread = new ActivityThread(); thread.attach(false); ...... } }

frameworks/base/core/java/android/app/ActivityManagerNative.java

class ActivityManagerProxy implements IActivityManager { ...... public void attachApplication(IApplicationThread app) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(app.asBinder()); mRemote.transact(ATTACH_APPLICATION_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); } ...... }

ActivityManagerService attachApplication attachApplicationLocked generateApplicationProvidersLocked List APP ApplicationThread bindApplication

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... private final boolean attachApplicationLocked(IApplicationThread thread, int pid) { ...... boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info); List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null; if (providers != null && checkAppInLaunchingProvidersLocked(app)) { Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG); msg.obj = app; mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT); } ...... try { ...... thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, profilerInfo, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace, isRestrictedBackupMode || !normalMode, app.persistent, new Configuration(mConfiguration), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked()); ...... } catch (Exception e) { ...... } ...... } ...... @Override public final void attachApplication(IApplicationThread thread) { synchronized (this) { int callingPid = Binder.getCallingPid(); final long origId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid); Binder.restoreCallingIdentity(origId); } } ...... }

generateApplicationProvidersLocked ProviderInfo ProviderInfo ActivityManagerService getContentProviderImpl mProviderMap ContentProviderRecord ProcessRecord pubProviders Map

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) { List<ProviderInfo> providers = null; try { ParceledListSlice<ProviderInfo> slice = AppGlobals.getPackageManager(). queryContentProviders(app.processName, app.uid, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS); providers = slice != null ? slice.getList() : null; } catch (RemoteException ex) { } if (DEBUG_MU) Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.uid); int userId = app.userId; if (providers != null) { int N = providers.size(); app.pubProviders.ensureCapacity(N + app.pubProviders.size()); for (int i=0; i<N; i++) { ProviderInfo cpi = (ProviderInfo)providers.get(i); ...... ComponentName comp = new ComponentName(cpi.packageName, cpi.name); ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId); if (cpr == null) { cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton); mProviderMap.putProviderByClass(comp, cpr); } if (DEBUG_MU) Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid); // ProcessRecord pubProviders Map ContentProviderRecord app.pubProviders.put(cpi.name, cpr); if (!cpi.multiprocess || !"android".equals(cpi.packageName)) { // // apk app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode, mProcessStats); } ensurePackageDexOpt(cpi.applicationInfo.packageName); } } return providers; } ...... }

ApplicationThread bindApplication AppBindData mH H handleMessage

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread { ...... private class ApplicationThread extends ApplicationThreadNative { ...... public final void bindApplication(String processName, ApplicationInfo appInfo, List<ProviderInfo> providers, ComponentName instrumentationName, ProfilerInfo profilerInfo, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, IUiAutomationConnection instrumentationUiConnection, int debugMode, boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle 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.enableOpenGlTrace = enableOpenGlTrace; data.restrictedBackupMode = isRestrictedBackupMode; data.persistent = persistent; data.config = config; data.compatInfo = compatInfo; data.initProfilerInfo = profilerInfo; sendMessage(H.BIND_APPLICATION, data); } ...... } ...... private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { if (DEBUG_MESSAGES) Slog.v( TAG, "SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + "/" + obj); Message msg = Message.obtain(); msg.what = what; msg.obj = obj; msg.arg1 = arg1; msg.arg2 = arg2; if (async) { msg.setAsynchronous(true); } mH.sendMessage(msg); } ...... }

handleMessage AppBindData ActivityThread handleBindApplication

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread { ...... 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; ...... } } ...... } ...... }

handleBindApplication AppBindData List installContentProviders

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread { ...... private void handleBindApplication(AppBindData data) { ...... // final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); try { // application Application app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; // Application if (!data.restrictedBackupMode) { List<ProviderInfo> providers = data.providers; if (providers != null) { installContentProviders(app, providers); // JIT mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); } } ...... } finally { StrictMode.setThreadPolicy(savedPolicy); } } ...... }

installContentProviders ProviderInfo installProvider publishContentProviders

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread { ...... private void installContentProviders( Context context, List<ProviderInfo> providers) { final ArrayList<IActivityManager.ContentProviderHolder> results = new ArrayList<IActivityManager.ContentProviderHolder>(); 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()); } //1. IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); if (cph != null) { cph.noReleaseNeeded = true; results.add(cph); } } try { //2. ActivityManagerNative.getDefault().publishContentProviders( getApplicationThread(), results); } catch (RemoteException ex) { } } ...... }

1. ContentProvider ContentProviderHolder provider ContentProviderHolder

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread { ...... private IActivityManager.ContentProviderHolder installProvider(Context context, IActivityManager.ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ContentProvider localProvider = null; IContentProvider provider; if (holder == null || holder.provider == null) { 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 { 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(); // ContentProvider 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); // ContentProvider onCreate 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(); 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); // ContentProviderHolder provider holder.provider = provider; holder.noReleaseNeeded = true; pr = installProviderAuthoritiesLocked(provider, localProvider, holder); mLocalProviders.put(jBinder, pr); mLocalProvidersByName.put(cname, pr); } retHolder = pr.mHolder; } else { ...... } } return retHolder; } ...... }

ContentProvider attachInfo onCreate Demo DemoProvider onCreate

frameworks/base/core/java/android/content/ContentProvider.java

public abstract class ContentProvider implements ComponentCallbacks2 { ...... 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); } ContentProvider.this.onCreate(); } } ...... }

2. ActivityManagerProxy ActivityManagerService publishContentProviders

frameworks/base/core/java/android/app/ActivityManagerNative.java

class ActivityManagerProxy implements IActivityManager { ...... public void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); data.writeTypedList(providers); mRemote.transact(PUBLISH_CONTENT_PROVIDERS_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); } ...... }

ActivityManagerService publishContentProviders

1. List ProcessRecord pubProviders key value ContentProviderRecord Map

2.mLaunchingProviders

3. ContentProviderRecord provider notifyAll ActivityManagerService getContentProviderImpl getContentProviderImpl

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... public final void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) { if (providers == null) { return; } enforceNotIsolatedCaller("publishContentProviders"); synchronized (this) { final ProcessRecord r = getRecordForAppLocked(caller); if (DEBUG_MU) Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid); if (r == null) { throw new SecurityException( "Unable to find app for caller " + caller + " (pid=" + Binder.getCallingPid() + ") when publishing content providers"); } final long origId = Binder.clearCallingIdentity(); final int N = providers.size(); for (int i = 0; i < N; i++) { ContentProviderHolder src = providers.get(i); if (src == null || src.info == null || src.provider == null) { continue; } ContentProviderRecord dst = r.pubProviders.get(src.info.name); if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid); 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++) { 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); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } } Binder.restoreCallingIdentity(origId); } } ...... }

ActivityManagerService getContentProviderImpl 2.

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... ContentProviderConnection incProviderCountLocked(ProcessRecord r, final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) { if (r != null) { ...... ContentProviderConnection conn = new ContentProviderConnection(cpr, r); if (stable) { conn.stableCount = 1; conn.numStableIncs = 1; } else { ...... } cpr.connections.add(conn); r.conProviders.add(conn); startAssociationLocked(r.uid, r.processName, cpr.uid, cpr.name, cpr.info.processName); return conn; } cpr.addExternalProcessHandleLocked(externalProcessToken); return null; } ...... }

incProviderCountLocked ContentProviderConnection conn.stableCount conn.numStableIncs 1 ContentProviderRecord ProcessRecord ContentProviderRecord ContentProviderConnection startAssociationLocked Association

ActivityManagerService getContentProviderImpl ContentProviderHolder cpr.newHolder(conn) ContentProviderHolder

frameworks/base/services/core/java/com/android/server/am/ContentProviderRecord.java

final class ContentProviderRecord { ...... public ContentProviderHolder newHolder(ContentProviderConnection conn) { ContentProviderHolder holder = new ContentProviderHolder(info); holder.provider = provider; holder.noReleaseNeeded = noReleaseNeeded; holder.connection = conn; return holder; } ...... }

ContentProviderRecord newHolder ContentProviderHolder provider noReleaseNeeded connection

frameworks/base/core/java/android/app/IActivityManager.java

public interface IActivityManager extends IInterface { ...... /** */ public static class ContentProviderHolder implements Parcelable { public final ProviderInfo info; public IContentProvider provider; public IBinder connection; public boolean noReleaseNeeded; public ContentProviderHolder(ProviderInfo _info) { info = _info; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { info.writeToParcel(dest, 0); if (provider != null) { dest.writeStrongBinder(provider.asBinder()); } else { dest.writeStrongBinder(null); } dest.writeStrongBinder(connection); dest.writeInt(noReleaseNeeded ? 1 : 0); } public static final Parcelable.Creator<ContentProviderHolder> CREATOR = new Parcelable.Creator<ContentProviderHolder>() { @Override public ContentProviderHolder createFromParcel(Parcel source) { return new ContentProviderHolder(source); } @Override public ContentProviderHolder[] newArray(int size) { return new ContentProviderHolder[size]; } }; private ContentProviderHolder(Parcel source) { info = ProviderInfo.CREATOR.createFromParcel(source); provider = ContentProviderNative.asInterface( source.readStrongBinder()); connection = source.readStrongBinder(); noReleaseNeeded = source.readInt() != 0; } } ...... }

ActivityManagerService getContentProviderImpl ContentProviderHolder ActivityManagerProxy getContentProvider ContentProviderHolder provider provider.asBinder() ContentProviderNative.asInterface ContentProviderProxy

frameworks/base/core/java/android/app/ActivityManagerNative.java

class ActivityManagerProxy implements IActivityManager { ...... public ContentProviderHolder getContentProvider(IApplicationThread caller, String name, int userId, boolean stable) throws RemoteException { ...... ContentProviderHolder cph = null; if (res != 0) { cph = ContentProviderHolder.CREATOR.createFromParcel(reply); } ...... return cph; } ...... }

ActivityThread acquireProvider IActivityManager.ContentProviderHolder IContentProvider ContentProviderProxy

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread { ...... public final IContentProvider acquireProvider( Context c, String auth, int userId, boolean stable) { final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable); ...... IActivityManager.ContentProviderHolder holder = null; 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; } ...... return holder.provider; } ...... }