AppDirObserve

1306人阅读 评论(4) 收藏 举报
分类:

AppDirObserver主要负责监控相应目录下面apk的变动

我们以监视/system/app的代码为例

mSystemInstallObserver = newAppDirObserver(mSystemAppDir.getPath(), OBSERVER_EVENTS, true);

 mSystemInstallObserver.startWatching();

首先看一下这几个类的关系:


主要的监控工作是通过ObserverThread来实现的,而ObserverThread又是通过JNI调用C层的代码来控制的。

AppDirObserver继承自FileObserver,而FileObserver有一个ObserverThread类型的变量s_observerThread

FileObserver内有一段这样的初始化代码

static {
        s_observerThread = new ObserverThread();
        s_observerThread.start();
}

public ObserverThread() {
            super("FileObserver");
            m_fd = init();
        }

这里调用native函数init并返回一个句柄m_fd

对应的jni实现在android_util_FileObserver.cpp

static jint android_os_fileobserver_init(JNIEnv* env, jobject object)
{
#ifdef HAVE_INOTIFY

    return (jint)inotify_init();    

#else // HAVE_INOTIFY

    return -1;

#endif // HAVE_INOTIFY
}

只是简单的调用inotify_init, Linux 2.6.13 内核开始,Linux 就推出了 inotify,允许监控程序打开一个独立文件描述符,并针对事件集监控一个或者多个文件,例如打开、关闭、移动/重命名、删除、创建或者改变属性

inotify_init用于创建一个 inotify 实例的系统调用,并返回一个指向该实例的文件描述符。

回到static部分,接着调用s_observerThread.start,进入run

 public void run() {
            observe(m_fd);
        }

Observe也是一个native函数,这里的参数m_fd是前面init返回的

static void android_os_fileobserver_observe(JNIEnv* env, jobject object, jint fd)
{
#ifdef HAVE_INOTIFY
 
    char event_buf[512];
    struct inotify_event* event;
         
    while (1)
    {
        int event_pos = 0;
        int num_bytes = read(fd, event_buf, sizeof(event_buf));
        
        if (num_bytes < (int)sizeof(*event))
        {
            if (errno == EINTR)
                continue;

            ALOGE("***** ERROR! android_os_fileobserver_observe() got a short event!");
            return;
        }
        
        while (num_bytes >= (int)sizeof(*event))
        {
            int event_size;
            event = (struct inotify_event *)(event_buf + event_pos);

            jstring path = NULL;
            
            if (event->len > 0)
            {
                path = env->NewStringUTF(event->name);
            }

            env->CallVoidMethod(object, method_onEvent, event->wd, event->mask, path);
            if (env->ExceptionCheck()) {
                env->ExceptionDescribe();
                env->ExceptionClear();
            }
            if (path != NULL)
            {
                env->DeleteLocalRef(path);
            }

            event_size = sizeof(*event) + event->len;
            num_bytes -= event_size;
            event_pos += event_size;

        }
    }
    
#endif // HAVE_INOTIFY
}

这里读取fd上是否有事件发生,如果有则调用Java层的onEvent函数

 

了解了这么多,我们回到

