Android USB安全调试
Android 4.2.2 引入了USB安全调试方面的内容,当启用安全调试的时候,只有被用户认证过的主机才可以通过Android SDK自带的ADB工具经由USB连接来访问设备的内部构件。
下面以android-4.3_r3.1源码分析其实现:
首先打 usb调试界面在systemui中的UsbDebuggingActivity.java实现:
View Codepublic class UsbDebuggingActivity extends AlertActivity implements DialogInterface.OnClickListener { private static final String TAG = "UsbDebuggingActivity"; private CheckBox mAlwaysAllow; private UsbDisconnectedReceiver mDisconnectedReceiver; private String mKey; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) { mDisconnectedReceiver = new UsbDisconnectedReceiver(this); } Intent intent = getIntent(); String fingerprints = intent.getStringExtra("fingerprints"); mKey = intent.getStringExtra("key"); if (fingerprints == null || mKey == null) { finish(); return; } final AlertController.AlertParams ap = mAlertParams; ap.mTitle = getString(R.string.usb_debugging_title); ap.mIconId = com.android.internal.R.drawable.ic_dialog_usb; ap.mMessage = getString(R.string.usb_debugging_message, fingerprints); ap.mPositiveButtonText = getString(android.R.string.ok); ap.mNegativeButtonText = getString(android.R.string.cancel); ap.mPositiveButtonListener = this; ap.mNegativeButtonListener = this; // add "always allow" checkbox LayoutInflater inflater = LayoutInflater.from(ap.mContext); View checkbox = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null); mAlwaysAllow = (CheckBox)checkbox.findViewById(com.android.internal.R.id.alwaysUse); mAlwaysAllow.setText(getString(R.string.usb_debugging_always)); ap.mView = checkbox; setupAlert(); } private class UsbDisconnectedReceiver extends BroadcastReceiver { private final Activity mActivity; public UsbDisconnectedReceiver(Activity activity) { mActivity = activity; } @Override public void onReceive(Context content, Intent intent) { String action = intent.getAction(); if (!UsbManager.ACTION_USB_STATE.equals(action)) { return; } boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false); if (!connected) { mActivity.finish(); } } } @Override public void onStart() { super.onStart(); IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE); registerReceiver(mDisconnectedReceiver, filter); } @Override protected void onStop() { if (mDisconnectedReceiver != null) { unregisterReceiver(mDisconnectedReceiver); } super.onStop(); } @Override public void onClick(DialogInterface dialog, int which) { boolean allow = (which == AlertDialog.BUTTON_POSITIVE); boolean alwaysAllow = allow && mAlwaysAllow.isChecked(); try { IBinder b = ServiceManager.getService(USB_SERVICE); IUsbManager service = IUsbManager.Stub.asInterface(b); if (allow) { service.allowUsbDebugging(alwaysAllow, mKey); } else { service.denyUsbDebugging(); } } catch (Exception e) { Log.e(TAG, "Unable to notify Usb service", e); } finish(); } }
其主要功能实现于UsbDebuggingManager.java中:
View Codepublic class UsbDebuggingManager implements Runnable { private static final String TAG = "UsbDebuggingManager"; private static final boolean DEBUG = false; private final String ADBD_SOCKET = "adbd"; private final String ADB_DIRECTORY = "misc/adb"; private final String ADB_KEYS_FILE = "adb_keys"; private final int BUFFER_SIZE = 4096; ... class UsbDebuggingHandler extends Handler { private static final int MESSAGE_ADB_ENABLED = 1; private static final int MESSAGE_ADB_DISABLED = 2; private static final int MESSAGE_ADB_ALLOW = 3; private static final int MESSAGE_ADB_DENY = 4; private static final int MESSAGE_ADB_CONFIRM = 5; private static final int MESSAGE_ADB_CLEAR = 6; public UsbDebuggingHandler(Looper looper) { super(looper); } public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_ADB_ENABLED: if (mAdbEnabled) break; mAdbEnabled = true; mThread = new Thread(UsbDebuggingManager.this); mThread.start(); break; case MESSAGE_ADB_DISABLED: if (!mAdbEnabled) break; mAdbEnabled = false; closeSocket(); try { mThread.join(); } catch (Exception ex) { } mThread = null; mOutputStream = null; mSocket = null; break; case MESSAGE_ADB_ALLOW: { String key = (String)msg.obj; String fingerprints = getFingerprints(key); if (!fingerprints.equals(mFingerprints)) { Slog.e(TAG, "Fingerprints do not match. Got " + fingerprints + ", expected " + mFingerprints); break; } if (msg.arg1 == 1) { writeKey(key); } sendResponse("OK"); break; } case MESSAGE_ADB_DENY: sendResponse("NO"); break; case MESSAGE_ADB_CONFIRM: { String key = (String)msg.obj; mFingerprints = getFingerprints(key); showConfirmationDialog(key, mFingerprints); break; } case MESSAGE_ADB_CLEAR: deleteKeyFile(); break; } } } private String getFingerprints(String key) { String hex = "0123456789ABCDEF"; StringBuilder sb = new StringBuilder(); MessageDigest digester; try { digester = MessageDigest.getInstance("MD5"); } catch (Exception ex) { Slog.e(TAG, "Error getting digester: " + ex); return ""; } byte[] base64_data = key.split("\\s+")[0].getBytes(); byte[] digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT)); for (int i = 0; i < digest.length; i++) { sb.append(hex.charAt((digest[i] >> 4) & 0xf)); sb.append(hex.charAt(digest[i] & 0xf)); if (i < digest.length - 1) sb.append(":"); } return sb.toString(); } private void showConfirmationDialog(String key, String fingerprints) { Intent dialogIntent = new Intent(); dialogIntent.setClassName("com.android.systemui", "com.android.systemui.usb.UsbDebuggingActivity"); dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); dialogIntent.putExtra("key", key); dialogIntent.putExtra("fingerprints", fingerprints); try { mContext.startActivity(dialogIntent); } catch (ActivityNotFoundException e) { Slog.e(TAG, "unable to start UsbDebuggingActivity"); } } private File getUserKeyFile() { File dataDir = Environment.getDataDirectory(); File adbDir = new File(dataDir, ADB_DIRECTORY); if (!adbDir.exists()) { Slog.e(TAG, "ADB data directory does not exist"); return null; } return new File(adbDir, ADB_KEYS_FILE); } private void writeKey(String key) { try { File keyFile = getUserKeyFile(); if (keyFile == null) { return; } if (!keyFile.exists()) { keyFile.createNewFile(); FileUtils.setPermissions(keyFile.toString(), FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1); } FileOutputStream fo = new FileOutputStream(keyFile, true); fo.write(key.getBytes()); fo.write('\n'); fo.close(); } catch (IOException ex) { Slog.e(TAG, "Error writing key:" + ex); } } private void deleteKeyFile() { File keyFile = getUserKeyFile(); if (keyFile != null) { keyFile.delete(); } }
可见,若要删除特定连接Android设备的电脑只能删除Android设备中的文件:/data/misc/adb/adb_keys,或将Android设备强制刷机。