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