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