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