14

I have been asked to write a small simple app for an Android-based product. The device comes with two Android system images with different features. The app I'm writing is just a proof of concept where when you click on a button, it uses the Recovery System to replace the current OS with one of the images.

The device is rooted and the application runs as a system app.

I use

RecoverySystem.installPackage(context, packageFile);

(see here for reference) to replace the OS with one of the images. This should reboot the system and initialize the recovery system to install the image.

The problem that I have is that this call fails because the RecoverySystem.installPackage method can't seem to access the /cache/recovery/command file. I guess it tries to write some commands for the recovery system to be executed on reboot, but fails. Here is the exception I am getting:

02-27 16:44:39.463: E/BroadSign Resolution Switcher(4439): java.io.FileNotFoundException: /cache/recovery/command: open failed: EACCES (Permission denied)
02-27 16:44:39.463: E/BroadSign Resolution Switcher(4439):  at libcore.io.IoBridge.open(IoBridge.java:416)
02-27 16:44:39.463: E/BroadSign Resolution Switcher(4439):  at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
02-27 16:44:39.463: E/BroadSign Resolution Switcher(4439):  at java.io.FileOutputStream.<init>(FileOutputStream.java:73)
02-27 16:44:39.463: E/BroadSign Resolution Switcher(4439):  at java.io.FileWriter.<init>(FileWriter.java:42)
02-27 16:44:39.463: E/BroadSign Resolution Switcher(4439):  at android.os.RecoverySystem.bootCommand(RecoverySystem.java:381)
02-27 16:44:39.463: E/BroadSign Resolution Switcher(4439):  at android.os.RecoverySystem.installPackage(RecoverySystem.java:330)

So, I am assuming that I don't have the proper permission to access this file. Though here are the permission that I have set in my manifest:

<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
<uses-permission android:name="android.permission.DELETE_CACHE_FILES" />

I know I need the REBOOT permission for the RecoverySystem. However, I don't know if the other two are relevant. I don't even know if I need some other permission to create/write to files in the /cache partition.

Does anybody know what I could be missing?


UPDATE:

Well, it looks like I figured it out. The permissions were right after all. Everything was right.

When I was setting up the app as a system app by moving the apk file to /system/app I hadn't put all the permissions in yet. When I ran the latest code on my device, it installed it in /data/app and recognized it as an update to the system app. Because of that, only the permissions of the original system app were recognized.

After I re-set up my latest code to run from /system/app directly, it worked.

Now the only issue I have is that when it reboots it doesn't install the image and I end up with a dead green robot with an exclamation mark. I'll keep investigating.

I hope this post helps anyone with the same issue. I'll be happy to answer any questions as well.

Stephane
  • 141
  • 1
  • 1
  • 4
  • I did the same and I am able to run FOTA upgrade. But my problem is that, the app which I made has a label as platform_app, and the app used by google for upgrade falls in untrusted_app. Is there any way to make my app as untrusted_app with above cache permissions intact, because these permissions are given only to system apps I guess – Pankaj Kushwaha Dec 02 '14 at 07:46
  • @PankajKushwaha Hi pankaj i am also trying to implement FOTA in my aap programitically . The [link](http://shriduttkothari.blogspot.in/2013/08/fota-in-android-mechanism.html) i found some sol but i cannot understand how to proceed further with this. – user3207655 Apr 16 '15 at 06:06
  • I will tell you the basic procedure which I have followed- 1) run make otapackage. Lets name it update.zip 2) copy the zip file in /cache/recovery/update.zip 3) write the following command "--update_package=/cache/update.zip" in /cache/recovery/command 4) Reboot device in recovery mode. 5) Device will automatically get upgraded. You can find sample code in /frameworks/base/core/java/android/os/RecoverySystem.java The app which I use is a sytem app (signed with platform key) – Pankaj Kushwaha Apr 16 '15 at 12:01

5 Answers5

2

Before when my app was installed in /system/app I was getting below error:

