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