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.UsbOperationInternal.USB_OPERATION_ERROR_PORT_MISMATCH; 20 import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL; 21 import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED; 22 import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE; 23 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE; 24 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST; 25 import static android.hardware.usb.UsbPortStatus.MODE_DFP; 26 import static android.hardware.usb.UsbPortStatus.MODE_DUAL; 27 import static android.hardware.usb.UsbPortStatus.MODE_UFP; 28 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK; 29 import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE; 30 import static com.android.server.usb.hal.port.UsbPortHal.HAL_POWER_ROLE_SOURCE; 31 import static com.android.server.usb.hal.port.UsbPortHal.HAL_POWER_ROLE_SINK; 32 import static com.android.server.usb.hal.port.UsbPortHal.HAL_DATA_ROLE_HOST; 33 import static com.android.server.usb.hal.port.UsbPortHal.HAL_DATA_ROLE_DEVICE; 34 import static com.android.server.usb.hal.port.UsbPortHal.HAL_MODE_DFP; 35 import static com.android.server.usb.hal.port.UsbPortHal.HAL_MODE_UFP; 36 37 import static com.android.internal.usb.DumpUtils.writePort; 38 import static com.android.internal.usb.DumpUtils.writePortStatus; 39 40 import android.Manifest; 41 import android.annotation.NonNull; 42 import android.app.Notification; 43 import android.app.NotificationManager; 44 import android.app.PendingIntent; 45 import android.content.ComponentName; 46 import android.content.Context; 47 import android.content.Intent; 48 import android.content.res.Resources; 49 import android.hardware.usb.IDisplayPortAltModeInfoListener; 50 import android.hardware.usb.IUsbOperationInternal; 51 import android.hardware.usb.ParcelableUsbPort; 52 import android.hardware.usb.UsbManager; 53 import android.hardware.usb.UsbPort; 54 import android.hardware.usb.UsbPortStatus; 55 import android.hardware.usb.DisplayPortAltModeInfo; 56 import android.hardware.usb.V1_0.IUsb; 57 import android.hardware.usb.V1_0.PortRole; 58 import android.hardware.usb.V1_0.PortRoleType; 59 import android.hardware.usb.V1_0.Status; 60 import android.hardware.usb.V1_1.PortStatus_1_1; 61 import android.hardware.usb.V1_2.IUsbCallback; 62 import android.hardware.usb.V1_2.PortStatus; 63 import android.hidl.manager.V1_0.IServiceManager; 64 import android.hidl.manager.V1_0.IServiceNotification; 65 import android.os.Bundle; 66 import android.os.Handler; 67 import android.os.HwBinder; 68 import android.os.IBinder; 69 import android.os.IInterface; 70 import android.os.Message; 71 import android.os.Parcel; 72 import android.os.Parcelable; 73 import android.os.RemoteException; 74 import android.os.SystemClock; 75 import android.os.UserHandle; 76 import android.service.ServiceProtoEnums; 77 import android.service.usb.UsbPortInfoProto; 78 import android.service.usb.UsbPortManagerProto; 79 import android.util.ArrayMap; 80 import android.util.IntArray; 81 import android.util.Log; 82 import android.util.Slog; 83 84 import com.android.internal.annotations.GuardedBy; 85 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 86 import com.android.internal.notification.SystemNotificationChannels; 87 import com.android.internal.util.FrameworkStatsLog; 88 import com.android.internal.util.IndentingPrintWriter; 89 import com.android.internal.util.dump.DualDumpOutputStream; 90 import com.android.server.FgThread; 91 import com.android.server.usb.hal.port.RawPortInfo; 92 import com.android.server.usb.hal.port.UsbPortHal; 93 import com.android.server.usb.hal.port.UsbPortHalInstance; 94 95 import java.util.Arrays; 96 import java.util.ArrayList; 97 import java.util.LinkedList; 98 import java.util.NoSuchElementException; 99 import java.util.Objects; 100 import java.util.concurrent.Executor; 101 102 /** 103 * Allows trusted components to control the properties of physical USB ports 104 * via the IUsb.hal. 105 * <p> 106 * Note: This interface may not be supported on all chipsets since the USB drivers 107 * must be changed to publish this information through the module. At the moment 108 * we only need this for devices with USB Type C ports to allow the System UI to 109 * control USB charging and data direction. On devices that do not support this 110 * interface the list of ports may incorrectly appear to be empty 111 * (but we don't care today). 112 * </p> 113 */ 114 public class UsbPortManager implements IBinder.DeathRecipient { 115 private static final String TAG = "UsbPortManager"; 116 117 private static final int MSG_UPDATE_PORTS = 1; 118 private static final int MSG_SYSTEM_READY = 2; 119 120 // All non-trivial role combinations. 121 private static final int COMBO_SOURCE_HOST = 122 UsbPort.combineRolesAsBit(POWER_ROLE_SOURCE, DATA_ROLE_HOST); 123 private static final int COMBO_SOURCE_DEVICE = UsbPort.combineRolesAsBit( 124 POWER_ROLE_SOURCE, DATA_ROLE_DEVICE); 125 private static final int COMBO_SINK_HOST = 126 UsbPort.combineRolesAsBit(POWER_ROLE_SINK, DATA_ROLE_HOST); 127 private static final int COMBO_SINK_DEVICE = UsbPort.combineRolesAsBit( 128 POWER_ROLE_SINK, DATA_ROLE_DEVICE); 129 130 // The system context. 131 private final Context mContext; 132 133 // Callback when the UsbPort status is changed by the kernel. 134 // Mostly due a command sent by the remote Usb device. 135 //private HALCallback mHALCallback = new HALCallback(null, this); 136 137 // Used as the key while sending the bundle to Main thread. 138 private static final String PORT_INFO = "port_info"; 139 140 // This is monitored to prevent updating the protInfo before the system 141 // is ready. 142 private boolean mSystemReady; 143 144 // Mutex for all mutable shared state. 145 private final Object mLock = new Object(); 146 147 // List of all ports, indexed by id. 148 // Ports may temporarily have different dispositions as they are added or removed 149 // but the class invariant is that this list will only contain ports with DISPOSITION_READY 150 // except while updatePortsLocked() is in progress. 151 private final ArrayMap<String, PortInfo> mPorts = new ArrayMap<>(); 152 153 // List of all simulated ports, indexed by id. 154 private final ArrayMap<String, RawPortInfo> mSimulatedPorts = 155 new ArrayMap<>(); 156 157 // Maintains the current connected status of the port. 158 // Uploads logs only when the connection status is changes. 159 private final ArrayMap<String, Boolean> mConnected = new ArrayMap<>(); 160 161 // Maintains the USB contaminant status that was previously logged. 162 // Logs get uploaded only when contaminant presence status changes. 163 private final ArrayMap<String, Integer> mContaminantStatus = new ArrayMap<>(); 164 165 private NotificationManager mNotificationManager; 166 167 // Maintains a list of DisplayPortAltModeInfo Event listeners, 168 // protected by mDisplayPortListenerLock for broadcasts/register/unregister events 169 private final Object mDisplayPortListenerLock = new Object(); 170 private final ArrayMap<IBinder, IDisplayPortAltModeInfoListener> mDisplayPortListeners = 171 new ArrayMap<IBinder, IDisplayPortAltModeInfoListener>(); 172 173 /** 174 * If there currently is a notification related to contaminated USB port management 175 * shown the id of the notification, or 0 if there is none. 176 */ 177 private int mIsPortContaminatedNotificationId; 178 179 private UsbPortHal mUsbPortHal; 180 181 private long mTransactionId; 182 UsbPortManager(Context context)183 public UsbPortManager(Context context) { 184 mContext = context; 185 mUsbPortHal = UsbPortHalInstance.getInstance(this, null); 186 logAndPrint(Log.DEBUG, null, "getInstance done"); 187 } 188 systemReady()189 public void systemReady() { 190 mSystemReady = true; 191 if (mUsbPortHal != null) { 192 mUsbPortHal.systemReady(); 193 try { 194 mUsbPortHal.queryPortStatus(++mTransactionId); 195 } catch (Exception e) { 196 logAndPrintException(null, 197 "ServiceStart: Failed to query port status", e); 198 } 199 } 200 mHandler.sendEmptyMessage(MSG_SYSTEM_READY); 201 } 202 updateContaminantNotification()203 private void updateContaminantNotification() { 204 PortInfo currentPortInfo = null; 205 Resources r = mContext.getResources(); 206 int contaminantStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED; 207 208 // Not handling multiple ports here. Showing the notification 209 // for the first port that returns CONTAMINANT_PRESENCE_DETECTED. 210 for (PortInfo portInfo : mPorts.values()) { 211 contaminantStatus = portInfo.mUsbPortStatus.getContaminantDetectionStatus(); 212 if (contaminantStatus == UsbPortStatus.CONTAMINANT_DETECTION_DETECTED 213 || contaminantStatus == UsbPortStatus.CONTAMINANT_DETECTION_DISABLED) { 214 currentPortInfo = portInfo; 215 break; 216 } 217 } 218 219 // Current contminant status is detected while "safe to use usb port" 220 // notification is displayed. Remove safe to use usb port notification 221 // and push contaminant detected notification. 222 if (contaminantStatus == UsbPortStatus.CONTAMINANT_DETECTION_DETECTED 223 && mIsPortContaminatedNotificationId 224 != SystemMessage.NOTE_USB_CONTAMINANT_DETECTED) { 225 if (mIsPortContaminatedNotificationId 226 == SystemMessage.NOTE_USB_CONTAMINANT_NOT_DETECTED) { 227 mNotificationManager.cancelAsUser(null, mIsPortContaminatedNotificationId, 228 UserHandle.ALL); 229 } 230 231 mIsPortContaminatedNotificationId = SystemMessage.NOTE_USB_CONTAMINANT_DETECTED; 232 int titleRes = com.android.internal.R.string.usb_contaminant_detected_title; 233 CharSequence title = r.getText(titleRes); 234 String channel = SystemNotificationChannels.ALERTS; 235 CharSequence message = r.getText( 236 com.android.internal.R.string.usb_contaminant_detected_message); 237 238 Intent intent = new Intent(); 239 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 240 intent.setComponent(ComponentName.unflattenFromString(r.getString( 241 com.android.internal.R.string.config_usbContaminantActivity))); 242 intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(currentPortInfo.mUsbPort)); 243 intent.putExtra(UsbManager.EXTRA_PORT_STATUS, currentPortInfo.mUsbPortStatus); 244 245 // Simple notification clicks are immutable 246 PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, 247 intent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT); 248 249 Notification.Builder builder = new Notification.Builder(mContext, channel) 250 .setOngoing(true) 251 .setTicker(title) 252 .setColor(mContext.getColor( 253 com.android.internal.R.color 254 .system_notification_accent_color)) 255 .setContentIntent(pi) 256 .setContentTitle(title) 257 .setContentText(message) 258 .setVisibility(Notification.VISIBILITY_PUBLIC) 259 .setSmallIcon(android.R.drawable.stat_sys_warning) 260 .setStyle(new Notification.BigTextStyle() 261 .bigText(message)); 262 Notification notification = builder.build(); 263 mNotificationManager.notifyAsUser(null, mIsPortContaminatedNotificationId, notification, 264 UserHandle.ALL); 265 // No contaminant is detected but contaminant detection notification is displayed. 266 // Remove contaminant detection notification and push safe to use USB port notification. 267 } else if (contaminantStatus != UsbPortStatus.CONTAMINANT_DETECTION_DETECTED 268 && mIsPortContaminatedNotificationId 269 == SystemMessage.NOTE_USB_CONTAMINANT_DETECTED) { 270 mNotificationManager.cancelAsUser(null, mIsPortContaminatedNotificationId, 271 UserHandle.ALL); 272 mIsPortContaminatedNotificationId = 0; 273 274 // Dont show safe to use notification when contaminant detection is disabled. 275 // Show only when the status is changing from detected to not detected. 276 if (contaminantStatus == UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED) { 277 mIsPortContaminatedNotificationId = 278 SystemMessage.NOTE_USB_CONTAMINANT_NOT_DETECTED; 279 int titleRes = com.android.internal.R.string.usb_contaminant_not_detected_title; 280 CharSequence title = r.getText(titleRes); 281 String channel = SystemNotificationChannels.ALERTS; 282 CharSequence message = r.getText( 283 com.android.internal.R.string.usb_contaminant_not_detected_message); 284 285 Notification.Builder builder = new Notification.Builder(mContext, channel) 286 .setSmallIcon(com.android.internal.R.drawable.ic_usb_48dp) 287 .setTicker(title) 288 .setColor(mContext.getColor( 289 com.android.internal.R.color 290 .system_notification_accent_color)) 291 .setContentTitle(title) 292 .setContentText(message) 293 .setVisibility(Notification.VISIBILITY_PUBLIC) 294 .setStyle(new Notification.BigTextStyle() 295 .bigText(message)); 296 Notification notification = builder.build(); 297 mNotificationManager.notifyAsUser(null, mIsPortContaminatedNotificationId, 298 notification, UserHandle.ALL); 299 } 300 } 301 } 302 getPorts()303 public UsbPort[] getPorts() { 304 synchronized (mLock) { 305 final int count = mPorts.size(); 306 final UsbPort[] result = new UsbPort[count]; 307 for (int i = 0; i < count; i++) { 308 result[i] = mPorts.valueAt(i).mUsbPort; 309 } 310 return result; 311 } 312 } 313 getPortStatus(String portId)314 public UsbPortStatus getPortStatus(String portId) { 315 synchronized (mLock) { 316 final PortInfo portInfo = mPorts.get(portId); 317 return portInfo != null ? portInfo.mUsbPortStatus : null; 318 } 319 } 320 321 /** 322 * Enables/disables contaminant detection. 323 * 324 * @param portId port identifier. 325 * @param enable enable contaminant detection when set to true. 326 */ enableContaminantDetection(@onNull String portId, boolean enable, @NonNull IndentingPrintWriter pw)327 public void enableContaminantDetection(@NonNull String portId, boolean enable, 328 @NonNull IndentingPrintWriter pw) { 329 final PortInfo portInfo = mPorts.get(portId); 330 if (portInfo == null) { 331 if (pw != null) { 332 pw.println("No such USB port: " + portId); 333 } 334 return; 335 } 336 337 if (!portInfo.mUsbPort.supportsEnableContaminantPresenceDetection()) { 338 return; 339 } 340 341 if ((enable && portInfo.mUsbPortStatus.getContaminantDetectionStatus() 342 != UsbPortStatus.CONTAMINANT_DETECTION_DISABLED) || (!enable 343 && portInfo.mUsbPortStatus.getContaminantDetectionStatus() 344 == UsbPortStatus.CONTAMINANT_DETECTION_DISABLED) 345 || (portInfo.mUsbPortStatus.getContaminantDetectionStatus() 346 == UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED)) { 347 return; 348 } 349 350 try { 351 mUsbPortHal.enableContaminantPresenceDetection(portId, enable, ++mTransactionId); 352 } catch (Exception e) { 353 logAndPrintException(pw, "Failed to set contaminant detection", e); 354 } 355 } 356 357 /** 358 * Limits power transfer in/out of USB-C port. 359 * 360 * @param portId port identifier. 361 * @param limit limit power transfer when true. 362 */ enableLimitPowerTransfer(@onNull String portId, boolean limit, long transactionId, IUsbOperationInternal callback, IndentingPrintWriter pw)363 public void enableLimitPowerTransfer(@NonNull String portId, boolean limit, long transactionId, 364 IUsbOperationInternal callback, IndentingPrintWriter pw) { 365 Objects.requireNonNull(portId); 366 final PortInfo portInfo = mPorts.get(portId); 367 if (portInfo == null) { 368 logAndPrint(Log.ERROR, pw, "enableLimitPowerTransfer: No such port: " + portId 369 + " opId:" + transactionId); 370 try { 371 if (callback != null) { 372 callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH); 373 } 374 } catch (RemoteException e) { 375 logAndPrintException(pw, 376 "enableLimitPowerTransfer: Failed to call OperationComplete. opId:" 377 + transactionId, e); 378 } 379 return; 380 } 381 382 try { 383 try { 384 mUsbPortHal.enableLimitPowerTransfer(portId, limit, transactionId, callback); 385 } catch (Exception e) { 386 logAndPrintException(pw, 387 "enableLimitPowerTransfer: Failed to limit power transfer. opId:" 388 + transactionId , e); 389 if (callback != null) { 390 callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); 391 } 392 } 393 } catch (RemoteException e) { 394 logAndPrintException(pw, 395 "enableLimitPowerTransfer:Failed to call onOperationComplete. opId:" 396 + transactionId, e); 397 } 398 } 399 400 /** 401 * Enables USB data when disabled due to {@link UsbPortStatus#DATA_STATUS_DISABLED_DOCK} 402 */ enableUsbDataWhileDocked(@onNull String portId, long transactionId, IUsbOperationInternal callback, IndentingPrintWriter pw)403 public void enableUsbDataWhileDocked(@NonNull String portId, long transactionId, 404 IUsbOperationInternal callback, IndentingPrintWriter pw) { 405 Objects.requireNonNull(portId); 406 final PortInfo portInfo = mPorts.get(portId); 407 if (portInfo == null) { 408 logAndPrint(Log.ERROR, pw, "enableUsbDataWhileDocked: No such port: " + portId 409 + " opId:" + transactionId); 410 try { 411 if (callback != null) { 412 callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH); 413 } 414 } catch (RemoteException e) { 415 logAndPrintException(pw, 416 "enableUsbDataWhileDocked: Failed to call OperationComplete. opId:" 417 + transactionId, e); 418 } 419 return; 420 } 421 422 try { 423 try { 424 mUsbPortHal.enableUsbDataWhileDocked(portId, transactionId, callback); 425 } catch (Exception e) { 426 logAndPrintException(pw, 427 "enableUsbDataWhileDocked: Failed to limit power transfer. opId:" 428 + transactionId , e); 429 if (callback != null) { 430 callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); 431 } 432 } 433 } catch (RemoteException e) { 434 logAndPrintException(pw, 435 "enableUsbDataWhileDocked:Failed to call onOperationComplete. opId:" 436 + transactionId, e); 437 } 438 } 439 440 /** 441 * Enable/disable the USB data signaling 442 * 443 * @param enable enable or disable USB data signaling 444 */ enableUsbData(@onNull String portId, boolean enable, int transactionId, @NonNull IUsbOperationInternal callback, IndentingPrintWriter pw)445 public boolean enableUsbData(@NonNull String portId, boolean enable, int transactionId, 446 @NonNull IUsbOperationInternal callback, IndentingPrintWriter pw) { 447 Objects.requireNonNull(callback); 448 Objects.requireNonNull(portId); 449 final PortInfo portInfo = mPorts.get(portId); 450 if (portInfo == null) { 451 logAndPrint(Log.ERROR, pw, "enableUsbData: No such port: " + portId 452 + " opId:" + transactionId); 453 try { 454 callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH); 455 } catch (RemoteException e) { 456 logAndPrintException(pw, 457 "enableUsbData: Failed to call OperationComplete. opId:" 458 + transactionId, e); 459 } 460 return false; 461 } 462 463 try { 464 try { 465 return mUsbPortHal.enableUsbData(portId, enable, transactionId, callback); 466 } catch (Exception e) { 467 logAndPrintException(pw, 468 "enableUsbData: Failed to invoke enableUsbData. opId:" 469 + transactionId , e); 470 callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); 471 } 472 } catch (RemoteException e) { 473 logAndPrintException(pw, 474 "enableUsbData: Failed to call onOperationComplete. opId:" 475 + transactionId, e); 476 } 477 478 return false; 479 } 480 481 /** 482 * Get USB HAL version 483 * 484 * @param none 485 * @return {@link UsbManager#USB_HAL_RETRY} returned when hal version 486 * is yet to be determined. 487 */ getUsbHalVersion()488 public int getUsbHalVersion() { 489 if (mUsbPortHal != null) { 490 try { 491 return mUsbPortHal.getUsbHalVersion(); 492 } catch (RemoteException e) { 493 return UsbManager.USB_HAL_RETRY; 494 } 495 } 496 return UsbManager.USB_HAL_RETRY; 497 } 498 toHalUsbDataRole(int usbDataRole)499 private int toHalUsbDataRole(int usbDataRole) { 500 if (usbDataRole == DATA_ROLE_DEVICE) 501 return HAL_DATA_ROLE_DEVICE; 502 else 503 return HAL_DATA_ROLE_HOST; 504 } 505 toHalUsbPowerRole(int usbPowerRole)506 private int toHalUsbPowerRole(int usbPowerRole) { 507 if (usbPowerRole == POWER_ROLE_SINK) 508 return HAL_POWER_ROLE_SINK; 509 else 510 return HAL_POWER_ROLE_SOURCE; 511 } 512 toHalUsbMode(int usbMode)513 private int toHalUsbMode(int usbMode) { 514 if (usbMode == MODE_UFP) 515 return HAL_MODE_UFP; 516 else 517 return HAL_MODE_DFP; 518 } 519 520 /** 521 * Reset USB port. 522 * 523 * @param portId port identifier. 524 */ resetUsbPort(@onNull String portId, int transactionId, @NonNull IUsbOperationInternal callback, IndentingPrintWriter pw)525 public void resetUsbPort(@NonNull String portId, int transactionId, 526 @NonNull IUsbOperationInternal callback, IndentingPrintWriter pw) { 527 synchronized (mLock) { 528 Objects.requireNonNull(callback); 529 Objects.requireNonNull(portId); 530 final PortInfo portInfo = mPorts.get(portId); 531 if (portInfo == null) { 532 logAndPrint(Log.ERROR, pw, "resetUsbPort: No such port: " + portId 533 + " opId:" + transactionId); 534 try { 535 callback.onOperationComplete( 536 USB_OPERATION_ERROR_PORT_MISMATCH); 537 } catch (RemoteException e) { 538 logAndPrintException(pw, 539 "resetUsbPort: Failed to call OperationComplete. opId:" 540 + transactionId, e); 541 } 542 } 543 544 try { 545 try { 546 mUsbPortHal.resetUsbPort(portId, transactionId, callback); 547 } catch (Exception e) { 548 logAndPrintException(pw, 549 "reseetUsbPort: Failed to resetUsbPort. opId:" 550 + transactionId , e); 551 callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL); 552 } 553 } catch (RemoteException e) { 554 logAndPrintException(pw, 555 "resetUsbPort: Failed to call onOperationComplete. opId:" 556 + transactionId, e); 557 } 558 } 559 } 560 setPortRoles(String portId, int newPowerRole, int newDataRole, IndentingPrintWriter pw)561 public void setPortRoles(String portId, int newPowerRole, int newDataRole, 562 IndentingPrintWriter pw) { 563 synchronized (mLock) { 564 final PortInfo portInfo = mPorts.get(portId); 565 if (portInfo == null) { 566 if (pw != null) { 567 pw.println("No such USB port: " + portId); 568 } 569 return; 570 } 571 572 // Check whether the new role is actually supported. 573 if (!portInfo.mUsbPortStatus.isRoleCombinationSupported(newPowerRole, newDataRole)) { 574 logAndPrint(Log.ERROR, pw, "Attempted to set USB port into unsupported " 575 + "role combination: portId=" + portId 576 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 577 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 578 return; 579 } 580 581 // Check whether anything actually changed. 582 final int currentDataRole = portInfo.mUsbPortStatus.getCurrentDataRole(); 583 final int currentPowerRole = portInfo.mUsbPortStatus.getCurrentPowerRole(); 584 if (currentDataRole == newDataRole && currentPowerRole == newPowerRole) { 585 if (pw != null) { 586 pw.println("No change."); 587 } 588 return; 589 } 590 591 // Determine whether we need to change the mode in order to accomplish this goal. 592 // We prefer not to do this since it's more likely to fail. 593 // 594 // Note: Arguably it might be worth allowing the client to influence this policy 595 // decision so that we could show more powerful developer facing UI but let's 596 // see how far we can get without having to do that. 597 final boolean canChangeMode = portInfo.mCanChangeMode; 598 final boolean canChangePowerRole = portInfo.mCanChangePowerRole; 599 final boolean canChangeDataRole = portInfo.mCanChangeDataRole; 600 final int currentMode = portInfo.mUsbPortStatus.getCurrentMode(); 601 final int newMode; 602 if ((!canChangePowerRole && currentPowerRole != newPowerRole) 603 || (!canChangeDataRole && currentDataRole != newDataRole)) { 604 if (canChangeMode && newPowerRole == POWER_ROLE_SOURCE 605 && newDataRole == DATA_ROLE_HOST) { 606 newMode = MODE_DFP; 607 } else if (canChangeMode && newPowerRole == POWER_ROLE_SINK 608 && newDataRole == DATA_ROLE_DEVICE) { 609 newMode = MODE_UFP; 610 } else { 611 logAndPrint(Log.ERROR, pw, "Found mismatch in supported USB role combinations " 612 + "while attempting to change role: " + portInfo 613 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 614 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 615 return; 616 } 617 } else { 618 newMode = currentMode; 619 } 620 621 // Make it happen. 622 logAndPrint(Log.INFO, pw, "Setting USB port mode and role: portId=" + portId 623 + ", currentMode=" + UsbPort.modeToString(currentMode) 624 + ", currentPowerRole=" + UsbPort.powerRoleToString(currentPowerRole) 625 + ", currentDataRole=" + UsbPort.dataRoleToString(currentDataRole) 626 + ", newMode=" + UsbPort.modeToString(newMode) 627 + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole) 628 + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)); 629 630 RawPortInfo sim = mSimulatedPorts.get(portId); 631 if (sim != null) { 632 // Change simulated state. 633 sim.currentMode = newMode; 634 sim.currentPowerRole = newPowerRole; 635 sim.currentDataRole = newDataRole; 636 updatePortsLocked(pw, null); 637 } else if (mUsbPortHal != null) { 638 if (currentMode != newMode) { 639 // Changing the mode will have the side-effect of also changing 640 // the power and data roles but it might take some time to apply 641 // and the renegotiation might fail. Due to limitations of the USB 642 // hardware, we have no way of knowing whether it will work apriori 643 // which is why we would prefer to set the power and data roles 644 // directly instead. 645 646 logAndPrint(Log.ERROR, pw, "Trying to set the USB port mode: " 647 + "portId=" + portId 648 + ", newMode=" + UsbPort.modeToString(newMode)); 649 try { 650 mUsbPortHal.switchMode(portId, toHalUsbMode(newMode), ++mTransactionId); 651 } catch (Exception e) { 652 logAndPrintException(pw, "Failed to set the USB port mode: " 653 + "portId=" + portId 654 + ", newMode=" + UsbPort.modeToString(newMode), e); 655 } 656 } else { 657 // Change power and data role independently as needed. 658 if (currentPowerRole != newPowerRole) { 659 try { 660 mUsbPortHal.switchPowerRole(portId, toHalUsbPowerRole(newPowerRole), 661 ++mTransactionId); 662 } catch (Exception e) { 663 logAndPrintException(pw, "Failed to set the USB port power role: " 664 + "portId=" + portId 665 + ", newPowerRole=" + UsbPort.powerRoleToString 666 (newPowerRole), 667 e); 668 return; 669 } 670 } 671 if (currentDataRole != newDataRole) { 672 try { 673 mUsbPortHal.switchDataRole(portId, toHalUsbDataRole(newDataRole), 674 ++mTransactionId); 675 } catch (Exception e) { 676 logAndPrintException(pw, "Failed to set the USB port data role: " 677 + "portId=" + portId 678 + ", newDataRole=" + UsbPort.dataRoleToString 679 (newDataRole), 680 e); 681 } 682 } 683 } 684 } 685 } 686 } 687 688 @Override binderDied()689 public void binderDied() { 690 // All calls should go to binderDied(IBinder deadBinder) 691 Slog.wtf(TAG, "binderDied() called unexpectedly"); 692 } 693 binderDied(IBinder deadBinder)694 public void binderDied(IBinder deadBinder) { 695 synchronized (mDisplayPortListenerLock) { 696 mDisplayPortListeners.remove(deadBinder); 697 Slog.d(TAG, "DisplayPortEventDispatcherListener died at " + deadBinder); 698 } 699 } 700 registerForDisplayPortEvents( @onNull IDisplayPortAltModeInfoListener listener)701 public boolean registerForDisplayPortEvents( 702 @NonNull IDisplayPortAltModeInfoListener listener) { 703 synchronized (mDisplayPortListenerLock) { 704 if (!mDisplayPortListeners.containsKey(listener.asBinder())) { 705 try { 706 listener.asBinder().linkToDeath(this, 0); 707 } catch (RemoteException e) { 708 logAndPrintException(null, "Caught RemoteException in " + 709 "registerForDisplayPortEvents: ", e); 710 return false; 711 } 712 mDisplayPortListeners.put(listener.asBinder(), listener); 713 return true; 714 } 715 } 716 return false; 717 } 718 unregisterForDisplayPortEvents( @onNull IDisplayPortAltModeInfoListener listener)719 public void unregisterForDisplayPortEvents( 720 @NonNull IDisplayPortAltModeInfoListener listener) { 721 synchronized (mDisplayPortListenerLock) { 722 if (mDisplayPortListeners.remove(listener.asBinder()) != null) { 723 listener.asBinder().unlinkToDeath(this, 0); 724 } 725 } 726 } 727 updatePorts(ArrayList<RawPortInfo> newPortInfo)728 public void updatePorts(ArrayList<RawPortInfo> newPortInfo) { 729 Message message = mHandler.obtainMessage(); 730 Bundle bundle = new Bundle(); 731 bundle.putParcelableArrayList(PORT_INFO, newPortInfo); 732 message.what = MSG_UPDATE_PORTS; 733 message.setData(bundle); 734 mHandler.sendMessage(message); 735 } 736 addSimulatedPort(String portId, int supportedModes, boolean supportsComplianceWarnings, boolean supportsDisplayPortAltMode, IndentingPrintWriter pw)737 public void addSimulatedPort(String portId, int supportedModes, 738 boolean supportsComplianceWarnings, boolean supportsDisplayPortAltMode, 739 IndentingPrintWriter pw) { 740 int supportedAltModes = supportsDisplayPortAltMode ? 741 UsbPort.FLAG_ALT_MODE_TYPE_DISPLAYPORT : 0; 742 DisplayPortAltModeInfo displayPortAltModeInfo = null; 743 744 if (supportsDisplayPortAltMode) { 745 displayPortAltModeInfo = new DisplayPortAltModeInfo(); 746 } 747 748 synchronized (mLock) { 749 if (mSimulatedPorts.containsKey(portId)) { 750 pw.println("Port with same name already exists. Please remove it first."); 751 return; 752 } 753 754 pw.println("Adding simulated port: portId=" + portId 755 + ", supportedModes=" + UsbPort.modeToString(supportedModes)); 756 mSimulatedPorts.put(portId, 757 new RawPortInfo( 758 portId, 759 supportedModes, 760 UsbPortStatus.CONTAMINANT_PROTECTION_NONE, 761 UsbPortStatus.MODE_NONE, 762 false, 763 UsbPortStatus.POWER_ROLE_NONE, 764 false, 765 UsbPortStatus.DATA_ROLE_NONE, 766 false, 767 false, 768 UsbPortStatus.CONTAMINANT_PROTECTION_NONE, 769 false, 770 UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED, 771 UsbPortStatus.DATA_STATUS_UNKNOWN, 772 false, 773 UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN, 774 supportsComplianceWarnings, 775 new int[] {}, 776 UsbPortStatus.PLUG_STATE_UNKNOWN, 777 supportedAltModes, 778 displayPortAltModeInfo)); 779 updatePortsLocked(pw, null); 780 } 781 } 782 connectSimulatedPort(String portId, int mode, boolean canChangeMode, int powerRole, boolean canChangePowerRole, int dataRole, boolean canChangeDataRole, IndentingPrintWriter pw)783 public void connectSimulatedPort(String portId, int mode, boolean canChangeMode, 784 int powerRole, boolean canChangePowerRole, 785 int dataRole, boolean canChangeDataRole, IndentingPrintWriter pw) { 786 synchronized (mLock) { 787 final RawPortInfo portInfo = mSimulatedPorts.get(portId); 788 if (portInfo == null) { 789 pw.println("Cannot connect simulated port which does not exist."); 790 return; 791 } 792 793 if (mode == 0 || powerRole == 0 || dataRole == 0) { 794 pw.println("Cannot connect simulated port in null mode, " 795 + "power role, or data role."); 796 return; 797 } 798 799 if ((portInfo.supportedModes & mode) == 0) { 800 pw.println("Simulated port does not support mode: " + UsbPort.modeToString(mode)); 801 return; 802 } 803 804 pw.println("Connecting simulated port: portId=" + portId 805 + ", mode=" + UsbPort.modeToString(mode) 806 + ", canChangeMode=" + canChangeMode 807 + ", powerRole=" + UsbPort.powerRoleToString(powerRole) 808 + ", canChangePowerRole=" + canChangePowerRole 809 + ", dataRole=" + UsbPort.dataRoleToString(dataRole) 810 + ", canChangeDataRole=" + canChangeDataRole); 811 portInfo.currentMode = mode; 812 portInfo.canChangeMode = canChangeMode; 813 portInfo.currentPowerRole = powerRole; 814 portInfo.canChangePowerRole = canChangePowerRole; 815 portInfo.currentDataRole = dataRole; 816 portInfo.canChangeDataRole = canChangeDataRole; 817 updatePortsLocked(pw, null); 818 } 819 } 820 821 /** 822 * Sets contaminant status for simulated USB port objects. 823 */ simulateContaminantStatus(String portId, boolean detected, IndentingPrintWriter pw)824 public void simulateContaminantStatus(String portId, boolean detected, 825 IndentingPrintWriter pw) { 826 synchronized (mLock) { 827 final RawPortInfo portInfo = mSimulatedPorts.get(portId); 828 if (portInfo == null) { 829 pw.println("Simulated port not found."); 830 return; 831 } 832 833 pw.println("Simulating wet port: portId=" + portId 834 + ", wet=" + detected); 835 portInfo.contaminantDetectionStatus = detected 836 ? UsbPortStatus.CONTAMINANT_DETECTION_DETECTED 837 : UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED; 838 updatePortsLocked(pw, null); 839 } 840 } 841 842 /** 843 * Sets Compliance Warnings for simulated USB port objects. 844 */ simulateComplianceWarnings(String portId, String complianceWarningsString, IndentingPrintWriter pw)845 public void simulateComplianceWarnings(String portId, String complianceWarningsString, 846 IndentingPrintWriter pw) { 847 synchronized (mLock) { 848 final RawPortInfo portInfo = mSimulatedPorts.get(portId); 849 if (portInfo == null) { 850 pw.println("Simulated port not found"); 851 return; 852 } 853 854 IntArray complianceWarnings = new IntArray(); 855 for (String s : complianceWarningsString.split("[, ]")) { 856 if (s.length() > 0) { 857 complianceWarnings.add(Integer.parseInt(s)); 858 } 859 } 860 pw.println("Simulating Compliance Warnings: portId=" + portId 861 + " Warnings=" + complianceWarningsString); 862 portInfo.complianceWarnings = complianceWarnings.toArray(); 863 updatePortsLocked(pw, null); 864 } 865 } 866 867 simulateDisplayPortAltModeInfo(String portId, int partnerSinkStatus, int cableStatus, int numLanes, boolean hpd, int linkTrainingStatus, IndentingPrintWriter pw)868 public void simulateDisplayPortAltModeInfo(String portId, int partnerSinkStatus, 869 int cableStatus, int numLanes, boolean hpd, int linkTrainingStatus, 870 IndentingPrintWriter pw) { 871 synchronized (mLock) { 872 final RawPortInfo portInfo = mSimulatedPorts.get(portId); 873 if (portInfo == null) { 874 pw.println("Simulated port not found"); 875 return; 876 } 877 878 DisplayPortAltModeInfo displayPortAltModeInfo = 879 new DisplayPortAltModeInfo(partnerSinkStatus, cableStatus, numLanes, hpd, 880 linkTrainingStatus); 881 portInfo.displayPortAltModeInfo = displayPortAltModeInfo; 882 pw.println("Simulating DisplayPort Info: " + displayPortAltModeInfo); 883 updatePortsLocked(pw, null); 884 } 885 886 } 887 disconnectSimulatedPort(String portId, IndentingPrintWriter pw)888 public void disconnectSimulatedPort(String portId, IndentingPrintWriter pw) { 889 synchronized (mLock) { 890 final RawPortInfo portInfo = mSimulatedPorts.get(portId); 891 if (portInfo == null) { 892 pw.println("Cannot disconnect simulated port which does not exist."); 893 return; 894 } 895 896 pw.println("Disconnecting simulated port: portId=" + portId); 897 portInfo.currentMode = 0; 898 portInfo.canChangeMode = false; 899 portInfo.currentPowerRole = 0; 900 portInfo.canChangePowerRole = false; 901 portInfo.currentDataRole = 0; 902 portInfo.canChangeDataRole = false; 903 updatePortsLocked(pw, null); 904 } 905 } 906 removeSimulatedPort(String portId, IndentingPrintWriter pw)907 public void removeSimulatedPort(String portId, IndentingPrintWriter pw) { 908 synchronized (mLock) { 909 final int index = mSimulatedPorts.indexOfKey(portId); 910 if (index < 0) { 911 pw.println("Cannot remove simulated port which does not exist."); 912 return; 913 } 914 915 pw.println("Disconnecting simulated port: portId=" + portId); 916 mSimulatedPorts.removeAt(index); 917 updatePortsLocked(pw, null); 918 } 919 } 920 resetSimulation(IndentingPrintWriter pw)921 public void resetSimulation(IndentingPrintWriter pw) { 922 synchronized (mLock) { 923 pw.println("Removing all simulated ports and ending simulation."); 924 if (!mSimulatedPorts.isEmpty()) { 925 mSimulatedPorts.clear(); 926 updatePortsLocked(pw, null); 927 } 928 } 929 } 930 931 /** 932 * Dump the USB port state. 933 */ dump(DualDumpOutputStream dump, String idName, long id)934 public void dump(DualDumpOutputStream dump, String idName, long id) { 935 long token = dump.start(idName, id); 936 937 synchronized (mLock) { 938 dump.write("is_simulation_active", UsbPortManagerProto.IS_SIMULATION_ACTIVE, 939 !mSimulatedPorts.isEmpty()); 940 941 for (PortInfo portInfo : mPorts.values()) { 942 portInfo.dump(dump, "usb_ports", UsbPortManagerProto.USB_PORTS); 943 } 944 945 dump.write("usb_hal_version", UsbPortManagerProto.HAL_VERSION, getUsbHalVersion()); 946 } 947 948 dump.end(token); 949 } 950 951 /** 952 * Simulated ports directly add the new roles to mSimulatedPorts before calling. 953 * USB hal callback populates and sends the newPortInfo. 954 */ updatePortsLocked(IndentingPrintWriter pw, ArrayList<RawPortInfo> newPortInfo)955 private void updatePortsLocked(IndentingPrintWriter pw, ArrayList<RawPortInfo> newPortInfo) { 956 for (int i = mPorts.size(); i-- > 0; ) { 957 mPorts.valueAt(i).mDisposition = PortInfo.DISPOSITION_REMOVED; 958 } 959 960 // Enumerate all extant ports. 961 if (!mSimulatedPorts.isEmpty()) { 962 final int count = mSimulatedPorts.size(); 963 for (int i = 0; i < count; i++) { 964 final RawPortInfo portInfo = mSimulatedPorts.valueAt(i); 965 addOrUpdatePortLocked(portInfo.portId, portInfo.supportedModes, 966 portInfo.supportedContaminantProtectionModes, 967 portInfo.currentMode, portInfo.canChangeMode, 968 portInfo.currentPowerRole, portInfo.canChangePowerRole, 969 portInfo.currentDataRole, portInfo.canChangeDataRole, 970 portInfo.supportsEnableContaminantPresenceProtection, 971 portInfo.contaminantProtectionStatus, 972 portInfo.supportsEnableContaminantPresenceDetection, 973 portInfo.contaminantDetectionStatus, 974 portInfo.usbDataStatus, 975 portInfo.powerTransferLimited, 976 portInfo.powerBrickConnectionStatus, 977 portInfo.supportsComplianceWarnings, 978 portInfo.complianceWarnings, 979 portInfo.plugState, 980 portInfo.supportedAltModes, 981 portInfo.displayPortAltModeInfo, 982 pw); 983 } 984 } else { 985 for (RawPortInfo currentPortInfo : newPortInfo) { 986 addOrUpdatePortLocked(currentPortInfo.portId, currentPortInfo.supportedModes, 987 currentPortInfo.supportedContaminantProtectionModes, 988 currentPortInfo.currentMode, currentPortInfo.canChangeMode, 989 currentPortInfo.currentPowerRole, currentPortInfo.canChangePowerRole, 990 currentPortInfo.currentDataRole, currentPortInfo.canChangeDataRole, 991 currentPortInfo.supportsEnableContaminantPresenceProtection, 992 currentPortInfo.contaminantProtectionStatus, 993 currentPortInfo.supportsEnableContaminantPresenceDetection, 994 currentPortInfo.contaminantDetectionStatus, 995 currentPortInfo.usbDataStatus, 996 currentPortInfo.powerTransferLimited, 997 currentPortInfo.powerBrickConnectionStatus, 998 currentPortInfo.supportsComplianceWarnings, 999 currentPortInfo.complianceWarnings, 1000 currentPortInfo.plugState, 1001 currentPortInfo.supportedAltModes, 1002 currentPortInfo.displayPortAltModeInfo, 1003 pw); 1004 } 1005 } 1006 1007 // Process the updates. 1008 // Once finished, the list of ports will only contain ports in DISPOSITION_READY. 1009 for (int i = mPorts.size(); i-- > 0; ) { 1010 final PortInfo portInfo = mPorts.valueAt(i); 1011 switch (portInfo.mDisposition) { 1012 case PortInfo.DISPOSITION_ADDED: 1013 handlePortAddedLocked(portInfo, pw); 1014 portInfo.mDisposition = PortInfo.DISPOSITION_READY; 1015 break; 1016 case PortInfo.DISPOSITION_CHANGED: 1017 handlePortChangedLocked(portInfo, pw); 1018 portInfo.mDisposition = PortInfo.DISPOSITION_READY; 1019 break; 1020 case PortInfo.DISPOSITION_REMOVED: 1021 mPorts.removeAt(i); 1022 portInfo.mUsbPortStatus = null; // must do this early 1023 handlePortRemovedLocked(portInfo, pw); 1024 break; 1025 } 1026 if (portInfo.mComplianceWarningChange == portInfo.COMPLIANCE_WARNING_CHANGED) { 1027 handlePortComplianceWarningLocked(portInfo, pw); 1028 } 1029 if (portInfo.mDisplayPortAltModeChange == portInfo.ALTMODE_INFO_CHANGED) { 1030 handleDpAltModeLocked(portInfo, pw); 1031 } 1032 } 1033 } 1034 1035 // 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, int usbDataStatus, boolean powerTransferLimited, int powerBrickConnectionStatus, boolean supportsComplianceWarnings, @NonNull int[] complianceWarnings, int plugState, int supportedAltModes, DisplayPortAltModeInfo displayPortAltModeInfo, IndentingPrintWriter pw)1036 private void addOrUpdatePortLocked(String portId, int supportedModes, 1037 int supportedContaminantProtectionModes, 1038 int currentMode, boolean canChangeMode, 1039 int currentPowerRole, boolean canChangePowerRole, 1040 int currentDataRole, boolean canChangeDataRole, 1041 boolean supportsEnableContaminantPresenceProtection, 1042 int contaminantProtectionStatus, 1043 boolean supportsEnableContaminantPresenceDetection, 1044 int contaminantDetectionStatus, 1045 int usbDataStatus, 1046 boolean powerTransferLimited, 1047 int powerBrickConnectionStatus, 1048 boolean supportsComplianceWarnings, 1049 @NonNull int[] complianceWarnings, 1050 int plugState, 1051 int supportedAltModes, 1052 DisplayPortAltModeInfo displayPortAltModeInfo, 1053 IndentingPrintWriter pw) { 1054 // Only allow mode switch capability for dual role ports. 1055 // Validate that the current mode matches the supported modes we expect. 1056 if ((supportedModes & MODE_DUAL) != MODE_DUAL) { 1057 canChangeMode = false; 1058 if (currentMode != 0 && currentMode != supportedModes) { 1059 logAndPrint(Log.WARN, pw, "Ignoring inconsistent current mode from USB " 1060 + "port driver: supportedModes=" + UsbPort.modeToString(supportedModes) 1061 + ", currentMode=" + UsbPort.modeToString(currentMode)); 1062 currentMode = 0; 1063 } 1064 } 1065 1066 // Determine the supported role combinations. 1067 // Note that the policy is designed to prefer setting the power and data 1068 // role independently rather than changing the mode. 1069 int supportedRoleCombinations = UsbPort.combineRolesAsBit( 1070 currentPowerRole, currentDataRole); 1071 if (currentMode != 0 && currentPowerRole != 0 && currentDataRole != 0) { 1072 if (canChangePowerRole && canChangeDataRole) { 1073 // Can change both power and data role independently. 1074 // Assume all combinations are possible. 1075 supportedRoleCombinations |= 1076 COMBO_SOURCE_HOST | COMBO_SOURCE_DEVICE 1077 | COMBO_SINK_HOST | COMBO_SINK_DEVICE; 1078 } else if (canChangePowerRole) { 1079 // Can only change power role. 1080 // Assume data role must remain at its current value. 1081 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 1082 POWER_ROLE_SOURCE, currentDataRole); 1083 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 1084 POWER_ROLE_SINK, currentDataRole); 1085 } else if (canChangeDataRole) { 1086 // Can only change data role. 1087 // Assume power role must remain at its current value. 1088 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 1089 currentPowerRole, DATA_ROLE_HOST); 1090 supportedRoleCombinations |= UsbPort.combineRolesAsBit( 1091 currentPowerRole, DATA_ROLE_DEVICE); 1092 } else if (canChangeMode) { 1093 // Can only change the mode. 1094 // Assume both standard UFP and DFP configurations will become available 1095 // when this happens. 1096 supportedRoleCombinations |= COMBO_SOURCE_HOST | COMBO_SINK_DEVICE; 1097 } 1098 } 1099 1100 // Update the port data structures. 1101 PortInfo portInfo = mPorts.get(portId); 1102 if (portInfo == null) { 1103 portInfo = new PortInfo(mContext.getSystemService(UsbManager.class), 1104 portId, supportedModes, supportedContaminantProtectionModes, 1105 supportsEnableContaminantPresenceProtection, 1106 supportsEnableContaminantPresenceDetection, 1107 supportsComplianceWarnings, 1108 supportedAltModes); 1109 portInfo.setStatus(currentMode, canChangeMode, 1110 currentPowerRole, canChangePowerRole, 1111 currentDataRole, canChangeDataRole, 1112 supportedRoleCombinations, contaminantProtectionStatus, 1113 contaminantDetectionStatus, usbDataStatus, 1114 powerTransferLimited, powerBrickConnectionStatus, 1115 complianceWarnings, plugState, displayPortAltModeInfo); 1116 mPorts.put(portId, portInfo); 1117 } else { 1118 // Validate that ports aren't changing definition out from under us. 1119 if (supportedModes != portInfo.mUsbPort.getSupportedModes()) { 1120 logAndPrint(Log.WARN, pw, "Ignoring inconsistent list of supported modes from " 1121 + "USB port driver (should be immutable): " 1122 + "previous=" + UsbPort.modeToString( 1123 portInfo.mUsbPort.getSupportedModes()) 1124 + ", current=" + UsbPort.modeToString(supportedModes)); 1125 } 1126 1127 if (supportsEnableContaminantPresenceProtection 1128 != portInfo.mUsbPort.supportsEnableContaminantPresenceProtection()) { 1129 logAndPrint(Log.WARN, pw, 1130 "Ignoring inconsistent supportsEnableContaminantPresenceProtection" 1131 + "USB port driver (should be immutable): " 1132 + "previous=" 1133 + portInfo.mUsbPort.supportsEnableContaminantPresenceProtection() 1134 + ", current=" + supportsEnableContaminantPresenceProtection); 1135 } 1136 1137 if (supportsEnableContaminantPresenceDetection 1138 != portInfo.mUsbPort.supportsEnableContaminantPresenceDetection()) { 1139 logAndPrint(Log.WARN, pw, 1140 "Ignoring inconsistent supportsEnableContaminantPresenceDetection " 1141 + "USB port driver (should be immutable): " 1142 + "previous=" 1143 + portInfo.mUsbPort.supportsEnableContaminantPresenceDetection() 1144 + ", current=" + supportsEnableContaminantPresenceDetection); 1145 } 1146 1147 if (portInfo.setStatus(currentMode, canChangeMode, 1148 currentPowerRole, canChangePowerRole, 1149 currentDataRole, canChangeDataRole, 1150 supportedRoleCombinations, contaminantProtectionStatus, 1151 contaminantDetectionStatus, usbDataStatus, 1152 powerTransferLimited, powerBrickConnectionStatus, 1153 complianceWarnings, plugState, displayPortAltModeInfo)) { 1154 portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED; 1155 } else { 1156 portInfo.mDisposition = PortInfo.DISPOSITION_READY; 1157 } 1158 } 1159 } 1160 handlePortLocked(PortInfo portInfo, IndentingPrintWriter pw)1161 private void handlePortLocked(PortInfo portInfo, IndentingPrintWriter pw) { 1162 sendPortChangedBroadcastLocked(portInfo); 1163 logToStatsd(portInfo, pw); 1164 updateContaminantNotification(); 1165 } 1166 handlePortAddedLocked(PortInfo portInfo, IndentingPrintWriter pw)1167 private void handlePortAddedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 1168 logAndPrint(Log.INFO, pw, "USB port added: " + portInfo); 1169 handlePortLocked(portInfo, pw); 1170 } 1171 handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw)1172 private void handlePortChangedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 1173 logAndPrint(Log.INFO, pw, "USB port changed: " + portInfo); 1174 enableContaminantDetectionIfNeeded(portInfo, pw); 1175 disableLimitPowerTransferIfNeeded(portInfo, pw); 1176 handlePortLocked(portInfo, pw); 1177 } 1178 handlePortComplianceWarningLocked(PortInfo portInfo, IndentingPrintWriter pw)1179 private void handlePortComplianceWarningLocked(PortInfo portInfo, IndentingPrintWriter pw) { 1180 logAndPrint(Log.INFO, pw, "USB port compliance warning changed: " + portInfo); 1181 logToStatsdComplianceWarnings(portInfo); 1182 sendComplianceWarningBroadcastLocked(portInfo); 1183 } 1184 handleDpAltModeLocked(PortInfo portInfo, IndentingPrintWriter pw)1185 private void handleDpAltModeLocked(PortInfo portInfo, IndentingPrintWriter pw) { 1186 logAndPrint(Log.INFO, pw, "USB port DisplayPort Alt Mode Status Changed: " + portInfo); 1187 sendDpAltModeCallbackLocked(portInfo, pw); 1188 } 1189 handlePortRemovedLocked(PortInfo portInfo, IndentingPrintWriter pw)1190 private void handlePortRemovedLocked(PortInfo portInfo, IndentingPrintWriter pw) { 1191 logAndPrint(Log.INFO, pw, "USB port removed: " + portInfo); 1192 handlePortLocked(portInfo, pw); 1193 } 1194 1195 // Constants have to be converted between USB HAL V1.2 ContaminantDetectionStatus 1196 // to usb.proto as proto guidelines recommends 0 to be UNKNOWN/UNSUPPORTTED 1197 // whereas HAL policy is against a loosely defined constant. convertContaminantDetectionStatusToProto(int contaminantDetectionStatus)1198 private static int convertContaminantDetectionStatusToProto(int contaminantDetectionStatus) { 1199 switch (contaminantDetectionStatus) { 1200 case UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED: 1201 return ServiceProtoEnums.CONTAMINANT_STATUS_NOT_SUPPORTED; 1202 case UsbPortStatus.CONTAMINANT_DETECTION_DISABLED: 1203 return ServiceProtoEnums.CONTAMINANT_STATUS_DISABLED; 1204 case UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED: 1205 return ServiceProtoEnums.CONTAMINANT_STATUS_NOT_DETECTED; 1206 case UsbPortStatus.CONTAMINANT_DETECTION_DETECTED: 1207 return ServiceProtoEnums.CONTAMINANT_STATUS_DETECTED; 1208 default: 1209 return ServiceProtoEnums.CONTAMINANT_STATUS_UNKNOWN; 1210 } 1211 } 1212 1213 // Constants have to be converted to stats-log constants toStatsLogConstant(@onNull int[] complianceWarnings)1214 private static int[] toStatsLogConstant(@NonNull int[] complianceWarnings) { 1215 IntArray complianceWarningsProto = new IntArray(); 1216 for (int warning : complianceWarnings) { 1217 switch (warning) { 1218 case UsbPortStatus.COMPLIANCE_WARNING_OTHER: 1219 complianceWarningsProto.add(FrameworkStatsLog 1220 .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_OTHER); 1221 continue; 1222 case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY: 1223 complianceWarningsProto.add(FrameworkStatsLog 1224 .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_DEBUG_ACCESSORY); 1225 continue; 1226 case UsbPortStatus.COMPLIANCE_WARNING_BC_1_2: 1227 complianceWarningsProto.add(FrameworkStatsLog 1228 .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_BC_1_2); 1229 continue; 1230 case UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP: 1231 complianceWarningsProto.add(FrameworkStatsLog 1232 .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_MISSING_RP); 1233 continue; 1234 } 1235 } 1236 return complianceWarningsProto.toArray(); 1237 } 1238 sendPortChangedBroadcastLocked(PortInfo portInfo)1239 private void sendPortChangedBroadcastLocked(PortInfo portInfo) { 1240 final Intent intent = new Intent(UsbManager.ACTION_USB_PORT_CHANGED); 1241 intent.addFlags( 1242 Intent.FLAG_RECEIVER_FOREGROUND | 1243 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1244 intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(portInfo.mUsbPort)); 1245 intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus); 1246 1247 // Guard against possible reentrance by posting the broadcast from the handler 1248 // instead of from within the critical section. 1249 mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 1250 Manifest.permission.MANAGE_USB)); 1251 } 1252 sendComplianceWarningBroadcastLocked(PortInfo portInfo)1253 private void sendComplianceWarningBroadcastLocked(PortInfo portInfo) { 1254 if (portInfo.mComplianceWarningChange == portInfo.COMPLIANCE_WARNING_UNCHANGED) { 1255 return; 1256 } 1257 final Intent intent = new Intent(UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED); 1258 intent.addFlags( 1259 Intent.FLAG_RECEIVER_FOREGROUND | 1260 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1261 intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(portInfo.mUsbPort)); 1262 intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus); 1263 1264 // Guard against possible reentrance by posting the broadcast from the handler 1265 // instead of from within the critical section. 1266 mHandler.post(() -> mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 1267 Manifest.permission.MANAGE_USB)); 1268 } 1269 sendDpAltModeCallbackLocked(PortInfo portInfo, IndentingPrintWriter pw)1270 private void sendDpAltModeCallbackLocked(PortInfo portInfo, IndentingPrintWriter pw) { 1271 String portId = portInfo.mUsbPort.getId(); 1272 synchronized (mDisplayPortListenerLock) { 1273 for (IDisplayPortAltModeInfoListener mListener : mDisplayPortListeners.values()) { 1274 try { 1275 mListener.onDisplayPortAltModeInfoChanged(portId, 1276 portInfo.mUsbPortStatus.getDisplayPortAltModeInfo()); 1277 } catch (RemoteException e) { 1278 logAndPrintException(pw, "Caught RemoteException at " 1279 + "sendDpAltModeCallbackLocked", e); 1280 } 1281 } 1282 } 1283 } 1284 enableContaminantDetectionIfNeeded(PortInfo portInfo, IndentingPrintWriter pw)1285 private void enableContaminantDetectionIfNeeded(PortInfo portInfo, IndentingPrintWriter pw) { 1286 if (!mConnected.containsKey(portInfo.mUsbPort.getId())) { 1287 return; 1288 } 1289 1290 if (mConnected.get(portInfo.mUsbPort.getId()) 1291 && !portInfo.mUsbPortStatus.isConnected() 1292 && portInfo.mUsbPortStatus.getContaminantDetectionStatus() 1293 == UsbPortStatus.CONTAMINANT_DETECTION_DISABLED) { 1294 // Contaminant detection might have been temporarily disabled by the user 1295 // through SystemUI. 1296 // Re-enable contaminant detection when the accessory is unplugged. 1297 enableContaminantDetection(portInfo.mUsbPort.getId(), true, pw); 1298 } 1299 } 1300 disableLimitPowerTransferIfNeeded(PortInfo portInfo, IndentingPrintWriter pw)1301 private void disableLimitPowerTransferIfNeeded(PortInfo portInfo, IndentingPrintWriter pw) { 1302 if (!mConnected.containsKey(portInfo.mUsbPort.getId())) { 1303 return; 1304 } 1305 1306 if (mConnected.get(portInfo.mUsbPort.getId()) 1307 && !portInfo.mUsbPortStatus.isConnected() 1308 && portInfo.mUsbPortStatus.isPowerTransferLimited()) { 1309 // Relax enableLimitPowerTransfer upon unplug. 1310 enableLimitPowerTransfer(portInfo.mUsbPort.getId(), false, ++mTransactionId, null, pw); 1311 } 1312 } 1313 logToStatsd(PortInfo portInfo, IndentingPrintWriter pw)1314 private void logToStatsd(PortInfo portInfo, IndentingPrintWriter pw) { 1315 // Port is removed 1316 if (portInfo.mUsbPortStatus == null) { 1317 if (mConnected.containsKey(portInfo.mUsbPort.getId())) { 1318 //Previous logged a connected. Set it to disconnected. 1319 if (mConnected.get(portInfo.mUsbPort.getId())) { 1320 FrameworkStatsLog.write(FrameworkStatsLog.USB_CONNECTOR_STATE_CHANGED, 1321 FrameworkStatsLog 1322 .USB_CONNECTOR_STATE_CHANGED__STATE__STATE_DISCONNECTED, 1323 portInfo.mUsbPort.getId(), portInfo.mLastConnectDurationMillis); 1324 } 1325 mConnected.remove(portInfo.mUsbPort.getId()); 1326 } 1327 1328 if (mContaminantStatus.containsKey(portInfo.mUsbPort.getId())) { 1329 //Previous logged a contaminant detected. Set it to not detected. 1330 if ((mContaminantStatus.get(portInfo.mUsbPort.getId()) 1331 == UsbPortStatus.CONTAMINANT_DETECTION_DETECTED)) { 1332 FrameworkStatsLog.write(FrameworkStatsLog.USB_CONTAMINANT_REPORTED, 1333 portInfo.mUsbPort.getId(), 1334 convertContaminantDetectionStatusToProto( 1335 UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED)); 1336 } 1337 mContaminantStatus.remove(portInfo.mUsbPort.getId()); 1338 } 1339 return; 1340 } 1341 1342 if (!mConnected.containsKey(portInfo.mUsbPort.getId()) 1343 || (mConnected.get(portInfo.mUsbPort.getId()) 1344 != portInfo.mUsbPortStatus.isConnected())) { 1345 mConnected.put(portInfo.mUsbPort.getId(), portInfo.mUsbPortStatus.isConnected()); 1346 FrameworkStatsLog.write(FrameworkStatsLog.USB_CONNECTOR_STATE_CHANGED, 1347 portInfo.mUsbPortStatus.isConnected() 1348 ? FrameworkStatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_CONNECTED : 1349 FrameworkStatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_DISCONNECTED, 1350 portInfo.mUsbPort.getId(), portInfo.mLastConnectDurationMillis); 1351 } 1352 1353 if (!mContaminantStatus.containsKey(portInfo.mUsbPort.getId()) 1354 || (mContaminantStatus.get(portInfo.mUsbPort.getId()) 1355 != portInfo.mUsbPortStatus.getContaminantDetectionStatus())) { 1356 mContaminantStatus.put(portInfo.mUsbPort.getId(), 1357 portInfo.mUsbPortStatus.getContaminantDetectionStatus()); 1358 FrameworkStatsLog.write(FrameworkStatsLog.USB_CONTAMINANT_REPORTED, 1359 portInfo.mUsbPort.getId(), 1360 convertContaminantDetectionStatusToProto( 1361 portInfo.mUsbPortStatus.getContaminantDetectionStatus())); 1362 } 1363 } 1364 logToStatsdComplianceWarnings(PortInfo portInfo)1365 private void logToStatsdComplianceWarnings(PortInfo portInfo) { 1366 // Don't report if there isn't anything to report 1367 if (portInfo.mUsbPortStatus == null 1368 || portInfo.mUsbPortStatus.getComplianceWarnings().length == 0) { 1369 return; 1370 } 1371 1372 FrameworkStatsLog.write(FrameworkStatsLog.USB_COMPLIANCE_WARNINGS_REPORTED, 1373 portInfo.mUsbPort.getId(), 1374 toStatsLogConstant(portInfo.mUsbPortStatus.getComplianceWarnings())); 1375 } 1376 logAndPrint(int priority, IndentingPrintWriter pw, String msg)1377 public static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) { 1378 Slog.println(priority, TAG, msg); 1379 if (pw != null) { 1380 pw.println(msg); 1381 } 1382 } 1383 logAndPrintException(IndentingPrintWriter pw, String msg, Exception e)1384 public static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) { 1385 Slog.e(TAG, msg, e); 1386 if (pw != null) { 1387 pw.println(msg + e); 1388 } 1389 } 1390 1391 private final Handler mHandler = new Handler(FgThread.get().getLooper()) { 1392 @Override 1393 public void handleMessage(Message msg) { 1394 switch (msg.what) { 1395 case MSG_UPDATE_PORTS: { 1396 Bundle b = msg.getData(); 1397 ArrayList<RawPortInfo> PortInfo = b.getParcelableArrayList(PORT_INFO, com.android.server.usb.hal.port.RawPortInfo.class); 1398 synchronized (mLock) { 1399 updatePortsLocked(null, PortInfo); 1400 } 1401 break; 1402 } 1403 case MSG_SYSTEM_READY: { 1404 mNotificationManager = (NotificationManager) 1405 mContext.getSystemService(Context.NOTIFICATION_SERVICE); 1406 break; 1407 } 1408 } 1409 } 1410 }; 1411 1412 /** 1413 * Describes a USB port. 1414 */ 1415 public static final class PortInfo { 1416 public static final int DISPOSITION_ADDED = 0; 1417 public static final int DISPOSITION_CHANGED = 1; 1418 public static final int DISPOSITION_READY = 2; 1419 public static final int DISPOSITION_REMOVED = 3; 1420 1421 public static final int COMPLIANCE_WARNING_UNCHANGED = 0; 1422 public static final int COMPLIANCE_WARNING_CHANGED = 1; 1423 1424 public static final int ALTMODE_INFO_UNCHANGED = 0; 1425 public static final int ALTMODE_INFO_CHANGED = 1; 1426 1427 public final UsbPort mUsbPort; 1428 public UsbPortStatus mUsbPortStatus; 1429 public boolean mCanChangeMode; 1430 public boolean mCanChangePowerRole; 1431 public boolean mCanChangeDataRole; 1432 // default initialized to 0 which means added 1433 public int mDisposition; 1434 // Tracks elapsedRealtime() of when the port was connected 1435 public long mConnectedAtMillis; 1436 // 0 when port is connected. Else reports the last connected duration 1437 public long mLastConnectDurationMillis; 1438 // default initialized to 0 which means no changes reported 1439 public int mComplianceWarningChange; 1440 // default initialized to 0 which means unchanged 1441 public int mDisplayPortAltModeChange; 1442 PortInfo(@onNull UsbManager usbManager, @NonNull String portId, int supportedModes, int supportedContaminantProtectionModes, boolean supportsEnableContaminantPresenceDetection, boolean supportsEnableContaminantPresenceProtection, boolean supportsComplianceWarnings, int supportedAltModes)1443 PortInfo(@NonNull UsbManager usbManager, @NonNull String portId, int supportedModes, 1444 int supportedContaminantProtectionModes, 1445 boolean supportsEnableContaminantPresenceDetection, 1446 boolean supportsEnableContaminantPresenceProtection, 1447 boolean supportsComplianceWarnings, 1448 int supportedAltModes) { 1449 mUsbPort = new UsbPort(usbManager, portId, supportedModes, 1450 supportedContaminantProtectionModes, 1451 supportsEnableContaminantPresenceDetection, 1452 supportsEnableContaminantPresenceProtection, 1453 supportsComplianceWarnings, 1454 supportedAltModes); 1455 mComplianceWarningChange = COMPLIANCE_WARNING_UNCHANGED; 1456 mDisplayPortAltModeChange = ALTMODE_INFO_UNCHANGED; 1457 } 1458 complianceWarningsChanged(@onNull int[] complianceWarnings)1459 public boolean complianceWarningsChanged(@NonNull int[] complianceWarnings) { 1460 if (Arrays.equals(complianceWarnings, mUsbPortStatus.getComplianceWarnings())) { 1461 mComplianceWarningChange = COMPLIANCE_WARNING_UNCHANGED; 1462 return false; 1463 } 1464 mComplianceWarningChange = COMPLIANCE_WARNING_CHANGED; 1465 return true; 1466 } 1467 displayPortAltModeChanged(DisplayPortAltModeInfo displayPortAltModeInfo)1468 public boolean displayPortAltModeChanged(DisplayPortAltModeInfo 1469 displayPortAltModeInfo) { 1470 DisplayPortAltModeInfo currentDisplayPortAltModeInfo = 1471 mUsbPortStatus.getDisplayPortAltModeInfo(); 1472 1473 mDisplayPortAltModeChange = ALTMODE_INFO_UNCHANGED; 1474 1475 if (displayPortAltModeInfo == null 1476 && currentDisplayPortAltModeInfo != null) { 1477 mDisplayPortAltModeChange = ALTMODE_INFO_CHANGED; 1478 return true; 1479 } 1480 1481 if (currentDisplayPortAltModeInfo == null) { 1482 if (displayPortAltModeInfo != null) { 1483 mDisplayPortAltModeChange = ALTMODE_INFO_CHANGED; 1484 return true; 1485 } 1486 return false; 1487 } 1488 1489 if (!(currentDisplayPortAltModeInfo.equals(displayPortAltModeInfo))) { 1490 mDisplayPortAltModeChange = ALTMODE_INFO_CHANGED; 1491 return true; 1492 } 1493 return false; 1494 } 1495 setStatus(int currentMode, boolean canChangeMode, int currentPowerRole, boolean canChangePowerRole, int currentDataRole, boolean canChangeDataRole, int supportedRoleCombinations)1496 public boolean setStatus(int currentMode, boolean canChangeMode, 1497 int currentPowerRole, boolean canChangePowerRole, 1498 int currentDataRole, boolean canChangeDataRole, 1499 int supportedRoleCombinations) { 1500 boolean dispositionChanged = false; 1501 1502 mCanChangeMode = canChangeMode; 1503 mCanChangePowerRole = canChangePowerRole; 1504 mCanChangeDataRole = canChangeDataRole; 1505 if (mUsbPortStatus == null 1506 || mUsbPortStatus.getCurrentMode() != currentMode 1507 || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole 1508 || mUsbPortStatus.getCurrentDataRole() != currentDataRole 1509 || mUsbPortStatus.getSupportedRoleCombinations() 1510 != supportedRoleCombinations) { 1511 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, 1512 supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE, 1513 UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED, 1514 UsbPortStatus.DATA_STATUS_UNKNOWN, false, 1515 UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN, 1516 new int[] {}, 0, null); 1517 dispositionChanged = true; 1518 } 1519 1520 if (mUsbPortStatus.isConnected() && mConnectedAtMillis == 0) { 1521 mConnectedAtMillis = SystemClock.elapsedRealtime(); 1522 mLastConnectDurationMillis = 0; 1523 } else if (!mUsbPortStatus.isConnected() && mConnectedAtMillis != 0) { 1524 mLastConnectDurationMillis = SystemClock.elapsedRealtime() - mConnectedAtMillis; 1525 mConnectedAtMillis = 0; 1526 } 1527 1528 return dispositionChanged; 1529 } 1530 setStatus(int currentMode, boolean canChangeMode, int currentPowerRole, boolean canChangePowerRole, int currentDataRole, boolean canChangeDataRole, int supportedRoleCombinations, int contaminantProtectionStatus, int contaminantDetectionStatus, int usbDataStatus, boolean powerTransferLimited, int powerBrickConnectionStatus)1531 public boolean setStatus(int currentMode, boolean canChangeMode, 1532 int currentPowerRole, boolean canChangePowerRole, 1533 int currentDataRole, boolean canChangeDataRole, 1534 int supportedRoleCombinations, int contaminantProtectionStatus, 1535 int contaminantDetectionStatus, int usbDataStatus, 1536 boolean powerTransferLimited, int powerBrickConnectionStatus) { 1537 boolean dispositionChanged = false; 1538 1539 mCanChangeMode = canChangeMode; 1540 mCanChangePowerRole = canChangePowerRole; 1541 mCanChangeDataRole = canChangeDataRole; 1542 if (mUsbPortStatus == null 1543 || mUsbPortStatus.getCurrentMode() != currentMode 1544 || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole 1545 || mUsbPortStatus.getCurrentDataRole() != currentDataRole 1546 || mUsbPortStatus.getSupportedRoleCombinations() 1547 != supportedRoleCombinations 1548 || mUsbPortStatus.getContaminantProtectionStatus() 1549 != contaminantProtectionStatus 1550 || mUsbPortStatus.getContaminantDetectionStatus() 1551 != contaminantDetectionStatus 1552 || mUsbPortStatus.getUsbDataStatus() 1553 != usbDataStatus 1554 || mUsbPortStatus.isPowerTransferLimited() 1555 != powerTransferLimited 1556 || mUsbPortStatus.getPowerBrickConnectionStatus() 1557 != powerBrickConnectionStatus) { 1558 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, 1559 supportedRoleCombinations, contaminantProtectionStatus, 1560 contaminantDetectionStatus, usbDataStatus, 1561 powerTransferLimited, powerBrickConnectionStatus, 1562 new int[] {}, 0, null); 1563 dispositionChanged = true; 1564 } 1565 1566 if (mUsbPortStatus.isConnected() && mConnectedAtMillis == 0) { 1567 mConnectedAtMillis = SystemClock.elapsedRealtime(); 1568 mLastConnectDurationMillis = 0; 1569 } else if (!mUsbPortStatus.isConnected() && mConnectedAtMillis != 0) { 1570 mLastConnectDurationMillis = SystemClock.elapsedRealtime() - mConnectedAtMillis; 1571 mConnectedAtMillis = 0; 1572 } 1573 1574 return dispositionChanged; 1575 } 1576 setStatus(int currentMode, boolean canChangeMode, int currentPowerRole, boolean canChangePowerRole, int currentDataRole, boolean canChangeDataRole, int supportedRoleCombinations, int contaminantProtectionStatus, int contaminantDetectionStatus, int usbDataStatus, boolean powerTransferLimited, int powerBrickConnectionStatus, @NonNull int[] complianceWarnings, int plugState, DisplayPortAltModeInfo displayPortAltModeInfo)1577 public boolean setStatus(int currentMode, boolean canChangeMode, 1578 int currentPowerRole, boolean canChangePowerRole, 1579 int currentDataRole, boolean canChangeDataRole, 1580 int supportedRoleCombinations, int contaminantProtectionStatus, 1581 int contaminantDetectionStatus, int usbDataStatus, 1582 boolean powerTransferLimited, int powerBrickConnectionStatus, 1583 @NonNull int[] complianceWarnings, 1584 int plugState, DisplayPortAltModeInfo displayPortAltModeInfo) { 1585 boolean dispositionChanged = false; 1586 boolean complianceChanged = false; 1587 boolean displayPortChanged = false; 1588 1589 if (mUsbPortStatus != null) { 1590 complianceChanged = complianceWarningsChanged(complianceWarnings); 1591 displayPortChanged = displayPortAltModeChanged(displayPortAltModeInfo); 1592 } 1593 1594 mCanChangeMode = canChangeMode; 1595 mCanChangePowerRole = canChangePowerRole; 1596 mCanChangeDataRole = canChangeDataRole; 1597 if (mUsbPortStatus == null 1598 || mUsbPortStatus.getCurrentMode() != currentMode 1599 || mUsbPortStatus.getCurrentPowerRole() != currentPowerRole 1600 || mUsbPortStatus.getCurrentDataRole() != currentDataRole 1601 || mUsbPortStatus.getSupportedRoleCombinations() 1602 != supportedRoleCombinations 1603 || mUsbPortStatus.getContaminantProtectionStatus() 1604 != contaminantProtectionStatus 1605 || mUsbPortStatus.getContaminantDetectionStatus() 1606 != contaminantDetectionStatus 1607 || mUsbPortStatus.getUsbDataStatus() 1608 != usbDataStatus 1609 || mUsbPortStatus.isPowerTransferLimited() 1610 != powerTransferLimited 1611 || mUsbPortStatus.getPowerBrickConnectionStatus() 1612 != powerBrickConnectionStatus 1613 || mUsbPortStatus.getPlugState() 1614 != plugState) { 1615 if (mUsbPortStatus == null && complianceWarnings.length > 0) { 1616 mComplianceWarningChange = COMPLIANCE_WARNING_CHANGED; 1617 } 1618 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole, 1619 supportedRoleCombinations, contaminantProtectionStatus, 1620 contaminantDetectionStatus, usbDataStatus, 1621 powerTransferLimited, powerBrickConnectionStatus, 1622 complianceWarnings, plugState, displayPortAltModeInfo); 1623 dispositionChanged = true; 1624 // Case used in order to send compliance warning broadcast or signal DisplayPort 1625 // listeners. These targeted broadcasts don't use dispositionChanged to broadcast to 1626 // general ACTION_USB_PORT_CHANGED. 1627 } else if (complianceChanged || displayPortChanged) { 1628 mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, 1629 currentDataRole, supportedRoleCombinations, 1630 contaminantProtectionStatus, contaminantDetectionStatus, 1631 usbDataStatus, powerTransferLimited, powerBrickConnectionStatus, 1632 complianceWarnings, plugState, displayPortAltModeInfo); 1633 } 1634 1635 if (mUsbPortStatus.isConnected() && mConnectedAtMillis == 0) { 1636 mConnectedAtMillis = SystemClock.elapsedRealtime(); 1637 mLastConnectDurationMillis = 0; 1638 } else if (!mUsbPortStatus.isConnected() && mConnectedAtMillis != 0) { 1639 mLastConnectDurationMillis = SystemClock.elapsedRealtime() - mConnectedAtMillis; 1640 mConnectedAtMillis = 0; 1641 } 1642 1643 return dispositionChanged; 1644 } 1645 dump(@onNull DualDumpOutputStream dump, @NonNull String idName, long id)1646 void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) { 1647 long token = dump.start(idName, id); 1648 1649 writePort(dump, "port", UsbPortInfoProto.PORT, mUsbPort); 1650 writePortStatus(dump, "status", UsbPortInfoProto.STATUS, mUsbPortStatus); 1651 dump.write("can_change_mode", UsbPortInfoProto.CAN_CHANGE_MODE, mCanChangeMode); 1652 dump.write("can_change_power_role", UsbPortInfoProto.CAN_CHANGE_POWER_ROLE, 1653 mCanChangePowerRole); 1654 dump.write("can_change_data_role", UsbPortInfoProto.CAN_CHANGE_DATA_ROLE, 1655 mCanChangeDataRole); 1656 dump.write("connected_at_millis", 1657 UsbPortInfoProto.CONNECTED_AT_MILLIS, mConnectedAtMillis); 1658 dump.write("last_connect_duration_millis", 1659 UsbPortInfoProto.LAST_CONNECT_DURATION_MILLIS, mLastConnectDurationMillis); 1660 dump.end(token); 1661 } 1662 1663 @Override toString()1664 public String toString() { 1665 return "port=" + mUsbPort + ", status=" + mUsbPortStatus 1666 + ", canChangeMode=" + mCanChangeMode 1667 + ", canChangePowerRole=" + mCanChangePowerRole 1668 + ", canChangeDataRole=" + mCanChangeDataRole 1669 + ", connectedAtMillis=" + mConnectedAtMillis 1670 + ", lastConnectDurationMillis=" + mLastConnectDurationMillis; 1671 } 1672 } 1673 } 1674