背景 安装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中下载即可,否则需要联系厂商。
生成jks1 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(); } }
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(); if (mInstallingTask == null ) { PackageInstaller installer = getPackageManager().getPackageInstaller(); PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId); if (sessionInfo != null && !sessionInfo.isActive()) { mInstallingTask = new InstallingAsyncTask(); mInstallingTask.execute(); } else { 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 { 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 { startActivity(launchIntent); } catch (ActivityNotFoundException | SecurityException e) { Log.e(LOG_TAG, "Could not start activity" , e); } finish(); }); } } }
这里我们可以通过调用PackageInstallerService来安装