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