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