Home | History | Annotate | Download | only in usb
      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 
     17 package com.android.server.usb;
     18 
     19 import android.content.ActivityNotFoundException;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.net.LocalSocket;
     23 import android.net.LocalSocketAddress;
     24 import android.os.Handler;
     25 import android.os.HandlerThread;
     26 import android.os.Environment;
     27 import android.os.FileUtils;
     28 import android.os.Looper;
     29 import android.os.Message;
     30 import android.os.Process;
     31 import android.os.SystemClock;
     32 import android.util.Slog;
     33 import android.util.Base64;
     34 
     35 import java.lang.Thread;
     36 import java.io.File;
     37 import java.io.FileDescriptor;
     38 import java.io.FileOutputStream;
     39 import java.io.IOException;
     40 import java.io.InputStream;
     41 import java.io.OutputStream;
     42 import java.io.PrintWriter;
     43 import java.security.MessageDigest;
     44 import java.util.Arrays;
     45 
     46 public 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         private static final int MESSAGE_ADB_CLEAR = 6;
    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 = new Thread(UsbDebuggingManager.this);
    169                     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 
    185                     mThread = null;
    186                     mOutputStream = null;
    187                     mSocket = null;
    188                     break;
    189 
    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                 }
    218 
    219                 case MESSAGE_ADB_CLEAR:
    220                     deleteKeyFile();
    221                     break;
    222             }
    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 
    265     private File getUserKeyFile() {
    266         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");
    271             return null;
    272         }
    273 
    274         return new File(adbDir, ADB_KEYS_FILE);
    275     }
    276 
    277     private void writeKey(String key) {
    278         try {
    279             File keyFile = getUserKeyFile();
    280 
    281             if (keyFile == null) {
    282                 return;
    283             }
    284 
    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 
    302     private void deleteKeyFile() {
    303         File keyFile = getUserKeyFile();
    304         if (keyFile != null) {
    305             keyFile.delete();
    306         }
    307     }
    308 
    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 
    325     public void clearUsbDebuggingKeys() {
    326         mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_CLEAR);
    327     }
    328 
    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 }
    347