* commit '0cb1cfdcc306f78030403c8da22bfcda630c5527': Add USB port manager.
This commit is contained in:
@@ -19,6 +19,8 @@ package android.hardware.usb;
|
||||
import android.app.PendingIntent;
|
||||
import android.hardware.usb.UsbAccessory;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbPort;
|
||||
import android.hardware.usb.UsbPortStatus;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
@@ -108,4 +110,13 @@ interface IUsbManager
|
||||
|
||||
/* Clear public keys installed for secure USB debugging */
|
||||
void clearUsbDebuggingKeys();
|
||||
|
||||
/* Gets the list of USB ports. */
|
||||
UsbPort[] getPorts();
|
||||
|
||||
/* Gets the status of the specified USB port. */
|
||||
UsbPortStatus getPortStatus(in String portId);
|
||||
|
||||
/* Sets the port's current role. */
|
||||
void setPortRoles(in String portId, int powerRole, int dataRole);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
package android.hardware.usb;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
@@ -74,6 +76,22 @@ public class UsbManager {
|
||||
public static final String ACTION_USB_STATE =
|
||||
"android.hardware.usb.action.USB_STATE";
|
||||
|
||||
/**
|
||||
* Broadcast Action: A broadcast for USB port changes.
|
||||
*
|
||||
* This intent is sent when a USB port is added, removed, or changes state.
|
||||
* <ul>
|
||||
* <li> {@link #EXTRA_PORT} containing the {@link android.hardware.usb.UsbPort}
|
||||
* for the port.
|
||||
* <li> {@link #EXTRA_PORT_STATUS} containing the {@link android.hardware.usb.UsbPortStatus}
|
||||
* for the port, or null if the port has been removed
|
||||
* </ul>
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String ACTION_USB_PORT_CHANGED =
|
||||
"android.hardware.usb.action.USB_PORT_CHANGED";
|
||||
|
||||
/**
|
||||
* Broadcast Action: A broadcast for USB device attached event.
|
||||
*
|
||||
@@ -213,6 +231,23 @@ public class UsbManager {
|
||||
*/
|
||||
public static final String USB_FUNCTION_ACCESSORY = "accessory";
|
||||
|
||||
/**
|
||||
* Name of extra for {@link #ACTION_USB_PORT_CHANGED}
|
||||
* containing the {@link UsbPort} object for the port.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String EXTRA_PORT = "port";
|
||||
|
||||
/**
|
||||
* Name of extra for {@link #ACTION_USB_PORT_CHANGED}
|
||||
* containing the {@link UsbPortStatus} object for the port, or null if the port
|
||||
* was removed.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String EXTRA_PORT_STATUS = "portStatus";
|
||||
|
||||
/**
|
||||
* Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and
|
||||
* {@link #ACTION_USB_DEVICE_DETACHED} broadcasts
|
||||
@@ -499,6 +534,77 @@ public class UsbManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of physical USB ports on the device.
|
||||
* <p>
|
||||
* This list is guaranteed to contain all dual-role USB Type C ports but it might
|
||||
* be missing other ports depending on whether the kernel USB drivers have been
|
||||
* updated to publish all of the device's ports through the new "dual_role_usb"
|
||||
* device class (which supports all types of ports despite its name).
|
||||
* </p>
|
||||
*
|
||||
* @return The list of USB ports, or null if none.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public UsbPort[] getPorts() {
|
||||
try {
|
||||
return mService.getPorts();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "RemoteException in getPorts", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status of the specified USB port.
|
||||
*
|
||||
* @param port The port to query.
|
||||
* @return The status of the specified USB port, or null if unknown.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public UsbPortStatus getPortStatus(UsbPort port) {
|
||||
Preconditions.checkNotNull(port, "port must not be null");
|
||||
|
||||
try {
|
||||
return mService.getPortStatus(port.getId());
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "RemoteException in getPortStatus", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the desired role combination of the port.
|
||||
* <p>
|
||||
* The supported role combinations depend on what is connected to the port and may be
|
||||
* determined by consulting
|
||||
* {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
|
||||
* </p><p>
|
||||
* Note: This function is asynchronous and may fail silently without applying
|
||||
* the requested changes. If this function does cause a status change to occur then
|
||||
* a {@link #ACTION_USB_PORT_CHANGED} broadcast will be sent.
|
||||
* </p>
|
||||
*
|
||||
* @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
|
||||
* or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
|
||||
* @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
|
||||
* or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setPortRoles(UsbPort port, int powerRole, int dataRole) {
|
||||
Preconditions.checkNotNull(port, "port must not be null");
|
||||
UsbPort.checkRoles(powerRole, dataRole);
|
||||
|
||||
try {
|
||||
mService.setPortRoles(port.getId(), powerRole, dataRole);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "RemoteException in setPortRole", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static String addFunction(String functions, String function) {
|
||||
if ("none".equals(functions)) {
|
||||
|
||||
19
core/java/android/hardware/usb/UsbPort.aidl
Normal file
19
core/java/android/hardware/usb/UsbPort.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2015, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.usb;
|
||||
|
||||
parcelable UsbPort;
|
||||
238
core/java/android/hardware/usb/UsbPort.java
Normal file
238
core/java/android/hardware/usb/UsbPort.java
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.usb;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Represents a physical USB port and describes its characteristics.
|
||||
* <p>
|
||||
* This object is immutable.
|
||||
* </p>
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class UsbPort implements Parcelable {
|
||||
private final String mId;
|
||||
private final int mSupportedModes;
|
||||
|
||||
/**
|
||||
* Mode bit: This USB port can act as a downstream facing port (host).
|
||||
* <p>
|
||||
* Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
|
||||
* combination of roles (and possibly others as well).
|
||||
* </p>
|
||||
*/
|
||||
public static final int MODE_DFP = 1 << 0;
|
||||
|
||||
/**
|
||||
* Mode bit: This USB port can act as an upstream facing port (device).
|
||||
* <p>
|
||||
* Implies that the port supports the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
|
||||
* combination of roles (and possibly others as well).
|
||||
* </p>
|
||||
*/
|
||||
public static final int MODE_UFP = 1 << 1;
|
||||
|
||||
/**
|
||||
* Mode bit: This USB port can act either as an downstream facing port (host) or as
|
||||
* an upstream facing port (device).
|
||||
* <p>
|
||||
* Implies that the port supports the {@link #POWER_ROLE_SOURCE} and {@link #DATA_ROLE_HOST}
|
||||
* combination of roles and the {@link #POWER_ROLE_SINK} and {@link #DATA_ROLE_DEVICE}
|
||||
* combination of roles (and possibly others as well).
|
||||
* </p>
|
||||
*/
|
||||
public static final int MODE_DUAL = MODE_DFP | MODE_UFP;
|
||||
|
||||
/**
|
||||
* Power role: This USB port can act as a source (provide power).
|
||||
*/
|
||||
public static final int POWER_ROLE_SOURCE = 1;
|
||||
|
||||
/**
|
||||
* Power role: This USB port can act as a sink (receive power).
|
||||
*/
|
||||
public static final int POWER_ROLE_SINK = 2;
|
||||
|
||||
/**
|
||||
* Data role: This USB port can act as a host (access data services).
|
||||
*/
|
||||
public static final int DATA_ROLE_HOST = 1;
|
||||
|
||||
/**
|
||||
* Data role: This USB port can act as a device (offer data services).
|
||||
*/
|
||||
public static final int DATA_ROLE_DEVICE = 2;
|
||||
|
||||
private static final int NUM_DATA_ROLES = 3;
|
||||
|
||||
/** @hide */
|
||||
public UsbPort(String id, int supportedModes) {
|
||||
mId = id;
|
||||
mSupportedModes = supportedModes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique id of the port.
|
||||
*
|
||||
* @return The unique id of the port; not intended for display.
|
||||
*/
|
||||
public String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the supported modes of the port.
|
||||
* <p>
|
||||
* The actual mode of the port may vary depending on what is plugged into it.
|
||||
* </p>
|
||||
*
|
||||
* @return The supported modes: one of {@link #MODE_DFP}, {@link #MODE_UFP}, or
|
||||
* {@link #MODE_DUAL}.
|
||||
*/
|
||||
public int getSupportedModes() {
|
||||
return mSupportedModes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines one power and one data role together into a unique value with
|
||||
* exactly one bit set. This can be used to efficiently determine whether
|
||||
* a combination of roles is supported by testing whether that bit is present
|
||||
* in a bit-field.
|
||||
*
|
||||
* @param powerRole The desired power role: {@link UsbPort#POWER_ROLE_SOURCE}
|
||||
* or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
|
||||
* @param dataRole The desired data role: {@link UsbPort#DATA_ROLE_HOST}
|
||||
* or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
|
||||
* @hide
|
||||
*/
|
||||
public static int combineRolesAsBit(int powerRole, int dataRole) {
|
||||
checkRoles(powerRole, dataRole);
|
||||
final int index = powerRole * NUM_DATA_ROLES + dataRole;
|
||||
return 1 << index;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static String modeToString(int mode) {
|
||||
switch (mode) {
|
||||
case 0:
|
||||
return "none";
|
||||
case MODE_DFP:
|
||||
return "dfp";
|
||||
case MODE_UFP:
|
||||
return "ufp";
|
||||
case MODE_DUAL:
|
||||
return "dual";
|
||||
default:
|
||||
return Integer.toString(mode);
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static String powerRoleToString(int role) {
|
||||
switch (role) {
|
||||
case 0:
|
||||
return "no-power";
|
||||
case POWER_ROLE_SOURCE:
|
||||
return "source";
|
||||
case POWER_ROLE_SINK:
|
||||
return "sink";
|
||||
default:
|
||||
return Integer.toString(role);
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static String dataRoleToString(int role) {
|
||||
switch (role) {
|
||||
case 0:
|
||||
return "no-data";
|
||||
case DATA_ROLE_HOST:
|
||||
return "host";
|
||||
case DATA_ROLE_DEVICE:
|
||||
return "device";
|
||||
default:
|
||||
return Integer.toString(role);
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static String roleCombinationsToString(int combo) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append("[");
|
||||
|
||||
boolean first = true;
|
||||
while (combo != 0) {
|
||||
final int index = Integer.numberOfTrailingZeros(combo);
|
||||
combo &= ~(1 << index);
|
||||
final int powerRole = index / NUM_DATA_ROLES;
|
||||
final int dataRole = index % NUM_DATA_ROLES;
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
result.append(", ");
|
||||
}
|
||||
result.append(powerRoleToString(powerRole));
|
||||
result.append(':');
|
||||
result.append(dataRoleToString(dataRole));
|
||||
}
|
||||
|
||||
result.append("]");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/** @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");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UsbPort{id=" + mId + ", supportedModes=" + modeToString(mSupportedModes) + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(mId);
|
||||
dest.writeInt(mSupportedModes);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<UsbPort> CREATOR =
|
||||
new Parcelable.Creator<UsbPort>() {
|
||||
@Override
|
||||
public UsbPort createFromParcel(Parcel in) {
|
||||
String id = in.readString();
|
||||
int supportedModes = in.readInt();
|
||||
return new UsbPort(id, supportedModes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbPort[] newArray(int size) {
|
||||
return new UsbPort[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
19
core/java/android/hardware/usb/UsbPortStatus.aidl
Normal file
19
core/java/android/hardware/usb/UsbPortStatus.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2015, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.usb;
|
||||
|
||||
parcelable UsbPortStatus;
|
||||
144
core/java/android/hardware/usb/UsbPortStatus.java
Normal file
144
core/java/android/hardware/usb/UsbPortStatus.java
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.hardware.usb;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Describes the status of a USB port.
|
||||
* <p>
|
||||
* This object is immutable.
|
||||
* </p>
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class UsbPortStatus implements Parcelable {
|
||||
private final int mCurrentMode;
|
||||
private final int mCurrentPowerRole;
|
||||
private final int mCurrentDataRole;
|
||||
private final int mSupportedRoleCombinations;
|
||||
|
||||
/** @hide */
|
||||
public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
|
||||
int supportedRoleCombinations) {
|
||||
mCurrentMode = currentMode;
|
||||
mCurrentPowerRole = currentPowerRole;
|
||||
mCurrentDataRole = currentDataRole;
|
||||
mSupportedRoleCombinations = supportedRoleCombinations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is anything connected to the port.
|
||||
*
|
||||
* @return True if there is anything connected to the port.
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return mCurrentMode != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current mode of the port.
|
||||
*
|
||||
* @return The current mode: {@link UsbPort#MODE_DFP}, {@link UsbPort#MODE_UFP},
|
||||
* or 0 if nothing is connected.
|
||||
*/
|
||||
public int getCurrentMode() {
|
||||
return mCurrentMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current power role of the port.
|
||||
*
|
||||
* @return The current power role: {@link UsbPort#POWER_ROLE_SOURCE},
|
||||
* {@link UsbPort#POWER_ROLE_SINK}, or 0 if nothing is connected.
|
||||
*/
|
||||
public int getCurrentPowerRole() {
|
||||
return mCurrentPowerRole;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current data role of the port.
|
||||
*
|
||||
* @return The current data role: {@link UsbPort#DATA_ROLE_HOST},
|
||||
* {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if nothing is connected.
|
||||
*/
|
||||
public int getCurrentDataRole() {
|
||||
return mCurrentDataRole;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified power and data role combination is supported
|
||||
* given what is currently connected to the port.
|
||||
*
|
||||
* @param powerRole The power role to check: {@link UsbPort#POWER_ROLE_SOURCE}
|
||||
* or {@link UsbPort#POWER_ROLE_SINK}, or 0 if no power role.
|
||||
* @param dataRole The data role to check: either {@link UsbPort#DATA_ROLE_HOST}
|
||||
* or {@link UsbPort#DATA_ROLE_DEVICE}, or 0 if no data role.
|
||||
*/
|
||||
public boolean isRoleCombinationSupported(int powerRole, int dataRole) {
|
||||
return (mSupportedRoleCombinations &
|
||||
UsbPort.combineRolesAsBit(powerRole, dataRole)) != 0;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public int getSupportedRoleCombinations() {
|
||||
return mSupportedRoleCombinations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UsbPortStatus{connected=" + isConnected()
|
||||
+ ", currentMode=" + UsbPort.modeToString(mCurrentMode)
|
||||
+ ", currentPowerRole=" + UsbPort.powerRoleToString(mCurrentPowerRole)
|
||||
+ ", currentDataRole=" + UsbPort.dataRoleToString(mCurrentDataRole)
|
||||
+ ", supportedRoleCombinations="
|
||||
+ UsbPort.roleCombinationsToString(mSupportedRoleCombinations)
|
||||
+ "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mCurrentMode);
|
||||
dest.writeInt(mCurrentPowerRole);
|
||||
dest.writeInt(mCurrentDataRole);
|
||||
dest.writeInt(mSupportedRoleCombinations);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<UsbPortStatus> CREATOR =
|
||||
new Parcelable.Creator<UsbPortStatus>() {
|
||||
@Override
|
||||
public UsbPortStatus createFromParcel(Parcel in) {
|
||||
int currentMode = in.readInt();
|
||||
int currentPowerRole = in.readInt();
|
||||
int currentDataRole = in.readInt();
|
||||
int supportedRoleCombinations = in.readInt();
|
||||
return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
|
||||
supportedRoleCombinations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbPortStatus[] newArray(int size) {
|
||||
return new UsbPortStatus[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -186,6 +186,7 @@
|
||||
<protected-broadcast android:name="android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED" />
|
||||
|
||||
<protected-broadcast android:name="android.hardware.usb.action.USB_STATE" />
|
||||
<protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
|
||||
<protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
|
||||
<protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
|
||||
<protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||
|
||||
753
services/usb/java/com/android/server/usb/UsbPortManager.java
Normal file
753
services/usb/java/com/android/server/usb/UsbPortManager.java
Normal file
@@ -0,0 +1,753 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.UEventObserver;
|
||||
import android.os.UserHandle;
|
||||
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 libcore.io.IoUtils;
|
||||
|
||||
/**
|
||||
* Allows trusted components to control the properties of physical USB ports
|
||||
* via the "/sys/class/dual_role_usb" kernel interface.
|
||||
* <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
|
||||
* we only need this for devices with USB Type C ports to allow the System UI to
|
||||
* control USB charging and data direction. On devices that do not support this
|
||||
* interface the list of ports may incorrectly appear to be empty
|
||||
* (but we don't care today).
|
||||
* </p>
|
||||
*/
|
||||
public class UsbPortManager {
|
||||
private static final String TAG = "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";
|
||||
|
||||
// All non-trivial role combinations.
|
||||
private static final int COMBO_SOURCE_HOST =
|
||||
UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST);
|
||||
private static final int COMBO_SOURCE_DEVICE =
|
||||
UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE);
|
||||
private static final int COMBO_SINK_HOST =
|
||||
UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST);
|
||||
private static final int COMBO_SINK_DEVICE =
|
||||
UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE);
|
||||
|
||||
// The system context.
|
||||
private final Context mContext;
|
||||
|
||||
// True if we have kernel support.
|
||||
private final boolean mHaveKernelSupport;
|
||||
|
||||
// Mutex for all mutable shared state.
|
||||
private final Object mLock = new Object();
|
||||
|
||||
// List of all ports, indexed by id.
|
||||
// Ports may temporarily have different dispositions as they are added or removed
|
||||
// but the class invariant is that this list will only contain ports with DISPOSITION_READY
|
||||
// except while updatePortsLocked() is in progress.
|
||||
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>();
|
||||
|
||||
public UsbPortManager(Context context) {
|
||||
mContext = context;
|
||||
mHaveKernelSupport = new File(SYSFS_CLASS).exists();
|
||||
}
|
||||
|
||||
public void systemReady() {
|
||||
mUEventObserver.startObserving(UEVENT_FILTER);
|
||||
scheduleUpdatePorts();
|
||||
}
|
||||
|
||||
public UsbPort[] getPorts() {
|
||||
synchronized (mLock) {
|
||||
final int count = mPorts.size();
|
||||
final UsbPort[] result = new UsbPort[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
result[i] = mPorts.valueAt(i).mUsbPort;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public UsbPortStatus getPortStatus(String portId) {
|
||||
synchronized (mLock) {
|
||||
final PortInfo portInfo = mPorts.get(portId);
|
||||
return portInfo != null ? portInfo.mUsbPortStatus : null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPortRoles(String portId, int newPowerRole, int newDataRole,
|
||||
IndentingPrintWriter pw) {
|
||||
synchronized (mLock) {
|
||||
final PortInfo portInfo = mPorts.get(portId);
|
||||
if (portInfo == null) {
|
||||
if (pw != null) {
|
||||
pw.println("No such USB port: " + portId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether the new role is actually supported.
|
||||
if (!portInfo.mUsbPortStatus.isRoleCombinationSupported(newPowerRole, newDataRole)) {
|
||||
logAndPrint(Log.ERROR, pw, "Attempted to set USB port into unsupported "
|
||||
+ "role combination: portId=" + portId
|
||||
+ ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
|
||||
+ ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether anything actually changed.
|
||||
final int currentDataRole = portInfo.mUsbPortStatus.getCurrentDataRole();
|
||||
final int currentPowerRole = portInfo.mUsbPortStatus.getCurrentPowerRole();
|
||||
if (currentDataRole == newDataRole && currentPowerRole == newPowerRole) {
|
||||
if (pw != null) {
|
||||
pw.println("No change.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine whether we need to change the mode in order to accomplish this goal.
|
||||
// We prefer not to do this since it's more likely to fail.
|
||||
//
|
||||
// Note: Arguably it might be worth allowing the client to influence this policy
|
||||
// decision so that we could show more powerful developer facing UI but let's
|
||||
// see how far we can get without having to do that.
|
||||
final boolean canChangeMode = portInfo.mCanChangeMode;
|
||||
final boolean canChangePowerRole = portInfo.mCanChangePowerRole;
|
||||
final boolean canChangeDataRole = portInfo.mCanChangeDataRole;
|
||||
final int currentMode = portInfo.mUsbPortStatus.getCurrentMode();
|
||||
final int newMode;
|
||||
if ((!canChangePowerRole && currentPowerRole != newPowerRole)
|
||||
|| (!canChangeDataRole && currentDataRole != newDataRole)) {
|
||||
if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SOURCE
|
||||
&& newDataRole == UsbPort.DATA_ROLE_HOST) {
|
||||
newMode = UsbPort.MODE_DFP;
|
||||
} else if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SINK
|
||||
&& newDataRole == UsbPort.DATA_ROLE_DEVICE) {
|
||||
newMode = UsbPort.MODE_UFP;
|
||||
} else {
|
||||
logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations "
|
||||
+ "while attempting to change role: " + portInfo
|
||||
+ ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
|
||||
+ ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
newMode = currentMode;
|
||||
}
|
||||
|
||||
// Make it happen.
|
||||
logAndPrint(Log.INFO, pw, "Setting USB port mode and role: portId=" + portId
|
||||
+ ", currentMode=" + UsbPort.modeToString(currentMode)
|
||||
+ ", currentPowerRole=" + UsbPort.powerRoleToString(currentPowerRole)
|
||||
+ ", currentDataRole=" + UsbPort.dataRoleToString(currentDataRole)
|
||||
+ ", newMode=" + UsbPort.modeToString(newMode)
|
||||
+ ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
|
||||
+ ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
|
||||
|
||||
SimulatedPortInfo 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;
|
||||
}
|
||||
|
||||
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
|
||||
// and the renegotiation might fail. Due to limitations of the USB
|
||||
// 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: "
|
||||
+ "portId=" + portId
|
||||
+ ", newMode=" + UsbPort.modeToString(newMode));
|
||||
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)) {
|
||||
logAndPrint(Log.ERROR, pw, "Failed to set the USB port power role: "
|
||||
+ "portId=" + portId
|
||||
+ ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (currentDataRole != newDataRole) {
|
||||
if (!writeFile(portDir, SYSFS_PORT_DATA_ROLE,
|
||||
newDataRole == UsbPort.DATA_ROLE_HOST
|
||||
? PORT_DATA_ROLE_HOST : PORT_DATA_ROLE_DEVICE)) {
|
||||
logAndPrint(Log.ERROR, pw, "Failed to set the USB port data role: "
|
||||
+ "portId=" + portId
|
||||
+ ", newDataRole=" + UsbPort.dataRoleToString(newDataRole));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updatePortsLocked(pw);
|
||||
}
|
||||
}
|
||||
|
||||
public void addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw) {
|
||||
synchronized (mLock) {
|
||||
if (mSimulatedPorts.containsKey(portId)) {
|
||||
pw.println("Port with same name already exists. Please remove it first.");
|
||||
return;
|
||||
}
|
||||
|
||||
pw.println("Adding simulated port: portId=" + portId
|
||||
+ ", supportedModes=" + UsbPort.modeToString(supportedModes));
|
||||
mSimulatedPorts.put(portId,
|
||||
new SimulatedPortInfo(portId, supportedModes));
|
||||
updatePortsLocked(pw);
|
||||
}
|
||||
}
|
||||
|
||||
public void connectSimulatedPort(String portId, int mode, boolean canChangeMode,
|
||||
int powerRole, boolean canChangePowerRole,
|
||||
int dataRole, boolean canChangeDataRole, IndentingPrintWriter pw) {
|
||||
synchronized (mLock) {
|
||||
final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId);
|
||||
if (portInfo == null) {
|
||||
pw.println("Cannot connect simulated port which does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == 0 || powerRole == 0 || dataRole == 0) {
|
||||
pw.println("Cannot connect simulated port in null mode, "
|
||||
+ "power role, or data role.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((portInfo.mSupportedModes & mode) == 0) {
|
||||
pw.println("Simulated port does not support mode: " + UsbPort.modeToString(mode));
|
||||
return;
|
||||
}
|
||||
|
||||
pw.println("Connecting simulated port: portId=" + portId
|
||||
+ ", mode=" + UsbPort.modeToString(mode)
|
||||
+ ", canChangeMode=" + canChangeMode
|
||||
+ ", powerRole=" + UsbPort.powerRoleToString(powerRole)
|
||||
+ ", canChangePowerRole=" + canChangePowerRole
|
||||
+ ", dataRole=" + UsbPort.dataRoleToString(dataRole)
|
||||
+ ", canChangeDataRole=" + canChangeDataRole);
|
||||
portInfo.mCurrentMode = mode;
|
||||
portInfo.mCanChangeMode = canChangeMode;
|
||||
portInfo.mCurrentPowerRole = powerRole;
|
||||
portInfo.mCanChangePowerRole = canChangePowerRole;
|
||||
portInfo.mCurrentDataRole = dataRole;
|
||||
portInfo.mCanChangeDataRole = canChangeDataRole;
|
||||
updatePortsLocked(pw);
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnectSimulatedPort(String portId, IndentingPrintWriter pw) {
|
||||
synchronized (mLock) {
|
||||
final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId);
|
||||
if (portInfo == null) {
|
||||
pw.println("Cannot disconnect simulated port which does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
pw.println("Disconnecting simulated port: portId=" + portId);
|
||||
portInfo.mCurrentMode = 0;
|
||||
portInfo.mCanChangeMode = false;
|
||||
portInfo.mCurrentPowerRole = 0;
|
||||
portInfo.mCanChangePowerRole = false;
|
||||
portInfo.mCurrentDataRole = 0;
|
||||
portInfo.mCanChangeDataRole = false;
|
||||
updatePortsLocked(pw);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeSimulatedPort(String portId, IndentingPrintWriter pw) {
|
||||
synchronized (mLock) {
|
||||
final int index = mSimulatedPorts.indexOfKey(portId);
|
||||
if (index < 0) {
|
||||
pw.println("Cannot remove simulated port which does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
pw.println("Disconnecting simulated port: portId=" + portId);
|
||||
mSimulatedPorts.removeAt(index);
|
||||
updatePortsLocked(pw);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetSimulation(IndentingPrintWriter pw) {
|
||||
synchronized (mLock) {
|
||||
pw.println("Removing all simulated ports and ending simulation.");
|
||||
if (!mSimulatedPorts.isEmpty()) {
|
||||
mSimulatedPorts.clear();
|
||||
updatePortsLocked(pw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dump(IndentingPrintWriter pw) {
|
||||
synchronized (mLock) {
|
||||
pw.print("USB Port State:");
|
||||
if (!mSimulatedPorts.isEmpty()) {
|
||||
pw.print(" (simulation active; end with 'dumpsys usb reset')");
|
||||
}
|
||||
pw.println();
|
||||
|
||||
if (mPorts.isEmpty()) {
|
||||
pw.println(" <no ports>");
|
||||
} else {
|
||||
for (PortInfo portInfo : mPorts.values()) {
|
||||
pw.println(" " + portInfo.mUsbPort.getId() + ": " + portInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePortsLocked(IndentingPrintWriter pw) {
|
||||
// Assume all ports are gone unless informed otherwise.
|
||||
// Kind of pessimistic but simple.
|
||||
for (int i = mPorts.size(); i-- > 0; ) {
|
||||
mPorts.valueAt(i).mDisposition = PortInfo.DISPOSITION_REMOVED;
|
||||
}
|
||||
|
||||
// Enumerate all extant ports.
|
||||
if (!mSimulatedPorts.isEmpty()) {
|
||||
final int count = mSimulatedPorts.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final SimulatedPortInfo 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process the updates.
|
||||
// Once finished, the list of ports will only contain ports in DISPOSITION_READY.
|
||||
for (int i = mPorts.size(); i-- > 0; ) {
|
||||
final PortInfo portInfo = mPorts.valueAt(i);
|
||||
switch (portInfo.mDisposition) {
|
||||
case PortInfo.DISPOSITION_ADDED:
|
||||
handlePortAddedLocked(portInfo, pw);
|
||||
portInfo.mDisposition = PortInfo.DISPOSITION_READY;
|
||||
break;
|
||||
case PortInfo.DISPOSITION_CHANGED:
|
||||
handlePortChangedLocked(portInfo, pw);
|
||||
portInfo.mDisposition = PortInfo.DISPOSITION_READY;
|
||||
break;
|
||||
case PortInfo.DISPOSITION_REMOVED:
|
||||
mPorts.removeAt(i);
|
||||
portInfo.mUsbPortStatus = null; // must do this early
|
||||
handlePortRemovedLocked(portInfo, pw);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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));
|
||||
currentMode = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the supported role combinations.
|
||||
// Note that the policy is designed to prefer setting the power and data
|
||||
// role independently rather than changing the mode.
|
||||
int supportedRoleCombinations = UsbPort.combineRolesAsBit(
|
||||
currentPowerRole, currentDataRole);
|
||||
if (currentMode != 0 && currentPowerRole != 0 && currentDataRole != 0) {
|
||||
if (canChangePowerRole && canChangeDataRole) {
|
||||
// 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;
|
||||
} else if (canChangePowerRole) {
|
||||
// Can only change power role.
|
||||
// Assume data role must remain at its current value.
|
||||
supportedRoleCombinations |= UsbPort.combineRolesAsBit(
|
||||
UsbPort.POWER_ROLE_SOURCE, currentDataRole);
|
||||
supportedRoleCombinations |= UsbPort.combineRolesAsBit(
|
||||
UsbPort.POWER_ROLE_SINK, currentDataRole);
|
||||
} else if (canChangeDataRole) {
|
||||
// Can only change data role.
|
||||
// Assume power role must remain at its current value.
|
||||
supportedRoleCombinations |= UsbPort.combineRolesAsBit(
|
||||
currentPowerRole, UsbPort.DATA_ROLE_HOST);
|
||||
supportedRoleCombinations |= UsbPort.combineRolesAsBit(
|
||||
currentPowerRole, UsbPort.DATA_ROLE_DEVICE);
|
||||
} else if (canChangeMode) {
|
||||
// Can only change the mode.
|
||||
// Assume both standard UFP and DFP configurations will become available
|
||||
// when this happens.
|
||||
supportedRoleCombinations |= COMBO_SOURCE_HOST | COMBO_SINK_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the port data structures.
|
||||
PortInfo portInfo = mPorts.get(portId);
|
||||
if (portInfo == null) {
|
||||
portInfo = new PortInfo(portId, supportedModes);
|
||||
portInfo.setStatus(currentMode, canChangeMode,
|
||||
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));
|
||||
}
|
||||
|
||||
if (portInfo.setStatus(currentMode, canChangeMode,
|
||||
currentPowerRole, canChangePowerRole,
|
||||
currentDataRole, canChangeDataRole,
|
||||
supportedRoleCombinations)) {
|
||||
portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
|
||||
} else {
|
||||
portInfo.mDisposition = PortInfo.DISPOSITION_READY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePortAddedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
|
||||
logAndPrint(Log.INFO, pw, "USB port added: " + portInfo);
|
||||
sendPortChangedBroadcastLocked(portInfo);
|
||||
}
|
||||
|
||||
private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
|
||||
logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo);
|
||||
sendPortChangedBroadcastLocked(portInfo);
|
||||
}
|
||||
|
||||
private void handlePortRemovedLocked(PortInfo portInfo, IndentingPrintWriter pw) {
|
||||
logAndPrint(Log.INFO, pw, "USB port removed: " + portInfo);
|
||||
sendPortChangedBroadcastLocked(portInfo);
|
||||
}
|
||||
|
||||
private void sendPortChangedBroadcastLocked(PortInfo portInfo) {
|
||||
final Intent intent = new Intent(UsbManager.ACTION_USB_PORT_CHANGED);
|
||||
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort);
|
||||
intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus);
|
||||
|
||||
// Guard against possible reentrance by posting the broadcast from the handler
|
||||
// instead of from within the critical section.
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 canChangeMode(File portDir) {
|
||||
return new File(portDir, SYSFS_PORT_MODE).canWrite();
|
||||
}
|
||||
|
||||
private static boolean canChangePowerRole(File portDir) {
|
||||
return new File(portDir, SYSFS_PORT_POWER_ROLE).canWrite();
|
||||
}
|
||||
|
||||
private static boolean canChangeDataRole(File portDir) {
|
||||
return new File(portDir, SYSFS_PORT_DATA_ROLE).canWrite();
|
||||
}
|
||||
|
||||
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 writeFile(File dir, String filename, String contents) {
|
||||
final File file = new File(dir, filename);
|
||||
try {
|
||||
try (FileWriter writer = new FileWriter(file)) {
|
||||
writer.write(contents);
|
||||
}
|
||||
return true;
|
||||
} catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
|
||||
Slog.println(priority, TAG, msg);
|
||||
if (pw != null) {
|
||||
pw.println(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private final Handler mHandler = new Handler(FgThread.get().getLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_PORTS: {
|
||||
synchronized (mLock) {
|
||||
updatePortsLocked(null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final UEventObserver mUEventObserver = new UEventObserver() {
|
||||
@Override
|
||||
public void onUEvent(UEvent event) {
|
||||
scheduleUpdatePorts();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes a USB port.
|
||||
*/
|
||||
private static final class PortInfo {
|
||||
public static final int DISPOSITION_ADDED = 0;
|
||||
public static final int DISPOSITION_CHANGED = 1;
|
||||
public static final int DISPOSITION_READY = 2;
|
||||
public static final int DISPOSITION_REMOVED = 3;
|
||||
|
||||
public final UsbPort mUsbPort;
|
||||
public UsbPortStatus mUsbPortStatus;
|
||||
public boolean mCanChangeMode;
|
||||
public boolean mCanChangePowerRole;
|
||||
public boolean mCanChangeDataRole;
|
||||
public int mDisposition; // default initialized to 0 which means added
|
||||
|
||||
public PortInfo(String portId, int supportedModes) {
|
||||
mUsbPort = new UsbPort(portId, supportedModes);
|
||||
}
|
||||
|
||||
public boolean setStatus(int currentMode, boolean canChangeMode,
|
||||
int currentPowerRole, boolean canChangePowerRole,
|
||||
int currentDataRole, boolean canChangeDataRole,
|
||||
int supportedRoleCombinations) {
|
||||
mCanChangeMode = canChangeMode;
|
||||
mCanChangePowerRole = canChangePowerRole;
|
||||
mCanChangeDataRole = canChangeDataRole;
|
||||
if (mUsbPortStatus == null
|
||||
|| mUsbPortStatus.getCurrentMode() != currentMode
|
||||
|| mUsbPortStatus.getCurrentPowerRole() != currentPowerRole
|
||||
|| mUsbPortStatus.getCurrentDataRole() != currentDataRole
|
||||
|| mUsbPortStatus.getSupportedRoleCombinations()
|
||||
!= supportedRoleCombinations) {
|
||||
mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
|
||||
supportedRoleCombinations);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "port=" + mUsbPort + ", status=" + mUsbPortStatus
|
||||
+ ", canChangeMode=" + mCanChangeMode
|
||||
+ ", canChangePowerRole=" + mCanChangePowerRole
|
||||
+ ", canChangeDataRole=" + mCanChangeDataRole;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a simulated USB port.
|
||||
* Roughly mirrors the information we would ordinarily get from the kernel.
|
||||
*/
|
||||
private static final class SimulatedPortInfo {
|
||||
public final String mPortId;
|
||||
public final int mSupportedModes;
|
||||
public int mCurrentMode;
|
||||
public boolean mCanChangeMode;
|
||||
public int mCurrentPowerRole;
|
||||
public boolean mCanChangePowerRole;
|
||||
public int mCurrentDataRole;
|
||||
public boolean mCanChangeDataRole;
|
||||
|
||||
public SimulatedPortInfo(String portId, int supportedModes) {
|
||||
mPortId = portId;
|
||||
mSupportedModes = supportedModes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,9 @@ import android.hardware.usb.IUsbManager;
|
||||
import android.hardware.usb.UsbAccessory;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.hardware.usb.UsbPort;
|
||||
import android.hardware.usb.UsbPortStatus;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.UserHandle;
|
||||
@@ -36,6 +39,7 @@ import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.SystemService;
|
||||
|
||||
import java.io.File;
|
||||
@@ -78,6 +82,7 @@ public class UsbService extends IUsbManager.Stub {
|
||||
|
||||
private UsbDeviceManager mDeviceManager;
|
||||
private UsbHostManager mHostManager;
|
||||
private UsbPortManager mPortManager;
|
||||
private final UsbAlsaManager mAlsaManager;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
@@ -110,6 +115,9 @@ public class UsbService extends IUsbManager.Stub {
|
||||
if (new File("/sys/class/android_usb").exists()) {
|
||||
mDeviceManager = new UsbDeviceManager(context, mAlsaManager);
|
||||
}
|
||||
if (mHostManager != null || mDeviceManager != null) {
|
||||
mPortManager = new UsbPortManager(context);
|
||||
}
|
||||
|
||||
setCurrentUser(UserHandle.USER_OWNER);
|
||||
|
||||
@@ -160,6 +168,9 @@ public class UsbService extends IUsbManager.Stub {
|
||||
if (mHostManager != null) {
|
||||
mHostManager.systemReady();
|
||||
}
|
||||
if (mPortManager != null) {
|
||||
mPortManager.systemReady();
|
||||
}
|
||||
}
|
||||
|
||||
public void bootCompleted() {
|
||||
@@ -345,30 +356,259 @@ public class UsbService extends IUsbManager.Stub {
|
||||
mDeviceManager.clearUsbDebuggingKeys();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbPort[] getPorts() {
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return mPortManager != null ? mPortManager.getPorts() : null;
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbPortStatus getPortStatus(String portId) {
|
||||
Preconditions.checkNotNull(portId, "portId must not be null");
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return mPortManager != null ? mPortManager.getPortStatus(portId) : null;
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPortRoles(String portId, int powerRole, int dataRole) {
|
||||
Preconditions.checkNotNull(portId, "portId must not be null");
|
||||
UsbPort.checkRoles(powerRole, dataRole);
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (mPortManager != null) {
|
||||
mPortManager.setPortRoles(portId, powerRole, dataRole, null);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
|
||||
|
||||
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
|
||||
pw.println("USB Manager State:");
|
||||
pw.increaseIndent();
|
||||
if (mDeviceManager != null) {
|
||||
mDeviceManager.dump(pw);
|
||||
}
|
||||
if (mHostManager != null) {
|
||||
mHostManager.dump(pw);
|
||||
}
|
||||
mAlsaManager.dump(pw);
|
||||
|
||||
synchronized (mLock) {
|
||||
for (int i = 0; i < mSettingsByUser.size(); i++) {
|
||||
final int userId = mSettingsByUser.keyAt(i);
|
||||
final UsbSettingsManager settings = mSettingsByUser.valueAt(i);
|
||||
pw.println("Settings for user " + userId + ":");
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
if (args == null || args.length == 0 || "-a".equals(args[0])) {
|
||||
pw.println("USB Manager State:");
|
||||
pw.increaseIndent();
|
||||
settings.dump(pw);
|
||||
pw.decreaseIndent();
|
||||
if (mDeviceManager != null) {
|
||||
mDeviceManager.dump(pw);
|
||||
}
|
||||
if (mHostManager != null) {
|
||||
mHostManager.dump(pw);
|
||||
}
|
||||
if (mPortManager != null) {
|
||||
mPortManager.dump(pw);
|
||||
}
|
||||
mAlsaManager.dump(pw);
|
||||
|
||||
synchronized (mLock) {
|
||||
for (int i = 0; i < mSettingsByUser.size(); i++) {
|
||||
final int userId = mSettingsByUser.keyAt(i);
|
||||
final UsbSettingsManager settings = mSettingsByUser.valueAt(i);
|
||||
pw.println("Settings for user " + userId + ":");
|
||||
pw.increaseIndent();
|
||||
settings.dump(pw);
|
||||
pw.decreaseIndent();
|
||||
}
|
||||
}
|
||||
} else if (args.length == 4 && "set-port-roles".equals(args[0])) {
|
||||
final String portId = args[1];
|
||||
final int powerRole;
|
||||
switch (args[2]) {
|
||||
case "source":
|
||||
powerRole = UsbPort.POWER_ROLE_SOURCE;
|
||||
break;
|
||||
case "sink":
|
||||
powerRole = UsbPort.POWER_ROLE_SINK;
|
||||
break;
|
||||
case "no-power":
|
||||
powerRole = 0;
|
||||
break;
|
||||
default:
|
||||
pw.println("Invalid power role: " + args[2]);
|
||||
return;
|
||||
}
|
||||
final int dataRole;
|
||||
switch (args[3]) {
|
||||
case "host":
|
||||
dataRole = UsbPort.DATA_ROLE_HOST;
|
||||
break;
|
||||
case "device":
|
||||
dataRole = UsbPort.DATA_ROLE_DEVICE;
|
||||
break;
|
||||
case "no-data":
|
||||
dataRole = 0;
|
||||
break;
|
||||
default:
|
||||
pw.println("Invalid data role: " + args[3]);
|
||||
return;
|
||||
}
|
||||
if (mPortManager != null) {
|
||||
mPortManager.setPortRoles(portId, powerRole, dataRole, pw);
|
||||
// Note: It might take some time for the side-effects of this operation
|
||||
// to be fully applied by the kernel since the driver may need to
|
||||
// renegotiate the USB port mode. If this proves to be an issue
|
||||
// during debugging, it might be worth adding a sleep here before
|
||||
// dumping the new state.
|
||||
pw.println();
|
||||
mPortManager.dump(pw);
|
||||
}
|
||||
} else if (args.length == 3 && "add-port".equals(args[0])) {
|
||||
final String portId = args[1];
|
||||
final int supportedModes;
|
||||
switch (args[2]) {
|
||||
case "ufp":
|
||||
supportedModes = UsbPort.MODE_UFP;
|
||||
break;
|
||||
case "dfp":
|
||||
supportedModes = UsbPort.MODE_DFP;
|
||||
break;
|
||||
case "dual":
|
||||
supportedModes = UsbPort.MODE_DUAL;
|
||||
break;
|
||||
case "none":
|
||||
supportedModes = 0;
|
||||
break;
|
||||
default:
|
||||
pw.println("Invalid mode: " + args[2]);
|
||||
return;
|
||||
}
|
||||
if (mPortManager != null) {
|
||||
mPortManager.addSimulatedPort(portId, supportedModes, pw);
|
||||
pw.println();
|
||||
mPortManager.dump(pw);
|
||||
}
|
||||
} else if (args.length == 5 && "connect-port".equals(args[0])) {
|
||||
final String portId = args[1];
|
||||
final int mode;
|
||||
final boolean canChangeMode = args[2].endsWith("?");
|
||||
switch (canChangeMode ? removeLastChar(args[2]) : args[2]) {
|
||||
case "ufp":
|
||||
mode = UsbPort.MODE_UFP;
|
||||
break;
|
||||
case "dfp":
|
||||
mode = UsbPort.MODE_DFP;
|
||||
break;
|
||||
default:
|
||||
pw.println("Invalid mode: " + args[2]);
|
||||
return;
|
||||
}
|
||||
final int powerRole;
|
||||
final boolean canChangePowerRole = args[3].endsWith("?");
|
||||
switch (canChangePowerRole ? removeLastChar(args[3]) : args[3]) {
|
||||
case "source":
|
||||
powerRole = UsbPort.POWER_ROLE_SOURCE;
|
||||
break;
|
||||
case "sink":
|
||||
powerRole = UsbPort.POWER_ROLE_SINK;
|
||||
break;
|
||||
default:
|
||||
pw.println("Invalid power role: " + args[3]);
|
||||
return;
|
||||
}
|
||||
final int dataRole;
|
||||
final boolean canChangeDataRole = args[4].endsWith("?");
|
||||
switch (canChangeDataRole ? removeLastChar(args[4]) : args[4]) {
|
||||
case "host":
|
||||
dataRole = UsbPort.DATA_ROLE_HOST;
|
||||
break;
|
||||
case "device":
|
||||
dataRole = UsbPort.DATA_ROLE_DEVICE;
|
||||
break;
|
||||
default:
|
||||
pw.println("Invalid data role: " + args[4]);
|
||||
return;
|
||||
}
|
||||
if (mPortManager != null) {
|
||||
mPortManager.connectSimulatedPort(portId, mode, canChangeMode,
|
||||
powerRole, canChangePowerRole, dataRole, canChangeDataRole, pw);
|
||||
pw.println();
|
||||
mPortManager.dump(pw);
|
||||
}
|
||||
} else if (args.length == 2 && "disconnect-port".equals(args[0])) {
|
||||
final String portId = args[1];
|
||||
if (mPortManager != null) {
|
||||
mPortManager.disconnectSimulatedPort(portId, pw);
|
||||
pw.println();
|
||||
mPortManager.dump(pw);
|
||||
}
|
||||
} else if (args.length == 2 && "remove-port".equals(args[0])) {
|
||||
final String portId = args[1];
|
||||
if (mPortManager != null) {
|
||||
mPortManager.removeSimulatedPort(portId, pw);
|
||||
pw.println();
|
||||
mPortManager.dump(pw);
|
||||
}
|
||||
} else if (args.length == 1 && "reset".equals(args[0])) {
|
||||
if (mPortManager != null) {
|
||||
mPortManager.resetSimulation(pw);
|
||||
pw.println();
|
||||
mPortManager.dump(pw);
|
||||
}
|
||||
} else if (args.length == 1 && "ports".equals(args[0])) {
|
||||
if (mPortManager != null) {
|
||||
mPortManager.dump(pw);
|
||||
}
|
||||
} else {
|
||||
pw.println("Dump current USB state or issue command:");
|
||||
pw.println(" ports");
|
||||
pw.println(" set-port-roles <id> <source|sink|no-power> <host|device|no-data>");
|
||||
pw.println(" add-port <id> <ufp|dfp|dual|none>");
|
||||
pw.println(" connect-port <id> <ufp|dfp><?> <source|sink><?> <host|device><?>");
|
||||
pw.println(" (add ? suffix if mode, power role, or data role can be changed)");
|
||||
pw.println(" disconnect-port <id>");
|
||||
pw.println(" remove-port <id>");
|
||||
pw.println(" reset");
|
||||
pw.println();
|
||||
pw.println("Example USB type C port role switch:");
|
||||
pw.println(" dumpsys usb set-port-roles \"default\" source device");
|
||||
pw.println();
|
||||
pw.println("Example USB type C port simulation with full capabilities:");
|
||||
pw.println(" dumpsys usb add-port \"matrix\" dual");
|
||||
pw.println(" dumpsys usb connect-port \"matrix\" ufp? sink? device?");
|
||||
pw.println(" dumpsys usb ports");
|
||||
pw.println(" dumpsys usb disconnect-port \"matrix\"");
|
||||
pw.println(" dumpsys usb remove-port \"matrix\"");
|
||||
pw.println(" dumpsys usb reset");
|
||||
pw.println();
|
||||
pw.println("Example USB type C port where only power role can be changed:");
|
||||
pw.println(" dumpsys usb add-port \"matrix\" dual");
|
||||
pw.println(" dumpsys usb connect-port \"matrix\" dfp source? host");
|
||||
pw.println(" dumpsys usb reset");
|
||||
pw.println();
|
||||
pw.println("Example USB OTG port where id pin determines function:");
|
||||
pw.println(" dumpsys usb add-port \"matrix\" dual");
|
||||
pw.println(" dumpsys usb connect-port \"matrix\" dfp source host");
|
||||
pw.println(" dumpsys usb reset");
|
||||
pw.println();
|
||||
pw.println("Example USB device-only port:");
|
||||
pw.println(" dumpsys usb add-port \"matrix\" ufp");
|
||||
pw.println(" dumpsys usb connect-port \"matrix\" ufp sink device");
|
||||
pw.println(" dumpsys usb reset");
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String removeLastChar(String value) {
|
||||
return value.substring(0, value.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user