Merge "Refactor UsbPortManager to use the binderised USB HAL"
This commit is contained in:
committed by
Android (Google) Code Review
commit
d979398983
@@ -537,6 +537,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 := \
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
@@ -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).
|
||||
* <p>
|
||||
@@ -40,7 +42,7 @@ public final class UsbPort implements Parcelable {
|
||||
* combination of roles (and possibly others as well).
|
||||
* </p>
|
||||
*/
|
||||
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).
|
||||
* </p>
|
||||
*/
|
||||
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).
|
||||
* </p>
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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<String, PortInfo> mPorts = new ArrayMap<String, PortInfo>();
|
||||
|
||||
// List of all simulated ports, indexed by id.
|
||||
private final ArrayMap<String, SimulatedPortInfo> mSimulatedPorts =
|
||||
new ArrayMap<String, SimulatedPortInfo>();
|
||||
private final ArrayMap<String, RawPortInfo> mSimulatedPorts =
|
||||
new ArrayMap<String, RawPortInfo>();
|
||||
|
||||
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<PortStatus> currentPortStatus, int retval) {
|
||||
if (!portManager.mSystemReady) return;
|
||||
|
||||
if (retval != Status.SUCCESS) {
|
||||
logAndPrint(Log.ERROR, pw, "port status enquiry failed");
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<RawPortInfo> newPortInfo = new ArrayList<RawPortInfo>();
|
||||
|
||||
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<RawPortInfo> 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<RawPortInfo> 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<RawPortInfo> CREATOR =
|
||||
new Parcelable.Creator<RawPortInfo>() {
|
||||
@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];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user