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 static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED; 20 import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE; 21 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE; 22 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST; 23 import static android.hardware.usb.UsbPortStatus.MODE_DFP; 24 import static android.hardware.usb.UsbPortStatus.MODE_DUAL; 25 import static android.hardware.usb.UsbPortStatus.MODE_UFP; 26 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK; 27 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE; 28 29 import static com.android.internal.usb.DumpUtils.writePort; 30 import static com.android.internal.usb.DumpUtils.writePortStatus; 31 32 import android.Manifest; 33 import android.annotation.NonNull; 34 import android.app.Notification; 35 import android.app.NotificationManager; 36 import android.app.PendingIntent; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.res.Resources; 40 import android.hardware.usb.ParcelableUsbPort; 41 import android.hardware.usb.UsbManager; 42 import android.hardware.usb.UsbPort; 43 import android.hardware.usb.UsbPortStatus; 44 import android.hardware.usb.V1_0.IUsb; 45 import android.hardware.usb.V1_0.PortRole; 46 import android.hardware.usb.V1_0.PortRoleType; 47 import android.hardware.usb.V1_0.Status; 48 import android.hardware.usb.V1_1.PortStatus_1_1; 49 import android.hardware.usb.V1_2.IUsbCallback; 50 import android.hardware.usb.V1_2.PortStatus; 51 import android.hidl.manager.V1_0.IServiceManager; 52 import android.hidl.manager.V1_0.IServiceNotification; 53 import android.os.Bundle; 54 import android.os.Handler; 55 import android.os.HwBinder; 56 import android.os.Message; 57 import android.os.Parcel; 58 import android.os.Parcelable; 59 import android.os.RemoteException; 60 import android.os.SystemClock; 61 import android.os.UserHandle; 62 import android.service.usb.UsbPortInfoProto; 63 import android.service.usb.UsbPortManagerProto; 64 import android.service.usb.UsbServiceProto; 65 import android.util.ArrayMap; 66 import android.util.Log; 67 import android.util.Slog; 68 import android.util.StatsLog; 69 70 import com.android.internal.annotations.GuardedBy; 71 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 72 import com.android.internal.notification.SystemNotificationChannels; 73 import com.android.internal.util.IndentingPrintWriter; 74 import com.android.internal.util.dump.DualDumpOutputStream; 75 import com.android.server.FgThread; 76 77 import java.util.ArrayList; 78 import java.util.NoSuchElementException; 79 80 /** 81 * Allows trusted components to control the properties of physical USB ports 82 * via the IUsb.hal. 83 * <p> 84 * Note: This interface may not be supported on all chipsets since the USB drivers 85 * must be changed to publish this information through the module. At the moment 86 * we only need this for devices with USB Type C ports to allow the System UI to 87 * control USB charging and data direction. On devices that do not support this 88 * interface the list of ports may incorrectly appear to be empty 89 * (but we don't care today). 90 * </p> 91 */ 92 public class UsbPortManager { 93 private static final String TAG = "UsbPortManager"; 94 95 private static final int MSG_UPDATE_PORTS = 1; 96 private static final int MSG_SYSTEM_READY = 2; 97 98 // All non-trivial role combinations. 99 private static final int COMBO_SOURCE_HOST = 100 UsbPort.combineRolesAsBit(POWER_ROLE_SOURCE, DATA_ROLE_HOST); 101 private static final int COMBO_SOURCE_DEVICE = UsbPort.combineRolesAsBit( 102 POWER_ROLE_SOURCE, DATA_ROLE_DEVICE); 103 private static final int COMBO_SINK_HOST = 104 UsbPort.combineRolesAsBit(POWER_ROLE_SINK, DATA_ROLE_HOST); 105 private static final int COMBO_SINK_DEVICE = UsbPort.combineRolesAsBit( 106 POWER_ROLE_SINK, DATA_ROLE_DEVICE); 107 108 // The system context. 109 private final Context mContext; 110 111 // Proxy object for the usb hal daemon. 112 @GuardedBy("mLock") 113 private IUsb mProxy = null; 114 115 // Callback when the UsbPort status is changed by the kernel. 116 // Mostly due a command sent by the remote Usb device. 117 private HALCallback mHALCallback = new HALCallback(null, this); 118 119 // Cookie sent for usb hal death notification. 120 private static final int USB_HAL_DEATH_COOKIE = 1000; 121 122 // Used as the key while sending the bundle to Main thread. 123 private static final String PORT_INFO = "port_info"; 124 125 // This is monitored to prevent updating the protInfo before the system 126 // is ready. 127 private boolean mSystemReady; 128 129 // Mutex for all mutable shared state. 130 private final Object mLock = new Object(); 131 132 // List of all ports, indexed by id. 133 // Ports may temporarily have different dispositions as they are added or removed 134 // but the class invariant is that this list will only contain ports with DISPOSITION_READY 135 // except while updatePortsLocked() is in progress. 136 private final ArrayMap<String, PortInfo> mPorts = new ArrayMap<>(); 137 138 // List of all simulated ports, indexed by id. 139 private final ArrayMap<String, RawPortInfo> mSimulatedPorts = 140 new ArrayMap<>(); 141 142 // Maintains the current connected status of the port. 143 // Uploads logs only when the connection status is changes. 144 private final ArrayMap<String, Boolean> mConnected = new ArrayMap<>(); 145 146 // Maintains the USB contaminant status that was previously logged. 147 // Logs get uploaded only when contaminant presence status changes. 148 private final ArrayMap<String, Integer> mContaminantStatus = new ArrayMap<>(); 149 150 private NotificationManager mNotificationManager; 151 152 /** 153 * If there currently is a notification related to contaminated USB port management 154 * shown the id of the notification, or 0 if there is none. 155 */ 156 private int mIsPortContaminatedNotificationId; 157 UsbPortManager(Context context)158 public UsbPortManager(Context context) { 159 mContext = context; 160 try { 161 ServiceNotification serviceNotification = new ServiceNotification(); 162 163 boolean ret = IServiceManager.getService() 164 .registerForNotifications("android.hardware.usb@1.0::IUsb", 165 "", serviceNotification); 166 if (!ret) { 167 logAndPrint(Log.ERROR, null, 168 "Failed to register service start notification"); 169 } 170 } catch (RemoteException e) { 171 logAndPrintException(null, 172 "Failed to register service start notification", e); 173 return; 174 } 175 connectToProxy(null); 176 } 177 systemReady()178 public void systemReady() { 179 mSystemReady = true; 180 if (mProxy != null) { 181 try { 182 mProxy.queryPortStatus(); 183 } catch (RemoteException e) { 184 logAndPrintException(null, 185 "ServiceStart: Failed to query port status", e); 186 } 187 } 188 mHandler.sendEmptyMessage(MSG_SYSTEM_READY); 189 } 190 updateContaminantNotification()191 private void updateContaminantNotification() { 192 PortInfo currentPortInfo = null; 193 Resources r = mContext.getResources(); 194 int contaminantStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED; 195 196 // Not handling multiple ports here. Showing the notification 197 // for the first port that returns CONTAMINANT_PRESENCE_DETECTED. 198 for (PortInfo portInfo : mPorts.values()) { 199 contaminantStatus = portInfo.mUsbPortStatus.getContaminantDetectionStatus(); 200 if (contaminantStatus == UsbPortStatus.CONTAMINANT_DETECTION_DETECTED 201 || contaminantStatus == UsbPortStatus.CONTAMINANT_DETECTION_DISABLED) { 202 currentPortInfo = portInfo; 203 break; 204 } 205 } 206 207 // Current contminant status is detected while "safe to use usb port" 208 // notification is displayed. Remove safe to use usb port notification 209 // and push contaminant detected notification. 210 if (contaminantStatus == UsbPortStatus.CONTAMINANT_DETECTION_DETECTED 211 && mIsPortContaminatedNotificationId 212 != SystemMessage.NOTE_USB_CONTAMINANT_DETECTED) { 213 if (mIsPortContaminatedNotificationId 214 == SystemMessage.NOTE_USB_CONTAMINANT_NOT_DETECTED) { 215 mNotificationManager.cancelAsUser(null, mIsPortContaminatedNotificationId, 216 UserHandle.ALL); 217 } 218 219 mIsPortContaminatedNotificationId = SystemMessage.NOTE_USB_CONTAMINANT_DETECTED; 220 int titleRes = com.android.internal.R.string.usb_contaminant_detected_title; 221 CharSequence title = r.getText(titleRes); 222 String channel = SystemNotificationChannels.ALERTS; 223 CharSequence message = r.getText( 224 com.android.internal.R.string.usb_contaminant_detected_message); 225 226 Intent intent = new Intent(); 227 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 228 intent.setClassName("com.android.systemui", 229 "com.android.systemui.usb.UsbContaminantActivity"); 230 intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(currentPortInfo.mUsbPort)); 231 232 PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, 233 intent, 0, null, UserHandle.CURRENT); 234 235 Notification.Builder builder = new Notification.Builder(mContext, channel) 236 .setOngoing(true) 237 .setTicker(title) 238 .setColor(mContext.getColor( 239 com.android.internal.R.color 240 .system_notification_accent_color)) 241 .setContentIntent(pi) 242 .setContentTitle(title) 243 .setContentText(message) 244 .setVisibility(Notification.VISIBILITY_PUBLIC) 245 .setSmallIcon(android.R.drawable.stat_sys_warning) 246 .setStyle(new Notification.BigTextStyle() 247 .bigText(message)); 248 Notification notification = builder.build(); 249 mNotificationManager.notifyAsUser(null, mIsPortContaminatedNotificationId, notification, 250 UserHandle.ALL); 251 // No contaminant is detected but contaminant detection notification is displayed. 252 // Remove contaminant detection notification and push safe to use USB port notification. 253 } else if (contaminantStatus != UsbPortStatus.CONTAMINANT_DETECTION_DETECTED 254 && mIsPortContaminatedNotificationId 255 == SystemMessage.NOTE_USB_CONTAMINANT_DETECTED) { 256 mNotificationManager.cancelAsUser(null, mIsPortContaminatedNotificationId, 257 UserHandle.ALL); 258 mIsPortContaminatedNotificationId = 0; 259 260 // Dont show safe to use notification when contaminant detection is disabled. 261 // Show only when the status is changing from detected to not detected. 262 if (contaminantStatus == UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED) { 263 mIsPortContaminatedNotificationId = 264 SystemMessage.NOTE_USB_CONTAMINANT_NOT_DETECTED; 265 int titleRes = com.android.internal.R.string.usb_contaminant_not_detected_title; 266 CharSequence title = r.getText(titleRes); 267 String channel = SystemNotificationChannels.ALERTS; 268 CharSequence message = r.getText( 269 com.android.internal.R.string.usb_contaminant_not_detected_message); 270 271 Notification.Builder builder = new Notification.Builder(mContext, channel) 272 .setSmallIcon(com.android.internal.R.drawable.ic_usb_48dp) 273 .setTicker(title) 274 .setColor(mContext.getColor( 275 com.android.internal.R.color 276 .system_notification_accent_color)) 277 .setContentTitle(title) 278 .setContentText(message) 279 .setVisibility(Notification.VISIBILITY_PUBLIC) 280 .setStyle(new Notification.BigTextStyle() 281 .bigText(message)); 282 Notification notification = builder.build(); 283 mNotificationManager.notifyAsUser(null, mIsPortContaminatedNotificationId, 284 notification, UserHandle.ALL); 285 } 286 } 287 } 288 getPorts()289 public UsbPort[] getPorts() { 290 synchronized (mLock) { 291 final int count = mPorts.size(); 292 final UsbPort[] result = new UsbPort[count]; 293 for (int i = 0; i < count; i++) { 294 result[i] = mPorts.valueAt(i).mUsbPort; 295 } 296 return result; 297 } 298 } 299 getPortStatus(String portId)300 public UsbPortStatus getPortStatus(String portId) { 301 synchronized (mLock) { 302 final PortInfo portInfo = mPorts.get(portId); 303 return portInfo != null ? portInfo.mUsbPortStatus : null; 304 } 305 } 306 307 /** 308 * Enables/disables contaminant detection. 309 * 310 * @param portId port identifier. 311 * @param enable enable contaminant detection when set to true. 312 */ enableContaminantDetection(@onNull String portId, boolean enable, @NonNull IndentingPrintWriter pw)313 public void enableContaminantDetection(@NonNull String portId, boolean enable, 314 @NonNull IndentingPrintWriter pw) { 315 final PortInfo portInfo = mPorts.get(portId); 316 if (portInfo == null) { 317 if (pw != null) { 318 pw.println("No such USB port: " + portId); 319 } 320 return; 321 } 322 323 if (!portInfo.mUsbPort.supportsEnableContaminantPresenceDetection()) { 324 return; 325 } 326 327 if ((enable && portInfo.mUsbPortStatus.getContaminantDetectionStatus() 328 != UsbPortStatus.CONTAMINANT_DETECTION_DISABLED) || (!enable 329 && portInfo.mUsbPortStatus.getContaminantDetectionStatus() 330 == UsbPortStatus.CONTAMINANT_DETECTION_DISABLED) 331 || (portInfo.mUsbPortStatus.getContaminantDetectionStatus() 332 == UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED)) { 333 return; 334 } 335 336 try { 337 // Oneway call into the hal. Use the castFrom method from HIDL. 338 android.hardware.usb.V1_2.IUsb proxy = android.hardware.usb.V1_2.IUsb.castFrom(mProxy); 339 proxy.enableContaminantPresenceDetection(portId, enable); 340 } catch (RemoteException e) { 341 logAndPrintException(pw, "Failed to set contaminant detection", e); 342 } catch (ClassCastException e) { 343 logAndPrintException(pw, "Method only applicable to V1.2 or above implementation", e); 344 } 345 } 346 setPortRoles(String portId, int newPowerRole, int newDataRole, IndentingPrintWriter pw)347 public void setPortRoles(String portId, int newPowerRole, int newDataRole, 348 IndentingPrintWriter pw) { 349 synchronized (mLock) { 350 final PortInfo portInfo = mPorts.get(portId); 351 if (portInfo == null) { 352 if (pw != null) { 353 pw.println("No such USB port: " + portId); 354 } 355 return; 356 } 357 358 // Check whether the new role is actually supported. 359 if (!portInfo.mUsbPortStatus.isRoleCombinationSupported(newPowerRole, newDataRole)) { 360 logAndPrint(Log.ERROR, pw, "Attempted to set USB port into unsupported " 361 + "role combination: portId=" + portId 362 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 363 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 364 return; 365 } 366 367 // Check whether anything actually changed. 368 final int currentDataRole = portInfo.mUsbPortStatus.getCurrentDataRole(); 369 final int currentPowerRole = portInfo.mUsbPortStatus.getCurrentPowerRole(); 370 if (currentDataRole == newDataRole && currentPowerRole == newPowerRole) { 371 if (pw != null) { 372 pw.println("No change."); 373 } 374 return; 375 } 376 377 // Determine whether we need to change the mode in order to accomplish this goal. 378 // We prefer not to do this since it's more likely to fail. 379 // 380 // Note: Arguably it might be worth allowing the client to influence this policy 381 // decision so that we could show more powerful developer facing UI but let's 382 // see how far we can get without having to do that. 383 final boolean canChangeMode = portInfo.mCanChangeMode; 384 final boolean canChangePowerRole = portInfo.mCanChangePowerRole; 385 final boolean canChangeDataRole = portInfo.mCanChangeDataRole; 386 final int currentMode = portInfo.mUsbPortStatus.getCurrentMode(); 387 final int newMode; 388 if ((!canChangePowerRole && currentPowerRole != newPowerRole) 389 || (!canChangeDataRole && currentDataRole != newDataRole)) { 390 if (canChangeMode && newPowerRole == POWER_ROLE_SOURCE 391 && newDataRole == DATA_ROLE_HOST) { 392 newMode = MODE_DFP; 393 } else if (canChangeMode && newPowerRole == POWER_ROLE_SINK 394 && newDataRole == DATA_ROLE_DEVICE) { 395 newMode = MODE_UFP; 396 } else { 397 logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations " 398 + "while attempting to change role: " + portInfo 399 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 400 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 401 return; 402 } 403 } else { 404 newMode = currentMode; 405 } 406 407 // Make it happen. 408 logAndPrint(Log.INFO, pw, "Setting USB port mode and role: portId=" + portId 409 + ", currentMode=" + UsbPort.modeToString(currentMode) 410 + ", currentPowerRole=" + UsbPort.powerRoleToString(currentPowerRole) 411 + ", currentDataRole=" + UsbPort.dataRoleToString(currentDataRole) 412 + ", newMode=" + UsbPort.modeToString(newMode) 413 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 414 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 415 416 RawPortInfo sim = mSimulatedPorts.get(portId); 417 if (sim != null) { 418 // Change simulated state. 419 sim.currentMode = newMode; 420 sim.currentPowerRole = newPowerRole; 421 sim.currentDataRole = newDataRole; 422 updatePortsLocked(pw, null); 423 } else if (mProxy != null) { 424 if (currentMode != newMode) { 425 // Changing the mode will have the side-effect of also changing 426 // the power and data roles but it might take some time to apply 427 // and the renegotiation might fail. Due to limitations of the USB 428 // hardware, we have no way of knowing whether it will work apriori 429 // which is why we would prefer to set the power and data roles 430 // directly instead. 431 432 logAndPrint(Log.ERROR, pw, "Trying to set the USB port mode: " 433 + "portId=" + portId 434 + ", newMode=" + UsbPort.modeToString(newMode)); 435 PortRole newRole = new PortRole(); 436 newRole.type = PortRoleType.MODE; 437 newRole.role = newMode; 438 try { 439 mProxy.switchRole(portId, newRole); 440 } catch (RemoteException e) { 441 logAndPrintException(pw, "Failed to set the USB port mode: " 442 + "portId=" + portId 443 + ", newMode=" + UsbPort.modeToString(newRole.role), e); 444 } 445 } else { 446 // Change power and data role independently as needed. 447 if (currentPowerRole != newPowerRole) { 448 PortRole newRole = new PortRole(); 449 newRole.type = PortRoleType.POWER_ROLE; 450 newRole.role = newPowerRole; 451 try { 452 mProxy.switchRole(portId, newRole); 453 } catch (RemoteException e) { 454 logAndPrintException(pw, "Failed to set the USB port power role: " 455 + "portId=" + portId 456 + ", newPowerRole=" + UsbPort.powerRoleToString 457 (newRole.role), 458 e); 459 return; 460 } 461 } 462 if (currentDataRole != newDataRole) { 463 PortRole newRole = new PortRole(); 464 newRole.type = PortRoleType.DATA_ROLE; 465 newRole.role = newDataRole; 466 try { 467 mProxy.switchRole(portId, newRole); 468 } catch (RemoteException e) { 469 logAndPrintException(pw, "Failed to set the USB port data role: " 470 + "portId=" + portId 471 + ", newDataRole=" + UsbPort.dataRoleToString(newRole 472 .role), 473 e); 474 } 475 } 476 } 477 } 478 } 479 } 480 addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw)481 public void addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw) { 482 synchronized (mLock) { 483 if (mSimulatedPorts.containsKey(portId)) { 484 pw.println("Port with same name already exists. Please remove it first."); 485 return; 486 } 487 488 pw.println("Adding simulated port: portId=" + portId 489 + ", supportedModes=" + UsbPort.modeToString(supportedModes)); 490 mSimulatedPorts.put(portId, 491 new RawPortInfo(portId, supportedModes)); 492 updatePortsLocked(pw, null); 493 } 494 } 495 connectSimulatedPort(String portId, int mode, boolean canChangeMode, int powerRole, boolean canChangePowerRole, int dataRole, boolean canChangeDataRole, IndentingPrintWriter pw)496 public void connectSimulatedPort(String portId, int mode, boolean canChangeMode, 497 int powerRole, boolean canChangePowerRole, 498 int dataRole, boolean canChangeDataRole, IndentingPrintWriter pw) { 499 synchronized (mLock) { 500 final RawPortInfo portInfo = mSimulatedPorts.get(portId); 501 if (portInfo == null) { 502 pw.println("Cannot connect simulated port which does not exist."); 503 return; 504 } 505 506 if (mode == 0 || powerRole == 0 || dataRole == 0) { 507 pw.println("Cannot connect simulated port in null mode, " 508 + "power role, or data role."); 509 return; 510 } 511 512 if ((portInfo.supportedModes & mode) == 0) { 513 pw.println("Simulated port does not support mode: " + UsbPort.modeToString(mode)); 514 return; 515 } 516 517 pw.println("Connecting simulated port: portId=" + portId 518 + ", mode=" + UsbPort.modeToString(mode) 519 + ", canChangeMode=" + canChangeMode 520 + ", powerRole=" + UsbPort.powerRoleToString(powerRole) 521 + ", canChangePowerRole=" + canChangePowerRole 522 + ", dataRole=" + UsbPort.dataRoleToString(dataRole) 523 + ", canChangeDataRole=" + canChangeDataRole); 524 portInfo.currentMode = mode; 525 portInfo.canChangeMode = canChangeMode; 526 portInfo.currentPowerRole = powerRole; 527 portInfo.canChangePowerRole = canChangePowerRole; 528 portInfo.currentDataRole = dataRole; 529 portInfo.canChangeDataRole = canChangeDataRole; 530 updatePortsLocked(pw, null); 531 } 532 } 533 534 /** 535 * Sets contaminant status for simulated USB port objects. 536 */ simulateContaminantStatus(String portId, boolean detected, IndentingPrintWriter pw)537 public void simulateContaminantStatus(String portId, boolean detected, 538 IndentingPrintWriter pw) { 539 synchronized (mLock) { 540 final RawPortInfo portInfo = mSimulatedPorts.get(portId); 541 if (portInfo == null) { 542 pw.println("Simulated port not found."); 543 return; 544 } 545 546 pw.println("Simulating wet port: portId=" + portId 547 + ", wet=" + detected); 548 portInfo.contaminantDetectionStatus = detected 549 ? UsbPortStatus.CONTAMINANT_DETECTION_DETECTED 550 : UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED; 551 updatePortsLocked(pw, null); 552 } 553 } 554 disconnectSimulatedPort(String portId, IndentingPrintWriter pw)555 public void disconnectSimulatedPort(String portId, IndentingPrintWriter pw) { 556 synchronized (mLock) { 557 final RawPortInfo portInfo = mSimulatedPorts.get(portId); 558 if (portInfo == null) { 559 pw.println("Cannot disconnect simulated port which does not exist."); 560 return; 561 } 562 563 pw.println("Disconnecting simulated port: portId=" + portId); 564 portInfo.currentMode = 0; 565 portInfo.canChangeMode = false; 566 portInfo.currentPowerRole = 0; 567 portInfo.canChangePowerRole = false; 568 portInfo.currentDataRole = 0; 569 portInfo.canChangeDataRole = false; 570 updatePortsLocked(pw, null); 571 } 572 } 573 removeSimulatedPort(String portId, IndentingPrintWriter pw)574 public void removeSimulatedPort(String portId, IndentingPrintWriter pw) { 575 synchronized (mLock) { 576 final int index = mSimulatedPorts.indexOfKey(portId); 577 if (index < 0) { 578 pw.println("Cannot remove simulated port which does not exist."); 579 return; 580 } 581 582 pw.println("Disconnecting simulated port: portId=" + portId); 583 mSimulatedPorts.removeAt(index); 584 updatePortsLocked(pw, null); 585 } 586 } 587 resetSimulation(IndentingPrintWriter pw)588 public void resetSimulation(IndentingPrintWriter pw) { 589 synchronized (mLock) { 590 pw.println("Removing all simulated ports and ending simulation."); 591 if (!mSimulatedPorts.isEmpty()) { 592 mSimulatedPorts.clear(); 593 updatePortsLocked(pw, null); 594 } 595 } 596 } 597 598 /** 599 * Dump the USB port state. 600 */ dump(DualDumpOutputStream dump, String idName, long id)601 public void dump(DualDumpOutputStream dump, String idName, long id) { 602 long token = dump.start(idName, id); 603 604 synchronized (mLock) { 605 dump.write("is_simulation_active", UsbPortManagerProto.IS_SIMULATION_ACTIVE, 606 !mSimulatedPorts.isEmpty()); 607 608 for (PortInfo portInfo : mPorts.values()) { 609 portInfo.dump(dump, "usb_ports", UsbPortManagerProto.USB_PORTS); 610 } 611 } 612 613 dump.end(token); 614 } 615 616 private static class HALCallback extends IUsbCallback.Stub { 617 public IndentingPrintWriter pw; 618 public UsbPortManager portManager; 619 HALCallback(IndentingPrintWriter pw, UsbPortManager portManager)620 HALCallback(IndentingPrintWriter pw, UsbPortManager portManager) { 621 this.pw = pw; 622 this.portManager = portManager; 623 } 624 notifyPortStatusChange( ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval)625 public void notifyPortStatusChange( 626 ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) { 627 if (!portManager.mSystemReady) { 628 return; 629 } 630 631 if (retval != Status.SUCCESS) { 632 logAndPrint(Log.ERROR, pw, "port status enquiry failed"); 633 return; 634 } 635 636 ArrayList<RawPortInfo> newPortInfo = new ArrayList<>(); 637 638 for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) { 639 RawPortInfo temp = new RawPortInfo(current.portName, 640 current.supportedModes, CONTAMINANT_PROTECTION_NONE, 641 current.currentMode, 642 current.canChangeMode, current.currentPowerRole, 643 current.canChangePowerRole, 644 current.currentDataRole, current.canChangeDataRole, 645 false, CONTAMINANT_PROTECTION_NONE, 646 false, CONTAMINANT_DETECTION_NOT_SUPPORTED); 647 newPortInfo.add(temp); 648 logAndPrint(Log.INFO, pw, "ClientCallback V1_0: " + current.portName); 649 } 650 651 Message message = portManager.mHandler.obtainMessage(); 652 Bundle bundle = new Bundle(); 653 bundle.putParcelableArrayList(PORT_INFO, newPortInfo); 654 message.what = MSG_UPDATE_PORTS; 655 message.setData(bundle); 656 portManager.mHandler.sendMessage(message); 657 } 658 659 notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus, int retval)660 public void notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus, 661 int retval) { 662 if (!portManager.mSystemReady) { 663 return; 664 } 665 666 if (retval != Status.SUCCESS) { 667 logAndPrint(Log.ERROR, pw, "port status enquiry failed"); 668 return; 669 } 670 671 ArrayList<RawPortInfo> newPortInfo = new ArrayList<>(); 672 673 int numStatus = currentPortStatus.size(); 674 for (int i = 0; i < numStatus; i++) { 675 PortStatus_1_1 current = currentPortStatus.get(i); 676 RawPortInfo temp = new RawPortInfo(current.status.portName, 677 current.supportedModes, CONTAMINANT_PROTECTION_NONE, 678 current.currentMode, 679 current.status.canChangeMode, current.status.currentPowerRole, 680 current.status.canChangePowerRole, 681 current.status.currentDataRole, current.status.canChangeDataRole, 682 false, CONTAMINANT_PROTECTION_NONE, 683 false, CONTAMINANT_DETECTION_NOT_SUPPORTED); 684 newPortInfo.add(temp); 685 logAndPrint(Log.INFO, pw, "ClientCallback V1_1: " + current.status.portName); 686 } 687 688 Message message = portManager.mHandler.obtainMessage(); 689 Bundle bundle = new Bundle(); 690 bundle.putParcelableArrayList(PORT_INFO, newPortInfo); 691 message.what = MSG_UPDATE_PORTS; 692 message.setData(bundle); 693 portManager.mHandler.sendMessage(message); 694 } 695 notifyPortStatusChange_1_2( ArrayList<PortStatus> currentPortStatus, int retval)696 public void notifyPortStatusChange_1_2( 697 ArrayList<PortStatus> currentPortStatus, int retval) { 698 if (!portManager.mSystemReady) { 699 return; 700 } 701 702 if (retval != Status.SUCCESS) { 703 logAndPrint(Log.ERROR, pw, "port status enquiry failed"); 704 return; 705 } 706 707 ArrayList<RawPortInfo> newPortInfo = new ArrayList<>(); 708 709 int numStatus = currentPortStatus.size(); 710 for (int i = 0; i < numStatus; i++) { 711 PortStatus current = currentPortStatus.get(i); 712 RawPortInfo temp = new RawPortInfo(current.status_1_1.status.portName, 713 current.status_1_1.supportedModes, 714 current.supportedContaminantProtectionModes, 715 current.status_1_1.currentMode, 716 current.status_1_1.status.canChangeMode, 717 current.status_1_1.status.currentPowerRole, 718 current.status_1_1.status.canChangePowerRole, 719 current.status_1_1.status.currentDataRole, 720 current.status_1_1.status.canChangeDataRole, 721 current.supportsEnableContaminantPresenceProtection, 722 current.contaminantProtectionStatus, 723 current.supportsEnableContaminantPresenceDetection, 724 current.contaminantDetectionStatus); 725 newPortInfo.add(temp); 726 logAndPrint(Log.INFO, pw, "ClientCallback V1_2: " 727 + current.status_1_1.status.portName); 728 } 729 730 Message message = portManager.mHandler.obtainMessage(); 731 Bundle bundle = new Bundle(); 732 bundle.putParcelableArrayList(PORT_INFO, newPortInfo); 733 message.what = MSG_UPDATE_PORTS; 734 message.setData(bundle); 735 portManager.mHandler.sendMessage(message); 736 } 737 notifyRoleSwitchStatus(String portName, PortRole role, int retval)738 public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) { 739 if (retval == Status.SUCCESS) { 740 logAndPrint(Log.INFO, pw, portName + " role switch successful"); 741 } else { 742 logAndPrint(Log.ERROR, pw, portName + " role switch failed"); 743 } 744 } 745 } 746 747 final class DeathRecipient implements HwBinder.DeathRecipient { 748 public IndentingPrintWriter pw; 749 DeathRecipient(IndentingPrintWriter pw)750 DeathRecipient(IndentingPrintWriter pw) { 751 this.pw = pw; 752 } 753 754 @Override serviceDied(long cookie)755 public void serviceDied(long cookie) { 756 if (cookie == USB_HAL_DEATH_COOKIE) { 757 logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie); 758 synchronized (mLock) { 759 mProxy = null; 760 } 761 } 762 } 763 } 764 765 final class ServiceNotification extends IServiceNotification.Stub { 766 @Override onRegistration(String fqName, String name, boolean preexisting)767 public void onRegistration(String fqName, String name, boolean preexisting) { 768 logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name); 769 connectToProxy(null); 770 } 771 } 772 connectToProxy(IndentingPrintWriter pw)773 private void connectToProxy(IndentingPrintWriter pw) { 774 synchronized (mLock) { 775 if (mProxy != null) { 776 return; 777 } 778 779 try { 780 mProxy = IUsb.getService(); 781 mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE); 782 mProxy.setCallback(mHALCallback); 783 mProxy.queryPortStatus(); 784 } catch (NoSuchElementException e) { 785 logAndPrintException(pw, "connectToProxy: usb hal service not found." 786 + " Did the service fail to start?", e); 787 } catch (RemoteException e) { 788 logAndPrintException(pw, "connectToProxy: usb hal service not responding", e); 789 } 790 } 791 } 792 793 /** 794 * Simulated ports directly add the new roles to mSimulatedPorts before calling. 795 * USB hal callback populates and sends the newPortInfo. 796 */ updatePortsLocked(IndentingPrintWriter pw, ArrayList<RawPortInfo> newPortInfo)797 private void updatePortsLocked(IndentingPrintWriter pw, ArrayList<RawPortInfo> newPortInfo) { 798 for (int i = mPorts.size(); i-- > 0; ) { 799 mPorts.valueAt(i).mDisposition = PortInfo.DISPOSITION_REMOVED; 800 } 801 802 // Enumerate all extant ports. 803 if (!mSimulatedPorts.isEmpty()) { 804 final int count = mSimulatedPorts.size(); 805 for (int i = 0; i < count; i++) { 806 final RawPortInfo portInfo = mSimulatedPorts.valueAt(i); 807 addOrUpdatePortLocked(portInfo.portId, portInfo.supportedModes, 808 portInfo.supportedContaminantProtectionModes, 809 portInfo.currentMode, portInfo.canChangeMode, 810 portInfo.currentPowerRole, portInfo.canChangePowerRole, 811 portInfo.currentDataRole, portInfo.canChangeDataRole, 812 portInfo.supportsEnableContaminantPresenceProtection, 813 portInfo.contaminantProtectionStatus, 814 portInfo.supportsEnableContaminantPresenceDetection, 815 portInfo.contaminantDetectionStatus, pw); 816 } 817 } else { 818 for (RawPortInfo currentPortInfo : newPortInfo) { 819 addOrUpdatePortLocked(currentPortInfo.portId, currentPortInfo.supportedModes, 820 currentPortInfo.supportedContaminantProtectionModes, 821 currentPortInfo.currentMode, currentPortInfo.canChangeMode, 822 currentPortInfo.currentPowerRole, currentPortInfo.canChangePowerRole, 823 currentPortInfo.currentDataRole, currentPortInfo.canChangeDataRole, 824 currentPortInfo.supportsEnableContaminantPresenceProtection, 825 currentPortInfo.contaminantProtectionStatus, 826 currentPortInfo.supportsEnableContaminantPresenceDetection, 827 currentPortInfo.contaminantDetectionStatus, pw); 828 } 829 } 830 831 // Process the updates. 832 // Once finished, the list of ports will only contain ports in DISPOSITION_READY. 833 for (int i = mPorts.size(); i-- > 0; ) { 834 final PortInfo portInfo = mPorts.valueAt(i); 835 switch (portInfo.mDisposition) { 836 case PortInfo.DISPOSITION_ADDED: 837 handlePortAddedLocked(portInfo, pw); 838 portInfo.mDisposition = PortInfo.DISPOSITION_READY; 839 break; 840 case PortInfo.DISPOSITION_CHANGED: 841 handlePortChangedLocked(portInfo, pw); 842 portInfo.mDisposition = PortInfo.DISPOSITION_READY; 843 break; 844 case PortInfo.DISPOSITION_REMOVED: 845 mPorts.removeAt(i); 846 portInfo.mUsbPortStatus = null; // must do this early 847 handlePortRemovedLocked(portInfo, pw); 848 break; 849 } 850 } 851 } 852 853 // Must only be called by updatePortsLocked. addOrUpdatePortLocked(String portId, int supportedModes, int supportedContaminantProtectionModes, int currentMode, boolean canChangeMode, int currentPowerRole, boolean canChangePowerRole, int currentDataRole, boolean canChangeDataRole, boolean supportsEnableContaminantPresenceProtection, int contaminantProtectionStatus, boolean supportsEnableContaminantPresenceDetection, int contaminantDetectionStatus, IndentingPrintWriter pw)854 private void addOrUpdatePortLocked(String portId, int supportedModes, 855 int supportedContaminantProtectionModes, 856 int currentMode, boolean canChangeMode, 857 int currentPowerRole, boolean canChangePowerRole, 858 int currentDataRole, boolean canChangeDataRole, 859 boolean supportsEnableContaminantPresenceProtection, 860 int contaminantProtectionStatus, 861 boolean supportsEnableContaminantPresenceDetection, 862 int contaminantDetectionStatus, 863 IndentingPrintWriter pw) { 864 // Only allow mode switch capability for dual role ports. 865 // Validate that the current mode matches the supported modes we expect. 866 if ((supportedModes & MODE_DUAL) != MODE_DUAL) { 867 canChangeMode = false; 868 if (currentMode != 0 && currentMode != supportedModes) { 869 logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB " 870 + "port driver: supportedModes=" + UsbPort.modeToString(supportedModes) 871 + ", currentMode=" + UsbPort.modeToString(currentMode)); 872 currentMode = 0; 873 } 874 } 875 876 // Determine the supported role combinations. 877 // Note that the policy is designed to prefer setting the power and data 878 // role independently rather than changing the mode. 879 int supportedRoleCombinations = UsbPort.combineRolesAsBit( 880 currentPowerRole, currentDataRole); 881 if (currentMode != 0 && currentPowerRole != 0 && currentDataRole != 0) { 882 if (canChangePowerRole && canChangeDataRole) { 883 // Can change both power and data role independently. 884 // Assume all combinations are possible. 885 supportedRoleCombinations |= 886 COMBO_SOURCE_HOST | COMBO_SOURCE_DEVICE 887 | COMBO_SINK_HOST | COMBO_SINK_DEVICE; 888 } else if (canChangePowerRole) { 889 // Can only change power role. 890 // Assume data role must remain at its current value. 891 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 892 POWER_ROLE_SOURCE, currentDataRole); 893 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 894 POWER_ROLE_SINK, currentDataRole); 895 } else if (canChangeDataRole) { 896 // Can only change data role. 897 // Assume power role must remain at its current value. 898 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 899 currentPowerRole, DATA_ROLE_HOST); 900 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 901 currentPowerRole, DATA_ROLE_DEVICE); 902 } else if (canChangeMode) { 903 // Can only change the mode. 904 // Assume both standard UFP and DFP configurations will become available 905 // when this happens. 906 supportedRoleCombinations |= COMBO_SOURCE_HOST | COMBO_SINK_DEVICE; 907 } 908 } 909 910 // Update the port data structures. 911 PortInfo portInfo = mPorts.get(portId); 912 if (portInfo == null) { 913 portInfo = new PortInfo(mContext.getSystemService(UsbManager.class), 914 portId, supportedModes, supportedContaminantProtectionModes, 915 supportsEnableContaminantPresenceProtection, 916 supportsEnableContaminantPresenceDetection); 917 portInfo.setStatus(currentMode, canChangeMode, 918 currentPowerRole, canChangePowerRole, 919 currentDataRole, canChangeDataRole, 920 supportedRoleCombinations, contaminantProtectionStatus, 921 contaminantDetectionStatus); 922 mPorts.put(portId, portInfo); 923 } else { 924 // Sanity check that ports aren't changing definition out from under us. 925 if (supportedModes != portInfo.mUsbPort.getSupportedModes()) { 926 logAndPrint(Log.WARN, pw, "Ignoring inconsistent list of supported modes from " 927 + "USB port driver (should be immutable): " 928 + "previous=" + UsbPort.modeToString( 929 portInfo.mUsbPort.getSupportedModes()) 930 + ", current=" + UsbPort.modeToString(supportedModes)); 931 } 932 933 if (supportsEnableContaminantPresenceProtection 934 != portInfo.mUsbPort.supportsEnableContaminantPresenceProtection()) { 935 logAndPrint(Log.WARN, pw, 936 "Ignoring inconsistent supportsEnableContaminantPresenceProtection" 937 + "USB port driver (should be immutable): " 938 + "previous=" 939 + portInfo.mUsbPort.supportsEnableContaminantPresenceProtection() 940 + ", current=" + supportsEnableContaminantPresenceProtection); 941 } 942 943 if (supportsEnableContaminantPresenceDetection 944 != portInfo.mUsbPort.supportsEnableContaminantPresenceDetection()) { 945 logAndPrint(Log.WARN, pw, 946 "Ignoring inconsistent supportsEnableContaminantPresenceDetection " 947 + "USB port driver (should be immutable): " 948 + "previous=" 949 + portInfo.mUsbPort.supportsEnableContaminantPresenceDetection() 950 + ", current=" + supportsEnableContaminantPresenceDetection); 951 } 952 953 954 if (portInfo.setStatus(currentMode, canChangeMode, 955 currentPowerRole, canChangePowerRole, 956 currentDataRole, canChangeDataRole, 957 supportedRoleCombinations, contaminantProtectionStatus, 958 contaminantDetectionStatus)) { 959 portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED; 960 } else { 961 portInfo.mDisposition = PortInfo.DISPOSITION_READY; 962 } 963 } 964 } 965 handlePortLocked(PortInfo portInfo, IndentingPrintWriter pw)966 private void handlePortLocked(PortInfo portInfo, IndentingPrintWriter pw) { 967 sendPortChangedBroadcastLocked(portInfo); 968 logToStatsd(portInfo, pw); 969 updateContaminantNotification(); 970 } 971 handlePortAddedLocked(PortInfo portInfo, IndentingPrintWriter pw)972 private void handlePortAddedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 973 logAndPrint(Log.INFO, pw, "USB port added: " + portInfo); 974 handlePortLocked(portInfo, pw); 975 } 976 handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw)977 private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 978 logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo); 979 enableContaminantDetectionIfNeeded(portInfo, pw); 980 handlePortLocked(portInfo, pw); 981 } 982 handlePortRemovedLocked(PortInfo portInfo, IndentingPrintWriter pw)983 private void handlePortRemovedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 984 logAndPrint(Log.INFO, pw, "USB port removed: " + portInfo); 985 handlePortLocked(portInfo, pw); 986 } 987 988 // Constants have to be converted between USB HAL V1.2 ContaminantDetectionStatus 989 // to usb.proto as proto guidelines recommends 0 to be UNKNOWN/UNSUPPORTTED 990 // whereas HAL policy is against a loosely defined constant. convertContaminantDetectionStatusToProto(int contaminantDetectionStatus)991 private static int convertContaminantDetectionStatusToProto(int contaminantDetectionStatus) { 992 switch (contaminantDetectionStatus) { 993 case UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED: 994 return UsbServiceProto.CONTAMINANT_STATUS_NOT_SUPPORTED; 995 case UsbPortStatus.CONTAMINANT_DETECTION_DISABLED: 996 return UsbServiceProto.CONTAMINANT_STATUS_DISABLED; 997 case UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED: 998 return UsbServiceProto.CONTAMINANT_STATUS_NOT_DETECTED; 999 case UsbPortStatus.CONTAMINANT_DETECTION_DETECTED: 1000 return UsbServiceProto.CONTAMINANT_STATUS_DETECTED; 1001 default: 1002 return UsbServiceProto.CONTAMINANT_STATUS_UNKNOWN; 1003 } 1004 } 1005 sendPortChangedBroadcastLocked(PortInfo portInfo)1006 private void sendPortChangedBroadcastLocked(PortInfo portInfo) { 1007 final Intent intent = new Intent(UsbManager.ACTION_USB_PORT_CHANGED); 1008 intent.addFlags( 1009 Intent.FLAG_RECEIVER_FOREGROUND | 1010 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1011 intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(portInfo.mUsbPort)); 1012 intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus); 1013 1014 // Guard against possible reentrance by posting the broadcast from the handler 1015 // instead of from within the critical section. 1016 mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 1017 Manifest.permission.MANAGE_USB)); 1018 } 1019 enableContaminantDetectionIfNeeded(PortInfo portInfo, IndentingPrintWriter pw)1020 private void enableContaminantDetectionIfNeeded(PortInfo portInfo, IndentingPrintWriter pw) { 1021 if (!mConnected.containsKey(portInfo.mUsbPort.getId())) { 1022 return; 1023 } 1024 1025 if (mConnected.get(portInfo.mUsbPort.getId()) 1026 && !portInfo.mUsbPortStatus.isConnected() 1027 && portInfo.mUsbPortStatus.getContaminantDetectionStatus() 1028 == UsbPortStatus.CONTAMINANT_DETECTION_DISABLED) { 1029 // Contaminant detection might have been temporarily disabled by the user 1030 // through SystemUI. 1031 // Re-enable contaminant detection when the accessory is unplugged. 1032 enableContaminantDetection(portInfo.mUsbPort.getId(), true, pw); 1033 } 1034 } 1035 logToStatsd(PortInfo portInfo, IndentingPrintWriter pw)1036 private void logToStatsd(PortInfo portInfo, IndentingPrintWriter pw) { 1037 // Port is removed 1038 if (portInfo.mUsbPortStatus == null) { 1039 if (mConnected.containsKey(portInfo.mUsbPort.getId())) { 1040 //Previous logged a connected. Set it to disconnected. 1041 if (mConnected.get(portInfo.mUsbPort.getId())) { 1042 StatsLog.write(StatsLog.USB_CONNECTOR_STATE_CHANGED, 1043 StatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_DISCONNECTED, 1044 portInfo.mUsbPort.getId(), portInfo.mLastConnectDurationMillis); 1045 } 1046 mConnected.remove(portInfo.mUsbPort.getId()); 1047 } 1048 1049 if (mContaminantStatus.containsKey(portInfo.mUsbPort.getId())) { 1050 //Previous logged a contaminant detected. Set it to not detected. 1051 if ((mContaminantStatus.get(portInfo.mUsbPort.getId()) 1052 == UsbPortStatus.CONTAMINANT_DETECTION_DETECTED)) { 1053 StatsLog.write(StatsLog.USB_CONTAMINANT_REPORTED, 1054 portInfo.mUsbPort.getId(), 1055 convertContaminantDetectionStatusToProto( 1056 UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED)); 1057 } 1058 mContaminantStatus.remove(portInfo.mUsbPort.getId()); 1059 } 1060 return; 1061 } 1062 1063 if (!mConnected.containsKey(portInfo.mUsbPort.getId()) 1064 || (mConnected.get(portInfo.mUsbPort.getId()) 1065 != portInfo.mUsbPortStatus.isConnected())) { 1066 mConnected.put(portInfo.mUsbPort.getId(), portInfo.mUsbPortStatus.isConnected()); 1067 StatsLog.write(StatsLog.USB_CONNECTOR_STATE_CHANGED, 1068 portInfo.mUsbPortStatus.isConnected() 1069 ? StatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_CONNECTED : 1070 StatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_DISCONNECTED, 1071 portInfo.mUsbPort.getId(), portInfo.mLastConnectDurationMillis); 1072 } 1073 1074 if (!mContaminantStatus.containsKey(portInfo.mUsbPort.getId()) 1075 || (mContaminantStatus.get(portInfo.mUsbPort.getId()) 1076 != portInfo.mUsbPortStatus.getContaminantDetectionStatus())) { 1077 mContaminantStatus.put(portInfo.mUsbPort.getId(), 1078 portInfo.mUsbPortStatus.getContaminantDetectionStatus()); 1079 StatsLog.write(StatsLog.USB_CONTAMINANT_REPORTED, 1080 portInfo.mUsbPort.getId(), 1081 convertContaminantDetectionStatusToProto( 1082 portInfo.mUsbPortStatus.getContaminantDetectionStatus())); 1083 } 1084 } 1085 logAndPrint(int priority, IndentingPrintWriter pw, String msg)1086 private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) { 1087 Slog.println(priority, TAG, msg); 1088 if (pw != null) { 1089 pw.println(msg); 1090 } 1091 } 1092 logAndPrintException(IndentingPrintWriter pw, String msg, Exception e)1093 private static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) { 1094 Slog.e(TAG, msg, e); 1095 if (pw != null) { 1096 pw.println(msg + e); 1097 } 1098 } 1099 1100 private final Handler mHandler = new Handler(FgThread.get().getLooper()) { 1101 @Override 1102 public void handleMessage(Message msg) { 1103 switch (msg.what) { 1104 case MSG_UPDATE_PORTS: { 1105 Bundle b = msg.getData(); 1106 ArrayList<RawPortInfo> PortInfo = b.getParcelableArrayList(PORT_INFO); 1107 synchronized (mLock) { 1108 updatePortsLocked(null, PortInfo); 1109 } 1110 break; 1111 } 1112 case MSG_SYSTEM_READY: { 1113 mNotificationManager = (NotificationManager) 1114 mContext.getSystemService(Context.NOTIFICATION_SERVICE); 1115 break; 1116 } 1117 } 1118 } 1119 }; 1120 1121 /** 1122 * Describes a USB port. 1123 */ 1124 private static final class PortInfo { 1125 public static final int DISPOSITION_ADDED = 0; 1126 public static final int DISPOSITION_CHANGED = 1; 1127 public static final int DISPOSITION_READY = 2; 1128 public static final int DISPOSITION_REMOVED = 3; 1129 1130 public final UsbPort mUsbPort; 1131 public UsbPortStatus mUsbPortStatus; 1132 public boolean mCanChangeMode; 1133 public boolean mCanChangePowerRole; 1134 public boolean mCanChangeDataRole; 1135 // default initialized to 0 which means added 1136 public int mDisposition; 1137 // Tracks elapsedRealtime() of when the port was connected 1138 public long mConnectedAtMillis; 1139 // 0 when port is connected. Else reports the last connected duration 1140 public long mLastConnectDurationMillis; 1141 PortInfo(@onNull UsbManager usbManager, @NonNull String portId, int supportedModes, int supportedContaminantProtectionModes, boolean supportsEnableContaminantPresenceDetection, boolean supportsEnableContaminantPresenceProtection)1142 PortInfo(@NonNull UsbManager usbManager, @NonNull String portId, int supportedModes, 1143 int supportedContaminantProtectionModes, 1144 boolean supportsEnableContaminantPresenceDetection, 1145 boolean supportsEnableContaminantPresenceProtection) { 1146 mUsbPort = new UsbPort(usbManager, portId, supportedModes, 1147 supportedContaminantProtectionModes, 1148 supportsEnableContaminantPresenceDetection, 1149 supportsEnableContaminantPresenceProtection); 1150 } 1151 setStatus(int currentMode, boolean canChangeMode, int currentPowerRole, boolean canChangePowerRole, int currentDataRole, boolean canChangeDataRole, int supportedRoleCombinations)1152 public boolean setStatus(int currentMode, boolean canChangeMode, 1153 int currentPowerRole, boolean canChangePowerRole, 1154 int currentDataRole, boolean canChangeDataRole, 1155 int supportedRoleCombinations) { 1156 boolean dispositionChanged = false; 1157 1158 mCanChangeMode = canChangeMode; 1159 mCanChangePowerRole = canChangePowerRole; 1160 mCanChangeDataRole = canChangeDataRole; 1161 if (mUsbPortStatus == null 1162 || mUsbPortStatus.getCurrentMode() != currentMode 1163 || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole 1164 || mUsbPortStatus.getCurrentDataRole() != currentDataRole 1165 || mUsbPortStatus.getSupportedRoleCombinations() 1166 != supportedRoleCombinations) { 1167 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, 1168 supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE, 1169 UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED); 1170 dispositionChanged = true; 1171 } 1172 1173 if (mUsbPortStatus.isConnected() && mConnectedAtMillis == 0) { 1174 mConnectedAtMillis = SystemClock.elapsedRealtime(); 1175 mLastConnectDurationMillis = 0; 1176 } else if (!mUsbPortStatus.isConnected() && mConnectedAtMillis != 0) { 1177 mLastConnectDurationMillis = SystemClock.elapsedRealtime() - mConnectedAtMillis; 1178 mConnectedAtMillis = 0; 1179 } 1180 1181 return dispositionChanged; 1182 } 1183 setStatus(int currentMode, boolean canChangeMode, int currentPowerRole, boolean canChangePowerRole, int currentDataRole, boolean canChangeDataRole, int supportedRoleCombinations, int contaminantProtectionStatus, int contaminantDetectionStatus)1184 public boolean setStatus(int currentMode, boolean canChangeMode, 1185 int currentPowerRole, boolean canChangePowerRole, 1186 int currentDataRole, boolean canChangeDataRole, 1187 int supportedRoleCombinations, int contaminantProtectionStatus, 1188 int contaminantDetectionStatus) { 1189 boolean dispositionChanged = false; 1190 1191 mCanChangeMode = canChangeMode; 1192 mCanChangePowerRole = canChangePowerRole; 1193 mCanChangeDataRole = canChangeDataRole; 1194 if (mUsbPortStatus == null 1195 || mUsbPortStatus.getCurrentMode() != currentMode 1196 || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole 1197 || mUsbPortStatus.getCurrentDataRole() != currentDataRole 1198 || mUsbPortStatus.getSupportedRoleCombinations() 1199 != supportedRoleCombinations 1200 || mUsbPortStatus.getContaminantProtectionStatus() 1201 != contaminantProtectionStatus 1202 || mUsbPortStatus.getContaminantDetectionStatus() 1203 != contaminantDetectionStatus) { 1204 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, 1205 supportedRoleCombinations, contaminantProtectionStatus, 1206 contaminantDetectionStatus); 1207 dispositionChanged = true; 1208 } 1209 1210 if (mUsbPortStatus.isConnected() && mConnectedAtMillis == 0) { 1211 mConnectedAtMillis = SystemClock.elapsedRealtime(); 1212 mLastConnectDurationMillis = 0; 1213 } else if (!mUsbPortStatus.isConnected() && mConnectedAtMillis != 0) { 1214 mLastConnectDurationMillis = SystemClock.elapsedRealtime() - mConnectedAtMillis; 1215 mConnectedAtMillis = 0; 1216 } 1217 1218 return dispositionChanged; 1219 } 1220 dump(@onNull DualDumpOutputStream dump, @NonNull String idName, long id)1221 void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) { 1222 long token = dump.start(idName, id); 1223 1224 writePort(dump, "port", UsbPortInfoProto.PORT, mUsbPort); 1225 writePortStatus(dump, "status", UsbPortInfoProto.STATUS, mUsbPortStatus); 1226 dump.write("can_change_mode", UsbPortInfoProto.CAN_CHANGE_MODE, mCanChangeMode); 1227 dump.write("can_change_power_role", UsbPortInfoProto.CAN_CHANGE_POWER_ROLE, 1228 mCanChangePowerRole); 1229 dump.write("can_change_data_role", UsbPortInfoProto.CAN_CHANGE_DATA_ROLE, 1230 mCanChangeDataRole); 1231 dump.write("connected_at_millis", 1232 UsbPortInfoProto.CONNECTED_AT_MILLIS, mConnectedAtMillis); 1233 dump.write("last_connect_duration_millis", 1234 UsbPortInfoProto.LAST_CONNECT_DURATION_MILLIS, mLastConnectDurationMillis); 1235 1236 dump.end(token); 1237 } 1238 1239 @Override toString()1240 public String toString() { 1241 return "port=" + mUsbPort + ", status=" + mUsbPortStatus 1242 + ", canChangeMode=" + mCanChangeMode 1243 + ", canChangePowerRole=" + mCanChangePowerRole 1244 + ", canChangeDataRole=" + mCanChangeDataRole 1245 + ", connectedAtMillis=" + mConnectedAtMillis 1246 + ", lastConnectDurationMillis=" + mLastConnectDurationMillis; 1247 } 1248 } 1249 1250 /** 1251 * Used for storing the raw data from the kernel 1252 * Values of the member variables mocked directly incase of emulation. 1253 */ 1254 private static final class RawPortInfo implements Parcelable { 1255 public final String portId; 1256 public final int supportedModes; 1257 public final int supportedContaminantProtectionModes; 1258 public int currentMode; 1259 public boolean canChangeMode; 1260 public int currentPowerRole; 1261 public boolean canChangePowerRole; 1262 public int currentDataRole; 1263 public boolean canChangeDataRole; 1264 public boolean supportsEnableContaminantPresenceProtection; 1265 public int contaminantProtectionStatus; 1266 public boolean supportsEnableContaminantPresenceDetection; 1267 public int contaminantDetectionStatus; 1268 RawPortInfo(String portId, int supportedModes)1269 RawPortInfo(String portId, int supportedModes) { 1270 this.portId = portId; 1271 this.supportedModes = supportedModes; 1272 this.supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE; 1273 this.supportsEnableContaminantPresenceProtection = false; 1274 this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE; 1275 this.supportsEnableContaminantPresenceDetection = false; 1276 this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED; 1277 } 1278 RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes, int currentMode, boolean canChangeMode, int currentPowerRole, boolean canChangePowerRole, int currentDataRole, boolean canChangeDataRole, boolean supportsEnableContaminantPresenceProtection, int contaminantProtectionStatus, boolean supportsEnableContaminantPresenceDetection, int contaminantDetectionStatus)1279 RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes, 1280 int currentMode, boolean canChangeMode, 1281 int currentPowerRole, boolean canChangePowerRole, 1282 int currentDataRole, boolean canChangeDataRole, 1283 boolean supportsEnableContaminantPresenceProtection, 1284 int contaminantProtectionStatus, 1285 boolean supportsEnableContaminantPresenceDetection, 1286 int contaminantDetectionStatus) { 1287 this.portId = portId; 1288 this.supportedModes = supportedModes; 1289 this.supportedContaminantProtectionModes = supportedContaminantProtectionModes; 1290 this.currentMode = currentMode; 1291 this.canChangeMode = canChangeMode; 1292 this.currentPowerRole = currentPowerRole; 1293 this.canChangePowerRole = canChangePowerRole; 1294 this.currentDataRole = currentDataRole; 1295 this.canChangeDataRole = canChangeDataRole; 1296 this.supportsEnableContaminantPresenceProtection = 1297 supportsEnableContaminantPresenceProtection; 1298 this.contaminantProtectionStatus = contaminantProtectionStatus; 1299 this.supportsEnableContaminantPresenceDetection = 1300 supportsEnableContaminantPresenceDetection; 1301 this.contaminantDetectionStatus = contaminantDetectionStatus; 1302 } 1303 1304 1305 @Override describeContents()1306 public int describeContents() { 1307 return 0; 1308 } 1309 1310 @Override writeToParcel(Parcel dest, int flags)1311 public void writeToParcel(Parcel dest, int flags) { 1312 dest.writeString(portId); 1313 dest.writeInt(supportedModes); 1314 dest.writeInt(supportedContaminantProtectionModes); 1315 dest.writeInt(currentMode); 1316 dest.writeByte((byte) (canChangeMode ? 1 : 0)); 1317 dest.writeInt(currentPowerRole); 1318 dest.writeByte((byte) (canChangePowerRole ? 1 : 0)); 1319 dest.writeInt(currentDataRole); 1320 dest.writeByte((byte) (canChangeDataRole ? 1 : 0)); 1321 dest.writeBoolean(supportsEnableContaminantPresenceProtection); 1322 dest.writeInt(contaminantProtectionStatus); 1323 dest.writeBoolean(supportsEnableContaminantPresenceDetection); 1324 dest.writeInt(contaminantDetectionStatus); 1325 } 1326 1327 public static final Parcelable.Creator<RawPortInfo> CREATOR = 1328 new Parcelable.Creator<RawPortInfo>() { 1329 @Override 1330 public RawPortInfo createFromParcel(Parcel in) { 1331 String id = in.readString(); 1332 int supportedModes = in.readInt(); 1333 int supportedContaminantProtectionModes = in.readInt(); 1334 int currentMode = in.readInt(); 1335 boolean canChangeMode = in.readByte() != 0; 1336 int currentPowerRole = in.readInt(); 1337 boolean canChangePowerRole = in.readByte() != 0; 1338 int currentDataRole = in.readInt(); 1339 boolean canChangeDataRole = in.readByte() != 0; 1340 boolean supportsEnableContaminantPresenceProtection = in.readBoolean(); 1341 int contaminantProtectionStatus = in.readInt(); 1342 boolean supportsEnableContaminantPresenceDetection = in.readBoolean(); 1343 int contaminantDetectionStatus = in.readInt(); 1344 return new RawPortInfo(id, supportedModes, 1345 supportedContaminantProtectionModes, currentMode, canChangeMode, 1346 currentPowerRole, canChangePowerRole, 1347 currentDataRole, canChangeDataRole, 1348 supportsEnableContaminantPresenceProtection, 1349 contaminantProtectionStatus, 1350 supportsEnableContaminantPresenceDetection, 1351 contaminantDetectionStatus); 1352 } 1353 1354 @Override 1355 public RawPortInfo[] newArray(int size) { 1356 return new RawPortInfo[size]; 1357 } 1358 }; 1359 } 1360 } 1361