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