Usb: Add UVC support to UsbManager and UsbDeviceManager

This CL adds support for UVC gadget function in USB System service.

Bug: 242344221
Test: Manually tested that the UVC function can be enabled when needed.
API-Coverage-Bug: 267667903
Change-Id: Ifd93f065426a224274ec39f53366dd6e3fee0bfa
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 4a0b2eb..3c6014c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5832,6 +5832,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public long getCurrentFunctions();
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USB) public java.util.List<android.hardware.usb.UsbPort> getPorts();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
+    method public static boolean isUvcSupportEnabled();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public boolean registerDisplayPortAltModeInfoListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.usb.UsbManager.DisplayPortAltModeInfoListener);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
@@ -5853,6 +5854,7 @@
     field public static final long FUNCTION_NONE = 0L; // 0x0L
     field public static final long FUNCTION_PTP = 16L; // 0x10L
     field public static final long FUNCTION_RNDIS = 32L; // 0x20L
+    field public static final long FUNCTION_UVC = 128L; // 0x80L
     field public static final String USB_CONFIGURED = "configured";
     field public static final String USB_CONNECTED = "connected";
     field public static final String USB_FUNCTION_NCM = "ncm";
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 0483b71..71ec1c6 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -45,6 +45,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
@@ -99,6 +100,8 @@
      * audio source function is enabled
      * <li> {@link #USB_FUNCTION_MIDI} boolean extra indicating whether the
      * MIDI function is enabled
+     * <li> {@link #USB_FUNCTION_UVC} boolean extra indicating whether the
+     * UVC function is enabled
      * </ul>
      * If the sticky intent has not been found, that indicates USB is disconnected,
      * USB is not configured, MTP function is enabled, and all the other functions are disabled.
@@ -314,6 +317,14 @@
     public static final String USB_FUNCTION_NCM = "ncm";
 
     /**
+     * Name of the UVC USB function.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
+     *
+     * @hide
+     */
+    public static final String USB_FUNCTION_UVC = "uvc";
+
+    /**
      * Name of Gadget Hal Not Present;
      *
      * @hide
@@ -683,8 +694,17 @@
     @SystemApi
     public static final long FUNCTION_NCM = 1 << 10;
 
+    /**
+     * Code for the uvc usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
+     * Only supported if {@link #isUvcSupportEnabled()} returns true.
+     * Must be equal to {@link GadgetFunction#UVC}
+     * @hide
+     */
+    @SystemApi
+    public static final long FUNCTION_UVC = 1 << 7;
+
     private static final long SETTABLE_FUNCTIONS = FUNCTION_MTP | FUNCTION_PTP | FUNCTION_RNDIS
-            | FUNCTION_MIDI | FUNCTION_NCM;
+            | FUNCTION_MIDI | FUNCTION_NCM | FUNCTION_UVC;
 
     private static final Map<String, Long> FUNCTION_NAME_TO_CODE = new HashMap<>();
 
@@ -702,6 +722,7 @@
         FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, FUNCTION_AUDIO_SOURCE);
         FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ADB, FUNCTION_ADB);
         FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_NCM, FUNCTION_NCM);
+        FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_UVC, FUNCTION_UVC);
     }
 
     /** @hide */
@@ -715,6 +736,7 @@
             FUNCTION_AUDIO_SOURCE,
             FUNCTION_ADB,
             FUNCTION_NCM,
+            FUNCTION_UVC,
     })
     public @interface UsbFunctionMode {}
 
@@ -1337,6 +1359,21 @@
     }
 
     /**
+     * Returns whether UVC is advertised to be supported or not. SELinux
+     * enforces that this function returns {@code false} when called from a
+     * process that doesn't belong either to a system app, or the
+     * DeviceAsWebcam Service.
+     *
+     * @return true if UVC is supported, false if UVC is not supported or if
+     *         called from a non-system app that isn't DeviceAsWebcam Service.
+     * @hide
+     */
+    @SystemApi
+    public static boolean isUvcSupportEnabled() {
+        return SystemProperties.getBoolean("ro.usb.uvc.enabled", false);
+    }
+
+    /**
      * Enable/Disable the USB data signaling.
      * <p>
      * Enables/Disables USB data path of all USB ports.
@@ -1716,7 +1753,7 @@
 
     /**
      * Returns whether the given functions are valid inputs to UsbManager.
-     * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI, NCM are accepted.
+     * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI, NCM, UVC are accepted.
      *
      * Only one function may be set at a time, except for RNDIS and NCM, which can be set together
      * because from a user perspective they are the same function (tethering).
@@ -1762,6 +1799,9 @@
         if ((functions & FUNCTION_NCM) != 0) {
             joiner.add(UsbManager.USB_FUNCTION_NCM);
         }
+        if ((functions & FUNCTION_UVC) != 0) {
+            joiner.add(UsbManager.USB_FUNCTION_UVC);
+        }
         if ((functions & FUNCTION_ADB) != 0) {
             joiner.add(UsbManager.USB_FUNCTION_ADB);
         }
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index c98e346..8889320 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -45,7 +45,7 @@
 message UsbHandlerProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    /* Same as android.hardware.usb.gadget.V1_0.GadgetFunction.* */
