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