diff --git a/Android.mk b/Android.mk index 21bd76b76a62f..7be48e0c51742 100644 --- a/Android.mk +++ b/Android.mk @@ -535,6 +535,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ framework-protos \ android.hardware.thermal@1.0-java-constants \ android.hardware.health@1.0-java-constants \ + android.hardware.usb@1.0-java-constants \ LOCAL_PROTOC_OPTIMIZE_TYPE := stream LOCAL_PROTOC_FLAGS := \ diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java index c9a4e9b6015ab..fea730e492c88 100644 --- a/core/java/android/hardware/usb/UsbPort.java +++ b/core/java/android/hardware/usb/UsbPort.java @@ -16,11 +16,12 @@ package android.hardware.usb; -import com.android.internal.util.Preconditions; - +import android.hardware.usb.V1_0.Constants; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.util.Preconditions; + /** * Represents a physical USB port and describes its characteristics. *

@@ -33,6 +34,7 @@ public final class UsbPort implements Parcelable { private final String mId; private final int mSupportedModes; + public static final int MODE_NONE = Constants.PortMode.NONE; /** * Mode bit: This USB port can act as a downstream facing port (host). *

@@ -40,7 +42,7 @@ public final class UsbPort implements Parcelable { * combination of roles (and possibly others as well). *

*/ - public static final int MODE_DFP = 1 << 0; + public static final int MODE_DFP = Constants.PortMode.DFP; /** * Mode bit: This USB port can act as an upstream facing port (device). @@ -49,7 +51,7 @@ public final class UsbPort implements Parcelable { * combination of roles (and possibly others as well). *

*/ - public static final int MODE_UFP = 1 << 1; + public static final int MODE_UFP = Constants.PortMode.UFP; /** * Mode bit: This USB port can act either as an downstream facing port (host) or as @@ -60,29 +62,43 @@ public final class UsbPort implements Parcelable { * combination of roles (and possibly others as well). *

*/ - public static final int MODE_DUAL = MODE_DFP | MODE_UFP; + public static final int MODE_DUAL = Constants.PortMode.DRP; + + /** + * Power role: This USB port does not have a power role. + */ + public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE; /** * Power role: This USB port can act as a source (provide power). */ - public static final int POWER_ROLE_SOURCE = 1; + public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE; /** * Power role: This USB port can act as a sink (receive power). */ - public static final int POWER_ROLE_SINK = 2; + public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK; + + /** + * Power role: This USB port does not have a data role. + */ + public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE; /** * Data role: This USB port can act as a host (access data services). */ - public static final int DATA_ROLE_HOST = 1; + public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST; /** * Data role: This USB port can act as a device (offer data services). */ - public static final int DATA_ROLE_DEVICE = 2; + public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE; - private static final int NUM_DATA_ROLES = 3; + private static final int NUM_DATA_ROLES = Constants.PortDataRole.NUM_DATA_ROLES; + /** + * Points to the first power role in the IUsb HAL. + */ + private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE; /** @hide */ public UsbPort(String id, int supportedModes) { @@ -126,14 +142,14 @@ public final class UsbPort implements Parcelable { */ public static int combineRolesAsBit(int powerRole, int dataRole) { checkRoles(powerRole, dataRole); - final int index = powerRole * NUM_DATA_ROLES + dataRole; + final int index = ((powerRole - POWER_ROLE_OFFSET) * NUM_DATA_ROLES) + dataRole; return 1 << index; } /** @hide */ public static String modeToString(int mode) { switch (mode) { - case 0: + case MODE_NONE: return "none"; case MODE_DFP: return "dfp"; @@ -149,7 +165,7 @@ public final class UsbPort implements Parcelable { /** @hide */ public static String powerRoleToString(int role) { switch (role) { - case 0: + case POWER_ROLE_NONE: return "no-power"; case POWER_ROLE_SOURCE: return "source"; @@ -163,7 +179,7 @@ public final class UsbPort implements Parcelable { /** @hide */ public static String dataRoleToString(int role) { switch (role) { - case 0: + case DATA_ROLE_NONE: return "no-data"; case DATA_ROLE_HOST: return "host"; @@ -183,7 +199,7 @@ public final class UsbPort implements Parcelable { while (combo != 0) { final int index = Integer.numberOfTrailingZeros(combo); combo &= ~(1 << index); - final int powerRole = index / NUM_DATA_ROLES; + final int powerRole = (index / NUM_DATA_ROLES + POWER_ROLE_OFFSET); final int dataRole = index % NUM_DATA_ROLES; if (first) { first = false; @@ -199,10 +215,29 @@ public final class UsbPort implements Parcelable { return result.toString(); } + /** @hide */ + public static void checkMode(int powerRole) { + Preconditions.checkArgumentInRange(powerRole, Constants.PortMode.NONE, + Constants.PortMode.NUM_MODES - 1, "portMode"); + } + + /** @hide */ + public static void checkPowerRole(int dataRole) { + Preconditions.checkArgumentInRange(dataRole, Constants.PortPowerRole.NONE, + Constants.PortPowerRole.NUM_POWER_ROLES - 1, "powerRole"); + } + + /** @hide */ + public static void checkDataRole(int mode) { + Preconditions.checkArgumentInRange(mode, Constants.PortDataRole.NONE, + Constants.PortDataRole.NUM_DATA_ROLES - 1, "powerRole"); + } + /** @hide */ public static void checkRoles(int powerRole, int dataRole) { - Preconditions.checkArgumentInRange(powerRole, 0, POWER_ROLE_SINK, "powerRole"); - Preconditions.checkArgumentInRange(dataRole, 0, DATA_ROLE_DEVICE, "dataRole"); + Preconditions.checkArgumentInRange(powerRole, POWER_ROLE_NONE, POWER_ROLE_SINK, + "powerRole"); + Preconditions.checkArgumentInRange(dataRole, DATA_ROLE_NONE, DATA_ROLE_DEVICE, "dataRole"); } @Override diff --git a/services/usb/Android.mk b/services/usb/Android.mk index feabf0a970c24..f560e71ebe6a7 100644 --- a/services/usb/Android.mk +++ b/services/usb/Android.mk @@ -8,5 +8,7 @@ LOCAL_SRC_FILES += \ $(call all-java-files-under,java) LOCAL_JAVA_LIBRARIES := services.core +LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.usb@1.0-java-static \ +android.hidl.manager@1.0-java-static include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java index 4aff3d54834a9..4b8e4c8ab13a8 100644 --- a/services/usb/java/com/android/server/usb/UsbPortManager.java +++ b/services/usb/java/com/android/server/usb/UsbPortManager.java @@ -16,36 +16,40 @@ package com.android.server.usb; -import com.android.internal.util.IndentingPrintWriter; -import com.android.server.FgThread; - import android.content.Context; import android.content.Intent; import android.hardware.usb.UsbManager; import android.hardware.usb.UsbPort; import android.hardware.usb.UsbPortStatus; +import android.hardware.usb.V1_0.IUsb; +import android.hardware.usb.V1_0.IUsbCallback; +import android.hardware.usb.V1_0.PortRole; +import android.hardware.usb.V1_0.PortRoleType; +import android.hardware.usb.V1_0.PortStatus; +import android.hardware.usb.V1_0.Status; +import android.hidl.manager.V1_0.IServiceManager; +import android.hidl.manager.V1_0.IServiceNotification; +import android.os.Bundle; import android.os.Handler; +import android.os.HwBinder; import android.os.Message; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.UEventObserver; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; import android.os.UserHandle; -import android.system.ErrnoException; -import android.system.Os; -import android.system.OsConstants; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; +import com.android.internal.util.IndentingPrintWriter; +import com.android.server.FgThread; -import libcore.io.IoUtils; +import java.util.ArrayList; +import java.util.NoSuchElementException; /** * Allows trusted components to control the properties of physical USB ports - * via the "/sys/class/dual_role_usb" kernel interface. + * via the IUsb.hal. *

* Note: This interface may not be supported on all chipsets since the USB drivers * must be changed to publish this information through the module. At the moment @@ -60,43 +64,6 @@ public class UsbPortManager { private static final int MSG_UPDATE_PORTS = 1; - // UEvent path to watch. - private static final String UEVENT_FILTER = "SUBSYSTEM=dual_role_usb"; - - // SysFS directory that contains USB ports as subdirectories. - private static final String SYSFS_CLASS = "/sys/class/dual_role_usb"; - - // SysFS file that contains a USB port's supported modes. (read-only) - // Contents: "", "ufp", "dfp", or "ufp dfp". - private static final String SYSFS_PORT_SUPPORTED_MODES = "supported_modes"; - - // SysFS file that contains a USB port's current mode. (read-write if configurable) - // Contents: "", "ufp", or "dfp". - private static final String SYSFS_PORT_MODE = "mode"; - - // SysFS file that contains a USB port's current power role. (read-write if configurable) - // Contents: "", "source", or "sink". - private static final String SYSFS_PORT_POWER_ROLE = "power_role"; - - // SysFS file that contains a USB port's current data role. (read-write if configurable) - // Contents: "", "host", or "device". - private static final String SYSFS_PORT_DATA_ROLE = "data_role"; - - // Port modes: upstream facing port or downstream facing port. - private static final String PORT_MODE_DFP = "dfp"; - private static final String PORT_MODE_UFP = "ufp"; - - // Port power roles: source or sink. - private static final String PORT_POWER_ROLE_SOURCE = "source"; - private static final String PORT_POWER_ROLE_SINK = "sink"; - - // Port data roles: host or device. - private static final String PORT_DATA_ROLE_HOST = "host"; - private static final String PORT_DATA_ROLE_DEVICE = "device"; - - private static final String USB_TYPEC_PROP_PREFIX = "sys.usb.typec."; - private static final String USB_TYPEC_STATE = "sys.usb.typec.state"; - // All non-trivial role combinations. private static final int COMBO_SOURCE_HOST = UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST); @@ -110,8 +77,29 @@ public class UsbPortManager { // The system context. private final Context mContext; - // True if we have kernel support. - private final boolean mHaveKernelSupport; + // Proxy object for the usb hal daemon. + // @GuardedBy("mLock") + private IUsb mProxy = null; + + // Callback when the UsbPort status is changed by the kernel. + // Mostly due a command sent by the remote Usb device. + private HALCallback mHALCallback = new HALCallback(null, this); + + // Notification object used to listen to the start of the usb daemon. + private final ServiceNotification mServiceNotification = new ServiceNotification(); + + // Cookie sent for usb hal death notification. + private static final int USB_HAL_DEATH_COOKIE = 1000; + + // Usb hal service name. + private static String sSERVICENAME = "usb_hal"; + + // Used as the key while sending the bundle to Main thread. + private static final String PORT_INFO = "port_info"; + + // This is monitored to prevent updating the protInfo before the system + // is ready. + private boolean mSystemReady; // Mutex for all mutable shared state. private final Object mLock = new Object(); @@ -123,17 +111,37 @@ public class UsbPortManager { private final ArrayMap mPorts = new ArrayMap(); // List of all simulated ports, indexed by id. - private final ArrayMap mSimulatedPorts = - new ArrayMap(); + private final ArrayMap mSimulatedPorts = + new ArrayMap(); public UsbPortManager(Context context) { mContext = context; - mHaveKernelSupport = new File(SYSFS_CLASS).exists(); + try { + boolean ret = IServiceManager.getService("manager") + .registerForNotifications("android.hardware.usb@1.0::IUsb", + "", mServiceNotification); + if (!ret) { + logAndPrint(Log.ERROR, null, "Failed to register service start notification"); + } + } catch (RemoteException e) { + logAndPrint(Log.ERROR, null, "Failed to register service start notification"); + Thread.dumpStack(); + return; + } + connectToProxy(null); } public void systemReady() { - mUEventObserver.startObserving(UEVENT_FILTER); - scheduleUpdatePorts(); + if (mProxy != null) { + try { + mProxy.queryPortStatus(); + } catch (RemoteException e) { + logAndPrint(Log.ERROR, null, + "ServiceStart: Failed to query port status"); + Thread.dumpStack(); + } + } + mSystemReady = true; } public UsbPort[] getPorts() { @@ -223,20 +231,14 @@ public class UsbPortManager { + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); - SimulatedPortInfo sim = mSimulatedPorts.get(portId); + RawPortInfo sim = mSimulatedPorts.get(portId); if (sim != null) { // Change simulated state. sim.mCurrentMode = newMode; sim.mCurrentPowerRole = newPowerRole; sim.mCurrentDataRole = newDataRole; - } else if (mHaveKernelSupport) { - // Change actual state. - final File portDir = new File(SYSFS_CLASS, portId); - if (!portDir.exists()) { - logAndPrint(Log.ERROR, pw, "USB port not found: portId=" + portId); - return; - } - + updatePortsLocked(pw, null); + } else if (mProxy != null) { if (currentMode != newMode) { // Changing the mode will have the side-effect of also changing // the power and data roles but it might take some time to apply @@ -244,38 +246,54 @@ public class UsbPortManager { // hardware, we have no way of knowing whether it will work apriori // which is why we would prefer to set the power and data roles // directly instead. - if (!writeFile(portDir, SYSFS_PORT_MODE, - newMode == UsbPort.MODE_DFP ? PORT_MODE_DFP : PORT_MODE_UFP)) { - logAndPrint(Log.ERROR, pw, "Failed to set the USB port mode: " + + logAndPrint(Log.ERROR, pw, "Trying to set the USB port mode: " + "portId=" + portId + ", newMode=" + UsbPort.modeToString(newMode)); + PortRole newRole = new PortRole(); + newRole.type = PortRoleType.MODE; + newRole.role = newMode; + try { + mProxy.switchRole(portId, newRole); + } catch (RemoteException e) { + logAndPrint(Log.ERROR, pw, "Failed to set the USB port mode: " + + "portId=" + portId + + ", newMode=" + UsbPort.modeToString(newRole.role)); + Thread.dumpStack(); return; } } else { // Change power and data role independently as needed. if (currentPowerRole != newPowerRole) { - if (!writeFile(portDir, SYSFS_PORT_POWER_ROLE, - newPowerRole == UsbPort.POWER_ROLE_SOURCE - ? PORT_POWER_ROLE_SOURCE : PORT_POWER_ROLE_SINK)) { + PortRole newRole = new PortRole(); + newRole.type = PortRoleType.POWER_ROLE; + newRole.role = newPowerRole; + try { + mProxy.switchRole(portId, newRole); + } catch (RemoteException e) { logAndPrint(Log.ERROR, pw, "Failed to set the USB port power role: " + "portId=" + portId - + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)); + + ", newPowerRole=" + UsbPort.powerRoleToString(newRole.role)); + Thread.dumpStack(); return; } } if (currentDataRole != newDataRole) { - if (!writeFile(portDir, SYSFS_PORT_DATA_ROLE, - newDataRole == UsbPort.DATA_ROLE_HOST - ? PORT_DATA_ROLE_HOST : PORT_DATA_ROLE_DEVICE)) { + PortRole newRole = new PortRole(); + newRole.type = PortRoleType.DATA_ROLE; + newRole.role = newDataRole; + try { + mProxy.switchRole(portId, newRole); + } catch (RemoteException e) { logAndPrint(Log.ERROR, pw, "Failed to set the USB port data role: " + "portId=" + portId - + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); + + ", newDataRole=" + UsbPort.dataRoleToString(newRole.role)); + Thread.dumpStack(); return; } } } } - updatePortsLocked(pw); } } @@ -289,8 +307,8 @@ public class UsbPortManager { pw.println("Adding simulated port: portId=" + portId + ", supportedModes=" + UsbPort.modeToString(supportedModes)); mSimulatedPorts.put(portId, - new SimulatedPortInfo(portId, supportedModes)); - updatePortsLocked(pw); + new RawPortInfo(portId, supportedModes)); + updatePortsLocked(pw, null); } } @@ -298,7 +316,7 @@ public class UsbPortManager { int powerRole, boolean canChangePowerRole, int dataRole, boolean canChangeDataRole, IndentingPrintWriter pw) { synchronized (mLock) { - final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId); + final RawPortInfo portInfo = mSimulatedPorts.get(portId); if (portInfo == null) { pw.println("Cannot connect simulated port which does not exist."); return; @@ -328,13 +346,13 @@ public class UsbPortManager { portInfo.mCanChangePowerRole = canChangePowerRole; portInfo.mCurrentDataRole = dataRole; portInfo.mCanChangeDataRole = canChangeDataRole; - updatePortsLocked(pw); + updatePortsLocked(pw, null); } } public void disconnectSimulatedPort(String portId, IndentingPrintWriter pw) { synchronized (mLock) { - final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId); + final RawPortInfo portInfo = mSimulatedPorts.get(portId); if (portInfo == null) { pw.println("Cannot disconnect simulated port which does not exist."); return; @@ -347,7 +365,7 @@ public class UsbPortManager { portInfo.mCanChangePowerRole = false; portInfo.mCurrentDataRole = 0; portInfo.mCanChangeDataRole = false; - updatePortsLocked(pw); + updatePortsLocked(pw, null); } } @@ -361,7 +379,7 @@ public class UsbPortManager { pw.println("Disconnecting simulated port: portId=" + portId); mSimulatedPorts.removeAt(index); - updatePortsLocked(pw); + updatePortsLocked(pw, null); } } @@ -370,7 +388,7 @@ public class UsbPortManager { pw.println("Removing all simulated ports and ending simulation."); if (!mSimulatedPorts.isEmpty()) { mSimulatedPorts.clear(); - updatePortsLocked(pw); + updatePortsLocked(pw, null); } } } @@ -393,9 +411,108 @@ public class UsbPortManager { } } - private void updatePortsLocked(IndentingPrintWriter pw) { - // Assume all ports are gone unless informed otherwise. - // Kind of pessimistic but simple. + private static class HALCallback extends IUsbCallback.Stub { + public IndentingPrintWriter pw; + public UsbPortManager portManager; + + HALCallback() { + super(); + } + + HALCallback(IndentingPrintWriter pw, UsbPortManager portManager) { + this.pw = pw; + this.portManager = portManager; + } + + public void notifyPortStatusChange(ArrayList currentPortStatus, int retval) { + if (!portManager.mSystemReady) return; + + if (retval != Status.SUCCESS) { + logAndPrint(Log.ERROR, pw, "port status enquiry failed"); + return; + } + + ArrayList newPortInfo = new ArrayList(); + + for (PortStatus current : currentPortStatus) { + RawPortInfo temp = new RawPortInfo(current.portName, + current.supportedModes, current.currentMode, + current.canChangeMode, current.currentPowerRole, + current.canChangePowerRole, + current.currentDataRole, current.canChangeDataRole); + newPortInfo.add(temp); + logAndPrint(Log.INFO, pw, "ClientCallback: " + current.portName); + } + + Message message = portManager.mHandler.obtainMessage(); + Bundle bundle = new Bundle(); + bundle.putParcelableArrayList(PORT_INFO, newPortInfo); + message.what = MSG_UPDATE_PORTS; + message.setData(bundle); + portManager.mHandler.sendMessage(message); + return; + } + + public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) { + if (retval == Status.SUCCESS) { + logAndPrint(Log.INFO, pw, portName + "role switch successful"); + } else { + logAndPrint(Log.ERROR, pw, portName + "role switch failed"); + } + } + }; + + final class DeathRecipient implements HwBinder.DeathRecipient { + public IndentingPrintWriter pw; + + DeathRecipient(IndentingPrintWriter pw) { + this.pw = pw; + } + + @Override + public void serviceDied(long cookie) { + if (cookie == USB_HAL_DEATH_COOKIE) { + logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie); + synchronized (mLock) { + mProxy = null; + } + } + } + } + + final class ServiceNotification extends IServiceNotification.Stub { + @Override + public void onRegistration(String fqName, String name, boolean preexisting) { + logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name); + connectToProxy(null); + } + } + + private void connectToProxy(IndentingPrintWriter pw) { + synchronized (mLock) { + if (mProxy != null) return; + + try { + mProxy = IUsb.getService(sSERVICENAME); + mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE); + mProxy.setCallback(mHALCallback); + mProxy.queryPortStatus(); + } catch (NoSuchElementException e) { + logAndPrint(Log.ERROR, pw, sSERVICENAME + "not found." + + " Did the service failed to start ?"); + Thread.dumpStack(); + } catch (RemoteException e) { + logAndPrint(Log.ERROR, pw, sSERVICENAME + "connectToProxy: Service not responding"); + Thread.dumpStack(); + } + } + } + + /** + * Simulated ports directly add the new roles to mSimulatedPorts before calling. + * USB hal callback populates and sends the newPortInfo. + */ + private void updatePortsLocked(IndentingPrintWriter pw, ArrayList newPortInfo) { for (int i = mPorts.size(); i-- > 0; ) { mPorts.valueAt(i).mDisposition = PortInfo.DISPOSITION_REMOVED; } @@ -404,34 +521,18 @@ public class UsbPortManager { if (!mSimulatedPorts.isEmpty()) { final int count = mSimulatedPorts.size(); for (int i = 0; i < count; i++) { - final SimulatedPortInfo portInfo = mSimulatedPorts.valueAt(i); + final RawPortInfo portInfo = mSimulatedPorts.valueAt(i); addOrUpdatePortLocked(portInfo.mPortId, portInfo.mSupportedModes, portInfo.mCurrentMode, portInfo.mCanChangeMode, portInfo.mCurrentPowerRole, portInfo.mCanChangePowerRole, portInfo.mCurrentDataRole, portInfo.mCanChangeDataRole, pw); } - } else if (mHaveKernelSupport) { - final File[] portDirs = new File(SYSFS_CLASS).listFiles(); - if (portDirs != null) { - for (File portDir : portDirs) { - if (!portDir.isDirectory()) { - continue; - } - - // Parse the sysfs file contents. - final String portId = portDir.getName(); - final int supportedModes = readSupportedModes(portDir); - final int currentMode = readCurrentMode(portDir); - final boolean canChangeMode = canChangeMode(portDir); - final int currentPowerRole = readCurrentPowerRole(portDir); - final boolean canChangePowerRole = canChangePowerRole(portDir); - final int currentDataRole = readCurrentDataRole(portDir); - final boolean canChangeDataRole = canChangeDataRole(portDir); - addOrUpdatePortLocked(portId, supportedModes, - currentMode, canChangeMode, - currentPowerRole, canChangePowerRole, - currentDataRole, canChangeDataRole, pw); - } + } else { + for (RawPortInfo currentPortInfo : newPortInfo) { + addOrUpdatePortLocked(currentPortInfo.mPortId, currentPortInfo.mSupportedModes, + currentPortInfo.mCurrentMode, currentPortInfo.mCanChangeMode, + currentPortInfo.mCurrentPowerRole, currentPortInfo.mCanChangePowerRole, + currentPortInfo.mCurrentDataRole, currentPortInfo.mCanChangeDataRole, pw); } } @@ -457,20 +558,21 @@ public class UsbPortManager { } } + // Must only be called by updatePortsLocked. private void addOrUpdatePortLocked(String portId, int supportedModes, - int currentMode, boolean canChangeMode, - int currentPowerRole, boolean canChangePowerRole, - int currentDataRole, boolean canChangeDataRole, - IndentingPrintWriter pw) { + int currentMode, boolean canChangeMode, + int currentPowerRole, boolean canChangePowerRole, + int currentDataRole, boolean canChangeDataRole, + IndentingPrintWriter pw) { // Only allow mode switch capability for dual role ports. // Validate that the current mode matches the supported modes we expect. if (supportedModes != UsbPort.MODE_DUAL) { canChangeMode = false; if (currentMode != 0 && currentMode != supportedModes) { logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB " - + "port driver: supportedModes=" + UsbPort.modeToString(supportedModes) - + ", currentMode=" + UsbPort.modeToString(currentMode)); + + "port driver: supportedModes=" + UsbPort.modeToString(supportedModes) + + ", currentMode=" + UsbPort.modeToString(currentMode)); currentMode = 0; } } @@ -485,8 +587,8 @@ public class UsbPortManager { // Can change both power and data role independently. // Assume all combinations are possible. supportedRoleCombinations |= - COMBO_SOURCE_HOST | COMBO_SOURCE_DEVICE - | COMBO_SINK_HOST | COMBO_SINK_DEVICE; + COMBO_SOURCE_HOST | COMBO_SOURCE_DEVICE + | COMBO_SINK_HOST | COMBO_SINK_DEVICE; } else if (canChangePowerRole) { // Can only change power role. // Assume data role must remain at its current value. @@ -514,24 +616,24 @@ public class UsbPortManager { if (portInfo == null) { portInfo = new PortInfo(portId, supportedModes); portInfo.setStatus(currentMode, canChangeMode, - currentPowerRole, canChangePowerRole, - currentDataRole, canChangeDataRole, - supportedRoleCombinations); + currentPowerRole, canChangePowerRole, + currentDataRole, canChangeDataRole, + supportedRoleCombinations); mPorts.put(portId, portInfo); } else { // Sanity check that ports aren't changing definition out from under us. if (supportedModes != portInfo.mUsbPort.getSupportedModes()) { logAndPrint(Log.WARN, pw, "Ignoring inconsistent list of supported modes from " - + "USB port driver (should be immutable): " - + "previous=" + UsbPort.modeToString( - portInfo.mUsbPort.getSupportedModes()) - + ", current=" + UsbPort.modeToString(supportedModes)); + + "USB port driver (should be immutable): " + + "previous=" + UsbPort.modeToString( + portInfo.mUsbPort.getSupportedModes()) + + ", current=" + UsbPort.modeToString(supportedModes)); } if (portInfo.setStatus(currentMode, canChangeMode, - currentPowerRole, canChangePowerRole, - currentDataRole, canChangeDataRole, - supportedRoleCombinations)) { + currentPowerRole, canChangePowerRole, + currentDataRole, canChangeDataRole, + supportedRoleCombinations)) { portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED; } else { portInfo.mDisposition = PortInfo.DISPOSITION_READY; @@ -572,120 +674,6 @@ public class UsbPortManager { }); } - private void scheduleUpdatePorts() { - if (!mHandler.hasMessages(MSG_UPDATE_PORTS)) { - mHandler.sendEmptyMessage(MSG_UPDATE_PORTS); - } - } - - private static int readSupportedModes(File portDir) { - int modes = 0; - final String contents = readFile(portDir, SYSFS_PORT_SUPPORTED_MODES); - if (contents != null) { - if (contents.contains(PORT_MODE_DFP)) { - modes |= UsbPort.MODE_DFP; - } - if (contents.contains(PORT_MODE_UFP)) { - modes |= UsbPort.MODE_UFP; - } - } - return modes; - } - - private static int readCurrentMode(File portDir) { - final String contents = readFile(portDir, SYSFS_PORT_MODE); - if (contents != null) { - if (contents.equals(PORT_MODE_DFP)) { - return UsbPort.MODE_DFP; - } - if (contents.equals(PORT_MODE_UFP)) { - return UsbPort.MODE_UFP; - } - } - return 0; - } - - private static int readCurrentPowerRole(File portDir) { - final String contents = readFile(portDir, SYSFS_PORT_POWER_ROLE); - if (contents != null) { - if (contents.equals(PORT_POWER_ROLE_SOURCE)) { - return UsbPort.POWER_ROLE_SOURCE; - } - if (contents.equals(PORT_POWER_ROLE_SINK)) { - return UsbPort.POWER_ROLE_SINK; - } - } - return 0; - } - - private static int readCurrentDataRole(File portDir) { - final String contents = readFile(portDir, SYSFS_PORT_DATA_ROLE); - if (contents != null) { - if (contents.equals(PORT_DATA_ROLE_HOST)) { - return UsbPort.DATA_ROLE_HOST; - } - if (contents.equals(PORT_DATA_ROLE_DEVICE)) { - return UsbPort.DATA_ROLE_DEVICE; - } - } - return 0; - } - - private static boolean fileIsRootWritable(String path) { - try { - // If the file is user writable, then it is root writable. - return (Os.stat(path).st_mode & OsConstants.S_IWUSR) != 0; - } catch (ErrnoException e) { - return false; - } - } - - private static boolean canChangeMode(File portDir) { - return fileIsRootWritable(new File(portDir, SYSFS_PORT_MODE).getPath()); - } - - private static boolean canChangePowerRole(File portDir) { - return fileIsRootWritable(new File(portDir, SYSFS_PORT_POWER_ROLE).getPath()); - } - - private static boolean canChangeDataRole(File portDir) { - return fileIsRootWritable(new File(portDir, SYSFS_PORT_DATA_ROLE).getPath()); - } - - private static String readFile(File dir, String filename) { - final File file = new File(dir, filename); - try { - return IoUtils.readFileAsString(file.getAbsolutePath()).trim(); - } catch (IOException ex) { - return null; - } - } - - private static boolean waitForState(String property, String state) { - // wait for the transition to complete. - // give up after 5 seconds. - // 5 seconds is probably too long, but we have seen hardware that takes - // over 3 seconds to change states. - String value = null; - for (int i = 0; i < 100; i++) { - // State transition is done when property is set to the new configuration - value = SystemProperties.get(property); - if (state.equals(value)) return true; - SystemClock.sleep(50); - } - Slog.e(TAG, "waitForState(" + state + ") for " + property + " FAILED: got " + value); - return false; - } - - private static String propertyFromFilename(String filename) { - return USB_TYPEC_PROP_PREFIX + filename; - } - - private static boolean writeFile(File dir, String filename, String contents) { - SystemProperties.set(propertyFromFilename(filename), contents); - return waitForState(USB_TYPEC_STATE, contents); - } - private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) { Slog.println(priority, TAG, msg); if (pw != null) { @@ -698,8 +686,10 @@ public class UsbPortManager { public void handleMessage(Message msg) { switch (msg.what) { case MSG_UPDATE_PORTS: { + Bundle b = msg.getData(); + ArrayList PortInfo = b.getParcelableArrayList(PORT_INFO); synchronized (mLock) { - updatePortsLocked(null); + updatePortsLocked(null, PortInfo); } break; } @@ -707,13 +697,6 @@ public class UsbPortManager { } }; - private final UEventObserver mUEventObserver = new UEventObserver() { - @Override - public void onUEvent(UEvent event) { - scheduleUpdatePorts(); - } - }; - /** * Describes a USB port. */ @@ -764,10 +747,10 @@ public class UsbPortManager { } /** - * Describes a simulated USB port. - * Roughly mirrors the information we would ordinarily get from the kernel. + * Used for storing the raw data from the kernel + * Values of the member variables mocked directly incase of emulation. */ - private static final class SimulatedPortInfo { + private static final class RawPortInfo implements Parcelable { public final String mPortId; public final int mSupportedModes; public int mCurrentMode; @@ -777,9 +760,63 @@ public class UsbPortManager { public int mCurrentDataRole; public boolean mCanChangeDataRole; - public SimulatedPortInfo(String portId, int supportedModes) { + RawPortInfo(String portId, int supportedModes) { mPortId = portId; mSupportedModes = supportedModes; } + + RawPortInfo(String portId, int supportedModes, + int currentMode, boolean canChangeMode, + int currentPowerRole, boolean canChangePowerRole, + int currentDataRole, boolean canChangeDataRole) { + mPortId = portId; + mSupportedModes = supportedModes; + mCurrentMode = currentMode; + mCanChangeMode = canChangeMode; + mCurrentPowerRole = currentPowerRole; + mCanChangePowerRole = canChangePowerRole; + mCurrentDataRole = currentDataRole; + mCanChangeDataRole = canChangeDataRole; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mPortId); + dest.writeInt(mSupportedModes); + dest.writeInt(mCurrentMode); + dest.writeByte((byte) (mCanChangeMode ? 1 : 0)); + dest.writeInt(mCurrentPowerRole); + dest.writeByte((byte) (mCanChangePowerRole ? 1 : 0)); + dest.writeInt(mCurrentDataRole); + dest.writeByte((byte) (mCanChangeDataRole ? 1 : 0)); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public RawPortInfo createFromParcel(Parcel in) { + String id = in.readString(); + int supportedModes = in.readInt(); + int currentMode = in.readInt(); + boolean canChangeMode = in.readByte() != 0; + int currentPowerRole = in.readInt(); + boolean canChangePowerRole = in.readByte() != 0; + int currentDataRole = in.readInt(); + boolean canChangeDataRole = in.readByte() != 0; + return new RawPortInfo(id, supportedModes, currentMode, canChangeMode, + currentPowerRole, canChangePowerRole, + currentDataRole, canChangeDataRole); + } + + @Override + public RawPortInfo[] newArray(int size) { + return new RawPortInfo[size]; + } + }; } }