Using ContentProvider for Cross‑Process Communication with UIAutomator 1.0 on Android
This article explains how to keep UIAutomator 1.0 test cases running on Android 11+ by implementing a server‑side ContentProvider, accessing it via reflection from the UIAutomator or shell process, and demonstrates the complete Java code for provider creation and invocation.
Background – UIAutomator 1.0 is deprecated after Android 11, but test cases can still be executed by launching the UIAutomator 1.0 service through reflection. The article proposes using a ContentProvider in a server app to expose services even when the server app is not running, allowing UIAutomator 1.0 to communicate via the provider.
Implementation – server app ContentProvider
package com.xxxx.xxxx.ticker.server;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
/**
* @author walker
* @date 2021/1/29.
* @description Provides a ContentProvider interface for external calls
*/
public class CommonProvider_tme extends ContentProvider {
@Override
public boolean onCreate() {
init();
return false;
}
private void init() {}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; }
@Override
public String getType(Uri uri) { return null; }
@Override
public Uri insert(Uri uri, ContentValues values) { return null; }
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; }
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; }
public static final String RES_DATA = "data";
public static final String RES_CODE = "code";
public static final String RES_INFO = "info";
@Override
public Bundle call(String authority, String method, String arg, Bundle extras) {
Bundle resBundle = new Bundle();
System.out.println("®®® call contentprovider authority=" + authority + "; method=" + method + "; arg=" + arg + "; extras" + extras);
try {
if (method == null || method.trim().length() == 0) {
resBundle.putString(RES_DATA, "");
resBundle.putInt(RES_CODE, 404);
resBundle.putString(RES_INFO, "Provider错误:必须指定调用方法名");
return resBundle;
}
if (!"com.xxxx.xxxx.xxxx".equals(authority)) {
resBundle.putString(RES_DATA, "");
resBundle.putInt(RES_CODE, 100);
resBundle.putString(RES_INFO, "Provider错误:必须指定有效authorities信息");
return resBundle;
}
resBundle.putString(RES_INFO, "ok");
resBundle.putInt(RES_CODE, 200);
boolean res = true;
// switch(method) { ... }
if (!res) {
resBundle.putInt(RES_CODE, 500);
}
} catch (Exception e) {
resBundle.putInt(RES_CODE, 501);
resBundle.putString(RES_INFO, e.getMessage());
}
return resBundle;
}
}In the Android manifest, declare the provider with matching android:authorities :
<application ...>
<provider android:name=".server.CommonProvider"
android:authorities="com.xxxx.xxxx.xxxx"
android:exported="true"
android:enabled="true" />
</application>Accessing the ContentProvider from a Shell or UIAutomator process
Obtain IActivityManager via ActivityManagerNative.getDefault() .
Use reflection to call getContentProviderExternal (different signatures for SDK >28 and ≤28) to retrieve a ContentProviderHolder .
Extract the actual IContentProvider instance from the holder's provider field.
Invoke the provider's call method with appropriate parameters ( calling_package , authority , method , arg , extras ), handling SDK version differences.
// Example of obtaining the provider (SDK > 28)
IActivityManager activityManager = (IActivityManager) ActivityManagerNative.getDefault();
Method method = activityManager.getClass().getDeclaredMethod(
"getContentProviderExternal", String.class, int.class, IBinder.class, String.class);
Object holder = method.invoke(activityManager, authority, USER_SYSTEM, token, null);
Field field = holder.getClass().getDeclaredField("provider");
field.setAccessible(true);
IContentProvider provider = (IContentProvider) field.get(holder);Calling the provider (SDK ≥30 example):
Method callMethod = provider.getClass().getDeclaredMethod(
"call", String.class, String.class, String.class, String.class,
String.class, String.class, Bundle.class);
Bundle result = (Bundle) callMethod.invoke(provider, calling_package, attributionTag,
authority, method, arg, extras);Full helper class – The article provides a ShellContentProvider class that encapsulates the above steps, exposing callProvider methods for convenient use.
public class ShellContentProvider {
int USER_SYSTEM = 0;
String calling_package = "com.android.shell";
String authority = "com.xxxx.xxxx.xxxx";
IContentProvider provider = null;
// initProvider(), callProvider(...) implementations as shown in the article
}Finally, an example of invoking a custom method:
Bundle bundle = new Bundle();
bundle.putBoolean("isCalled", true);
bundle.putInt("callFlag", 1);
bundle.putString("arg", "test");
Bundle res = new ShellContentProvider().callProvider("testMethod", "hello", bundle);The ContentProvider architecture is a core Android IPC mechanism, and the presented solution enables UIAutomator 1.0 test cases to continue working on newer Android versions by leveraging this mechanism.
360 Quality & Efficiency
360 Quality & Efficiency focuses on seamlessly integrating quality and efficiency in R&D, sharing 360’s internal best practices with industry peers to foster collaboration among Chinese enterprises and drive greater efficiency value.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.