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.deviceinfo;
17 
18 import android.content.Context;
19 import android.content.Intent;
20 import android.content.IntentFilter;
21 import android.content.pm.PackageManager;
22 import android.hardware.usb.UsbManager;
23 import android.hardware.usb.UsbPort;
24 import android.hardware.usb.UsbPortStatus;
25 import android.os.UserHandle;
26 import android.os.UserManager;
27 
28 public class UsbBackend {
29 
30     private static final int MODE_POWER_MASK  = 0x01;
31     public static final int MODE_POWER_SINK   = 0x00;
32     public static final int MODE_POWER_SOURCE = 0x01;
33 
34     private static final int MODE_DATA_MASK  = 0x03 << 1;
35     public static final int MODE_DATA_NONE   = 0x00 << 1;
36     public static final int MODE_DATA_MTP    = 0x01 << 1;
37     public static final int MODE_DATA_PTP    = 0x02 << 1;
38     public static final int MODE_DATA_MIDI   = 0x03 << 1;
39 
40     private final boolean mRestricted;
41     private final boolean mRestrictedBySystem;
42     private final boolean mMidi;
43 
44     private UserManager mUserManager;
45     private UsbManager mUsbManager;
46     private UsbPort mPort;
47     private UsbPortStatus mPortStatus;
48 
49     private boolean mIsUnlocked;
50 
UsbBackend(Context context)51     public UsbBackend(Context context) {
52         Intent intent = context.registerReceiver(null,
53                 new IntentFilter(UsbManager.ACTION_USB_STATE));
54         mIsUnlocked = intent == null ?
55                 false : intent.getBooleanExtra(UsbManager.USB_DATA_UNLOCKED, false);
56 
57         mUserManager = UserManager.get(context);
58         mUsbManager = context.getSystemService(UsbManager.class);
59 
60         mRestricted = mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
61         mRestrictedBySystem = mUserManager.hasBaseUserRestriction(
62                 UserManager.DISALLOW_USB_FILE_TRANSFER, UserHandle.of(UserHandle.myUserId()));
63         mMidi = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
64 
65         UsbPort[] ports = mUsbManager.getPorts();
66         // For now look for a connected port, in the future we should identify port in the
67         // notification and pick based on that.
68         final int N = ports.length;
69         for (int i = 0; i < N; i++) {
70             UsbPortStatus status = mUsbManager.getPortStatus(ports[i]);
71             if (status.isConnected()) {
72                 mPort = ports[i];
73                 mPortStatus = status;
74                 break;
75             }
76         }
77     }
78 
getCurrentMode()79     public int getCurrentMode() {
80         if (mPort != null) {
81             int power = mPortStatus.getCurrentPowerRole() == UsbPort.POWER_ROLE_SOURCE
82                     ? MODE_POWER_SOURCE : MODE_POWER_SINK;
83             return power | getUsbDataMode();
84         }
85         return MODE_POWER_SINK | getUsbDataMode();
86     }
87 
getUsbDataMode()88     public int getUsbDataMode() {
89         if (!mIsUnlocked) {
90             return MODE_DATA_NONE;
91         } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MTP)) {
92             return MODE_DATA_MTP;
93         } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_PTP)) {
94             return MODE_DATA_PTP;
95         } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MIDI)) {
96             return MODE_DATA_MIDI;
97         }
98         return MODE_DATA_NONE; // ...
99     }
100 
setUsbFunction(int mode)101     private void setUsbFunction(int mode) {
102         switch (mode) {
103             case MODE_DATA_MTP:
104                 mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP);
105                 mUsbManager.setUsbDataUnlocked(true);
106                 break;
107             case MODE_DATA_PTP:
108                 mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_PTP);
109                 mUsbManager.setUsbDataUnlocked(true);
110                 break;
111             case MODE_DATA_MIDI:
112                 mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MIDI);
113                 mUsbManager.setUsbDataUnlocked(true);
114                 break;
115             default:
116                 mUsbManager.setCurrentFunction(null);
117                 mUsbManager.setUsbDataUnlocked(false);
118                 break;
119         }
120     }
121 
setMode(int mode)122     public void setMode(int mode) {
123         if (mPort != null) {
124             int powerRole = modeToPower(mode);
125             // If we aren't using any data modes and we support host mode, then go to host mode
126             // so maybe? the other device can provide data if it wants, otherwise go into device
127             // mode because we have no choice.
128             int dataRole = (mode & MODE_DATA_MASK) == MODE_DATA_NONE
129                     && mPortStatus.isRoleCombinationSupported(powerRole, UsbPort.DATA_ROLE_HOST)
130                     ? UsbPort.DATA_ROLE_HOST : UsbPort.DATA_ROLE_DEVICE;
131             mUsbManager.setPortRoles(mPort, powerRole, dataRole);
132         }
133         setUsbFunction(mode & MODE_DATA_MASK);
134     }
135 
modeToPower(int mode)136     private int modeToPower(int mode) {
137         return (mode & MODE_POWER_MASK) == MODE_POWER_SOURCE
138                     ? UsbPort.POWER_ROLE_SOURCE : UsbPort.POWER_ROLE_SINK;
139     }
140 
isModeDisallowed(int mode)141     public boolean isModeDisallowed(int mode) {
142         if (mRestricted && (mode & MODE_DATA_MASK) != MODE_DATA_NONE
143                 && (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) {
144             // No USB data modes are supported.
145             return true;
146         }
147         return false;
148     }
149 
isModeDisallowedBySystem(int mode)150     public boolean isModeDisallowedBySystem(int mode) {
151         if (mRestrictedBySystem && (mode & MODE_DATA_MASK) != MODE_DATA_NONE
152                 && (mode & MODE_DATA_MASK) != MODE_DATA_MIDI) {
153             // No USB data modes are supported.
154             return true;
155         }
156         return false;
157     }
158 
isModeSupported(int mode)159     public boolean isModeSupported(int mode) {
160         if (!mMidi && (mode & MODE_DATA_MASK) == MODE_DATA_MIDI) {
161             return false;
162         }
163 
164         if (mPort != null) {
165             int power = modeToPower(mode);
166             if ((mode & MODE_DATA_MASK) != 0) {
167                 // We have a port and data, need to be in device mode.
168                 return mPortStatus.isRoleCombinationSupported(power,
169                         UsbPort.DATA_ROLE_DEVICE);
170             } else {
171                 // No data needed, we can do this power mode in either device or host.
172                 return mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_DEVICE)
173                         || mPortStatus.isRoleCombinationSupported(power, UsbPort.DATA_ROLE_HOST);
174             }
175         }
176         // No port, support sink modes only.
177         return (mode & MODE_POWER_MASK) != MODE_POWER_SOURCE;
178     }
179 }
180