Home | History | Annotate | Download | only in usb
      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 android.annotation.Nullable;
     19 import android.content.Context;
     20 import android.content.pm.PackageManager;
     21 import android.hardware.usb.UsbManager;
     22 import android.hardware.usb.UsbPort;
     23 import android.hardware.usb.UsbPortStatus;
     24 import android.net.ConnectivityManager;
     25 import android.os.UserHandle;
     26 import android.os.UserManager;
     27 import android.support.annotation.VisibleForTesting;
     28 
     29 /**
     30  * Provides access to underlying system USB functionality.
     31  */
     32 public class UsbBackend {
     33 
     34     static final int PD_ROLE_SWAP_TIMEOUT_MS = 3000;
     35     static final int NONPD_ROLE_SWAP_TIMEOUT_MS = 15000;
     36 
     37     private final boolean mFileTransferRestricted;
     38     private final boolean mFileTransferRestrictedBySystem;
     39     private final boolean mTetheringRestricted;
     40     private final boolean mTetheringRestrictedBySystem;
     41     private final boolean mMidiSupported;
     42     private final boolean mTetheringSupported;
     43 
     44     private UsbManager mUsbManager;
     45 
     46     @Nullable
     47     private UsbPort mPort;
     48     @Nullable
     49     private UsbPortStatus mPortStatus;
     50 
     51     public UsbBackend(Context context) {
     52         this(context, (UserManager) context.getSystemService(Context.USER_SERVICE));
     53     }
     54 
     55     @VisibleForTesting
     56     public UsbBackend(Context context, UserManager userManager) {
     57         mUsbManager = context.getSystemService(UsbManager.class);
     58 
     59         mFileTransferRestricted = isUsbFileTransferRestricted(userManager);
     60         mFileTransferRestrictedBySystem = isUsbFileTransferRestrictedBySystem(userManager);
     61         mTetheringRestricted = isUsbTetheringRestricted(userManager);
     62         mTetheringRestrictedBySystem = isUsbTetheringRestrictedBySystem(userManager);
     63 
     64         mMidiSupported = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
     65         ConnectivityManager cm =
     66                 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
     67         mTetheringSupported = cm.isTetheringSupported();
     68 
     69         updatePorts();
     70     }
     71 
     72     public long getCurrentFunctions() {
     73         return mUsbManager.getCurrentFunctions();
     74     }
     75 
     76     public void setCurrentFunctions(long functions) {
     77         mUsbManager.setCurrentFunctions(functions);
     78     }
     79 
     80     public long getDefaultUsbFunctions() {
     81         return mUsbManager.getScreenUnlockedFunctions();
     82     }
     83 
     84     public void setDefaultUsbFunctions(long functions) {
     85         mUsbManager.setScreenUnlockedFunctions(functions);
     86     }
     87 
     88     public boolean areFunctionsSupported(long functions) {
     89         if ((!mMidiSupported && (functions & UsbManager.FUNCTION_MIDI) != 0)
     90                 || (!mTetheringSupported && (functions & UsbManager.FUNCTION_RNDIS) != 0)) {
     91             return false;
     92         }
     93         return !(areFunctionDisallowed(functions) || areFunctionsDisallowedBySystem(functions));
     94     }
     95 
     96     public int getPowerRole() {
     97         updatePorts();
     98         return mPortStatus == null ? UsbPort.POWER_ROLE_NONE : mPortStatus.getCurrentPowerRole();
     99     }
    100 
    101     public int getDataRole() {
    102         updatePorts();
    103         return mPortStatus == null ? UsbPort.DATA_ROLE_NONE : mPortStatus.getCurrentDataRole();
    104     }
    105 
    106     public void setPowerRole(int role) {
    107         int newDataRole = getDataRole();
    108         if (!areAllRolesSupported()) {
    109             switch (role) {
    110                 case UsbPort.POWER_ROLE_SINK:
    111                     newDataRole = UsbPort.DATA_ROLE_DEVICE;
    112                     break;
    113                 case UsbPort.POWER_ROLE_SOURCE:
    114                     newDataRole = UsbPort.DATA_ROLE_HOST;
    115                     break;
    116                 default:
    117                     newDataRole = UsbPort.DATA_ROLE_NONE;
    118             }
    119         }
    120         if (mPort != null) {
    121             mUsbManager.setPortRoles(mPort, role, newDataRole);
    122         }
    123     }
    124 
    125     public void setDataRole(int role) {
    126         int newPowerRole = getPowerRole();
    127         if (!areAllRolesSupported()) {
    128             switch (role) {
    129                 case UsbPort.DATA_ROLE_DEVICE:
    130                     newPowerRole = UsbPort.POWER_ROLE_SINK;
    131                     break;
    132                 case UsbPort.DATA_ROLE_HOST:
    133                     newPowerRole = UsbPort.POWER_ROLE_SOURCE;
    134                     break;
    135                 default:
    136                     newPowerRole = UsbPort.POWER_ROLE_NONE;
    137             }
    138         }
    139         if (mPort != null) {
    140             mUsbManager.setPortRoles(mPort, newPowerRole, role);
    141         }
    142     }
    143 
    144     public boolean areAllRolesSupported() {
    145         return mPort != null && mPortStatus != null
    146                 && mPortStatus
    147                 .isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE)
    148                 && mPortStatus
    149                 .isRoleCombinationSupported(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST)
    150                 && mPortStatus
    151                 .isRoleCombinationSupported(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE)
    152                 && mPortStatus
    153                 .isRoleCombinationSupported(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST);
    154     }
    155 
    156     public static String usbFunctionsToString(long functions) {
    157         // TODO replace with UsbManager.usbFunctionsToString once supported by Roboelectric
    158         return Long.toBinaryString(functions);
    159     }
    160 
    161     public static long usbFunctionsFromString(String functions) {
    162         // TODO replace with UsbManager.usbFunctionsFromString once supported by Roboelectric
    163         return Long.parseLong(functions, 2);
    164     }
    165 
    166     public static String dataRoleToString(int role) {
    167         return Integer.toString(role);
    168     }
    169 
    170     public static int dataRoleFromString(String role) {
    171         return Integer.parseInt(role);
    172     }
    173 
    174     private static boolean isUsbFileTransferRestricted(UserManager userManager) {
    175         return userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
    176     }
    177 
    178     private static boolean isUsbTetheringRestricted(UserManager userManager) {
    179         return userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
    180     }
    181 
    182     private static boolean isUsbFileTransferRestrictedBySystem(UserManager userManager) {
    183         return userManager.hasBaseUserRestriction(
    184                 UserManager.DISALLOW_USB_FILE_TRANSFER, UserHandle.of(UserHandle.myUserId()));
    185     }
    186 
    187     private static boolean isUsbTetheringRestrictedBySystem(UserManager userManager) {
    188         return userManager.hasBaseUserRestriction(
    189                 UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(UserHandle.myUserId()));
    190     }
    191 
    192     private boolean areFunctionDisallowed(long functions) {
    193         return (mFileTransferRestricted && ((functions & UsbManager.FUNCTION_MTP) != 0
    194                 || (functions & UsbManager.FUNCTION_PTP) != 0))
    195                 || (mTetheringRestricted && ((functions & UsbManager.FUNCTION_RNDIS) != 0));
    196     }
    197 
    198     private boolean areFunctionsDisallowedBySystem(long functions) {
    199         return (mFileTransferRestrictedBySystem && ((functions & UsbManager.FUNCTION_MTP) != 0
    200                 || (functions & UsbManager.FUNCTION_PTP) != 0))
    201                 || (mTetheringRestrictedBySystem && ((functions & UsbManager.FUNCTION_RNDIS) != 0));
    202     }
    203 
    204     private void updatePorts() {
    205         mPort = null;
    206         mPortStatus = null;
    207         UsbPort[] ports = mUsbManager.getPorts();
    208         if (ports == null) {
    209             return;
    210         }
    211         // For now look for a connected port, in the future we should identify port in the
    212         // notification and pick based on that.
    213         final int N = ports.length;
    214         for (int i = 0; i < N; i++) {
    215             UsbPortStatus status = mUsbManager.getPortStatus(ports[i]);
    216             if (status.isConnected()) {
    217                 mPort = ports[i];
    218                 mPortStatus = status;
    219                 break;
    220             }
    221         }
    222     }
    223 }
    224