如何读取第三方应用的数据库?(PART 1)

经理:“Tank,我们需要访问第三方APP的数据库”
ME:“这个简单呀,用AS自带的Device File Explorer就可以”
经理:“不是,我的意思是你能不能写一个APP打印一下其他应用创建的数据库”
ME:“。。。可以ROOT吗?”
经理:“只要能取到,啥都可以”

多么神奇的对话呀~
想要解决这个需求,我们首先要知道APP创建的数据库放在哪里。
一般来说,应用创建的数据库会存储在data/data/应用包名/databases目录下,看到这个目录应该就会了解该需求有多么奇葩了吧?
在安卓7.0之后的版本,修改了权限的访问权限,大致可以分为公有空间和私有空间,外部存储即公有空间,允许所有的应用读取和访问,但是data对应的目录即为私有空间,在这种机制下是不允许应用访问第三方应用的数据库所在目录的。

怎么办呢?要不要去告诉经理该需求难以实现呢?

哈哈,咱们回顾下开头的那段对话,里面有我们的解决方案!
没错,我们可以ROOT嘛,虽然安卓并不是很喜欢ROOT这个东西,但是针对这个需求,它却是一个好东西呢。
至于如何ROOT,网上有太多太多的方案,这里就不再赘述,只是给大家分享一下我参考的一种方式:
ROOT方案

ROOT之后,我们通过File的listFile去遍历文件试试,如果不出意外的话会报一个No such file错误。可以我们通过Device File Explorer是可以看到对应文件的,难道是ROOT之后还是没法访问到data目录吗?
在这里插入图片描述
或许是没有ROOT成功?我们用adb shell来认证一下:
首先,我们打开cmd窗口,输入adb shell命令运行,结果如下图:
在这里插入图片描述
输入su之后运行
在这里插入图片描述
我们看到$变成了#,这说明手机是ROOT成功了的。
既然我们已经打开了cmd,不如通过命令的方式去查看下数据库所在的目录?刚才提到过,我们借助Device File Explorer是能够看到对应的目录的,我们直接通过cd命令访问某目录,并ls打印列表看一下。
在这里插入图片描述
神奇不神奇?
通过命令我们竟然能够访问到数据库所在的目录,那么在安卓代码中是不是也可以通过命令来访问呢?
答案显然是可以的。
我们写一个方法来获取数据库列表,代码如下:

private  List<String> getDbListByPkg(String pkgName){
        List<String> dbs = new ArrayList<>();
        ProcessBuilder pb = new ProcessBuilder("/system/bin/sh");
        pb.directory(new File("/"));//设置shell的当前目录。
        try {
            Process proc = pb.start();
            //获取输入流,可以通过它获取SHELL的输出。
            BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            BufferedReader err = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
            //获取输出流,可以通过它向SHELL发送命令。
            PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc
                    .getOutputStream())), true);
            out.println("pwd");
            out.println("su root");//执行这一句时会弹出对话框(以下程序要求授予最高权限...),要求用户确认。
            out.println("cd /data/data/"+pkgName+"/databases");//这个目录在系统中要求有root权限才可以访问的。
            out.println("ls -l");//这个命令如果能列出当前安装的APK的数据文件存放目录,就说明我们有了ROOT权限。
            out.println("exit");
            out.println("exit");
            // proc.waitFor();
            String line;
            while ((line = in.readLine()) != null) {
                if(line.endsWith(".db")){
                    String[] infos = line.split("\\s+");
                    if (infos.length == 8) {
                        dbs.add(infos[7]);
                    }
                }
            }
            in.close();
            out.close();
            proc.destroy();
        } catch (Exception e) {
            System.out.println("exception:" + e);
        }

        return dbs;
    }

然后我们还是查询com.android.providers.calendar的数据库,将该包名作为参数调用方法后打印List的内容,也就是数据库名字,效果如下:
在这里插入图片描述
显而易见,我们完全可以通过命令来遍历某应用所创建的数据库。但是,仅仅获取到数据库的名字是不是有点太寒酸了呢?下一篇,我们一起来打印数据库里面具体的内容。

猜你喜欢

转载自blog.csdn.net/RobinTan24/article/details/108473209