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