【Android】使用deviceowner 配置手机设置 (Monkey自动化测试删去状态栏、设定输入法、静音、APP自动权限授予、Kiosk模式打开)

背景:

需要使用Monkey进行自动化测试,但是测试时Monkey经常会下拉状态栏导致网络丢失等一系列问题,当前直接在百度上搜到的方法都是使用adb让屏幕全屏,但是我的测试机并不支持这个行为,所以需要找其它的方法。

初步方法汇总

在谷歌上搜索,找到了三种解决办法:

1. 屏幕固定功能ScreenPinning

这个功能可以在手机设置中开启,开启后可以固定手机只使用这个应用且屏蔽导航栏,开启需要使用虚拟按键。

所谓虚拟按键指的是屏幕下方出现返回、主页面、多任务 这些按钮,与之相对的是手势,也就是一般手机上推屏幕会出现多任务管理这个功能。

使用流程是:开启功能后,打开多任务管理,长按app,会出现锁定按钮,点击即可锁定。关闭是同时长按主页面和返回。

我的情况不太支持使用这个功能,首先我是对一些列app进行连续monkey测试,每次切换app锁定很困难,可能需要使用Appium之类的配合,其次,我需要app的截图,开启这个功能需要使用虚拟按键,app的截图会多出来虚拟按键的部分

2. 使用BroadcastReceiver打开和关闭WIFI

这个是我结合下面两个文章想到的
Android判断wifi状态 监听wifi连接
Android中使用BroadcastReceiver打开和关闭WIFI
完全可以试着写一个app监听wifi状态,在wifi关闭的时候自己设置打开wifi。
但是我没有实现这个想法。

3. 调用DeviceOwner中API,关闭状态栏。

最后一个方法就是使用DeviceOwner的API,
依旧是写个APP,调用Android中设备管理相关类的API

DevicePolicyManager下面的setStatusDisabled 函数
在这里插入图片描述

DeviceOwner设置和使用

概念

下面介绍几个概念:
device-owner:特殊的device-admin
设备管理者 device-admin
https://developer.android.com/guide/topics/admin/device-admin

app结构

常规的就是写一个最简单的app + 可以调用deviceOwner的一些设置
要完成调用deviceOwner首先,需要激活device-admin,此时可以使用一些deviceadmin相关的API设置,比如关闭相机等等,激活完device-admin再激活deviceOwner,之后就可以使用deviceOwner相关的API了

我自己这里是参照大佬的设计http://floatingmuseum.github.io/2016/07/device-admin-practice
也写成了设置界面的样式

但是核心设置其实就几点:https://github.com/XYScience/DeviceOwner
上面这个大佬的readme写的很清晰,我的app也基本参照两位大佬的代码

代码分析

下面对大佬的代码进行一下讲解
首先是device-admin编写:

1. 在res/xml目录下新建device_admin.xml文件;

<?xml version="1.0" encoding="utf-8"?>
<device-admin
    xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-policies>
    </uses-policies>
</device-admin>

2. 注册一个广播继承DeviceAdminReceiver;

public class MyDeviceAdminReceiver extends DeviceAdminReceiver{

    @Override
    public void onProfileProvisioningComplete(Context context, Intent intent) {
        super.onProfileProvisioningComplete(context, intent);
    }

    @Override
    public void onEnabled(Context context, Intent intent) {
        super.onEnabled(context, intent);
    }

    @Override
    public CharSequence onDisableRequested(Context context, Intent intent) {
        return super.onDisableRequested(context, intent);
    }

    @Override
    public void onDisabled(Context context, Intent intent) {
        super.onDisabled(context, intent);
    }

    @Override
    public void onPasswordChanged(Context context, Intent intent) {
        super.onPasswordChanged(context, intent);
        Logger.d("onPasswordChanged");
    }

    @Override
    public void onPasswordFailed(Context context, Intent intent) {
        super.onPasswordFailed(context, intent);
        Logger.d("onPasswordFailed");
    }

    @Override
    public void onPasswordSucceeded(Context context, Intent intent) {
        super.onPasswordSucceeded(context, intent);
        Logger.d("onPasswordSucceeded");
    }

    @Override
    public void onPasswordExpiring(Context context, Intent intent) {
        super.onPasswordExpiring(context, intent);
        Logger.d("onPasswordExpiring");
    }

