1

I am building AOSP for Android version 8.1 and I want to implement my own app store and hence need to install apks onto the system.

The only way I've seen so far is using something like the following:

val intent = Intent(Intent.ACTION_VIEW)
val file = File(Environment.getExternalStorageDirectory().toString() + "/APKs/" + "spotify.apk")
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive")
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)

However, this requests confirmation before each package is installed from the user. Is there any other way to achieve this without user confirmation if the app is a system app built into the image? Also what is the installation effectively doing in the background? Just moving the APK file to /data/app or what exactly happens?

phoebus
  • 1,280
  • 1
  • 16
  • 36

2 Answers2

1

Take a look runInstall() method

https://android.googlesource.com/platform/frameworks/base/+/master/cmds/pm/src/com/android/commands/pm/Pm.java

Required permission:

<uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
You Kim
  • 2,355
  • 1
  • 10
  • 11
  • I'll have a look into hit, thank you. Another question though, is there also a way to automatically populate login fields for installed apps? I want to automatically log into them (I have the credentials on the system). I am currently doing it via an AccessibilityService which seems cumbersome though. – phoebus May 06 '18 at 18:43
1

I solved it like so:

Prerequisite:

Your APK needs to be signed by system as correctly pointed out earlier. One way to achieve that is building the AOSP image yourself and adding the source code into the build.

Code:

Once installed as a system app, you can use the package manager methods to install and uninstall an APK as following:

Install:

public boolean install(final String apkPath, final Context context) {
    Log.d(TAG, "Installing apk at " + apkPath);
    try {
        final Uri apkUri = Uri.fromFile(new File(apkPath));
        final String installerPackageName = "MyInstaller";
        context.getPackageManager().installPackage(apkUri, installObserver, PackageManager.INSTALL_REPLACE_EXISTING, installerPackageName);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

Uninstall:

public boolean uninstall(final String packageName, final Context context) {
    Log.d(TAG, "Uninstalling package " + packageName);
    try {
        context.getPackageManager().deletePackage(packageName, deleteObserver, PackageManager.DELETE_ALL_USERS);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

To have a callback once your APK is installed/uninstalled you can use this:

/**
 * Callback after a package was installed be it success or failure.
 */
private class InstallObserver implements IPackageInstallObserver {

    @Override
    public void packageInstalled(String packageName, int returnCode) throws RemoteException {

        if (packageName != null) {
            Log.d(TAG, "Successfully installed package " + packageName);
            callback.onAppInstalled(true, packageName);
        } else {
            Log.e(TAG, "Failed to install package.");
            callback.onAppInstalled(false, null);
        }
    }

    @Override
    public IBinder asBinder() {
        return null;
    }
}

/**
 * Callback after a package was deleted be it success or failure.
 */
private class DeleteObserver implements IPackageDeleteObserver {

    @Override
    public void packageDeleted(String packageName, int returnCode) throws RemoteException {
        if (packageName != null) {
            Log.d(TAG, "Successfully uninstalled package " + packageName);
            callback.onAppUninstalled(true, packageName);
        } else {
            Log.e(TAG, "Failed to uninstall package.");
            callback.onAppUninstalled(false, null);
        }
    }

    @Override
    public IBinder asBinder() {
        return null;
    }
}

/**
 * Callback to give the flow back to the calling class.
 */
public interface InstallerCallback {
    void onAppInstalled(final boolean success, final String packageName);
    void onAppUninstalled(final boolean success, final String packageName);
}
phoebus
  • 1,280
  • 1
  • 16
  • 36
  • does this mean this build would result in non-certified google device ? Because we are making a change in build code – Tahseen Jul 17 '19 at 05:56