In the previous chapter: Android Dual Open Sandbox VirtualApp Source Code Analysis (V) Broadcast Receiver
Provider registration
In retrospect, when Activity starts, it checks if the Application is initialized and calls bindApplication, which executes the method of installing Provider:
private void installContentProviders(Context app, List<ProviderInfo> providers) {
long origId = Binder.clearCallingIdentity();
Object mainThread = VirtualCore.mainThread();
try {
for (ProviderInfo cpi : providers) {
if (cpi.enabled) {
ActivityThread.installProvider(mainThread, app, cpi, null);
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
This is very simple. Calling ActivityThread.installProvider() to register is done.
But when you think about it carefully, it's not that simple. According to this logic, ContentProvider is registered when Application is started. If Application is not started, then there will be no registration. So how can other Apps find Provider?
Activity is because there is a tubActivity registered in the Minefiest of VA, so starting StubActivity naturally starts the process of': p(n)'. Correspondingly, does VA use StubContentProvider? Yes, indeed.
But please don't get me wrong, this StubContentProvider registered in the "p(n)" process (inherited from StubContentProvider's C1, C2, Cn... ) It's not a tub component like StubActivity that's meant to take a hole in the plug-in Provider.
The real purpose of StubContentProvider is to let AMS bring up the "p(n) process" through system_process, and then VAMS holds the IBinder handle of the plug-in IClient by calling StubProvider.call() remotely. In this way, VAMS can call the method in the plug-in process "p(n)" remotely.
As a matter of fact, I said something about the second App when it was launched, but it is necessary to repeat it in order to avoid misunderstanding.
public class StubContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
return true;
}
@Override
public Bundle call(String method, String arg, Bundle extras) {
if ("_VA_|_init_process_".equals(method)) {
return initProcess(extras);
}
return null;
}
private Bundle initProcess(Bundle extras) {
ConditionVariable lock = VirtualCore.get().getInitLock();
if (lock != null) {
lock.block();
}
IBinder token = BundleCompat.getBinder(extras,"_VA_|_binder_");
int vuid = extras.getInt("_VA_|_vuid_");
VClientImpl client = VClientImpl.get();
client.initProcess(token, vuid);
Bundle res = new Bundle();
BundleCompat.putBinder(res, "_VA_|_client_", client.asBinder());
res.putInt("_VA_|_pid_", Process.myPid());
return res;
}
getContentProvider
Hook still has the getContentProvider method here:
static class GetContentProvider extends MethodProxy {
@Override
public String getMethodName() {
return "getContentProvider";
}
@Override
public Object call(Object who, Method method, Object... args) throws Throwable {
int nameIdx = getProviderNameIndex();
String name = (String) args[nameIdx];
int userId = VUserHandle.myUserId();
// Call VPMS remotely to get ProviderInfo from VPackage
ProviderInfo info = VPackageManager.get().resolveContentProvider(name, 0, userId);
// Failure to get the indication that the target App is not started
if (info != null && info.enabled && isAppPkg(info.packageName)) {
// The VAMS is called remotely, and then the VAMS initializes the plug-in process by calling StubContentProvider.call registered in the plug-in process remotely via AMS.
int targetVPid = VActivityManager.get().initProcess(info.packageName, info.processName, userId);
if (targetVPid == -1) {
return null;
}
args[nameIdx] = VASettings.getStubAuthority(targetVPid);
Object holder = method.invoke(who, args);
if (holder == null) {
return null;
}
// ContentProvider Holder has two member variables, provider and connection. Provider is the IBinder handle of the target Provider.
// connection is callback
if (BuildCompat.isOreo()) {
IInterface provider = ContentProviderHolderOreo.provider.get(holder);
if (provider != null) {
// Here's the point. The acquireProvider Client of VAMS is called remotely.
provider = VActivityManager.get().acquireProviderClient(userId, info);
}
ContentProviderHolderOreo.provider.set(holder, provider);
ContentProviderHolderOreo.info.set(holder, info);
} else {
IInterface provider = IActivityManager.ContentProviderHolder.provider.get(holder);
if (provider != null) {
provider = VActivityManager.get().acquireProviderClient(userId, info);
}
IActivityManager.ContentProviderHolder.provider.set(holder, provider);
IActivityManager.ContentProviderHolder.info.set(holder, info);
}
return holder;
}
Object holder = method.invoke(who, args);
if (holder != null) {
if (BuildCompat.isOreo()) {
IInterface provider = ContentProviderHolderOreo.provider.get(holder);
info = ContentProviderHolderOreo.info.get(holder);
if (provider != null) {
provider = ProviderHook.createProxy(true, info.authority, provider);
}
ContentProviderHolderOreo.provider.set(holder, provider);
} else {
IInterface provider = IActivityManager.ContentProviderHolder.provider.get(holder);
info = IActivityManager.ContentProviderHolder.info.get(holder);
if (provider != null) {
provider = ProviderHook.createProxy(true, info.authority, provider);
}
IActivityManager.ContentProviderHolder.provider.set(holder, provider);
}
return holder;
}
return null;
}
public int getProviderNameIndex() {
return 1;
}
@Override
public boolean isEnable() {
return isAppProcess();
}
}
Here are a few main things to do:
- Get the parsed target Provider Info through VPMS and start the process where the target Provider is located. How to start? The VAMS is called remotely, and then the VAMS initializes the plug-in process by calling StubContentProvider.call registered in the plug-in process remotely via AMS. At this point, VAMS will hold the IClient handle of the target plug-in process for subsequent calls.
- To prepare for the ContentProvider Holder, ContentProvider Holder has two member variables, provider and connection, which are the IBinder handle of the target Provider.
- The acquireProviderClient() of VAMS is called remotely, and the task is thrown to the remote VAMS.
Let's take a look at VAMS.acquireProviderClient()
@Override
public IBinder acquireProviderClient(int userId, ProviderInfo info) {
ProcessRecord callerApp;
synchronized (mPidsSelfLocked) {
callerApp = findProcessLocked(VBinder.getCallingPid());
}
if (callerApp == null) {
throw new SecurityException("Who are you?");
}
String processName = info.processName;
ProcessRecord r;
synchronized (this) {
r = startProcessIfNeedLocked(processName, userId, info.packageName);
}
if (r != null && r.client.asBinder().isBinderAlive()) {
try {
return r.client.acquireProviderClient(info);
} catch (RemoteException e) {
e.printStackTrace();
}
}
return null;
}
You can see that r.client.acquireProviderClient(info) is called;
r.client is the IClient handle of the plug-in process saved from the previous initProcess, which is equivalent to the remote call to the plug-in's VClientImpl.acquireProviderClient():
Note that the start process is now in the Client App process of the target Provider
@Override
public IBinder acquireProviderClient(ProviderInfo info) {
if (mTempLock != null) {
mTempLock.block();
}
// Here, check if the Application is started, and note that the logic for registering Provider is also in it.
if (!isBound()) {
VClientImpl.get().bindApplication(info.packageName, info.processName);
}
// Prepare Content Provider Client
IInterface provider = null;
String[] authorities = info.authority.split(";");
String authority = authorities.length == 0 ? info.authority : authorities[0];
ContentResolver resolver = VirtualCore.get().getContext().getContentResolver();
ContentProviderClient client = null;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
client = resolver.acquireUnstableContentProviderClient(authority);
} else {
client = resolver.acquireContentProviderClient(authority);
}
} catch (Throwable e) {
e.printStackTrace();
}
if (client != null) {
// Reflective acquisition provider
provider = mirror.android.content.ContentProviderClient.mContentProvider.get(client);
client.release();
}
return provider != null ? provider.asBinder() : null;
}
Finally, you see where the bindApplication is called. If the provider is not registered, then the provider will be registered here.
Finally, the handle of the target provider is obtained by reflecting android.content.ContentProviderClient.mContentProvider, and then the provider goes back to the calling process of Client App along the target Client App process - > VAMS process - > and the whole acquisition process is formally completed.
So far, the agents of the four components have been completed, and the analysis is basically finished. The next chapter will write some summaries and supplementary contents.