blob: 93d31144815f90acb10479e2641449ef0ae62e12 [file] [log] [blame]
Benoit Goby4e68bd42012-04-25 18:06:00 -07001/*
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;
Benoit Goby4e68bd42012-04-25 18:06:00 -070056 private final Handler mHandler;
57 private final HandlerThread mHandlerThread;
Benoit Goby509bf2d2012-08-29 15:23:30 -070058 private Thread mThread;
Benoit Goby4e68bd42012-04-25 18:06:00 -070059 private boolean mAdbEnabled = false;
60 private String mFingerprints;
61 private LocalSocket mSocket = null;
62 private OutputStream mOutputStream = null;
63
64 public UsbDebuggingManager(Context context) {
Benoit Goby4e68bd42012-04-25 18:06:00 -070065 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;
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800154 private static final int MESSAGE_ADB_CLEAR = 6;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700155
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
Benoit Goby509bf2d2012-08-29 15:23:30 -0700168 mThread = new Thread(UsbDebuggingManager.this);
Benoit Goby4e68bd42012-04-25 18:06:00 -0700169 mThread.start();
170
171 break;
172
173 case MESSAGE_ADB_DISABLED:
174 if (!mAdbEnabled)
175 break;
176
177 mAdbEnabled = false;
178 closeSocket();
179
180 try {
181 mThread.join();
182 } catch (Exception ex) {
183 }
184
Benoit Goby509bf2d2012-08-29 15:23:30 -0700185 mThread = null;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700186 mOutputStream = null;
187 mSocket = null;
Benoit Goby37ce5c52012-08-29 13:49:07 -0700188 break;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700189
190 case MESSAGE_ADB_ALLOW: {
191 String key = (String)msg.obj;
192 String fingerprints = getFingerprints(key);
193
194 if (!fingerprints.equals(mFingerprints)) {
195 Slog.e(TAG, "Fingerprints do not match. Got "
196 + fingerprints + ", expected " + mFingerprints);
197 break;
198 }
199
200 if (msg.arg1 == 1) {
201 writeKey(key);
202 }
203
204 sendResponse("OK");
205 break;
206 }
207
208 case MESSAGE_ADB_DENY:
209 sendResponse("NO");
210 break;
211
212 case MESSAGE_ADB_CONFIRM: {
213 String key = (String)msg.obj;
214 mFingerprints = getFingerprints(key);
215 showConfirmationDialog(key, mFingerprints);
216 break;
217 }
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800218
219 case MESSAGE_ADB_CLEAR:
220 deleteKeyFile();
221 break;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700222 }
223 }
224 }
225
226 private String getFingerprints(String key) {
227 String hex = "0123456789ABCDEF";
228 StringBuilder sb = new StringBuilder();
229 MessageDigest digester;
230
231 try {
232 digester = MessageDigest.getInstance("MD5");
233 } catch (Exception ex) {
234 Slog.e(TAG, "Error getting digester: " + ex);
235 return "";
236 }
237
238 byte[] base64_data = key.split("\\s+")[0].getBytes();
239 byte[] digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));
240
241 for (int i = 0; i < digest.length; i++) {
242 sb.append(hex.charAt((digest[i] >> 4) & 0xf));
243 sb.append(hex.charAt(digest[i] & 0xf));
244 if (i < digest.length - 1)
245 sb.append(":");
246 }
247 return sb.toString();
248 }
249
250 private void showConfirmationDialog(String key, String fingerprints) {
251 Intent dialogIntent = new Intent();
252
253 dialogIntent.setClassName("com.android.systemui",
254 "com.android.systemui.usb.UsbDebuggingActivity");
255 dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
256 dialogIntent.putExtra("key", key);
257 dialogIntent.putExtra("fingerprints", fingerprints);
258 try {
259 mContext.startActivity(dialogIntent);
260 } catch (ActivityNotFoundException e) {
261 Slog.e(TAG, "unable to start UsbDebuggingActivity");
262 }
263 }
264
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800265 private File getUserKeyFile() {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700266 File dataDir = Environment.getDataDirectory();
267 File adbDir = new File(dataDir, ADB_DIRECTORY);
268
269 if (!adbDir.exists()) {
270 Slog.e(TAG, "ADB data directory does not exist");
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800271 return null;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700272 }
273
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800274 return new File(adbDir, ADB_KEYS_FILE);
275 }
276
277 private void writeKey(String key) {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700278 try {
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800279 File keyFile = getUserKeyFile();
280
281 if (keyFile == null) {
282 return;
283 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700284
285 if (!keyFile.exists()) {
286 keyFile.createNewFile();
287 FileUtils.setPermissions(keyFile.toString(),
288 FileUtils.S_IRUSR | FileUtils.S_IWUSR |
289 FileUtils.S_IRGRP, -1, -1);
290 }
291
292 FileOutputStream fo = new FileOutputStream(keyFile, true);
293 fo.write(key.getBytes());
294 fo.write('\n');
295 fo.close();
296 }
297 catch (IOException ex) {
298 Slog.e(TAG, "Error writing key:" + ex);
299 }
300 }
301
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800302 private void deleteKeyFile() {
303 File keyFile = getUserKeyFile();
304 if (keyFile != null) {
305 keyFile.delete();
306 }
307 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700308
309 public void setAdbEnabled(boolean enabled) {
310 mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED
311 : UsbDebuggingHandler.MESSAGE_ADB_DISABLED);
312 }
313
314 public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
315 Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW);
316 msg.arg1 = alwaysAllow ? 1 : 0;
317 msg.obj = publicKey;
318 mHandler.sendMessage(msg);
319 }
320
321 public void denyUsbDebugging() {
322 mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY);
323 }
324
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800325 public void clearUsbDebuggingKeys() {
326 mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_CLEAR);
327 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700328
329 public void dump(FileDescriptor fd, PrintWriter pw) {
330 pw.println(" USB Debugging State:");
331 pw.println(" Connected to adbd: " + (mOutputStream != null));
332 pw.println(" Last key received: " + mFingerprints);
333 pw.println(" User keys:");
334 try {
335 pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
336 } catch (IOException e) {
337 pw.println("IOException: " + e);
338 }
339 pw.println(" System keys:");
340 try {
341 pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null));
342 } catch (IOException e) {
343 pw.println("IOException: " + e);
344 }
345 }
346}