ProfileOwner 是配置文件所有者,从Android5.0开始推出。
在系统中只能设置一个Profile Owner程序,程序在设置为ProfileOwner后不能取消,也不能卸载,所以想要需要,只有恢复出厂设置。
要使程序成为ProfileOwner,需要程序拥有系统权限:
1.AndroidManifest文件中manifest标签添加:
android:sharedUserId="android.uid.system"
2.在app的build.gradle中使用系统签名
想要成为ProfileOwner,有些方式首先需要先成为DeviceAdmin。
1.创建DeviceReceiver继承DeviceAdminReceiver:
public class AdminReceiver extends DeviceAdminReceiver {
private static final String TAG = "AdminReceiver";
@Override
public void onEnabled(Context context, Intent intent) {
super.onEnabled(context, intent);
Logg.i(TAG, "Device Owner Enabled");
}
@Override
public CharSequence onDisableRequested(Context context, Intent intent) {
return "Warning: Device Admin is going to be disabled.";
}
@Override
public void onDisabled(Context context, Intent intent) {
}
@Override
public void onLockTaskModeEntering(Context context, Intent intent,
String pkg) {
Logg.i(TAG, "onLockTaskModeEntering");
}
@Override
public void onLockTaskModeExiting(Context context, Intent intent) {
Logg.i(TAG, "onLockTaskModeExiting");
}
}
2.AndroidManifest注册:
<receiver
android:name=".receiver.AdminReceiver"
android:label="@string/app_name"
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" />
</intent-filter>
</receiver>
3.res/xml目录下添加文件admin.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>
4.激活deviceAdmin:
mComName = new ComponentName(context, AdminReceiver.class);
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mComName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "激活此设备管理员后可免root停用应用");
startActivityForResult(intent, 1);
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1) {
if (resultCode == Activity.RESULT_OK) {
Log.i(TAG, "[ProfileOwner][Admin]User accepted");
} else {
Log.i(TAG, "[ProfileOwner][Admin]User rejected");
}
}
}
使用上面的代码可以成为DeviceAdmin,会弹出窗口提示用户是否设置DeviceAdmin,用户同意后该程序会成为DeviceAdmin。
设置完成后,可以使用isAdminActive方法判断是否激活成功。
使用例子:
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
dpm.isAdminActive(mComName);
成为profile owner有三种方式。
2.3.1 adb命令
使用adb命令设置Profile Owner:
adb shell;
dpm set-profile-owner com.our.xxx/com.our.xxx.receiver.AdminReceiver;
设置您在代码中设定的Receiver,这种方式是将当前用户拥有AdminReceiver的程序设置为Profile Owner。
该方式不需要先成为DeviceAdmin,可以直接设置成为ProfileOwner,并且设备没有任何的弹窗提示用户点击同意按钮。
2.3.2 反射DevicePolicyManager方法
可以使用反射,反射出DevicePolicyManager中的设置Profile Owner的方法进行设置。
有两个方法都可以进行设置(该方法不适用于android10及以上版本):
1.setProfileOwner
使用例子:
mComName = new ComponentName(context, AdminReceiver.class);
Class c = Class.forName("android.app.admin.DevicePolicyManager");
Method method = c.getMethod("setProfileOwner", ComponentName.class, String.class, int.class);
method.invoke(mDevicePolicyManager, mComName, "ProfileOwner", 0);
setProfileOwner三个参数,第一个参数为ComponentName,第二个参数为 ownerName,第三个参数为用户id。ownerName为您要设置的名称,可随意;用户id为您要设置哪个用户中的程序成为ProfileOwner(系统首次启动时,默认会拥有用户id为0的用户空间)。
该方法需要先将该程序设置为DeviceAdmin,设置DeviceAdmin成功后,再调用该方法才可以设置成功,适用与android10以下版本。
2.setActiveProfileOwner
使用例子:
mComName = new ComponentName(context, AdminReceiver.class);
Class c = Class.forName("android.app.admin.DevicePolicyManager");
Method method = c.getMethod("setActiveProfileOwner", ComponentName.class, String.class);
method.invoke(mDevicePolicyManager, mComName, "ProfileOwner");
setActiveProfileOwner两个参数,第一个参数为ComponentName,第二个参数为 ownerName。ownerName为您要设置的名称,可随意。
这里为什么不设置用户id呢?是因为用户id在这个方法中会自动获取当前用户id设置,不需要指定用户id。
该方法不能先将该程序设置为DeviceAdmin,会直接设置成为ProfileOwner(若程序已经是DeviceAdmin,使用该方法会失败),并且设备没有任何的弹窗提示用户点击同意按钮,适用与android10以下版本。
该方式需要先成为DeviceAdmin,然后执行下面的步骤成为ProfileOwner。该方式会新建一个用户空间,新建的这个用户空间中的指定程序会成为ProfileOwner。
1.打开申请Profile Owner的界面:
Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, getPackageName());
} else {
final ComponentName component = new ComponentName(this, AdminReceiver.class.getName());
intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, component);
}
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, 1);
} else {
Log.e("ProfileTest", "Device provisioning is not enabled. Stopping.");
}
2.打印用户是否同意设置ProfileOwner:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (requestCode == 1) {
if (resultCode == Activity.RESULT_OK) {
Log.i("ProfileTest", "Provisioning done.");
} else {
Log.i("ProfileTest", "Provisioning failed.");
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
3.在AdminReceiver.java中添加:
@Override
public void onProfileProvisioningComplete(Context context, Intent intent) {
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName componentName = new ComponentName(context, AdminReceiver.class);
dpm.setProfileName(componentName, "Test");
dpm.setProfileEnabled(componentName);
}
执行完AdminReceiver中代码后,ProfileOwner将正式创建成功,若没有执行AdminReceiver中的代码都是创建失败。
上面设置完成后,可以使用isProfileOwnerApp方法,使用包名判断该app是否已经被设置为ProfileOwner。
使用例子:
dpm.isProfileOwnerApp("com.our.xxx");
首次开机后:
1.先设置自己程序的ProfileOwner权限,设置成功后,可以使用google商店(但无法验证google商店是否设置ProfileOwner/DeviceOwner权限成功);
2.先打开google商店,登录后,再设置自己程序的ProfileOwner权限,会失败(提示Not allowed to set the profile owner because there are already some accounts on the profile)。
所以需让自己程序设置为ProfileOwner,然后再使用google相关功能。
成为ProfileOwner后,可以控制用户的权限。
使用例子:
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName componentName = new ComponentName(mContext, AdminReceiver.class);
dpm.addUserRestriction(componentName, DISALLOW_OUTGOING_BEAM);
使用addUserRestriction方法可以对该用户添加权限,可以添加的权限如下所示(下表中仅列出测试成功的权限,部分权限没有进行测试):
Key | 功能 |
DISALLOW_CONFIG_LOCALE | 禁止用户更改设备语言 |
DISALLOW_INSTALL_APPS | 禁止安装应用 |
DISALLOW_UNINSTALL_APPS | 禁止卸载应用 |
DISALLOW_BLUETOOTH | 禁止操作蓝牙 |
DISALLOW_DEBUGGING_FEATURES | 禁用adb调试 |
DISALLOW_CONFIG_LOCATION | 禁用定位gps |
DISALLOW_CONFIG_DATE_TIME | 不允许配置日期、时间和时区 |
DISALLOW_ADJUST_VOLUME | 静音,不允许调整音量 |
成为ProfileOwner后,可以清除赋给用户的权限。
使用例子:
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName componentName = new ComponentName(mContext, AdminReceiver.class);
dpm.clearUserRestriction(componentName, DISALLOW_OUTGOING_BEAM);
可以清除的权限与上面添加权限一致。
成为ProfileOwner后,可以设置哪些应用不可使用,应用图标会置灰,不可点击。
使用例子:
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName componentName = new ComponentName(mContext, AdminReceiver.class);
dpm.setPackagesSuspended(componentName, packageNames, suspended);
使用setPackagesSuspended方法可以对该用户的应用进行使用限制,将要控制的应用包名放入数组中,设置对这些应用是可使用还是不可使用,可使用suspended为false,不可使用suspended为true。
下面的功能都未进行测试。
接口名称 | setApplicationHidden | ||
功能描述 | 隐藏应用图标,且应用不可用 | ||
参数 | 名称 | 类型 | |
admin | ComponentName | ||
packageName | String | ||
hidden | boolean | ||
返回值 | 类型 | 说明 | |
boolean | boolean Whether the hidden setting of the package was successfully updated. |
接口名称 | setPermissionGrantState | ||
功能描述 | 通过包名设置权限 | ||
参数 | 名称 | 类型 | |
admin | ComponentName | ||
packageName | String | ||
permission | String | ||
grantState | int | ||
返回值 | 类型 | 说明 | |
boolean | whether the permission was successfully granted or revoked |
接口名称 | getPermissionGrantState | ||
功能描述 | 通过包名获取应用程序的运行时权限状态 | ||
参数 | 名称 | 类型 | |
admin | ComponentName | ||
packageName | String | ||
permission | String | ||
返回值 | 类型 | 说明 | |
int | The current grant state specified by device policy |
上面只列出部分功能,还有很多关于设备密码以及锁屏等功能相关的方法。