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