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                     String fingerprints = getFingerprints(key);
    211                     if ("".equals(fingerprints)) {
    212                         sendResponse("NO");
    213                         break;
    214                     }
    215                     mFingerprints = fingerprints;
    216                     startConfirmation(key, mFingerprints);
    217                     break;
    218                 }
    219 
    220                 case MESSAGE_ADB_CLEAR:
    221                     deleteKeyFile();
    222                     break;
    223             }
    224         }
    225     }
    226 
    227     private String getFingerprints(String key) {
    228         String hex = "0123456789ABCDEF";
    229         StringBuilder sb = new StringBuilder();
    230         MessageDigest digester;
    231 
    232         if (key == null) {
    233             return "";
    234         }
    235 
    236         try {
    237             digester = MessageDigest.getInstance("MD5");
    238         } catch (Exception ex) {
    239             Slog.e(TAG, "Error getting digester", ex);
    240             return "";
    241         }
    242 
    243         byte[] base64_data = key.split("\\s+")[0].getBytes();
    244         byte[] digest;
    245         try {
    246             digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));
    247         } catch (IllegalArgumentException e) {
    248             Slog.e(TAG, "error doing base64 decoding", e);
    249             return "";
    250         }
    251         for (int i = 0; i < digest.length; i++) {
    252             sb.append(hex.charAt((digest[i] >> 4) & 0xf));
    253             sb.append(hex.charAt(digest[i] & 0xf));
    254             if (i < digest.length - 1)
    255                 sb.append(":");
    256         }
    257         return sb.toString();
    258     }
    259 
    260     private void startConfirmation(String key, String fingerprints) {
    261         String nameString = Resources.getSystem().getString(
    262                 com.android.internal.R.string.config_customAdbPublicKeyConfirmationComponent);
    263         ComponentName componentName = ComponentName.unflattenFromString(nameString);
    264         if (startConfirmationActivity(componentName, key, fingerprints)
    265                 || startConfirmationService(componentName, key, fingerprints)) {
    266             return;
    267         }
    268         Slog.e(TAG, "unable to start customAdbPublicKeyConfirmationComponent "
    269                 + nameString + " as an Activity or a Service");
    270     }
    271 
    272     /**
    273      * @returns true if the componentName led to an Activity that was started.
    274      */
    275     private boolean startConfirmationActivity(ComponentName componentName, String key,
    276             String fingerprints) {
    277         PackageManager packageManager = mContext.getPackageManager();
    278         Intent intent = createConfirmationIntent(componentName, key, fingerprints);
    279         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    280         if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
    281             try {
    282                 mContext.startActivity(intent);
    283                 return true;
    284             } catch (ActivityNotFoundException e) {
    285                 Slog.e(TAG, "unable to start adb whitelist activity: " + componentName, e);
    286             }
    287         }
    288         return false;
    289     }
    290 
    291     /**
    292      * @returns true if the componentName led to a Service that was started.
    293      */
    294     private boolean startConfirmationService(ComponentName componentName, String key,
    295             String fingerprints) {
    296         Intent intent = createConfirmationIntent(componentName, key, fingerprints);
    297         try {
    298             if (mContext.startService(intent) != null) {
    299                 return true;
    300             }
    301         } catch (SecurityException e) {
    302             Slog.e(TAG, "unable to start adb whitelist service: " + componentName, e);
    303         }
    304         return false;
    305     }
    306 
    307     private Intent createConfirmationIntent(ComponentName componentName, String key,
    308             String fingerprints) {
    309         Intent intent = new Intent();
    310         intent.setClassName(componentName.getPackageName(), componentName.getClassName());
    311         intent.putExtra("key", key);
    312         intent.putExtra("fingerprints", fingerprints);
    313         return intent;
    314     }
    315 
    316     private File getUserKeyFile() {
    317         File dataDir = Environment.getDataDirectory();
    318         File adbDir = new File(dataDir, ADB_DIRECTORY);
    319 
    320         if (!adbDir.exists()) {
    321             Slog.e(TAG, "ADB data directory does not exist");
    322             return null;
    323         }
    324 
    325         return new File(adbDir, ADB_KEYS_FILE);
    326     }
    327 
    328     private void writeKey(String key) {
    329         try {
    330             File keyFile = getUserKeyFile();
    331 
    332             if (keyFile == null) {
    333                 return;
    334             }
    335 
    336             if (!keyFile.exists()) {
    337                 keyFile.createNewFile();
    338                 FileUtils.setPermissions(keyFile.toString(),
    339                     FileUtils.S_IRUSR | FileUtils.S_IWUSR |
    340                     FileUtils.S_IRGRP, -1, -1);
    341             }
    342 
    343             FileOutputStream fo = new FileOutputStream(keyFile, true);
    344             fo.write(key.getBytes());
    345             fo.write('\n');
    346             fo.close();
    347         }
    348         catch (IOException ex) {
    349             Slog.e(TAG, "Error writing key:" + ex);
    350         }
    351     }
    352 
    353     private void deleteKeyFile() {
    354         File keyFile = getUserKeyFile();
    355         if (keyFile != null) {
    356             keyFile.delete();
    357         }
    358     }
    359 
    360     public void setAdbEnabled(boolean enabled) {
    361         mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED
    362                                           : UsbDebuggingHandler.MESSAGE_ADB_DISABLED);
    363     }
    364 
    365     public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
    366         Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW);
    367         msg.arg1 = alwaysAllow ? 1 : 0;
    368         msg.obj = publicKey;
    369         mHandler.sendMessage(msg);
    370     }
    371 
    372     public void denyUsbDebugging() {
    373         mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY);
    374     }
    375 
    376     public void clearUsbDebuggingKeys() {
    377         mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_CLEAR);
    378     }
    379 
    380     public void dump(FileDescriptor fd, PrintWriter pw) {
    381         pw.println("  USB Debugging State:");
    382         pw.println("    Connected to adbd: " + (mOutputStream != null));
    383         pw.println("    Last key received: " + mFingerprints);
    384         pw.println("    User keys:");
    385         try {
    386             pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
    387         } catch (IOException e) {
    388             pw.println("IOException: " + e);
    389         }
    390         pw.println("    System keys:");
    391         try {
    392             pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null));
    393         } catch (IOException e) {
    394             pw.println("IOException: " + e);
    395         }
    396     }
    397 }
    398