也许一些使用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安全:forceStopPackage对android的Alarm的影响的更多相关文章

  1. 【Android 应用开发】Android 开发错误集锦

    1. eclipse的Device中不显示手机 在eclipse中连接不上手机,出现adb server didn't ACK  fail to start daemon 错误. 出现这种原因是因为a ...

  2. 我的Android进阶之旅------>Android使用AlarmManager全局定时器实现定时更换壁纸

    该DEMO将会通过AlarmManager来周期的调用ChangeService,从而让系统实现定时更换壁纸的功能. 更换壁纸的API为android.app.WallpaperManager,它提供 ...

  3. Android数据存储之Android 6.0运行时权限下文件存储的思考

    前言: 在我们做App开发的过程中基本上都会用到文件存储,所以文件存储对于我们来说是相当熟悉了,不过自从Android 6.0发布之后,基于运行时权限机制访问外置sdcard是需要动态申请权限,所以以 ...

  4. Android权限管理之Android 6.0运行时权限及解决办法

    前言: 今天还是围绕着最近面试的一个热门话题Android 6.0权限适配来总结学习,其实Android 6.0权限适配我们公司是在今年5月份才开始做,算是比较晚的吧,不过现在Android 6.0以 ...

  5. Android应用架构之Android MVP使用

    前两篇已经将Retrofit和RxAndroid应用到了项目中,这篇本打算直接将Dagger2引进项目,但是考虑到整个项目结构,就来个结构整理吧,一起来看看网上炒得火热MVP模式. 说到MVP就不得不 ...

  6. [译]:Xamarin.Android开发入门——Hello,Android Multiscreen深入理解

    原文链接:Hello, Android Multiscreen_DeepDive. 译文链接:Xamarin.Android开发入门--Hello,Android Multiscreen深入理解. 本 ...

  7. [译]:Xamarin.Android开发入门——Hello,Android Multiscreen快速上手

    原文链接:Hello, Android Multiscreen Quickstart. 译文链接:Hello,Android Multiscreen快速上手 本部分介绍利用Xamarin.Androi ...

  8. [译]:Xamarin.Android开发入门——Hello,Android深入理解

    返回索引目录 原文链接:Hello, Android_DeepDive. 译文链接:Xamarin.Android开发入门--Hello,Android深入理解 本部分介绍利用Xamarin开发And ...

  9. [译]:Xamarin.Android开发入门——Hello,Android快速上手

    返回索引目录 原文链接:Hello, Android_Quickstart. 译文链接:Xamarin.Android开发入门--Hello,Android快速上手 本部分介绍利用Xamarin开发A ...

  10. Android学习第二天-android常用命令

    上一篇文章中,我们重点讲解了adb的常用命令,下面我们一起来看看其它常用的命令 2 android 2.1 查看机器上所有已经安装的Android版本和AVD设备 2.1.1查看机器上已经安装的AVD ...

随机推荐

  1. python内建函数sorted方法概述

    python中,具体到对list进行排序的方法有俩,一个是list自带的sort方法,这个是直接对list进行操作,只有list才包含的方法:另外一个是内建函数sorted方法,可以对所有可迭代的对象 ...

  2. PHPSTORM 与 Xdebug 配合调试

    基本的配置可以参考网上的文档, 浏览器中装插件(xdebug)或直接在请求中加上如下的参数也可启动调试 ?XDEBUG_SESSION_START=PHPSTORM

  3. PIC和PIE

    PIC指的是位置无关代码,用于生成位置无关的共享库,所谓位置无关,指的是共享库的代码断是只读的,存放在代码段,多个进程可同时公用这份代码段而不需要拷贝副本.库中的变量(全局变量和静态变量)通过GOT表 ...

  4. linux 条件变量

    互斥量就是一把锁,在访问数据时能保证同一时间内只有一个线程访问数据,在访问完以后再释放互斥量上的锁. 条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条 ...

  5. 如何创建Asp.net MVC ViewModel

    ASP.NET MVC View Model Patterns Since MVC has been released I have observed much confusion about how ...

  6. 挖坟之Spring.NET IOC容器初始化

    因查找ht项目中一个久未解决spring内部异常,翻了一段时间源码.以此文总结springIOC,容器初始化过程. 语言背景是C#.网上有一些基于java的spring源码分析文档,大而乱,乱而不全, ...

  7. HW7.15

    public class Solution { public static void main(String[] args) { double[][] set1 = {{1, 1}, {2, 2}, ...

  8. html5 canvas 旋转扩大

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  9. 了解shell

    1. shell 脚本文件第一行:    #!/bin/sh 或 #!/bin/bash "#!"  又称为纪数,在执行bash脚本的时候,内核会根据它来确定该用哪个程序来解释脚本 ...

  10. 轻松学习Linux之进程监视与管理

    前后台进程转换-1 前后台进程转换-2 本文出自 "李晨光原创技术博客" 博客,谢绝转载!