最近按项目要求,需要做一个车机Android在线升级操作,但是cache内存太小了,最后只能寄存希望在 data/文件夹下,但是data/目录在6.0之后禁止recovery读取data文件.最后的解决方案是在/data/文件下创建一个系统app能够操作的文件夹,进行升级操作.

 

1.升级流程

Android自带升级流程 API  RecoverySystem.java  (framework/base/core/java/android/os/RecoverySystem.java) 

调用如下代码就可以进行升级操作,对你没看错,就是一句代码,将你下载好的压缩包路径传过去就行.

   private void excuteUpdateZip(String local_path) {        // TODO Auto-generated method stub        try {            RecoverySystem.installPackage(this, new File(local_path));            //RecoverySystem.installPackage(this, new File(local_path));        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }

但是 事情永远是比想象的麻烦, 因为我们的车机要求的是将压缩包放到 /data/目录下. 那放就放吧  先编译好一个全包 使用命令

adb root adb remountadb push  updata.zip /data/

使用模拟升级命令进行升级操作

adb shell "echo --update_package=/data/update.zip > /cache/recovery/command"adb shell syncadb reboot recovery

就这样成功进行了升级,what?完成了? 这么简单,原来这才是跳坑的开始,看到生面测试成功了之后,迫不及待的在自己的程序中将路径填写成  /data/update.zip ,开始了升级操作,结果失败了.开始检查原因

是不是AndroidManifest.xml中没有添加 如下权限

android:sharedUserId="android.uid.system"

查看添加了,是不是没有对自己的apk进行系统签名?发现也签名了(如何给APK系统签名),没有放到system/app/文件夹下?也放了,.那原因出在哪里那?不想了,先去看看log

Android机器中 /cache/recovery/的目录结构如下:

cache/        └── recovery          ├── last_install    ├── last_kmsg  #系统升级的时候的全log. 如果有last_kmsg.1                    #那last_kmsg 永远是最后一次升级的log,所以每                   #次只查看last_kmsg就行    ├── last_locale    └── last_log   #升级过程中的简log,能看到为何升级失败

在log中查看到的是  [email protected]/cache/recovery/block.map 看到这个结果的时候,第一感觉就是apk没有安装成功,于是加了log 发现加的log 成功打印,吓了一跳,自己撞见鬼了? 

抱着程序员不明原因不放弃的心态,看了一下RecoverySystem.java 中的 installPackage 方法,原来是 installpackage在代码中做了限制, 要是设置的路径是以 /data/ 开头强制更改@/cache/recovery/block.map  

  @SystemApi    public static void installPackage(Context context, File packageFile, boolean processed)            throws IOException {        //省略代码......       if (filename.startsWith("/data/")) {                if (processed) {                   //省略代码......                } else {                //省略代码......                }                //TODO 重点就是这句话,将路径强制求改                filename = "@/cache/recovery/block.map";            }             final String filenameArg = "--update_package=" + filename + "\n";            final String localeArg = "--locale=" + Locale.getDefault().toString() + "\n";            final String securityArg = "--security\n";            String command = filenameArg + localeArg;            //省略代码......        }    }

看到这,挡了我的路,必定铲除,将代码很愉快的注掉了,重新编译进行测试.

激动的等待中.........

又失败了?难道还有拦路虎? 我的心要崩溃了,为什么还是失败,还是查看log发现 --update_package=data/update.zip . 这么看来已经能写到重启文件中了.

查看log提示是  --update_package=data/update.zip  Permission Denied 看来是datarecover没有权限读取/data目录啊

 

快速验证 是不是selinux问题 可以修改 BoardConfig.mk 添加如下代码 编译boot文件测试,如下代码是禁用selinux代码

BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive

 

 

2.selinux权限问题

经过一番调查 发现 data的 type属于 system_data_file类型的

在file_contexts中可以查看到

/data(/.*)?u:object_r:system_data_file:s0

在recovery.te中发现了如下代码

neverallow recovery data_file_type:file { no_w_file_perms no_x_file_perms };neverallow recovery data_file_type:dir no_w_dir_perms;

也就是说明 recover.te何时都不能读取和操作/data/目录,那怎么办?当然是有办法的,我们可以新建一个文件夹 和自定义文件类型啊

首先是开机的时候要有一个文件夹

我们可以在init.rc 或者 init.qcom.rc中创建一个文件夹

#Create directory for downloadmkdir /data/download 0771chown system system /data/download

创建完成之后需要app可以读取因此 需要在 system_app.te 中增加对应的权限

# allow system_app to access /data/downloadallow system_app download_data_file:dir { search write add_name getattr remove_name };allow system_app download_data_file:file { create read write open getattr unlink rename };

这样的话 我们已经创建了 文件夹,并且system app级别的应用可以读取.

那既然存储好了之后是不是下面需要 recovery可以读取到文件那?系统不允许读取 recovery.te /data/路径.那我们就自己创建一个文件类型,在file_contexts 增加类型

/data/download(/.*)?           u:object_r:download_data_file:s0  

然后在file.te中定义download_data_file类型, 注意是只属于file_type类型的。

type download_data_file, file_type;   

然后在recovery.te中增加对download_data_file的权限

allow recovery download_data_file:dir { write search remove_name };allow recovery download_data_file:file { read getattr open unlink };

因为data目录有可能需要进行加密处理,我们还需要在uncrypt.te中增加如下。

allow uncrypt download_data_file:dir { search getattr };  allow uncrypt download_data_file:file { getattr read open };

以上就是完成了添加,我本地全编译,测试成功.

 

最后升级的过程中可能会看到Permission Denied 最后可以cat   last_kmsg 查看 最后一样都会输出失败的权限原因

比如 :

avc:  denied  { open } for  pid=381 comm="recovery"path="/data/download/update.zip" dev="mmcblk0p51" ino=513074 scontext=u:r:recovery:s0 tcontext=u:object_r:download_data_file:s0 tclass=file permissive=0

这种权限只能报错什么权限添加什么权限,我们可以遵循这个方法,从头开始寻找关键对象,然后调整一下顺序,生成一条语句,最后将该语句填写到.te中即可。

denied { open }            scontext=u:r:recovery:s0          tcontext=u:object_r:download_data_file:s0        tclass=file               

             A                                    B                                                             C                                                  D
 
             B                                    C                                                             D                                                   A

allow recovery  download_data_file:tclass   open;

如果升级没有错误,APP操作文件夹中的文件无效时,可以在操作的时候执行如下命令,如果报了相关权限问题,加上即可,

adb shell dmesg | grep denied

 

更多相关文章

  1. Android HAL 层框架分析以及代码示例
  2. 读懂Android (1):使用Android内部的DownloadProvider下载文件,并
  3. Android init源代码分析(1)概要分析
  4. Android设备之间通过Wifi通信的示例代码
  5. Android ActionBar的源代码分析(三)
  6. android 使用自定义权限(1)
  7. Android代码开发性能指引

随机推荐

  1. 【Android】Android(安卓)Studio 1.5+ 中
  2. Android(安卓)之 ContentProvider的简介-
  3. Android(安卓)系统(125)---Android通过Dial
  4. android的响铃MediaPlayer
  5. Android核心分析(21)----Android应用框架之
  6. Android(安卓)4.2启动代码分析(一)
  7. android小复习
  8. Android(安卓)用SlidingDrawer实现抽屉效
  9. Android(安卓)应用程序中使用 Internet
  10. 学习Android(安卓)应注意的十个问题