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.connecteddevice.usb; 17 18 import android.annotation.Nullable; 19 import android.content.Context; 20 import android.content.pm.PackageManager; 21 import android.hardware.usb.UsbManager; 22 import android.hardware.usb.UsbPort; 23 import android.hardware.usb.UsbPortStatus; 24 import android.net.ConnectivityManager; 25 import android.os.UserHandle; 26 import android.os.UserManager; 27 import android.support.annotation.VisibleForTesting; 28 29 /** 30 * Provides access to underlying system USB functionality. 31 */ 32 public class UsbBackend { 33 34 static final int PD_ROLE_SWAP_TIMEOUT_MS = 3000; 35 static final int NONPD_ROLE_SWAP_TIMEOUT_MS = 15000; 36 37 private final boolean mFileTransferRestricted; 38 private final boolean mFileTransferRestrictedBySystem; 39 private final boolean mTetheringRestricted; 40 private final boolean mTetheringRestrictedBySystem; 41 private final boolean mMidiSupported; 42 private final boolean mTetheringSupported; 43 44 private UsbManager mUsbManager; 45 46 @Nullable 47 private UsbPort mPort; 48 @Nullable 49 private UsbPortStatus mPortStatus; 50 51 public UsbBackend(Context context) { 52 this(context, (UserManager) context.getSystemService(Context.USER_SERVICE)); 53 } 54 55 @VisibleForTesting 56 public UsbBackend(Context context, UserManager userManager) { 57 mUsbManager = context.getSystemService(UsbManager.class); 58 59 mFileTransferRestricted = isUsbFileTransferRestricted(userManager); 60 mFileTransferRestrictedBySystem = isUsbFileTransferRestrictedBySystem(userManager); 61 mTetheringRestricted = isUsbTetheringRestricted(userManager); 62 mTetheringRestrictedBySystem = isUsbTetheringRestrictedBySystem(userManager); 63 64 mMidiSupported = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI); 65 ConnectivityManager cm = 66 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 67 mTetheringSupported = cm.isTetheringSupported(); 68 69 updatePorts(); 70 } 71 72 public long getCurrentFunctions() { 73 return mUsbManager.getCurrentFunctions(); 74 } 75 76 public void setCurrentFunctions(long functions) { 77 mUsbManager.setCurrentFunctions(functions); 78 } 79 80 public long getDefaultUsbFunctions() { 81 return mUsbManager.getScreenUnlockedFunctions(); 82 } 83 84 public void setDefaultUsbFunctions(long functions) { 85 mUsbManager.setScreenUnlockedFunctions(functions); 86 } 87 88 public boolean areFunctionsSupported(long functions) { 89 if ((!mMidiSupported && (functions & UsbManager.FUNCTION_MIDI) != 0) 90 || (!mTetheringSupported && (functions & UsbManager.FUNCTION_RNDIS) != 0)) { 91 return false; 92 } 93 return !(areFunctionDisallowed(functions) || areFunctionsDisallowedBySystem(functions)); 94 } 95 96 public int getPowerRole() { 97 updatePorts(); 98 return mPortStatus == null ? UsbPort.POWER_ROLE_NONE : mPortStatus.getCurrentPowerRole(); 99 } 100 101 public int getDataRole() { 102 updatePorts(); 103 return mPortStatus == null ? UsbPort.DATA_ROLE_NONE : mPortStatus.getCurrentDataRole(); 104 } 105 106 public void setPowerRole(int role) { 107 int newDataRole = getDataRole(); 108 if (!areAllRolesSupported()) { 109 switch (role) { 110 case UsbPort.POWER_ROLE_SINK: 111 newDataRole = UsbPort.DATA_ROLE_DEVICE; 112 break; 113 case UsbPort.POWER_ROLE_SOURCE: 114 newDataRole = UsbPort.DATA_ROLE_HOST; 115 break; 116 default: 117 newDataRole = UsbPort.DATA_ROLE_NONE; 118 } 119 } 120 if (mPort != null) { 121 mUsbManager.setPortRoles(mPort, role, newDataRole); 122 } 123 } 124 125 public void setDataRole(int role) { 126 int newPowerRole = getPowerRole(); 127 if (!areAllRolesSupported()) { 128 switch (role) { 129 case UsbPort.DATA_ROLE_DEVICE: 130 newPowerRole = UsbPort.POWER_ROLE_SINK; 131 break; 132 case UsbPort.DATA_ROLE_HOST: 133 newPowerRole = UsbPort.POWER_ROLE_SOURCE; 134 break; 135 default: 136 newPowerRole = UsbPort.POWER_ROLE_NONE; 137 } 138 } 139 if (mPort != null) { 140 mUsbManager.setPortRoles(mPort, newPowerRole, role); 141 } 142 } 143 144 public boolean areAllRolesSupported() { 145 return mPort != null && mPortStatus != null 146 && mPortStatus 147 .isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE) 148 && mPortStatus 149 .isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST) 150 && mPortStatus 151 .isRoleCombinationSupported(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE) 152 && mPortStatus 153 .isRoleCombinationSupported(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST); 154 } 155 156 public static String usbFunctionsToString(long functions) { 157 // TODO replace with UsbManager.usbFunctionsToString once supported by Roboelectric 158 return Long.toBinaryString(functions); 159 } 160 161 public static long usbFunctionsFromString(String functions) { 162 // TODO replace with UsbManager.usbFunctionsFromString once supported by Roboelectric 163 return Long.parseLong(functions, 2); 164 } 165 166 public static String dataRoleToString(int role) { 167 return Integer.toString(role); 168 } 169 170 public static int dataRoleFromString(String role) { 171 return Integer.parseInt(role); 172 } 173 174 private static boolean isUsbFileTransferRestricted(UserManager userManager) { 175 return userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER); 176 } 177 178 private static boolean isUsbTetheringRestricted(UserManager userManager) { 179 return userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); 180 } 181 182 private static boolean isUsbFileTransferRestrictedBySystem(UserManager userManager) { 183 return userManager.hasBaseUserRestriction( 184 UserManager.DISALLOW_USB_FILE_TRANSFER, UserHandle.of(UserHandle.myUserId())); 185 } 186 187 private static boolean isUsbTetheringRestrictedBySystem(UserManager userManager) { 188 return userManager.hasBaseUserRestriction( 189 UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(UserHandle.myUserId())); 190 } 191 192 private boolean areFunctionDisallowed(long functions) { 193 return (mFileTransferRestricted && ((functions & UsbManager.FUNCTION_MTP) != 0 194 || (functions & UsbManager.FUNCTION_PTP) != 0)) 195 || (mTetheringRestricted && ((functions & UsbManager.FUNCTION_RNDIS) != 0)); 196 } 197 198 private boolean areFunctionsDisallowedBySystem(long functions) { 199 return (mFileTransferRestrictedBySystem && ((functions & UsbManager.FUNCTION_MTP) != 0 200 || (functions & UsbManager.FUNCTION_PTP) != 0)) 201 || (mTetheringRestrictedBySystem && ((functions & UsbManager.FUNCTION_RNDIS) != 0)); 202 } 203 204 private void updatePorts() { 205 mPort = null; 206 mPortStatus = null; 207 UsbPort[] ports = mUsbManager.getPorts(); 208 if (ports == null) { 209 return; 210 } 211 // For now look for a connected port, in the future we should identify port in the 212 // notification and pick based on that. 213 final int N = ports.length; 214 for (int i = 0; i < N; i++) { 215 UsbPortStatus status = mUsbManager.getPortStatus(ports[i]); 216 if (status.isConnected()) { 217 mPort = ports[i]; 218 mPortStatus = status; 219 break; 220 } 221 } 222 } 223 } 224