[Android] Use deviceowner to configure mobile phone settings (Monkey automated test to delete the status bar, set the input method, mute, APP automatic permission grant, Kiosk mode open)

background:

Monkey needs to be used 自动化测试, but when testing , Monkeyit often pulls down 状态栏and causes 网络丢失a series of problems. The current method directly found on Baidu is to use adb to make the screen full screen, but my test machine does not support this behavior, so I need to find other methods. method.

Summary of preliminary methods

Searching on Google, I found three solutions:

1. Screen pinning function ScreenPinning

This function can be turned on in the phone settings. After it is turned on, the phone can be fixed to use only this app and the navigation bar is blocked. To turn it on, you need to use a virtual button.

The so-called virtual buttons refer to buttons such as return, home page, and multitasking that appear at the bottom of the screen. The opposite is gestures, that is, the function of multitasking management will appear when pushing the screen on a general mobile phone.

The usage process is: after enabling the function, open the multitasking management, press and hold the app, a lock button will appear, click to lock. To close is to press and hold the main page and return at the same time.

My situation does not support the use of this function. First of all, I conduct continuous monkey tests on a series of apps. It is very difficult to lock the app each time. It may be necessary to use Appium or the like. Secondly, I need a screenshot of the app to enable this function. Need to use the virtual button, the screenshot of the app will show the part of the virtual button

2. Use BroadcastReceiver to turn on and off WIFI

This is what I thought of based on the following two articles.
Android judges the wifi status and monitors the wifi connection.
In Android, use BroadcastReceiver to turn on and off the WIFI.
You can try to write an app to monitor the wifi status, and set it to turn on the wifi when the wifi is turned off.
But I didn't realize this idea.

3. Call the API in DeviceOwner to close the status bar.

The last method is to use the API of DeviceOwner.
It is still to write an APP and call the API of device management related classes in Android.

The setStatusDisabled function under DevicePolicyManager
insert image description here

DeviceOwner settings and use

concept

The following introduces several concepts:
device-owner: special device-admin
device manager device-admin
https://developer.android.com/guide/topics/admin/device-admin

app structure

The routine is to write the simplest app + some settings that can call deviceOwner.
To finish calling deviceOwner, first, you need to activate device-admin. At this time, you can use some deviceadmin-related API settings, such as turning off the camera, etc. After activating device-admin Then activate deviceOwner, and then you can use deviceOwner-related APIs

I am referring to the design of the boss here http://floatingmuseum.github.io/2016/07/device-admin-practice
and also wrote the style of the setting interface

But the core settings are actually just a few points: https://github.com/XYScience/DeviceOwner
The readme of the boss above is very clear, and my app basically refers to the code of the two bosses

code analysis

Let’s explain the big guy’s code
first. It’s written by device-admin:

1. Create a new device_admin.xml file in the res/xml directory;

<?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. Register a broadcast inheriting 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. Register the broadcast in the manifest file;

<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>

This completes a device-admin broadcast, followed by activation and use

4. Activate the device manager in other classes

Activated core code:

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()
}

This code can be used in a suitable place, the following code,

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

Get dpm In Fragment's onCreate,
the boss app settings use PreferenceFragment (outdated, fragment similar to the setting structure), click the corresponding preference to trigger the getAdmin() function, activate deviceadmin, the specific code can be found in the first boss github analysis


 @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. Set device-admin permissions

When using the device-admin API, some settings require special permission
permission constants
corresponding to setting policies in 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. Then activate the deviceOwner

Now there are generally three ways: I use the second adb way
to use the ADB command

$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

Log out of all accounts in Settings-Accounts, and try ADB settings again

Problems Encountered When Activating DeviceOwner
1. At first, when I directly input adb dpm set-…… on the PC command line, an error was reported Error: Bad admin: ComponentInfo{com.example.deviceowner/com.example.deviceowner.MyDeviceAdminReceiver}

As shown in the picture below: there will be a lot of instructions for use, and there will be a little error message at the end

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}
insert image description here

When I first use the adb shell to enter the phone command line, and then use the dpm command, it will not

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)
insert image description here

This error is very common. Simply put, the DeviceOwner just wants to use it on a new test device, so no other account can be bound to him.

For specific explanation, please see Not allowed to set the device owner because there are already several users on the device

"Device owner can only be set on an unprovisioned device unless it was launched by "adb", in which case we allow it if there is no account associated with the device". Therefore, before using the dpm command, make sure you do not have any accounts (such as Gmail) associated with the current user set"

Regarding the problem of account handling, I have seen other people's more detailed summary of the handling method:
adb setting DeviceOwner has a problem

Let me record the method I used myself, which is to simply delete all of them directly. I originally wanted to back them up, but it didn't work.

I. Delete too many userInfo
adb shell pm list users

Notallowedtosetthedevice owner because there are already some accounts on thedevice
insert image description here
delete accounts other than 0

adb shell pm remove-user 999
II. Delete account
adb shell dunmpsys account

You can see all the accounts.
insert image description here
Directly open the phone settings -> there are accounts in it, and then delete them. Some accounts that cannot be deleted can be uninstalled directly. Finally,
execute dpm.
insert image description here

7. Call the API of deviceOwner

After activating the deviceOwner, you can call the required API.
I have used three types, the status bar is unavailable, mute, and APP automatic permission grant. For
specific functions, you can go to the Android documentation. The function is still very powerful
https://developer. android.com/reference/android/app/admin/DevicePolicyManager

Here is an example of setting mute and unavailable status bar, which is to call the corresponding API

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

dpm is also placed in onCreate to obtain

  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);
    }

Guess you like

Origin blog.csdn.net/m0_54352040/article/details/124354932