栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Shell 进程通过 ContentProvider 实现跨进程通信

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Shell 进程通过 ContentProvider 实现跨进程通信

背景

Android系统的UI测试框架有Uiautomator1.0和Uiautomator2.0,虽然Uiautomator1.0在Android11及以后的版本被放弃了,但是我们仍然可以通过反射FrameWork代码初的方式始化Uiautomator1.0服务,这样我们原有的测试用例就可以继续运行了。

今天分享一下Uiautomator1.0与server app跨进程通信的方案,我们一般情况下都使用socket进行通信,但是当server app没有运行时就很难及时处理,我们让server app实现ContentProvider来对外提供服务,即使server app没有运行,我们也能正常调用服务,系统会自动帮我们启动server app。

在应用(server app)内提供ContentProvider服务是很简单的,但是Uiautomator1.0和其他Shell进程是没有办法直接访问的,我们需要依赖反射技术进行调用。

实现

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;



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;
    }
}
  1. 在清单文件中声明
<application
    ...>
     <provider android:name=".server.CommonProvider"
android:authorities="com.xxxx.xxxx.xxxx"
            android:exported="true"
            android:enabled="true"
            />
</application>

在Shell进程或Uiautomator1.0内访问CommonProvider

IActivityManager activityManager = (IActivityManager) ActivityManagerNative.getDefault();
  1. 通过反射机制获取ContentProviderHolder
if (Build.VERSION.SDK_INT > 28) {
    Method method=activityManager.getClass().getDeclaredMethod("getContentProviderExternal",String.class,int.class, IBinder.class,String.class);
    holder=method.invoke(activityManager,authority, USER_SYSTEM, token, null);
} else {
    Method method=activityManager.getClass().getDeclaredMethod("getContentProviderExternal",String.class,int.class, IBinder.class);
    holder=method.invoke(activityManager,authority, USER_SYSTEM, token);
}
  1. 读取ContentProviderHolder的provider字段
Field field =holder.getClass().getDeclaredField("provider");
field.setAccessible(true);
provider = (IContentProvider)field.get(holder);
  1. 调用prover服务

**provider:**前面得到的IContentProvider对象

**calling_package:**指定任意字符串即可

**attributionTag:**无意义字符串,传递NULL即可

**authority:**校验字符串,必须与server app内指定的保持一致

**method:**调用的方法名

**arg:**访问字符串参数,一般不用

if (Build.VERSION.SDK_INT >= 30) {
    try {
        Method method1=provider.getClass().getDeclaredMethod("call", String.class, String.class, String.class, String.class, String.class, Bundle.class);
        return (Bundle) method1.invoke(provider,calling_package,attributionTag,authority,method,arg,extras);
    } catch (Exception e) {
        System.err.println(e.getMessage());
    }
} else if (Build.VERSION.SDK_INT > 28) {
    try {
        Method method1=provider.getClass().getDeclaredMethod("call", String.class, String.class, String.class,  String.class, Bundle.class);
        return (Bundle) method1.invoke(provider,calling_package,authority,method,arg,extras);
    } catch (Exception e) {
        System.err.println(e.getMessage());
    }
} else {
    return provider.call(calling_package, method, arg, extras);
}

附完整代码

public class ShellContentProvider {

int USER_SYSTEM = 0;

String calling_package = "com.android.shell";

String authority = "com.xxxx.xxxx.xxxx";

public ShellContentProvider() {

initProvider();

}

IContentProvider provider = null;

void initProvider(){

try {

IActivityManager activityManager = (IActivityManager) ActivityManagerNative.getDefault();

final IBinder token = new Binder();

Object holder = null;

try {

if (Build.VERSION.SDK_INT > 28) {

Method method=activityManager.getClass().getDeclaredMethod("getContentProviderExternal",String.class,int.class, IBinder.class,String.class);

holder=method.invoke(activityManager,authority, USER_SYSTEM, token, null);

} else {

Method method=activityManager.getClass().getDeclaredMethod("getContentProviderExternal",String.class,int.class, IBinder.class);

holder=method.invoke(activityManager,authority, USER_SYSTEM, token);

}

if (holder == null) {

throw new IllegalStateException("Could not find provider");

}

Field field =holder.getClass().getDeclaredField("provider");

field.setAccessible(true);

provider = (IContentProvider)field.get(holder);

} finally {

}

} catch (Exception e) {

System.err.println("Error while accessing settings provider");

System.err.println(e.getMessage());

}

}

public Bundle callProvider( final String authority, final String method, final String arg, final Bundle extras){

return callProvider(provider,calling_package,null,authority,method,arg,extras);

}

public Bundle callProvider(final String method, final String arg, final Bundle extras){

return callProvider(provider,calling_package,null,authority,method,arg,extras);

}

private Bundle callProvider(IContentProvider provider, final String calling_package, final String attributionTag, final String authority, final String method, final String arg, final Bundle extras) {

if(provider==null){

initProvider();

}

try {

if (Build.VERSION.SDK_INT >= 30) {

try {

Method method1=provider.getClass().getDeclaredMethod("call", String.class, String.class, String.class, String.class, String.class, Bundle.class);

return (Bundle) method1.invoke(provider,calling_package,attributionTag,authority,method,arg,extras);

} catch (Exception e) {

System.err.println(e.getMessage());

}

} else if (Build.VERSION.SDK_INT > 28) {

try {

Method method1=provider.getClass().getDeclaredMethod("call", String.class, String.class, String.class, String.class, Bundle.class);

return (Bundle) method1.invoke(provider,calling_package,authority,method,arg,extras);

} catch (Exception e) {

System.err.println(e.getMessage());

}

} else {

return provider.call(calling_package, method, arg, extras);

}

} catch (RemoteException e) {

System.err.println("Can't set key " + arg + " for user " + USER_SYSTEM);

}

return null;

}

}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1028451.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号