今天遇到个问题, 使用action为“Android.media.action.IMAGE_CAPTURE“打不开Android6.0系统的小米4手机照相机, 问题是你不知道到底是否启动了相机。 我试了很多种方法, 最终只想到一个土办法:点击拍照按钮后延迟1秒判断是否执行了onStop函数或自己是否前台进程,方法很low也可能误判。(PS:从自己app打开照相机后会执行onPause-onStop函数)但在尝试的过程中学到不少东西, 可以分享一下:

        目前没找到6.0版本获取top activity的方法, google把我能想到的路都封死了。 因为这是个漏洞,假设钓鱼app监听打开支付宝、微信输入密码的activity时,覆盖一模一样的界面就可以盗取你的机密信息了。  

        

1、 Android5.0以下的方法在5.0及后续版本不再有效。

[java] view plain copy
  1. List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);  
  2. if (tasks != null && !tasks.isEmpty()) {  
  3.     ComponentName componentName = tasks.get(0).topActivity;  
  4.     if (componentName != null) {  
  5.         return componentName.getClassName();  
  6.     }  
  7. }  

2、尝试ActivityManager的getRunningAppProcesses方法,但从Andriod5.1版本后只能拿到自己的进程信息, 所以此路不通;


3、使用UsageStatsManager类验证, 但它需要系统权限, 实际使用场景下很难被授权, 此路不通;


4、思路:在android Runtime运行dumpsys meminfo , 拿到输出并截取前台进程, 但是拿不到top activity, 可以做个测试手段。

在cmd窗口执行adb shell dumpsys meminfo 后输出所有运行进程的信息,能明显的看出那个是前台进程。 但在Android运行时报无DUMP权限, 这是系统签名才能执行的操作。

   373663 kB:       0 kB: Foreground
              265450 kB:       0 kB: com.android.browser (pid 9131 / activities)
               30164 kB:       0 kB: com.miui.securitycenter.remote (pid 3191)

               28803 kB:       0 kB: com.miui.networkassistant.deamon (pid 3125)


[java] view plain copy
  1. do_exec("dumpsys meminfo ");   
  2.  String do_exec(String cmd) {  
  3.        String s = "/n";  
  4.        try {  
  5.            Process p = Runtime.getRuntime().exec(cmd);  
  6.            BufferedReader in = new BufferedReader(  
  7.                    new InputStreamReader(p.getInputStream()));  
  8.            String line = null;  
  9.            while ((line = in.readLine()) != null) {  
  10.                s += line + "/n";  
  11.            }  
  12.        } catch (IOException e) {  
  13.            // TODO Auto-generated catch block  
  14.            e.printStackTrace();  
  15.        }  
  16.        return s;  
  17.    }  

5、hook AMS, 能够监听到打开照相机的action,但无法确定是否启动了照相机。 原理是只能hook ActivityManagerNative类,是ActivityManagerNative(参考Android 插件化原理解析——Hook机制之AMS&PMS)的代理对象,运行在app当前进程;无法hook AMS的本地对象(运行在system_server进程)。


6、尝试打开手机proc目录下进程id目录下的cmdline文件, 部分机型能够看到包名。如果是前台进程,那么oom_score_adj文件内容为0;否则不是0。 而cmdline文件中是对应的包名。  因为Linux限制打开其它进程的文件,无法读取其它进程目录下的cmdline和oom_adj文件, 但可以了解一下linux命令。

[java] view plain copy
  1. private String getForegroundApp() {  
  2.        File[] files = new File("/proc").listFiles();  
  3.        String foregroundProcess = null;  
  4.        int i = 0;  
  5.        for (File file : files) {  
  6.            i++;  
  7.            Log.d("brycegao""proc file:" + file.getName()  
  8.                      + ", loop:" + i);  
  9.            if (file.isFile()) {  
  10.                continue;  
  11.            }  
  12.            int pid;  
  13.   
  14.            Log.d("brycegao""proc filename:" + file.getName());  
  15.            try {  
  16.                pid = Integer.parseInt(file.getName());  
  17.            } catch (NumberFormatException e) {  
  18.                continue;  
  19.            }  
  20.   
  21.            try {  
  22.                //读取进程名称  
  23.                String cmdline = read(String.format("/proc/%d/cmdline", pid));  
  24.                String oomAdj = read(String.format("/proc/%d/oom_adj", pid));  
  25.                Log.d("brycegao""adj1111:" + oomAdj + ",pkg:" + cmdline);  
  26.   
  27.                if (oomAdj.equalsIgnoreCase("0")) {  
  28.                    //前台进程  
  29.                    Log.d("brycegao""adj:" + oomAdj + ",pkg:" + cmdline);  
  30.                } else {  
  31.                    continue;  
  32.                }  
  33.   
  34.                if (cmdline.contains("systemui")  
  35.                        || cmdline.contains("/")) {  
  36.                    continue;  
  37.                }  
  38.   
  39.                foregroundProcess = cmdline;  
  40.            } catch (IOException e) {  
  41.                e.printStackTrace();  
  42.            }  
  43.        }  
  44.        return foregroundProcess;  
  45.   
  46.    }  

