MDM项目实现禁用adb调试和USB计算机连接方式

9 阅读4分钟

我们有一个需求,管理员可以从远程下发一个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就好,具体不再详述