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