近期在调试一个usb 设备的插拔事件上报功能发现上层未能收到插拔事件。
核心代码就两行kobject_uevent(&dev->kobj, KOBJ_CHANGE);
private static final String USB_STATE_IGNOR = “DEVPATH=/devices/platform/usb_xhci”;
mUEventObserver = new UsbUEventObserver();
mUEventObserver.startObserving(USB_STATE_MATCH);
+mUEventObserver.startObserving(USB_STATE_IGNOR);
mUEventObserver.startObserving(ACCESSORY_START_MATCH);
但其中涉及到的代码块跨底层到上层
kernel 到hardware 到jni 到framework manager
其中底层核心代码为
kobject_uevent(&dev->kobj, KOBJ_CHANGE);
device_error 函数是参照device_add 作的修改,目的是在usb 识别失败时调用,同时通过uevent change 事件 告诉android 系统 有事件发生。
上层要做的就是监听当前事件上报的文件节点。如果不知道具体节点可以监听其上一级节点。
但device_error 函数是有问题的,当前为了特殊功能才加的。
#ifdef CONFIG_BICV_USB_AUTHENTICATE
int device_error(struct device *dev)
{
struct device *parent = NULL;
struct kobject *kobj;
//struct class_interface *class_intf;
int error = -EINVAL;
struct kobject *glue_dir = NULL;
dev = get_device(dev);
#if 0
if (!dev)
goto done;
#endif
if (!dev->p) {
error = device_private_init(dev);
#if 0
if (error)
goto done;
#endif
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
/* subsystems can specify simple device enumeration */
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
if (!dev_name(dev)) {
error = -EINVAL;
//goto name_error;
}
printk("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent);
kobj = get_device_parent(dev, parent);
if (IS_ERR(kobj)) {
error = PTR_ERR(kobj);
//goto parent_error;
}
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node */
if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
printk("device: '%s': %s 01\n", dev_name(dev), __func__);
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
printk("device: '%s': %s 02\n", dev_name(dev), __func__);
if (error) {
glue_dir = get_glue_dir(dev);
//goto Error;
}
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
kobject_uevent(&dev->kobj, KOBJ_CHANGE);
printk("device: '%s': %s 03\n", dev_name(dev), __func__);
//kobject_uevent(&dev->kobj, KOBJ_MAX);
//kobject_del(&dev->kobj);
//put_device(dev);
return error;
#if 0
error = device_create_file(dev, &dev_attr_uevent);
if (error)
goto attrError;
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
error = device_add_attrs(dev);
if (error)
goto AttrsError;
error = bus_add_device(dev);
if (error)
goto BusError;
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &dev_attr_dev);
if (error)
goto DevAttrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto SysEntryError;
devtmpfs_create_node(dev);
}
/* Notify clients of device addition. This call must come
* after dpm_sysfs_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_probe_device(dev);
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->mutex);
}
done:
put_device(dev);
return error;
SysEntryError:
if (MAJOR(dev->devt))
device_remove_file(dev, &dev_attr_dev);
DevAttrError:
device_pm_remove(dev);
dpm_sysfs_remove(dev);
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
device_remove_file(dev, &dev_attr_uevent);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
glue_dir = get_glue_dir(dev);
kobject_del(&dev->kobj);
Error:
cleanup_glue_dir(dev, glue_dir);
parent_error:
put_device(parent);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
#endif
}
EXPORT_SYMBOL_GPL(device_error);
#endif
/*
- Copyright © 2008 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the “License”);
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an “AS IS” BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
*/
package android.os;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
/**
-
UEventObserver is an abstract class that receives UEvents from the kernel.
-
Subclass UEventObserver, implementing onUEvent(UEvent event), then call
-
startObserving() with a match string. The UEvent thread will then call your
-
onUEvent() method when a UEvent occurs that contains your match string.
-
Call stopObserving() to stop receiving UEvents.
-
There is only one UEvent thread per process, even if that process has
-
multiple UEventObserver subclass instances. The UEvent thread starts when
-
the startObserving() is called for the first time in that process. Once
-
started the UEvent thread will not stop (although it can stop notifying
-
UEventObserver’s via stopObserving()).
-
@hide
*/
public abstract class UEventObserver {
private static final String TAG = “UEventObserver”;
private static final boolean DEBUG = true;//false;private static UEventThread sThread;
private static native void nativeSetup();
private static native String nativeWaitForNextEvent();
private static native void nativeAddMatch(String match);
private static native void nativeRemoveMatch(String match);@UnsupportedAppUsage
public UEventObserver() {
}@Override
protected void finalize() throws Throwable {
try {
stopObserving();
} finally {
super.finalize();
}
}private static UEventThread getThread() {
synchronized (UEventObserver.class) {
if (sThread == null) {
sThread = new UEventThread();
sThread.start();
}
return sThread;
}
}private static UEventThread peekThread() {
synchronized (UEventObserver.class) {
return sThread;
}
}/**
-
Begin observation of UEvents.
-
This method will cause the UEvent thread to start if this is the first
-
invocation of startObserving in this process.
-
Once called, the UEvent thread will call onUEvent() when an incoming
-
UEvent matches the specified string.
-
This method can be called multiple times to register multiple matches.
-
Only one call to stopObserving is required even with multiple registered
-
matches.
-
@param match A substring of the UEvent to match. Try to be as specific
-
as possible to avoid incurring unintended additional cost from processing
-
irrelevant messages. Netlink messages can be moderately high bandwidth and
-
are expensive to parse. For example, some devices may send one netlink message
-
for each vsync period.
*/
@UnsupportedAppUsage
public final void startObserving(String match) {
if (match == null || match.isEmpty()) {
throw new IllegalArgumentException(“match substring must be non-empty”);
}final UEventThread t = getThread();
t.addObserver(match, this);
}
/**
- End observation of UEvents.
- This process’s UEvent thread will never call onUEvent() on this
- UEventObserver after this call. Repeated calls have no effect.
*/
@UnsupportedAppUsage
public final void stopObserving() {
final UEventThread t = peekThread();
if (t != null) {
t.removeObserver(this);
}
}
/**
- Subclasses of UEventObserver should override this method to handle
- UEvents.
*/
@UnsupportedAppUsage
public abstract void onUEvent(UEvent event);
/**
-
Representation of a UEvent.
*/
public static final class UEvent {
// collection of key=value pairs parsed from the uevent message
private final HashMap<String,String> mMap = new HashMap<String,String>();public UEvent(String message) {
int offset = 0;
int length = message.length();while (offset < length) { int equals = message.indexOf('=', offset); int at = message.indexOf('\0', offset); if (at < 0) break; if (equals > offset && equals < at) { // key is before the equals sign, and value is after mMap.put(message.substring(offset, equals), message.substring(equals + 1, at)); } offset = at + 1; }
}
@UnsupportedAppUsage
public String get(String key) {
return mMap.get(key);
}@UnsupportedAppUsage
public String get(String key, String defaultValue) {
String result = mMap.get(key);
return (result == null ? defaultValue : result);
}public String toString() {
return mMap.toString();
}
}
private static final class UEventThread extends Thread {
/** Many to many mapping of string match to observer.
* Multimap would be better, but not available in android, so use
* an ArrayList where even elements are the String match and odd
* elements the corresponding UEventObserver observer */
private final ArrayList mKeysAndObservers = new ArrayList();private final ArrayList<UEventObserver> mTempObserversToSignal = new ArrayList<UEventObserver>(); public UEventThread() { super("UEventObserver"); } @Override public void run() { nativeSetup(); while (true) { String message = nativeWaitForNextEvent(); if (message != null) { if (DEBUG) { Log.d(TAG, message); } Log.d(TAG, "UEventObserver begin"); sendEvent(message); } } } private void sendEvent(String message) { synchronized (mKeysAndObservers) { final int N = mKeysAndObservers.size(); for (int i = 0; i < N; i += 2) { final String key = (String)mKeysAndObservers.get(i); if (message.contains(key)) { final UEventObserver observer = (UEventObserver)mKeysAndObservers.get(i + 1); mTempObserversToSignal.add(observer); } } } if (!mTempObserversToSignal.isEmpty()) { final UEvent event = new UEvent(message); final int N = mTempObserversToSignal.size(); for (int i = 0; i < N; i++) { final UEventObserver observer = mTempObserversToSignal.get(i); observer.onUEvent(event); } mTempObserversToSignal.clear(); } } public void addObserver(String match, UEventObserver observer) { synchronized (mKeysAndObservers) { mKeysAndObservers.add(match); mKeysAndObservers.add(observer); nativeAddMatch(match); } } /** Removes every key/value pair where value=observer from mObservers */ public void removeObserver(UEventObserver observer) { synchronized (mKeysAndObservers) { for (int i = 0; i < mKeysAndObservers.size(); ) { if (mKeysAndObservers.get(i + 1) == observer) { mKeysAndObservers.remove(i + 1); final String match = (String)mKeysAndObservers.remove(i); nativeRemoveMatch(match); } else { i += 2; } } } }
}
} -
private static native void nativeSetup();
private static native String nativeWaitForNextEvent();
private static native void nativeAddMatch(String match);
private static native void nativeRemoveMatch(String match);
/*
- Copyright © 2008 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the “License”);
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an “AS IS” BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
*/
#define LOG_TAG “UEventObserver”
#define LOG_NDEBUG 1
#include “utils/Log.h”
#include “hardware_legacy/uevent.h”
#include “jni.h”
#include <nativehelper/JNIHelp.h>
#include “core_jni_helpers.h”
#include <utils/Mutex.h>
#include <utils/Vector.h>
#include <utils/String8.h>
#include <nativehelper/ScopedUtfChars.h>
namespace android {
static Mutex gMatchesMutex;
static Vector gMatches;
static void nativeSetup(JNIEnv *env, jclass clazz) {
if (!uevent_init()) {
jniThrowException(env, “java/lang/RuntimeException”,
“Unable to open socket for UEventObserver”);
}
}
static bool isMatch(const char* buffer, size_t length) {
AutoMutex _l(gMatchesMutex);
for (size_t i = 0; i < gMatches.size(); i++) {
const String8& match = gMatches.itemAt(i);
// Consider all zero-delimited fields of the buffer.
const char* field = buffer;
const char* end = buffer + length + 1;
do {
if (strstr(field, match.string())) {
ALOGV("Matched uevent message with pattern: %s", match.string());
return true;
}
field += strlen(field) + 1;
} while (field != end);
}
return false;
}
static jstring nativeWaitForNextEvent(JNIEnv *env, jclass clazz) {
char buffer[1024];
for (;;) {
int length = uevent_next_event(buffer, sizeof(buffer) - 1);
if (length <= 0) {
return NULL;
}
buffer[length] = '\0';
ALOGV("Received uevent message: %s", buffer);
if (isMatch(buffer, length)) {
// Assume the message is ASCII.
jchar message[length];
for (int i = 0; i < length; i++) {
message[i] = buffer[i];
}
return env->NewString(message, length);
}
}
}
static void nativeAddMatch(JNIEnv* env, jclass clazz, jstring matchStr) {
ScopedUtfChars match(env, matchStr);
AutoMutex _l(gMatchesMutex);
gMatches.add(String8(match.c_str()));
}
static void nativeRemoveMatch(JNIEnv* env, jclass clazz, jstring matchStr) {
ScopedUtfChars match(env, matchStr);
AutoMutex _l(gMatchesMutex);
for (size_t i = 0; i < gMatches.size(); i++) {
if (gMatches.itemAt(i) == match.c_str()) {
gMatches.removeAt(i);
break; // only remove first occurrence
}
}
}
static const JNINativeMethod gMethods[] = {
{ “nativeSetup”, “()V”,
(void *)nativeSetup },
{ “nativeWaitForNextEvent”, “()Ljava/lang/String;”,
(void *)nativeWaitForNextEvent },
{ “nativeAddMatch”, “(Ljava/lang/String;)V”,
(void *)nativeAddMatch },
{ “nativeRemoveMatch”, “(Ljava/lang/String;)V”,
(void *)nativeRemoveMatch },
};
int register_android_os_UEventObserver(JNIEnv *env)
{
FindClassOrDie(env, “android/os/UEventObserver”);
return RegisterMethodsOrDie(env, "android/os/UEventObserver", gMethods, NELEM(gMethods));
}
} // namespace android
#include “hardware_legacy/uevent.h”
hardware/libhardware_legacy/uevent.c
/*
- Copyright © 2008 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the “License”);
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an “AS IS” BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
*/
#include <hardware_legacy/uevent.h>
#include <malloc.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/queue.h>
#include <linux/netlink.h>
LIST_HEAD(uevent_handler_head, uevent_handler) uevent_handler_list;
pthread_mutex_t uevent_handler_list_lock = PTHREAD_MUTEX_INITIALIZER;
struct uevent_handler {
void (*handler)(void *data, const char *msg, int msg_len);
void *handler_data;
LIST_ENTRY(uevent_handler) list;
};
static int fd = -1;
/* Returns 0 on failure, 1 on success /
int uevent_init()
{
struct sockaddr_nl addr;
int sz = 641024;
int s;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = 0xffffffff;
s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if(s < 0)
return 0;
setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
close(s);
return 0;
}
fd = s;
return (fd > 0);
}
int uevent_get_fd()
{
return fd;
}
int uevent_next_event(char* buffer, int buffer_length)
{
while (1) {
struct pollfd fds;
int nr;
fds.fd = fd;
fds.events = POLLIN;
fds.revents = 0;
nr = poll(&fds, 1, -1);
if(nr > 0 && (fds.revents & POLLIN)) {
int count = recv(fd, buffer, buffer_length, 0);
if (count > 0) {
struct uevent_handler *h;
pthread_mutex_lock(&uevent_handler_list_lock);
LIST_FOREACH(h, &uevent_handler_list, list)
h->handler(h->handler_data, buffer, buffer_length);
pthread_mutex_unlock(&uevent_handler_list_lock);
return count;
}
}
}
// won't get here
return 0;
}
int uevent_add_native_handler(void (*handler)(void *data, const char *msg, int msg_len),
void *handler_data)
{
struct uevent_handler *h;
h = malloc(sizeof(struct uevent_handler));
if (h == NULL)
return -1;
h->handler = handler;
h->handler_data = handler_data;
pthread_mutex_lock(&uevent_handler_list_lock);
LIST_INSERT_HEAD(&uevent_handler_list, h, list);
pthread_mutex_unlock(&uevent_handler_list_lock);
return 0;
}
int uevent_remove_native_handler(void (*handler)(void *data, const char *msg, int msg_len))
{
struct uevent_handler *h;
int err = -1;
pthread_mutex_lock(&uevent_handler_list_lock);
LIST_FOREACH(h, &uevent_handler_list, list) {
if (h->handler == handler) {
LIST_REMOVE(h, list);
err = 0;
break;
}
}
pthread_mutex_unlock(&uevent_handler_list_lock);
return err;
}