Write descriptors for Mtp in UsbService

The current model for setting up a functionfs
function is:

UsbDeviceManager#setCurrentFunctions() ->
intent is sent to MtpReceiver to write the descriptors ->
init/hal waits for descriptors to write, then pulls up gadget ->
Gadget is configured, a USB_STATE intent starts MtpServer

The main downside of this is a lack of reliability because
the Mtp process could be killed at any point. Normally, a
gadget is unbound if its control endpoint is closed. no_disconnect
works around this, but is still a little janky. In addition, the
extra intent delays the startup of the gadget.

With the new model, UsbDeviceManager writes the descriptors
on initialization. Since it is a system service, it won't be killed.

UsbDeviceManager#setCurrentFunctions() ->
init/hal pulls up gadget ->
Gadget is configured, a USB_STATE intent starts MtpServer
MtpServer calls UsbManager#getControlFd to get a dup of the control
endpoint.

Also modify permissions so system server can access mtp files.

Bug: 72877174
Test: Change usb configurations to ptp/mtp
Change-Id: Id17d2b5930f4e1f37ec1b4f00add9d594174ad49
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 427b9d2..89efe12 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -86,6 +86,7 @@
         "libhardware_legacy",
         "libhidlbase",
         "libkeystore_binder",
+        "libmtp",
         "libnativehelper",
         "libutils",
         "libui",
diff --git a/services/core/jni/com_android_server_UsbDeviceManager.cpp b/services/core/jni/com_android_server_UsbDeviceManager.cpp
index f37f870..ff1ec04 100644
--- a/services/core/jni/com_android_server_UsbDeviceManager.cpp
+++ b/services/core/jni/com_android_server_UsbDeviceManager.cpp
@@ -21,6 +21,7 @@
 #include <nativehelper/JNIHelp.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
+#include "MtpDescriptors.h"
 
 #include <stdio.h>
 #include <asm/byteorder.h>
@@ -118,6 +119,38 @@
     return result;
 }
 
+static jobject android_server_UsbDeviceManager_openControl(JNIEnv *env, jobject /* thiz */, jstring jFunction) {
+    const char *function = env->GetStringUTFChars(jFunction, NULL);
+    bool ptp = false;
+    int fd = -1;
+    if (!strcmp(function, "ptp")) {
+        ptp = true;
+    }
+    if (!strcmp(function, "mtp") || ptp) {
+        fd = TEMP_FAILURE_RETRY(open(ptp ? FFS_PTP_EP0 : FFS_MTP_EP0, O_RDWR));
+        if (fd < 0) {
+            ALOGE("could not open control for %s %s", function, strerror(errno));
+            goto error;
+        }
+        if (!writeDescriptors(fd, ptp)) {
+            goto error;
+        }
+    }
+
+    if (function != NULL) {
+        env->ReleaseStringUTFChars(jFunction, function);
+    }
+    return jniCreateFileDescriptor(env, fd);
+error:
+    if (fd != -1) {
+        close(fd);
+    }
+    if (function != NULL) {
+        env->ReleaseStringUTFChars(jFunction, function);
+    }
+    return NULL;
+}
+
 static const JNINativeMethod method_table[] = {
     { "nativeGetAccessoryStrings",  "()[Ljava/lang/String;",
                                     (void*)android_server_UsbDeviceManager_getAccessoryStrings },
@@ -127,6 +160,8 @@
                                     (void*)android_server_UsbDeviceManager_isStartRequested },
     { "nativeGetAudioMode",         "()I",
                                     (void*)android_server_UsbDeviceManager_getAudioMode },
+    { "nativeOpenControl",          "(Ljava/lang/String;)Ljava/io/FileDescriptor;",
+                                    (void*)android_server_UsbDeviceManager_openControl },
 };
 
 int register_android_server_UsbDeviceManager(JNIEnv *env)
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index ef0780a..9f8042c 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -83,6 +83,7 @@
 import com.android.server.LocalServices;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.HashMap;
@@ -181,6 +182,7 @@
     private final UEventObserver mUEventObserver;
 
     private static Set<Integer> sBlackListedInterfaces;
+    private HashMap<Long, FileDescriptor> mControlFds;
 
     static {
         sBlackListedInterfaces = new HashSet<>();
@@ -270,6 +272,18 @@
             Slog.i(TAG, "USB GADGET HAL not present in the device", e);
         }
 
