背景 安装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中下载即可,否则需要联系厂商。
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) { } else { } } }
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" ); 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(); 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 ) != PackageInstaller.STATUS_SUCCESS) { return 1 ; } if (doCommitSession(sessionId, false ) != PackageInstaller.STATUS_SUCCESS) { return 1 ; } abandonSession = false ; pw.println("Success" ); return 0 ; } finally { if (abandonSession) { try { doAbandonSession(sessionId, false ); } 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) -> { 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 () { 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(); } }