• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.car.developeroptions.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