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