Android Question Installing .apks from another .apk on Android 7+

biometrics

Active Member
Licensed User
Longtime User
Our product is made up of several .apks. To make life easier for our clients we package them all into a single Setup.apk. Setup.apk in turn will install each .apk. (Note we don't distribute via the Play Store).

Our .apks, including Setup.apk, require special permissions. To get these permissions we need the platform certificate for each device we support, we get the platform certificate from the manufacturers. (They are media players, not phones).

By adding the required permission to the manifest, compiling to release mode, and signing the .apk with the platform certificate we acquire these permissions. Examples of these permissions are:

INSTALL_PACKAGES
ACCESS_SURFACE_FLINGER
READ_FRAME_BUFFER
etc.

Until now our target SDK in the manifest was 14 as we have many clients still running Android 4 devices.

Using INSTALL_PACKAGES Setup.apk can shell to the command line and use Package Manager to install each .apk in turn. This has been working perfectly for years and years. Until Android 7. Now it's not working. So I need help with this, more detailed info below:

The supplier of our latest device says it's not working because Android 7 requires Dynamic Permissions. So I have tried implementing runtime permissions, which I've done with other Play Store apps, as below:

Set the target SDK in the manifest to 26:

B4X:
android:targetSdkVersion="26"

I have the following permissions requested in the manifest:

B4X:
<uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

I select the RuntimePermissions library in Process_Globals:

B4X:
Dim libRuntimePermissions As RuntimePermissions

In Activity_Create I request run time permissions:

B4X:
'Permissions
Dim iPermissionCounter As Int
  
'INSTALL_PACKAGES
iPermissionCounter = 0
Do While True
   libRuntimePermissions.CheckAndRequest("android.permission.INSTALL_PACKAGES")
   Wait For Activity_PermissionResult (Permission As String, bResult As Boolean)
   If bResult Then
       Exit
   Else
       If iPermissionCounter >= 2 Then
           Msgbox("You have repeatedly denied permission for the app to install packages." & CRLF & CRLF & "The app will not work without this permission.", "Setup")
           ExitApplication
       End If
       Msgbox("This app requires permission to install packages. Please allow the app to install packages.", "Setup")
       iPermissionCounter = iPermissionCounter + 1
   End If
Loop  

'WRITE_EXTERNAL_STORAGE
iPermissionCounter = 0
Do While True
   libRuntimePermissions.CheckAndRequest("android.permission.WRITE_EXTERNAL_STORAGE")
   Wait For Activity_PermissionResult (Permission As String, bResult As Boolean)
   If bResult Then
       Exit
   Else
       If iPermissionCounter >= 2 Then
           Msgbox("You have repeatedly denied permission for the app to install packages." & CRLF & CRLF & "The app will not work without this permission.", "Setup")
           ExitApplication
       End If
       Msgbox("This app requires permission to install packages. Please allow the app to install packages.", "Setup")
       iPermissionCounter = iPermissionCounter + 1
   End If
Loop

I then compile all the .apks and sign each of them with the platform certificate I was supplied, autorun.apk for example:

B4X:
"C:\Program Files\Java\jdk1.8.0_211\bin\java" -jar "C:\Root\Player\Android\Signing\signapk.jar" "C:\Root\Player\Android\Signing\platform.x509.pem" "C:\Root\Player\Android\Signing\platform.pk8" Autorun.apk Autorun-signed.apk

We then copy the .apks we want installed via Setup.apk to it's Files folder for inclusion in Setup.apk, compile it and sign it.

We then copy the final Setup.apk to a flash drive and install it on the new device.

Setup.apk installs and runs. It copies the embedded .apks to a newly created /mnt/sdcard/Player/Setup directory. It then attempts to install each via a command line shell using Package Manager like autorun for example:

B4X:
pm install -r /mnt/sdcard/Player/Setup/autorun.apk

The shell return string is then logged to a log file.

With the above manifest permission requests and runtime permission requests and the target SDK set to 26, our .apks do not install. Package Manager returns the following error:

B4X:
Error: java.lang.SecurityException: Permission Denial: runInstallCreate from pm command asks to run as user -1 but is calling from user 0; this requires android.permission.INTERACT_ACROSS_USERS_FULL

I then added android.permission.INTERACT_ACROSS_USERS_FULL to both the manifest and runtime permissions. Repeating the above process, our .apks still don't install but the Package Manager error changes to:

B4X:
Error: java.lang.NullPointerException

I note that I only got one runtime permission dialog when Setup.apk started and that was for WRITE_EXTERNAL_STORAGE.

At this stage I am stumped. This worked perfectly for years. Any ideas how to solve this?
 

biometrics

Active Member
Licensed User
Longtime User
This code works on all Android devices: https://www.b4x.com/android/forum/threads/version-safe-apk-installation.87667/

Try it, and install a new app with a new package name.
Thanks, I found that yesterday and wanted to try it before giving feedback here.

This does work but it requires interaction from the user which means we can't use it for unattended remote updates (we're not in the Play Store). These devices are often rooted, this one is, I'm going to see if I can make it work using root...
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
This does work but it requires interaction from the user
The interaction needs to be done ONCE; you can run it attended, set the permission.
Further updates do not require the user to interact.
 
