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 
     17 package com.android.server.usb;
     18 
     19 import com.android.internal.util.IndentingPrintWriter;
     20 import com.android.server.FgThread;
     21 
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.hardware.usb.UsbManager;
     25 import android.hardware.usb.UsbPort;
     26 import android.hardware.usb.UsbPortStatus;
     27 import android.os.Handler;
     28 import android.os.Message;
     29 import android.os.SystemClock;
     30 import android.os.SystemProperties;
     31 import android.os.UEventObserver;
     32 import android.os.UserHandle;
     33 import android.system.ErrnoException;
     34 import android.system.Os;
     35 import android.system.OsConstants;
     36 import android.util.ArrayMap;
     37 import android.util.Log;
     38 import android.util.Slog;
     39 
     40 import java.io.File;
     41 import java.io.FileWriter;
     42 import java.io.IOException;
     43 
     44 import libcore.io.IoUtils;
     45 
     46 /**
     47  * Allows trusted components to control the properties of physical USB ports
     48  * via the "/sys/class/dual_role_usb" kernel interface.
     49  * <p>
     50  * Note: This interface may not be supported on all chipsets since the USB drivers
     51  * must be changed to publish this information through the module.  At the moment
     52  * we only need this for devices with USB Type C ports to allow the System UI to
     53  * control USB charging and data direction.  On devices that do not support this
     54  * interface the list of ports may incorrectly appear to be empty
     55  * (but we don't care today).
     56  * </p>
     57  */
     58 public class UsbPortManager {
     59     private static final String TAG = "UsbPortManager";
     60 
     61     private static final int MSG_UPDATE_PORTS = 1;
     62 
     63     // UEvent path to watch.
     64     private static final String UEVENT_FILTER = "SUBSYSTEM=dual_role_usb";
     65 
     66     // SysFS directory that contains USB ports as subdirectories.
     67     private static final String SYSFS_CLASS = "/sys/class/dual_role_usb";
     68 
     69     // SysFS file that contains a USB port's supported modes.  (read-only)
     70     // Contents: "", "ufp", "dfp", or "ufp dfp".
     71     private static final String SYSFS_PORT_SUPPORTED_MODES = "supported_modes";
     72 
     73     // SysFS file that contains a USB port's current mode.  (read-write if configurable)
     74     // Contents: "", "ufp", or "dfp".
     75     private static final String SYSFS_PORT_MODE = "mode";
     76 
     77     // SysFS file that contains a USB port's current power role.  (read-write if configurable)
     78     // Contents: "", "source", or "sink".
     79     private static final String SYSFS_PORT_POWER_ROLE = "power_role";
     80 
     81     // SysFS file that contains a USB port's current data role.  (read-write if configurable)
     82     // Contents: "", "host", or "device".
     83     private static final String SYSFS_PORT_DATA_ROLE = "data_role";
     84 
     85     // Port modes: upstream facing port or downstream facing port.
     86     private static final String PORT_MODE_DFP = "dfp";
     87     private static final String PORT_MODE_UFP = "ufp";
     88 
     89     // Port power roles: source or sink.
     90     private static final String PORT_POWER_ROLE_SOURCE = "source";
     91     private static final String PORT_POWER_ROLE_SINK = "sink";
     92 
     93     // Port data roles: host or device.
     94     private static final String PORT_DATA_ROLE_HOST = "host";
     95     private static final String PORT_DATA_ROLE_DEVICE = "device";
     96 
     97     private static final String USB_TYPEC_PROP_PREFIX = "sys.usb.typec.";
     98     private static final String USB_TYPEC_STATE = "sys.usb.typec.state";
     99 
    100     // All non-trivial role combinations.
    101     private static final int COMBO_SOURCE_HOST =
    102             UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST);
    103     private static final int COMBO_SOURCE_DEVICE =
    104             UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE);
    105     private static final int COMBO_SINK_HOST =
    106             UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST);
    107     private static final int COMBO_SINK_DEVICE =
    108             UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE);
    109 
    110     // The system context.
    111     private final Context mContext;
    112 
    113     // True if we have kernel support.
    114     private final boolean mHaveKernelSupport;
    115 
    116     // Mutex for all mutable shared state.
    117     private final Object mLock = new Object();
    118 
    119     // List of all ports, indexed by id.
    120     // Ports may temporarily have different dispositions as they are added or removed
    121     // but the class invariant is that this list will only contain ports with DISPOSITION_READY
    122     // except while updatePortsLocked() is in progress.
    123     private final ArrayMap<String, PortInfo> mPorts = new ArrayMap<String, PortInfo>();
    124 
    125     // List of all simulated ports, indexed by id.
    126     private final ArrayMap<String, SimulatedPortInfo> mSimulatedPorts =
    127             new ArrayMap<String, SimulatedPortInfo>();
    128 
    129     public UsbPortManager(Context context) {
    130         mContext = context;
    131         mHaveKernelSupport = new File(SYSFS_CLASS).exists();
    132     }
    133 
    134     public void systemReady() {
    135         mUEventObserver.startObserving(UEVENT_FILTER);
    136         scheduleUpdatePorts();
    137     }
    138 
    139     public UsbPort[] getPorts() {
    140         synchronized (mLock) {
    141             final int count = mPorts.size();
    142             final UsbPort[] result = new UsbPort[count];
    143             for (int i = 0; i < count; i++) {
    144                 result[i] = mPorts.valueAt(i).mUsbPort;
    145             }
    146             return result;
    147         }
    148     }
    149 
    150     public UsbPortStatus getPortStatus(String portId) {
    151         synchronized (mLock) {
    152             final PortInfo portInfo = mPorts.get(portId);
    153             return portInfo != null ? portInfo.mUsbPortStatus : null;
    154         }
    155     }
    156 
    157     public void setPortRoles(String portId, int newPowerRole, int newDataRole,
    158             IndentingPrintWriter pw) {
    159         synchronized (mLock) {
    160             final PortInfo portInfo = mPorts.get(portId);
    161             if (portInfo == null) {
    162                 if (pw != null) {
    163                     pw.println("No such USB port: " + portId);
    164                 }
    165                 return;
    166             }
    167 
    168             // Check whether the new role is actually supported.
    169             if (!portInfo.mUsbPortStatus.isRoleCombinationSupported(newPowerRole, newDataRole)) {
    170                 logAndPrint(Log.ERROR, pw, "Attempted to set USB port into unsupported "
    171                         + "role combination: portId=" + portId
    172                         + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
    173                         + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
    174                 return;
    175             }
    176 
    177             // Check whether anything actually changed.
    178             final int currentDataRole = portInfo.mUsbPortStatus.getCurrentDataRole();
    179             final int currentPowerRole = portInfo.mUsbPortStatus.getCurrentPowerRole();
    180             if (currentDataRole == newDataRole && currentPowerRole == newPowerRole) {
    181                 if (pw != null) {
    182                     pw.println("No change.");
    183                 }
    184                 return;
    185             }
    186 
    187             // Determine whether we need to change the mode in order to accomplish this goal.
    188             // We prefer not to do this since it's more likely to fail.
    189             //
    190             // Note: Arguably it might be worth allowing the client to influence this policy
    191             // decision so that we could show more powerful developer facing UI but let's
    192             // see how far we can get without having to do that.
    193             final boolean canChangeMode = portInfo.mCanChangeMode;
    194             final boolean canChangePowerRole = portInfo.mCanChangePowerRole;
    195             final boolean canChangeDataRole = portInfo.mCanChangeDataRole;
    196             final int currentMode = portInfo.mUsbPortStatus.getCurrentMode();
    197             final int newMode;
    198             if ((!canChangePowerRole && currentPowerRole != newPowerRole)
    199                     || (!canChangeDataRole && currentDataRole != newDataRole)) {
    200                 if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SOURCE
    201                         && newDataRole == UsbPort.DATA_ROLE_HOST) {
    202                     newMode = UsbPort.MODE_DFP;
    203                 } else if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SINK
    204                         && newDataRole == UsbPort.DATA_ROLE_DEVICE) {
    205                     newMode = UsbPort.MODE_UFP;
    206                 } else {
    207                     logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations "
    208                             + "while attempting to change role: " + portInfo
    209                             + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
    210                             + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
    211                     return;
    212                 }
    213             } else {
    214                 newMode = currentMode;
    215             }
    216 
    217             // Make it happen.
    218             logAndPrint(Log.INFO, pw, "Setting USB port mode and role: portId=" + portId
    219                     + ", currentMode=" + UsbPort.modeToString(currentMode)
    220                     + ", currentPowerRole=" + UsbPort.powerRoleToString(currentPowerRole)
    221                     + ", currentDataRole=" + UsbPort.dataRoleToString(currentDataRole)
    222                     + ", newMode=" + UsbPort.modeToString(newMode)
    223                     + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
    224                     + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
    225 
    226             SimulatedPortInfo sim = mSimulatedPorts.get(portId);
    227             if (sim != null) {
    228                 // Change simulated state.
    229                 sim.mCurrentMode = newMode;
    230                 sim.mCurrentPowerRole = newPowerRole;
    231                 sim.mCurrentDataRole = newDataRole;
    232             } else if (mHaveKernelSupport) {
    233                 // Change actual state.
    234                 final File portDir = new File(SYSFS_CLASS, portId);
    235                 if (!portDir.exists()) {
    236                     logAndPrint(Log.ERROR, pw, "USB port not found: portId=" + portId);
    237                     return;
    238                 }
    239 
    240                 if (currentMode != newMode) {
    241                     // Changing the mode will have the side-effect of also changing
    242                     // the power and data roles but it might take some time to apply
    243                     // and the renegotiation might fail.  Due to limitations of the USB
    244                     // hardware, we have no way of knowing whether it will work apriori
    245                     // which is why we would prefer to set the power and data roles
    246                     // directly instead.
    247                     if (!writeFile(portDir, SYSFS_PORT_MODE,
    248                             newMode == UsbPort.MODE_DFP ? PORT_MODE_DFP : PORT_MODE_UFP)) {
    249                         logAndPrint(Log.ERROR, pw, "Failed to set the USB port mode: "
    250                                 + "portId=" + portId
    251                                 + ", newMode=" + UsbPort.modeToString(newMode));
    252                         return;
    253                     }
    254                 } else {
    255                     // Change power and data role independently as needed.
    256                     if (currentPowerRole != newPowerRole) {
    257                         if (!writeFile(portDir, SYSFS_PORT_POWER_ROLE,
    258                                 newPowerRole == UsbPort.POWER_ROLE_SOURCE
    259                                 ? PORT_POWER_ROLE_SOURCE : PORT_POWER_ROLE_SINK)) {
    260                             logAndPrint(Log.ERROR, pw, "Failed to set the USB port power role: "
    261                                     + "portId=" + portId
    262                                     + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole));
    263                             return;
    264                         }
    265                     }
    266                     if (currentDataRole != newDataRole) {
    267                         if (!writeFile(portDir, SYSFS_PORT_DATA_ROLE,
    268                                 newDataRole == UsbPort.DATA_ROLE_HOST
    269                                 ? PORT_DATA_ROLE_HOST : PORT_DATA_ROLE_DEVICE)) {
    270                             logAndPrint(Log.ERROR, pw, "Failed to set the USB port data role: "
    271                                     + "portId=" + portId
    272                                     + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
    273                             return;
    274                         }
    275                     }
    276                 }
    277             }
    278             updatePortsLocked(pw);
    279         }
    280     }
    281 
    282     public void addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw) {
    283         synchronized (mLock) {
    284             if (mSimulatedPorts.containsKey(portId)) {
    285                 pw.println("Port with same name already exists.  Please remove it first.");
    286                 return;
    287             }
    288 
    289             pw.println("Adding simulated port: portId=" + portId
    290                     + ", supportedModes=" + UsbPort.modeToString(supportedModes));
    291             mSimulatedPorts.put(portId,
    292                     new SimulatedPortInfo(portId, supportedModes));
    293             updatePortsLocked(pw);
    294         }
    295     }
    296 
    297     public void connectSimulatedPort(String portId, int mode, boolean canChangeMode,
    298             int powerRole, boolean canChangePowerRole,
    299             int dataRole, boolean canChangeDataRole, IndentingPrintWriter pw) {
    300         synchronized (mLock) {
    301             final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId);
    302             if (portInfo == null) {
    303                 pw.println("Cannot connect simulated port which does not exist.");
    304                 return;
    305             }
    306 
    307             if (mode == 0 || powerRole == 0 || dataRole == 0) {
    308                 pw.println("Cannot connect simulated port in null mode, "
    309                         + "power role, or data role.");
    310                 return;
    311             }
    312 
    313             if ((portInfo.mSupportedModes & mode) == 0) {
    314                 pw.println("Simulated port does not support mode: " + UsbPort.modeToString(mode));
    315                 return;
    316             }
    317 
    318             pw.println("Connecting simulated port: portId=" + portId
    319                     + ", mode=" + UsbPort.modeToString(mode)
    320                     + ", canChangeMode=" + canChangeMode
    321                     + ", powerRole=" + UsbPort.powerRoleToString(powerRole)
    322                     + ", canChangePowerRole=" + canChangePowerRole
    323                     + ", dataRole=" + UsbPort.dataRoleToString(dataRole)
    324                     + ", canChangeDataRole=" + canChangeDataRole);
    325             portInfo.mCurrentMode = mode;
    326             portInfo.mCanChangeMode = canChangeMode;
    327             portInfo.mCurrentPowerRole = powerRole;
    328             portInfo.mCanChangePowerRole = canChangePowerRole;
    329             portInfo.mCurrentDataRole = dataRole;
    330             portInfo.mCanChangeDataRole = canChangeDataRole;
    331             updatePortsLocked(pw);
    332         }
    333     }
    334 
    335     public void disconnectSimulatedPort(String portId, IndentingPrintWriter pw) {
    336         synchronized (mLock) {
    337             final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId);
    338             if (portInfo == null) {
    339                 pw.println("Cannot disconnect simulated port which does not exist.");
    340                 return;
    341             }
    342 
    343             pw.println("Disconnecting simulated port: portId=" + portId);
    344             portInfo.mCurrentMode = 0;
    345             portInfo.mCanChangeMode = false;
    346             portInfo.mCurrentPowerRole = 0;
    347             portInfo.mCanChangePowerRole = false;
    348             portInfo.mCurrentDataRole = 0;
    349             portInfo.mCanChangeDataRole = false;
    350             updatePortsLocked(pw);
    351         }
    352     }
    353 
    354     public void removeSimulatedPort(String portId, IndentingPrintWriter pw) {
    355         synchronized (mLock) {
    356             final int index = mSimulatedPorts.indexOfKey(portId);
    357             if (index < 0) {
    358                 pw.println("Cannot remove simulated port which does not exist.");
    359                 return;
    360             }
    361 
    362             pw.println("Disconnecting simulated port: portId=" + portId);
    363             mSimulatedPorts.removeAt(index);
    364             updatePortsLocked(pw);
    365         }
    366     }
    367 
    368     public void resetSimulation(IndentingPrintWriter pw) {
    369         synchronized (mLock) {
    370             pw.println("Removing all simulated ports and ending simulation.");
    371             if (!mSimulatedPorts.isEmpty()) {
    372                 mSimulatedPorts.clear();
    373                 updatePortsLocked(pw);
    374             }
    375         }
    376     }
    377 
    378     public void dump(IndentingPrintWriter pw) {
    379         synchronized (mLock) {
    380             pw.print("USB Port State:");
    381             if (!mSimulatedPorts.isEmpty()) {
    382                 pw.print(" (simulation active; end with 'dumpsys usb reset')");
    383             }
    384             pw.println();
    385 
    386             if (mPorts.isEmpty()) {
    387                 pw.println("  <no ports>");
    388             } else {
    389                 for (PortInfo portInfo : mPorts.values()) {
    390                     pw.println("  " + portInfo.mUsbPort.getId() + ": " + portInfo);
    391                 }
    392             }
    393         }
    394     }
    395 
    396     private void updatePortsLocked(IndentingPrintWriter pw) {
    397         // Assume all ports are gone unless informed otherwise.
    398         // Kind of pessimistic but simple.
    399         for (int i = mPorts.size(); i-- > 0; ) {
    400             mPorts.valueAt(i).mDisposition = PortInfo.DISPOSITION_REMOVED;
    401         }
    402 
    403         // Enumerate all extant ports.
    404         if (!mSimulatedPorts.isEmpty()) {
    405             final int count = mSimulatedPorts.size();
    406             for (int i = 0; i < count; i++) {
    407                 final SimulatedPortInfo portInfo = mSimulatedPorts.valueAt(i);
    408                 addOrUpdatePortLocked(portInfo.mPortId, portInfo.mSupportedModes,
    409                         portInfo.mCurrentMode, portInfo.mCanChangeMode,
    410                         portInfo.mCurrentPowerRole, portInfo.mCanChangePowerRole,
    411                         portInfo.mCurrentDataRole, portInfo.mCanChangeDataRole, pw);
    412             }
    413         } else if (mHaveKernelSupport) {
    414             final File[] portDirs = new File(SYSFS_CLASS).listFiles();
    415             if (portDirs != null) {
    416                 for (File portDir : portDirs) {
    417                     if (!portDir.isDirectory()) {
    418                         continue;
    419                     }
    420 
    421                     // Parse the sysfs file contents.
    422                     final String portId = portDir.getName();
    423                     final int supportedModes = readSupportedModes(portDir);
    424                     final int currentMode = readCurrentMode(portDir);
    425                     final boolean canChangeMode = canChangeMode(portDir);
    426                     final int currentPowerRole = readCurrentPowerRole(portDir);
    427                     final boolean canChangePowerRole = canChangePowerRole(portDir);
    428                     final int currentDataRole = readCurrentDataRole(portDir);
    429                     final boolean canChangeDataRole = canChangeDataRole(portDir);
    430                     addOrUpdatePortLocked(portId, supportedModes,
    431                             currentMode, canChangeMode,
    432                             currentPowerRole, canChangePowerRole,
    433                             currentDataRole, canChangeDataRole, pw);
    434                  }
    435             }
    436         }
    437 
    438         // Process the updates.
    439         // Once finished, the list of ports will only contain ports in DISPOSITION_READY.
    440         for (int i = mPorts.size(); i-- > 0; ) {
    441             final PortInfo portInfo = mPorts.valueAt(i);
    442             switch (portInfo.mDisposition) {
    443                 case PortInfo.DISPOSITION_ADDED:
    444                     handlePortAddedLocked(portInfo, pw);
    445                     portInfo.mDisposition = PortInfo.DISPOSITION_READY;
    446                     break;
    447                 case PortInfo.DISPOSITION_CHANGED:
    448                     handlePortChangedLocked(portInfo, pw);
    449                     portInfo.mDisposition = PortInfo.DISPOSITION_READY;
    450                     break;
    451                 case PortInfo.DISPOSITION_REMOVED:
    452                     mPorts.removeAt(i);
    453                     portInfo.mUsbPortStatus = null; // must do this early
    454                     handlePortRemovedLocked(portInfo, pw);
    455                     break;
    456             }
    457         }
    458     }
    459 
    460     // Must only be called by updatePortsLocked.
    461     private void addOrUpdatePortLocked(String portId, int supportedModes,
    462             int currentMode, boolean canChangeMode,
    463             int currentPowerRole, boolean canChangePowerRole,
    464             int currentDataRole, boolean canChangeDataRole,
    465             IndentingPrintWriter pw) {
    466         // Only allow mode switch capability for dual role ports.
    467         // Validate that the current mode matches the supported modes we expect.
    468         if (supportedModes != UsbPort.MODE_DUAL) {
    469             canChangeMode = false;
    470             if (currentMode != 0 && currentMode != supportedModes) {
    471                 logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB "
    472                         + "port driver: supportedModes=" + UsbPort.modeToString(supportedModes)
    473                         + ", currentMode=" + UsbPort.modeToString(currentMode));
    474                 currentMode = 0;
    475             }
    476         }
    477 
    478         // Determine the supported role combinations.
    479         // Note that the policy is designed to prefer setting the power and data
    480         // role independently rather than changing the mode.
    481         int supportedRoleCombinations = UsbPort.combineRolesAsBit(
    482                 currentPowerRole, currentDataRole);
    483         if (currentMode != 0 && currentPowerRole != 0 && currentDataRole != 0) {
    484             if (canChangePowerRole && canChangeDataRole) {
    485                 // Can change both power and data role independently.
    486                 // Assume all combinations are possible.
    487                 supportedRoleCombinations |=
    488                         COMBO_SOURCE_HOST | COMBO_SOURCE_DEVICE
    489                                 | COMBO_SINK_HOST | COMBO_SINK_DEVICE;
    490             } else if (canChangePowerRole) {
    491                 // Can only change power role.
    492                 // Assume data role must remain at its current value.
    493                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
    494                         UsbPort.POWER_ROLE_SOURCE, currentDataRole);
    495                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
    496                         UsbPort.POWER_ROLE_SINK, currentDataRole);
    497             } else if (canChangeDataRole) {
    498                 // Can only change data role.
    499                 // Assume power role must remain at its current value.
    500                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
    501                         currentPowerRole, UsbPort.DATA_ROLE_HOST);
    502                 supportedRoleCombinations |= UsbPort.combineRolesAsBit(
    503                         currentPowerRole, UsbPort.DATA_ROLE_DEVICE);
    504             } else if (canChangeMode) {
    505                 // Can only change the mode.
    506                 // Assume both standard UFP and DFP configurations will become available
    507                 // when this happens.
    508                 supportedRoleCombinations |= COMBO_SOURCE_HOST | COMBO_SINK_DEVICE;
    509             }
    510         }
    511 
    512         // Update the port data structures.
    513         PortInfo portInfo = mPorts.get(portId);
    514         if (portInfo == null) {
    515             portInfo = new PortInfo(portId, supportedModes);
    516             portInfo.setStatus(currentMode, canChangeMode,
    517                     currentPowerRole, canChangePowerRole,
    518                     currentDataRole, canChangeDataRole,
    519                     supportedRoleCombinations);
    520             mPorts.put(portId, portInfo);
    521         } else {
    522             // Sanity check that ports aren't changing definition out from under us.
    523             if (supportedModes != portInfo.mUsbPort.getSupportedModes()) {
    524                 logAndPrint(Log.WARN, pw, "Ignoring inconsistent list of supported modes from "
    525                         + "USB port driver (should be immutable): "
    526                         + "previous=" + UsbPort.modeToString(
    527                                 portInfo.mUsbPort.getSupportedModes())
    528                         + ", current=" + UsbPort.modeToString(supportedModes));
    529             }
    530 
    531             if (portInfo.setStatus(currentMode, canChangeMode,
    532                     currentPowerRole, canChangePowerRole,
    533                     currentDataRole, canChangeDataRole,
    534                     supportedRoleCombinations)) {
    535                 portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
    536             } else {
    537                 portInfo.mDisposition = PortInfo.DISPOSITION_READY;
    538             }
    539         }
    540     }
    541 
    542     private void handlePortAddedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
    543         logAndPrint(Log.INFO, pw, "USB port added: " + portInfo);
    544         sendPortChangedBroadcastLocked(portInfo);
    545     }
    546 
    547     private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
    548         logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo);
    549         sendPortChangedBroadcastLocked(portInfo);
    550     }
    551 
    552     private void handlePortRemovedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
    553         logAndPrint(Log.INFO, pw, "USB port removed: " + portInfo);
    554         sendPortChangedBroadcastLocked(portInfo);
    555     }
    556 
    557     private void sendPortChangedBroadcastLocked(PortInfo portInfo) {
    558         final Intent intent = new Intent(UsbManager.ACTION_USB_PORT_CHANGED);
    559         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    560         intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort);
    561         intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus);
    562 
    563         // Guard against possible reentrance by posting the broadcast from the handler
    564         // instead of from within the critical section.
    565         mHandler.post(new Runnable() {
    566             @Override
    567             public void run() {
    568                 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
    569             }
    570         });
    571     }
    572 
    573     private void scheduleUpdatePorts() {
    574         if (!mHandler.hasMessages(MSG_UPDATE_PORTS)) {
    575             mHandler.sendEmptyMessage(MSG_UPDATE_PORTS);
    576         }
    577     }
    578 
    579     private static int readSupportedModes(File portDir) {
    580         int modes = 0;
    581         final String contents = readFile(portDir, SYSFS_PORT_SUPPORTED_MODES);
    582         if (contents != null) {
    583             if (contents.contains(PORT_MODE_DFP)) {
    584                 modes |= UsbPort.MODE_DFP;
    585             }
    586             if (contents.contains(PORT_MODE_UFP)) {
    587                 modes |= UsbPort.MODE_UFP;
    588             }
    589         }
    590         return modes;
    591     }
    592 
    593     private static int readCurrentMode(File portDir) {
    594         final String contents = readFile(portDir, SYSFS_PORT_MODE);
    595         if (contents != null) {
    596             if (contents.equals(PORT_MODE_DFP)) {
    597                 return UsbPort.MODE_DFP;
    598             }
    599             if (contents.equals(PORT_MODE_UFP)) {
    600                 return UsbPort.MODE_UFP;
    601             }
    602         }
    603         return 0;
    604     }
    605 
    606     private static int readCurrentPowerRole(File portDir) {
    607         final String contents = readFile(portDir, SYSFS_PORT_POWER_ROLE);
    608         if (contents != null) {
    609             if (contents.equals(PORT_POWER_ROLE_SOURCE)) {
    610                 return UsbPort.POWER_ROLE_SOURCE;
    611             }
    612             if (contents.equals(PORT_POWER_ROLE_SINK)) {
    613                 return UsbPort.POWER_ROLE_SINK;
    614             }
    615         }
    616         return 0;
    617     }
    618 
    619     private static int readCurrentDataRole(File portDir) {
    620         final String contents = readFile(portDir, SYSFS_PORT_DATA_ROLE);
    621         if (contents != null) {
    622             if (contents.equals(PORT_DATA_ROLE_HOST)) {
    623                 return UsbPort.DATA_ROLE_HOST;
    624             }
    625             if (contents.equals(PORT_DATA_ROLE_DEVICE)) {
    626                 return UsbPort.DATA_ROLE_DEVICE;
    627             }
    628         }
    629         return 0;
    630     }
    631 
    632     private static boolean fileIsRootWritable(String path) {
    633         try {
    634             // If the file is user writable, then it is root writable.
    635             return (Os.stat(path).st_mode & OsConstants.S_IWUSR) != 0;
    636         } catch (ErrnoException e) {
    637             return false;
    638         }
    639     }
    640 
    641     private static boolean canChangeMode(File portDir) {
    642         return fileIsRootWritable(new File(portDir, SYSFS_PORT_MODE).getPath());
    643     }
    644 
    645     private static boolean canChangePowerRole(File portDir) {
    646         return fileIsRootWritable(new File(portDir, SYSFS_PORT_POWER_ROLE).getPath());
    647     }
    648 
    649     private static boolean canChangeDataRole(File portDir) {
    650         return fileIsRootWritable(new File(portDir, SYSFS_PORT_DATA_ROLE).getPath());
    651     }
    652 
    653     private static String readFile(File dir, String filename) {
    654         final File file = new File(dir, filename);
    655         try {
    656             return IoUtils.readFileAsString(file.getAbsolutePath()).trim();
    657         } catch (IOException ex) {
    658             return null;
    659         }
    660     }
    661 
    662     private static boolean waitForState(String property, String state) {
    663         // wait for the transition to complete.
    664         // give up after 5 seconds.
    665         // 5 seconds is probably too long, but we have seen hardware that takes
    666         // over 3 seconds to change states.
    667         String value = null;
    668         for (int i = 0; i < 100; i++) {
    669             // State transition is done when property is set to the new configuration
    670             value = SystemProperties.get(property);
    671             if (state.equals(value)) return true;
    672             SystemClock.sleep(50);
    673         }
    674         Slog.e(TAG, "waitForState(" + state + ") for " + property + " FAILED: got " + value);
    675         return false;
    676     }
    677 
    678     private static String propertyFromFilename(String filename) {
    679         return USB_TYPEC_PROP_PREFIX + filename;
    680     }
    681 
    682     private static boolean writeFile(File dir, String filename, String contents) {
    683         SystemProperties.set(propertyFromFilename(filename), contents);
    684         return waitForState(USB_TYPEC_STATE, contents);
    685     }
    686 
    687     private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
    688         Slog.println(priority, TAG, msg);
    689         if (pw != null) {
    690             pw.println(msg);
    691         }
    692     }
    693 
    694     private final Handler mHandler = new Handler(FgThread.get().getLooper()) {
    695         @Override
    696         public void handleMessage(Message msg) {
    697             switch (msg.what) {
    698                 case MSG_UPDATE_PORTS: {
    699                     synchronized (mLock) {
    700                         updatePortsLocked(null);
    701                     }
    702                     break;
    703                 }
    704             }
    705         }
    706     };
    707 
    708     private final UEventObserver mUEventObserver = new UEventObserver() {
    709         @Override
    710         public void onUEvent(UEvent event) {
    711             scheduleUpdatePorts();
    712         }
    713     };
    714 
    715     /**
    716      * Describes a USB port.
    717      */
    718     private static final class PortInfo {
    719         public static final int DISPOSITION_ADDED = 0;
    720         public static final int DISPOSITION_CHANGED = 1;
    721         public static final int DISPOSITION_READY = 2;
    722         public static final int DISPOSITION_REMOVED = 3;
    723 
    724         public final UsbPort mUsbPort;
    725         public UsbPortStatus mUsbPortStatus;
    726         public boolean mCanChangeMode;
    727         public boolean mCanChangePowerRole;
    728         public boolean mCanChangeDataRole;
    729         public int mDisposition; // default initialized to 0 which means added
    730 
    731         public PortInfo(String portId, int supportedModes) {
    732             mUsbPort = new UsbPort(portId, supportedModes);
    733         }
    734 
    735         public boolean setStatus(int currentMode, boolean canChangeMode,
    736                 int currentPowerRole, boolean canChangePowerRole,
    737                 int currentDataRole, boolean canChangeDataRole,
    738                 int supportedRoleCombinations) {
    739             mCanChangeMode = canChangeMode;
    740             mCanChangePowerRole = canChangePowerRole;
    741             mCanChangeDataRole = canChangeDataRole;
    742             if (mUsbPortStatus == null
    743                     || mUsbPortStatus.getCurrentMode() != currentMode
    744                     || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole
    745                     || mUsbPortStatus.getCurrentDataRole() != currentDataRole
    746                     || mUsbPortStatus.getSupportedRoleCombinations()
    747                             != supportedRoleCombinations) {
    748                 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
    749                         supportedRoleCombinations);
    750                 return true;
    751             }
    752             return false;
    753         }
    754 
    755         @Override
    756         public String toString() {
    757             return "port=" + mUsbPort + ", status=" + mUsbPortStatus
    758                     + ", canChangeMode=" + mCanChangeMode
    759                     + ", canChangePowerRole=" + mCanChangePowerRole
    760                     + ", canChangeDataRole=" + mCanChangeDataRole;
    761         }
    762     }
    763 
    764     /**
    765      * Describes a simulated USB port.
    766      * Roughly mirrors the information we would ordinarily get from the kernel.
    767      */
    768     private static final class SimulatedPortInfo {
    769         public final String mPortId;
    770         public final int mSupportedModes;
    771         public int mCurrentMode;
    772         public boolean mCanChangeMode;
    773         public int mCurrentPowerRole;
    774         public boolean mCanChangePowerRole;
    775         public int mCurrentDataRole;
    776         public boolean mCanChangeDataRole;
    777 
    778         public SimulatedPortInfo(String portId, int supportedModes) {
    779             mPortId = portId;
    780             mSupportedModes = supportedModes;
    781         }
    782     }
    783 }
    784