1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.usb; 18 19 import com.android.internal.util.IndentingPrintWriter; 20 import com.android.server.FgThread; 21 22 import android.content.Context; 23 import android.content.Intent; 24 import android.hardware.usb.UsbManager; 25 import android.hardware.usb.UsbPort; 26 import android.hardware.usb.UsbPortStatus; 27 import android.os.Handler; 28 import android.os.Message; 29 import android.os.SystemClock; 30 import android.os.SystemProperties; 31 import android.os.UEventObserver; 32 import android.os.UserHandle; 33 import android.system.ErrnoException; 34 import android.system.Os; 35 import android.system.OsConstants; 36 import android.util.ArrayMap; 37 import android.util.Log; 38 import android.util.Slog; 39 40 import java.io.File; 41 import java.io.FileWriter; 42 import java.io.IOException; 43 44 import libcore.io.IoUtils; 45 46 /** 47 * Allows trusted components to control the properties of physical USB ports 48 * via the "/sys/class/dual_role_usb" kernel interface. 49 * <p> 50 * Note: This interface may not be supported on all chipsets since the USB drivers 51 * must be changed to publish this information through the module. At the moment 52 * we only need this for devices with USB Type C ports to allow the System UI to 53 * control USB charging and data direction. On devices that do not support this 54 * interface the list of ports may incorrectly appear to be empty 55 * (but we don't care today). 56 * </p> 57 */ 58 public class UsbPortManager { 59 private static final String TAG = "UsbPortManager"; 60 61 private static final int MSG_UPDATE_PORTS = 1; 62 63 // UEvent path to watch. 64 private static final String UEVENT_FILTER = "SUBSYSTEM=dual_role_usb"; 65 66 // SysFS directory that contains USB ports as subdirectories. 67 private static final String SYSFS_CLASS = "/sys/class/dual_role_usb"; 68 69 // SysFS file that contains a USB port's supported modes. (read-only) 70 // Contents: "", "ufp", "dfp", or "ufp dfp". 71 private static final String SYSFS_PORT_SUPPORTED_MODES = "supported_modes"; 72 73 // SysFS file that contains a USB port's current mode. (read-write if configurable) 74 // Contents: "", "ufp", or "dfp". 75 private static final String SYSFS_PORT_MODE = "mode"; 76 77 // SysFS file that contains a USB port's current power role. (read-write if configurable) 78 // Contents: "", "source", or "sink". 79 private static final String SYSFS_PORT_POWER_ROLE = "power_role"; 80 81 // SysFS file that contains a USB port's current data role. (read-write if configurable) 82 // Contents: "", "host", or "device". 83 private static final String SYSFS_PORT_DATA_ROLE = "data_role"; 84 85 // Port modes: upstream facing port or downstream facing port. 86 private static final String PORT_MODE_DFP = "dfp"; 87 private static final String PORT_MODE_UFP = "ufp"; 88 89 // Port power roles: source or sink. 90 private static final String PORT_POWER_ROLE_SOURCE = "source"; 91 private static final String PORT_POWER_ROLE_SINK = "sink"; 92 93 // Port data roles: host or device. 94 private static final String PORT_DATA_ROLE_HOST = "host"; 95 private static final String PORT_DATA_ROLE_DEVICE = "device"; 96 97 private static final String USB_TYPEC_PROP_PREFIX = "sys.usb.typec."; 98 private static final String USB_TYPEC_STATE = "sys.usb.typec.state"; 99 100 // All non-trivial role combinations. 101 private static final int COMBO_SOURCE_HOST = 102 UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST); 103 private static final int COMBO_SOURCE_DEVICE = 104 UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE); 105 private static final int COMBO_SINK_HOST = 106 UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST); 107 private static final int COMBO_SINK_DEVICE = 108 UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE); 109 110 // The system context. 111 private final Context mContext; 112 113 // True if we have kernel support. 114 private final boolean mHaveKernelSupport; 115 116 // Mutex for all mutable shared state. 117 private final Object mLock = new Object(); 118 119 // List of all ports, indexed by id. 120 // Ports may temporarily have different dispositions as they are added or removed 121 // but the class invariant is that this list will only contain ports with DISPOSITION_READY 122 // except while updatePortsLocked() is in progress. 123 private final ArrayMap<String, PortInfo> mPorts = new ArrayMap<String, PortInfo>(); 124 125 // List of all simulated ports, indexed by id. 126 private final ArrayMap<String, SimulatedPortInfo> mSimulatedPorts = 127 new ArrayMap<String, SimulatedPortInfo>(); 128 129 public UsbPortManager(Context context) { 130 mContext = context; 131 mHaveKernelSupport = new File(SYSFS_CLASS).exists(); 132 } 133 134 public void systemReady() { 135 mUEventObserver.startObserving(UEVENT_FILTER); 136 scheduleUpdatePorts(); 137 } 138 139 public UsbPort[] getPorts() { 140 synchronized (mLock) { 141 final int count = mPorts.size(); 142 final UsbPort[] result = new UsbPort[count]; 143 for (int i = 0; i < count; i++) { 144 result[i] = mPorts.valueAt(i).mUsbPort; 145 } 146 return result; 147 } 148 } 149 150 public UsbPortStatus getPortStatus(String portId) { 151 synchronized (mLock) { 152 final PortInfo portInfo = mPorts.get(portId); 153 return portInfo != null ? portInfo.mUsbPortStatus : null; 154 } 155 } 156 157 public void setPortRoles(String portId, int newPowerRole, int newDataRole, 158 IndentingPrintWriter pw) { 159 synchronized (mLock) { 160 final PortInfo portInfo = mPorts.get(portId); 161 if (portInfo == null) { 162 if (pw != null) { 163 pw.println("No such USB port: " + portId); 164 } 165 return; 166 } 167 168 // Check whether the new role is actually supported. 169 if (!portInfo.mUsbPortStatus.isRoleCombinationSupported(newPowerRole, newDataRole)) { 170 logAndPrint(Log.ERROR, pw, "Attempted to set USB port into unsupported " 171 + "role combination: portId=" + portId 172 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 173 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 174 return; 175 } 176 177 // Check whether anything actually changed. 178 final int currentDataRole = portInfo.mUsbPortStatus.getCurrentDataRole(); 179 final int currentPowerRole = portInfo.mUsbPortStatus.getCurrentPowerRole(); 180 if (currentDataRole == newDataRole && currentPowerRole == newPowerRole) { 181 if (pw != null) { 182 pw.println("No change."); 183 } 184 return; 185 } 186 187 // Determine whether we need to change the mode in order to accomplish this goal. 188 // We prefer not to do this since it's more likely to fail. 189 // 190 // Note: Arguably it might be worth allowing the client to influence this policy 191 // decision so that we could show more powerful developer facing UI but let's 192 // see how far we can get without having to do that. 193 final boolean canChangeMode = portInfo.mCanChangeMode; 194 final boolean canChangePowerRole = portInfo.mCanChangePowerRole; 195 final boolean canChangeDataRole = portInfo.mCanChangeDataRole; 196 final int currentMode = portInfo.mUsbPortStatus.getCurrentMode(); 197 final int newMode; 198 if ((!canChangePowerRole && currentPowerRole != newPowerRole) 199 || (!canChangeDataRole && currentDataRole != newDataRole)) { 200 if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SOURCE 201 && newDataRole == UsbPort.DATA_ROLE_HOST) { 202 newMode = UsbPort.MODE_DFP; 203 } else if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SINK 204 && newDataRole == UsbPort.DATA_ROLE_DEVICE) { 205 newMode = UsbPort.MODE_UFP; 206 } else { 207 logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations " 208 + "while attempting to change role: " + portInfo 209 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 210 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 211 return; 212 } 213 } else { 214 newMode = currentMode; 215 } 216 217 // Make it happen. 218 logAndPrint(Log.INFO, pw, "Setting USB port mode and role: portId=" + portId 219 + ", currentMode=" + UsbPort.modeToString(currentMode) 220 + ", currentPowerRole=" + UsbPort.powerRoleToString(currentPowerRole) 221 + ", currentDataRole=" + UsbPort.dataRoleToString(currentDataRole) 222 + ", newMode=" + UsbPort.modeToString(newMode) 223 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 224 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 225 226 SimulatedPortInfo sim = mSimulatedPorts.get(portId); 227 if (sim != null) { 228 // Change simulated state. 229 sim.mCurrentMode = newMode; 230 sim.mCurrentPowerRole = newPowerRole; 231 sim.mCurrentDataRole = newDataRole; 232 } else if (mHaveKernelSupport) { 233 // Change actual state. 234 final File portDir = new File(SYSFS_CLASS, portId); 235 if (!portDir.exists()) { 236 logAndPrint(Log.ERROR, pw, "USB port not found: portId=" + portId); 237 return; 238 } 239 240 if (currentMode != newMode) { 241 // Changing the mode will have the side-effect of also changing 242 // the power and data roles but it might take some time to apply 243 // and the renegotiation might fail. Due to limitations of the USB 244 // hardware, we have no way of knowing whether it will work apriori 245 // which is why we would prefer to set the power and data roles 246 // directly instead. 247 if (!writeFile(portDir, SYSFS_PORT_MODE, 248 newMode == UsbPort.MODE_DFP ? PORT_MODE_DFP : PORT_MODE_UFP)) { 249 logAndPrint(Log.ERROR, pw, "Failed to set the USB port mode: " 250 + "portId=" + portId 251 + ", newMode=" + UsbPort.modeToString(newMode)); 252 return; 253 } 254 } else { 255 // Change power and data role independently as needed. 256 if (currentPowerRole != newPowerRole) { 257 if (!writeFile(portDir, SYSFS_PORT_POWER_ROLE, 258 newPowerRole == UsbPort.POWER_ROLE_SOURCE 259 ? PORT_POWER_ROLE_SOURCE : PORT_POWER_ROLE_SINK)) { 260 logAndPrint(Log.ERROR, pw, "Failed to set the USB port power role: " 261 + "portId=" + portId 262 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)); 263 return; 264 } 265 } 266 if (currentDataRole != newDataRole) { 267 if (!writeFile(portDir, SYSFS_PORT_DATA_ROLE, 268 newDataRole == UsbPort.DATA_ROLE_HOST 269 ? PORT_DATA_ROLE_HOST : PORT_DATA_ROLE_DEVICE)) { 270 logAndPrint(Log.ERROR, pw, "Failed to set the USB port data role: " 271 + "portId=" + portId 272 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 273 return; 274 } 275 } 276 } 277 } 278 updatePortsLocked(pw); 279 } 280 } 281 282 public void addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw) { 283 synchronized (mLock) { 284 if (mSimulatedPorts.containsKey(portId)) { 285 pw.println("Port with same name already exists. Please remove it first."); 286 return; 287 } 288 289 pw.println("Adding simulated port: portId=" + portId 290 + ", supportedModes=" + UsbPort.modeToString(supportedModes)); 291 mSimulatedPorts.put(portId, 292 new SimulatedPortInfo(portId, supportedModes)); 293 updatePortsLocked(pw); 294 } 295 } 296 297 public void connectSimulatedPort(String portId, int mode, boolean canChangeMode, 298 int powerRole, boolean canChangePowerRole, 299 int dataRole, boolean canChangeDataRole, IndentingPrintWriter pw) { 300 synchronized (mLock) { 301 final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId); 302 if (portInfo == null) { 303 pw.println("Cannot connect simulated port which does not exist."); 304 return; 305 } 306 307 if (mode == 0 || powerRole == 0 || dataRole == 0) { 308 pw.println("Cannot connect simulated port in null mode, " 309 + "power role, or data role."); 310 return; 311 } 312 313 if ((portInfo.mSupportedModes & mode) == 0) { 314 pw.println("Simulated port does not support mode: " + UsbPort.modeToString(mode)); 315 return; 316 } 317 318 pw.println("Connecting simulated port: portId=" + portId 319 + ", mode=" + UsbPort.modeToString(mode) 320 + ", canChangeMode=" + canChangeMode 321 + ", powerRole=" + UsbPort.powerRoleToString(powerRole) 322 + ", canChangePowerRole=" + canChangePowerRole 323 + ", dataRole=" + UsbPort.dataRoleToString(dataRole) 324 + ", canChangeDataRole=" + canChangeDataRole); 325 portInfo.mCurrentMode = mode; 326 portInfo.mCanChangeMode = canChangeMode; 327 portInfo.mCurrentPowerRole = powerRole; 328 portInfo.mCanChangePowerRole = canChangePowerRole; 329 portInfo.mCurrentDataRole = dataRole; 330 portInfo.mCanChangeDataRole = canChangeDataRole; 331 updatePortsLocked(pw); 332 } 333 } 334 335 public void disconnectSimulatedPort(String portId, IndentingPrintWriter pw) { 336 synchronized (mLock) { 337 final SimulatedPortInfo portInfo = mSimulatedPorts.get(portId); 338 if (portInfo == null) { 339 pw.println("Cannot disconnect simulated port which does not exist."); 340 return; 341 } 342 343 pw.println("Disconnecting simulated port: portId=" + portId); 344 portInfo.mCurrentMode = 0; 345 portInfo.mCanChangeMode = false; 346 portInfo.mCurrentPowerRole = 0; 347 portInfo.mCanChangePowerRole = false; 348 portInfo.mCurrentDataRole = 0; 349 portInfo.mCanChangeDataRole = false; 350 updatePortsLocked(pw); 351 } 352 } 353 354 public void removeSimulatedPort(String portId, IndentingPrintWriter pw) { 355 synchronized (mLock) { 356 final int index = mSimulatedPorts.indexOfKey(portId); 357 if (index < 0) { 358 pw.println("Cannot remove simulated port which does not exist."); 359 return; 360 } 361 362 pw.println("Disconnecting simulated port: portId=" + portId); 363 mSimulatedPorts.removeAt(index); 364 updatePortsLocked(pw); 365 } 366 } 367 368 public void resetSimulation(IndentingPrintWriter pw) { 369 synchronized (mLock) { 370 pw.println("Removing all simulated ports and ending simulation."); 371 if (!mSimulatedPorts.isEmpty()) { 372 mSimulatedPorts.clear(); 373 updatePortsLocked(pw); 374 } 375 } 376 } 377 378 public void dump(IndentingPrintWriter pw) { 379 synchronized (mLock) { 380 pw.print("USB Port State:"); 381 if (!mSimulatedPorts.isEmpty()) { 382 pw.print(" (simulation active; end with 'dumpsys usb reset')"); 383 } 384 pw.println(); 385 386 if (mPorts.isEmpty()) { 387 pw.println(" <no ports>"); 388 } else { 389 for (PortInfo portInfo : mPorts.values()) { 390 pw.println(" " + portInfo.mUsbPort.getId() + ": " + portInfo); 391 } 392 } 393 } 394 } 395 396 private void updatePortsLocked(IndentingPrintWriter pw) { 397 // Assume all ports are gone unless informed otherwise. 398 // Kind of pessimistic but simple. 399 for (int i = mPorts.size(); i-- > 0; ) { 400 mPorts.valueAt(i).mDisposition = PortInfo.DISPOSITION_REMOVED; 401 } 402 403 // Enumerate all extant ports. 404 if (!mSimulatedPorts.isEmpty()) { 405 final int count = mSimulatedPorts.size(); 406 for (int i = 0; i < count; i++) { 407 final SimulatedPortInfo portInfo = mSimulatedPorts.valueAt(i); 408 addOrUpdatePortLocked(portInfo.mPortId, portInfo.mSupportedModes, 409 portInfo.mCurrentMode, portInfo.mCanChangeMode, 410 portInfo.mCurrentPowerRole, portInfo.mCanChangePowerRole, 411 portInfo.mCurrentDataRole, portInfo.mCanChangeDataRole, pw); 412 } 413 } else if (mHaveKernelSupport) { 414 final File[] portDirs = new File(SYSFS_CLASS).listFiles(); 415 if (portDirs != null) { 416 for (File portDir : portDirs) { 417 if (!portDir.isDirectory()) { 418 continue; 419 } 420 421 // Parse the sysfs file contents. 422 final String portId = portDir.getName(); 423 final int supportedModes = readSupportedModes(portDir); 424 final int currentMode = readCurrentMode(portDir); 425 final boolean canChangeMode = canChangeMode(portDir); 426 final int currentPowerRole = readCurrentPowerRole(portDir); 427 final boolean canChangePowerRole = canChangePowerRole(portDir); 428 final int currentDataRole = readCurrentDataRole(portDir); 429 final boolean canChangeDataRole = canChangeDataRole(portDir); 430 addOrUpdatePortLocked(portId, supportedModes, 431 currentMode, canChangeMode, 432 currentPowerRole, canChangePowerRole, 433 currentDataRole, canChangeDataRole, pw); 434 } 435 } 436 } 437 438 // Process the updates. 439 // Once finished, the list of ports will only contain ports in DISPOSITION_READY. 440 for (int i = mPorts.size(); i-- > 0; ) { 441 final PortInfo portInfo = mPorts.valueAt(i); 442 switch (portInfo.mDisposition) { 443 case PortInfo.DISPOSITION_ADDED: 444 handlePortAddedLocked(portInfo, pw); 445 portInfo.mDisposition = PortInfo.DISPOSITION_READY; 446 break; 447 case PortInfo.DISPOSITION_CHANGED: 448 handlePortChangedLocked(portInfo, pw); 449 portInfo.mDisposition = PortInfo.DISPOSITION_READY; 450 break; 451 case PortInfo.DISPOSITION_REMOVED: 452 mPorts.removeAt(i); 453 portInfo.mUsbPortStatus = null; // must do this early 454 handlePortRemovedLocked(portInfo, pw); 455 break; 456 } 457 } 458 } 459 460 // Must only be called by updatePortsLocked. 461 private void addOrUpdatePortLocked(String portId, int supportedModes, 462 int currentMode, boolean canChangeMode, 463 int currentPowerRole, boolean canChangePowerRole, 464 int currentDataRole, boolean canChangeDataRole, 465 IndentingPrintWriter pw) { 466 // Only allow mode switch capability for dual role ports. 467 // Validate that the current mode matches the supported modes we expect. 468 if (supportedModes != UsbPort.MODE_DUAL) { 469 canChangeMode = false; 470 if (currentMode != 0 && currentMode != supportedModes) { 471 logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB " 472 + "port driver: supportedModes=" + UsbPort.modeToString(supportedModes) 473 + ", currentMode=" + UsbPort.modeToString(currentMode)); 474 currentMode = 0; 475 } 476 } 477 478 // Determine the supported role combinations. 479 // Note that the policy is designed to prefer setting the power and data 480 // role independently rather than changing the mode. 481 int supportedRoleCombinations = UsbPort.combineRolesAsBit( 482 currentPowerRole, currentDataRole); 483 if (currentMode != 0 && currentPowerRole != 0 && currentDataRole != 0) { 484 if (canChangePowerRole && canChangeDataRole) { 485 // Can change both power and data role independently. 486 // Assume all combinations are possible. 487 supportedRoleCombinations |= 488 COMBO_SOURCE_HOST | COMBO_SOURCE_DEVICE 489 | COMBO_SINK_HOST | COMBO_SINK_DEVICE; 490 } else if (canChangePowerRole) { 491 // Can only change power role. 492 // Assume data role must remain at its current value. 493 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 494 UsbPort.POWER_ROLE_SOURCE, currentDataRole); 495 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 496 UsbPort.POWER_ROLE_SINK, currentDataRole); 497 } else if (canChangeDataRole) { 498 // Can only change data role. 499 // Assume power role must remain at its current value. 500 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 501 currentPowerRole, UsbPort.DATA_ROLE_HOST); 502 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 503 currentPowerRole, UsbPort.DATA_ROLE_DEVICE); 504 } else if (canChangeMode) { 505 // Can only change the mode. 506 // Assume both standard UFP and DFP configurations will become available 507 // when this happens. 508 supportedRoleCombinations |= COMBO_SOURCE_HOST | COMBO_SINK_DEVICE; 509 } 510 } 511 512 // Update the port data structures. 513 PortInfo portInfo = mPorts.get(portId); 514 if (portInfo == null) { 515 portInfo = new PortInfo(portId, supportedModes); 516 portInfo.setStatus(currentMode, canChangeMode, 517 currentPowerRole, canChangePowerRole, 518 currentDataRole, canChangeDataRole, 519 supportedRoleCombinations); 520 mPorts.put(portId, portInfo); 521 } else { 522 // Sanity check that ports aren't changing definition out from under us. 523 if (supportedModes != portInfo.mUsbPort.getSupportedModes()) { 524 logAndPrint(Log.WARN, pw, "Ignoring inconsistent list of supported modes from " 525 + "USB port driver (should be immutable): " 526 + "previous=" + UsbPort.modeToString( 527 portInfo.mUsbPort.getSupportedModes()) 528 + ", current=" + UsbPort.modeToString(supportedModes)); 529 } 530 531 if (portInfo.setStatus(currentMode, canChangeMode, 532 currentPowerRole, canChangePowerRole, 533 currentDataRole, canChangeDataRole, 534 supportedRoleCombinations)) { 535 portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED; 536 } else { 537 portInfo.mDisposition = PortInfo.DISPOSITION_READY; 538 } 539 } 540 } 541 542 private void handlePortAddedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 543 logAndPrint(Log.INFO, pw, "USB port added: " + portInfo); 544 sendPortChangedBroadcastLocked(portInfo); 545 } 546 547 private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 548 logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo); 549 sendPortChangedBroadcastLocked(portInfo); 550 } 551 552 private void handlePortRemovedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 553 logAndPrint(Log.INFO, pw, "USB port removed: " + portInfo); 554 sendPortChangedBroadcastLocked(portInfo); 555 } 556 557 private void sendPortChangedBroadcastLocked(PortInfo portInfo) { 558 final Intent intent = new Intent(UsbManager.ACTION_USB_PORT_CHANGED); 559 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 560 intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort); 561 intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus); 562 563 // Guard against possible reentrance by posting the broadcast from the handler 564 // instead of from within the critical section. 565 mHandler.post(new Runnable() { 566 @Override 567 public void run() { 568 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 569 } 570 }); 571 } 572 573 private void scheduleUpdatePorts() { 574 if (!mHandler.hasMessages(MSG_UPDATE_PORTS)) { 575 mHandler.sendEmptyMessage(MSG_UPDATE_PORTS); 576 } 577 } 578 579 private static int readSupportedModes(File portDir) { 580 int modes = 0; 581 final String contents = readFile(portDir, SYSFS_PORT_SUPPORTED_MODES); 582 if (contents != null) { 583 if (contents.contains(PORT_MODE_DFP)) { 584 modes |= UsbPort.MODE_DFP; 585 } 586 if (contents.contains(PORT_MODE_UFP)) { 587 modes |= UsbPort.MODE_UFP; 588 } 589 } 590 return modes; 591 } 592 593 private static int readCurrentMode(File portDir) { 594 final String contents = readFile(portDir, SYSFS_PORT_MODE); 595 if (contents != null) { 596 if (contents.equals(PORT_MODE_DFP)) { 597 return UsbPort.MODE_DFP; 598 } 599 if (contents.equals(PORT_MODE_UFP)) { 600 return UsbPort.MODE_UFP; 601 } 602 } 603 return 0; 604 } 605 606 private static int readCurrentPowerRole(File portDir) { 607 final String contents = readFile(portDir, SYSFS_PORT_POWER_ROLE); 608 if (contents != null) { 609 if (contents.equals(PORT_POWER_ROLE_SOURCE)) { 610 return UsbPort.POWER_ROLE_SOURCE; 611 } 612 if (contents.equals(PORT_POWER_ROLE_SINK)) { 613 return UsbPort.POWER_ROLE_SINK; 614 } 615 } 616 return 0; 617 } 618 619 private static int readCurrentDataRole(File portDir) { 620 final String contents = readFile(portDir, SYSFS_PORT_DATA_ROLE); 621 if (contents != null) { 622 if (contents.equals(PORT_DATA_ROLE_HOST)) { 623 return UsbPort.DATA_ROLE_HOST; 624 } 625 if (contents.equals(PORT_DATA_ROLE_DEVICE)) { 626 return UsbPort.DATA_ROLE_DEVICE; 627 } 628 } 629 return 0; 630 } 631 632 private static boolean fileIsRootWritable(String path) { 633 try { 634 // If the file is user writable, then it is root writable. 635 return (Os.stat(path).st_mode & OsConstants.S_IWUSR) != 0; 636 } catch (ErrnoException e) { 637 return false; 638 } 639 } 640 641 private static boolean canChangeMode(File portDir) { 642 return fileIsRootWritable(new File(portDir, SYSFS_PORT_MODE).getPath()); 643 } 644 645 private static boolean canChangePowerRole(File portDir) { 646 return fileIsRootWritable(new File(portDir, SYSFS_PORT_POWER_ROLE).getPath()); 647 } 648 649 private static boolean canChangeDataRole(File portDir) { 650 return fileIsRootWritable(new File(portDir, SYSFS_PORT_DATA_ROLE).getPath()); 651 } 652 653 private static String readFile(File dir, String filename) { 654 final File file = new File(dir, filename); 655 try { 656 return IoUtils.readFileAsString(file.getAbsolutePath()).trim(); 657 } catch (IOException ex) { 658 return null; 659 } 660 } 661 662 private static boolean waitForState(String property, String state) { 663 // wait for the transition to complete. 664 // give up after 5 seconds. 665 // 5 seconds is probably too long, but we have seen hardware that takes 666 // over 3 seconds to change states. 667 String value = null; 668 for (int i = 0; i < 100; i++) { 669 // State transition is done when property is set to the new configuration 670 value = SystemProperties.get(property); 671 if (state.equals(value)) return true; 672 SystemClock.sleep(50); 673 } 674 Slog.e(TAG, "waitForState(" + state + ") for " + property + " FAILED: got " + value); 675 return false; 676 } 677 678 private static String propertyFromFilename(String filename) { 679 return USB_TYPEC_PROP_PREFIX + filename; 680 } 681 682 private static boolean writeFile(File dir, String filename, String contents) { 683 SystemProperties.set(propertyFromFilename(filename), contents); 684 return waitForState(USB_TYPEC_STATE, contents); 685 } 686 687 private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) { 688 Slog.println(priority, TAG, msg); 689 if (pw != null) { 690 pw.println(msg); 691 } 692 } 693 694 private final Handler mHandler = new Handler(FgThread.get().getLooper()) { 695 @Override 696 public void handleMessage(Message msg) { 697 switch (msg.what) { 698 case MSG_UPDATE_PORTS: { 699 synchronized (mLock) { 700 updatePortsLocked(null); 701 } 702 break; 703 } 704 } 705 } 706 }; 707 708 private final UEventObserver mUEventObserver = new UEventObserver() { 709 @Override 710 public void onUEvent(UEvent event) { 711 scheduleUpdatePorts(); 712 } 713 }; 714 715 /** 716 * Describes a USB port. 717 */ 718 private static final class PortInfo { 719 public static final int DISPOSITION_ADDED = 0; 720 public static final int DISPOSITION_CHANGED = 1; 721 public static final int DISPOSITION_READY = 2; 722 public static final int DISPOSITION_REMOVED = 3; 723 724 public final UsbPort mUsbPort; 725 public UsbPortStatus mUsbPortStatus; 726 public boolean mCanChangeMode; 727 public boolean mCanChangePowerRole; 728 public boolean mCanChangeDataRole; 729 public int mDisposition; // default initialized to 0 which means added 730 731 public PortInfo(String portId, int supportedModes) { 732 mUsbPort = new UsbPort(portId, supportedModes); 733 } 734 735 public boolean setStatus(int currentMode, boolean canChangeMode, 736 int currentPowerRole, boolean canChangePowerRole, 737 int currentDataRole, boolean canChangeDataRole, 738 int supportedRoleCombinations) { 739 mCanChangeMode = canChangeMode; 740 mCanChangePowerRole = canChangePowerRole; 741 mCanChangeDataRole = canChangeDataRole; 742 if (mUsbPortStatus == null 743 || mUsbPortStatus.getCurrentMode() != currentMode 744 || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole 745 || mUsbPortStatus.getCurrentDataRole() != currentDataRole 746 || mUsbPortStatus.getSupportedRoleCombinations() 747 != supportedRoleCombinations) { 748 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, 749 supportedRoleCombinations); 750 return true; 751 } 752 return false; 753 } 754 755 @Override 756 public String toString() { 757 return "port=" + mUsbPort + ", status=" + mUsbPortStatus 758 + ", canChangeMode=" + mCanChangeMode 759 + ", canChangePowerRole=" + mCanChangePowerRole 760 + ", canChangeDataRole=" + mCanChangeDataRole; 761 } 762 } 763 764 /** 765 * Describes a simulated USB port. 766 * Roughly mirrors the information we would ordinarily get from the kernel. 767 */ 768 private static final class SimulatedPortInfo { 769 public final String mPortId; 770 public final int mSupportedModes; 771 public int mCurrentMode; 772 public boolean mCanChangeMode; 773 public int mCurrentPowerRole; 774 public boolean mCanChangePowerRole; 775 public int mCurrentDataRole; 776 public boolean mCanChangeDataRole; 777 778 public SimulatedPortInfo(String portId, int supportedModes) { 779 mPortId = portId; 780 mSupportedModes = supportedModes; 781 } 782 } 783 } 784