Android,检测其他应用程序何时启动

103
我正在开发一个应用程序,可以在没有密码的情况下防止用户使用指定的应用程序。场景如下:
  1. 用户点击“电子邮件”应用(例如)
  2. 我的应用程序检测到应用程序的启动
  3. 我的应用程序确认它是“电子邮件”应用程序
  4. 我的应用程序打开一个视图,要求输入密码
  5. 用户输入密码,如果正确,我的应用程序消失,留下“电子邮件”应用程序
我可以完成其余部分,但第二部分让我感到困惑,经过多天阅读广播意图等方面的相关知识,并尝试在我的试验项目中监听“android.intent.action.MAIN”等内容,似乎无法检测到启动除我的应用程序之外的应用程序。
有人能帮忙吗?我是否以正确的方式寻找新应用程序广播来启动,或者应该在系统日志中查找新意图,或者在本地代码中做一些处理?
任何提示都会有所帮助,即使您不能完全回答,我也能进行更多研究。非常感谢。Ian

我不确定他们是如何做到的,但像App Protector这样的应用程序确实可以完全满足您的要求,因此从技术上讲是可能的。 - user340145
@lan,你是如何解决你的问题的?能否分享一下你的经验? - nida
你好,你有解决方案吗? - ask4solutions
8个回答

34

我认为我们可以使用 logcat 并分析它的输出。

在所有类似的程序中,我找到了这个权限:

android.permission.READ_LOGS

这意味着它们都在使用它,但似乎程序会启动,然后我们的程序(应用程序保护器)将启动并置于前台。

使用以下代码:

try
    {
        Process mLogcatProc = null;
        BufferedReader reader = null;
        mLogcatProc = Runtime.getRuntime().exec(new String[]{"logcat", "-d"});

        reader = new BufferedReader(new InputStreamReader(mLogcatProc.getInputStream()));

        String line;
        final StringBuilder log = new StringBuilder();
        String separator = System.getProperty("line.separator"); 

        while ((line = reader.readLine()) != null)
        {
            log.append(line);
            log.append(separator);
        }
        String w = log.toString();
        Toast.makeText(getApplicationContext(),w, Toast.LENGTH_LONG).show();
    }
    catch (Exception e) 
    {
        Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
    }

不要忘记在Manifest文件中添加它的权限。


62
从JellyBean(安卓4.1)及以上版本将不再适用。READ_LOGS权限现在仅为系统应用程序所保留。 - Ran
6
你非常确定这一点吗?因为Smart AppLock似乎即使在JB设备上也能做到这一点。这是因为该应用程序将自身提升为设备管理员状态吗? - Karthik Balakrishnan
我已经尝试了这段代码,它可以正常工作,但是启动线程会导致内存溢出异常,因为它将打印大量模拟器日志。如何处理这个内存溢出异常? - Nitish Patel
1
@Torcellite,该应用具有“获取正在运行的任务”权限,因此它可能是使用该技术。 - Sam
1
@Ran,现在该怎么做才能使用它呢...是否已经有可用的解决方案来解决问题了,因为我需要在果冻豆以上...请尽快给出您的反馈... - Shreyan Mehta
显示剩余4条评论

21
一个花里胡哨的方法是创建一个定时循环的服务来检查,但不推荐这种方式。
ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfo = am.getRunningAppProcesses();

您可以浏览该列表以查看手机上正在运行的内容。 现在,您可以通过 ID 和 processName 进行标识,因此对于标准活动而言较为容易,但对于自定义应用程序,除非停止它们所有的运行,否则很难进行区分......

注意:这不是屏幕上实际显示的内容列表,而只是正在运行的内容列表...这可能使您的目标无效,但至少您会知道何时有东西开始运行...即使在后台,它仍将继续出现在该列表中。

对于密码问题,您可以在找到受保护或其他应用程序时启动您的活动。


能否获取应用程序启动/恢复的时间? - 0LLiena
4
在Android L中,请使用android.app.usage软件包。https://developer.android.com/reference/android/app/usage/package-summary.html - Plo_Koon

13
class CheckRunningActivity extends Thread{
    ActivityManager am = null;
    Context context = null;

    public CheckRunningActivity(Context con){
        context = con;
        am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    }

    public void run(){
        Looper.prepare();

        while(true){
            // Return a list of the tasks that are currently running,
            // with the most recent being first and older ones after in order.
            // Taken 1 inside getRunningTasks method means want to take only
            // top activity from stack and forgot the olders.
            List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(1);

            String currentRunningActivityName = taskInfo.get(0).topActivity.getClassName();

            if (currentRunningActivityName.equals("PACKAGE_NAME.ACTIVITY_NAME")) {
                // show your activity here on top of PACKAGE_NAME.ACTIVITY_NAME
            }
        }
        Looper.loop();
    }
}

您可以获取当前运行的Activity,并检查此Activity是否对应于电子邮件应用程序。

应用程序启动时(或设备启动时),运行CheckRunningActivity 线程

new CheckRunningActivity().start();

更新: 这个类需要android.permission.GET_TASKS权限,因此将下一行添加到清单文件中:

<uses-permission android:name="android.permission.GET_TASKS" />

