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的这一机制,让用户能够有机会干掉病毒进程。