+        mControlFds = new HashMap<>();
+        FileDescriptor mtpFd = nativeOpenControl(UsbManager.USB_FUNCTION_MTP);
+        if (mtpFd == null) {
+            Slog.e(TAG, "Failed to open control for mtp");
+        }
+        mControlFds.put(UsbManager.FUNCTION_MTP, mtpFd);
+        FileDescriptor ptpFd = nativeOpenControl(UsbManager.USB_FUNCTION_PTP);
+        if (mtpFd == null) {
+            Slog.e(TAG, "Failed to open control for mtp");
+        }
+        mControlFds.put(UsbManager.FUNCTION_PTP, ptpFd);
+
         boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
         boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
         if (secureAdbEnabled && !dataEncrypted) {
@@ -704,8 +718,7 @@
             return false;
         }
 
-        protected void updateUsbStateBroadcastIfNeeded(long functions,
-                boolean configChanged) {
+        protected void updateUsbStateBroadcastIfNeeded(long functions) {
             // send a sticky broadcast containing current USB state
             Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -716,7 +729,6 @@
             intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
             intent.putExtra(UsbManager.USB_DATA_UNLOCKED,
                     isUsbTransferAllowed() && isUsbDataTransferActive(mCurrentFunctions));
-            intent.putExtra(UsbManager.USB_CONFIG_CHANGED, configChanged);
 
             long remainingFunctions = functions;
             while (remainingFunctions != 0) {
@@ -726,7 +738,7 @@
             }
 
             // send broadcast intent only if the USB state has changed
-            if (!isUsbStateChanged(intent) && !configChanged) {
+            if (!isUsbStateChanged(intent)) {
                 if (DEBUG) {
                     Slog.d(TAG, "skip broadcasting " + intent + " extras: " + intent.getExtras());
                 }
@@ -798,8 +810,7 @@
                     updateUsbNotification(false);
                     updateAdbNotification(false);
                     if (mBootCompleted) {
-                        updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions),
-                                false);
+                        updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
                     }
                     if ((mCurrentFunctions & UsbManager.FUNCTION_ACCESSORY) != 0) {
                         updateCurrentAccessory();
@@ -812,7 +823,7 @@
                                     && mScreenUnlockedFunctions != UsbManager.FUNCTION_NONE) {
                                 setScreenUnlockedFunctions();
                             } else {
-                                setEnabledFunctions(UsbManager.FUNCTION_NONE, !mAdbEnabled);
+                                setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
                             }
                         }
                         updateUsbFunctions();
@@ -847,8 +858,7 @@
                     updateUsbNotification(false);
                     if (mBootCompleted) {
                         if (mHostConnected || prevHostConnected) {
-                            updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions),
-                                    false);
+                            updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
                         }
                     } else {
                         mPendingBootBroadcast = true;
@@ -994,7 +1004,7 @@
         protected void finishBoot() {
             if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) {
                 if (mPendingBootBroadcast) {
-                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), false);
+                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
                     mPendingBootBroadcast = false;
                 }
                 if (!mScreenLocked
@@ -1597,7 +1607,7 @@
                     /**
                      * Start up dependent services.
                      */
-                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions), true);
+                    updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
                 }
 
                 if (!waitForState(oemFunctions)) {
@@ -1943,7 +1953,7 @@
 
                 if (mBootCompleted && isUsbDataTransferActive(functions)) {
                     // Start up dependent services.
-                    updateUsbStateBroadcastIfNeeded(functions, true);
+                    updateUsbStateBroadcastIfNeeded(functions);
                 }
             }
         }
@@ -1979,6 +1989,22 @@
         return mHandler.getEnabledFunctions();
     }
 
+    /**
+     * Returns a dup of the control file descriptor for the given function.
+     */
+    public ParcelFileDescriptor getControlFd(long usbFunction) {
+        FileDescriptor fd = mControlFds.get(usbFunction);
+        if (fd == null) {
+            return null;
+        }
+        try {
+            return ParcelFileDescriptor.dup(fd);
+        } catch (IOException e) {
+            Slog.e(TAG, "Could not dup fd for " + usbFunction);
+            return null;
+        }
+    }
+
     public long getScreenUnlockedFunctions() {
         return mHandler.getScreenUnlockedFunctions();
     }
@@ -2063,6 +2089,8 @@
 
     private native ParcelFileDescriptor nativeOpenAccessory();
 
+    private native FileDescriptor nativeOpenControl(String usbFunction);
+
     private native boolean nativeIsStartRequested();
 
     private native int nativeGetAudioMode();
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index c1a7591..e92bd74 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -304,6 +304,13 @@
         return null;
     }
 
+    /* Returns a dup of the control file descriptor for the given function. */
+    @Override
+    public ParcelFileDescriptor getControlFd(long function) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_MTP, null);
+        return mDeviceManager.getControlFd(function);
+    }
+
     @Override
     public void setDevicePackage(UsbDevice device, String packageName, int userId) {
         device = Preconditions.checkNotNull(device);