上周项目中有个在apk上层实现息屏亮屏、禁止卸载应用、应用隐藏、禁用机器模块的需求,刚开始在网上看到是DevicePolicyManager来实现的,虽然最后根据自己项目特定的环境下并没有使用DevicePolicyManager这个类去实现息屏功能,但看到很多有意思的API值的学习;以备不时之需。
个人感觉首先应该看看: Device Administration API,然后在看其他人的理解
先学习API完成项目的功能需求吧;
先预览一下几个相关的Android系统SDK提供的类吧:
**通过DevicePolicyManager实现息屏代码流程:
- 获的DevicePolicyManager的实例:
- 申请设备管理权限;
- 调用DevicePolicyManager的API,完成息屏等其他需求得到操作**
申请设备管理权限:
申请设备管理权限时需要一个ComponentName(Context pkg, Class cls) 实例,需要一个DeviceAdminReceiver的Class;但是DeviceAdminReceiver本身就是一个BroadcastReceiver,于是新建一个DeviceReceiver类extends DeviceAdminReceiver作为DeviceAdminReceiver实例
package br.com.tectoy.ttlauncher;
import android.app.admin.DeviceAdminReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
/**
* @author liuw
*/
public class DeviceReceiver extends DeviceAdminReceiver {
@Override
public void onEnabled(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.e("TAG000","------onEnabled-------");
super.onEnabled(context, intent);
}
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.e("TAG000","--------onReceive-----");
super.onReceive(context, intent);
}
}
DeviceAdminReceiver类的实现:
AdminReciver继承DeviceAdminReceiver 在mainfest中添加配置:
<receiver
android:name=".DeviceReceiver"
android:exported="false"
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>
android:permission、meta-data、action ;
重点是:meta-data的配置,在meta-data配置本DeviceAdminReceiver中申请设备管理器的具体权限,
有点类似在mainfest中配置App需要的各个uses-permission权限;
本次配置的mete-data的name为:android.app.device_admin【系统中读取配置文件的key固定的】;
可申请的设备管理Iitem的XML文件格式如下
<?xml version="1.0" encoding="utf-8"?>
<device-admin
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-policies>
<limit-password />
<!-- 限制密码类型 -->
<watch-login />
<!-- 监控登录尝试 -->
<reset-password />
<!-- 重置密码 -->
<force-lock />
<!--锁屏 -->
<wipe-data />
<!-- 恢复出厂设置 -->
</uses-policies>
</device-admin>
申请设备管理权限:
package br.com.tectoy.ttlauncher;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.lang.reflect.Method;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final String MODULE_ICC = "ICC";
private static final String MODULE_MAG = "MAG";
private static final String MODULE_PICC = "PICC";
private static final String MODULE_PRINTER = "PRINTER";
private static final String MODULE_SCANNER = "SCANNER";
private static final String MODULE_PED = "PED";
private DevicePolicyManager mDevicePolicyManager;
private ComponentName mAdmin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDevicePolicyManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
mAdmin = new ComponentName(this, DeviceReceiver.class);
if(mDevicePolicyManager.isAdminActive(mAdmin)){
Toast.makeText(MainActivity.this, "已经注册", Toast.LENGTH_LONG).show();
}else{
ActiveMNG();
}
}
private void ActiveMNG(){
Intent intent = new Intent (DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdmin);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,"--其他描述--");
startActivityForResult(intent , 0 );
}
boolean mFlag = true;
public void setUninstallBlocked(View view) {
mDevicePolicyManager.setUninstallBlocked(mAdmin, "com.example.liuw.camera2demo", mFlag);
Toast.makeText(this,"setUninstallBlocked mFlag :"+mFlag,Toast.LENGTH_SHORT).show();
mFlag = !mFlag;
}
public void isUninstallBlocked(View view) {
boolean uninstallBlocked = mDevicePolicyManager.isUninstallBlocked(mAdmin, "com.example.liuw.camera2demo");
Toast.makeText(this, "uninstallBlocked :" + uninstallBlocked, Toast.LENGTH_SHORT).show();
}
public void setApplicationHidden(View view) {
boolean setApplicationHidden = mDevicePolicyManager.setApplicationHidden(mAdmin, "com.example.liuw.camera2demo", mFlag);
Toast.makeText(this, "setApplicationHidden :" + setApplicationHidden +" ,mFlag :"+mFlag, Toast.LENGTH_SHORT).show();
mFlag = !mFlag;
}
public void isApplicationHidden(View view) {
boolean isApplicationHidden = mDevicePolicyManager.isApplicationHidden(mAdmin, "com.example.liuw.camera2demo");
Toast.makeText(this, "isApplicationHidden :" + isApplicationHidden, Toast.LENGTH_SHORT).show();
}
public void setLockTaskPackages(View view) {
mDevicePolicyManager.setLockTaskPackages(mAdmin,new String[]{"br.com.tectoy.ttlauncher"});
Log.w(TAG,"set setLockTaskPackages success!");
}
public void disableModule(View view) {
Class<DevicePolicyManager> devicePolicyManagerClass = DevicePolicyManager.class;
try {
Method disableModule = devicePolicyManagerClass.getDeclaredMethod("disableModule", String.class, boolean.class);
disableModule.setAccessible(true);
boolean disableModuleResult = (boolean) disableModule.invoke(mDevicePolicyManager, MODULE_ICC, mFlag);
boolean disableModuleResult2 = (boolean) disableModule.invoke(mDevicePolicyManager, MODULE_MAG, mFlag);
boolean disableModuleResult3 = (boolean) disableModule.invoke(mDevicePolicyManager, MODULE_PICC, mFlag);
boolean disableModuleResult4 = (boolean) disableModule.invoke(mDevicePolicyManager, MODULE_PRINTER, mFlag);
boolean disableModuleResult5 = (boolean) disableModule.invoke(mDevicePolicyManager, MODULE_SCANNER, mFlag);
boolean disableModuleResult6 = (boolean) disableModule.invoke(mDevicePolicyManager, MODULE_PED, mFlag);
Toast.makeText(this, "disableModuleResult :" + disableModuleResult +" ,mFlag :"+mFlag, Toast.LENGTH_SHORT).show();
mFlag = !mFlag;
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Call disableModule failed :" + e);
}
}
public void getModuleStatus(View view) {
Class<DevicePolicyManager> devicePolicyManagerClass = DevicePolicyManager.class;
try {
Method getModuleStatus = devicePolicyManagerClass.getDeclaredMethod("getModuleStatus",String.class);
getModuleStatus.setAccessible(true);
boolean getModuleStatusResult = (boolean) getModuleStatus.invoke(mDevicePolicyManager, MODULE_ICC);
Log.w(TAG, "getModuleStatusResult :" + getModuleStatusResult);
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Call getModuleStatus failed :" + e);
}
}
public void lockScreen(View view){
mDevicePolicyManager.lockNow();
}
}
贴上build.gradle文件
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 33
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "br.com.tectoy.ttlauncher"
minSdkVersion 22
targetSdkVersion 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
主界面布局文件 button也默认配置好
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="setUninstallBlocked"
android:text="setUninstallBlocked"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="isUninstallBlocked"
android:text="isUninstallBlocked"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="setApplicationHidden"
android:text="setApplicationHidden"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="isApplicationHidden"
android:text="isApplicationHidden"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="setLockTaskPackages"
android:text="setLockTaskPackages"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="disableModule"
android:text="disableModule"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="getModuleStatus"
android:text="getModuleStatus"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="lockScreen"
android:text="lockScreen"
android:textAllCaps="false" />
</LinearLayout>
项目的build.gradle文件
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.2"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Manifest需要配置的权限
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
/>
<uses-permission android:name="android.permission.WRITE_APN_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>