mSystemInstallObserver = new AppDirObserver(mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
 mSystemInstallObserver.startWatching();

这里

private static final int OBSERVER_EVENTS =REMOVE_EVENTS | ADD_EVENTS;

表示只监听文件添加和删除事件

当我们调用startWatching时

*/
    public void startWatching() {
        if (m_descriptor < 0) {
            m_descriptor = s_observerThread.startWatching(m_path, m_mask, this);
        }
}

调用ObserverThread的startWatching

public int startWatching(String path, int mask, FileObserver observer) {
            int wfd = startWatching(m_fd, path, mask);

            Integer i = new Integer(wfd);
            if (wfd >= 0) {
                synchronized (m_observers) {
                    m_observers.put(i, new WeakReference(observer));
                }
            }

            return i;
        }

调用native 函数startWatching并添加到m_observers

static jint android_os_fileobserver_startWatching(JNIEnv* env, jobject object, jint fd, jstring pathString, jint mask)
{
    int res = -1;
    
#ifdef HAVE_INOTIFY
   
    if (fd >= 0)
    {
        const char* path = env->GetStringUTFChars(pathString, NULL);
        
        res = inotify_add_watch(fd, path, mask);
        
        env->ReleaseStringUTFChars(pathString, path);
    }

#endif // HAVE_INOTIFY
    
    return res;
}

inotify_add_watch

增加对文件或者目录的监控,并指定需要监控哪些事件。标志用于控制是否将事件添加到已有的监控中,是否只有路径代表一个目录才进行监控,是否要追踪符号链接,是否进行一次性监控,当首次事件出现后就停止监控。

 

当有事件到来时,调用java层的onEvent,在AppDirObserver中实现

 public void onEvent(int event, String path) {
            String removedPackage = null;
            int removedAppId = -1;
            int[] removedUsers = null;
            String addedPackage = null;
            int addedAppId = -1;
            int[] addedUsers = null;

            // TODO post a message to the handler to obtain serial ordering
            synchronized (mInstallLock) {
                String fullPathStr = null;
                File fullPath = null;
                if (path != null) {
                    fullPath = new File(mRootDir, path);
                    fullPathStr = fullPath.getPath();
                }

                if (DEBUG_APP_DIR_OBSERVER)
                    Log.v(TAG, "File " + fullPathStr + " changed: " + Integer.toHexString(event));

                if (!isPackageFilename(path)) {
                    if (DEBUG_APP_DIR_OBSERVER)
                        Log.v(TAG, "Ignoring change of non-package file: " + fullPathStr);
                    return;
                }

                // Ignore packages that are being installed or
                // have just been installed.
                if (ignoreCodePath(fullPathStr)) {
                    return;
                }
                PackageParser.Package p = null;
                PackageSetting ps = null;
                // reader
                synchronized (mPackages) {
                    p = mAppDirs.get(fullPathStr);
                    if (p != null) {
                        ps = mSettings.mPackages.get(p.applicationInfo.packageName);
                        if (ps != null) {
                            removedUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
                        } else {
                            removedUsers = sUserManager.getUserIds();
                        }
                    }
                    addedUsers = sUserManager.getUserIds();
                }
                if ((event&REMOVE_EVENTS) != 0) {
                    if (ps != null) {
                        removePackageLI(ps, true);
                        removedPackage = ps.name;
                        removedAppId = ps.appId;
                    }
                }

                if ((event&ADD_EVENTS) != 0) {
                    if (p == null) {
                        p = scanPackageLI(fullPath,
                                (mIsRom ? PackageParser.PARSE_IS_SYSTEM
                                        | PackageParser.PARSE_IS_SYSTEM_DIR: 0) |
                                PackageParser.PARSE_CHATTY |
                                PackageParser.PARSE_MUST_BE_APK,
                                SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
                                System.currentTimeMillis(), UserHandle.ALL);
                        if (p != null) {
                            /*
                             * TODO this seems dangerous as the package may have
                             * changed since we last acquired the mPackages
                             * lock.
                             */
                            // writer
                            synchronized (mPackages) {
                                updatePermissionsLPw(p.packageName, p,
                                        p.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0);
                            }
                            addedPackage = p.applicationInfo.packageName;
                            addedAppId = UserHandle.getAppId(p.applicationInfo.uid);
                        }
                    }
                }

                // reader
                synchronized (mPackages) {
                    mSettings.writeLPr();
                }
            }

            if (removedPackage != null) {
                Bundle extras = new Bundle(1);
                extras.putInt(Intent.EXTRA_UID, removedAppId);
                extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
                sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
                        extras, null, null, removedUsers);
            }
            if (addedPackage != null) {
                Bundle extras = new Bundle(1);
                extras.putInt(Intent.EXTRA_UID, addedAppId);
                sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,
                        extras, null, null, addedUsers);
            }
        }

从代码中可以看出,如果是添加事件,则调用scanPackageLI,并使用updatePermissionsLPw授权;如果是删除事件则调用removePackageLI移除该apk的相关信息。最后都要调用writeLPr重新保存相关信息到packages.xml


0
0

  相关文章推荐
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1179783次
    • 积分:15335
    • 等级:
    • 排名:第749名
    • 原创:358篇
    • 转载:152篇
    • 译文:60篇
    • 评论:296条
    博客专栏
    最新评论