Home | History | Annotate | Download | only in usb
      1 /*
      2  * Copyright (C) 2011 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.PendingIntent;
     20 import android.app.Notification;
     21 import android.app.NotificationManager;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ComponentName;
     24 import android.content.ContentResolver;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.content.pm.PackageManager;
     29 import android.content.res.Resources;
     30 import android.database.ContentObserver;
     31 import android.hardware.usb.UsbAccessory;
     32 import android.hardware.usb.UsbManager;
     33 import android.net.Uri;
     34 import android.os.Binder;
     35 import android.os.Bundle;
     36 import android.os.FileUtils;
     37 import android.os.Handler;
     38 import android.os.HandlerThread;
     39 import android.os.Looper;
     40 import android.os.Message;
     41 import android.os.Parcelable;
     42 import android.os.ParcelFileDescriptor;
     43 import android.os.Process;
     44 import android.os.storage.StorageManager;
     45 import android.os.storage.StorageVolume;
     46 import android.os.SystemProperties;
     47 import android.os.UEventObserver;
     48 import android.provider.Settings;
     49 import android.util.Pair;
     50 import android.util.Slog;
     51 
     52 import java.io.File;
     53 import java.io.FileDescriptor;
     54 import java.io.FileNotFoundException;
     55 import java.io.IOException;
     56 import java.io.PrintWriter;
     57 import java.util.ArrayList;
     58 import java.util.LinkedList;
     59 import java.util.List;
     60 import java.util.HashMap;
     61 import java.util.Map;
     62 
     63 /**
     64  * UsbDeviceManager manages USB state in device mode.
     65  */
     66 public class UsbDeviceManager {
     67 
     68     private static final String TAG = UsbDeviceManager.class.getSimpleName();
     69     private static final boolean DEBUG = false;
     70 
     71     private static final String USB_STATE_MATCH =
     72             "DEVPATH=/devices/virtual/android_usb/android0";
     73     private static final String ACCESSORY_START_MATCH =
     74             "DEVPATH=/devices/virtual/misc/usb_accessory";
     75     private static final String FUNCTIONS_PATH =
     76             "/sys/class/android_usb/android0/functions";
     77     private static final String STATE_PATH =
     78             "/sys/class/android_usb/android0/state";
     79     private static final String MASS_STORAGE_FILE_PATH =
     80             "/sys/class/android_usb/android0/f_mass_storage/lun/file";
     81     private static final String RNDIS_ETH_ADDR_PATH =
     82             "/sys/class/android_usb/android0/f_rndis/ethaddr";
     83 
     84     private static final int MSG_UPDATE_STATE = 0;
     85     private static final int MSG_ENABLE_ADB = 1;
     86     private static final int MSG_SET_CURRENT_FUNCTION = 2;
     87     private static final int MSG_SYSTEM_READY = 3;
     88     private static final int MSG_BOOT_COMPLETED = 4;
     89 
     90     // Delay for debouncing USB disconnects.
     91     // We often get rapid connect/disconnect events when enabling USB functions,
     92     // which need debouncing.
     93     private static final int UPDATE_DELAY = 1000;
     94 
     95     private static final String BOOT_MODE_PROPERTY = "ro.bootmode";
     96 
     97     private UsbHandler mHandler;
     98     private boolean mBootCompleted;
     99 
    100     private final Context mContext;
    101     private final ContentResolver mContentResolver;
    102     private final UsbSettingsManager mSettingsManager;
    103     private NotificationManager mNotificationManager;
    104     private final boolean mHasUsbAccessory;
    105     private boolean mUseUsbNotification;
    106     private boolean mAdbEnabled;
    107     private Map<String, List<Pair<String, String>>> mOemModeMap;
    108 
    109     private class AdbSettingsObserver extends ContentObserver {
    110         public AdbSettingsObserver() {
    111             super(null);
    112         }
    113         @Override
    114         public void onChange(boolean selfChange) {
    115             boolean enable = (Settings.Secure.getInt(mContentResolver,
    116                     Settings.Secure.ADB_ENABLED, 0) > 0);
    117             mHandler.sendMessage(MSG_ENABLE_ADB, enable);
    118         }
    119     }
    120 
    121     /*
    122      * Listens for uevent messages from the kernel to monitor the USB state
    123      */
    124     private final UEventObserver mUEventObserver = new UEventObserver() {
    125         @Override
    126         public void onUEvent(UEventObserver.UEvent event) {
    127             if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString());
    128 
    129             String state = event.get("USB_STATE");
    130             String accessory = event.get("ACCESSORY");
    131             if (state != null) {
    132                 mHandler.updateState(state);
    133             } else if ("START".equals(accessory)) {
    134                 if (DEBUG) Slog.d(TAG, "got accessory start");
    135                 setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false);
    136             }
    137         }
    138     };
    139 
    140     public UsbDeviceManager(Context context, UsbSettingsManager settingsManager) {
    141         mContext = context;
    142         mContentResolver = context.getContentResolver();
    143         mSettingsManager = settingsManager;
    144         PackageManager pm = mContext.getPackageManager();
    145         mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
    146         initRndisAddress();
    147 
    148         readOemUsbOverrideConfig();
    149 
    150         // create a thread for our Handler
    151         HandlerThread thread = new HandlerThread("UsbDeviceManager",
    152                 Process.THREAD_PRIORITY_BACKGROUND);
    153         thread.start();
    154         mHandler = new UsbHandler(thread.getLooper());
    155 
    156         if (nativeIsStartRequested()) {
    157             if (DEBUG) Slog.d(TAG, "accessory attached at boot");
    158             setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false);
    159         }
    160     }
    161 
    162     public void systemReady() {
    163         if (DEBUG) Slog.d(TAG, "systemReady");
    164 
    165         mNotificationManager = (NotificationManager)
    166                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    167 
    168         // We do not show the USB notification if the primary volume supports mass storage.
    169         // The legacy mass storage UI will be used instead.
    170         boolean massStorageSupported = false;
    171         StorageManager storageManager = (StorageManager)
    172                 mContext.getSystemService(Context.STORAGE_SERVICE);
    173         StorageVolume[] volumes = storageManager.getVolumeList();
    174         if (volumes.length > 0) {
    175             massStorageSupported = volumes[0].allowMassStorage();
    176         }
    177         mUseUsbNotification = !massStorageSupported;
    178 
    179         // make sure the ADB_ENABLED setting value matches the current state
    180         Settings.Secure.putInt(mContentResolver, Settings.Secure.ADB_ENABLED, mAdbEnabled ? 1 : 0);
    181 
    182         mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
    183     }
    184 
    185     private static void initRndisAddress() {
    186         // configure RNDIS ethernet address based on our serial number using the same algorithm
    187         // we had been previously using in kernel board files
    188         final int ETH_ALEN = 6;
    189         int address[] = new int[ETH_ALEN];
    190         // first byte is 0x02 to signify a locally administered address
    191         address[0] = 0x02;
    192 
    193         String serial = SystemProperties.get("ro.serialno", "1234567890ABCDEF");
    194         int serialLength = serial.length();
    195         // XOR the USB serial across the remaining 5 bytes
    196         for (int i = 0; i < serialLength; i++) {
    197             address[i % (ETH_ALEN - 1) + 1] ^= (int)serial.charAt(i);
    198         }
    199         String addrString = String.format("%02X:%02X:%02X:%02X:%02X:%02X",
    200             address[0], address[1], address[2], address[3], address[4], address[5]);
    201         try {
    202             FileUtils.stringToFile(RNDIS_ETH_ADDR_PATH, addrString);
    203         } catch (IOException e) {
    204            Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
    205         }
    206     }
    207 
    208      private static String addFunction(String functions, String function) {
    209         if (!containsFunction(functions, function)) {
    210             if (functions.length() > 0) {
    211                 functions += ",";
    212             }
    213             functions += function;
    214         }
    215         return functions;
    216     }
    217 
    218     private static String removeFunction(String functions, String function) {
    219         String[] split = functions.split(",");
    220         for (int i = 0; i < split.length; i++) {
    221             if (function.equals(split[i])) {
    222                 split[i] = null;
    223             }
    224         }
    225         StringBuilder builder = new StringBuilder();
    226          for (int i = 0; i < split.length; i++) {
    227             String s = split[i];
    228             if (s != null) {
    229                 if (builder.length() > 0) {
    230                     builder.append(",");
    231                 }
    232                 builder.append(s);
    233             }
    234         }
    235         return builder.toString();
    236     }
    237 
    238     private static boolean containsFunction(String functions, String function) {
    239         int index = functions.indexOf(function);
    240         if (index < 0) return false;
    241         if (index > 0 && functions.charAt(index - 1) != ',') return false;
    242         int charAfter = index + function.length();
    243         if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
    244         return true;
    245     }
    246 
    247     private final class UsbHandler extends Handler {
    248 
    249         // current USB state
    250         private boolean mConnected;
    251         private boolean mConfigured;
    252         private String mCurrentFunctions;
    253         private String mDefaultFunctions;
    254         private UsbAccessory mCurrentAccessory;
    255         private int mUsbNotificationId;
    256         private boolean mAdbNotificationShown;
    257 
    258         private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
    259             public void onReceive(Context context, Intent intent) {
    260                 if (DEBUG) Slog.d(TAG, "boot completed");
    261                 mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
    262             }
    263         };
    264 
    265         public UsbHandler(Looper looper) {
    266             super(looper);
    267             try {
    268                 // persist.sys.usb.config should never be unset.  But if it is, set it to "adb"
    269                 // so we have a chance of debugging what happened.
    270                 mDefaultFunctions = SystemProperties.get("persist.sys.usb.config", "adb");
    271 
    272                 // Check if USB mode needs to be overridden depending on OEM specific bootmode.
    273                 mDefaultFunctions = processOemUsbOverride(mDefaultFunctions);
    274 
    275                 // sanity check the sys.usb.config system property
    276                 // this may be necessary if we crashed while switching USB configurations
    277                 String config = SystemProperties.get("sys.usb.config", "none");
    278                 if (!config.equals(mDefaultFunctions)) {
    279                     Slog.w(TAG, "resetting config to persistent property: " + mDefaultFunctions);
    280                     SystemProperties.set("sys.usb.config", mDefaultFunctions);
    281                 }
    282 
    283                 mCurrentFunctions = mDefaultFunctions;
    284                 String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
    285                 updateState(state);
    286                 mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
    287 
    288                 // Upgrade step for previous versions that used persist.service.adb.enable
    289                 String value = SystemProperties.get("persist.service.adb.enable", "");
    290                 if (value.length() > 0) {
    291                     char enable = value.charAt(0);
    292                     if (enable == '1') {
    293                         setAdbEnabled(true);
    294                     } else if (enable == '0') {
    295                         setAdbEnabled(false);
    296                     }
    297                     SystemProperties.set("persist.service.adb.enable", "");
    298                 }
    299 
    300                 // register observer to listen for settings changes
    301                 mContentResolver.registerContentObserver(
    302                         Settings.Secure.getUriFor(Settings.Secure.ADB_ENABLED),
    303                                 false, new AdbSettingsObserver());
    304 
    305                 // Watch for USB configuration changes
    306                 mUEventObserver.startObserving(USB_STATE_MATCH);
    307                 mUEventObserver.startObserving(ACCESSORY_START_MATCH);
    308 
    309                 mContext.registerReceiver(mBootCompletedReceiver,
    310                         new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
    311             } catch (Exception e) {
    312                 Slog.e(TAG, "Error initializing UsbHandler", e);
    313             }
    314         }
    315 
    316         public void sendMessage(int what, boolean arg) {
    317             removeMessages(what);
    318             Message m = Message.obtain(this, what);
    319             m.arg1 = (arg ? 1 : 0);
    320             sendMessage(m);
    321         }
    322 
    323         public void sendMessage(int what, Object arg) {
    324             removeMessages(what);
    325             Message m = Message.obtain(this, what);
    326             m.obj = arg;
    327             sendMessage(m);
    328         }
    329 
    330         public void sendMessage(int what, Object arg0, boolean arg1) {
    331             removeMessages(what);
    332             Message m = Message.obtain(this, what);
    333             m.obj = arg0;
    334             m.arg1 = (arg1 ? 1 : 0);
    335             sendMessage(m);
    336         }
    337 
    338         public void updateState(String state) {
    339             int connected, configured;
    340 
    341             if ("DISCONNECTED".equals(state)) {
    342                 connected = 0;
    343                 configured = 0;
    344             } else if ("CONNECTED".equals(state)) {
    345                 connected = 1;
    346                 configured = 0;
    347             } else if ("CONFIGURED".equals(state)) {
    348                 connected = 1;
    349                 configured = 1;
    350             } else {
    351                 Slog.e(TAG, "unknown state " + state);
    352                 return;
    353             }
    354             removeMessages(MSG_UPDATE_STATE);
    355             Message msg = Message.obtain(this, MSG_UPDATE_STATE);
    356             msg.arg1 = connected;
    357             msg.arg2 = configured;
    358             // debounce disconnects to avoid problems bringing up USB tethering
    359             sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0);
    360         }
    361 
    362         private boolean waitForState(String state) {
    363             // wait for the transition to complete.
    364             // give up after 1 second.
    365             for (int i = 0; i < 20; i++) {
    366                 // State transition is done when sys.usb.state is set to the new configuration
    367                 if (state.equals(SystemProperties.get("sys.usb.state"))) return true;
    368                 try {
    369                     // try again in 50ms
    370                     Thread.sleep(50);
    371                 } catch (InterruptedException e) {
    372                 }
    373             }
    374             Slog.e(TAG, "waitForState(" + state + ") FAILED");
    375             return false;
    376         }
    377 
    378         private boolean setUsbConfig(String config) {
    379             if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")");
    380             // set the new configuration
    381             SystemProperties.set("sys.usb.config", config);
    382             return waitForState(config);
    383         }
    384 
    385         private void setAdbEnabled(boolean enable) {
    386             if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable);
    387             if (enable != mAdbEnabled) {
    388                 mAdbEnabled = enable;
    389                 // Due to the persist.sys.usb.config property trigger, changing adb state requires
    390                 // switching to default function
    391                 setEnabledFunctions(mDefaultFunctions, true);
    392                 updateAdbNotification();
    393             }
    394         }
    395 
    396         private void setEnabledFunctions(String functions, boolean makeDefault) {
    397 
    398             // Do not update persystent.sys.usb.config if the device is booted up
    399             // with OEM specific mode.
    400             if (functions != null && makeDefault && !needsOemUsbOverride()) {
    401 
    402                 if (mAdbEnabled) {
    403                     functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB);
    404                 } else {
    405                     functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB);
    406                 }
    407                 if (!mDefaultFunctions.equals(functions)) {
    408                     if (!setUsbConfig("none")) {
    409                         Slog.e(TAG, "Failed to disable USB");
    410                         // revert to previous configuration if we fail
    411                         setUsbConfig(mCurrentFunctions);
    412                         return;
    413                     }
    414                     // setting this property will also change the current USB state
    415                     // via a property trigger
    416                     SystemProperties.set("persist.sys.usb.config", functions);
    417                     if (waitForState(functions)) {
    418                         mCurrentFunctions = functions;
    419                         mDefaultFunctions = functions;
    420                     } else {
    421                         Slog.e(TAG, "Failed to switch persistent USB config to " + functions);
    422                         // revert to previous configuration if we fail
    423                         SystemProperties.set("persist.sys.usb.config", mDefaultFunctions);
    424                     }
    425                 }
    426             } else {
    427                 if (functions == null) {
    428                     functions = mDefaultFunctions;
    429                 }
    430 
    431                 // Override with bootmode specific usb mode if needed
    432                 functions = processOemUsbOverride(functions);
    433 
    434                 if (mAdbEnabled) {
    435                     functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB);
    436                 } else {
    437                     functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB);
    438                 }
    439                 if (!mCurrentFunctions.equals(functions)) {
    440                     if (!setUsbConfig("none")) {
    441                         Slog.e(TAG, "Failed to disable USB");
    442                         // revert to previous configuration if we fail
    443                         setUsbConfig(mCurrentFunctions);
    444                         return;
    445                     }
    446                     if (setUsbConfig(functions)) {
    447                         mCurrentFunctions = functions;
    448                     } else {
    449                         Slog.e(TAG, "Failed to switch USB config to " + functions);
    450                         // revert to previous configuration if we fail
    451                         setUsbConfig(mCurrentFunctions);
    452                     }
    453                 }
    454             }
    455         }
    456 
    457         private void updateCurrentAccessory() {
    458             if (!mHasUsbAccessory) return;
    459 
    460             if (mConfigured) {
    461                 String[] strings = nativeGetAccessoryStrings();
    462                 if (strings != null) {
    463                     mCurrentAccessory = new UsbAccessory(strings);
    464                     Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
    465                     // defer accessoryAttached if system is not ready
    466                     if (mBootCompleted) {
    467                         mSettingsManager.accessoryAttached(mCurrentAccessory);
    468                     } // else handle in mBootCompletedReceiver
    469                 } else {
    470                     Slog.e(TAG, "nativeGetAccessoryStrings failed");
    471                 }
    472             } else if (!mConnected) {
    473                 // make sure accessory mode is off
    474                 // and restore default functions
    475                 Slog.d(TAG, "exited USB accessory mode");
    476                 setEnabledFunctions(mDefaultFunctions, false);
    477 
    478                 if (mCurrentAccessory != null) {
    479                     if (mBootCompleted) {
    480                         mSettingsManager.accessoryDetached(mCurrentAccessory);
    481                     }
    482                     mCurrentAccessory = null;
    483                 }
    484             }
    485         }
    486 
    487         private void updateUsbState() {
    488             // send a sticky broadcast containing current USB state
    489             Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
    490             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
    491             intent.putExtra(UsbManager.USB_CONNECTED, mConnected);
    492             intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
    493 
    494             if (mCurrentFunctions != null) {
    495                 String[] functions = mCurrentFunctions.split(",");
    496                 for (int i = 0; i < functions.length; i++) {
    497                     intent.putExtra(functions[i], true);
    498                 }
    499             }
    500 
    501             mContext.sendStickyBroadcast(intent);
    502         }
    503 
    504         @Override
    505         public void handleMessage(Message msg) {
    506             switch (msg.what) {
    507                 case MSG_UPDATE_STATE:
    508                     mConnected = (msg.arg1 == 1);
    509                     mConfigured = (msg.arg2 == 1);
    510                     updateUsbNotification();
    511                     updateAdbNotification();
    512                     if (containsFunction(mCurrentFunctions,
    513                             UsbManager.USB_FUNCTION_ACCESSORY)) {
    514                         updateCurrentAccessory();
    515                     }
    516 
    517                     if (!mConnected) {
    518                         // restore defaults when USB is disconnected
    519                         setEnabledFunctions(mDefaultFunctions, false);
    520                     }
    521                     if (mBootCompleted) {
    522                         updateUsbState();
    523                     }
    524                     break;
    525                 case MSG_ENABLE_ADB:
    526                     setAdbEnabled(msg.arg1 == 1);
    527                     break;
    528                 case MSG_SET_CURRENT_FUNCTION:
    529                     String function = (String)msg.obj;
    530                     boolean makeDefault = (msg.arg1 == 1);
    531                     setEnabledFunctions(function, makeDefault);
    532                     break;
    533                 case MSG_SYSTEM_READY:
    534                     updateUsbNotification();
    535                     updateAdbNotification();
    536                     updateUsbState();
    537                     break;
    538                 case MSG_BOOT_COMPLETED:
    539                     mBootCompleted = true;
    540                     if (mCurrentAccessory != null) {
    541                         mSettingsManager.accessoryAttached(mCurrentAccessory);
    542                     }
    543                     break;
    544             }
    545         }
    546 
    547         public UsbAccessory getCurrentAccessory() {
    548             return mCurrentAccessory;
    549         }
    550 
    551         private void updateUsbNotification() {
    552             if (mNotificationManager == null || !mUseUsbNotification) return;
    553             int id = 0;
    554             Resources r = mContext.getResources();
    555             if (mConnected) {
    556                 if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)) {
    557                     id = com.android.internal.R.string.usb_mtp_notification_title;
    558                 } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP)) {
    559                     id = com.android.internal.R.string.usb_ptp_notification_title;
    560                 } else if (containsFunction(mCurrentFunctions,
    561                         UsbManager.USB_FUNCTION_MASS_STORAGE)) {
    562                     id = com.android.internal.R.string.usb_cd_installer_notification_title;
    563                 } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) {
    564                     id = com.android.internal.R.string.usb_accessory_notification_title;
    565                 } else {
    566                     // There is a different notification for USB tethering so we don't need one here
    567                     if (!containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_RNDIS)) {
    568                         Slog.e(TAG, "No known USB function in updateUsbNotification");
    569                     }
    570                 }
    571             }
    572             if (id != mUsbNotificationId) {
    573                 // clear notification if title needs changing
    574                 if (mUsbNotificationId != 0) {
    575                     mNotificationManager.cancel(mUsbNotificationId);
    576                     mUsbNotificationId = 0;
    577                 }
    578                 if (id != 0) {
    579                     CharSequence message = r.getText(
    580                             com.android.internal.R.string.usb_notification_message);
    581                     CharSequence title = r.getText(id);
    582 
    583                     Notification notification = new Notification();
    584                     notification.icon = com.android.internal.R.drawable.stat_sys_data_usb;
    585                     notification.when = 0;
    586                     notification.flags = Notification.FLAG_ONGOING_EVENT;
    587                     notification.tickerText = title;
    588                     notification.defaults = 0; // please be quiet
    589                     notification.sound = null;
    590                     notification.vibrate = null;
    591 
    592                     Intent intent = Intent.makeRestartActivityTask(
    593                             new ComponentName("com.android.settings",
    594                                     "com.android.settings.UsbSettings"));
    595                     PendingIntent pi = PendingIntent.getActivity(mContext, 0,
    596                             intent, 0);
    597                     notification.setLatestEventInfo(mContext, title, message, pi);
    598                     mNotificationManager.notify(id, notification);
    599                     mUsbNotificationId = id;
    600                 }
    601             }
    602         }
    603 
    604         private void updateAdbNotification() {
    605             if (mNotificationManager == null) return;
    606             final int id = com.android.internal.R.string.adb_active_notification_title;
    607             if (mAdbEnabled && mConnected) {
    608                 if ("0".equals(SystemProperties.get("persist.adb.notify"))) return;
    609 
    610                 if (!mAdbNotificationShown) {
    611                     Resources r = mContext.getResources();
    612                     CharSequence title = r.getText(id);
    613                     CharSequence message = r.getText(
    614                             com.android.internal.R.string.adb_active_notification_message);
    615 
    616                     Notification notification = new Notification();
    617                     notification.icon = com.android.internal.R.drawable.stat_sys_adb;
    618                     notification.when = 0;
    619                     notification.flags = Notification.FLAG_ONGOING_EVENT;
    620                     notification.tickerText = title;
    621                     notification.defaults = 0; // please be quiet
    622                     notification.sound = null;
    623                     notification.vibrate = null;
    624 
    625                     Intent intent = Intent.makeRestartActivityTask(
    626                             new ComponentName("com.android.settings",
    627                                     "com.android.settings.DevelopmentSettings"));
    628                     PendingIntent pi = PendingIntent.getActivity(mContext, 0,
    629                             intent, 0);
    630                     notification.setLatestEventInfo(mContext, title, message, pi);
    631                     mAdbNotificationShown = true;
    632                     mNotificationManager.notify(id, notification);
    633                 }
    634             } else if (mAdbNotificationShown) {
    635                 mAdbNotificationShown = false;
    636                 mNotificationManager.cancel(id);
    637             }
    638         }
    639 
    640         public void dump(FileDescriptor fd, PrintWriter pw) {
    641             pw.println("  USB Device State:");
    642             pw.println("    Current Functions: " + mCurrentFunctions);
    643             pw.println("    Default Functions: " + mDefaultFunctions);
    644             pw.println("    mConnected: " + mConnected);
    645             pw.println("    mConfigured: " + mConfigured);
    646             pw.println("    mCurrentAccessory: " + mCurrentAccessory);
    647             try {
    648                 pw.println("    Kernel state: "
    649                         + FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim());
    650                 pw.println("    Kernel function list: "
    651                         + FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim());
    652                 pw.println("    Mass storage backing file: "
    653                         + FileUtils.readTextFile(new File(MASS_STORAGE_FILE_PATH), 0, null).trim());
    654             } catch (IOException e) {
    655                 pw.println("IOException: " + e);
    656             }
    657         }
    658     }
    659 
    660     /* returns the currently attached USB accessory */
    661     public UsbAccessory getCurrentAccessory() {
    662         return mHandler.getCurrentAccessory();
    663     }
    664 
    665     /* opens the currently attached USB accessory */
    666     public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
    667         UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
    668         if (currentAccessory == null) {
    669             throw new IllegalArgumentException("no accessory attached");
    670         }
    671         if (!currentAccessory.equals(accessory)) {
    672             String error = accessory.toString()
    673                     + " does not match current accessory "
    674                     + currentAccessory;
    675             throw new IllegalArgumentException(error);
    676         }
    677         mSettingsManager.checkPermission(accessory);
    678         return nativeOpenAccessory();
    679     }
    680 
    681     public void setCurrentFunction(String function, boolean makeDefault) {
    682         if (DEBUG) Slog.d(TAG, "setCurrentFunction(" + function + ") default: " + makeDefault);
    683         mHandler.sendMessage(MSG_SET_CURRENT_FUNCTION, function, makeDefault);
    684     }
    685 
    686     public void setMassStorageBackingFile(String path) {
    687         if (path == null) path = "";
    688         try {
    689             FileUtils.stringToFile(MASS_STORAGE_FILE_PATH, path);
    690         } catch (IOException e) {
    691            Slog.e(TAG, "failed to write to " + MASS_STORAGE_FILE_PATH);
    692         }
    693     }
    694 
    695     private void readOemUsbOverrideConfig() {
    696         String[] configList = mContext.getResources().getStringArray(
    697             com.android.internal.R.array.config_oemUsbModeOverride);
    698 
    699         if (configList != null) {
    700             for (String config: configList) {
    701                 String[] items = config.split(":");
    702                 if (items.length == 3) {
    703                     if (mOemModeMap == null) {
    704                         mOemModeMap = new HashMap<String, List<Pair<String, String>>>();
    705                     }
    706                     List overrideList = mOemModeMap.get(items[0]);
    707                     if (overrideList == null) {
    708                         overrideList = new LinkedList<Pair<String, String>>();
    709                         mOemModeMap.put(items[0], overrideList);
    710                     }
    711                     overrideList.add(new Pair<String, String>(items[1], items[2]));
    712                 }
    713             }
    714         }
    715     }
    716 
    717     private boolean needsOemUsbOverride() {
    718         if (mOemModeMap == null) return false;
    719 
    720         String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
    721         return (mOemModeMap.get(bootMode) != null) ? true : false;
    722     }
    723 
    724     private String processOemUsbOverride(String usbFunctions) {
    725         if ((usbFunctions == null) || (mOemModeMap == null)) return usbFunctions;
    726 
    727         String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown");
    728 
    729         List<Pair<String, String>> overrides = mOemModeMap.get(bootMode);
    730         if (overrides != null) {
    731             for (Pair<String, String> pair: overrides) {
    732                 if (pair.first.equals(usbFunctions)) {
    733                     Slog.d(TAG, "OEM USB override: " + pair.first + " ==> " + pair.second);
    734                     return pair.second;
    735                 }
    736             }
    737         }
    738         // return passed in functions as is.
    739         return usbFunctions;
    740     }
    741 
    742     public void dump(FileDescriptor fd, PrintWriter pw) {
    743         if (mHandler != null) {
    744             mHandler.dump(fd, pw);
    745         }
    746     }
    747 
    748     private native String[] nativeGetAccessoryStrings();
    749     private native ParcelFileDescriptor nativeOpenAccessory();
    750     private native boolean nativeIsStartRequested();
    751 }
    752