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