• 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     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