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