Search code examples
androidroot

Should a data application signed with the vendor key be able to run 'su'


Background: I am developing for a signage device which is to be remotely operated. I need an application that can

  • Fetch and install new packages
  • Reboot the device (for troubleshooting)

I have an unrooted Android device. I also have files which I am told are the platform keys.

I have developed an application which attempts to kick off the su process.

Process p = Runtime.getRuntime().exec("su");

Before I signed the application with the platform keys this was throwing an IOException, with the message being Permission Denied.

I signed the application with these platform keys, and I am still getting the Permission Denied exception.

Here are three contradictory statements. Which one of these statements (if any) is correct?

Statment 1: This should work. The application, even though is is stored in /data/app, should be able to run su. Either I have the wrong keys, or there's some other entry I need to add to the manifest to get it to work.

Statement 2: This shouldn't work. Even though it is signed with the platform key, the application is in /data/app, so it's a data application, not a system application. Data applications cannot run su on an unrooted devices. If this application was installed into /system/app, then it would be able to run su. (And I can't get it into /system/app because it's unrooted, so I'm stuck).

Statment 3: This will never work. If the device is not rooted, then NOTHING can run su, even if it is a signed system app.


Solution

  • Android shouldn't even have a su binary if you didn't flash some sort of root method to the device, such as Magisk or SuperSU.

    Even if it does have a su binary, I wouldn't expect it to work, for one of two reasons. Assuming that your device comes with a preinstalled su binary, who's managing it? If it's unmanaged, it should just deny all requests. If you flash a root method, then it's up to that manager to decide if your app gets access to su, regardless of whether you have signature-level permissions or nor (the root manager uses a different signature, after all).

    And why would you even need access to su as a signature app? You have total access to the device anyway. If you need to run a command, you should have no problems no matter what you run, as long as it's done from your platform-signed package. But since you have full access, the native APIs should let you do everything you need.

    As for the IOException returned when you try to execute su in a Process, that's just a weird Android quirk. If there's no su binary installed, it'll sometimes return command not found and other times permission denied, depending on the device.

    The point I think I'm making is that, unless your app is the root manager, you could be part of the system_server and still have the same access to su as everyone else. For which statement I agree with, I think #3, although I don't fully agree with it, because chances are su just doesn't exist, or it's a dud binary.

    I've explained why #1 shouldn't be true, but #2 is just incorrect. If you look at the platform manifest, every permission that requires a privileged app can also be granted to signature apps. So even if you did move your app to /system/priv-app/ (/system/app/ won't make it privileged), it wouldn't make a difference. Basically, if your app is signed by the platform signature, it doesn't matter where it's installed.

    EDIT:

    You can easily reboot by just running reboot as a command, since you have signature-level access to the system, but it's a little more elegant to use the proper API for this. If you use the API, you get the shutdown animation, but you also let the system shut down gracefully, stopping services and sending the ACTION_REBOOT broadcast to any apps that might be listening for that.

    To use the API, first add the following permission to your AndroidManifest:

    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
    

    Now, where you need to call the reboot action, use the following code:

    IStatusBarService bar = IStatusBarService.Stub.asInterface(ServiceManager.getService(Context.STATUS_BAR_SERVICE));
    bar.reboot(false); //using true here will reboot to Safe Mode
    

    This method is a hidden method, so if you're using Android Studio to edit and compile, it'll error out. You can use reflection, or use Android Hidden API to access it directly.

    This is how System UI implements it in the power menu: https://github.com/aosp-mirror/platform_frameworks_base/blob/master/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java

    This is the class that implements IStatusBar service: https://github.com/aosp-mirror/platform_frameworks_base/blob/master/services/core/java/com/android/server/statusbar/StatusBarManagerService.java#L969