Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 586d94ac authored by Mike Lockwood's avatar Mike Lockwood Committed by Android (Google) Code Review
Browse files

Merge "UsbService: Re-enable previously enabled functions when exiting accessory mode"

parents 03901913 ddc6cade
Loading
Loading
Loading
Loading
+113 −56
Original line number Original line Diff line number Diff line
@@ -44,7 +44,11 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashMap;


/**
/**
 * <p>UsbService monitors for changes to USB state.
 * UsbService monitors for changes to USB state.
 * This includes code for both USB host support (where the android device is the host)
 * as well as USB device support (android device is connected to a USB host).
 * Accessory mode is a special case of USB device mode, where the android device is
 * connected to a USB host that supports the android accessory protocol.
 */
 */
class UsbService extends IUsbManager.Stub {
class UsbService extends IUsbManager.Stub {
    private static final String TAG = UsbService.class.getSimpleName();
    private static final String TAG = UsbService.class.getSimpleName();
@@ -63,7 +67,9 @@ class UsbService extends IUsbManager.Stub {
    private static final String USB_COMPOSITE_CLASS_PATH =
    private static final String USB_COMPOSITE_CLASS_PATH =
            "/sys/class/usb_composite";
            "/sys/class/usb_composite";


    private static final int MSG_UPDATE = 0;
    private static final int MSG_UPDATE_STATE = 0;
    private static final int MSG_FUNCTION_ENABLED = 1;
    private static final int MSG_FUNCTION_DISABLED = 2;


    // Delay for debouncing USB disconnects.
    // Delay for debouncing USB disconnects.
    // We often get rapid connect/disconnect events when enabling USB functions,
    // We often get rapid connect/disconnect events when enabling USB functions,
@@ -79,7 +85,6 @@ class UsbService extends IUsbManager.Stub {
    private int mLastConfiguration = -1;
    private int mLastConfiguration = -1;


    // lists of enabled and disabled USB functions (for USB device mode)
    // lists of enabled and disabled USB functions (for USB device mode)
    // synchronize on mEnabledFunctions when using either of these lists
    private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
    private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
    private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
    private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();


@@ -90,12 +95,35 @@ class UsbService extends IUsbManager.Stub {
    private final String[] mHostBlacklist;
    private final String[] mHostBlacklist;


    private boolean mSystemReady;
    private boolean mSystemReady;

    private UsbAccessory mCurrentAccessory;
    private UsbAccessory mCurrentAccessory;
    // functions to restore after exiting accessory mode
    private final ArrayList<String> mAccessoryRestoreFunctions = new ArrayList<String>();


    private final Context mContext;
    private final Context mContext;
    private final Object mLock = new Object();


    private final void functionEnabled(String function, boolean enabled) {
    /*
        synchronized (mEnabledFunctions) {
     * Handles USB function enable/disable events (device mode)
     */
    private final void functionEnabledLocked(String function, boolean enabled) {
        boolean enteringAccessoryMode =
            (enabled && UsbManager.USB_FUNCTION_ACCESSORY.equals(function));

        if (enteringAccessoryMode) {
            // keep a list of functions to reenable after exiting accessory mode
            mAccessoryRestoreFunctions.clear();
            int count = mEnabledFunctions.size();
            for (int i = 0; i < count; i++) {
                String f = mEnabledFunctions.get(i);
                // RNDIS should not be restored and adb is handled automatically
                if (!UsbManager.USB_FUNCTION_RNDIS.equals(f) &&
                    !UsbManager.USB_FUNCTION_ADB.equals(f) &&
                    !UsbManager.USB_FUNCTION_ACCESSORY.equals(f)) {
                    mAccessoryRestoreFunctions.add(f);
                }
            }
        }
        if (enabled) {
        if (enabled) {
            if (!mEnabledFunctions.contains(function)) {
            if (!mEnabledFunctions.contains(function)) {
                mEnabledFunctions.add(function);
                mEnabledFunctions.add(function);
@@ -107,9 +135,8 @@ class UsbService extends IUsbManager.Stub {
            }
            }
            mEnabledFunctions.remove(function);
            mEnabledFunctions.remove(function);
        }
        }
        }


        if (enabled && UsbManager.USB_FUNCTION_ACCESSORY.equals(function)) {
        if (enteringAccessoryMode) {
            String[] strings = nativeGetAccessoryStrings();
            String[] strings = nativeGetAccessoryStrings();
            if (strings != null) {
            if (strings != null) {
                Log.d(TAG, "entering USB accessory mode");
                Log.d(TAG, "entering USB accessory mode");
@@ -136,6 +163,9 @@ class UsbService extends IUsbManager.Stub {
        }
        }
    }
    }


    /*
     * Listens for uevent messages from the kernel to monitor the USB state (device mode)
     */
    private final UEventObserver mUEventObserver = new UEventObserver() {
    private final UEventObserver mUEventObserver = new UEventObserver() {
        @Override
        @Override
        public void onUEvent(UEventObserver.UEvent event) {
        public void onUEvent(UEventObserver.UEvent event) {
@@ -143,7 +173,7 @@ class UsbService extends IUsbManager.Stub {
                Slog.v(TAG, "USB UEVENT: " + event.toString());
                Slog.v(TAG, "USB UEVENT: " + event.toString());
            }
            }


            synchronized (this) {
            synchronized (mLock) {
                String name = event.get("SWITCH_NAME");
                String name = event.get("SWITCH_NAME");
                String state = event.get("SWITCH_STATE");
                String state = event.get("SWITCH_STATE");
                if (name != null && state != null) {
                if (name != null && state != null) {
@@ -172,8 +202,11 @@ class UsbService extends IUsbManager.Stub {
                    if (function != null && enabledStr != null) {
                    if (function != null && enabledStr != null) {
                        // Note: we do not broadcast a change when a function is enabled or disabled.
                        // Note: we do not broadcast a change when a function is enabled or disabled.
                        // We just record the state change for the next broadcast.
                        // We just record the state change for the next broadcast.
                        boolean enabled = "1".equals(enabledStr);
                        int what = ("1".equals(enabledStr) ?
                        functionEnabled(function, enabled);
                                MSG_FUNCTION_ENABLED : MSG_FUNCTION_DISABLED);
                        Message msg = Message.obtain(mHandler, what);
                        msg.obj = function;
                        mHandler.sendMessage(msg);
                    }
                    }
                }
                }
            }
            }
@@ -197,6 +230,7 @@ class UsbService extends IUsbManager.Stub {
    private final void init() {
    private final void init() {
        char[] buffer = new char[1024];
        char[] buffer = new char[1024];


        // Read initial USB state (device mode)
        mConfiguration = -1;
        mConfiguration = -1;
        try {
        try {
            FileReader file = new FileReader(USB_CONNECTED_PATH);
            FileReader file = new FileReader(USB_CONNECTED_PATH);
@@ -217,8 +251,8 @@ class UsbService extends IUsbManager.Stub {
        if (mConfiguration < 0)
        if (mConfiguration < 0)
            return;
            return;


        // Read initial list of enabled and disabled functions (device mode)
        try {
        try {
            synchronized (mEnabledFunctions) {
            File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
            File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
            for (int i = 0; i < files.length; i++) {
            for (int i = 0; i < files.length; i++) {
                File file = new File(files[i], "enable");
                File file = new File(files[i], "enable");
@@ -233,7 +267,6 @@ class UsbService extends IUsbManager.Stub {
                    mDisabledFunctions.add(functionName);
                    mDisabledFunctions.add(functionName);
                }
                }
            }
            }
            }
        } catch (FileNotFoundException e) {
        } catch (FileNotFoundException e) {
            Slog.w(TAG, "This kernel does not have USB composite class support");
            Slog.w(TAG, "This kernel does not have USB composite class support");
        } catch (Exception e) {
        } catch (Exception e) {
@@ -251,6 +284,7 @@ class UsbService extends IUsbManager.Stub {
        return false;
        return false;
    }
    }


    /* returns true if the USB device should not be accessible by applications (host mode) */
    private boolean isBlackListed(int clazz, int subClass, int protocol) {
    private boolean isBlackListed(int clazz, int subClass, int protocol) {
        // blacklist hubs
        // blacklist hubs
        if (clazz == UsbConstants.USB_CLASS_HUB) return true;
        if (clazz == UsbConstants.USB_CLASS_HUB) return true;
@@ -264,7 +298,7 @@ class UsbService extends IUsbManager.Stub {
        return false;
        return false;
    }
    }


    // called from JNI in monitorUsbHostBus()
    /* Called from JNI in monitorUsbHostBus() to report new USB devices (host mode) */
    private void usbDeviceAdded(String deviceName, int vendorID, int productID,
    private void usbDeviceAdded(String deviceName, int vendorID, int productID,
            int deviceClass, int deviceSubclass, int deviceProtocol,
            int deviceClass, int deviceSubclass, int deviceProtocol,
            /* array of quintuples containing id, class, subclass, protocol
            /* array of quintuples containing id, class, subclass, protocol
@@ -279,7 +313,7 @@ class UsbService extends IUsbManager.Stub {
            return;
            return;
        }
        }


        synchronized (mDevices) {
        synchronized (mLock) {
            if (mDevices.get(deviceName) != null) {
            if (mDevices.get(deviceName) != null) {
                Log.w(TAG, "device already on mDevices list: " + deviceName);
                Log.w(TAG, "device already on mDevices list: " + deviceName);
                return;
                return;
@@ -338,9 +372,9 @@ class UsbService extends IUsbManager.Stub {
        }
        }
    }
    }


    // called from JNI in monitorUsbHostBus()
    /* Called from JNI in monitorUsbHostBus to report USB device removal (host mode) */
    private void usbDeviceRemoved(String deviceName) {
    private void usbDeviceRemoved(String deviceName) {
        synchronized (mDevices) {
        synchronized (mLock) {
            UsbDevice device = mDevices.remove(deviceName);
            UsbDevice device = mDevices.remove(deviceName);
            if (device != null) {
            if (device != null) {
                Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
                Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
@@ -363,7 +397,7 @@ class UsbService extends IUsbManager.Stub {
    }
    }


    void systemReady() {
    void systemReady() {
        synchronized (this) {
        synchronized (mLock) {
            if (mContext.getResources().getBoolean(
            if (mContext.getResources().getBoolean(
                    com.android.internal.R.bool.config_hasUsbHostSupport)) {
                    com.android.internal.R.bool.config_hasUsbHostSupport)) {
                // start monitoring for connected USB devices
                // start monitoring for connected USB devices
@@ -375,21 +409,27 @@ class UsbService extends IUsbManager.Stub {
        }
        }
    }
    }


    /*
     * Sends a message to update the USB connected and configured state (device mode).
     * If delayed is true, then we add a small delay in sending the message to debounce
     * the USB connection when enabling USB tethering.
     */
    private final void update(boolean delayed) {
    private final void update(boolean delayed) {
        mHandler.removeMessages(MSG_UPDATE);
        mHandler.removeMessages(MSG_UPDATE_STATE);
        mHandler.sendEmptyMessageDelayed(MSG_UPDATE, delayed ? UPDATE_DELAY : 0);
        mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0);
    }
    }


    /* Returns a list of all currently attached USB devices */
    /* Returns a list of all currently attached USB devices (host mdoe) */
    public void getDeviceList(Bundle devices) {
    public void getDeviceList(Bundle devices) {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
        synchronized (mDevices) {
        synchronized (mLock) {
            for (String name : mDevices.keySet()) {
            for (String name : mDevices.keySet()) {
                devices.putParcelable(name, mDevices.get(name));
                devices.putParcelable(name, mDevices.get(name));
            }
            }
        }
        }
    }
    }


    /* Opens the specified USB device (host mode) */
    public ParcelFileDescriptor openDevice(String deviceName) {
    public ParcelFileDescriptor openDevice(String deviceName) {
        if (isBlackListed(deviceName)) {
        if (isBlackListed(deviceName)) {
            throw new SecurityException("USB device is on a restricted bus");
            throw new SecurityException("USB device is on a restricted bus");
@@ -403,19 +443,23 @@ class UsbService extends IUsbManager.Stub {
        return nativeOpenDevice(deviceName);
        return nativeOpenDevice(deviceName);
    }
    }


    /* returns the currently attached USB accessory (device mode) */
    public UsbAccessory getCurrentAccessory() {
    public UsbAccessory getCurrentAccessory() {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
        return mCurrentAccessory;
        return mCurrentAccessory;
    }
    }


    /* opens the currently attached USB accessory (device mode) */
    public ParcelFileDescriptor openAccessory() {
    public ParcelFileDescriptor openAccessory() {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null);
        return nativeOpenAccessory();
        return nativeOpenAccessory();
    }
    }


    /*
     * This handler is for deferred handling of events related to device mode and accessories.
     */
    private final Handler mHandler = new Handler() {
    private final Handler mHandler = new Handler() {
        private void addEnabledFunctions(Intent intent) {
        private void addEnabledFunctionsLocked(Intent intent) {
            synchronized (mEnabledFunctions) {
            // include state of all USB functions in our extras
            // include state of all USB functions in our extras
            for (int i = 0; i < mEnabledFunctions.size(); i++) {
            for (int i = 0; i < mEnabledFunctions.size(); i++) {
                intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
                intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
@@ -424,13 +468,12 @@ class UsbService extends IUsbManager.Stub {
                intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
                intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
            }
            }
        }
        }
        }


        @Override
        @Override
        public void handleMessage(Message msg) {
        public void handleMessage(Message msg) {
            synchronized (mLock) {
                switch (msg.what) {
                switch (msg.what) {
                case MSG_UPDATE:
                    case MSG_UPDATE_STATE:
                    synchronized (this) {
                        if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
                        if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
                            if (mConnected == 0 && mCurrentAccessory != null) {
                            if (mConnected == 0 && mCurrentAccessory != null) {
                                // turn off accessory mode when we are disconnected
                                // turn off accessory mode when we are disconnected
@@ -438,6 +481,14 @@ class UsbService extends IUsbManager.Stub {
                                        UsbManager.USB_FUNCTION_ACCESSORY, false)) {
                                        UsbManager.USB_FUNCTION_ACCESSORY, false)) {
                                    Log.d(TAG, "exited USB accessory mode");
                                    Log.d(TAG, "exited USB accessory mode");


                                    // restore previously enabled functions
                                    for (String function : mAccessoryRestoreFunctions) {
                                        if (UsbManager.setFunctionEnabled(function, true)) {
                                            Log.e(TAG, "could not reenable function " + function);
                                        }
                                    }
                                    mAccessoryRestoreFunctions.clear();

                                    Intent intent = new Intent(
                                    Intent intent = new Intent(
                                            UsbManager.ACTION_USB_ACCESSORY_DETACHED);
                                            UsbManager.ACTION_USB_ACCESSORY_DETACHED);
                                    intent.putExtra(UsbManager.EXTRA_ACCESSORY, mCurrentAccessory);
                                    intent.putExtra(UsbManager.EXTRA_ACCESSORY, mCurrentAccessory);
@@ -468,17 +519,23 @@ class UsbService extends IUsbManager.Stub {
                            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
                            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
                            intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
                            intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
                            intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
                            intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
                            addEnabledFunctions(intent);
                            addEnabledFunctionsLocked(intent);
                            mContext.sendStickyBroadcast(intent);
                            mContext.sendStickyBroadcast(intent);
                        }
                        }
                    }
                        break;
                        break;
                    case MSG_FUNCTION_ENABLED:
                    case MSG_FUNCTION_DISABLED:
                        functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
                        break;
                }
            }
            }
        }
        }
    };
    };


    // host support
    private native void monitorUsbHostBus();
    private native void monitorUsbHostBus();
    private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
    private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
    // accessory support
    private native String[] nativeGetAccessoryStrings();
    private native String[] nativeGetAccessoryStrings();
    private native ParcelFileDescriptor nativeOpenAccessory();
    private native ParcelFileDescriptor nativeOpenAccessory();
}
}