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