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 17 package com.android.server.usb; 18 19 import android.content.ActivityNotFoundException; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.pm.PackageManager; 24 import android.content.pm.ResolveInfo; 25 import android.content.res.Resources; 26 import android.net.LocalSocket; 27 import android.net.LocalSocketAddress; 28 import android.os.Handler; 29 import android.os.Environment; 30 import android.os.FileUtils; 31 import android.os.Looper; 32 import android.os.Message; 33 import android.os.SystemClock; 34 import android.util.Slog; 35 import android.util.Base64; 36 import com.android.server.FgThread; 37 38 import java.lang.Thread; 39 import java.io.File; 40 import java.io.FileDescriptor; 41 import java.io.FileOutputStream; 42 import java.io.IOException; 43 import java.io.InputStream; 44 import java.io.OutputStream; 45 import java.io.PrintWriter; 46 import java.security.MessageDigest; 47 import java.util.Arrays; 48 49 public class UsbDebuggingManager implements Runnable { 50 private static final String TAG = "UsbDebuggingManager"; 51 private static final boolean DEBUG = false; 52 53 private final String ADBD_SOCKET = "adbd"; 54 private final String ADB_DIRECTORY = "misc/adb"; 55 private final String ADB_KEYS_FILE = "adb_keys"; 56 private final int BUFFER_SIZE = 4096; 57 58 private final Context mContext; 59 private final Handler mHandler; 60 private Thread mThread; 61 private boolean mAdbEnabled = false; 62 private String mFingerprints; 63 private LocalSocket mSocket = null; 64 private OutputStream mOutputStream = null; 65 66 public UsbDebuggingManager(Context context) { 67 mHandler = new UsbDebuggingHandler(FgThread.get().getLooper()); 68 mContext = context; 69 } 70 71 private void listenToSocket() throws IOException { 72 try { 73 byte[] buffer = new byte[BUFFER_SIZE]; 74 LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET, 75 LocalSocketAddress.Namespace.RESERVED); 76 InputStream inputStream = null; 77 78 mSocket = new LocalSocket(); 79 mSocket.connect(address); 80 81 mOutputStream = mSocket.getOutputStream(); 82 inputStream = mSocket.getInputStream(); 83 84 while (true) { 85 int count = inputStream.read(buffer); 86 if (count < 0) { 87 break; 88 } 89 90 if (buffer[0] == 'P' && buffer[1] == 'K') { 91 String key = new String(Arrays.copyOfRange(buffer, 2, count)); 92 Slog.d(TAG, "Received public key: " + key); 93 Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_CONFIRM); 94 msg.obj = key; 95 mHandler.sendMessage(msg); 96 } 97 else { 98 Slog.e(TAG, "Wrong message: " + (new String(Arrays.copyOfRange(buffer, 0, 2)))); 99 break; 100 } 101 } 102 } finally { 103 closeSocket(); 104 } 105 } 106 107 @Override 108 public void run() { 109 while (mAdbEnabled) { 110 try { 111 listenToSocket(); 112 } catch (Exception e) { 113 /* Don't loop too fast if adbd dies, before init restarts it */ 114 SystemClock.sleep(1000); 115 } 116 } 117 } 118 119 private void closeSocket() { 120 try { 121 mOutputStream.close(); 122 } catch (IOException e) { 123 Slog.e(TAG, "Failed closing output stream: " + e); 124 } 125 126 try { 127 mSocket.close(); 128 } catch (IOException ex) { 129 Slog.e(TAG, "Failed closing socket: " + ex); 130 } 131 } 132 133 private void sendResponse(String msg) { 134 if (mOutputStream != null) { 135 try { 136 mOutputStream.write(msg.getBytes()); 137 } 138 catch (IOException ex) { 139 Slog.e(TAG, "Failed to write response:", ex); 140 } 141 } 142 } 143 144 class UsbDebuggingHandler extends Handler { 145 private static final int MESSAGE_ADB_ENABLED = 1; 146 private static final int MESSAGE_ADB_DISABLED = 2; 147 private static final int MESSAGE_ADB_ALLOW = 3; 148 private static final int MESSAGE_ADB_DENY = 4; 149 private static final int MESSAGE_ADB_CONFIRM = 5; 150 private static final int MESSAGE_ADB_CLEAR = 6; 151 152 public UsbDebuggingHandler(Looper looper) { 153 super(looper); 154 } 155 156 public void handleMessage(Message msg) { 157 switch (msg.what) { 158 case MESSAGE_ADB_ENABLED: 159 if (mAdbEnabled) 160 break; 161 162 mAdbEnabled = true; 163 164 mThread = new Thread(UsbDebuggingManager.this, TAG); 165 mThread.start(); 166 167 break; 168 169 case MESSAGE_ADB_DISABLED: 170 if (!mAdbEnabled) 171 break; 172 173 mAdbEnabled = false; 174 closeSocket(); 175 176 try { 177 mThread.join(); 178 } catch (Exception ex) { 179 } 180 181 mThread = null; 182 mOutputStream = null; 183 mSocket = null; 184 break; 185 186 case MESSAGE_ADB_ALLOW: { 187 String key = (String)msg.obj; 188 String fingerprints = getFingerprints(key); 189 190 if (!fingerprints.equals(mFingerprints)) { 191 Slog.e(TAG, "Fingerprints do not match. Got " 192 + fingerprints + ", expected " + mFingerprints); 193 break; 194 } 195 196 if (msg.arg1 == 1) { 197 writeKey(key); 198 } 199 200 sendResponse("OK"); 201 break; 202 } 203 204 case MESSAGE_ADB_DENY: 205 sendResponse("NO"); 206 break; 207 208 case MESSAGE_ADB_CONFIRM: { 209 String key = (String)msg.obj; 210 String fingerprints = getFingerprints(key); 211 if ("".equals(fingerprints)) { 212 sendResponse("NO"); 213 break; 214 } 215 mFingerprints = fingerprints; 216 startConfirmation(key, mFingerprints); 217 break; 218 } 219 220 case MESSAGE_ADB_CLEAR: 221 deleteKeyFile(); 222 break; 223 } 224 } 225 } 226 227 private String getFingerprints(String key) { 228 String hex = "0123456789ABCDEF"; 229 StringBuilder sb = new StringBuilder(); 230 MessageDigest digester; 231 232 if (key == null) { 233 return ""; 234 } 235 236 try { 237 digester = MessageDigest.getInstance("MD5"); 238 } catch (Exception ex) { 239 Slog.e(TAG, "Error getting digester", ex); 240 return ""; 241 } 242 243 byte[] base64_data = key.split("\\s+")[0].getBytes(); 244 byte[] digest; 245 try { 246 digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT)); 247 } catch (IllegalArgumentException e) { 248 Slog.e(TAG, "error doing base64 decoding", e); 249 return ""; 250 } 251 for (int i = 0; i < digest.length; i++) { 252 sb.append(hex.charAt((digest[i] >> 4) & 0xf)); 253 sb.append(hex.charAt(digest[i] & 0xf)); 254 if (i < digest.length - 1) 255 sb.append(":"); 256 } 257 return sb.toString(); 258 } 259 260 private void startConfirmation(String key, String fingerprints) { 261 String nameString = Resources.getSystem().getString( 262 com.android.internal.R.string.config_customAdbPublicKeyConfirmationComponent); 263 ComponentName componentName = ComponentName.unflattenFromString(nameString); 264 if (startConfirmationActivity(componentName, key, fingerprints) 265 || startConfirmationService(componentName, key, fingerprints)) { 266 return; 267 } 268 Slog.e(TAG, "unable to start customAdbPublicKeyConfirmationComponent " 269 + nameString + " as an Activity or a Service"); 270 } 271 272 /** 273 * @returns true if the componentName led to an Activity that was started. 274 */ 275 private boolean startConfirmationActivity(ComponentName componentName, String key, 276 String fingerprints) { 277 PackageManager packageManager = mContext.getPackageManager(); 278 Intent intent = createConfirmationIntent(componentName, key, fingerprints); 279 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 280 if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) { 281 try { 282 mContext.startActivity(intent); 283 return true; 284 } catch (ActivityNotFoundException e) { 285 Slog.e(TAG, "unable to start adb whitelist activity: " + componentName, e); 286 } 287 } 288 return false; 289 } 290 291 /** 292 * @returns true if the componentName led to a Service that was started. 293 */ 294 private boolean startConfirmationService(ComponentName componentName, String key, 295 String fingerprints) { 296 Intent intent = createConfirmationIntent(componentName, key, fingerprints); 297 try { 298 if (mContext.startService(intent) != null) { 299 return true; 300 } 301 } catch (SecurityException e) { 302 Slog.e(TAG, "unable to start adb whitelist service: " + componentName, e); 303 } 304 return false; 305 } 306 307 private Intent createConfirmationIntent(ComponentName componentName, String key, 308 String fingerprints) { 309 Intent intent = new Intent(); 310 intent.setClassName(componentName.getPackageName(), componentName.getClassName()); 311 intent.putExtra("key", key); 312 intent.putExtra("fingerprints", fingerprints); 313 return intent; 314 } 315 316 private File getUserKeyFile() { 317 File dataDir = Environment.getDataDirectory(); 318 File adbDir = new File(dataDir, ADB_DIRECTORY); 319 320 if (!adbDir.exists()) { 321 Slog.e(TAG, "ADB data directory does not exist"); 322 return null; 323 } 324 325 return new File(adbDir, ADB_KEYS_FILE); 326 } 327 328 private void writeKey(String key) { 329 try { 330 File keyFile = getUserKeyFile(); 331 332 if (keyFile == null) { 333 return; 334 } 335 336 if (!keyFile.exists()) { 337 keyFile.createNewFile(); 338 FileUtils.setPermissions(keyFile.toString(), 339 FileUtils.S_IRUSR | FileUtils.S_IWUSR | 340 FileUtils.S_IRGRP, -1, -1); 341 } 342 343 FileOutputStream fo = new FileOutputStream(keyFile, true); 344 fo.write(key.getBytes()); 345 fo.write('\n'); 346 fo.close(); 347 } 348 catch (IOException ex) { 349 Slog.e(TAG, "Error writing key:" + ex); 350 } 351 } 352 353 private void deleteKeyFile() { 354 File keyFile = getUserKeyFile(); 355 if (keyFile != null) { 356 keyFile.delete(); 357 } 358 } 359 360 public void setAdbEnabled(boolean enabled) { 361 mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED 362 : UsbDebuggingHandler.MESSAGE_ADB_DISABLED); 363 } 364 365 public void allowUsbDebugging(boolean alwaysAllow, String publicKey) { 366 Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW); 367 msg.arg1 = alwaysAllow ? 1 : 0; 368 msg.obj = publicKey; 369 mHandler.sendMessage(msg); 370 } 371 372 public void denyUsbDebugging() { 373 mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY); 374 } 375 376 public void clearUsbDebuggingKeys() { 377 mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_CLEAR); 378 } 379 380 public void dump(FileDescriptor fd, PrintWriter pw) { 381 pw.println(" USB Debugging State:"); 382 pw.println(" Connected to adbd: " + (mOutputStream != null)); 383 pw.println(" Last key received: " + mFingerprints); 384 pw.println(" User keys:"); 385 try { 386 pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null)); 387 } catch (IOException e) { 388 pw.println("IOException: " + e); 389 } 390 pw.println(" System keys:"); 391 try { 392 pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null)); 393 } catch (IOException e) { 394 pw.println("IOException: " + e); 395 } 396 } 397 } 398