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