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.ComponentName;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.pm.PackageManager;
     24 import android.content.pm.ResolveInfo;
     25 import android.content.res.Resources;
     26 import android.net.LocalSocket;
     27 import android.net.LocalSocketAddress;
     28 import android.os.Handler;
     29 import android.os.Environment;
     30 import android.os.FileUtils;
     31 import android.os.Looper;
     32 import android.os.Message;
     33 import android.os.SystemClock;
     34 import android.util.Slog;
     35 import android.util.Base64;
     36 import com.android.server.FgThread;
     37 
     38 import java.lang.Thread;
     39 import java.io.File;
     40 import java.io.FileDescriptor;
     41 import java.io.FileOutputStream;
     42 import java.io.IOException;
     43 import java.io.InputStream;
     44 import java.io.OutputStream;
     45 import java.io.PrintWriter;
     46 import java.security.MessageDigest;
     47 import java.util.Arrays;
     48 
     49 public class UsbDebuggingManager implements Runnable {
     50     private static final String TAG = "UsbDebuggingManager";
     51     private static final boolean DEBUG = false;
     52 
     53     private final String ADBD_SOCKET = "adbd";
     54     private final String ADB_DIRECTORY = "misc/adb";
     55     private final String ADB_KEYS_FILE = "adb_keys";
     56     private final int BUFFER_SIZE = 4096;
     57 
     58     private final Context mContext;
     59     private final Handler mHandler;
     60     private Thread mThread;
     61     private boolean mAdbEnabled = false;
     62     private String mFingerprints;
     63     private LocalSocket mSocket = null;
     64     private OutputStream mOutputStream = null;
     65 
     66     public UsbDebuggingManager(Context context) {
     67         mHandler = new UsbDebuggingHandler(FgThread.get().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                     break;
     88                 }
     89 
     90                 if (buffer[0] == 'P' && buffer[1] == 'K') {
     91                     String key = new String(Arrays.copyOfRange(buffer, 2, count));
     92                     Slog.d(TAG, "Received public key: " + key);
     93                     Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_CONFIRM);
     94                     msg.obj = key;
     95                     mHandler.sendMessage(msg);
     96                 }
     97                 else {
     98                     Slog.e(TAG, "Wrong message: " + (new String(Arrays.copyOfRange(buffer, 0, 2))));
     99                     break;
    100                 }
    101             }
    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                     startConfirmation(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 startConfirmation(String key, String fingerprints) {
    247         String nameString = Resources.getSystem().getString(
    248                 com.android.internal.R.string.config_customAdbPublicKeyConfirmationComponent);
    249         ComponentName componentName = ComponentName.unflattenFromString(nameString);
    250         if (startConfirmationActivity(componentName, key, fingerprints)
    251                 || startConfirmationService(componentName, key, fingerprints)) {
    252             return;
    253         }
    254         Slog.e(TAG, "unable to start customAdbPublicKeyConfirmationComponent "
    255                 + nameString + " as an Activity or a Service");
    256     }
    257 
    258     /**
    259      * @returns true if the componentName led to an Activity that was started.
    260      */
    261     private boolean startConfirmationActivity(ComponentName componentName, String key,
    262             String fingerprints) {
    263         PackageManager packageManager = mContext.getPackageManager();
    264         Intent intent = createConfirmationIntent(componentName, key, fingerprints);
    265         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    266         if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
    267             try {
    268                 mContext.startActivity(intent);
    269                 return true;
    270             } catch (ActivityNotFoundException e) {
    271                 Slog.e(TAG, "unable to start adb whitelist activity: " + componentName, e);
    272             }
    273         }
    274         return false;
    275     }
    276 
    277     /**
    278      * @returns true if the componentName led to a Service that was started.
    279      */
    280     private boolean startConfirmationService(ComponentName componentName, String key,
    281             String fingerprints) {
    282         Intent intent = createConfirmationIntent(componentName, key, fingerprints);
    283         try {
    284             if (mContext.startService(intent) != null) {
    285                 return true;
    286             }
    287         } catch (SecurityException e) {
    288             Slog.e(TAG, "unable to start adb whitelist service: " + componentName, e);
    289         }
    290         return false;
    291     }
    292 
    293     private Intent createConfirmationIntent(ComponentName componentName, String key,
    294             String fingerprints) {
    295         Intent intent = new Intent();
    296         intent.setClassName(componentName.getPackageName(), componentName.getClassName());
    297         intent.putExtra("key", key);
    298         intent.putExtra("fingerprints", fingerprints);
    299         return intent;
    300     }
    301 
    302     private File getUserKeyFile() {
    303         File dataDir = Environment.getDataDirectory();
    304         File adbDir = new File(dataDir, ADB_DIRECTORY);
    305 
    306         if (!adbDir.exists()) {
    307             Slog.e(TAG, "ADB data directory does not exist");
    308             return null;
    309         }
    310 
    311         return new File(adbDir, ADB_KEYS_FILE);
    312     }
    313 
    314     private void writeKey(String key) {
    315         try {
    316             File keyFile = getUserKeyFile();
    317 
    318             if (keyFile == null) {
    319                 return;
    320             }
    321 
    322             if (!keyFile.exists()) {
    323                 keyFile.createNewFile();
    324                 FileUtils.setPermissions(keyFile.toString(),
    325                     FileUtils.S_IRUSR | FileUtils.S_IWUSR |
    326                     FileUtils.S_IRGRP, -1, -1);
    327             }
    328 
    329             FileOutputStream fo = new FileOutputStream(keyFile, true);
    330             fo.write(key.getBytes());
    331             fo.write('\n');
    332             fo.close();
    333         }
    334         catch (IOException ex) {
    335             Slog.e(TAG, "Error writing key:" + ex);
    336         }
    337     }
    338 
    339     private void deleteKeyFile() {
    340         File keyFile = getUserKeyFile();
    341         if (keyFile != null) {
    342             keyFile.delete();
    343         }
    344     }
    345 
    346     public void setAdbEnabled(boolean enabled) {
    347         mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED
    348                                           : UsbDebuggingHandler.MESSAGE_ADB_DISABLED);
    349     }
    350 
    351     public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
    352         Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW);
    353         msg.arg1 = alwaysAllow ? 1 : 0;
    354         msg.obj = publicKey;
    355         mHandler.sendMessage(msg);
    356     }
    357 
    358     public void denyUsbDebugging() {
    359         mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY);
    360     }
    361 
    362     public void clearUsbDebuggingKeys() {
    363         mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_CLEAR);
    364     }
    365 
    366     public void dump(FileDescriptor fd, PrintWriter pw) {
    367         pw.println("  USB Debugging State:");
    368         pw.println("    Connected to adbd: " + (mOutputStream != null));
    369         pw.println("    Last key received: " + mFingerprints);
    370         pw.println("    User keys:");
    371         try {
    372             pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
    373         } catch (IOException e) {
    374             pw.println("IOException: " + e);
    375         }
    376         pw.println("    System keys:");
    377         try {
    378             pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null));
    379         } catch (IOException e) {
    380             pw.println("IOException: " + e);
    381         }
    382     }
    383 }
    384