1 /* 2 * Copyright (C) 2015 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 and 14 * limitations under the License. 15 */ 16 package com.android.settings.deviceinfo; 17 18 import android.content.Context; 19 import android.content.Intent; 20 import android.content.IntentFilter; 21 import android.content.pm.PackageManager; 22 import android.hardware.usb.UsbManager; 23 import android.hardware.usb.UsbPort; 24 import android.hardware.usb.UsbPortStatus; 25 import android.os.UserHandle; 26 import android.os.UserManager; 27 28 public class UsbBackend { 29 30 private static final int MODE_POWER_MASK = 0x01; 31 public static final int MODE_POWER_SINK = 0x00; 32 public static final int MODE_POWER_SOURCE = 0x01; 33 34 private static final int MODE_DATA_MASK = 0x03 << 1; 35 public static final int MODE_DATA_NONE = 0x00 << 1; 36 public static final int MODE_DATA_MTP = 0x01 << 1; 37 public static final int MODE_DATA_PTP = 0x02 << 1; 38 public static final int MODE_DATA_MIDI = 0x03 << 1; 39 40 private final boolean mRestricted; 41 private final boolean mRestrictedBySystem; 42 private final boolean mMidi; 43 44 private UserManager mUserManager; 45 private UsbManager mUsbManager; 46 private UsbPort mPort; 47 private UsbPortStatus mPortStatus; 48 49 private boolean mIsUnlocked; 50 UsbBackend(Context context)51 public UsbBackend(Context context) { 52 Intent intent = context.registerReceiver(null, 53 new IntentFilter(UsbManager.ACTION_USB_STATE)); 54 mIsUnlocked = intent == null ? 55 false : intent.getBooleanExtra(UsbManager.USB_DATA_UNLOCKED, false); 56 57 mUserManager = UserManager.get(context); 58 mUsbManager = context.getSystemService(UsbManager.class); 59 60 mRestricted = mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER); 61 mRestrictedBySystem = mUserManager.hasBaseUserRestriction( 62 UserManager.DISALLOW_USB_FILE_TRANSFER, UserHandle.of(UserHandle.myUserId())); 63 mMidi = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI); 64 65 UsbPort[] ports = mUsbManager.getPorts(); 66 // For now look for a connected port, in the future we should identify port in the 67 // notification and pick based on that. 68 final int N = ports.length; 69 for (int i = 0; i < N; i++) { 70 UsbPortStatus status = mUsbManager.getPortStatus(ports[i]); 71 if (status.isConnected()) { 72 mPort = ports[i]; 73 mPortStatus = status; 74 break; 75 } 76 } 77 } 78 getCurrentMode()79 public int getCurrentMode() { 80 if (mPort != null) { 81 int power = mPortStatus.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE 82 ? MODE_POWER_SOURCE : MODE_POWER_SINK; 83 return power | getUsbDataMode(); 84 } 85 return MODE_POWER_SINK | getUsbDataMode(); 86 } 87 getUsbDataMode()88 public int getUsbDataMode() { 89 if (!mIsUnlocked) { 90 return MODE_DATA_NONE; 91 } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MTP)) { 92 return MODE_DATA_MTP; 93 } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_PTP)) { 94 return MODE_DATA_PTP; 95 } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MIDI)) { 96 return MODE_DATA_MIDI; 97 } 98 return MODE_DATA_NONE; // ... 99 } 100 setUsbFunction(int mode)101 private void setUsbFunction(int mode) { 102 switch (mode) { 103 case MODE_DATA_MTP: 104 mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP); 105 mUsbManager.setUsbDataUnlocked(true); 106 break; 107 case MODE_DATA_PTP: 108 mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_PTP); 109 mUsbManager.setUsbDataUnlocked(true); 110 break; 111 case MODE_DATA_MIDI: 112 mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MIDI); 113 mUsbManager.setUsbDataUnlocked(true); 114 break; 115 default: 116 mUsbManager.setCurrentFunction(null); 117 mUsbManager.setUsbDataUnlocked(false); 118 break; 119 } 120 } 121 setMode(int mode)122 public void setMode(int mode) { 123 if (mPort != null) { 124 int powerRole = modeToPower(mode); 125 // If we aren't using any data modes and we support host mode, then go to host mode 126 // so maybe? the other device can provide data if it wants, otherwise go into device 127 // mode because we have no choice. 128 int dataRole = (mode & MODE_DATA_MASK) == MODE_DATA_NONE 129 && mPortStatus.isRoleCombinationSupported(powerRole, UsbPort.DATA_ROLE_HOST) 130 ? UsbPort.DATA_ROLE_HOST : UsbPort.DATA_ROLE_DEVICE; 131 mUsbManager.setPortRoles(mPort, powerRole, dataRole); 132 } 133 setUsbFunction(mode & MODE_DATA_MASK); 134 } 135 modeToPower(int mode)136 private int modeToPower(int mode) { 137 return (mode & MODE_POWER_MASK) == MODE_POWER_SOURCE 138 ? UsbPort.POWER_ROLE_SOURCE : UsbPort.POWER_ROLE_SINK; 139 } 140 isModeDisallowed(int mode)141 public boolean isModeDisallowed(int mode) { 142 if (mRestricted && (mode & MODE_DATA_MASK) != MODE_DATA_NONE 143 && (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) { 144 // No USB data modes are supported. 145 return true; 146 } 147 return false; 148 } 149 isModeDisallowedBySystem(int mode)150 public boolean isModeDisallowedBySystem(int mode) { 151 if (mRestrictedBySystem && (mode & MODE_DATA_MASK) != MODE_DATA_NONE 152 && (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) { 153 // No USB data modes are supported. 154 return true; 155 } 156 return false; 157 } 158 isModeSupported(int mode)159 public boolean isModeSupported(int mode) { 160 if (!mMidi && (mode & MODE_DATA_MASK) == MODE_DATA_MIDI) { 161 return false; 162 } 163 164 if (mPort != null) { 165 int power = modeToPower(mode); 166 if ((mode & MODE_DATA_MASK) != 0) { 167 // We have a port and data, need to be in device mode. 168 return mPortStatus.isRoleCombinationSupported(power, 169 UsbPort.DATA_ROLE_DEVICE); 170 } else { 171 // No data needed, we can do this power mode in either device or host. 172 return mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_DEVICE) 173 || mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_HOST); 174 } 175 } 176 // No port, support sink modes only. 177 return (mode & MODE_POWER_MASK) != MODE_POWER_SOURCE; 178 } 179 } 180