[Android][踩坑]Android Enterprise之Lock Task Mode

[Android]Android Enterprise之Lock Task Mode

官网链接:https://developer.android.com/work/dpc/dedicated-devices/lock-task-mode

前言

有个需求,需要在没有系统签名的前提下,完成某个应用始终保持在前台;(行业应用,非流氓应用)

第一反应是如下这个心路历程:

· 将该App设置为HOME,即Launcher;

否决原因:无法屏蔽状态栏;

· 将该App设置为HOME,再设置FullScreen模式;

否决原因:仍然无法完全屏蔽状态栏;

· 将该App设置为HOME,设置FullScreen模式,再在顶部创建一个透明的View,拦截TouchEvent;

否决原因:Android 8.0无法在非System App内创建高于StatusBar的View;

(如果Android 8.0以前,在这里可以直接移步https://stackoverflow.com/questions/7457730/preventing-status-bar-expansion

 

探索

反复尝试后,觉得上面的思路是行不通的;

同时,在网上发现了这么一个东西:LockTaskMode

App开发得少,这玩意长啥样都不知道,但是看官网介绍,貌似能用。

这里吐槽一下,这部分功能是属于Android Enterprise的,官网貌似写得很随意,而且应该是从demo里面直接截取的代码片段,很多常量根本不知道是啥;

这里就直接跳过踩坑环节,上代码:

首先LockTaskMode需要App是DeviceOwner或者ProfileOwner;前者是第三方应用能获取到的最高权限,后者在Android 8.0上需要创建一个关联用户并管理一个Work Profile;

然而,这些在LockTaskMode的官网介绍中都没有提到,按照它的指引写出来的代码只能是一堆FC……

这里其实两个Owner都不好拿到:

DeviceOwner需要首先获得Device Admin权限,然后通过如下两种方式之一获取DeviceOwner权限:

1. adb shell dpm set-device-owner com.example.sample/.MyDeviceAdminReceiver

2. 使用NFC Provisioning完成,这个非常复杂,详见谷歌Demo:https://github.com/android/enterprise-samples

ProfileOnwer需要首先获得Device Admin权限,然后创建一个Work Profile,然后将该Work Profile的用户添加为自己的关联用户(Affiliated User)

试了一天,最后还是决定先用adb授权DeviceOwner,先跳过这一步,后续有时间再写个NFC授权应用即可;

上代码:

首先创建一个继承自android.app.admin.DeviceAdminReceiver的DeviceAdmin:

public class DeviceAdmin extends DeviceAdminReceiver {
    @Override
    public void onEnabled(Context context, Intent intent) {}

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

然后修改清单配置文件:

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

res/xml/下创建adminmanager.xml,按需配置如下权限:

<?xml version="1.0" encoding="utf-8"?>
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-policies>
        <!--锁屏 -->
        <force-lock />
        <!--限制密码类型-->
        <limit-password />
        <!-- 监控屏幕解锁尝试次数 -->
        <watch-login />
        <!-- 重置密码-->
        <reset-password />
        <!--恢复出厂设置-->
        <wipe-data />
        <!-- 设置锁定屏幕密码的有效期 -->
        <expire-password />
        <!-- 设置存储设备加密 -->
        <encrypted-storage />
        <!-- 停用相机 -->
        <disable-camera />
    </uses-policies>
</device-admin>

然后在Activity启动过程中通过Intent跳转到DeviceAdmin的授权界面:

    DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
    ComponentName comName = new ComponentName(getApplicationContext(), DeviceAdmin.class);
    if (!dpm.isAdminActive(comName)) {
        Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
        intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, comName);
        intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "申请DeviceAdmin权限");
        startActivityForResult(intent, 10086);
    }

并在onActivityResult方法中监听结果:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (RESULT_OK == resultCode) {
            DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
            ComponentName comName = new ComponentName(getApplicationContext(), DeviceAdmin.class);
            switch (requestCode) {
                case 10086:
                    if (dpm.isDeviceOwnerApp(getPackageName()) || setDeviceOwner(true)) {
                        dpm.setLockTaskPackages(comName, new String[]{"com.zsui.fullscreendemo"});
                    } else {
                        Toast.makeText(this, "设置DeviceOwner失败", Toast.LENGTH_SHORT).show();
                    }
                    break;

            }
        }
    }

    private boolean setDeviceOwner(boolean enabled) {
        boolean result = true;
        Runtime runtime = Runtime.getRuntime();
        try {
            Process proc = runtime.exec("su");
            OutputStream os = proc.getOutputStream();
            if (enabled) {
                os.write("dpm set-device-owner com.zsui.fullscreendemo/.DeviceAdmin".getBytes());
            } else {
                os.write("dpm remove-active-admin com.zsui.fullscreendemo/.DeviceAdmin".getBytes());
            }
            os.flush();
            byte[] buff = new byte[8192];
            int n = proc.getInputStream().read(buff);
        } catch (IOException e) {
            e.printStackTrace();
            result = false;
        }
        return result;
    }

此时,如果app可以获取到root权限,或者通过adb执行了dpm set-device-owner com.zsui.fullscreendemo/.DeviceAdmin,此时应用应该是没有HOME键与RECENT键的,且下拉状态栏无效;

然后问题也来了,咋退出?

这里最好做个Button,然后在其onClickListener里进行反注册:

    public void onClick(View v) {
        DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
        if (dpm.isDeviceOwnerApp(getPackageName())) {
            ComponentName comName = new ComponentName(getApplicationContext(), DeviceAdmin.class);
            dpm.setLockTaskPackages(comName, new String[]{});
            stopLockTask();
        } else {
            finish();
        }

反注册就是把之前传给setLockTaskPackages方法的第二个参数String[]中的包名去掉,严格来说需要判断当前的DeviceOwner有哪些,只去掉自己的那个;这里为了展示方便,就不写那么多了;

 

时间紧促,本文仅供参考以及本人后续查阅;

如有疑问或者纰漏,欢迎留言讨论、指正;

后续有时间会更新NFC Provisiong的那部分;(极有可能没时间…)

谢谢!

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值