blob: ce953a4b134e7aa49b19276a3939ed72e0507b49 [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;
Benoit Goby4e68bd42012-04-25 18:06:00 -070025import android.os.Environment;
26import android.os.FileUtils;
27import android.os.Looper;
28import android.os.Message;
Benoit Goby4e68bd42012-04-25 18:06:00 -070029import android.os.SystemClock;
30import android.util.Slog;
31import android.util.Base64;
Dianne Hackborn8d044e82013-04-30 17:24:15 -070032import com.android.server.FgThread;
Benoit Goby4e68bd42012-04-25 18:06:00 -070033
34import java.lang.Thread;
35import java.io.File;
36import java.io.FileDescriptor;
37import java.io.FileOutputStream;
38import java.io.IOException;
39import java.io.InputStream;
40import java.io.OutputStream;
41import java.io.PrintWriter;
42import java.security.MessageDigest;
43import java.util.Arrays;
44
45public class UsbDebuggingManager implements Runnable {
46 private static final String TAG = "UsbDebuggingManager";
47 private static final boolean DEBUG = false;
48
49 private final String ADBD_SOCKET = "adbd";
50 private final String ADB_DIRECTORY = "misc/adb";
51 private final String ADB_KEYS_FILE = "adb_keys";
52 private final int BUFFER_SIZE = 4096;
53
54 private final Context mContext;
Benoit Goby4e68bd42012-04-25 18:06:00 -070055 private final Handler mHandler;
Benoit Goby509bf2d2012-08-29 15:23:30 -070056 private Thread mThread;
Benoit Goby4e68bd42012-04-25 18:06:00 -070057 private boolean mAdbEnabled = false;
58 private String mFingerprints;
59 private LocalSocket mSocket = null;
60 private OutputStream mOutputStream = null;
61
62 public UsbDebuggingManager(Context context) {
Dianne Hackborn8d044e82013-04-30 17:24:15 -070063 mHandler = new UsbDebuggingHandler(FgThread.get().getLooper());
Benoit Goby4e68bd42012-04-25 18:06:00 -070064 mContext = context;
65 }
66
67 private void listenToSocket() throws IOException {
68 try {
69 byte[] buffer = new byte[BUFFER_SIZE];
70 LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET,
71 LocalSocketAddress.Namespace.RESERVED);
72 InputStream inputStream = null;
73
74 mSocket = new LocalSocket();
75 mSocket.connect(address);
76
77 mOutputStream = mSocket.getOutputStream();
78 inputStream = mSocket.getInputStream();
79
80 while (true) {
81 int count = inputStream.read(buffer);
82 if (count < 0) {
83 Slog.e(TAG, "got " + count + " reading");
84 break;
85 }
86
87 if (buffer[0] == 'P' && buffer[1] == 'K') {
88 String key = new String(Arrays.copyOfRange(buffer, 2, count));
89 Slog.d(TAG, "Received public key: " + key);
90 Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_CONFIRM);
91 msg.obj = key;
92 mHandler.sendMessage(msg);
93 }
94 else {
95 Slog.e(TAG, "Wrong message: " + (new String(Arrays.copyOfRange(buffer, 0, 2))));
96 break;
97 }
98 }
99 } catch (IOException ex) {
100 Slog.e(TAG, "Communication error: ", ex);
101 throw ex;
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;
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800150 private static final int MESSAGE_ADB_CLEAR = 6;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700151
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
Dianne Hackbornefa92b22013-05-03 14:11:43 -0700164 mThread = new Thread(UsbDebuggingManager.this, TAG);
Benoit Goby4e68bd42012-04-25 18:06:00 -0700165 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
Benoit Goby509bf2d2012-08-29 15:23:30 -0700181 mThread = null;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700182 mOutputStream = null;
183 mSocket = null;
Benoit Goby37ce5c52012-08-29 13:49:07 -0700184 break;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700185
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 mFingerprints = getFingerprints(key);
211 showConfirmationDialog(key, mFingerprints);
212 break;
213 }
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800214
215 case MESSAGE_ADB_CLEAR:
216 deleteKeyFile();
217 break;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700218 }
219 }
220 }
221
222 private String getFingerprints(String key) {
223 String hex = "0123456789ABCDEF";
224 StringBuilder sb = new StringBuilder();
225 MessageDigest digester;
226
227 try {
228 digester = MessageDigest.getInstance("MD5");
229 } catch (Exception ex) {
230 Slog.e(TAG, "Error getting digester: " + ex);
231 return "";
232 }
233
234 byte[] base64_data = key.split("\\s+")[0].getBytes();
235 byte[] digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));
236
237 for (int i = 0; i < digest.length; i++) {
238 sb.append(hex.charAt((digest[i] >> 4) & 0xf));
239 sb.append(hex.charAt(digest[i] & 0xf));
240 if (i < digest.length - 1)
241 sb.append(":");
242 }
243 return sb.toString();
244 }
245
246 private void showConfirmationDialog(String key, String fingerprints) {
247 Intent dialogIntent = new Intent();
248
249 dialogIntent.setClassName("com.android.systemui",
250 "com.android.systemui.usb.UsbDebuggingActivity");
251 dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
252 dialogIntent.putExtra("key", key);
253 dialogIntent.putExtra("fingerprints", fingerprints);
254 try {
255 mContext.startActivity(dialogIntent);
256 } catch (ActivityNotFoundException e) {
257 Slog.e(TAG, "unable to start UsbDebuggingActivity");
258 }
259 }
260
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800261 private File getUserKeyFile() {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700262 File dataDir = Environment.getDataDirectory();
263 File adbDir = new File(dataDir, ADB_DIRECTORY);
264
265 if (!adbDir.exists()) {
266 Slog.e(TAG, "ADB data directory does not exist");
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800267 return null;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700268 }
269
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800270 return new File(adbDir, ADB_KEYS_FILE);
271 }
272
273 private void writeKey(String key) {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700274 try {
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800275 File keyFile = getUserKeyFile();
276
277 if (keyFile == null) {
278 return;
279 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700280
281 if (!keyFile.exists()) {
282 keyFile.createNewFile();
283 FileUtils.setPermissions(keyFile.toString(),
284 FileUtils.S_IRUSR | FileUtils.S_IWUSR |
285 FileUtils.S_IRGRP, -1, -1);
286 }
287
288 FileOutputStream fo = new FileOutputStream(keyFile, true);
289 fo.write(key.getBytes());
290 fo.write('\n');
291 fo.close();
292 }
293 catch (IOException ex) {
294 Slog.e(TAG, "Error writing key:" + ex);
295 }
296 }
297
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800298 private void deleteKeyFile() {
299 File keyFile = getUserKeyFile();
300 if (keyFile != null) {
301 keyFile.delete();
302 }
303 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700304
305 public void setAdbEnabled(boolean enabled) {
306 mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED
307 : UsbDebuggingHandler.MESSAGE_ADB_DISABLED);
308 }
309
310 public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
311 Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW);
312 msg.arg1 = alwaysAllow ? 1 : 0;
313 msg.obj = publicKey;
314 mHandler.sendMessage(msg);
315 }
316
317 public void denyUsbDebugging() {
318 mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY);
319 }
320
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800321 public void clearUsbDebuggingKeys() {
322 mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_CLEAR);
323 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700324
325 public void dump(FileDescriptor fd, PrintWriter pw) {
326 pw.println(" USB Debugging State:");
327 pw.println(" Connected to adbd: " + (mOutputStream != null));
328 pw.println(" Last key received: " + mFingerprints);
329 pw.println(" User keys:");
330 try {
331 pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
332 } catch (IOException e) {
333 pw.println("IOException: " + e);
334 }
335 pw.println(" System keys:");
336 try {
337 pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null));
338 } catch (IOException e) {
339 pw.println("IOException: " + e);
340 }
341 }
342}