0%

静默安装apk以及系统apk安装的2种方式

背景

安装apk的方式有以下两种

  • adb install 通过adb安装
  • Installer activity 通过系统的包管理Activity安装

实际调用上述两种方式并不能成功,即使手机root也不行,所以需要将app设置为系统app
Manifest配置

1
android:sharedUserId="android.uid.system"
  • 下载 签名文件“platform.pk8”和“platform.x509.pem”

位置:aosp/build/target/product/security/
如果厂商没有修改google原生的签名文件,直接在aosp中下载即可,否则需要联系厂商。

  • 生成jks
    1
    keytool -importkeystore -deststorepass android -destkeypass android -destkeystore platform.jks -srckeystore shared.pk12 -srcstoretype PKCS12 -srcstorepass android -alias androiddebugkey
    将app使用platform.jks签名即可

    代码

    以下代码根据系统源码整理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public void install(String path) {	
    PackageInstaller.Session session = context.getPackageManager().getPackageInstaller().openSession(1);
    File file = new File(path);
    ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(file,ParcelFileDescriptor.MODE_READ_ONLY);
    session.write("base.apk",0,file.length(),parcelFileDescriptor);
    Intent intent = new Intent(context,Receiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context,11,intent,PendingIntent.FLAG_UPDATE_CURRENT);
    session.commit(pendingIntent.getIntentSender());
    }


    public static class Receiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
    int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
    if (status == PackageInstaller.STATUS_SUCCESS) {
    // success
    } else {
    //fail
    }
    }
    }

    adb install源码分析

    源码位置:system/core/adb/client

main.cpp

1
2
3
4
5
6
int main(int argc, char* argv[], char* envp[]) {
__adb_argv = const_cast<const char**>(argv);
__adb_envp = const_cast<const char**>(envp);
adb_trace_init(argv);
return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
}

commandline.cpp

1
2
3
4
5
6
int adb_commandline(int argc, const char** argv) {
if (!strcmp(argv[0], "install")) {
if (argc < 2) error_exit("install requires an argument");
return install_app(argc, argv);
}
}

adb_install.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
int install_app(int argc, const char** argv) {

switch (installMode) {
case INSTALL_PUSH:
return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy, use_localagent);
case INSTALL_STREAM:
return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy, use_localagent);
case INSTALL_DEFAULT:
default:
return 1;
}
}


static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy,
bool use_localagent) {
printf("Performing Push Install\n");

// Find last APK argument.
// All other arguments passed through verbatim.
int last_apk = -1;
for (int i = argc - 1; i >= 0; i--) {
if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) {
error_exit("APEX packages are only compatible with Streamed Install");
}
if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {
last_apk = i;
break;
}
}

if (last_apk == -1) error_exit("need APK file on command line");

int result = -1;
std::vector<const char*> apk_file = {argv[last_apk]};
std::string apk_dest =
"/data/local/tmp/" + android::base::Basename(argv[last_apk]);

if (use_fastdeploy == true) {
#if defined(ENABLE_FASTDEPLOY)
TemporaryFile metadataTmpFile;
TemporaryFile patchTmpFile;

FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
extract_metadata(apk_file[0], metadataFile);
fclose(metadataFile);

create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
#else
error_exit("fastdeploy is disabled");
#endif
} else {
if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
}

argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
result = pm_command(argc, argv);

cleanup_apk:
if (use_fastdeploy == true) {
#if defined(ENABLE_FASTDEPLOY)
delete_device_patch_file(apk_file[0]);
#endif
}
delete_device_file(apk_dest);
return result;
}

static int pm_command(int argc, const char** argv) {
std::string cmd = "pm";

while (argc-- > 0) {
cmd += " " + escape_arg(*argv++);
}

return send_shell_command(cmd);
}

pm

1
2
#!/system/bin/sh
cmd package "$@"

这里package调用了PackageManagerService

PackageManagerService.java

1
2
3
4
5
6
7
8
9
10
public class PackageManagerService extends IPackageManager.Stub
implements PackageSender {
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
(new PackageManagerShellCommand(this)).exec(
this, in, out, err, args, callback, resultReceiver);
}
}

