今天,就為大家?guī)鞟ndroid 7.1車機系統(tǒng)的在線升級教程。想要輕松升級你的車機系統(tǒng)嗎?只需將安裝包下載至/data/目錄,即可一鍵升級,讓駕駛體驗更流暢!
最近按項目要求,需要做一個車機Android在線升級操作,但是cache內(nèi)存太小了,最后只能寄存希望在 data/文件夾下,但是data/目錄在6.0之后禁止recovery讀取data文件.最后的解決方案是在/data/文件下創(chuàng)建一個系統(tǒng)app能夠操作的文件夾,進行升級操作.
1、升級流程
Android自帶升級流程 API RecoverySystem.java (framework/base/core/java/android/os/RecoverySystem.java)
調(diào)用如下代碼就可以進行升級操作,對你沒看錯,就是一句代碼,將你下載好的壓縮包路徑傳過去就行.
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 remount
adb push updata.zip /data/
使用模擬升級命令進行升級操作
adb shell "echo --update_package=/data/update.zip > /cache/recovery/command"
adb shell sync
adb reboot recovery
就這樣成功進行了升級,what?完成了? 這么簡單,原來這才是跳坑的開始,看到生面測試成功了之后,迫不及待的在自己的程序中將路徑填寫成 /data/update.zip ,開始了升級操作,結(jié)果失敗了.開始檢查原因
是不是AndroidManifest.xml中沒有添加 如下權(quán)限
android:sharedUserId="android.uid.system"
查看添加了,是不是沒有對自己的apk進行系統(tǒng)簽名?發(fā)現(xiàn)也簽名了(如何給APK系統(tǒng)簽名),沒有放到system/app/文件夾下?也放了,.那原因出在哪里那?不想了,先去看看log
Android機器中 /cache/recovery/的目錄結(jié)構(gòu)如下:
cache/
└── recovery
├── last_install
├── last_kmsg #系統(tǒng)升級的時候的全log. 如果有l(wèi)ast_kmsg.1
#那last_kmsg 永遠是最后一次升級的log,所以每
#次只查看last_kmsg就行
├── last_locale
└── last_log #升級過程中的簡log,能看到為何升級失敗
在log中查看到的是 --update_package=@/cache/recovery/block.map 看到這個結(jié)果的時候,第一感覺就是apk沒有安裝成功,于是加了log 發(fā)現(xiàn)加的log 成功打印,嚇了一跳,自己撞見鬼了?
抱著程序員不明原因不放棄的心態(tài),看了一下RecoverySystem.java 中的 installPackage 方法,原來是 installpackage在代碼中做了限制, 要是設(shè)置的路徑是以 /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發(fā)現(xiàn) --update_package=data/update.zip . 這么看來已經(jīng)能寫到重啟文件中了.
查看log提示是 --update_package=data/update.zip Permission Denied 看來是datarecover沒有權(quán)限讀取/data目錄啊
快速驗證 是不是selinux問題 可以修改 BoardConfig.mk 添加如下代碼 編譯boot文件測試,如下代碼是禁用selinux代碼
BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive
2、具體的recovery修改
bootable/recovery/Android.mk 修改 文件
bootable/recovery/recovery.cpp 修改 文件
bootable/recovery/usb.h 新增文件
bootable/usb.cpp 新增文件
bootable/recovery/Android.mk
diff --git a/recovery/Android.mk b/recovery/Android.mk
--- a/recovery/Android.mk
+++ b/recovery/Android.mk
@@ -41,6 +41,7 @@
verifier.cpp \
wear_ui.cpp \
wear_touch.cpp \
+ usb.cpp
LOCAL_MODULE := recovery
bootable/recovery/recovery.cpp
diff --git a/recovery/recovery.cpp b/recovery/recovery.cpp
--- a/recovery/recovery.cpp
+++ b/recovery/recovery.cpp
@@ -68,6 +68,7 @@
#include "ui.h"
#include "unique_fd.h"
#include "screen_ui.h"
+#include "usb.h"
#include <stdio.h>
#include <memory.h>
@@ -1466,59 +1467,7 @@
}
}
-static bool is_battery_ok() {
- struct healthd_config healthd_config = {
- .batteryStatusPath = android::String8(android::String8::kEmptyString),
- .batteryHealthPath = android::String8(android::String8::kEmptyString),
- .batteryPresentPath = android::String8(android::String8::kEmptyString),
- .batteryCapacityPath = android::String8(android::String8::kEmptyString),
- .batteryVoltagePath = android::String8(android::String8::kEmptyString),
- .batteryTemperaturePath = android::String8(android::String8::kEmptyString),
- .batteryTechnologyPath = android::String8(android::String8::kEmptyString),
- .batteryCurrentNowPath = android::String8(android::String8::kEmptyString),
- .batteryCurrentAvgPath = android::String8(android::String8::kEmptyString),
- .batteryChargeCounterPath = android::String8(android::String8::kEmptyString),
- .batteryFullChargePath = android::String8(android::String8::kEmptyString),
- .batteryCycleCountPath = android::String8(android::String8::kEmptyString),
- .energyCounter = NULL,
- .boot_min_cap = 0,
- .screen_on = NULL
- };
- healthd_board_init(&healthd_config);
- android::BatteryMonitor monitor;
- monitor.init(&healthd_config);
-
- int wait_second = 0;
- while (true) {
- int charge_status = monitor.getChargeStatus();
- // Treat unknown status as charged.
- bool charged = (charge_status != android::BATTERY_STATUS_DISCHARGING &&
- charge_status != android::BATTERY_STATUS_NOT_CHARGING);
- android::BatteryProperty capacity;
- android::status_t status = monitor.getProperty(android::BATTERY_PROP_CAPACITY, &capacity);
- ui_print("charge_status %d, charged %d, status %d, capacity %lld\n", charge_status,
- charged, status, capacity.valueInt64);
- // At startup, the battery drivers in devices like N5X/N6P take some time to load
- // the battery profile. Before the load finishes, it reports value 50 as a fake
- // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected
- // to finish loading the battery profile earlier than 10 seconds after kernel startup.
- if (status == 0 && capacity.valueInt64 == 50) {
- if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) {
- sleep(1);
- wait_second++;
- continue;
- }
- }
- // If we can't read battery percentage, it may be a device without battery. In this
- // situation, use 100 as a fake battery percentage.
- if (status != 0) {
- capacity.valueInt64 = 100;
- }
- return (charged && capacity.valueInt64 >= BATTERY_WITH_CHARGER_OK_PERCENTAGE) ||
- (!charged && capacity.valueInt64 >= BATTERY_OK_PERCENTAGE);
- }
-}
static void set_retry_bootloader_message(int retry_count, int argc, char** argv) {
bootloader_message boot = {};
@@ -1623,56 +1572,6 @@
return __android_log_pmsg_file_write(logId, prio, name.c_str(), buf, len);
}
-
-static void setLine(int fd, int flags, int speed)
-{
- struct termios t;
- tcgetattr(fd, &t);
- t.c_cflag = flags | CREAD | HUPCL | CLOCAL;
- t.c_iflag = IGNBRK | IGNPAR;
- t.c_oflag = 0;
- t.c_lflag = 0;
- t.c_cc[VMIN] = 1;
- t.c_cc[VTIME] = 0;
- cfsetispeed(&t, speed);
- cfsetospeed(&t, speed);
- tcsetattr(fd, TCSANOW, &t);
-}
-
-int tty_fd = -1;
-void *heartbeatfun(void __unused *argv)
-{
- char boot[] = {0x80};
- char heartbeat[] = {0x81};
- char run[] = {0x95,0x01};
-
- write(tty_fd, boot, sizeof(boot));
- //usleep(100);
-// write(tty_fd, heartbeat, sizeof(heartbeat));
-// usleep(100);
-// write(tty_fd, run, sizeof(run));
- while(1)
- {
- write(tty_fd, heartbeat, sizeof(heartbeat));
- usleep(500000);
- }
-
- return NULL;
-}
-
-void start_heartbeat_thread(void)
-{
- pthread_t timerid;
- int err = 0;
-
- err = pthread_create(&timerid, NULL, heartbeatfun, NULL);
- if (err) {
- printf("can't creat heartbeatthread\n");
- }
-}
-
-
-
int main(int argc, char **argv) {
// Take last pmsg contents and rewrite it to the current pmsg session.
static const char filter[] = "recovery/";
@@ -1700,30 +1599,6 @@
time_t start = time(NULL);
- tty_fd = open("/dev/ttyHSL1", O_RDWR, 0);
- while(tty_fd < 0) {
-
- fprintf(stdout,"open error=%d %s\n", errno, strerror(errno));
- printf("deanji open ttyHSL1 failed \n");
- tty_fd = open("/dev/ttyHSL1", O_RDWR, 0);
- usleep(5000000);
- }
- fprintf(stdout, "update_package = 1 tty_fd=%d ...\n",tty_fd);
-
- if(tty_fd>0){
- int ldisc = N_BHDLC;
- int rc;
- setLine(tty_fd, CS8, B115200);
- rc = ioctl(tty_fd, TIOCSETD, &ldisc);
- if(rc < 0) {
- fprintf(stdout,"#####set line discipline error=%d %s\n",
- errno, strerror(errno));
- return rc;
- }
- fcntl(tty_fd, F_SETFL, fcntl(tty_fd, F_GETFL) & ~O_NONBLOCK);
- start_heartbeat_thread();
- }
-
// redirect_stdio should be called only in non-sideload mode. Otherwise
// we may have two logger instances with different timestamps.
redirect_stdio(TEMPORARY_LOG_FILE);
@@ -1852,24 +1727,33 @@
else
printf("modified_path allocation failed\n");
}
- if (!strncmp("/sdcard", update_package, 7)) {
- //If this is a UFS device lets mount the sdcard ourselves.Depending
- //on if the device is UFS or EMMC based the path to the sdcard
- //device changes so we cannot rely on the block dev path from
- //recovery.fstab
- if (is_ufs_dev()) {
- if(do_sdcard_mount_for_ufs() != 0) {
- status = INSTALL_ERROR;
- goto error;
- }
- if (ensure_path_mounted("/cache") != 0 || ensure_path_mounted("/tmp") != 0) {
- ui->Print("\nFailed to mount tmp/cache partition\n");
- status = INSTALL_ERROR;
- goto error;
- }
- mount_required = false;
- } else {
- ui->Print("Update via sdcard on EMMC dev. Using path from fstab\n");
+ else if (strncmp(update_package, "usb:", 4) == 0) {
+ printf("recovery from udisk\n");
+ char *absolutePath = (char*)malloc(PATH_MAX);
+ memset(absolutePath, 0, PATH_MAX);
+
+ char ota_name[128] = {'\0'};
+ strcpy(ota_name, update_package+4);
+ printf("ota_name=%s\n",ota_name);
+ // wait 3s , make sure usb mounted
+ sleep(3);
+ if (!search_file_in_usb(ota_name, absolutePath)) {
+ update_package = absolutePath;
+ printf("find update.zip in usb success!\n");
+ } else {
+ printf("find update.zip in usb failed!\n");
+ }
+ }
+ else if (!strncmp("/data", update_package, 5)) {
+ if (ensure_path_mounted(DATA_ROOT) != 0) {
+ ui->Print("\n-- Couldn't mount %s.\n", DATA_ROOT);
+ status = INSTALL_ERROR;
+ goto error;
+ }
+ if (ensure_path_mounted("/cache") != 0 || ensure_path_mounted("/tmp") != 0) {
+ ui->Print("\nFailed to mount tmp/cache partition\n");
+ status = INSTALL_ERROR;
+ goto error;
}
}
}
@@ -1884,14 +1768,7 @@
// to log the update attempt since update_package is non-NULL.
modified_flash = true;
- if (!is_battery_ok()) {
- ui->Print("battery capacity is not enough for installing package, needed is %d%%\n",
- BATTERY_OK_PERCENTAGE);
- // Log the error code to last_install when installation skips due to
- // low battery.
- log_failure_code(kLowBattery, update_package);
- status = INSTALL_SKIPPED;
- } else if (bootreason_in_blacklist()) {
+ if (bootreason_in_blacklist()) {
// Skip update-on-reboot when bootreason is kernel_panic or similar
ui->Print("bootreason is in the blacklist; skip OTA installation\n");
log_failure_code(kBootreasonInBlacklist, update_package);
@@ -1933,42 +1810,21 @@
}
}
}
- } else if (should_wipe_data) {
+ } if (should_wipe_data) {
if (!wipe_data(false, device)) {
status = INSTALL_ERROR;
}
- } else if (should_wipe_cache) {
+ } if (should_wipe_cache) {
if (!wipe_cache(false, device)) {
status = INSTALL_ERROR;
}
- } else if (should_wipe_ab) {
+ } if (should_wipe_ab) {
if (!wipe_ab_device(wipe_package_size)) {
status = INSTALL_ERROR;
}
- } else if (sideload) {
- // 'adb reboot sideload' acts the same as user presses key combinations
- // to enter the sideload mode. When 'sideload-auto-reboot' is used, text
- // display will NOT be turned on by default. And it will reboot after
- // sideload finishes even if there are errors. Unless one turns on the
- // text display during the installation. This is to enable automated
- // testing.
- if (!sideload_auto_reboot) {
- ui->ShowText(true);
- }
- status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
- if (status == INSTALL_SUCCESS) {
- ota_completed = true;
- }
- if (status == INSTALL_SUCCESS && should_wipe_cache) {
- if (!wipe_cache(false, device)) {
- status = INSTALL_ERROR;
- }
- }
- ui->Print("\nInstall from ADB complete (status: %d).\n", status);
- if (sideload_auto_reboot) {
- ui->Print("Rebooting automatically.\n");
- }
- } else if (!just_exit) {
+ }
+
+ if (!just_exit) {
status = INSTALL_NONE; // No command specified
ui->SetBackground(RecoveryUI::NO_COMMAND);
@@ -1984,7 +1840,8 @@
copy_logs();
ui->SetBackground(RecoveryUI::ERROR);
}
-
+
+ /*
Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
if ((status != INSTALL_SUCCESS && status != INSTALL_SKIPPED && !sideload_auto_reboot) ||
ui->IsTextVisible()) {
@@ -1993,10 +1850,16 @@
after = temp;
}
}
+ */
// Save logs and clean up before rebooting or shutting down.
finish_recovery(send_intent);
+ // no matter what result reboot
+ sleep(1);
+ property_set(ANDROID_RB_PROPERTY, "reboot,");
+
+ /*
switch (after) {
case Device::SHUTDOWN:
ui->Print("Shutting down...\n");
@@ -2016,6 +1879,7 @@
while (true) {
pause();
}
+ */
// Should be unreachable.
return EXIT_SUCCESS;
}
bootable/recovery/usb.h
diff --git a/recovery/usb.h b/recovery/usb.h
--- /dev/null
+++ b/recovery/usb.h
@@ -0,0 +1,21 @@
+#ifndef USB_H_
+#define USB_H_
+
+#include "common.h"
+
+//return 0 if exists
+int check_file_exists(const char *path);
+
+//return 0 if the usb mounted success
+int ensure_dev_mounted(const char *devPath, const char *mountedPoint);
+
+//return 0 if the usb unmounted success
+int ensure_dev_unmounted(const char *mountedPoint);
+
+//search file in usbs,return 0 if success, then save its absolute path arg2.
+int search_file_in_usb(const char *file, char *absolutePath);
+
+int in_usb_device(const char *file);
+
+#endif // USB_H_
+
bootable/usb.cpp
diff --git a/recovery/usb.cpp b/recovery/usb.cpp
--- /dev/null
+++ b/recovery/usb.cpp
@@ -0,0 +1,165 @@
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <string>
+#include <android/log.h>
+#include <log/logger.h>
+#include <private/android_logger.h>
+
+#include "usb.h"
+
+#define MAX_DISK 4
+#define MAX_PARTITION 8
+#define PATH_MAX 256
+#define TIME_OUT 6000000
+
+static const char *USB_ROOT = "/usb/";
+struct timeval tpstart,tpend;
+float timeuse = 0;
+
+#define MOUNT_EXFAT "/system/bin/mount.exfat"
+
+void startTiming(){
+ gettimeofday(&tpstart, NULL);
+}
+
+void endTimming(){
+ gettimeofday(&tpend, NULL);
+ timeuse = 1000000 * (tpend.tv_sec - tpstart.tv_sec) +
+ (tpend.tv_usec - tpstart.tv_usec);
+ LOGD("spend Time %f\n", timeuse);
+}
+
+int check_file_exists(const char * path){
+ int ret = -1;
+ if(path == NULL){
+ return -1;
+ }
+ ret = access(path, F_OK);
+ return ret;
+}
+
+int ensure_dev_mounted(const char * devPath,const char * mountedPoint){
+ int ret;
+ if(devPath == NULL || mountedPoint == NULL){
+ return -1;
+ }
+ mkdir(mountedPoint, 0755); //in case it doesn't already exist
+ startTiming();
+ ret = mount(devPath, mountedPoint, "vfat",
+ MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
+ endTimming();
+ if(ret == 0){
+ LOGD("mount %s with fs 'vfat' success\n", devPath);
+ return 0;
+ }else{
+ startTiming();
+ ret = mount(devPath, mountedPoint, "ntfs",
+ MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
+ endTimming();
+ if(ret == 0){
+ LOGD("mount %s with fs 'ntfs' success\n", devPath);
+ return 0;
+ }else{
+ startTiming();
+ ret = mount(devPath, mountedPoint, "ext4",
+ MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
+ endTimming();
+ if(ret == 0){
+ LOGD("mount %s with fs 'ext4' success\n", devPath);
+ return 0;
+ }
+ }
+ LOGD("failed to mount %s (%s)\n", devPath, strerror(errno));
+ return -1;
+ }
+}
+
+int search_file_in_dev(const char * file, char *absolutePath,
+ const char *devPath, const char *devName){
+ if(!check_file_exists(devPath)){
+ LOGD("dev %s exists\n", devPath);
+ char mountedPoint[32];
+ sprintf(mountedPoint, "%s%s", USB_ROOT, devName);
+ //if the dev exists, try to mount it
+ if(!ensure_dev_mounted(devPath, mountedPoint)){
+ LOGD("dev %s mounted in %s\n", devPath, mountedPoint);
+ char desFile[PATH_MAX];
+ sprintf(desFile, "%s/%s", mountedPoint, file);
+ //if mount success.search des file in it
+ if(!check_file_exists(desFile)){
+ //if find the file,return its absolute path
+ LOGD("file %s exist\n", desFile);
+ sprintf(absolutePath, "%s", desFile);
+ return 0;
+ }else{
+ ensure_dev_unmounted(mountedPoint);
+ }
+ }
+ }
+ return -1;
+}
+
+int search_file_in_usb(const char * file,char * absolutePath){
+ timeval now;
+ gettimeofday(&now, NULL);
+ int i = 0;
+ int j = 0;
+ timeval workTime;
+ long spends;
+ mkdir(USB_ROOT, 0755); //in case dir USB_ROOT doesn't already exist
+ //do main work here
+ do{
+ LOGD("begin....\n");
+ for(i = 0; i < MAX_DISK; i++){
+ char devDisk[32];
+ char devPartition[32];
+ char devName[8];
+ char parName[8];
+ sprintf(devName, "sd%c", 'a' + i);
+ sprintf(devDisk, "/dev/block/%s", devName);
+ LOGD("check disk %s\n", devDisk);
+ if(check_file_exists(devDisk)){
+ LOGD("dev %s does not exists (%s),waiting ...\n", devDisk, strerror(errno));
+ continue;
+ }
+ for(j = 1; j <= MAX_PARTITION; j++){
+ sprintf(parName, "%s%d", devName, j);
+ sprintf(devPartition, "%s%d" ,devDisk, j);
+ if(!search_file_in_dev(file, absolutePath, devPartition, parName)){
+ return 0;
+ }
+ }
+ if(j > MAX_PARTITION){
+ if(!search_file_in_dev(file, absolutePath, devDisk, devName)){
+ return 0;
+ }
+ }
+ }
+ usleep(500000);
+ gettimeofday(&workTime, NULL);
+ spends = (workTime.tv_sec - now.tv_sec)*1000000 + (workTime.tv_usec - now.tv_usec);
+ }while(spends < TIME_OUT);
+ LOGD("Time to search %s is %ld\n", file, spends);
+ return -1;
+}
+
+
+
+int ensure_dev_unmounted(const char * mountedPoint){
+ int ret = umount(mountedPoint);
+ return ret;
+}
+
+int in_usb_device(const char * file){
+ int len = strlen(USB_ROOT);
+ if (strncmp(file, USB_ROOT, len) == 0){
+ return 0;
+ }
+ return -1;
+}
3.selinux權(quán)限問題
經(jīng)過一番調(diào)查 發(fā)現(xiàn) data的 type屬于 system_data_file類型的
在file_contexts中可以查看到
/data(/.*)? u:object_r:system_data_file:s0
在recovery.te中發(fā)現(xiàn)了如下代碼
neverallow recovery data_file_type:file { no_w_file_perms no_x_file_perms };
neverallow recovery data_file_type:dir no_w_dir_perms;
allow recovery rootfs:dir rw_dir_perms;
也就是說明 recover.te何時都不能讀取和操作/data/目錄,那怎么辦?當然是有辦法的,我們可以新建一個文件夾 和自定義文件類型啊
首先是開機的時候要有一個文件夾
我們可以在init.rc 或者 init.qcom.rc中創(chuàng)建一個文件夾
#Create directory for download
mkdir /data/download 0771
chown system system /data/download
創(chuàng)建完成之后需要app可以讀取因此 需要在 system_app.te 中增加對應(yīng)的權(quán)限
# allow system_app to access /data/download
allow 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 };
這樣的話 我們已經(jīng)創(chuàng)建了 文件夾,并且system app級別的應(yīng)用可以讀取.
那既然存儲好了之后是不是下面需要 recovery可以讀取到文件那?系統(tǒng)不允許讀取 recovery.te /data/路徑.那我們就自己創(chuàng)建一個文件類型,在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的權(quán)限
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 查看 最后一樣都會輸出失敗的權(quán)限原因
比如 :
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
這種權(quán)限只能報錯什么權(quán)限添加什么權(quán)限,我們可以遵循這個方法,從頭開始尋找關(guān)鍵對象,然后調(diào)整一下順序,生成一條語句,最后將該語句填寫到.te中即可。
denied { open } scontext=u:r:recovery:s0 tcontext=u:object_r:download_data_file:s0 tclass=file
A B C D
上面的標記按照 allow -> B-> C -> D -> A ->[對應(yīng)權(quán)限] 順序擺放好即可
例如:
allow recovery download_data_file:tclass open;
如果升級沒有錯誤,APP操作文件夾中的文件無效時,可以在操作的時候執(zhí)行如下命令,如果報了相關(guān)權(quán)限問題,加上即可,
adb shell dmesg | grep denied
相關(guān)閱讀:
車機怎么快速打開APP 車機側(cè)邊欄工具下載
酷我音樂車機版怎么下載 完整使用酷我音樂所有功能版本
電視盒子刷機工具有哪些 16款電視盒子刷機工具免費下載
|