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