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
Create a session with argument -S , return session id.
(install-create, -S, 52488426)
52488426 -- total size of apks.
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, -)
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.