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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
