我们有一个需求,管理员可以从远程下发一个boolean值,如果为true,则禁止用户使用adb调试,同时USB计算机连接方式只能选择“仅充电”模式,无法选择“文件传输”、“照片传输”、“MIDI”、”驱动程序安装”、“PTP”、“USB网络共享”等模式;如果为false,则恢复禁用前的状态。 看到这里,我们想到的可能就是去系统设置Settings,看看adb调试开关和USB计算机连接方式的具体逻辑,并且在值为true时禁用相关选项操作。
首先,我们新建3个Settings值,用于保存远端下发的boolean值,禁用前的计算机连接方式和adb调试开关状态
frameworks/base/core/java/android/provider/Settings.java
public final class Settings {
public static final class Secure extends NameValueTable {
...
public static final String DISABLED_USB_ADB = "disable_usb_adb";
public static final String USB_ORIGINAL_FUNCTIONS = "usb_original_functions";
public static final String ADB_ORIGINAL_STATUS = "adb_original_status";
public static final String ADB_WIFI_ORIGINAL_STATUS = "adb_wifi_original_status";
}
}
对于adb调试状态,我们知道,有USB调试和无线调试两种,分别对于Settings.Global.ADB_ENABLED和Settings.Global.ADB_WIFI_ENABLED 查看AdbService代码,我们发现里面有注册监听这两个值
frameworks/base/services/core/java/com/android/server/adb/AdbService.java
private AdbService(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
mDebuggingManager = new AdbDebuggingManager(context);
registerContentObservers();
LocalServices.addService(AdbManagerInternal.class, new AdbManagerInternalImpl());
}
private void registerContentObservers() {
try {
// register observer to listen for settings changes
mObserver = new AdbSettingsObserver();
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
false, mObserver);
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED),
false, mObserver);
} catch (Exception e) {
Slog.e(TAG, "Error in registerContentObservers", e);
}
}
于是我们灵机一动,似乎可以在这里做点什么?
private class AdbSettingsObserver extends ContentObserver {
private final Uri mAdbUsbUri = Settings.Global.getUriFor(Settings.Global.ADB_ENABLED);
private final Uri mAdbWifiUri = Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED);
AdbSettingsObserver() {
super(null);
}
@Override
public void onChange(boolean selfChange, @NonNull Uri uri, @UserIdInt int userId) {
if (mAdbUsbUri.equals(uri)) {
boolean shouldEnable = (Settings.Global.getInt(mContentResolver,
Settings.Global.ADB_ENABLED, 0) > 0);
//如果为禁用状态,则修改Settings.Global.ADB_ENABLED的值为0,并且拦截handler消息
if (shouldEnable && Settings.Secure.getInt(mContentResolver,
Settings.Secure.DISABLED_USB_ADB, 0) == 1) {
Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, 0);
return;
}
FgThread.getHandler().sendMessage(obtainMessage(
AdbService::setAdbEnabled, AdbService.this, shouldEnable,
AdbTransportType.USB));
} else if (mAdbWifiUri.equals(uri)) {
boolean shouldEnable = (Settings.Global.getInt(mContentResolver,
Settings.Global.ADB_WIFI_ENABLED, 0) > 0);
// 如果为禁用状态,则修改Settings.Global.ADB_WIFI_ENABLED的值为0,并且拦截handler消息
if (shouldEnable && Settings.Secure.getInt(mContentResolver,
Settings.Secure.DISABLED_USB_ADB, 0) == 1) {
Settings.Global.putInt(mContentResolver, Settings.Global.ADB_WIFI_ENABLED, 0);
return;
}
FgThread.getHandler().sendMessage(obtainMessage(
AdbService::setAdbEnabled, AdbService.this, shouldEnable,
AdbTransportType.WIFI));
}
}
}
这样一来,就可以实现禁用adb调试了
查看Settings代码,发现修改usb计算机连接方式时,会调用UsbService里面的setCurrentFunctions方法
故技重施,我们可以在这里进行拦截
frameworks/base/services/usb/java/com/android/server/usb/UsbService.java
@Override
public void setCurrentFunctions(long functions, int operationId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
Preconditions.checkArgument(UsbManager.areSettableFunctions(functions));
Preconditions.checkState(mDeviceManager != null);
int disableUsbAdb = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.DISABLED_USB_ADB, 0);
if (disableUsbAdb == 1) {
// 如果禁用usb计算机连接方式,则直接return
return;
}
mDeviceManager.setCurrentFunctions(functions, operationId);
}
看上面代码,可以发现UsbService里面的setCurrentFunctions方法最终会调用UsbDeviceManager里面的setCurrentFunctions方法。因此,我们可以在这里监听远程下发的禁用状态
frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,
UsbSettingsManager settingsManager, UsbPermissionManager permissionManager) {
mContentResolver = context.getContentResolver();
...
if (mContentResolver != null) {
// 在这里监听usb计算机连接方式禁用状态
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.DISABLED_USB_ADB),
false,
new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
int disableUsbAdb = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.DISABLED_USB_ADB, 0);
// 如果为禁用状态,则修改为仅充电方式
if (disableUsbAdb == 1) {
setCurrentFunctions(UsbManager.FUNCTION_NONE, operationId);
} else {
// 如果不禁用,则恢复为原来的计算机连接方式
int operationId = sUsbOperationCount.incrementAndGet();
long usbFunction = Settings.Secure.getLong(mContext.getContentResolver(),
Settings.Secure.USB_ORIGINAL_FUNCTIONS, UsbManager.FUNCTION_NONE);
setCurrentFunctions(usbFunction,operationId);
}
}
});
}
...
}
上面代码中,Settings.Secure.DISABLED_USB_ADB出现频率很高,但似乎没看到在哪里给它赋值? 答案是在我们的客户端MdmExtention App里面
class MdmUsbAdbControl(val context: Context) :
BaseSystemServiceControl<CommonBean>(context) {
companion object {
const val TAG: String = "MdmUsbAdbControl"
const val DISABLED_USB_ADB: String = "disable_usb_adb"
const val ADB_ORIGINAL_STATUS: String = "adb_original_status"
const val ADB_WIFI_ORIGINAL_STATUS: String = "adb_wifi_original_status"
const val USB_ORIGINAL_FUNCTIONS: String = "usb_original_functions"
const val ADB_WIFI_ENABLED: String = "adb_wifi_enabled"
}
@SuppressLint("NewApi")
override fun handleCommand(
name: String,
bean: CommonBean,
callback: ControlCallback?
): Boolean {
val contentResolver = context.contentResolver
val disabledUdbAdb =
getSecureInt(contentResolver, DISABLED_USB_ADB, 0)
bean.enable?.let {
if (it) {
如果本身为禁用状态,远端下发的也是禁用状态,则直接return
if (disabledUdbAdb == 1) return true
val usbManager: UsbManager? = context.getSystemService(Context.USB_SERVICE) as? UsbManager
usbManager?.let {
val currentFunction: Long = usbManager.getCurrentFunctions()
// 保存旧的计算机连接方式
putSecureLong(contentResolver, USB_ORIGINAL_FUNCTIONS, currentFunction)
}
// 保存禁用状态
putSecureInt(contentResolver, DISABLED_USB_ADB, 1)
// 保存旧的USB调试状态
putSecureInt(contentResolver, ADB_ORIGINAL_STATUS,
getGlobalInt(contentResolver, Settings.Global.ADB_ENABLED, 0)
)
// 保存旧的无线调试状态
putSecureInt(contentResolver, ADB_WIFI_ORIGINAL_STATUS,
getGlobalInt(contentResolver, Settings.Global.ADB_WIFI_ENABLED, 0)
)
// 关闭USB调试
putGlobalInt(contentResolver, Settings.Global.ADB_ENABLED, 0)
if (getGlobalInt(contentResolver, ADB_WIFI_ENABLED, 0) == 1) {
// 关闭无线调试
putGlobalInt(contentResolver, ADB_WIFI_ENABLED, 0)
}
} else {
// 如果本身为不禁用,远端下发的也是不禁用状态,则直接return
if (disabledUdbAdb == 0) return true
// 保存非禁用状态
putSecureInt(contentResolver, DISABLED_USB_ADB, 0)
// 恢复adb调试状态
putAdbOriginalStatus(contentResolver)
}
}
return true
}
private fun putAdbOriginalStatus(contentResolver: ContentResolver) {
val isDevelopmentEnable = getGlobalInt(contentResolver,
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1
if (isDevelopmentEnable) {
putGlobalInt(contentResolver, Settings.Global.ADB_ENABLED,
getSecureInt(contentResolver, ADB_ORIGINAL_STATUS, 0))
putGlobalInt(contentResolver, Settings.Global.ADB_WIFI_ENABLED,
getSecureInt(contentResolver, ADB_WIFI_ORIGINAL_STATUS, 0))
}
}
override fun checkParamisNull(bean: CommonBean): Boolean {
return bean == null || bean.enable == null
}
}
Settings值相关的扩展方法
MdmExtUtils.kt
fun getGlobalInt(contextResolver: ContentResolver, name: String, def: Int): Int =
Settings.Global.getInt(contextResolver, name, def)
fun getSecureInt(contextResolver: ContentResolver, name: String, def: Int): Int =
Settings.Secure.getInt(contextResolver, name, def)
fun putGlobalInt(contextResolver: ContentResolver, name: String, value: Int) =
Settings.Global.putInt(contextResolver, name, value)
fun putSecureInt(contextResolver: ContentResolver, name: String, value: Int) =
Settings.Secure.putInt(contextResolver, name, value)
fun getSecureString(contextResolver: ContentResolver, name: String): String? =
Settings.Secure.getString(contextResolver, name)
fun putSecureString(contextResolver: ContentResolver, name: String, value: String) =
Settings.Secure.putString(contextResolver, name, value)
fun putSecureLong(contextResolver: ContentResolver, name: String, value: Long) =
Settings.Secure.putLong(contextResolver, name, value)
fun putSystemLong(contextResolver: ContentResolver, name: String, value: Long) =
Settings.System.putLong(contextResolver, name, value)
看到这里,我们的功能已经实现的差不多了,但发现了一个交互问题,就是插上USB线的时候,会弹USB计算机连接相关的通知,于是,我们亡羊补牢
frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
protected void updateUsbNotification(boolean force) {
boolean isDisabledUsb = Settings.Secure.getInt(mContentResolver, Settings.Secure.DISABLED_USB_ADB, 0) == 1;
if (isDisabledUsb) {
switch (mUsbNotificationId) {
case SystemMessage.NOTE_USB_MTP:
case SystemMessage.NOTE_USB_PTP:
case SystemMessage.NOTE_USB_MIDI:
case SystemMessage.NOTE_USB_BICR:
case SystemMessage.NOTE_USB_CHARGING:
// 取消通知
mNotificationManager.cancelAsUser(null, mUsbNotificationId,
UserHandle.ALL);
mUsbNotificationId = 0;
Slog.d(TAG, "Clear notification for isDisabledUsb");
// 直接return,拦截正常流程
return;
}
}
// updateUsbNotification原逻辑
...
}
最后一步,系统设置Settings APP里面adb调试和USB计算机连接方式相关的页面选项,通过禁用状态去enable或是disable就好,具体不再详述