android安全:forceStopPackage对android的Alarm的影响

也许一些使用alarmmanager做定时任务的同学遇到过这样的问题:设定alarm后,进入设置-->应用程序管理-->强行停止app后,定时任务就失效了。


简单的讲就是:force stop会导致alarm失效。


最典型的例子就是我碰到过的一个bug,使用android手机的时钟app设置一个闹钟,然后进入设置-->应用程序管理里面,将时钟这个app force stop掉,结果闹钟就不响了。

其实这不是bug,这是android系统的新加入的机制。下面我来详细分析一下来龙去脉。


1. 在设置的应用程序管理里面强行停止app:

    这里会最终会调用到 ActivityManagerService的forceStopPackageLocked()

源代码如下:

<SPAN style="FONT-SIZE: 18px">    private void forceStopPackageLocked(final String packageName, int uid) { 

        forceStopPackageLocked(packageName, uid, false, false, true, false); 

        Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED, 

                Uri.fromParts("package", packageName, null)); 

        if (!mProcessesReady) { 

            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 

        } 

        intent.putExtra(Intent.EXTRA_UID, uid); 

        broadcastIntentLocked(null, null, intent, 

                null, null, 0, null, null, null, 

                false, false, MY_PID, Process.SYSTEM_UID); 

    }</SPAN> 

    private void forceStopPackageLocked(final String packageName, int uid) {

        forceStopPackageLocked(packageName, uid, false, false, true, false);

        Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,

                Uri.fromParts("package", packageName, null));

        if (!mProcessesReady) {

            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);

        }

        intent.putExtra(Intent.EXTRA_UID, uid);

        broadcastIntentLocked(null, null, intent,

                null, null, 0, null, null, null,

                false, false, MY_PID, Process.SYSTEM_UID);

    }

代码里面发送了一个广播:ACTION_PACKAGE_RESTARTED,这个广播大有文章。

2. 再看看AlarmManagerService.java的代码,可以看一个内部类UninstallReceiver

源代码如下:

 

[java]

<SPAN style="FONT-SIZE: 18px">class UninstallReceiver extends BroadcastReceiver { 

        public UninstallReceiver() { 

            IntentFilter filter = new IntentFilter(); 

            filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 

            filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); 

            filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); 

            filter.addDataScheme("package"); 

            mContext.registerReceiver(this, filter); 

             // Register for events related to sdcard installation.  

            IntentFilter sdFilter = new IntentFilter(); 

            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 

            mContext.registerReceiver(this, sdFilter); 

        } 

         

        @Override 

        public void onReceive(Context context, Intent intent) { 

            synchronized (mLock) { 

                String action = intent.getAction(); 

                String pkgList[] = null; 

                if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) { 

                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); 

                    for (String packageName : pkgList) { 

                        if (lookForPackageLocked(packageName)) { 

                            setResultCode(Activity.RESULT_OK); 

                            return; 

                        } 

                    } 

                    return; 

                } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { 

                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); 

                } else { 

                    if (Intent.ACTION_PACKAGE_REMOVED.equals(action) 

                            && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 

                        // This package is being updated; don't kill its alarms.  

                        return; 

                    } 

                    Uri data = intent.getData(); 

                    if (data != null) { 

                        String pkg = data.getSchemeSpecificPart(); 

                        if (pkg != null) { 

                            pkgList = new String[]{pkg}; 

                        } 

                    } 

                } 

                if (pkgList != null && (pkgList.length > 0)) { 

                    for (String pkg : pkgList) { 

                        removeLocked(pkg); 

                        mBroadcastStats.remove(pkg); 

                    } 

                } 

            } 

        } 

    }</SPAN> 

class UninstallReceiver extends BroadcastReceiver {

        public UninstallReceiver() {

            IntentFilter filter = new IntentFilter();

            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);

            filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);

            filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);

            filter.addDataScheme("package");

            mContext.registerReceiver(this, filter);

             // Register for events related to sdcard installation.

            IntentFilter sdFilter = new IntentFilter();

            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);

            mContext.registerReceiver(this, sdFilter);

        }

       

        @Override

        public void onReceive(Context context, Intent intent) {

            synchronized (mLock) {

                String action = intent.getAction();

                String pkgList[] = null;

                if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {

                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);

                    for (String packageName : pkgList) {

                        if (lookForPackageLocked(packageName)) {

                            setResultCode(Activity.RESULT_OK);

                            return;

                        }

                    }

                    return;

                } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {

                    pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);

                } else {

                    if (Intent.ACTION_PACKAGE_REMOVED.equals(action)

                            && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {

                        // This package is being updated; don't kill its alarms.

                        return;

                    }

                    Uri data = intent.getData();

                    if (data != null) {

                        String pkg = data.getSchemeSpecificPart();

                        if (pkg != null) {

                            pkgList = new String[]{pkg};

                        }

                    }

                }

                if (pkgList != null && (pkgList.length > 0)) {

                    for (String pkg : pkgList) {

                        removeLocked(pkg);

                        mBroadcastStats.remove(pkg);

                    }

                }

            }

        }

    }可见AlarmManagerService接受了ACTION_PACKAGE_RESTARTED广播,而且执行了removeLocked(pkg)

removeLocked()是做什么的呢?继续看源码:



[java]

<SPAN style="FONT-SIZE: 18px">    public void removeLocked(String packageName) { 

        removeLocked(mRtcWakeupAlarms, packageName); 

        removeLocked(mRtcAlarms, packageName); 

        removeLocked(mElapsedRealtimeWakeupAlarms, packageName); 

        removeLocked(mElapsedRealtimeAlarms, packageName); 

    } 

 

    private void removeLocked(ArrayList<Alarm> alarmList, 

            String packageName) { 

        if (alarmList.size() <= 0) { 

            return; 

        } 

 

        // iterator over the list removing any it where the intent match  

        Iterator<Alarm> it = alarmList.iterator(); 

         

        while (it.hasNext()) { 

            Alarm alarm = it.next(); 

            if (alarm.operation.getTargetPackage().equals(packageName)) { 

                it.remove(); 

            } 

        } 

    }</SPAN> 

    public void removeLocked(String packageName) {

        removeLocked(mRtcWakeupAlarms, packageName);

        removeLocked(mRtcAlarms, packageName);

        removeLocked(mElapsedRealtimeWakeupAlarms, packageName);

        removeLocked(mElapsedRealtimeAlarms, packageName);

    }

    private void removeLocked(ArrayList<Alarm> alarmList,

            String packageName) {

        if (alarmList.size() <= 0) {

            return;

        }

        // iterator over the list removing any it where the intent match

        Iterator<Alarm> it = alarmList.iterator();

       

        while (it.hasNext()) {

            Alarm alarm = it.next();

            if (alarm.operation.getTargetPackage().equals(packageName)) {

                it.remove();

            }

        }

    }

看到这里,大家应该明白了,removeLocked就是将对应package设置的所有类型的alarm都remove掉。

看到这里大家应该知道为什么自己设置的alarm会不起作用了吧?

思考:

为什么google要加入这样的机制呢?

应该是出于系统安全的考虑,google在4.0系统中在安全方面做了很多努力。

很多病毒程序都不希望自己的进程被用户强行停止,希望自己的病毒程序可以一直运行,而常见的方式就是通过设置alarm,在病毒进程被杀死后,通过定时发送广播来拉起病毒进程,来实现病毒进程的重新启动。

google也正是看到了这个一点,所以加入了forceStopPackage的这一机制,让用户能够有机会干掉病毒进程。 

你可能感兴趣的:(android)