UsbDebuggingManager.java revision 37ce5c5d5e8216f02230aeb89b147c0395e18329
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions an 14 * limitations under the License. 15 */ 16 17package com.android.server.usb; 18 19import android.content.ActivityNotFoundException; 20import android.content.Context; 21import android.content.Intent; 22import android.net.LocalSocket; 23import android.net.LocalSocketAddress; 24import android.os.Handler; 25import android.os.HandlerThread; 26import android.os.Environment; 27import android.os.FileUtils; 28import android.os.Looper; 29import android.os.Message; 30import android.os.Process; 31import android.os.SystemClock; 32import android.util.Slog; 33import android.util.Base64; 34 35import java.lang.Thread; 36import java.io.File; 37import java.io.FileDescriptor; 38import java.io.FileOutputStream; 39import java.io.IOException; 40import java.io.InputStream; 41import java.io.OutputStream; 42import java.io.PrintWriter; 43import java.security.MessageDigest; 44import java.util.Arrays; 45 46public class UsbDebuggingManager implements Runnable { 47 private static final String TAG = "UsbDebuggingManager"; 48 private static final boolean DEBUG = false; 49 50 private final String ADBD_SOCKET = "adbd"; 51 private final String ADB_DIRECTORY = "misc/adb"; 52 private final String ADB_KEYS_FILE = "adb_keys"; 53 private final int BUFFER_SIZE = 4096; 54 55 private final Context mContext; 56 private final Thread mThread; 57 private final Handler mHandler; 58 private final HandlerThread mHandlerThread; 59 private boolean mAdbEnabled = false; 60 private String mFingerprints; 61 private LocalSocket mSocket = null; 62 private OutputStream mOutputStream = null; 63 64 public UsbDebuggingManager(Context context) { 65 mThread = new Thread(this); 66 mHandlerThread = new HandlerThread("UsbDebuggingHandler"); 67 mHandlerThread.start(); 68 mHandler = new UsbDebuggingHandler(mHandlerThread.getLooper()); 69 mContext = context; 70 } 71 72 private void listenToSocket() throws IOException { 73 try { 74 byte[] buffer = new byte[BUFFER_SIZE]; 75 LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET, 76 LocalSocketAddress.Namespace.RESERVED); 77 InputStream inputStream = null; 78 79 mSocket = new LocalSocket(); 80 mSocket.connect(address); 81 82 mOutputStream = mSocket.getOutputStream(); 83 inputStream = mSocket.getInputStream(); 84 85 while (true) { 86 int count = inputStream.read(buffer); 87 if (count < 0) { 88 Slog.e(TAG, "got " + count + " reading"); 89 break; 90 } 91 92 if (buffer[0] == 'P' && buffer[1] == 'K') { 93 String key = new String(Arrays.copyOfRange(buffer, 2, count)); 94 Slog.d(TAG, "Received public key: " + key); 95 Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_CONFIRM); 96 msg.obj = key; 97 mHandler.sendMessage(msg); 98 } 99 else { 100 Slog.e(TAG, "Wrong message: " + (new String(Arrays.copyOfRange(buffer, 0, 2)))); 101 break; 102 } 103 } 104 } catch (IOException ex) { 105 Slog.e(TAG, "Communication error: ", ex); 106 throw ex; 107 } finally { 108 closeSocket(); 109 } 110 } 111 112 @Override 113 public void run() { 114 while (mAdbEnabled) { 115 try { 116 listenToSocket(); 117 } catch (Exception e) { 118 /* Don't loop too fast if adbd dies, before init restarts it */ 119 SystemClock.sleep(1000); 120 } 121 } 122 } 123 124 private void closeSocket() { 125 try { 126 mOutputStream.close(); 127 } catch (IOException e) { 128 Slog.e(TAG, "Failed closing output stream: " + e); 129 } 130 131 try { 132 mSocket.close(); 133 } catch (IOException ex) { 134 Slog.e(TAG, "Failed closing socket: " + ex); 135 } 136 } 137 138 private void sendResponse(String msg) { 139 if (mOutputStream != null) { 140 try { 141 mOutputStream.write(msg.getBytes()); 142 } 143 catch (IOException ex) { 144 Slog.e(TAG, "Failed to write response:", ex); 145 } 146 } 147 } 148 149 class UsbDebuggingHandler extends Handler { 150 private static final int MESSAGE_ADB_ENABLED = 1; 151 private static final int MESSAGE_ADB_DISABLED = 2; 152 private static final int MESSAGE_ADB_ALLOW = 3; 153 private static final int MESSAGE_ADB_DENY = 4; 154 private static final int MESSAGE_ADB_CONFIRM = 5; 155 156 public UsbDebuggingHandler(Looper looper) { 157 super(looper); 158 } 159 160 public void handleMessage(Message msg) { 161 switch (msg.what) { 162 case MESSAGE_ADB_ENABLED: 163 if (mAdbEnabled) 164 break; 165 166 mAdbEnabled = true; 167 168 mThread.start(); 169 170 break; 171 172 case MESSAGE_ADB_DISABLED: 173 if (!mAdbEnabled) 174 break; 175 176 mAdbEnabled = false; 177 closeSocket(); 178 179 try { 180 mThread.join(); 181 } catch (Exception ex) { 182 } 183 184 mOutputStream = null; 185 mSocket = null; 186 break; 187 188 case MESSAGE_ADB_ALLOW: { 189 String key = (String)msg.obj; 190 String fingerprints = getFingerprints(key); 191 192 if (!fingerprints.equals(mFingerprints)) { 193 Slog.e(TAG, "Fingerprints do not match. Got " 194 + fingerprints + ", expected " + mFingerprints); 195 break; 196 } 197 198 if (msg.arg1 == 1) { 199 writeKey(key); 200 } 201 202 sendResponse("OK"); 203 break; 204 } 205 206 case MESSAGE_ADB_DENY: 207 sendResponse("NO"); 208 break; 209 210 case MESSAGE_ADB_CONFIRM: { 211 String key = (String)msg.obj; 212 mFingerprints = getFingerprints(key); 213 showConfirmationDialog(key, mFingerprints); 214 break; 215 } 216 } 217 } 218 } 219 220 private String getFingerprints(String key) { 221 String hex = "0123456789ABCDEF"; 222 StringBuilder sb = new StringBuilder(); 223 MessageDigest digester; 224 225 try { 226 digester = MessageDigest.getInstance("MD5"); 227 } catch (Exception ex) { 228 Slog.e(TAG, "Error getting digester: " + ex); 229 return ""; 230 } 231 232 byte[] base64_data = key.split("\\s+")[0].getBytes(); 233 byte[] digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT)); 234 235 for (int i = 0; i < digest.length; i++) { 236 sb.append(hex.charAt((digest[i] >> 4) & 0xf)); 237 sb.append(hex.charAt(digest[i] & 0xf)); 238 if (i < digest.length - 1) 239 sb.append(":"); 240 } 241 return sb.toString(); 242 } 243 244 private void showConfirmationDialog(String key, String fingerprints) { 245 Intent dialogIntent = new Intent(); 246 247 dialogIntent.setClassName("com.android.systemui", 248 "com.android.systemui.usb.UsbDebuggingActivity"); 249 dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 250 dialogIntent.putExtra("key", key); 251 dialogIntent.putExtra("fingerprints", fingerprints); 252 try { 253 mContext.startActivity(dialogIntent); 254 } catch (ActivityNotFoundException e) { 255 Slog.e(TAG, "unable to start UsbDebuggingActivity"); 256 } 257 } 258 259 private void writeKey(String key) { 260 File dataDir = Environment.getDataDirectory(); 261 File adbDir = new File(dataDir, ADB_DIRECTORY); 262 263 if (!adbDir.exists()) { 264 Slog.e(TAG, "ADB data directory does not exist"); 265 return; 266 } 267 268 try { 269 File keyFile = new File(adbDir, ADB_KEYS_FILE); 270 271 if (!keyFile.exists()) { 272 keyFile.createNewFile(); 273 FileUtils.setPermissions(keyFile.toString(), 274 FileUtils.S_IRUSR | FileUtils.S_IWUSR | 275 FileUtils.S_IRGRP, -1, -1); 276 } 277 278 FileOutputStream fo = new FileOutputStream(keyFile, true); 279 fo.write(key.getBytes()); 280 fo.write('\n'); 281 fo.close(); 282 } 283 catch (IOException ex) { 284 Slog.e(TAG, "Error writing key:" + ex); 285 } 286 } 287 288 289 public void setAdbEnabled(boolean enabled) { 290 mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED 291 : UsbDebuggingHandler.MESSAGE_ADB_DISABLED); 292 } 293 294 public void allowUsbDebugging(boolean alwaysAllow, String publicKey) { 295 Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW); 296 msg.arg1 = alwaysAllow ? 1 : 0; 297 msg.obj = publicKey; 298 mHandler.sendMessage(msg); 299 } 300 301 public void denyUsbDebugging() { 302 mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY); 303 } 304 305 306 public void dump(FileDescriptor fd, PrintWriter pw) { 307 pw.println(" USB Debugging State:"); 308 pw.println(" Connected to adbd: " + (mOutputStream != null)); 309 pw.println(" Last key received: " + mFingerprints); 310 pw.println(" User keys:"); 311 try { 312 pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null)); 313 } catch (IOException e) { 314 pw.println("IOException: " + e); 315 } 316 pw.println(" System keys:"); 317 try { 318 pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null)); 319 } catch (IOException e) { 320 pw.println("IOException: " + e); 321 } 322 } 323} 324