summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/hardware/usb/IUsbManager.aidl8
-rw-r--r--packages/SystemUI/AndroidManifest.xml8
-rw-r--r--packages/SystemUI/res/values/strings.xml9
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java137
-rw-r--r--services/java/com/android/server/usb/UsbDebuggingManager.java322
-rw-r--r--services/java/com/android/server/usb/UsbDeviceManager.java26
-rw-r--r--services/java/com/android/server/usb/UsbService.java10
7 files changed, 520 insertions, 0 deletions
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 9bab797..98bd4f5 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -87,4 +87,12 @@ interface IUsbManager
/* Sets the file path for USB mass storage backing file. */
void setMassStorageBackingFile(String path);
+
+ /* Allow USB debugging from the attached host. If alwaysAllow is true, add the
+ * the public key to list of host keys that the user has approved.
+ */
+ void allowUsbDebugging(boolean alwaysAllow, String publicKey);
+
+ /* Deny USB debugging from the attached host */
+ void denyUsbDebugging();
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e13378f..2eee31d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -132,6 +132,14 @@
android:excludeFromRecents="true">
</activity>
+ <!-- started from UsbDebuggingManager -->
+ <activity android:name=".usb.UsbDebuggingActivity"
+ android:permission="android.permission.MANAGE_USB"
+ android:theme="@*android:style/Theme.Holo.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
+
<!-- started from NetworkPolicyManagerService -->
<activity
android:name=".net.NetworkOverLimitActivity"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2ce950f..5747f22 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -154,6 +154,15 @@
<!-- Checkbox label for USB accessory dialogs. [CHAR LIMIT=50] -->
<string name="always_use_accessory">Use by default for this USB accessory</string>
+ <!-- Title of confirmation dialog for USB debugging -->
+ <string name="usb_debugging_title">Allow USB Debugging?</string>
+
+ <!-- Message of confirmation dialog for USB debugging -->
+ <string name="usb_debugging_message">Allow USB Debugging from this computer?\nYour RSA key fingerprint is\n<xliff:g id="fingerprint">%1$s</xliff:g></string>
+
+ <!-- Option to always allow USB debugging from the attached computer -->
+ <string name="usb_debugging_always">Always allow this computer</string>
+
<!-- Checkbox label for application compatibility mode ON (zooming app to look like it's running
on a phone). [CHAR LIMIT=25] -->
<string name="compat_mode_on">Zoom to fill screen</string>
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
new file mode 100644
index 0000000..9146ccd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.usb;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Typeface;
+import android.hardware.usb.IUsbManager;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+import com.android.systemui.R;
+
+public 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);
+
+ 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();
+ }
+}
diff --git a/services/java/com/android/server/usb/UsbDebuggingManager.java b/services/java/com/android/server/usb/UsbDebuggingManager.java
new file mode 100644
index 0000000..a3b45c7
--- /dev/null
+++ b/services/java/com/android/server/usb/UsbDebuggingManager.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions an
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.Base64;
+
+import java.lang.Thread;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+public 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;
+
+ private final Context mContext;
+ private final Thread mThread;
+ private final Handler mHandler;
+ private final HandlerThread mHandlerThread;
+ private boolean mAdbEnabled = false;
+ private String mFingerprints;
+ private LocalSocket mSocket = null;
+ private OutputStream mOutputStream = null;
+
+ public UsbDebuggingManager(Context context) {
+ mThread = new Thread(this);
+ mHandlerThread = new HandlerThread("UsbDebuggingHandler");
+ mHandlerThread.start();
+ mHandler = new UsbDebuggingHandler(mHandlerThread.getLooper());
+ mContext = context;
+ }
+
+ private void listenToSocket() throws IOException {
+ try {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET,
+ LocalSocketAddress.Namespace.RESERVED);
+ InputStream inputStream = null;
+
+ mSocket = new LocalSocket();
+ mSocket.connect(address);
+
+ mOutputStream = mSocket.getOutputStream();
+ inputStream = mSocket.getInputStream();
+
+ while (true) {
+ int count = inputStream.read(buffer);
+ if (count < 0) {
+ Slog.e(TAG, "got " + count + " reading");
+ break;
+ }
+
+ if (buffer[0] == 'P' && buffer[1] == 'K') {
+ String key = new String(Arrays.copyOfRange(buffer, 2, count));
+ Slog.d(TAG, "Received public key: " + key);
+ Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_CONFIRM);
+ msg.obj = key;
+ mHandler.sendMessage(msg);
+ }
+ else {
+ Slog.e(TAG, "Wrong message: " + (new String(Arrays.copyOfRange(buffer, 0, 2))));
+ break;
+ }
+ }
+ } catch (IOException ex) {
+ Slog.e(TAG, "Communication error: ", ex);
+ throw ex;
+ } finally {
+ closeSocket();
+ }
+ }
+
+ @Override
+ public void run() {
+ while (mAdbEnabled) {
+ try {
+ listenToSocket();
+ } catch (Exception e) {
+ /* Don't loop too fast if adbd dies, before init restarts it */
+ SystemClock.sleep(1000);
+ }
+ }
+ }
+
+ private void closeSocket() {
+ try {
+ mOutputStream.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed closing output stream: " + e);
+ }
+
+ try {
+ mSocket.close();
+ } catch (IOException ex) {
+ Slog.e(TAG, "Failed closing socket: " + ex);
+ }
+ }
+
+ private void sendResponse(String msg) {
+ if (mOutputStream != null) {
+ try {
+ mOutputStream.write(msg.getBytes());
+ }
+ catch (IOException ex) {
+ Slog.e(TAG, "Failed to write response:", ex);
+ }
+ }
+ }
+
+ 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;
+
+ public UsbDebuggingHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_ADB_ENABLED:
+ if (mAdbEnabled)
+ break;
+
+ mAdbEnabled = true;
+
+ mThread.start();
+
+ break;
+
+ case MESSAGE_ADB_DISABLED:
+ if (!mAdbEnabled)
+ break;
+
+ mAdbEnabled = false;
+ closeSocket();
+
+ try {
+ mThread.join();
+ } catch (Exception ex) {
+ }
+
+ mOutputStream = null;
+ mSocket = null;
+
+ 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;
+ }
+ }
+ }
+ }
+
+ 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 void writeKey(String key) {
+ File dataDir = Environment.getDataDirectory();
+ File adbDir = new File(dataDir, ADB_DIRECTORY);
+
+ if (!adbDir.exists()) {
+ Slog.e(TAG, "ADB data directory does not exist");
+ return;
+ }
+
+ try {
+ File keyFile = new File(adbDir, ADB_KEYS_FILE);
+
+ 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);
+ }
+ }
+
+
+ public void setAdbEnabled(boolean enabled) {
+ mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED
+ : UsbDebuggingHandler.MESSAGE_ADB_DISABLED);
+ }
+
+ public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
+ Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW);
+ msg.arg1 = alwaysAllow ? 1 : 0;
+ msg.obj = publicKey;
+ mHandler.sendMessage(msg);
+ }
+
+ public void denyUsbDebugging() {
+ mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY);
+ }
+
+
+ public void dump(FileDescriptor fd, PrintWriter pw) {
+ pw.println(" USB Debugging State:");
+ pw.println(" Connected to adbd: " + (mOutputStream != null));
+ pw.println(" Last key received: " + mFingerprints);
+ pw.println(" User keys:");
+ try {
+ pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
+ } catch (IOException e) {
+ pw.println("IOException: " + e);
+ }
+ pw.println(" System keys:");
+ try {
+ pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null));
+ } catch (IOException e) {
+ pw.println("IOException: " + e);
+ }
+ }
+}
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index a115345..ddecf14 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -114,6 +114,7 @@ public class UsbDeviceManager {
private boolean mAudioSourceEnabled;
private Map<String, List<Pair<String, String>>> mOemModeMap;
private String[] mAccessoryStrings;
+ private UsbDebuggingManager mDebuggingManager;
private class AdbSettingsObserver extends ContentObserver {
public AdbSettingsObserver() {
@@ -166,6 +167,10 @@ public class UsbDeviceManager {
if (DEBUG) Slog.d(TAG, "accessory attached at boot");
startAccessoryMode();
}
+
+ if ("1".equals(SystemProperties.get("ro.adb.secure"))) {
+ mDebuggingManager = new UsbDebuggingManager(context);
+ }
}
public void systemReady() {
@@ -425,6 +430,9 @@ public class UsbDeviceManager {
setEnabledFunctions(mDefaultFunctions, true);
updateAdbNotification();
}
+ if (mDebuggingManager != null) {
+ mDebuggingManager.setAdbEnabled(mAdbEnabled);
+ }
}
private void setEnabledFunctions(String functions, boolean makeDefault) {
@@ -601,6 +609,9 @@ public class UsbDeviceManager {
if (mCurrentAccessory != null) {
mSettingsManager.accessoryAttached(mCurrentAccessory);
}
+ if (mDebuggingManager != null) {
+ mDebuggingManager.setAdbEnabled(mAdbEnabled);
+ }
break;
}
}
@@ -802,10 +813,25 @@ public class UsbDeviceManager {
return usbFunctions;
}
+ public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
+ if (mDebuggingManager != null) {
+ mDebuggingManager.allowUsbDebugging(alwaysAllow, publicKey);
+ }
+ }
+
+ public void denyUsbDebugging() {
+ if (mDebuggingManager != null) {
+ mDebuggingManager.denyUsbDebugging();
+ }
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw) {
if (mHandler != null) {
mHandler.dump(fd, pw);
}
+ if (mDebuggingManager != null) {
+ mDebuggingManager.dump(fd, pw);
+ }
}
private native String[] nativeGetAccessoryStrings();
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index 0205ef8..bebcd56 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -164,6 +164,16 @@ public class UsbService extends IUsbManager.Stub {
}
}
+ public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ mDeviceManager.allowUsbDebugging(alwaysAllow, publicKey);
+ }
+
+ public void denyUsbDebugging() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ mDeviceManager.denyUsbDebugging();
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)