summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Roy Luo <royluo@google.com> 2024-05-21 22:45:07 +0000
committer Roy Luo <royluo@google.com> 2024-07-17 00:42:05 +0000
commit61816838bbadc008693e91d38de0cf046dc24d6b (patch)
tree946bc4265966398810cc8bb2d27a4eeb0fe39917
parent26e9784569d65e70ee7e99bd5e725493befc3716 (diff)
Add JNI methods to UsbDeviceManager to monitor usb state
The JNI method uses epoll to monitor usb device state in udc sysfs in a new thread, the result is reported to UsbDeviceManager by calling back updateGadgetState. This would be the new way to update usb state, replacing the old one that's depending on ACK specific android_usb sysfs. Note that we cannot assume "sys.usb.controller" is always valid because the property would be set whenever udc device becomes available, which depends on platform implementation. For the same reason, an attempt to monitor the usb state when the system server starts is required as udc uevent could have been emitted even before init. Bug: 339241080 Test: Tested on platforms with persistent udc device and with those which dynamically created/destroyed udc device. Flag: android.hardware.usb.flags.enable_udc_sysfs_usb_state_update Change-Id: I1420951e5b3aa24510b88a1dd9c870e53a37331b
-rw-r--r--core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig7
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--services/core/jni/com_android_server_UsbDeviceManager.cpp207
-rw-r--r--services/core/jni/onload.cpp4
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java68
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 d25f59d7c488..18b01e42a648 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();
}