yuralife
yuralife

Reputation: 1582

Silent install apk programmatically by system app (without root)

I have system-level app with android:sharedUserId="android.uid.system" (UID: 1000).

Device is not rooted! INSTALL_PACKAGES permission is included to Manifest.

Can I install downloaded *.apk silently?

Upvotes: 40

Views: 51807

Answers (3)

alperk01
alperk01

Reputation: 343

In order to autoupdate your app silently, you have 2 options.

  1. Sign your app with the manufacturer certificate.
  2. Put your app under /system/priv-app folder. Putting an apk under /system/ folder makes the app a system app, but it is not enough for it to autoupdate. During the autoupdate process, it will not throw any error, but autoupdate will fail. If the apk is under /system/priv-app folder or one of its subfolders, autoupdate will complete successfully.

Use PackageInstaller class for autoupdate.

 private boolean installPackage(Context context, String packageName, String apkPath) throws IOException {
    File f = new File(apkPath);
    if(!f.exists()){
        Log.i(TAG, "[installPackage] Apk file cannot be updated because file is not available. apkPath:"+apkPath);
        return false;
    }

    FileInputStream in = null;
    OutputStream out = null;
    PackageInstaller.Session session = null;
    int sessionId = 0;
    try{
        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(packageName);
        // set params
        sessionId = packageInstaller.createSession(params);
        session = packageInstaller.openSession(sessionId);

        // Copy APK file bytes into OutputStream provided by install Session
        out = session.openWrite(packageName, 0, -1);
        in = new FileInputStream(f);

        byte[] buffer = new byte[1024];
        int c;
        while ((c = in.read(buffer)) > 0) {
            out.write(buffer, 0, c);
        }
        out.flush();

        session.fsync(out);
    }catch (Exception e){
        Log.e(TAG, "[installPackage] apkPath:"+apkPath+", error:", e);
        return false;
    }finally {
        StreamUtil.closeSilently(out, in);
        out = null;
        in = null;
        System.gc();
    }

    try{
        // The app gets killed after installation session commit
        session.commit(PendingIntent.getBroadcast(context, sessionId, new Intent("android.intent.action.MAIN"), getPendingIntentFlag(0)).getIntentSender());
        session.close();
    }catch (Exception e){
        Log.e(TAG, "[installPackage] session commit error:", e);
        return false;
    }

    return true;
}
public int getPendingIntentFlag(int flag){
    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? (PendingIntent.FLAG_IMMUTABLE | flag) : flag;
}

Upvotes: 1

yuralife
yuralife

Reputation: 1582

I`ve found the issue.

  1. Added "android.permission.INSTALL_PACKAGES" permission.
  2. I signed my application using "signapk.jar" with certificate that I`ve got from manufacturer of my device.
  3. And now this app can install others using "pm install /.../app.apk" without "su".

It works!

P.S. Updating application using just "pm install" doesn`t replace existing application with new one, but returns exit_value = 0. So to update there are two ways:

  • use "pm install -r /.../app.apk";
  • firstly delete app using "pm uninstall com.mydomain.myapp" (you need "android.permission.DELETE_PACKAGES" permission) and then install new one.

Upvotes: 45

Raghav Sood
Raghav Sood

Reputation: 82563

No, you cannot. Simply having the same UID isn't enough. You must also be signed with the same key as the ROM itself.

Silently installing apps, or updates to your own app, isn't allowed for security reasons.

Upvotes: 6

Related Questions