• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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