Is it possible to merge/install split APK files (AKA “app bundle”), on Android device itself, without root?

后端 未结 8 1236
北恋
北恋 2020-12-09 02:50

Background

In the past, I\'ve asked about sharing or backup of app-bundle / split apk files, here .

This seems like an almost impossible task, which I coul

相关标签:
8条回答
  • 2020-12-09 03:41

    Well I don't know the coding part much, as I have not learnt android, but I can suggest something which you can try. If the task is just to make the split apka into one, what I do is

    1. Install the app using SAI, or Lucky patcher (since it started creating apks instead of apk, and so is able to install them)
    2. Extract as a single apk using apk extractor (the first appearing in the search, by Meher)
    3. Optional - Uninstall the app, if you only needed apk

    So you can look at their source code (if they are open source, otherwise something similar), and then try to make a single app to do all these processes (if you know android).

    Hope it helps, and please provide your app link, if you manage to create one.

    Thanks and cheers

    0 讨论(0)
  • 2020-12-09 03:46

    Please check this. when we send

    adb install-multiple apk1 apk2 ...
    

    it calls this code install-multiple

     std::string install_cmd;
        if (_use_legacy_install()) {
            install_cmd = "exec:pm";
        } else {
            install_cmd = "exec:cmd package";
        }
    
        std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, install_cmd.c_str(), total_size);
        for (i = 1; i < first_apk; i++) {
            cmd += " " + escape_arg(argv[i]);
        }
    

    which in turn calls Pm.java or a new way of executing PackageManagerService code, both are similar

    I tried to integrate that code in my app, The problem which I faced, apk installation was not able to complete, it is due to the reason that the app needs.

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

    But it is only given to system-priv apps. When I executed these steps from adb shell apk installation was successful and when I created my app a system priv-app apk install was successfull.

    code to call new apis of PackageManager, mostly copied from Pm.java Steps in installing split apks

    1. Create a session with argument -S , return session id.

      (install-create, -S, 52488426) 52488426 -- total size of apks.

    2. Write split apks in that session with size , name and path

      (install-write, -S, 44334187, 824704264, 1_base.apk, -)

      (install-write, -S, 1262034, 824704264, 2_split_config.en.apk, -)

      (install-write, -S, 266117, 824704264, 3_split_config.hdpi.apk, -)

      (install-write, -S, 6626088, 824704264, 4_split_config.x86.apk, -)

    3. commit the session with session id

      (install-commit, 824704264)

    I have placed airbnb apk in my sdcard.

    OnePlus5:/sdcard/com.airbnb.android-1 $ ll
    total 51264
    -rw-rw---- 1 root sdcard_rw 44334187 2019-04-01 14:20 base.apk
    -rw-rw---- 1 root sdcard_rw  1262034 2019-04-01 14:20 split_config.en.apk
    -rw-rw---- 1 root sdcard_rw   266117 2019-04-01 14:20 split_config.hdpi.apk
    -rw-rw---- 1 root sdcard_rw  6626088 2019-04-01 14:20 split_config.x86.apk
    

    and calling functions to install apk.

    final InstallParams installParams = makeInstallParams(52488426l);
    
                try {
                    int sessionId = runInstallCreate(installParams);
    
                    runInstallWrite(44334187,sessionId, "1_base.apk", "/sdcard/com.airbnb.android-1/base.apk");
    
                    runInstallWrite(1262034,sessionId, "2_split_config.en.apk", "/sdcard/com.airbnb.android-1/split_config.en.apk");
    
                    runInstallWrite(266117,sessionId, "3_split_config.hdpi.apk", "/sdcard/com.airbnb.android-1/split_config.hdpi.apk");
    
                    runInstallWrite(6626088,sessionId, "4_split_config.x86.apk", "/sdcard/com.airbnb.android-1/split_config.x86.apk");
    
    
                    if (doCommitSession(sessionId, false )
                            != PackageInstaller.STATUS_SUCCESS) {
                    }
                    System.out.println("Success");
    
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
    
    private int runInstallCreate(InstallParams installParams) throws RemoteException {
        final int sessionId = doCreateSession(installParams.sessionParams);
        System.out.println("Success: created install session [" + sessionId + "]");
        return sessionId;
    }
    
    private int doCreateSession(PackageInstaller.SessionParams params)
            throws RemoteException {
    
        int sessionId = 0 ;
        try {
            sessionId = packageInstaller.createSession(params);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sessionId;
    }
    
    private int runInstallWrite(long size, int sessionId , String splitName ,String path ) throws RemoteException {
        long sizeBytes = -1;
    
        String opt;
        sizeBytes = size;
        return doWriteSession(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
    }
    
    
    private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName,
                               boolean logSuccess) throws RemoteException {
        if ("-".equals(inPath)) {
            inPath = null;
        } else if (inPath != null) {
            final File file = new File(inPath);
            if (file.isFile()) {
                sizeBytes = file.length();
            }
        }
    
        final PackageInstaller.SessionInfo info = packageInstaller.getSessionInfo(sessionId);
    
        PackageInstaller.Session session = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            session = packageInstaller.openSession(sessionId);
    
            if (inPath != null) {
                in = new FileInputStream(inPath);
            }
    
            out = session.openWrite(splitName, 0, sizeBytes);
    
            int total = 0;
            byte[] buffer = new byte[65536];
            int c;
            while ((c = in.read(buffer)) != -1) {
                total += c;
                out.write(buffer, 0, c);
            }
            session.fsync(out);
    
            if (logSuccess) {
                System.out.println("Success: streamed " + total + " bytes");
            }
            return PackageInstaller.STATUS_SUCCESS;
        } catch (IOException e) {
            System.err.println("Error: failed to write; " + e.getMessage());
            return PackageInstaller.STATUS_FAILURE;
        } finally {
            try {
                out.close();
                in.close();
                session.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    
    private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
        PackageInstaller.Session session = null;
        try {
            try {
                session = packageInstaller.openSession(sessionId);
            } catch (IOException e) {
                e.printStackTrace();
            }
            session.commit(PendingIntent.getBroadcast(getApplicationContext(), sessionId,
                    new Intent("android.intent.action.MAIN"), 0).getIntentSender());
            System.out.println("install request sent");
    
            Log.d(TAG, "doCommitSession: " + packageInstaller.getMySessions());
    
            Log.d(TAG, "doCommitSession: after session commit ");
    
            return 1;
        } finally {
            session.close();
        }
    }
    
    
    
    private static class InstallParams {
        PackageInstaller.SessionParams sessionParams;
    }
    
    private InstallParams makeInstallParams(long totalSize ) {
        final PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        final InstallParams params = new InstallParams();
        params.sessionParams = sessionParams;
        String opt;
        sessionParams.setSize(totalSize);
        return params;
    }
    

    This is the list of commands that are actually received in Pm.java when we do adb install-multiple

    04-01 16:04:40.626  4886  4886 D Pm      : run() called with: args = [[install-create, -S, 52488426]]
    04-01 16:04:41.862  4897  4897 D Pm      : run() called with: args = [[install-write, -S, 44334187, 824704264, 1_base.apk, -]]
    04-01 16:04:56.036  4912  4912 D Pm      : run() called with: args = [[install-write, -S, 1262034, 824704264, 2_split_config.en.apk, -]]
    04-01 16:04:57.584  4924  4924 D Pm      : run() called with: args = [[install-write, -S, 266117, 824704264, 3_split_config.hdpi.apk, -]]
    04-01 16:04:58.842  4936  4936 D Pm      : run() called with: args = [[install-write, -S, 6626088, 824704264, 4_split_config.x86.apk, -]]
    04-01 16:05:01.304  4948  4948 D Pm      : run() called with: args = [[install-commit, 824704264]]
    

    So for apps which are not system priv-app, I don't know how can they can install split apks. Play store being a system priv-app can use these apis and install split apks without any issues.

    0 讨论(0)
提交回复
热议问题