Upvote 0

biometrics

Active Member
Licensed User
Longtime User
The interaction needs to be done ONCE; you can run it attended, set the permission.
Further updates do not require the user to interact.
Our product has four apks, so including Setup.apk there will be five prompts. That's not going to work.

But what you say is interesting, once an apk is installed this way there are no prompts on future installs?

Currently using root on the device seems the most promising.
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
once an apk is installed this way there are no prompts on future installs?
there are no prompts for any permission needed.

But i must say that the user needs to click on the "INSTALL" button. So userintervention is needed for any app installed.
Sorry.

I guess you can get it working on a rooted device only. But i can´t help any further here. I do not have any rooted Devices.
 
Upvote 0

biometrics

Active Member
Licensed User
Longtime User
Yay, got it working with the help of the device manufacturer. I share it here in case someone else ends up in this thread. I did not make use of root.

There are no user prompts other than the initial Setup.apk. Completely silent install for the apks it installs.

The app that has the other apks embedded in it is called Setup.apk. One of the embedded apks is autorun.apk. Setup.apk has it's Project/Build Configuration/Package set to companyname.setup. Setup.apk copies the embedded apks to /mnt/sdcard/Player/Setup.

Note both are signed with the platform certificate. These are the platform.pk8 and platform.x509.pem files.

From Setup.apk you run this:

B4X:
iReturn = libPhone.Shell("pm", Array As String("install", "-i", "companyname.setup", "--user", "0", "/mnt/sdcard/Player/Setup/autorun.apk", libStringOut, libStringError)
If iReturn = 0 Then
  LogFile("Package Manager returned 0: installed")
Else If iReturn = 9 Then
  LogFile("Package Manager returned 9: not installed (no permission)")
Else
  LogFile("Package Manager returned " & iReturn & ", " & libStringError.ToString)
End If
 
Upvote 0

techknight

Well-Known Member
Licensed User
Longtime User
Platform certificates dont seem like they would be easy to get unless your working directly with an OEM.

Platform certs are those special certs that allow standard apps to get all the same permissions as System apps, correct? (shutdown, reboot, change settings, etc).

Also, the kernel is signed with the same keyset I believe. This also means you dont need an unlocked bootloader, essentially having the keys to the kingdom here. I could be wrong, but if this is the case, then having that cert is almost the same as having root.
 
Upvote 0

biometrics

Active Member
Licensed User
Longtime User
Platform certificates dont seem like they would be easy to get unless your working directly with an OEM.

Platform certs are those special certs that allow standard apps to get all the same permissions as System apps, correct? (shutdown, reboot, change settings, etc).

Also, the kernel is signed with the same keyset I believe. This also means you dont need an unlocked bootloader, essentially having the keys to the kingdom here.

We get them from the OEM.

It gives us permissions such as reboot etc.

When you build Android it creates 4 sets of certificates. One is called the platform certificate. That is what we use.

Note it's not the equivalent of root, I can't for example access the system partition which you can do as root. This is more about specific permission you want to acquire, .e.g reboot.
 
Upvote 0

techknight

Well-Known Member
Licensed User
Longtime User
We sell a ton of android devices with our product going out in the field, would be nice to find an OEM to work with that wouldn't require tons of $$$ up front, and has decent bluetooth performance. We are low-volume type company.

Currrently, I just go out and buy whats on amazon or whats on the shelf, and that's a huge issue due to the junkware installed on devices that are irrelevant to our product line.
 
Upvote 0

biometrics

Active Member
Licensed User
Longtime User
The above worked for Android 7. But on Android 9 I get this error:

B4X:
Security exception: Reverse mode only supported from shell

Has anyone seen this?
 
Upvote 0
Top