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