    /**
     * 获取ComponentName,DevicePolicyManager的大多数方法都会用到
     */
    public static ComponentName getComponentName(Context context) {
        return new ComponentName(context.getApplicationContext(), MyDeviceAdminReceiver.class);
    }
}

3. 在清单文件里注册广播;

<receiver
   android:name=".MyDeviceAdminReceiver"
   android:permission="android.permission.BIND_DEVICE_ADMIN">
   <meta-data
      android:name="android.app.device_admin"
      android:resource="@xml/device_admin"/>
   <intent-filter>
      <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
      <action android:name="android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED"/>
      <action android:name="android.app.action.DEVICE_ADMIN_DISABLED"/>
   </intent-filter>
</receiver>

这样就完成了一个device-admin广播,之后就是激活和使用

4. 在其它类中激活设备管理器

激活的核心代码:

if (!devicePolicyManager.isAdminActive(comName)) {
    val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)
    intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, comName)
    intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "激活此设备管理员后可免root停用应用")
    startActivityForResult(intent, 1)
} else {
    Toast.makeText(this, "此App已激活设备管理器", Toast.LENGTH_SHORT).show()
}

这段代码可以放在合适的地方使用,下面的代码,

dpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);

获取dpm在Fragment的onCreate中,
大佬app设置使用了PreferenceFragment(已过时,类似setting结构的fragment),点击对应preference后触发getAdmin()函数,激活deviceadmin,具体代码可以去看第一位大佬的github分析


 @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.pref_deviceadmin);
        initListener();
        activity = DeviceAdminFragment.this.getActivity();
        if (dpm == null) {
            dpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
        }

        if (pm == null) {
            pm = activity.getPackageManager();
        }
        mComponentName = MyDeviceAdminReceiver.getComponentName(activity);
        if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.LOLLIPOP){
            Logger.d("isProfileOwnerApp:"+dpm.isProfileOwnerApp(activity.getPackageName()));
        }
    }

    private boolean checkDeviceAdminEnabled() {
        return dpm.isAdminActive(mComponentName);
    }

    private void getDeviceAdmin() {
        if (checkDeviceAdminEnabled()) {
            ToastUtil.show("已激活");
            return;
        }
        Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
        intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
                mComponentName);
        intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
                getString(R.string.device_admin_description));
        startActivity(intent);
    }

5. 设置device-admin权限

在使用device-admin的API时,有些设置需要特殊的权限
权限常量
对应在xml中设置政策:

<device-admin xmlns:android="http://schemas.android.com/apk/res/android" >
    <uses-policies>
        <!-- 设置密码规则 -->
        <limit-password />
        <!-- 监视屏幕解锁尝试次数 -->
        <watch-login />
        <!-- 更改解锁密码 -->
        <reset-password />
        <!-- 锁定屏幕 -->
        <force-lock />
        <!-- 清除数据,恢复出厂模式,在不发出警告的情况下 -->
        <wipe-data />
        <!-- 锁屏密码有效期 -->
        <expire-password />
        <!-- 对存储的应用数据加密 -->
        <encrypted-storage />
        <!-- 禁用锁屏信息 -->
        <disable-keyguard-features/>
        <!-- 禁用摄像头 -->
        <disable-camera />
    </uses-policies>
</device-admin>
<!--和<application>同级 --> 

6. 之后就是激活deviceOwner

现在一般有三种方式:我自己使用的是第二种adb的方式
利用ADB命令

$adb shell dpm set-device-owner com.example.deviceowner/.MyDeviceAdminReceiver

java.lang.IllegalStateException: Not allowed to set the device owner because there are already some accounts on the device

设置-账号中退出所有账户,然后重新尝试ADB设置

激活DeviceOwner时遇到的问题
1. 最初我直接在PC命令行输入 adb dpm set-…… 这一大串命令的时候,报错 Error: Bad admin: ComponentInfo{com.example.deviceowner/com.example.deviceowner.MyDeviceAdminReceiver}

如下图情况:会有一大堆使用说明最后有一点错误信息

