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.app.ActivityManager;
     20 import android.content.ActivityNotFoundException;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.pm.PackageManager;
     25 import android.content.pm.UserInfo;
     26 import android.content.res.Resources;
     27 import android.net.LocalSocket;
     28 import android.net.LocalSocketAddress;
     29 import android.os.Environment;
     30 import android.os.FileUtils;
     31 import android.os.Handler;
     32 import android.os.Looper;
     33 import android.os.Message;
     34 import android.os.SystemClock;
     35 import android.os.SystemProperties;
     36 import android.os.UserHandle;
     37 import android.os.UserManager;
     38 import android.util.Base64;
     39 import android.util.Slog;
     40 
     41 import com.android.internal.R;
     42 import com.android.internal.util.IndentingPrintWriter;
     43 import com.android.server.FgThread;
     44 
     45 import java.io.File;
     46 import java.io.FileOutputStream;
     47 import java.io.IOException;
     48 import java.io.InputStream;
     49 import java.io.OutputStream;
     50 import java.security.MessageDigest;
     51 import java.util.Arrays;
     52 
     53 public class UsbDebuggingManager {
     54     private static final String TAG = "UsbDebuggingManager";
     55     private static final boolean DEBUG = false;
     56 
     57     private static final String ADBD_SOCKET = "adbd";
     58     private static final String ADB_DIRECTORY = "misc/adb";
     59     private static final String ADB_KEYS_FILE = "adb_keys";
     60     private static final int BUFFER_SIZE = 4096;
     61 
     62     private final Context mContext;
     63     private final Handler mHandler;
     64     private UsbDebuggingThread mThread;
     65     private boolean mAdbEnabled = false;
     66     private String mFingerprints;
     67 
     68     public UsbDebuggingManager(Context context) {
     69         mHandler = new UsbDebuggingHandler(FgThread.get().getLooper());
     70         mContext = context;
     71     }
     72 
     73     class UsbDebuggingThread extends Thread {
     74         private boolean mStopped;
     75         private LocalSocket mSocket;
     76         private OutputStream mOutputStream;
     77         private InputStream mInputStream;
     78 
     79         UsbDebuggingThread() {
     80             super(TAG);
     81         }
     82 
     83         @Override
     84         public void run() {
     85             if (DEBUG) Slog.d(TAG, "Entering thread");
     86             while (true) {
     87                 synchronized (this) {
     88                     if (mStopped) {
     89                         if (DEBUG) Slog.d(TAG, "Exiting thread");
     90                         return;
     91                     }
     92                     try {
     93                         openSocketLocked();
     94                     } catch (Exception e) {
     95                         /* Don't loop too fast if adbd dies, before init restarts it */
     96                         SystemClock.sleep(1000);
     97                     }
     98                 }
     99                 try {
    100                     listenToSocket();
    101                 } catch (Exception e) {
    102                     /* Don't loop too fast if adbd dies, before init restarts it */
    103                     SystemClock.sleep(1000);
    104                 }
    105             }
    106         }
    107 
    108         private void openSocketLocked() throws IOException {
    109             try {
    110                 LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET,
    111                         LocalSocketAddress.Namespace.RESERVED);
    112                 mInputStream = null;
    113 
    114                 if (DEBUG) Slog.d(TAG, "Creating socket");
    115                 mSocket = new LocalSocket();
    116                 mSocket.connect(address);
    117 
    118                 mOutputStream = mSocket.getOutputStream();
    119                 mInputStream = mSocket.getInputStream();
    120             } catch (IOException ioe) {
    121                 closeSocketLocked();
    122                 throw ioe;
    123             }
    124         }
    125 
    126         private void listenToSocket() throws IOException {
    127             try {
    128                 byte[] buffer = new byte[BUFFER_SIZE];
    129                 while (true) {
    130                     int count = mInputStream.read(buffer);
    131                     if (count < 0) {
    132                         break;
    133                     }
    134 
    135                     if (buffer[0] == 'P' && buffer[1] == 'K') {
    136                         String key = new String(Arrays.copyOfRange(buffer, 2, count));
    137                         Slog.d(TAG, "Received public key: " + key);
    138                         Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_CONFIRM);
    139                         msg.obj = key;
    140                         mHandler.sendMessage(msg);
    141                     } else {
    142                         Slog.e(TAG, "Wrong message: "
    143                                 + (new String(Arrays.copyOfRange(buffer, 0, 2))));
    144                         break;
    145                     }
    146                 }
    147             } finally {
    148                 synchronized (this) {
    149                     closeSocketLocked();
    150                 }
    151             }
    152         }
    153 
    154         private void closeSocketLocked() {
    155             if (DEBUG) Slog.d(TAG, "Closing socket");
    156             try {
    157                 if (mOutputStream != null) {
    158                     mOutputStream.close();
    159                     mOutputStream = null;
    160                 }
    161             } catch (IOException e) {
    162                 Slog.e(TAG, "Failed closing output stream: " + e);
    163             }
    164 
    165             try {
    166                 if (mSocket != null) {
    167                     mSocket.close();
    168                     mSocket = null;
    169                 }
    170             } catch (IOException ex) {
    171                 Slog.e(TAG, "Failed closing socket: " + ex);
    172             }
    173         }
    174 
    175         /** Call to stop listening on the socket and exit the thread. */
    176         void stopListening() {
    177             synchronized (this) {
    178                 mStopped = true;
    179                 closeSocketLocked();
    180             }
    181         }
    182 
    183         void sendResponse(String msg) {
    184             synchronized (this) {
    185                 if (!mStopped && mOutputStream != null) {
    186                     try {
    187                         mOutputStream.write(msg.getBytes());
    188                     }
    189                     catch (IOException ex) {
    190                         Slog.e(TAG, "Failed to write response:", ex);
    191                     }
    192                 }
    193             }
    194         }
    195     }
    196 
    197     class UsbDebuggingHandler extends Handler {
    198         private static final int MESSAGE_ADB_ENABLED = 1;
    199         private static final int MESSAGE_ADB_DISABLED = 2;
    200         private static final int MESSAGE_ADB_ALLOW = 3;
    201         private static final int MESSAGE_ADB_DENY = 4;
    202         private static final int MESSAGE_ADB_CONFIRM = 5;
    203         private static final int MESSAGE_ADB_CLEAR = 6;
    204 
    205         public UsbDebuggingHandler(Looper looper) {
    206             super(looper);
    207         }
    208 
    209         public void handleMessage(Message msg) {
    210             switch (msg.what) {
    211                 case MESSAGE_ADB_ENABLED:
    212                     if (mAdbEnabled)
    213                         break;
    214 
    215                     mAdbEnabled = true;
    216 
    217                     mThread = new UsbDebuggingThread();
    218                     mThread.start();
    219 
    220                     break;
    221 
    222                 case MESSAGE_ADB_DISABLED:
    223                     if (!mAdbEnabled)
    224                         break;
    225 
    226                     mAdbEnabled = false;
    227 
    228                     if (mThread != null) {
    229                         mThread.stopListening();
    230                         mThread = null;
    231                     }
    232 
    233                     break;
    234 
    235                 case MESSAGE_ADB_ALLOW: {
    236                     String key = (String)msg.obj;
    237                     String fingerprints = getFingerprints(key);
    238 
    239                     if (!fingerprints.equals(mFingerprints)) {
    240                         Slog.e(TAG, "Fingerprints do not match. Got "
    241                                 + fingerprints + ", expected " + mFingerprints);
    242                         break;
    243                     }
    244 
    245                     if (msg.arg1 == 1) {
    246                         writeKey(key);
    247                     }
    248 
    249                     if (mThread != null) {
    250                         mThread.sendResponse("OK");
    251                     }
    252                     break;
    253                 }
    254 
    255                 case MESSAGE_ADB_DENY:
    256                     if (mThread != null) {
    257                         mThread.sendResponse("NO");
    258                     }
    259                     break;
    260 
    261                 case MESSAGE_ADB_CONFIRM: {
    262                     if ("trigger_restart_min_framework".equals(
    263                             SystemProperties.get("vold.decrypt"))) {
    264                         Slog.d(TAG, "Deferring adb confirmation until after vold decrypt");
    265                         if (mThread != null) {
    266                             mThread.sendResponse("NO");
    267                         }
    268                         break;
    269                     }
    270                     String key = (String)msg.obj;
    271                     String fingerprints = getFingerprints(key);
    272                     if ("".equals(fingerprints)) {
    273                         if (mThread != null) {
    274                             mThread.sendResponse("NO");
    275                         }
    276                         break;
    277                     }
    278                     mFingerprints = fingerprints;
    279                     startConfirmation(key, mFingerprints);
    280                     break;
    281                 }
    282 
    283                 case MESSAGE_ADB_CLEAR:
    284                     deleteKeyFile();
    285                     break;
    286             }
    287         }
    288     }
    289 
    290     private String getFingerprints(String key) {
    291         String hex = "0123456789ABCDEF";
    292         StringBuilder sb = new StringBuilder();
    293         MessageDigest digester;
    294 
    295         if (key == null) {
    296             return "";
    297         }
    298 
    299         try {
    300             digester = MessageDigest.getInstance("MD5");
    301         } catch (Exception ex) {
    302             Slog.e(TAG, "Error getting digester", ex);
    303             return "";
    304         }
    305 
    306         byte[] base64_data = key.split("\\s+")[0].getBytes();
    307         byte[] digest;
    308         try {
    309             digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));
    310         } catch (IllegalArgumentException e) {
    311             Slog.e(TAG, "error doing base64 decoding", e);
    312             return "";
    313         }
    314         for (int i = 0; i < digest.length; i++) {
    315             sb.append(hex.charAt((digest[i] >> 4) & 0xf));
    316             sb.append(hex.charAt(digest[i] & 0xf));
    317             if (i < digest.length - 1)
    318                 sb.append(":");
    319         }
    320         return sb.toString();
    321     }
    322 
    323     private void startConfirmation(String key, String fingerprints) {
    324         int currentUserId = ActivityManager.getCurrentUser();
    325         UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
    326         String componentString;
    327         if (userInfo.isAdmin()) {
    328             componentString = Resources.getSystem().getString(
    329                     com.android.internal.R.string.config_customAdbPublicKeyConfirmationComponent);
    330         } else {
    331             // If the current foreground user is not the admin user we send a different
    332             // notification specific to secondary users.
    333             componentString = Resources.getSystem().getString(
    334                     R.string.config_customAdbPublicKeyConfirmationSecondaryUserComponent);
    335         }
    336         ComponentName componentName = ComponentName.unflattenFromString(componentString);
    337         if (startConfirmationActivity(componentName, userInfo.getUserHandle(), key, fingerprints)
    338                 || startConfirmationService(componentName, userInfo.getUserHandle(),
    339                         key, fingerprints)) {
    340             return;
    341         }
    342         Slog.e(TAG, "unable to start customAdbPublicKeyConfirmation[SecondaryUser]Component "
    343                 + componentString + " as an Activity or a Service");
    344     }
    345 
    346     /**
    347      * @return true if the componentName led to an Activity that was started.
    348      */
    349     private boolean startConfirmationActivity(ComponentName componentName, UserHandle userHandle,
    350             String key, String fingerprints) {
    351         PackageManager packageManager = mContext.getPackageManager();
    352         Intent intent = createConfirmationIntent(componentName, key, fingerprints);
    353         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    354         if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
    355             try {
    356                 mContext.startActivityAsUser(intent, userHandle);
    357                 return true;
    358             } catch (ActivityNotFoundException e) {
    359                 Slog.e(TAG, "unable to start adb whitelist activity: " + componentName, e);
    360             }
    361         }
    362         return false;
    363     }
    364 
    365     /**
    366      * @return true if the componentName led to a Service that was started.
    367      */
    368     private boolean startConfirmationService(ComponentName componentName, UserHandle userHandle,
    369             String key, String fingerprints) {
    370         Intent intent = createConfirmationIntent(componentName, key, fingerprints);
    371         try {
    372             if (mContext.startServiceAsUser(intent, userHandle) != null) {
    373                 return true;
    374             }
    375         } catch (SecurityException e) {
    376             Slog.e(TAG, "unable to start adb whitelist service: " + componentName, e);
    377         }
    378         return false;
    379     }
    380 
    381     private Intent createConfirmationIntent(ComponentName componentName, String key,
    382             String fingerprints) {
    383         Intent intent = new Intent();
    384         intent.setClassName(componentName.getPackageName(), componentName.getClassName());
    385         intent.putExtra("key", key);
    386         intent.putExtra("fingerprints", fingerprints);
    387         return intent;
    388     }
    389 
    390     private File getUserKeyFile() {
    391         File dataDir = Environment.getDataDirectory();
    392         File adbDir = new File(dataDir, ADB_DIRECTORY);
    393 
    394         if (!adbDir.exists()) {
    395             Slog.e(TAG, "ADB data directory does not exist");
    396             return null;
    397         }
    398 
    399         return new File(adbDir, ADB_KEYS_FILE);
    400     }
    401 
    402     private void writeKey(String key) {
    403         try {
    404             File keyFile = getUserKeyFile();
    405 
    406             if (keyFile == null) {
    407                 return;
    408             }
    409 
    410             if (!keyFile.exists()) {
    411                 keyFile.createNewFile();
    412                 FileUtils.setPermissions(keyFile.toString(),
    413                     FileUtils.S_IRUSR | FileUtils.S_IWUSR |
    414                     FileUtils.S_IRGRP, -1, -1);
    415             }
    416 
    417             FileOutputStream fo = new FileOutputStream(keyFile, true);
    418             fo.write(key.getBytes());
    419             fo.write('\n');
    420             fo.close();
    421         }
    422         catch (IOException ex) {
    423             Slog.e(TAG, "Error writing key:" + ex);
    424         }
    425     }
    426 
    427     private void deleteKeyFile() {
    428         File keyFile = getUserKeyFile();
    429         if (keyFile != null) {
    430             keyFile.delete();
    431         }
    432     }
    433 
    434     public void setAdbEnabled(boolean enabled) {
    435         mHandler.sendEmptyMessage(enabled ? UsbDebuggingHandler.MESSAGE_ADB_ENABLED
    436                                           : UsbDebuggingHandler.MESSAGE_ADB_DISABLED);
    437     }
    438 
    439     public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
    440         Message msg = mHandler.obtainMessage(UsbDebuggingHandler.MESSAGE_ADB_ALLOW);
    441         msg.arg1 = alwaysAllow ? 1 : 0;
    442         msg.obj = publicKey;
    443         mHandler.sendMessage(msg);
    444     }
    445 
    446     public void denyUsbDebugging() {
    447         mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_DENY);
    448     }
    449 
    450     public void clearUsbDebuggingKeys() {
    451         mHandler.sendEmptyMessage(UsbDebuggingHandler.MESSAGE_ADB_CLEAR);
    452     }
    453 
    454     public void dump(IndentingPrintWriter pw) {
    455         pw.println("USB Debugging State:");
    456         pw.println("  Connected to adbd: " + (mThread != null));
    457         pw.println("  Last key received: " + mFingerprints);
    458         pw.println("  User keys:");
    459         try {
    460             pw.println(FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
    461         } catch (IOException e) {
    462             pw.println("IOException: " + e);
    463         }
    464         pw.println("  System keys:");
    465         try {
    466             pw.println(FileUtils.readTextFile(new File("/adb_keys"), 0, null));
    467         } catch (IOException e) {
    468             pw.println("IOException: " + e);
    469         }
    470     }
    471 }
    472