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