[java] view plain copy
  1. private String read(String path) throws IOException {  
  2.       StringBuilder output = new StringBuilder();  
  3.       BufferedReader reader = new BufferedReader(new FileReader(path));  
  4.       output.append(reader.readLine());  
  5.       for (String line = reader.readLine(); line != null; line = reader.readLine()) {  
  6.           output.append('\n').append(line);  
  7.       }  
  8.       reader.close();  
  9.       return output.toString();  
  10.   }  


7、尝试使用linux 的ps命令、grep命令和cat命令, 惊讶的发现使用cat可以查看其它进程目录下的文件!!! 对上面方法稍作改变,就能得到前台进程的名称了,注意前台进程是多个。 下面的示例代码只取一个前台进程名称, 其实应该是个ArrayList,懒的优化了、只说原理。 其实就是将打开文件的操作改成用cat查看, 这样就不会出现权限问题了。

[java] view plain copy
  1. private String getForegroundApp() {  
  2.         File[] files = new File("/proc").listFiles();  
  3.         String foregroundProcess = "";  
  4.         int i = 0;  
  5.         for (File file : files) {  
  6.             i++;  
  7.             Log.d("brycegao""proc file:" + file.getName()  
  8.                     + ", loop:" + i);  
  9.             if (file.isFile()) {  
  10.                 continue;  
  11.             }  
  12.             int pid;  
  13.   
  14.             Log.d("brycegao""proc filename:" + file.getName());  
  15.             try {  
  16.                 pid = Integer.parseInt(file.getName());  
  17.             } catch (NumberFormatException e) {  
  18.                 continue;  
  19.             }  
  20.   
  21.             try {  
  22.                 //读取进程名称  
  23.                 String cmdline = do_exec(String.format("cat /proc/%d/cmdline", pid));  
  24.                 String oomAdj = do_exec(String.format("cat /proc/%d/oom_adj", pid));  
  25.                 Log.d("brycegao""adj1111:" + oomAdj + ",pkg:" + cmdline);  
  26.                 if (oomAdj.equalsIgnoreCase("0")) {  
  27.                     //前台进程  
  28.                     Log.d("brycegao""adj:" + oomAdj + ",pkg:" + cmdline);  
  29.                 } else {  
  30.                     continue;  
  31.                 }  
  32.   
  33.                 if (cmdline.contains("systemui")  
  34.                         || cmdline.contains("/")) {  
  35.                     continue;  
  36.                 }  
  37.   
  38.                 foregroundProcess = cmdline;  
  39.             } catch (Exception e) {  
  40.                 e.printStackTrace();  
  41.             }  
  42.         }  
  43.   
  44.         Log.d("brycegao""forgroud process:" + foregroundProcess);  
  45.         return foregroundProcess;  
  46.   
  47.     }  


   第7个方法算是个黑科技了, 能拿到Android手机的前台进程名称(注意:前台进程可以是多个), 但无法判断当前正在显示的是哪个进程以及top activity。。。。


   8、  在cmd窗口输入adb shell dumpsys activity 后回车, mFocusedActivity后就是前台进程(有包名和activity名称);在Android里执行该语句需要android.permission.DUMP权限,这是系统权限,一般app拿不到的。

Android6.0获取前台进程名称的方法

       dump出的数据很多, 有兴趣的同学可以试试。 其实我们只要mFocusedActivity这一行数据, 添加grep命令即可:

adb shell dumpsys activity | grep mFocusedActivity

Android6.0获取前台进程名称的方法

     

Android6.0获取前台进程名称的方法


参考:Android内存管理篇 - adj的概念与进程adj级别控制

相关文章:

  • 2022-12-23
  • 2022-02-24
  • 2022-02-15
  • 2022-12-23
  • 2022-12-23
  • 2022-02-01
  • 2021-10-22
  • 2022-01-16
猜你喜欢
  • 2022-01-04
  • 2022-12-23
  • 2021-04-06
  • 2021-08-29
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案