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 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);
    }
  }