D:\UIDump-master>adb shell dpm set-device-owner com.example.deviceowner/.MyDeviceAdminReceiver
usage: dpm [subcommand] [options]
usage: dpm set-active-admin [ --user <USER_ID> | current ]
usage: dpm set-device-owner [ --user <USER_ID> | current EXPERIMENTAL ] [ --name ]
usage: dpm set-profile-owner [ --user <USER_ID> | current ] [ --name ]
usage: dpm remove-active-admin [ --user <USER_ID> | current ] [ --name ]
dpm set-active-admin: Sets the given component as active admin for an existing user.
dpm set-device-owner: Sets the given component as active admin, and its package as device owner.
dpm set-profile-owner: Sets the given component as active admin and profile owner for an existing user.
dpm remove-active-admin: Disables an active admin, the admin must have declared android:testOnly in the application in its
manifest. This will also remove device and profile owners.
dpm clear-freeze-period-record: clears framework-maintained record of past freeze periods that the device went through. For use during feature development to prevent triggering restriction on setting freeze periods.
dpm force-network-logs: makes all network logs available to the DPC and triggers DeviceAdminReceiver.onNetworkLogsAvailable() if needed.
dpm force-security-logs: makes all security logs available to the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed.
usage: dpm mark-profile-owner-on-organization-owned-device: [ --user <USER_ID> | current ]
Error: Bad admin: ComponentInfo{com.example.deviceowner/com.example.deviceowner.MyDeviceAdminReceiver}
在这里插入图片描述

当我先使用adb shell进入手机命令行,再使用dpm命令时就不会了

2. 经典错误 java.lang.IllegalStateException: Not allowed to set the device owner because there are already some accounts on the device

java.lang.IllegalStateException: Not allowed to set the device owner because there are already some accounts on the device
at android.os.Parcel.createExceptionOrNull(Parcel.java:2381)
at android.os.Parcel.createException(Parcel.java:2357)
at android.os.Parcel.readException(Parcel.java:2340)
at android.os.Parcel.readException(Parcel.java:2282)
at android.app.admin.IDevicePolicyManager S t u b Stub StubProxy.setDeviceOwner(IDevicePolicyManager.java:8665)
at com.android.commands.dpm.Dpm.runSetDeviceOwner(Dpm.java:203)
at com.android.commands.dpm.Dpm.onRun(Dpm.java:115)
at com.android.internal.os.BaseCommand.run(BaseCommand.java:60)
at com.android.commands.dpm.Dpm.main(Dpm.java:41)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:438)
在这里插入图片描述

这个错误很常见,简单说就是这个DeviceOwner本来就只是希望在新的测试设备上使用,所有不能有其他账户和他绑定。

具体解释可以看 Not allowed to set the device owner because there are already several users on the device

“设备所有者只能在未配置的设备上设置,除非它是由“adb”启动的,在这种情况下,如果没有与设备关联的帐户,我们会允许它”。因此,在使用 dpm 命令之前,请确保您没有与当前用户集关联的任何帐户(例如 Gmail) “

关于账号处理问题,我有看到别人比较详细的处理方法总结:
adb設置DeviceOwner發生問題

下面记录一下我自己采用的方法,就是简单直接全部删除了,本来我想备份一下来着,但是没成果。

I. 删掉多的userInfo
adb shell pm list users

Notallowedtosetthedevice owner because there are already some accounts on thedevice
在这里插入图片描述
删掉除了0以外的账户

adb shell pm remove-user 999
II. 删掉account
adb shell dunmpsys account

可以看到所有的账号
在这里插入图片描述
直接打开手机设置->里面有accounts,然后删掉就行,有些删除不了的账户可以直接卸载应用
最后再执行dpm就可以了
在这里插入图片描述

7. 调用deviceOwner的API

激活deviceOwner后即可调用需要的API,
我自己是使用了三种,状态栏不可用,静音、APP自动权限授予
具体有哪些函数可以去看安卓文档,功能还是很强大的
https://developer.android.com/reference/android/app/admin/DevicePolicyManager

这里给一个设置静音和状态栏不可用的样例,就是调用相应API

   dpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);

dpm也是放在了onCreate中获取

  private void setVolumeMuted() {
        boolean muted = SPUtil.getBoolean(activity, "muted", false);
        Logger.d("静音:" + muted);
        dpm.setMasterVolumeMuted(mComponentName, !muted);
        SPUtil.editBoolean(activity, "muted", !muted);
    }
    
  @TargetApi(Build.VERSION_CODES.M)
  private void disableStatusBar() {
        boolean result = dpm.setStatusBarDisabled(mComponentName,statusbar);
        statusbar = !statusbar;
        Logger.d("result:"+result+"...statusbar:"+statusbar);
    }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值