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 android.content.Context; 20 import android.content.Intent; 21 import android.hardware.usb.UsbManager; 22 import android.hardware.usb.UsbPort; 23 import android.hardware.usb.UsbPortStatus; 24 import android.hardware.usb.V1_0.IUsb; 25 import android.hardware.usb.V1_0.PortRole; 26 import android.hardware.usb.V1_0.PortRoleType; 27 import android.hardware.usb.V1_0.PortStatus; 28 import android.hardware.usb.V1_0.Status; 29 import android.hardware.usb.V1_1.IUsbCallback; 30 import android.hardware.usb.V1_1.PortStatus_1_1; 31 import android.hidl.manager.V1_0.IServiceManager; 32 import android.hidl.manager.V1_0.IServiceNotification; 33 import android.os.Bundle; 34 import android.os.Handler; 35 import android.os.HwBinder; 36 import android.os.Message; 37 import android.os.Parcel; 38 import android.os.Parcelable; 39 import android.os.RemoteException; 40 import android.os.UserHandle; 41 import android.util.ArrayMap; 42 import android.util.Log; 43 import android.util.Slog; 44 45 import com.android.internal.annotations.GuardedBy; 46 import com.android.internal.util.IndentingPrintWriter; 47 import com.android.server.FgThread; 48 49 import java.util.ArrayList; 50 import java.util.NoSuchElementException; 51 52 /** 53 * Allows trusted components to control the properties of physical USB ports 54 * via the IUsb.hal. 55 * <p> 56 * Note: This interface may not be supported on all chipsets since the USB drivers 57 * must be changed to publish this information through the module. At the moment 58 * we only need this for devices with USB Type C ports to allow the System UI to 59 * control USB charging and data direction. On devices that do not support this 60 * interface the list of ports may incorrectly appear to be empty 61 * (but we don't care today). 62 * </p> 63 */ 64 public class UsbPortManager { 65 private static final String TAG = "UsbPortManager"; 66 67 private static final int MSG_UPDATE_PORTS = 1; 68 69 // All non-trivial role combinations. 70 private static final int COMBO_SOURCE_HOST = 71 UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST); 72 private static final int COMBO_SOURCE_DEVICE = 73 UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_DEVICE); 74 private static final int COMBO_SINK_HOST = 75 UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_HOST); 76 private static final int COMBO_SINK_DEVICE = 77 UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SINK, UsbPort.DATA_ROLE_DEVICE); 78 79 // The system context. 80 private final Context mContext; 81 82 // Proxy object for the usb hal daemon. 83 @GuardedBy("mLock") 84 private IUsb mProxy = null; 85 86 // Callback when the UsbPort status is changed by the kernel. 87 // Mostly due a command sent by the remote Usb device. 88 private HALCallback mHALCallback = new HALCallback(null, this); 89 90 // Cookie sent for usb hal death notification. 91 private static final int USB_HAL_DEATH_COOKIE = 1000; 92 93 // Used as the key while sending the bundle to Main thread. 94 private static final String PORT_INFO = "port_info"; 95 96 // This is monitored to prevent updating the protInfo before the system 97 // is ready. 98 private boolean mSystemReady; 99 100 // Mutex for all mutable shared state. 101 private final Object mLock = new Object(); 102 103 // List of all ports, indexed by id. 104 // Ports may temporarily have different dispositions as they are added or removed 105 // but the class invariant is that this list will only contain ports with DISPOSITION_READY 106 // except while updatePortsLocked() is in progress. 107 private final ArrayMap<String, PortInfo> mPorts = new ArrayMap<>(); 108 109 // List of all simulated ports, indexed by id. 110 private final ArrayMap<String, RawPortInfo> mSimulatedPorts = 111 new ArrayMap<>(); 112 113 public UsbPortManager(Context context) { 114 mContext = context; 115 try { 116 ServiceNotification serviceNotification = new ServiceNotification(); 117 118 boolean ret = IServiceManager.getService() 119 .registerForNotifications("android.hardware.usb (at) 1.0::IUsb", 120 "", serviceNotification); 121 if (!ret) { 122 logAndPrint(Log.ERROR, null, 123 "Failed to register service start notification"); 124 } 125 } catch (RemoteException e) { 126 logAndPrintException(null, 127 "Failed to register service start notification", e); 128 return; 129 } 130 connectToProxy(null); 131 } 132 133 public void systemReady() { 134 if (mProxy != null) { 135 try { 136 mProxy.queryPortStatus(); 137 } catch (RemoteException e) { 138 logAndPrintException(null, 139 "ServiceStart: Failed to query port status", e); 140 } 141 } 142 mSystemReady = true; 143 } 144 145 public UsbPort[] getPorts() { 146 synchronized (mLock) { 147 final int count = mPorts.size(); 148 final UsbPort[] result = new UsbPort[count]; 149 for (int i = 0; i < count; i++) { 150 result[i] = mPorts.valueAt(i).mUsbPort; 151 } 152 return result; 153 } 154 } 155 156 public UsbPortStatus getPortStatus(String portId) { 157 synchronized (mLock) { 158 final PortInfo portInfo = mPorts.get(portId); 159 return portInfo != null ? portInfo.mUsbPortStatus : null; 160 } 161 } 162 163 public void setPortRoles(String portId, int newPowerRole, int newDataRole, 164 IndentingPrintWriter pw) { 165 synchronized (mLock) { 166 final PortInfo portInfo = mPorts.get(portId); 167 if (portInfo == null) { 168 if (pw != null) { 169 pw.println("No such USB port: " + portId); 170 } 171 return; 172 } 173 174 // Check whether the new role is actually supported. 175 if (!portInfo.mUsbPortStatus.isRoleCombinationSupported(newPowerRole, newDataRole)) { 176 logAndPrint(Log.ERROR, pw, "Attempted to set USB port into unsupported " 177 + "role combination: portId=" + portId 178 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 179 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 180 return; 181 } 182 183 // Check whether anything actually changed. 184 final int currentDataRole = portInfo.mUsbPortStatus.getCurrentDataRole(); 185 final int currentPowerRole = portInfo.mUsbPortStatus.getCurrentPowerRole(); 186 if (currentDataRole == newDataRole && currentPowerRole == newPowerRole) { 187 if (pw != null) { 188 pw.println("No change."); 189 } 190 return; 191 } 192 193 // Determine whether we need to change the mode in order to accomplish this goal. 194 // We prefer not to do this since it's more likely to fail. 195 // 196 // Note: Arguably it might be worth allowing the client to influence this policy 197 // decision so that we could show more powerful developer facing UI but let's 198 // see how far we can get without having to do that. 199 final boolean canChangeMode = portInfo.mCanChangeMode; 200 final boolean canChangePowerRole = portInfo.mCanChangePowerRole; 201 final boolean canChangeDataRole = portInfo.mCanChangeDataRole; 202 final int currentMode = portInfo.mUsbPortStatus.getCurrentMode(); 203 final int newMode; 204 if ((!canChangePowerRole && currentPowerRole != newPowerRole) 205 || (!canChangeDataRole && currentDataRole != newDataRole)) { 206 if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SOURCE 207 && newDataRole == UsbPort.DATA_ROLE_HOST) { 208 newMode = UsbPort.MODE_DFP; 209 } else if (canChangeMode && newPowerRole == UsbPort.POWER_ROLE_SINK 210 && newDataRole == UsbPort.DATA_ROLE_DEVICE) { 211 newMode = UsbPort.MODE_UFP; 212 } else { 213 logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations " 214 + "while attempting to change role: " + portInfo 215 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 216 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 217 return; 218 } 219 } else { 220 newMode = currentMode; 221 } 222 223 // Make it happen. 224 logAndPrint(Log.INFO, pw, "Setting USB port mode and role: portId=" + portId 225 + ", currentMode=" + UsbPort.modeToString(currentMode) 226 + ", currentPowerRole=" + UsbPort.powerRoleToString(currentPowerRole) 227 + ", currentDataRole=" + UsbPort.dataRoleToString(currentDataRole) 228 + ", newMode=" + UsbPort.modeToString(newMode) 229 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 230 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 231 232 RawPortInfo sim = mSimulatedPorts.get(portId); 233 if (sim != null) { 234 // Change simulated state. 235 sim.currentMode = newMode; 236 sim.currentPowerRole = newPowerRole; 237 sim.currentDataRole = newDataRole; 238 updatePortsLocked(pw, null); 239 } else if (mProxy != null) { 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 248 logAndPrint(Log.ERROR, pw, "Trying to set the USB port mode: " 249 + "portId=" + portId 250 + ", newMode=" + UsbPort.modeToString(newMode)); 251 PortRole newRole = new PortRole(); 252 newRole.type = PortRoleType.MODE; 253 newRole.role = newMode; 254 try { 255 mProxy.switchRole(portId, newRole); 256 } catch (RemoteException e) { 257 logAndPrintException(pw, "Failed to set the USB port mode: " 258 + "portId=" + portId 259 + ", newMode=" + UsbPort.modeToString(newRole.role), e); 260 } 261 } else { 262 // Change power and data role independently as needed. 263 if (currentPowerRole != newPowerRole) { 264 PortRole newRole = new PortRole(); 265 newRole.type = PortRoleType.POWER_ROLE; 266 newRole.role = newPowerRole; 267 try { 268 mProxy.switchRole(portId, newRole); 269 } catch (RemoteException e) { 270 logAndPrintException(pw, "Failed to set the USB port power role: " 271 + "portId=" + portId 272 + ", newPowerRole=" + UsbPort.powerRoleToString 273 (newRole.role), 274 e); 275 return; 276 } 277 } 278 if (currentDataRole != newDataRole) { 279 PortRole newRole = new PortRole(); 280 newRole.type = PortRoleType.DATA_ROLE; 281 newRole.role = newDataRole; 282 try { 283 mProxy.switchRole(portId, newRole); 284 } catch (RemoteException e) { 285 logAndPrintException(pw, "Failed to set the USB port data role: " 286 + "portId=" + portId 287 + ", newDataRole=" + UsbPort.dataRoleToString(newRole 288 .role), 289 e); 290 } 291 } 292 } 293 } 294 } 295 } 296 297 public void addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw) { 298 synchronized (mLock) { 299 if (mSimulatedPorts.containsKey(portId)) { 300 pw.println("Port with same name already exists. Please remove it first."); 301 return; 302 } 303 304 pw.println("Adding simulated port: portId=" + portId 305 + ", supportedModes=" + UsbPort.modeToString(supportedModes)); 306 mSimulatedPorts.put(portId, 307 new RawPortInfo(portId, supportedModes)); 308 updatePortsLocked(pw, null); 309 } 310 } 311 312 public void connectSimulatedPort(String portId, int mode, boolean canChangeMode, 313 int powerRole, boolean canChangePowerRole, 314 int dataRole, boolean canChangeDataRole, IndentingPrintWriter pw) { 315 synchronized (mLock) { 316 final RawPortInfo portInfo = mSimulatedPorts.get(portId); 317 if (portInfo == null) { 318 pw.println("Cannot connect simulated port which does not exist."); 319 return; 320 } 321 322 if (mode == 0 || powerRole == 0 || dataRole == 0) { 323 pw.println("Cannot connect simulated port in null mode, " 324 + "power role, or data role."); 325 return; 326 } 327 328 if ((portInfo.supportedModes & mode) == 0) { 329 pw.println("Simulated port does not support mode: " + UsbPort.modeToString(mode)); 330 return; 331 } 332 333 pw.println("Connecting simulated port: portId=" + portId 334 + ", mode=" + UsbPort.modeToString(mode) 335 + ", canChangeMode=" + canChangeMode 336 + ", powerRole=" + UsbPort.powerRoleToString(powerRole) 337 + ", canChangePowerRole=" + canChangePowerRole 338 + ", dataRole=" + UsbPort.dataRoleToString(dataRole) 339 + ", canChangeDataRole=" + canChangeDataRole); 340 portInfo.currentMode = mode; 341 portInfo.canChangeMode = canChangeMode; 342 portInfo.currentPowerRole = powerRole; 343 portInfo.canChangePowerRole = canChangePowerRole; 344 portInfo.currentDataRole = dataRole; 345 portInfo.canChangeDataRole = canChangeDataRole; 346 updatePortsLocked(pw, null); 347 } 348 } 349 350 public void disconnectSimulatedPort(String portId, IndentingPrintWriter pw) { 351 synchronized (mLock) { 352 final RawPortInfo portInfo = mSimulatedPorts.get(portId); 353 if (portInfo == null) { 354 pw.println("Cannot disconnect simulated port which does not exist."); 355 return; 356 } 357 358 pw.println("Disconnecting simulated port: portId=" + portId); 359 portInfo.currentMode = 0; 360 portInfo.canChangeMode = false; 361 portInfo.currentPowerRole = 0; 362 portInfo.canChangePowerRole = false; 363 portInfo.currentDataRole = 0; 364 portInfo.canChangeDataRole = false; 365 updatePortsLocked(pw, null); 366 } 367 } 368 369 public void removeSimulatedPort(String portId, IndentingPrintWriter pw) { 370 synchronized (mLock) { 371 final int index = mSimulatedPorts.indexOfKey(portId); 372 if (index < 0) { 373 pw.println("Cannot remove simulated port which does not exist."); 374 return; 375 } 376 377 pw.println("Disconnecting simulated port: portId=" + portId); 378 mSimulatedPorts.removeAt(index); 379 updatePortsLocked(pw, null); 380 } 381 } 382 383 public void resetSimulation(IndentingPrintWriter pw) { 384 synchronized (mLock) { 385 pw.println("Removing all simulated ports and ending simulation."); 386 if (!mSimulatedPorts.isEmpty()) { 387 mSimulatedPorts.clear(); 388 updatePortsLocked(pw, null); 389 } 390 } 391 } 392 393 public void dump(IndentingPrintWriter pw) { 394 synchronized (mLock) { 395 pw.print("USB Port State:"); 396 if (!mSimulatedPorts.isEmpty()) { 397 pw.print(" (simulation active; end with 'dumpsys usb reset')"); 398 } 399 pw.println(); 400 401 if (mPorts.isEmpty()) { 402 pw.println(" <no ports>"); 403 } else { 404 for (PortInfo portInfo : mPorts.values()) { 405 pw.println(" " + portInfo.mUsbPort.getId() + ": " + portInfo); 406 } 407 } 408 } 409 } 410 411 private static class HALCallback extends IUsbCallback.Stub { 412 public IndentingPrintWriter pw; 413 public UsbPortManager portManager; 414 415 HALCallback(IndentingPrintWriter pw, UsbPortManager portManager) { 416 this.pw = pw; 417 this.portManager = portManager; 418 } 419 420 public void notifyPortStatusChange(ArrayList<PortStatus> currentPortStatus, int retval) { 421 if (!portManager.mSystemReady) { 422 return; 423 } 424 425 if (retval != Status.SUCCESS) { 426 logAndPrint(Log.ERROR, pw, "port status enquiry failed"); 427 return; 428 } 429 430 ArrayList<RawPortInfo> newPortInfo = new ArrayList<>(); 431 432 for (PortStatus current : currentPortStatus) { 433 RawPortInfo temp = new RawPortInfo(current.portName, 434 current.supportedModes, current.currentMode, 435 current.canChangeMode, current.currentPowerRole, 436 current.canChangePowerRole, 437 current.currentDataRole, current.canChangeDataRole); 438 newPortInfo.add(temp); 439 logAndPrint(Log.INFO, pw, "ClientCallback: " + current.portName); 440 } 441 442 Message message = portManager.mHandler.obtainMessage(); 443 Bundle bundle = new Bundle(); 444 bundle.putParcelableArrayList(PORT_INFO, newPortInfo); 445 message.what = MSG_UPDATE_PORTS; 446 message.setData(bundle); 447 portManager.mHandler.sendMessage(message); 448 } 449 450 451 public void notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus, 452 int retval) { 453 if (!portManager.mSystemReady) { 454 return; 455 } 456 457 if (retval != Status.SUCCESS) { 458 logAndPrint(Log.ERROR, pw, "port status enquiry failed"); 459 return; 460 } 461 462 ArrayList<RawPortInfo> newPortInfo = new ArrayList<>(); 463 464 for (PortStatus_1_1 current : currentPortStatus) { 465 RawPortInfo temp = new RawPortInfo(current.status.portName, 466 current.supportedModes, current.currentMode, 467 current.status.canChangeMode, current.status.currentPowerRole, 468 current.status.canChangePowerRole, 469 current.status.currentDataRole, current.status.canChangeDataRole); 470 newPortInfo.add(temp); 471 logAndPrint(Log.INFO, pw, "ClientCallback: " + current.status.portName); 472 } 473 474 Message message = portManager.mHandler.obtainMessage(); 475 Bundle bundle = new Bundle(); 476 bundle.putParcelableArrayList(PORT_INFO, newPortInfo); 477 message.what = MSG_UPDATE_PORTS; 478 message.setData(bundle); 479 portManager.mHandler.sendMessage(message); 480 } 481 482 public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) { 483 if (retval == Status.SUCCESS) { 484 logAndPrint(Log.INFO, pw, portName + " role switch successful"); 485 } else { 486 logAndPrint(Log.ERROR, pw, portName + " role switch failed"); 487 } 488 } 489 } 490 491 final class DeathRecipient implements HwBinder.DeathRecipient { 492 public IndentingPrintWriter pw; 493 494 DeathRecipient(IndentingPrintWriter pw) { 495 this.pw = pw; 496 } 497 498 @Override 499 public void serviceDied(long cookie) { 500 if (cookie == USB_HAL_DEATH_COOKIE) { 501 logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie); 502 synchronized (mLock) { 503 mProxy = null; 504 } 505 } 506 } 507 } 508 509 final class ServiceNotification extends IServiceNotification.Stub { 510 @Override 511 public void onRegistration(String fqName, String name, boolean preexisting) { 512 logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name); 513 connectToProxy(null); 514 } 515 } 516 517 private void connectToProxy(IndentingPrintWriter pw) { 518 synchronized (mLock) { 519 if (mProxy != null) { 520 return; 521 } 522 523 try { 524 mProxy = IUsb.getService(); 525 mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE); 526 mProxy.setCallback(mHALCallback); 527 mProxy.queryPortStatus(); 528 } catch (NoSuchElementException e) { 529 logAndPrintException(pw, "connectToProxy: usb hal service not found." 530 + " Did the service fail to start?", e); 531 } catch (RemoteException e) { 532 logAndPrintException(pw, "connectToProxy: usb hal service not responding", e); 533 } 534 } 535 } 536 537 /** 538 * Simulated ports directly add the new roles to mSimulatedPorts before calling. 539 * USB hal callback populates and sends the newPortInfo. 540 */ 541 private void updatePortsLocked(IndentingPrintWriter pw, ArrayList<RawPortInfo> newPortInfo) { 542 for (int i = mPorts.size(); i-- > 0; ) { 543 mPorts.valueAt(i).mDisposition = PortInfo.DISPOSITION_REMOVED; 544 } 545 546 // Enumerate all extant ports. 547 if (!mSimulatedPorts.isEmpty()) { 548 final int count = mSimulatedPorts.size(); 549 for (int i = 0; i < count; i++) { 550 final RawPortInfo portInfo = mSimulatedPorts.valueAt(i); 551 addOrUpdatePortLocked(portInfo.portId, portInfo.supportedModes, 552 portInfo.currentMode, portInfo.canChangeMode, 553 portInfo.currentPowerRole, portInfo.canChangePowerRole, 554 portInfo.currentDataRole, portInfo.canChangeDataRole, pw); 555 } 556 } else { 557 for (RawPortInfo currentPortInfo : newPortInfo) { 558 addOrUpdatePortLocked(currentPortInfo.portId, currentPortInfo.supportedModes, 559 currentPortInfo.currentMode, currentPortInfo.canChangeMode, 560 currentPortInfo.currentPowerRole, currentPortInfo.canChangePowerRole, 561 currentPortInfo.currentDataRole, currentPortInfo.canChangeDataRole, pw); 562 } 563 } 564 565 // Process the updates. 566 // Once finished, the list of ports will only contain ports in DISPOSITION_READY. 567 for (int i = mPorts.size(); i-- > 0; ) { 568 final PortInfo portInfo = mPorts.valueAt(i); 569 switch (portInfo.mDisposition) { 570 case PortInfo.DISPOSITION_ADDED: 571 handlePortAddedLocked(portInfo, pw); 572 portInfo.mDisposition = PortInfo.DISPOSITION_READY; 573 break; 574 case PortInfo.DISPOSITION_CHANGED: 575 handlePortChangedLocked(portInfo, pw); 576 portInfo.mDisposition = PortInfo.DISPOSITION_READY; 577 break; 578 case PortInfo.DISPOSITION_REMOVED: 579 mPorts.removeAt(i); 580 portInfo.mUsbPortStatus = null; // must do this early 581 handlePortRemovedLocked(portInfo, pw); 582 break; 583 } 584 } 585 } 586 587 588 // Must only be called by updatePortsLocked. 589 private void addOrUpdatePortLocked(String portId, int supportedModes, 590 int currentMode, boolean canChangeMode, 591 int currentPowerRole, boolean canChangePowerRole, 592 int currentDataRole, boolean canChangeDataRole, 593 IndentingPrintWriter pw) { 594 // Only allow mode switch capability for dual role ports. 595 // Validate that the current mode matches the supported modes we expect. 596 if ((supportedModes & UsbPort.MODE_DUAL) != UsbPort.MODE_DUAL) { 597 canChangeMode = false; 598 if (currentMode != 0 && currentMode != supportedModes) { 599 logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB " 600 + "port driver: supportedModes=" + UsbPort.modeToString(supportedModes) 601 + ", currentMode=" + UsbPort.modeToString(currentMode)); 602 currentMode = 0; 603 } 604 } 605 606 // Determine the supported role combinations. 607 // Note that the policy is designed to prefer setting the power and data 608 // role independently rather than changing the mode. 609 int supportedRoleCombinations = UsbPort.combineRolesAsBit( 610 currentPowerRole, currentDataRole); 611 if (currentMode != 0 && currentPowerRole != 0 && currentDataRole != 0) { 612 if (canChangePowerRole && canChangeDataRole) { 613 // Can change both power and data role independently. 614 // Assume all combinations are possible. 615 supportedRoleCombinations |= 616 COMBO_SOURCE_HOST | COMBO_SOURCE_DEVICE 617 | COMBO_SINK_HOST | COMBO_SINK_DEVICE; 618 } else if (canChangePowerRole) { 619 // Can only change power role. 620 // Assume data role must remain at its current value. 621 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 622 UsbPort.POWER_ROLE_SOURCE, currentDataRole); 623 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 624 UsbPort.POWER_ROLE_SINK, currentDataRole); 625 } else if (canChangeDataRole) { 626 // Can only change data role. 627 // Assume power role must remain at its current value. 628 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 629 currentPowerRole, UsbPort.DATA_ROLE_HOST); 630 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 631 currentPowerRole, UsbPort.DATA_ROLE_DEVICE); 632 } else if (canChangeMode) { 633 // Can only change the mode. 634 // Assume both standard UFP and DFP configurations will become available 635 // when this happens. 636 supportedRoleCombinations |= COMBO_SOURCE_HOST | COMBO_SINK_DEVICE; 637 } 638 } 639 640 // Update the port data structures. 641 PortInfo portInfo = mPorts.get(portId); 642 if (portInfo == null) { 643 portInfo = new PortInfo(portId, supportedModes); 644 portInfo.setStatus(currentMode, canChangeMode, 645 currentPowerRole, canChangePowerRole, 646 currentDataRole, canChangeDataRole, 647 supportedRoleCombinations); 648 mPorts.put(portId, portInfo); 649 } else { 650 // Sanity check that ports aren't changing definition out from under us. 651 if (supportedModes != portInfo.mUsbPort.getSupportedModes()) { 652 logAndPrint(Log.WARN, pw, "Ignoring inconsistent list of supported modes from " 653 + "USB port driver (should be immutable): " 654 + "previous=" + UsbPort.modeToString( 655 portInfo.mUsbPort.getSupportedModes()) 656 + ", current=" + UsbPort.modeToString(supportedModes)); 657 } 658 659 if (portInfo.setStatus(currentMode, canChangeMode, 660 currentPowerRole, canChangePowerRole, 661 currentDataRole, canChangeDataRole, 662 supportedRoleCombinations)) { 663 portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED; 664 } else { 665 portInfo.mDisposition = PortInfo.DISPOSITION_READY; 666 } 667 } 668 } 669 670 private void handlePortAddedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 671 logAndPrint(Log.INFO, pw, "USB port added: " + portInfo); 672 sendPortChangedBroadcastLocked(portInfo); 673 } 674 675 private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 676 logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo); 677 sendPortChangedBroadcastLocked(portInfo); 678 } 679 680 private void handlePortRemovedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 681 logAndPrint(Log.INFO, pw, "USB port removed: " + portInfo); 682 sendPortChangedBroadcastLocked(portInfo); 683 } 684 685 private void sendPortChangedBroadcastLocked(PortInfo portInfo) { 686 final Intent intent = new Intent(UsbManager.ACTION_USB_PORT_CHANGED); 687 intent.addFlags( 688 Intent.FLAG_RECEIVER_FOREGROUND | 689 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 690 intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort); 691 intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus); 692 693 // Guard against possible reentrance by posting the broadcast from the handler 694 // instead of from within the critical section. 695 mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL)); 696 } 697 698 private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) { 699 Slog.println(priority, TAG, msg); 700 if (pw != null) { 701 pw.println(msg); 702 } 703 } 704 705 private static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) { 706 Slog.e(TAG, msg, e); 707 if (pw != null) { 708 pw.println(msg + e); 709 } 710 } 711 712 private final Handler mHandler = new Handler(FgThread.get().getLooper()) { 713 @Override 714 public void handleMessage(Message msg) { 715 switch (msg.what) { 716 case MSG_UPDATE_PORTS: { 717 Bundle b = msg.getData(); 718 ArrayList<RawPortInfo> PortInfo = b.getParcelableArrayList(PORT_INFO); 719 synchronized (mLock) { 720 updatePortsLocked(null, PortInfo); 721 } 722 break; 723 } 724 } 725 } 726 }; 727 728 /** 729 * Describes a USB port. 730 */ 731 private static final class PortInfo { 732 public static final int DISPOSITION_ADDED = 0; 733 public static final int DISPOSITION_CHANGED = 1; 734 public static final int DISPOSITION_READY = 2; 735 public static final int DISPOSITION_REMOVED = 3; 736 737 public final UsbPort mUsbPort; 738 public UsbPortStatus mUsbPortStatus; 739 public boolean mCanChangeMode; 740 public boolean mCanChangePowerRole; 741 public boolean mCanChangeDataRole; 742 public int mDisposition; // default initialized to 0 which means added 743 744 public PortInfo(String portId, int supportedModes) { 745 mUsbPort = new UsbPort(portId, supportedModes); 746 } 747 748 public boolean setStatus(int currentMode, boolean canChangeMode, 749 int currentPowerRole, boolean canChangePowerRole, 750 int currentDataRole, boolean canChangeDataRole, 751 int supportedRoleCombinations) { 752 mCanChangeMode = canChangeMode; 753 mCanChangePowerRole = canChangePowerRole; 754 mCanChangeDataRole = canChangeDataRole; 755 if (mUsbPortStatus == null 756 || mUsbPortStatus.getCurrentMode() != currentMode 757 || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole 758 || mUsbPortStatus.getCurrentDataRole() != currentDataRole 759 || mUsbPortStatus.getSupportedRoleCombinations() 760 != supportedRoleCombinations) { 761 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, 762 supportedRoleCombinations); 763 return true; 764 } 765 return false; 766 } 767 768 @Override 769 public String toString() { 770 return "port=" + mUsbPort + ", status=" + mUsbPortStatus 771 + ", canChangeMode=" + mCanChangeMode 772 + ", canChangePowerRole=" + mCanChangePowerRole 773 + ", canChangeDataRole=" + mCanChangeDataRole; 774 } 775 } 776 777 /** 778 * Used for storing the raw data from the kernel 779 * Values of the member variables mocked directly incase of emulation. 780 */ 781 private static final class RawPortInfo implements Parcelable { 782 public final String portId; 783 public final int supportedModes; 784 public int currentMode; 785 public boolean canChangeMode; 786 public int currentPowerRole; 787 public boolean canChangePowerRole; 788 public int currentDataRole; 789 public boolean canChangeDataRole; 790 791 RawPortInfo(String portId, int supportedModes) { 792 this.portId = portId; 793 this.supportedModes = supportedModes; 794 } 795 796 RawPortInfo(String portId, int supportedModes, 797 int currentMode, boolean canChangeMode, 798 int currentPowerRole, boolean canChangePowerRole, 799 int currentDataRole, boolean canChangeDataRole) { 800 this.portId = portId; 801 this.supportedModes = supportedModes; 802 this.currentMode = currentMode; 803 this.canChangeMode = canChangeMode; 804 this.currentPowerRole = currentPowerRole; 805 this.canChangePowerRole = canChangePowerRole; 806 this.currentDataRole = currentDataRole; 807 this.canChangeDataRole = canChangeDataRole; 808 } 809 810 @Override 811 public int describeContents() { 812 return 0; 813 } 814 815 @Override 816 public void writeToParcel(Parcel dest, int flags) { 817 dest.writeString(portId); 818 dest.writeInt(supportedModes); 819 dest.writeInt(currentMode); 820 dest.writeByte((byte) (canChangeMode ? 1 : 0)); 821 dest.writeInt(currentPowerRole); 822 dest.writeByte((byte) (canChangePowerRole ? 1 : 0)); 823 dest.writeInt(currentDataRole); 824 dest.writeByte((byte) (canChangeDataRole ? 1 : 0)); 825 } 826 827 public static final Parcelable.Creator<RawPortInfo> CREATOR = 828 new Parcelable.Creator<RawPortInfo>() { 829 @Override 830 public RawPortInfo createFromParcel(Parcel in) { 831 String id = in.readString(); 832 int supportedModes = in.readInt(); 833 int currentMode = in.readInt(); 834 boolean canChangeMode = in.readByte() != 0; 835 int currentPowerRole = in.readInt(); 836 boolean canChangePowerRole = in.readByte() != 0; 837 int currentDataRole = in.readInt(); 838 boolean canChangeDataRole = in.readByte() != 0; 839 return new RawPortInfo(id, supportedModes, currentMode, canChangeMode, 840 currentPowerRole, canChangePowerRole, 841 currentDataRole, canChangeDataRole); 842 } 843 844 @Override 845 public RawPortInfo[] newArray(int size) { 846 return new RawPortInfo[size]; 847 } 848 }; 849 } 850 } 851