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