我正在使用这种方法,但由于循环的原因,这将一遍又一遍地打开你的“// show your activity here on top of PACKAGE_NAME.ACTIVITY_NAME”。有什么解决办法吗? - Anuj Sharma
请您能否提供一个使用这种方法的可行完整示例。 - Anuj Sharma
你的回答有些令人困惑。我会继续搜索以获得正确的答案和解决方案。无论如何,感谢你的帮助。 - Anuj Sharma
如果有人关心代码的美观和正确性,那么应该使用这个解决方案。绝对比搞乱logcat要好。 - Dumoko
3
在您的代码中,Looper.loop()语句看起来永远不会被执行,因为while(true)循环永远不会结束。这是一个错误吗? - Sam
显示剩余7条评论

12

我认为并且希望这是不可能的。考虑到恶意软件会多么容易地滥用这种功能。你可以监听针对你的意图和广播的意图,但启动应用程序不应该是广播事件。

你可能能够做的是替换启动器,只有在用户同意的情况下才能进行。


1
为什么这不可能呢?这是我的设备,我可以决定在上面运行什么。这比我们通常授予的其他权限更成问题吗?替换启动器将无法捕获所有应用程序的启动,只能捕获直接由其启动的应用程序。这个问题有很多关于这个和类似主题的SO线程的评论声称,能够简单地看到意图过去会带来一些巨大的问题,但没有人解释问题在哪里以及为什么应该被视为如此恶劣,以至于现有的特权系统不能用于向用户清楚地说明正在发生什么。 - Kevin Whitefoot
作为潜在的权限,这个真是一个大挑战。拥有安全模型的目的是在防止大多数(理想情况下是全部)漏洞的同时,使大多数合法用例得以实现。需要保护的不仅是您(可能是一位知识渊博的用户),还有安装应用程序的天真用户和应用程序编写者,他们无需考虑另一个攻击向量。所有的安全都是权衡:在这种情况下,在效用和功能与大规模可利用性之间进行权衡。如果您真的想要自由度如此之高,可以克隆Android堆栈并编写自己的系统。 - Pontus Gagge

11
主要问题是你试图监听隐式意图,而启动器(主屏幕)通常使用显式意图。
隐式意图是指当你想说“有人播放这个视频”,Android会选择能够处理该意图的应用程序。
显式意图是指当你点击主屏幕上的“电子邮件”图标时发生的情况。它明确告诉Android打开具有完全限定名称(即com.android.mail或其他)的特定应用程序。
据我所知,没有办法拦截这种显式意图。这是Android中内置的一项安全措施,因为不允许两个Activity具有相同的完全限定包名。这可以防止第三方克隆应用程序并冒充该应用程序。如果你想做的事情是可能的,理论上你可以安装一个应用程序来阻止所有竞争对手的应用程序工作。
你所尝试的做法违反了Android的安全模型。
你可以做的一件事是与特定的应用程序开发人员合作,将意图转发到你的安全系统中,但这可能不是你想要处理的事情。

8

getRunningTasks()在Android L中已被弃用。

要获取应用程序使用统计信息,您可以使用UsageStats类,该类位于android.app.usage包中。

新的应用程序使用统计API允许应用程序开发人员收集与应用程序使用相关的统计信息。此API提供比已弃用的getRecentTasks()方法更详细的使用信息。

要使用此API,您必须首先在清单文件中声明android.permission.PACKAGE_USAGE_STATS权限。用户还必须通过设置 > 安全性 > 具有使用权限的应用程序启用此应用程序的访问权限。

这里是一个基本的应用程序示例,演示如何使用应用程序使用统计API让用户收集与应用程序使用相关的统计信息。


使用统计数据如何帮助我们知道哪个应用程序在前台? - Ajay

4
也许你需要一个服务,它可以在后台持续运行。然后让你的服务执行你所说的操作。监听 android.intent.action.MAIN 和 android.intent.category.LAUNCHER 类别。然后让广播接收器覆盖 onReceive 方法并检查应用程序名称等信息。

2
这似乎就是我想到的方法,但是我正在尝试使用基本的BroadcastReceiver接收MAIN(cat。LAUNCHER)广播,但遇到了困难。有人以前成功实现过吗?目前,我只是想检测应用程序是否已启动或恢复。然后,我可以将包名称与保存所需名称的字符串进行比较。 - Ian

1

更现代的答案(2022年11月):

这可以通过新的应用使用API来处理。

系统会按每个应用程序收集使用数据,并在每日、每周、每月和每年间隔上汇总数据。系统保留此数据的最长时间如下:

每日数据:7天 每周数据:4周 每月数据:6个月 每年数据:2年 对于每个应用程序,系统记录以下数据:

应用程序上次使用的时间

应用程序在该时间间隔内前台运行的总时长(按天、周、月或年计算)

时间戳捕获组件(由包和活动名称标识)何时在一天内移至前台或后台

时间戳捕获设备配置更改(例如,当设备方向因旋转而改变时)

可以使用后台服务来使用上述API不断监视特定应用程序的时间戳更新,以了解它们何时移至前台。

注:似乎从Android 5/6/7及更高版本开始,您不能读取有关其他应用程序的信息,包括它们的状态和日志。我相信这使得接受的答案不再有效。如果有人可以确认这一点,请在下面发表评论!此外,由@Veaceslav Gaidarji建议使用的getRunningTasks函数现已过时。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接