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