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