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