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