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