UsbDebuggingManager.java revision 37ce5c5d5e8216f02230aeb89b147c0395e18329
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.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 Thread mThread;
57    private final Handler mHandler;
58    private final HandlerThread mHandlerThread;
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        mThread = new Thread(this);
66        mHandlerThread = new HandlerThread("UsbDebuggingHandler");
67        mHandlerThread.start();
68        mHandler = new UsbDebuggingHandler(mHandlerThread.getLooper());
69        mContext = context;
70    }
71
72    private void listenToSocket() throws IOException {
73        try {
74            byte[] buffer = new byte[BUFFER_SIZE];
75            LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET,
76                                         LocalSocketAddress.Namespace.RESERVED);
77            InputStream inputStream = null;
78
79            mSocket = new LocalSocket();
80            mSocket.connect(address);
81
82            mOutputStream = mSocket.getOutputStream();
83            inputStream = mSocket.getInputStream();
84
85            while (true) {
86                int count = inputStream.read(buffer);
87                if (count < 0) {
88                    Slog.e(TAG, "got " + count + " reading");
89                    break;
90                }
91
92                if (buffer[0] == 'P' && buffer[1] == 'K') {
93                    String key = new String(Arrays.copyOfRange(buffer, 2, count));
94                    Slog.d(TAG, "Received public key: " + key);
95                    Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_CONFIRM);
96                    msg.obj = key;
97                    mHandler.sendMessage(msg);
98                }
99                else {
100                    Slog.e(TAG, "Wrong message: " + (new String(Arrays.copyOfRange(buffer, 0, 2))));
101                    break;
102                }
103            }
104        } catch (IOException ex) {
105            Slog.e(TAG, "Communication error: ", ex);
106            throw ex;
107        } finally {
108            closeSocket();
109        }
110    }
111
112    @Override
113    public void run() {
114        while (mAdbEnabled) {
115            try {
116                listenToSocket();
117            } catch (Exception e) {
118                /* Don't loop too fast if adbd dies, before init restarts it */
119                SystemClock.sleep(1000);
120            }
121        }
122    }
123
124    private void closeSocket() {
125        try {
126            mOutputStream.close();
127        } catch (IOException e) {
128            Slog.e(TAG, "Failed closing output stream: " + e);
129        }
130
131        try {
132            mSocket.close();
133        } catch (IOException ex) {
134            Slog.e(TAG, "Failed closing socket: " + ex);
135        }
136    }
137
138    private void sendResponse(String msg) {
139        if (mOutputStream != null) {
140            try {
141                mOutputStream.write(msg.getBytes());
142            }
143            catch (IOException ex) {
144                Slog.e(TAG, "Failed to write response:", ex);
145            }
146        }
147    }
148
149    class UsbDebuggingHandler extends Handler {
150        private static final int MESSAGE_ADB_ENABLED = 1;
151        private static final int MESSAGE_ADB_DISABLED = 2;
152        private static final int MESSAGE_ADB_ALLOW = 3;
153        private static final int MESSAGE_ADB_DENY = 4;
154        private static final int MESSAGE_ADB_CONFIRM = 5;
155
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
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                    mOutputStream = null;
185                    mSocket = null;
186                    break;
187
188                case MESSAGE_ADB_ALLOW: {
189                    String key = (String)msg.obj;
190                    String fingerprints = getFingerprints(key);
191
192                    if (!fingerprints.equals(mFingerprints)) {
193                        Slog.e(TAG, "Fingerprints do not match. Got "
194                                + fingerprints + ", expected " + mFingerprints);
195                        break;
196                    }
197
198                    if (msg.arg1 == 1) {
199                        writeKey(key);
200                    }
201
202                    sendResponse("OK");
203                    break;
204                }
205
206                case MESSAGE_ADB_DENY:
207                    sendResponse("NO");
208                    break;
209
210                case MESSAGE_ADB_CONFIRM: {
211                    String key = (String)msg.obj;
212                    mFingerprints = getFingerprints(key);
213                    showConfirmationDialog(key, mFingerprints);
214                    break;
215                }
216            }
217        }
218    }
219
220    private String getFingerprints(String key) {
221        String hex = "0123456789ABCDEF";
222        StringBuilder sb = new StringBuilder();
223        MessageDigest digester;
224
225        try {
226            digester = MessageDigest.getInstance("MD5");
227        } catch (Exception ex) {
228            Slog.e(TAG, "Error getting digester: " + ex);
229            return "";
230        }
231
232        byte[] base64_data = key.split("\\s+")[0].getBytes();
233        byte[] digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));
234
235        for (int i = 0; i < digest.length; i++) {
236            sb.append(hex.charAt((digest[i] >> 4) & 0xf));
237            sb.append(hex.charAt(digest[i] & 0xf));
238            if (i < digest.length - 1)
239                sb.append(":");
240        }
241        return sb.toString();
242    }
243
244    private void showConfirmationDialog(String key, String fingerprints) {
245        Intent dialogIntent = new Intent();
246
247        dialogIntent.setClassName("com.android.systemui",
248                "com.android.systemui.usb.UsbDebuggingActivity");
249        dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
250        dialogIntent.putExtra("key", key);
251        dialogIntent.putExtra("fingerprints", fingerprints);
252        try {
253            mContext.startActivity(dialogIntent);
254        } catch (ActivityNotFoundException e) {
255            Slog.e(TAG, "unable to start UsbDebuggingActivity");
256        }
257    }
258
259    private void writeKey(String key) {
260        File dataDir = Environment.getDataDirectory();
261        File adbDir = new File(dataDir, ADB_DIRECTORY);
262
263        if (!adbDir.exists()) {
264            Slog.e(TAG, "ADB data directory does not exist");
265            return;
266        }
267
268        try {
269            File keyFile = new File(adbDir, ADB_KEYS_FILE);
270
271            if (!keyFile.exists()) {
272                keyFile.createNewFile();
273                FileUtils.setPermissions(keyFile.toString(),
274                    FileUtils.S_IRUSR | FileUtils.S_IWUSR |
275                    FileUtils.S_IRGRP, -1, -1);
276            }
277
278            FileOutputStream fo = new FileOutputStream(keyFile, true);
279            fo.write(key.getBytes());
280            fo.write('\n');
281            fo.close();
282        }
283        catch (IOException ex) {
284            Slog.e(TAG, "Error writing key:" + ex);
285        }
286    }
287
288
289    public void setAdbEnabled(boolean enabled) {
290        mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED
291                                          : UsbDebuggingHandler.MESSAGE_ADB_DISABLED);
292    }
293
294    public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
295        Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW);
296        msg.arg1 = alwaysAllow ? 1 : 0;
297        msg.obj = publicKey;
298        mHandler.sendMessage(msg);
299    }
300
301    public void denyUsbDebugging() {
302        mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY);
303    }
304
305
306    public void dump(FileDescriptor fd, PrintWriter pw) {
307        pw.println("  USB Debugging State:");
308        pw.println("    Connected to adbd: " + (mOutputStream != null));
309        pw.println("    Last key received: " + mFingerprints);
310        pw.println("    User keys:");
311        try {
312            pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
313        } catch (IOException e) {
314            pw.println("IOException: " + e);
315        }
316        pw.println("    System keys:");
317        try {
318            pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null));
319        } catch (IOException e) {
320            pw.println("IOException: " + e);
321        }
322    }
323}
324