07-20 10:52:46.512      933-951/? W/RecoverySystem﹕ !!! REBOOTING TO INSTALL /storage/emulated/legacy/Download/Update.zip !!!
07-20 10:52:46.512      933-951/? W/System.err﹕ java.io.FileNotFoundException: /cache/recovery/command: open failed: EACCES (Permission denied)
07-20 10:52:46.512      933-951/? W/System.err﹕ at libcore.io.IoBridge.open(IoBridge.java:409)
07-20 10:52:46.512      933-951/? W/System.err﹕ at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
07-20 10:52:46.512      933-951/? W/System.err﹕ at java.io.FileOutputStream.<init>(FileOutputStream.java:73)
07-20 10:52:46.512      933-951/? W/System.err﹕ at java.io.FileWriter.<init>(FileWriter.java:42)
07-20 10:52:46.512      933-951/? W/System.err﹕ at android.os.RecoverySystem.bootCommand(RecoverySystem.java:389)
07-20 10:52:46.522      933-951/? W/System.err﹕ at android.os.RecoverySystem.installPackage(RecoverySystem.java:337)

I had tried all permissions that were required but I couldn't proceed.

So then since I was using API above 4.2 I tried to put my app into /system/priv-app and it worked for me.

Nathan Tuggy
  • 2,237
  • 27
  • 30
  • 38
Hiten Bahri
  • 259
  • 3
  • 9
1

System apps (apps with shared user ID set to android.uid.system) cannot install system updates on Android 5 and newer - it's forbidden by a SELinux policy. To be specific writing to /cache is forbidden for system apps. In other words:

  • /cache is owned by system user so your app running under system UID can write to it. But only if SELinux is disabled/permissive.
  • If you have android.permission.ACCESS_CACHE_FILESYSTEM platform signature permission, you can write to /cache.

You'll need to remove the shared user ID. You still have to sign the app with platform signature and ensure you have the following permissions:

  • android.permission.REBOOT
  • android.permission.ACCESS_CACHE_FILESYSTEM - to write to /cache
  • android.permission.RECOVERY - required on API 21 to reboot to recovery

This will work on Kitkat and Lollipop+ alike.

Eugen Pechanec
  • 37,669
  • 7
  • 103
  • 124
  • I have included all three mentioned permissions but still can't write to `/cache`. Does SELinux always need to be permissive to be able to write to `cache` or those 3 permissions suffice? – mahdi Nov 01 '20 at 14:05
  • If it's a SELinux policy issue you should see a [message](https://source.android.com/security/selinux/validate#reading_denials) in logcat. Is there enough space in your /cache? I've seen devices with minimal cache partition and you could only use /data for updates. – Eugen Pechanec Nov 01 '20 at 16:21
  • I believe there is enough space in my `/cache` since using `setenforce 0` solved the problem but I don't want to make SELinux permissive. I was asking if it's necessary to make SELinux permissive or there is another way to make it work without changing SELinux. – mahdi Nov 02 '20 at 10:04
  • @mahdi did you ever figure out a way to make this work? I believe I have similar issue with an Android 9 system app update issue and I really don't want to disable SELinux entirely (and figuring out custom policies has proven very difficult....) – Garry McKee Dec 13 '21 at 15:20
  • @GarryMcKee I wasn't able to fix the problem with system apps. As a work around, as mentioned in this answer, I removed `android.uid.system` from manifest. – mahdi Dec 14 '21 at 11:27
0

I have problem the same with you when create custom OtaUpdate app in android 5.0.2 and i have resolved it. I will share with you with 2 steps below:

  1. Add android:sharedUserId="com.google.uid.shared" in AndroidManifest.xml
  2. Push your apk to system/app or system/priv-app

For android 4.1.2 :

  1. Add android:sharedUserId="android.uid.system" in AndroidManifest.xml
  2. Push your apk to system/app
Tao Nhu
  • 1,980
  • 1
  • 15
  • 6
  • > Add android:sharedUserId="com.google.uid.shared" in AndroidManifest.xml This will create issues if you have Google apps installed. Just make sure that the app is in system/priv-app and you should be fine. – Aldrian Sep 06 '16 at 18:10
0

For Android 5.1.0

  1. Add android:sharedUserId="android.uid.system" in AndroidManifest.xml
  2. Push your apk to system/priv-app
  3. and then

    adb root
    adb shell setenforce 0
    
frogatto
  • 28,539
  • 11
  • 83
  • 129
Trinea
  • 649
  • 8
  • 10
0

I met the same problem in android 8.
If you add android:sharedUserId="android.uid.system" in AndroidManifest.xml, it should work.

Artemis
  • 2,553
  • 7
  • 21
  • 36