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