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