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.Environment;
     26 import android.os.FileUtils;
     27 import android.os.Looper;
     28 import android.os.Message;
     29 import android.os.SystemClock;
     30 import android.util.Slog;
     31 import android.util.Base64;
     32 import com.android.server.FgThread;
     33 
     34 import java.lang.Thread;
     35 import java.io.File;
     36 import java.io.FileDescriptor;
     37 import java.io.FileOutputStream;
     38 import java.io.IOException;
     39 import java.io.InputStream;
     40 import java.io.OutputStream;
     41 import java.io.PrintWriter;
     42 import java.security.MessageDigest;
     43 import java.util.Arrays;
     44 
     45 public 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;
     55     private final Handler mHandler;
     56     private Thread mThread;
     57     private boolean mAdbEnabled = false;
     58     private String mFingerprints;
     59     private LocalSocket mSocket = null;
     60     private OutputStream mOutputStream = null;
     61 
     62     public UsbDebuggingManager(Context context) {
     63         mHandler = new UsbDebuggingHandler(FgThread.get().getLooper());
     64         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;
    150         private static final int MESSAGE_ADB_CLEAR = 6;
    151 
    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 
    164                     mThread = new Thread(UsbDebuggingManager.this, TAG);
    165                     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 
    181                     mThread = null;
    182                     mOutputStream = null;
    183                     mSocket = null;
    184                     break;
    185 
    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                 }
    214 
    215                 case MESSAGE_ADB_CLEAR:
    216                     deleteKeyFile();
    217                     break;
    218             }
    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 
    261     private File getUserKeyFile() {
    262         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");
    267             return null;
    268         }
    269 
    270         return new File(adbDir, ADB_KEYS_FILE);
    271     }
    272 
    273     private void writeKey(String key) {
    274         try {
    275             File keyFile = getUserKeyFile();
    276 
    277             if (keyFile == null) {
    278                 return;
    279             }
    280 
    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 
    298     private void deleteKeyFile() {
    299         File keyFile = getUserKeyFile();
    300         if (keyFile != null) {
    301             keyFile.delete();
    302         }
    303     }
    304 
    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 
    321     public void clearUsbDebuggingKeys() {
    322         mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_CLEAR);
    323     }
    324 
    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 }
    343