diff options
| author | 2024-07-18 20:19:31 +0000 | |
|---|---|---|
| committer | 2024-07-18 20:19:31 +0000 | |
| commit | 40e2250fd16a84fafcb145b78997214bc4179e93 (patch) | |
| tree | 602f8c0a42e69dca15f534515cf824897e327fbf | |
| parent | e89174fc2b009744cb6d2d8858644505cf72ff76 (diff) | |
| parent | 61816838bbadc008693e91d38de0cf046dc24d6b (diff) | |
Merge "Add JNI methods to UsbDeviceManager to monitor usb state" into main
| -rw-r--r-- | core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig | 7 | ||||
| -rw-r--r-- | core/res/res/values/config.xml | 3 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 1 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_UsbDeviceManager.cpp | 207 | ||||
| -rw-r--r-- | services/core/jni/onload.cpp | 4 | ||||
| -rw-r--r-- | services/usb/java/com/android/server/usb/UsbDeviceManager.java | 68 |
6 files changed, 269 insertions, 21 deletions
diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig index 0f944cfb7f67..51024ba64bd9 100644 --- a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig +++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig @@ -36,3 +36,10 @@ flag { description: "Enable identifying midi device using USB sysfs" bug: "333778731" } + +flag { + name: "enable_udc_sysfs_usb_state_update" + namespace: "system_sw_usb" + description: "Enable usb state update based on udc sysfs" + bug: "339241080" +} diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index dc3d9355f148..236e7c5b7a62 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -7090,4 +7090,7 @@ <!-- Whether to show GAIA education screen during account login of private space setup. OEM/Partner can explicitly opt to disable the screen. --> <bool name="config_enableGaiaEducationInPrivateSpace">true</bool> + + <!-- Whether to enable usb state update via udc sysfs. --> + <bool name="config_enableUdcSysfsUsbStateUpdate">false</bool> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index fcafdaed8d1a..09688f2f7bec 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -523,6 +523,7 @@ <java-symbol type="integer" name="config_defaultAnalogClockSecondsHandFps"/> <java-symbol type="bool" name="config_notificationCloseButtonSupported"/> <java-symbol type="bool" name="config_enableGaiaEducationInPrivateSpace"/> + <java-symbol type="bool" name="config_enableUdcSysfsUsbStateUpdate"/> <java-symbol type="color" name="tab_indicator_text_v4" /> diff --git a/services/core/jni/com_android_server_UsbDeviceManager.cpp b/services/core/jni/com_android_server_UsbDeviceManager.cpp index 9dc70afad9d9..4ef9cf4d2388 100644 --- a/services/core/jni/com_android_server_UsbDeviceManager.cpp +++ b/services/core/jni/com_android_server_UsbDeviceManager.cpp @@ -15,33 +15,168 @@ */ #define LOG_TAG "UsbDeviceManagerJNI" -#include "utils/Log.h" - -#include "jni.h" +#include <android-base/properties.h> +#include <android-base/unique_fd.h> +#include <core_jni_helpers.h> +#include <fcntl.h> +#include <linux/usb/f_accessory.h> #include <nativehelper/JNIPlatformHelp.h> #include <nativehelper/ScopedUtfChars.h> -#include "android_runtime/AndroidRuntime.h" -#include "android_runtime/Log.h" -#include "MtpDescriptors.h" - #include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> +#include <sys/epoll.h> #include <sys/ioctl.h> -#include <linux/usb/f_accessory.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <thread> + +#include "MtpDescriptors.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" +#include "jni.h" +#include "utils/Log.h" #define DRIVER_NAME "/dev/usb_accessory" +#define EPOLL_MAX_EVENTS 4 +#define USB_STATE_MAX_LEN 20 namespace android { +static JavaVM *gvm = nullptr; +static jmethodID gUpdateGadgetStateMethod; + static struct parcel_file_descriptor_offsets_t { jclass mClass; jmethodID mConstructor; } gParcelFileDescriptorOffsets; +/* + * NativeGadgetMonitorThread starts a new thread to monitor udc state by epoll, + * convert and update the state to UsbDeviceManager. + */ +class NativeGadgetMonitorThread { + android::base::unique_fd mMonitorFd; + int mPipefd[2]; + std::thread mThread; + jobject mCallbackObj; + std::string mGadgetState; + + void handleStateUpdate(const char *state) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + std::string gadgetState; + + if (!std::strcmp(state, "not attached\n")) { + gadgetState = "DISCONNECTED"; + } else if (!std::strcmp(state, "attached\n") || !std::strcmp(state, "powered\n") || + !std::strcmp(state, "default\n") || !std::strcmp(state, "addressed\n")) { + gadgetState = "CONNECTED"; + } else if (!std::strcmp(state, "configured\n")) { + gadgetState = "CONFIGURED"; + } else if (!std::strcmp(state, "suspended\n")) { + return; + } else { + ALOGE("Unknown gadget state %s", state); + return; + } + + if (mGadgetState.compare(gadgetState)) { + mGadgetState = gadgetState; + jstring obj = env->NewStringUTF(gadgetState.c_str()); + env->CallVoidMethod(mCallbackObj, gUpdateGadgetStateMethod, obj); + } + } + + int setupEpoll(android::base::unique_fd &epollFd) { + struct epoll_event ev; + + ev.data.fd = mMonitorFd.get(); + ev.events = EPOLLPRI; + if (epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, mMonitorFd.get(), &ev) != 0) { + ALOGE("epoll_ctl failed for monitor fd; errno=%d", errno); + return errno; + } + + ev.data.fd = mPipefd[0]; + ev.events = EPOLLIN; + if (epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, mPipefd[0], &ev) != 0) { + ALOGE("epoll_ctl failed for pipe fd; errno=%d", errno); + return errno; + } + + return 0; + } + + void monitorLoop() { + android::base::unique_fd epollFd(epoll_create(EPOLL_MAX_EVENTS)); + if (epollFd.get() == -1) { + ALOGE("epoll_create failed; errno=%d", errno); + return; + } + if (setupEpoll(epollFd) != 0) return; + + JNIEnv *env = nullptr; + JavaVMAttachArgs aargs = {JNI_VERSION_1_4, "NativeGadgetMonitorThread", nullptr}; + if (gvm->AttachCurrentThread(&env, &aargs) != JNI_OK || env == nullptr) { + ALOGE("Couldn't attach thread"); + return; + } + + struct epoll_event events[EPOLL_MAX_EVENTS]; + int nevents = 0; + while (true) { + nevents = epoll_wait(epollFd.get(), events, EPOLL_MAX_EVENTS, -1); + if (nevents < 0) { + ALOGE("usb epoll_wait failed; errno=%d", errno); + continue; + } + for (int i = 0; i < nevents; ++i) { + int fd = events[i].data.fd; + if (fd == mPipefd[0]) { + goto exit; + } else if (fd == mMonitorFd.get()) { + char state[USB_STATE_MAX_LEN] = {0}; + lseek(fd, 0, SEEK_SET); + read(fd, &state, USB_STATE_MAX_LEN); + handleStateUpdate(state); + } + } + } + + exit: + auto res = gvm->DetachCurrentThread(); + ALOGE_IF(res != JNI_OK, "Couldn't detach thread"); + return; + } + + void stop() { + if (mThread.joinable()) { + int c = 'q'; + write(mPipefd[1], &c, 1); + mThread.join(); + } + } + + DISALLOW_COPY_AND_ASSIGN(NativeGadgetMonitorThread); + +public: + explicit NativeGadgetMonitorThread(jobject obj, android::base::unique_fd monitorFd) + : mMonitorFd(std::move(monitorFd)), mGadgetState("") { + mCallbackObj = AndroidRuntime::getJNIEnv()->NewGlobalRef(obj); + pipe(mPipefd); + mThread = std::thread(&NativeGadgetMonitorThread::monitorLoop, this); + } + + ~NativeGadgetMonitorThread() { + stop(); + close(mPipefd[0]); + close(mPipefd[1]); + AndroidRuntime::getJNIEnv()->DeleteGlobalRef(mCallbackObj); + } +}; +static std::unique_ptr<NativeGadgetMonitorThread> sGadgetMonitorThread; + static void set_accessory_string(JNIEnv *env, int fd, int cmd, jobjectArray strArray, int index) { char buffer[256]; @@ -135,6 +270,41 @@ static jobject android_server_UsbDeviceManager_openControl(JNIEnv *env, jobject return jifd; } +static jboolean android_server_UsbDeviceManager_startGadgetMonitor(JNIEnv *env, jobject thiz, + jstring jUdcName) { + std::string filePath; + ScopedUtfChars udcName(env, jUdcName); + + filePath = "/sys/class/udc/" + std::string(udcName.c_str()) + "/state"; + android::base::unique_fd fd(open(filePath.c_str(), O_RDONLY)); + + if (fd.get() == -1) { + ALOGE("Cannot open %s", filePath.c_str()); + return JNI_FALSE; + } + + ALOGI("Start monitoring %s", filePath.c_str()); + sGadgetMonitorThread.reset(new NativeGadgetMonitorThread(thiz, std::move(fd))); + + return JNI_TRUE; +} + +static void android_server_UsbDeviceManager_stopGadgetMonitor(JNIEnv *env, jobject /* thiz */) { + sGadgetMonitorThread.reset(); + return; +} + +static jstring android_server_UsbDeviceManager_waitAndGetProperty(JNIEnv *env, jobject thiz, + jstring jPropName) { + ScopedUtfChars propName(env, jPropName); + std::string propValue; + + while (!android::base::WaitForPropertyCreation(propName.c_str())); + propValue = android::base::GetProperty(propName.c_str(), "" /* default */); + + return env->NewStringUTF(propValue.c_str()); +} + static const JNINativeMethod method_table[] = { {"nativeGetAccessoryStrings", "()[Ljava/lang/String;", (void *)android_server_UsbDeviceManager_getAccessoryStrings}, @@ -143,16 +313,26 @@ static const JNINativeMethod method_table[] = { {"nativeIsStartRequested", "()Z", (void *)android_server_UsbDeviceManager_isStartRequested}, {"nativeOpenControl", "(Ljava/lang/String;)Ljava/io/FileDescriptor;", (void *)android_server_UsbDeviceManager_openControl}, + {"nativeStartGadgetMonitor", "(Ljava/lang/String;)Z", + (void *)android_server_UsbDeviceManager_startGadgetMonitor}, + {"nativeStopGadgetMonitor", "()V", + (void *)android_server_UsbDeviceManager_stopGadgetMonitor}, + {"nativeWaitAndGetProperty", "(Ljava/lang/String;)Ljava/lang/String;", + (void *)android_server_UsbDeviceManager_waitAndGetProperty}, }; -int register_android_server_UsbDeviceManager(JNIEnv *env) -{ +int register_android_server_UsbDeviceManager(JavaVM *vm, JNIEnv *env) { + gvm = vm; + jclass clazz = env->FindClass("com/android/server/usb/UsbDeviceManager"); if (clazz == NULL) { ALOGE("Can't find com/android/server/usb/UsbDeviceManager"); return -1; } + gUpdateGadgetStateMethod = + GetMethodIDOrDie(env, clazz, "updateGadgetState", "(Ljava/lang/String;)V"); + clazz = env->FindClass("android/os/ParcelFileDescriptor"); LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor"); gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); @@ -163,5 +343,4 @@ int register_android_server_UsbDeviceManager(JNIEnv *env) return jniRegisterNativeMethods(env, "com/android/server/usb/UsbDeviceManager", method_table, NELEM(method_table)); } - }; diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 6464081d615a..314ff9d3c808 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -37,7 +37,7 @@ int register_android_server_SerialService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); int register_android_server_UsbAlsaJackDetector(JNIEnv* env); int register_android_server_UsbAlsaMidiDevice(JNIEnv* env); -int register_android_server_UsbDeviceManager(JNIEnv* env); +int register_android_server_UsbDeviceManager(JavaVM* vm, JNIEnv* env); int register_android_server_UsbHostManager(JNIEnv* env); int register_android_server_vr_VrManagerService(JNIEnv* env); int register_android_server_vibrator_VibratorController(JavaVM* vm, JNIEnv* env); @@ -96,7 +96,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_SerialService(env); register_android_server_InputManager(env); register_android_server_LightsService(env); - register_android_server_UsbDeviceManager(env); + register_android_server_UsbDeviceManager(vm, env); register_android_server_UsbAlsaJackDetector(env); register_android_server_UsbAlsaMidiDevice(env); register_android_server_UsbHostManager(env); diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 175a09db54e3..14044135eca7 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -16,6 +16,8 @@ package com.android.server.usb; +import com.android.internal.annotations.Keep; + import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE; import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST; import static android.hardware.usb.UsbPortStatus.MODE_AUDIO_ACCESSORY; @@ -82,6 +84,7 @@ import android.util.Pair; import android.util.Slog; import android.text.TextUtils; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -147,6 +150,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser "DEVPATH=/devices/virtual/android_usb/android0"; private static final String ACCESSORY_START_MATCH = "DEVPATH=/devices/virtual/misc/usb_accessory"; + private static final String UDC_SUBSYS_MATCH = + "SUBSYSTEM=udc"; private static final String FUNCTIONS_PATH = "/sys/class/android_usb/android0/functions"; private static final String STATE_PATH = @@ -226,6 +231,9 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser private static UsbGadgetHal mUsbGadgetHal; + private final boolean mEnableUdcSysfsUsbStateUpdate; + private String mUdcName = ""; + /** * Counter for tracking UsbOperation operations. */ @@ -260,12 +268,9 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser if (DEBUG) Slog.d(TAG, "sEventLogger == null"); } - String state = event.get("USB_STATE"); String accessory = event.get("ACCESSORY"); - if (state != null) { - mHandler.updateState(state); - } else if ("GETPROTOCOL".equals(accessory)) { + if ("GETPROTOCOL".equals(accessory)) { if (DEBUG) Slog.d(TAG, "got accessory get protocol"); mHandler.setAccessoryUEventTime(SystemClock.elapsedRealtime()); resetAccessoryHandshakeTimeoutHandler(); @@ -279,6 +284,24 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser mHandler.setStartAccessoryTrue(); startAccessoryMode(); } + + if (mEnableUdcSysfsUsbStateUpdate) { + if (!mUdcName.isEmpty() + && "udc".equals(event.get("SUBSYSTEM")) + && event.get("DEVPATH").contains(mUdcName)) { + String action = event.get("ACTION"); + if ("add".equals(action)) { + nativeStartGadgetMonitor(mUdcName); + } else if ("remove".equals(action)) { + nativeStopGadgetMonitor(); + } + } + } else { + String state = event.get("USB_STATE"); + if (state != null) { + mHandler.updateState(state); + } + } } } @@ -406,9 +429,28 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser // Watch for USB configuration changes mUEventObserver = new UsbUEventObserver(); - mUEventObserver.startObserving(USB_STATE_MATCH); mUEventObserver.startObserving(ACCESSORY_START_MATCH); + mEnableUdcSysfsUsbStateUpdate = + android.hardware.usb.flags.Flags.enableUdcSysfsUsbStateUpdate() + && context.getResources().getBoolean(R.bool.config_enableUdcSysfsUsbStateUpdate); + + if (mEnableUdcSysfsUsbStateUpdate) { + mUEventObserver.startObserving(UDC_SUBSYS_MATCH); + new Thread("GetUsbControllerSysprop") { + public void run() { + String udcName; + // blocking wait until usb controller sysprop is available + udcName = nativeWaitAndGetProperty(USB_CONTROLLER_NAME_PROPERTY); + nativeStartGadgetMonitor(udcName); + mUdcName = udcName; + Slog.v(TAG, "USB controller name " + udcName); + } + }.start(); + } else { + mUEventObserver.startObserving(USB_STATE_MATCH); + } + sEventLogger = new EventLogger(DUMPSYS_LOG_BUFFER, "UsbDeviceManager activity"); } @@ -2609,11 +2651,27 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser dump.end(token); } + /** + * Update usb state (Called by native code). + */ + @Keep + private void updateGadgetState(String state) { + Slog.d(TAG, "Usb state update " + state); + + mHandler.updateState(state); + } + private native String[] nativeGetAccessoryStrings(); private native ParcelFileDescriptor nativeOpenAccessory(); + private native String nativeWaitAndGetProperty(String propName); + private native FileDescriptor nativeOpenControl(String usbFunction); private native boolean nativeIsStartRequested(); + + private native boolean nativeStartGadgetMonitor(String udcName); + + private native void nativeStopGadgetMonitor(); } |