PackageManagerShellCommand.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class PackageManagerShellCommand extends ShellCommand {
@Override
public int onCommand(String cmd) {
switch(cmd) {
case "install":
return runInstall();
}
}
private int runInstall() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final InstallParams params = makeInstallParams();
final String inPath = getNextArg();

setParamsSize(params, inPath);
final int sessionId = doCreateSession(params.sessionParams,
params.installerPackageName, params.userId);
boolean abandonSession = true;
try {
if (inPath == null && params.sessionParams.sizeBytes == -1) {
pw.println("Error: must either specify a package size or an APK file");
return 1;
}
final boolean isApex =
(params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
String splitName = "base." + (isApex ? "apex" : "apk");
if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName,
false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
return 1;
}
if (doCommitSession(sessionId, false /*logSuccess*/)
!= PackageInstaller.STATUS_SUCCESS) {
return 1;
}
abandonSession = false;
pw.println("Success"); //最后打印Success给终端
return 0;
} finally {
if (abandonSession) {
try {
doAbandonSession(sessionId, false /*logSuccess*/);
} catch (Exception ignore) {
}
}
}
}
}

所以使用Runtime 执行时,最后输出流中有Success表示安装成功
eg:

1
2
3
4
➜ adb push amap_bundle_portal-debug.apk /data/local/tmp/
amap_bundle_portal-debug.apk: 1 file pushed, 0 skipped. 40.7 MB/s (127578303 bytes in 2.990s)
➜ adb shell pm install /data/local/tmp/amap_bundle_portal-debug.apk
Success

Installer activity安装

源码位置:/aosp/frameworks/base/packages/PackageInstaller

PackageInstallerActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class PackageInstallerActivity {
private void bindUi() {
mAlert.setIcon(mAppSnippet.icon);
mAlert.setTitle(mAppSnippet.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
(ignored, ignored2) -> {
if (mOk.isEnabled()) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
finish();
} else {
// 开始安装
startInstall();
}
}
}, null);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
finish();
}, null);
setupAlert();

mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
mOk.setEnabled(false);
}
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
//安装中
newIntent.setClass(this, InstallInstalling.class);
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (mOriginatingURI != null) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
}
if (mReferrerURI != null) {
newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
}
if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
}
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
installerPackageName);
}
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
}
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
startActivity(newIntent);
finish();
}
}

InstallInstalling.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
public class InstallInstalling extends AlertActivity {
@Override
protected void onResume() {
super.onResume();

// This is the first onResume in a single life of the activity
if (mInstallingTask == null) {
PackageInstaller installer = getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);

if (sessionInfo != null && !sessionInfo.isActive()) {
mInstallingTask = new InstallingAsyncTask();
//开始安装
mInstallingTask.execute();
} else {
// we will receive a broadcast when the install is finished
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}

/**
* 安装成功
*/
private void launchSuccess() {
Intent successIntent = new Intent(getIntent());
successIntent.setClass(this, InstallSuccess.class);
successIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);

startActivity(successIntent);
finish();
}

private final class InstallingAsyncTask extends AsyncTask<Void, Void,
PackageInstaller.Session> {
volatile boolean isDone;

@Override
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
try {
// 这里调用了 PackageInstallerService
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
} catch (IOException e) {
return null;
}

session.setStagingProgress(0);

try {
File file = new File(mPackageURI.getPath());

try (InputStream in = new FileInputStream(file)) {
long sizeBytes = file.length();
try (OutputStream out = session
.openWrite("PackageInstaller", 0, sizeBytes)) {
byte[] buffer = new byte[1024 * 1024];
while (true) {
int numRead = in.read(buffer);

if (numRead == -1) {
session.fsync(out);
break;
}

if (isCancelled()) {
session.close();
break;
}

out.write(buffer, 0, numRead);
if (sizeBytes > 0) {
float fraction = ((float) numRead / (float) sizeBytes);
session.addProgress(fraction);
}
}
}
}

return session;
} catch (IOException | SecurityException e) {
Log.e(LOG_TAG, "Could not write package", e);

session.close();

return null;
} finally {
synchronized (this) {
isDone = true;
notifyAll();
}
}
}

@Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.setPackage(getPackageName());
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);

PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallInstalling.this,
mInstallId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);

session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);

if (!isCancelled()) {
launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
}
}
}
}
}

InstallSuccess.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class InstallSuccess extends AlertActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// 完成
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.done),
(ignored, ignored2) -> {
if (appInfo.packageName != null) {
Log.i(LOG_TAG, "Finished installing " + appInfo.packageName);
}
finish();
}, null);
// 打开
Button launchButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
if (enabled) {
launchButton.setOnClickListener(view -> {
try {
//跳转到安装的app activity
startActivity(launchIntent);
} catch (ActivityNotFoundException | SecurityException e) {
Log.e(LOG_TAG, "Could not start activity", e);
}
finish();
});
}
}
}

这里我们可以通过调用PackageInstallerService来安装