+    /* Same as android.hardware.usb.gadget.GadgetFunction.* */
     enum Function {
         FUNCTION_ADB = 1;
         FUNCTION_ACCESSORY = 2;
@@ -54,6 +54,7 @@
         FUNCTION_PTP = 16;
         FUNCTION_RNDIS = 32;
         FUNCTION_AUDIO_SOURCE = 64;
+        FUNCTION_UVC = 128;
     }
 
     repeated Function current_functions = 1;
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index da44c2e..db43813 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3699,6 +3699,8 @@
     <string name="usb_tether_notification_title">USB tethering turned on</string>
     <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in MIDI mode.  This is the title -->
     <string name="usb_midi_notification_title">MIDI via USB turned on</string>
+    <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in UVC mode.  This is the title -->
+    <string name="usb_uvc_notification_title">Device connected as Webcam</string>
     <!-- USB_PREFERENCES: Notification for when a USB accessory is attached.  This is the title -->
     <string name="usb_accessory_notification_title">USB accessory connected</string>
     <!-- See USB_PREFERENCES. This is the message. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7754256..c4be8c2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2143,6 +2143,7 @@
   <java-symbol type="string" name="usb_power_notification_message" />
   <java-symbol type="string" name="usb_ptp_notification_title" />
   <java-symbol type="string" name="usb_midi_notification_title" />
+  <java-symbol type="string" name="usb_uvc_notification_title" />
   <java-symbol type="string" name="usb_tether_notification_title" />
   <java-symbol type="string" name="usb_supplying_notification_title" />
   <java-symbol type="string" name="usb_unsupported_audio_accessory_title" />
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 12e7226..12a8230 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -306,6 +306,10 @@
     // Package: android
     NOTE_BT_APM_NOTIFICATION = 74;
 
+    // Inform that USB is configured as a Universal Video Class gadget
+    // Package: android
+    NOTE_USB_UVC = 75;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index a480ebd..12e5ed9 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -44,7 +44,6 @@
 import android.debug.AdbNotifications;
 import android.debug.AdbTransportType;
 import android.debug.IAdbTransport;
-import android.hardware.usb.IUsbOperationInternal;
 import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbConfiguration;
@@ -57,7 +56,6 @@
 import android.hardware.usb.gadget.V1_0.GadgetFunction;
 import android.hardware.usb.gadget.V1_0.Status;
 import android.hardware.usb.gadget.V1_2.UsbSpeed;
-import android.hidl.manager.V1_0.IServiceManager;
 import android.hidl.manager.V1_0.IServiceNotification;
 import android.os.BatteryManager;
 import android.os.Environment;
@@ -1351,6 +1349,9 @@
                         || (mCurrentFunctions == UsbManager.FUNCTION_NCM)) {
                     titleRes = com.android.internal.R.string.usb_tether_notification_title;
                     id = SystemMessage.NOTE_USB_TETHER;
+                } else if (mCurrentFunctions == UsbManager.FUNCTION_UVC) {
+                    titleRes = com.android.internal.R.string.usb_uvc_notification_title;
+                    id = SystemMessage.NOTE_USB_UVC;
                 } else if (mCurrentFunctions == UsbManager.FUNCTION_ACCESSORY) {
                     titleRes = com.android.internal.R.string.usb_accessory_notification_title;
                     id = SystemMessage.NOTE_USB_ACCESSORY;
@@ -2403,6 +2404,9 @@
             MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_RNDIS);
         } else if (functions == UsbManager.FUNCTION_ACCESSORY) {
             MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_ACCESSORY);
+        } else if (functions == UsbManager.FUNCTION_UVC) {
+            // MetricsLogger.action(mContext, MetricsEvent.ACTION_USB_CONFIG_UVC);
+            // TODO: Add MetricsEvent for UVC?
         }
         mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, operationId);
     }