176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown/* 276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * Copyright (C) 2015 The Android Open Source Project 376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * 476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * you may not use this file except in compliance with the License. 676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * You may obtain a copy of the License at 776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * 876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * http://www.apache.org/licenses/LICENSE-2.0 976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * 1076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * Unless required by applicable law or agreed to in writing, software 1176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 1276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * See the License for the specific language governing permissions and 1476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * limitations under the License. 1576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown */ 1676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 1776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownpackage com.android.server.usb; 1876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 1976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport com.android.internal.util.IndentingPrintWriter; 2076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport com.android.server.FgThread; 2176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 2276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport android.content.Context; 2376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport android.content.Intent; 2476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport android.hardware.usb.UsbManager; 2576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport android.hardware.usb.UsbPort; 2676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport android.hardware.usb.UsbPortStatus; 2776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport android.os.Handler; 2876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport android.os.Message; 296ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbournimport android.os.SystemClock; 306ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbournimport android.os.SystemProperties; 3176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport android.os.UEventObserver; 3276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport android.os.UserHandle; 336ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbournimport android.system.ErrnoException; 346ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbournimport android.system.Os; 356ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbournimport android.system.OsConstants; 3676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport android.util.ArrayMap; 3776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport android.util.Log; 3876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport android.util.Slog; 3976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 4076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport java.io.File; 4176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport java.io.FileWriter; 4276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport java.io.IOException; 4376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 4476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownimport libcore.io.IoUtils; 4576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 4676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown/** 4776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * Allows trusted components to control the properties of physical USB ports 4876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * via the "/sys/class/dual_role_usb" kernel interface. 4976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * <p> 5076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * Note: This interface may not be supported on all chipsets since the USB drivers 5176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * must be changed to publish this information through the module. At the moment 5276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * we only need this for devices with USB Type C ports to allow the System UI to 5376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * control USB charging and data direction. On devices that do not support this 5476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * interface the list of ports may incorrectly appear to be empty 5576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * (but we don't care today). 5676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * </p> 5776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown */ 5876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brownpublic class UsbPortManager { 5976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String TAG = "UsbPortManager"; 6076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 6176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final int MSG_UPDATE_PORTS = 1; 6276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 6376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // UEvent path to watch. 6476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String UEVENT_FILTER = "SUBSYSTEM=dual_role_usb"; 6576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 6676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // SysFS directory that contains USB ports as subdirectories. 6776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String SYSFS_CLASS = "/sys/class/dual_role_usb"; 6876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 6976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // SysFS file that contains a USB port's supported modes. (read-only) 7076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Contents: "", "ufp", "dfp", or "ufp dfp". 7176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String SYSFS_PORT_SUPPORTED_MODES = "supported_modes"; 7276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 7376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // SysFS file that contains a USB port's current mode. (read-write if configurable) 7476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Contents: "", "ufp", or "dfp". 7576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String SYSFS_PORT_MODE = "mode"; 7676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 7776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // SysFS file that contains a USB port's current power role. (read-write if configurable) 7876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Contents: "", "source", or "sink". 7976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String SYSFS_PORT_POWER_ROLE = "power_role"; 8076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 8176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // SysFS file that contains a USB port's current data role. (read-write if configurable) 8276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Contents: "", "host", or "device". 8376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String SYSFS_PORT_DATA_ROLE = "data_role"; 8476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 8576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Port modes: upstream facing port or downstream facing port. 8676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String PORT_MODE_DFP = "dfp"; 8776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String PORT_MODE_UFP = "ufp"; 8876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 8976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Port power roles: source or sink. 9076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String PORT_POWER_ROLE_SOURCE = "source"; 9176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String PORT_POWER_ROLE_SINK = "sink"; 9276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 9376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Port data roles: host or device. 9476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String PORT_DATA_ROLE_HOST = "host"; 9576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final String PORT_DATA_ROLE_DEVICE = "device"; 9676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 976ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn private static final String USB_TYPEC_PROP_PREFIX = "sys.usb.typec."; 986ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn private static final String USB_TYPEC_STATE = "sys.usb.typec.state"; 996ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn 10076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // All non-trivial role combinations. 10176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final int COMBO_SOURCE_HOST = 10276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST); 10376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final int COMBO_SOURCE_DEVICE = 10476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE); 10576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final int COMBO_SINK_HOST = 10676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST); 10776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final int COMBO_SINK_DEVICE = 10876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE); 10976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 11076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // The system context. 11176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private final Context mContext; 11276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 11376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // True if we have kernel support. 11476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private final boolean mHaveKernelSupport; 11576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 11676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Mutex for all mutable shared state. 11776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private final Object mLock = new Object(); 11876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 11976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // List of all ports, indexed by id. 12076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Ports may temporarily have different dispositions as they are added or removed 12176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // but the class invariant is that this list will only contain ports with DISPOSITION_READY 12276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // except while updatePortsLocked() is in progress. 12376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private final ArrayMap<String, PortInfo> mPorts = new ArrayMap<String, PortInfo>(); 12476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 12576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // List of all simulated ports, indexed by id. 12676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private final ArrayMap<String, SimulatedPortInfo> mSimulatedPorts = 12776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown new ArrayMap<String, SimulatedPortInfo>(); 12876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 12976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public UsbPortManager(Context context) { 13076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mContext = context; 13176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mHaveKernelSupport = new File(SYSFS_CLASS).exists(); 13276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 13376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 13476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public void systemReady() { 13576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mUEventObserver.startObserving(UEVENT_FILTER); 13676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown scheduleUpdatePorts(); 13776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 13876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 13976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public UsbPort[] getPorts() { 14076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown synchronized (mLock) { 14176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final int count = mPorts.size(); 14276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final UsbPort[] result = new UsbPort[count]; 14376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown for (int i = 0; i < count; i++) { 14476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown result[i] = mPorts.valueAt(i).mUsbPort; 14576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 14676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return result; 14776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 14876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 14976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 15076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public UsbPortStatus getPortStatus(String portId) { 15176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown synchronized (mLock) { 15276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final PortInfo portInfo = mPorts.get(portId); 15376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return portInfo != null ? portInfo.mUsbPortStatus : null; 15476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 15576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 15676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 15776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public void setPortRoles(String portId, int newPowerRole, int newDataRole, 15876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown IndentingPrintWriter pw) { 15976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown synchronized (mLock) { 16076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final PortInfo portInfo = mPorts.get(portId); 16176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (portInfo == null) { 16276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (pw != null) { 16376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("No such USB port: " + portId); 16476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 16576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 16676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 16776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 16876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Check whether the new role is actually supported. 16976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (!portInfo.mUsbPortStatus.isRoleCombinationSupported(newPowerRole, newDataRole)) { 17076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown logAndPrint(Log.ERROR, pw, "Attempted to set USB port into unsupported " 17176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + "role combination: portId=" + portId 17276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 17376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 17476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 17576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 17676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 17776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Check whether anything actually changed. 17876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final int currentDataRole = portInfo.mUsbPortStatus.getCurrentDataRole(); 17976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final int currentPowerRole = portInfo.mUsbPortStatus.getCurrentPowerRole(); 18076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (currentDataRole == newDataRole && currentPowerRole == newPowerRole) { 18176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (pw != null) { 18276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("No change."); 18376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 18476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 18576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 18676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 18776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Determine whether we need to change the mode in order to accomplish this goal. 18876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // We prefer not to do this since it's more likely to fail. 18976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // 19076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Note: Arguably it might be worth allowing the client to influence this policy 19176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // decision so that we could show more powerful developer facing UI but let's 19276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // see how far we can get without having to do that. 19376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final boolean canChangeMode = portInfo.mCanChangeMode; 19476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final boolean canChangePowerRole = portInfo.mCanChangePowerRole; 19576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final boolean canChangeDataRole = portInfo.mCanChangeDataRole; 19676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final int currentMode = portInfo.mUsbPortStatus.getCurrentMode(); 19776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final int newMode; 19876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if ((!canChangePowerRole && currentPowerRole != newPowerRole) 19976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown || (!canChangeDataRole && currentDataRole != newDataRole)) { 20076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SOURCE 20176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown && newDataRole == UsbPort.DATA_ROLE_HOST) { 20276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown newMode = UsbPort.MODE_DFP; 20376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } else if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SINK 20476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown && newDataRole == UsbPort.DATA_ROLE_DEVICE) { 20576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown newMode = UsbPort.MODE_UFP; 20676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } else { 20776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations " 20876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + "while attempting to change role: " + portInfo 20976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 21076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 21176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 21276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 21376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } else { 21476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown newMode = currentMode; 21576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 21676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 21776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Make it happen. 21876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown logAndPrint(Log.INFO, pw, "Setting USB port mode and role: portId=" + portId 21976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", currentMode=" + UsbPort.modeToString(currentMode) 22076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", currentPowerRole=" + UsbPort.powerRoleToString(currentPowerRole) 22176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", currentDataRole=" + UsbPort.dataRoleToString(currentDataRole) 22276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", newMode=" + UsbPort.modeToString(newMode) 22376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 22476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 22576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 22676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown SimulatedPortInfo sim = mSimulatedPorts.get(portId); 22776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (sim != null) { 22876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Change simulated state. 22976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown sim.mCurrentMode = newMode; 23076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown sim.mCurrentPowerRole = newPowerRole; 23176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown sim.mCurrentDataRole = newDataRole; 23276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } else if (mHaveKernelSupport) { 23376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Change actual state. 23476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final File portDir = new File(SYSFS_CLASS, portId); 23576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (!portDir.exists()) { 23676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown logAndPrint(Log.ERROR, pw, "USB port not found: portId=" + portId); 23776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 23876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 23976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 24076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (currentMode != newMode) { 24176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Changing the mode will have the side-effect of also changing 24276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // the power and data roles but it might take some time to apply 24376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // and the renegotiation might fail. Due to limitations of the USB 24476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // hardware, we have no way of knowing whether it will work apriori 24576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // which is why we would prefer to set the power and data roles 24676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // directly instead. 24776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (!writeFile(portDir, SYSFS_PORT_MODE, 24876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown newMode == UsbPort.MODE_DFP ? PORT_MODE_DFP : PORT_MODE_UFP)) { 24976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown logAndPrint(Log.ERROR, pw, "Failed to set the USB port mode: " 25076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + "portId=" + portId 25176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", newMode=" + UsbPort.modeToString(newMode)); 25276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 25376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 25476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } else { 25576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Change power and data role independently as needed. 25676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (currentPowerRole != newPowerRole) { 25776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (!writeFile(portDir, SYSFS_PORT_POWER_ROLE, 25876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown newPowerRole == UsbPort.POWER_ROLE_SOURCE 25976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown ? PORT_POWER_ROLE_SOURCE : PORT_POWER_ROLE_SINK)) { 26076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown logAndPrint(Log.ERROR, pw, "Failed to set the USB port power role: " 26176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + "portId=" + portId 26276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)); 26376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 26476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 26576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 26676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (currentDataRole != newDataRole) { 26776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (!writeFile(portDir, SYSFS_PORT_DATA_ROLE, 26876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown newDataRole == UsbPort.DATA_ROLE_HOST 26976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown ? PORT_DATA_ROLE_HOST : PORT_DATA_ROLE_DEVICE)) { 27076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown logAndPrint(Log.ERROR, pw, "Failed to set the USB port data role: " 27176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + "portId=" + portId 27276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 27376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 27476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 27576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 27676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 27776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 27876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown updatePortsLocked(pw); 27976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 28076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 28176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 28276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public void addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw) { 28376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown synchronized (mLock) { 28476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (mSimulatedPorts.containsKey(portId)) { 28576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("Port with same name already exists. Please remove it first."); 28676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 28776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 28876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 28976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("Adding simulated port: portId=" + portId 29076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", supportedModes=" + UsbPort.modeToString(supportedModes)); 29176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mSimulatedPorts.put(portId, 29276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown new SimulatedPortInfo(portId, supportedModes)); 29376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown updatePortsLocked(pw); 29476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 29576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 29676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 29776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public void connectSimulatedPort(String portId, int mode, boolean canChangeMode, 29876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown int powerRole, boolean canChangePowerRole, 29976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown int dataRole, boolean canChangeDataRole, IndentingPrintWriter pw) { 30076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown synchronized (mLock) { 30176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId); 30276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (portInfo == null) { 30376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("Cannot connect simulated port which does not exist."); 30476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 30576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 30676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 30776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (mode == 0 || powerRole == 0 || dataRole == 0) { 30876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("Cannot connect simulated port in null mode, " 30976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + "power role, or data role."); 31076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 31176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 31276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 31376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if ((portInfo.mSupportedModes & mode) == 0) { 31476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("Simulated port does not support mode: " + UsbPort.modeToString(mode)); 31576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 31676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 31776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 31876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("Connecting simulated port: portId=" + portId 31976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", mode=" + UsbPort.modeToString(mode) 32076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", canChangeMode=" + canChangeMode 32176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", powerRole=" + UsbPort.powerRoleToString(powerRole) 32276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", canChangePowerRole=" + canChangePowerRole 32376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", dataRole=" + UsbPort.dataRoleToString(dataRole) 32476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", canChangeDataRole=" + canChangeDataRole); 32576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCurrentMode = mode; 32676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCanChangeMode = canChangeMode; 32776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCurrentPowerRole = powerRole; 32876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCanChangePowerRole = canChangePowerRole; 32976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCurrentDataRole = dataRole; 33076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCanChangeDataRole = canChangeDataRole; 33176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown updatePortsLocked(pw); 33276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 33376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 33476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 33576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public void disconnectSimulatedPort(String portId, IndentingPrintWriter pw) { 33676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown synchronized (mLock) { 33776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId); 33876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (portInfo == null) { 33976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("Cannot disconnect simulated port which does not exist."); 34076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 34176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 34276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 34376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("Disconnecting simulated port: portId=" + portId); 34476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCurrentMode = 0; 34576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCanChangeMode = false; 34676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCurrentPowerRole = 0; 34776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCanChangePowerRole = false; 34876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCurrentDataRole = 0; 34976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCanChangeDataRole = false; 35076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown updatePortsLocked(pw); 35176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 35276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 35376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 35476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public void removeSimulatedPort(String portId, IndentingPrintWriter pw) { 35576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown synchronized (mLock) { 35676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final int index = mSimulatedPorts.indexOfKey(portId); 35776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (index < 0) { 35876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("Cannot remove simulated port which does not exist."); 35976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return; 36076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 36176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 36276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("Disconnecting simulated port: portId=" + portId); 36376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mSimulatedPorts.removeAt(index); 36476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown updatePortsLocked(pw); 36576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 36676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 36776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 36876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public void resetSimulation(IndentingPrintWriter pw) { 36976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown synchronized (mLock) { 37076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println("Removing all simulated ports and ending simulation."); 37176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (!mSimulatedPorts.isEmpty()) { 37276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mSimulatedPorts.clear(); 37376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown updatePortsLocked(pw); 37476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 37576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 37676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 37776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 37876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public void dump(IndentingPrintWriter pw) { 37976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown synchronized (mLock) { 38076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.print("USB Port State:"); 38176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (!mSimulatedPorts.isEmpty()) { 38276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.print(" (simulation active; end with 'dumpsys usb reset')"); 38376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 38476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println(); 38576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 38676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (mPorts.isEmpty()) { 38776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println(" <no ports>"); 38876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } else { 38976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown for (PortInfo portInfo : mPorts.values()) { 39076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println(" " + portInfo.mUsbPort.getId() + ": " + portInfo); 39176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 39276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 39376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 39476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 39576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 39676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private void updatePortsLocked(IndentingPrintWriter pw) { 39776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Assume all ports are gone unless informed otherwise. 39876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Kind of pessimistic but simple. 39976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown for (int i = mPorts.size(); i-- > 0; ) { 40076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mPorts.valueAt(i).mDisposition = PortInfo.DISPOSITION_REMOVED; 40176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 40276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 40376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Enumerate all extant ports. 40476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (!mSimulatedPorts.isEmpty()) { 40576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final int count = mSimulatedPorts.size(); 40676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown for (int i = 0; i < count; i++) { 40776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final SimulatedPortInfo portInfo = mSimulatedPorts.valueAt(i); 40876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown addOrUpdatePortLocked(portInfo.mPortId, portInfo.mSupportedModes, 40976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCurrentMode, portInfo.mCanChangeMode, 41076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCurrentPowerRole, portInfo.mCanChangePowerRole, 41176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mCurrentDataRole, portInfo.mCanChangeDataRole, pw); 41276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 41376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } else if (mHaveKernelSupport) { 41476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final File[] portDirs = new File(SYSFS_CLASS).listFiles(); 41576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (portDirs != null) { 41676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown for (File portDir : portDirs) { 41776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (!portDir.isDirectory()) { 41876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown continue; 41976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 42076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 42176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Parse the sysfs file contents. 42276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final String portId = portDir.getName(); 42376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final int supportedModes = readSupportedModes(portDir); 42476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final int currentMode = readCurrentMode(portDir); 42576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final boolean canChangeMode = canChangeMode(portDir); 42676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final int currentPowerRole = readCurrentPowerRole(portDir); 42776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final boolean canChangePowerRole = canChangePowerRole(portDir); 42876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final int currentDataRole = readCurrentDataRole(portDir); 42976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final boolean canChangeDataRole = canChangeDataRole(portDir); 43076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown addOrUpdatePortLocked(portId, supportedModes, 43176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown currentMode, canChangeMode, 43276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown currentPowerRole, canChangePowerRole, 43376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown currentDataRole, canChangeDataRole, pw); 43476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 43576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 43676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 43776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 43876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Process the updates. 43976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Once finished, the list of ports will only contain ports in DISPOSITION_READY. 44076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown for (int i = mPorts.size(); i-- > 0; ) { 44176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final PortInfo portInfo = mPorts.valueAt(i); 44276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown switch (portInfo.mDisposition) { 44376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown case PortInfo.DISPOSITION_ADDED: 44476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown handlePortAddedLocked(portInfo, pw); 44576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mDisposition = PortInfo.DISPOSITION_READY; 44676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown break; 44776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown case PortInfo.DISPOSITION_CHANGED: 44876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown handlePortChangedLocked(portInfo, pw); 44976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mDisposition = PortInfo.DISPOSITION_READY; 45076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown break; 45176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown case PortInfo.DISPOSITION_REMOVED: 45276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mPorts.removeAt(i); 45376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mUsbPortStatus = null; // must do this early 45476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown handlePortRemovedLocked(portInfo, pw); 45576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown break; 45676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 45776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 45876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 45976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 46076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Must only be called by updatePortsLocked. 46176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private void addOrUpdatePortLocked(String portId, int supportedModes, 46276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown int currentMode, boolean canChangeMode, 46376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown int currentPowerRole, boolean canChangePowerRole, 46476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown int currentDataRole, boolean canChangeDataRole, 46576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown IndentingPrintWriter pw) { 46676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Only allow mode switch capability for dual role ports. 46776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Validate that the current mode matches the supported modes we expect. 46876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (supportedModes != UsbPort.MODE_DUAL) { 46976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown canChangeMode = false; 47076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (currentMode != 0 && currentMode != supportedModes) { 47176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB " 47276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + "port driver: supportedModes=" + UsbPort.modeToString(supportedModes) 47376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", currentMode=" + UsbPort.modeToString(currentMode)); 47476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown currentMode = 0; 47576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 47676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 47776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 47876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Determine the supported role combinations. 47976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Note that the policy is designed to prefer setting the power and data 48076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // role independently rather than changing the mode. 48176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown int supportedRoleCombinations = UsbPort.combineRolesAsBit( 48276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown currentPowerRole, currentDataRole); 48376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (currentMode != 0 && currentPowerRole != 0 && currentDataRole != 0) { 48476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (canChangePowerRole && canChangeDataRole) { 48576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Can change both power and data role independently. 48676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Assume all combinations are possible. 48776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown supportedRoleCombinations |= 48876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown COMBO_SOURCE_HOST | COMBO_SOURCE_DEVICE 48976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown | COMBO_SINK_HOST | COMBO_SINK_DEVICE; 49076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } else if (canChangePowerRole) { 49176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Can only change power role. 49276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Assume data role must remain at its current value. 49376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown supportedRoleCombinations |= UsbPort.combineRolesAsBit( 49476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown UsbPort.POWER_ROLE_SOURCE, currentDataRole); 49576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown supportedRoleCombinations |= UsbPort.combineRolesAsBit( 49676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown UsbPort.POWER_ROLE_SINK, currentDataRole); 49776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } else if (canChangeDataRole) { 49876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Can only change data role. 49976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Assume power role must remain at its current value. 50076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown supportedRoleCombinations |= UsbPort.combineRolesAsBit( 50176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown currentPowerRole, UsbPort.DATA_ROLE_HOST); 50276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown supportedRoleCombinations |= UsbPort.combineRolesAsBit( 50376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown currentPowerRole, UsbPort.DATA_ROLE_DEVICE); 50476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } else if (canChangeMode) { 50576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Can only change the mode. 50676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Assume both standard UFP and DFP configurations will become available 50776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // when this happens. 50876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown supportedRoleCombinations |= COMBO_SOURCE_HOST | COMBO_SINK_DEVICE; 50976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 51076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 51176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 51276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Update the port data structures. 51376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown PortInfo portInfo = mPorts.get(portId); 51476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (portInfo == null) { 51576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo = new PortInfo(portId, supportedModes); 51676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.setStatus(currentMode, canChangeMode, 51776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown currentPowerRole, canChangePowerRole, 51876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown currentDataRole, canChangeDataRole, 51976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown supportedRoleCombinations); 52076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mPorts.put(portId, portInfo); 52176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } else { 52276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Sanity check that ports aren't changing definition out from under us. 52376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (supportedModes != portInfo.mUsbPort.getSupportedModes()) { 52476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown logAndPrint(Log.WARN, pw, "Ignoring inconsistent list of supported modes from " 52576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + "USB port driver (should be immutable): " 52676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + "previous=" + UsbPort.modeToString( 52776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mUsbPort.getSupportedModes()) 52876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", current=" + UsbPort.modeToString(supportedModes)); 52976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 53076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 53176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (portInfo.setStatus(currentMode, canChangeMode, 53276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown currentPowerRole, canChangePowerRole, 53376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown currentDataRole, canChangeDataRole, 53476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown supportedRoleCombinations)) { 53576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED; 53676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } else { 53776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown portInfo.mDisposition = PortInfo.DISPOSITION_READY; 53876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 53976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 54076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 54176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 54276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private void handlePortAddedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 54376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown logAndPrint(Log.INFO, pw, "USB port added: " + portInfo); 54476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown sendPortChangedBroadcastLocked(portInfo); 54576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 54676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 54776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 54876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo); 54976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown sendPortChangedBroadcastLocked(portInfo); 55076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 55176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 55276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private void handlePortRemovedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 55376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown logAndPrint(Log.INFO, pw, "USB port removed: " + portInfo); 55476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown sendPortChangedBroadcastLocked(portInfo); 55576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 55676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 55776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private void sendPortChangedBroadcastLocked(PortInfo portInfo) { 55876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final Intent intent = new Intent(UsbManager.ACTION_USB_PORT_CHANGED); 55976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 56076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort); 56176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus); 56276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 56376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // Guard against possible reentrance by posting the broadcast from the handler 56476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown // instead of from within the critical section. 56576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mHandler.post(new Runnable() { 56676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown @Override 56776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public void run() { 56876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 56976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 57076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown }); 57176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 57276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 57376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private void scheduleUpdatePorts() { 57476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (!mHandler.hasMessages(MSG_UPDATE_PORTS)) { 57576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mHandler.sendEmptyMessage(MSG_UPDATE_PORTS); 57676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 57776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 57876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 57976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static int readSupportedModes(File portDir) { 58076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown int modes = 0; 58176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final String contents = readFile(portDir, SYSFS_PORT_SUPPORTED_MODES); 58276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (contents != null) { 58376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (contents.contains(PORT_MODE_DFP)) { 58476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown modes |= UsbPort.MODE_DFP; 58576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 58676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (contents.contains(PORT_MODE_UFP)) { 58776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown modes |= UsbPort.MODE_UFP; 58876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 58976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 59076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return modes; 59176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 59276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 59376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static int readCurrentMode(File portDir) { 59476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final String contents = readFile(portDir, SYSFS_PORT_MODE); 59576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (contents != null) { 59676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (contents.equals(PORT_MODE_DFP)) { 59776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return UsbPort.MODE_DFP; 59876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 59976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (contents.equals(PORT_MODE_UFP)) { 60076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return UsbPort.MODE_UFP; 60176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 60276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 60376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return 0; 60476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 60576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 60676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static int readCurrentPowerRole(File portDir) { 60776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final String contents = readFile(portDir, SYSFS_PORT_POWER_ROLE); 60876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (contents != null) { 60976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (contents.equals(PORT_POWER_ROLE_SOURCE)) { 61076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return UsbPort.POWER_ROLE_SOURCE; 61176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 61276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (contents.equals(PORT_POWER_ROLE_SINK)) { 61376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return UsbPort.POWER_ROLE_SINK; 61476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 61576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 61676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return 0; 61776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 61876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 61976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static int readCurrentDataRole(File portDir) { 62076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final String contents = readFile(portDir, SYSFS_PORT_DATA_ROLE); 62176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (contents != null) { 62276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (contents.equals(PORT_DATA_ROLE_HOST)) { 62376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return UsbPort.DATA_ROLE_HOST; 62476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 62576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (contents.equals(PORT_DATA_ROLE_DEVICE)) { 62676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return UsbPort.DATA_ROLE_DEVICE; 62776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 62876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 62976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return 0; 63076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 63176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 6326ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn private static boolean fileIsRootWritable(String path) { 6336ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn try { 6346ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn // If the file is user writable, then it is root writable. 6356ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn return (Os.stat(path).st_mode & OsConstants.S_IWUSR) != 0; 6366ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn } catch (ErrnoException e) { 6376ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn return false; 6386ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn } 6396ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn } 6406ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn 64176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static boolean canChangeMode(File portDir) { 6426ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn return fileIsRootWritable(new File(portDir, SYSFS_PORT_MODE).getPath()); 64376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 64476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 64576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static boolean canChangePowerRole(File portDir) { 6466ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn return fileIsRootWritable(new File(portDir, SYSFS_PORT_POWER_ROLE).getPath()); 64776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 64876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 64976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static boolean canChangeDataRole(File portDir) { 6506ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn return fileIsRootWritable(new File(portDir, SYSFS_PORT_DATA_ROLE).getPath()); 65176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 65276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 65376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static String readFile(File dir, String filename) { 65476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown final File file = new File(dir, filename); 65576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown try { 65676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return IoUtils.readFileAsString(file.getAbsolutePath()).trim(); 65776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } catch (IOException ex) { 65876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return null; 65976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 66076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 66176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 6626ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn private static boolean waitForState(String property, String state) { 6636ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn // wait for the transition to complete. 6646ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn // give up after 5 seconds. 6656ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn // 5 seconds is probably too long, but we have seen hardware that takes 6666ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn // over 3 seconds to change states. 6676ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn String value = null; 6686ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn for (int i = 0; i < 100; i++) { 6696ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn // State transition is done when property is set to the new configuration 6706ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn value = SystemProperties.get(property); 6716ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn if (state.equals(value)) return true; 6726ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn SystemClock.sleep(50); 67376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 6746ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn Slog.e(TAG, "waitForState(" + state + ") for " + property + " FAILED: got " + value); 6756ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn return false; 6766ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn } 6776ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn 6786ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn private static String propertyFromFilename(String filename) { 6796ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn return USB_TYPEC_PROP_PREFIX + filename; 6806ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn } 6816ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn 6826ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn private static boolean writeFile(File dir, String filename, String contents) { 6836ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn SystemProperties.set(propertyFromFilename(filename), contents); 6846ea47a3542ad5fd79530cfd5d1e3b30c3d52f722Tim Kilbourn return waitForState(USB_TYPEC_STATE, contents); 68576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 68676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 68776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) { 68876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown Slog.println(priority, TAG, msg); 68976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (pw != null) { 69076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown pw.println(msg); 69176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 69276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 69376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 69476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private final Handler mHandler = new Handler(FgThread.get().getLooper()) { 69576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown @Override 69676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public void handleMessage(Message msg) { 69776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown switch (msg.what) { 69876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown case MSG_UPDATE_PORTS: { 69976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown synchronized (mLock) { 70076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown updatePortsLocked(null); 70176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 70276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown break; 70376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 70476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 70576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 70676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown }; 70776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 70876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private final UEventObserver mUEventObserver = new UEventObserver() { 70976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown @Override 71076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public void onUEvent(UEvent event) { 71176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown scheduleUpdatePorts(); 71276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 71376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown }; 71476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 71576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown /** 71676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * Describes a USB port. 71776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown */ 71876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final class PortInfo { 71976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public static final int DISPOSITION_ADDED = 0; 72076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public static final int DISPOSITION_CHANGED = 1; 72176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public static final int DISPOSITION_READY = 2; 72276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public static final int DISPOSITION_REMOVED = 3; 72376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 72476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public final UsbPort mUsbPort; 72576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public UsbPortStatus mUsbPortStatus; 72676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public boolean mCanChangeMode; 72776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public boolean mCanChangePowerRole; 72876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public boolean mCanChangeDataRole; 72976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public int mDisposition; // default initialized to 0 which means added 73076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 73176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public PortInfo(String portId, int supportedModes) { 73276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mUsbPort = new UsbPort(portId, supportedModes); 73376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 73476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 73576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public boolean setStatus(int currentMode, boolean canChangeMode, 73676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown int currentPowerRole, boolean canChangePowerRole, 73776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown int currentDataRole, boolean canChangeDataRole, 73876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown int supportedRoleCombinations) { 73976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mCanChangeMode = canChangeMode; 74076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mCanChangePowerRole = canChangePowerRole; 74176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mCanChangeDataRole = canChangeDataRole; 74276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown if (mUsbPortStatus == null 74376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown || mUsbPortStatus.getCurrentMode() != currentMode 74476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole 74576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown || mUsbPortStatus.getCurrentDataRole() != currentDataRole 74676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown || mUsbPortStatus.getSupportedRoleCombinations() 74776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown != supportedRoleCombinations) { 74876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, 74976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown supportedRoleCombinations); 75076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return true; 75176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 75276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return false; 75376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 75476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 75576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown @Override 75676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public String toString() { 75776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown return "port=" + mUsbPort + ", status=" + mUsbPortStatus 75876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", canChangeMode=" + mCanChangeMode 75976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", canChangePowerRole=" + mCanChangePowerRole 76076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown + ", canChangeDataRole=" + mCanChangeDataRole; 76176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 76276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 76376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 76476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown /** 76576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * Describes a simulated USB port. 76676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown * Roughly mirrors the information we would ordinarily get from the kernel. 76776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown */ 76876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown private static final class SimulatedPortInfo { 76976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public final String mPortId; 77076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public final int mSupportedModes; 77176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public int mCurrentMode; 77276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public boolean mCanChangeMode; 77376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public int mCurrentPowerRole; 77476c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public boolean mCanChangePowerRole; 77576c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public int mCurrentDataRole; 77676c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public boolean mCanChangeDataRole; 77776c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown 77876c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown public SimulatedPortInfo(String portId, int supportedModes) { 77976c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mPortId = portId; 78076c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown mSupportedModes = supportedModes; 78176c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 78276c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown } 78376c4c6668a1486bc003ab0c585bb1f41d16e27a7Jeff Brown} 784