/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.deviceinfo; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.hardware.usb.UsbManager; import android.hardware.usb.UsbPort; import android.hardware.usb.UsbPortStatus; import android.os.UserHandle; import android.os.UserManager; public class UsbBackend { private static final int MODE_POWER_MASK = 0x01; public static final int MODE_POWER_SINK = 0x00; public static final int MODE_POWER_SOURCE = 0x01; private static final int MODE_DATA_MASK = 0x03 << 1; public static final int MODE_DATA_NONE = 0x00 << 1; public static final int MODE_DATA_MTP = 0x01 << 1; public static final int MODE_DATA_PTP = 0x02 << 1; public static final int MODE_DATA_MIDI = 0x03 << 1; private final boolean mRestricted; private final boolean mRestrictedBySystem; private final boolean mMidi; private UserManager mUserManager; private UsbManager mUsbManager; private UsbPort mPort; private UsbPortStatus mPortStatus; private boolean mIsUnlocked; public UsbBackend(Context context) { Intent intent = context.registerReceiver(null, new IntentFilter(UsbManager.ACTION_USB_STATE)); mIsUnlocked = intent == null ? false : intent.getBooleanExtra(UsbManager.USB_DATA_UNLOCKED, false); mUserManager = UserManager.get(context); mUsbManager = context.getSystemService(UsbManager.class); mRestricted = mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER); mRestrictedBySystem = mUserManager.hasBaseUserRestriction( UserManager.DISALLOW_USB_FILE_TRANSFER, UserHandle.of(UserHandle.myUserId())); mMidi = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI); UsbPort[] ports = mUsbManager.getPorts(); // For now look for a connected port, in the future we should identify port in the // notification and pick based on that. final int N = ports.length; for (int i = 0; i < N; i++) { UsbPortStatus status = mUsbManager.getPortStatus(ports[i]); if (status.isConnected()) { mPort = ports[i]; mPortStatus = status; break; } } } public int getCurrentMode() { if (mPort != null) { int power = mPortStatus.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE ? MODE_POWER_SOURCE : MODE_POWER_SINK; return power | getUsbDataMode(); } return MODE_POWER_SINK | getUsbDataMode(); } public int getUsbDataMode() { if (!mIsUnlocked) { return MODE_DATA_NONE; } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MTP)) { return MODE_DATA_MTP; } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_PTP)) { return MODE_DATA_PTP; } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MIDI)) { return MODE_DATA_MIDI; } return MODE_DATA_NONE; // ... } private void setUsbFunction(int mode) { switch (mode) { case MODE_DATA_MTP: mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP, true); break; case MODE_DATA_PTP: mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_PTP, true); break; case MODE_DATA_MIDI: mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MIDI, true); break; default: mUsbManager.setCurrentFunction(null, false); break; } } public void setMode(int mode) { if (mPort != null) { int powerRole = modeToPower(mode); // If we aren't using any data modes and we support host mode, then go to host mode // so maybe? the other device can provide data if it wants, otherwise go into device // mode because we have no choice. int dataRole = (mode & MODE_DATA_MASK) == MODE_DATA_NONE && mPortStatus.isRoleCombinationSupported(powerRole, UsbPort.DATA_ROLE_HOST) ? UsbPort.DATA_ROLE_HOST : UsbPort.DATA_ROLE_DEVICE; mUsbManager.setPortRoles(mPort, powerRole, dataRole); } setUsbFunction(mode & MODE_DATA_MASK); } private int modeToPower(int mode) { return (mode & MODE_POWER_MASK) == MODE_POWER_SOURCE ? UsbPort.POWER_ROLE_SOURCE : UsbPort.POWER_ROLE_SINK; } public boolean isModeDisallowed(int mode) { if (mRestricted && (mode & MODE_DATA_MASK) != MODE_DATA_NONE && (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) { // No USB data modes are supported. return true; } return false; } public boolean isModeDisallowedBySystem(int mode) { if (mRestrictedBySystem && (mode & MODE_DATA_MASK) != MODE_DATA_NONE && (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) { // No USB data modes are supported. return true; } return false; } public boolean isModeSupported(int mode) { if (!mMidi && (mode & MODE_DATA_MASK) == MODE_DATA_MIDI) { return false; } if (mPort != null) { int power = modeToPower(mode); if ((mode & MODE_DATA_MASK) != 0) { // We have a port and data, need to be in device mode. return mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_DEVICE); } else { // No data needed, we can do this power mode in either device or host. return mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_DEVICE) || mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_HOST); } } // No port, support sink modes only. return (mode & MODE_POWER_MASK) != MODE_POWER_SOURCE; } }