Android uevent监控 UEventObserver

android提供了UEventObserver这个类来使java可以监听uevent事件,这个类是一个抽象类,使用这个类必须实现onUEvent函数。


一、监控过程



在UEventObserver这个类中做了一个单例的线程,

    private static UEventThread getThread() {
        synchronized (UEventObserver.class) {
            if (sThread == null) {
                sThread = new UEventThread();
                sThread.start();
            }
            return sThread;
        }
    }
下面我们分析下这个UEventThread类的run函数

        @Override
        public void run() {
            nativeSetup();

            while (true) {
                String message = nativeWaitForNextEvent();
                if (message != null) {
                    if (DEBUG) {
                        Log.d(TAG, message);
                    }
                    sendEvent(message);
                }
            }
        }

我们先来看nativeSetup函数

static void nativeSetup(JNIEnv *env, jclass clazz) {
    if (!uevent_init()) {
        jniThrowException(env, "java/lang/RuntimeException",
                "Unable to open socket for UEventObserver");
    }
}
而uevent_init函数,也是创建了一个Netlink socket和正常接收uevent事件流程一样。

int uevent_init()
{
    struct sockaddr_nl addr;
    int sz = 64*1024;
    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);
}
然后在UEventThread一直循环调用nativeWaitForNextEvent函数

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);
        }
    }
}

uevent_next_event函数就是接收Netlink socket的数据

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;
}

我们再来看isMatch函数,就是看数据和我们的gMatches中是否有匹配的

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;
}

最后由匹配的数据,我们再UEventThread中调用sendEvent函数

        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)) {//注册的observer时候有匹配的
                        final UEventObserver observer =
                                (UEventObserver)mKeysAndObservers.get(i + 1);//match的下一个就是Observer
                        mTempObserversToSignal.add(observer);//把匹配的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);//遍历所有满足的Observer,调用器onUEvent函数
                }
                mTempObserversToSignal.clear();
            }
        }


二、注册监控

我们在调用startObserving后,就把我们的监控放入线程汇总

    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);
    }

UEventThread函数的addObserver函数,把match和Observer都放入了mKeysAndObservers中,在使用的时候我们取match的下一个就是Observer了

        public void addObserver(String match, UEventObserver observer) {
            synchronized (mKeysAndObservers) {
                mKeysAndObservers.add(match);
                mKeysAndObservers.add(observer);
                nativeAddMatch(match);
            }
        }

我们再看下nativeAddMatch函数,在这个函数中把match直接接入到gMatches的全局变量中。

static void nativeAddMatch(JNIEnv* env, jclass clazz, jstring matchStr) {
    ScopedUtfChars match(env, matchStr);

    AutoMutex _l(gMatchesMutex);
    gMatches.add(String8(match.c_str()));
}

注销监控过程比较简单就不说了。

三、实例

我们举个BatteryService中的实例

DEVPATH=/devices/virtual/switch/invalid_charger 就是我们监测的match

        if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
            mInvalidChargerObserver.startObserving(
                    "DEVPATH=/devices/virtual/switch/invalid_charger");
        }
onUEvent就是监测到之后的处理函数

    private final UEventObserver mInvalidChargerObserver = new UEventObserver() {
        @Override
        public void onUEvent(UEventObserver.UEvent event) {
            final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
            synchronized (mLock) {
                if (mInvalidCharger != invalidCharger) {
                    mInvalidCharger = invalidCharger;
                }
            }
        }
    };


  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android系统中,可以通过监听uevent的方式来检测U盘的挂载地址。具体可以参考以下步骤: 1. 在Android系统中打开uevent的监听功能,可以使用命令:`mDev = new UEventObserver() {`。 2. 在UEventObserver的回调函数中,可以通过判断uevent的类型和属性来获取U盘的挂载地址,具体代码如下: ``` @Override public void onUEvent(UEventObserver.UEvent event) { String action = event.get("ACTION"); String devPath = event.get("DEVPATH"); String subsystem = event.get("SUBSYSTEM"); if (action.equals("add") && subsystem.equals("block")) { String devName = new File(devPath).getName(); if (devName.startsWith("sd") || devName.startsWith("mmcblk")) { String mountPoint = getMountPoint(devName); if (mountPoint != null) { // U盘已经挂载,可以在这里处理相关逻辑 } } } } ``` 3. 在getMountPoint函数中,可以通过读取系统的挂载信息来获取U盘的挂载地址,具体代码如下: ``` private String getMountPoint(String devName) { String mountPoint = null; File mountFile = new File("/proc/mounts"); if (mountFile.exists()) { Scanner scanner = null; try { scanner = new Scanner(mountFile); while (scanner.hasNextLine()) { String line = scanner.nextLine(); if (line.contains(devName)) { String[] parts = line.split("\\s+"); mountPoint = parts[1]; break; } } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (scanner != null) { scanner.close(); } } } return mountPoint; } ``` 通过以上步骤,就可以在Android系统中检测U盘的挂载地址了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值