This document provides a broadcast receiver on the android12 system. The app sends a broadcast and brings in the apk address to install it.
1. Broadcast registration
frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
Dependencies to be imported first
import android.app.PendingIntent; import android.content.IntentSender; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; // import android.os.RemoteException; import android.content.ComponentName; import android.util.Log; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInstaller; import android.util.Log; import static android.content.pm.PackageInstaller.SessionParams.UID_UNKNOWN; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.parsing.ApkLiteParseUtils; import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.net.Uri; //import com.android.packageinstaller.InstallEventReceiver; import com.android.server.policy.TemporaryFileManager; import com.android.internal.content.PackageHelper; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; import android.os.Environment;
to define a variable
private final static SynchronousQueue
Start defining that broadcast, the installed broadcast
filter.addAction("com.android.packageinstaller.ACTION_SILENCE_INSTALL"); filter.addAction("android.intent.action.BOOT_COMPLETED");
Then what should you do after receiving the installation broadcast?
BroadcastReceiver mDockReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) { mDefaultDisplayPolicy.setDockMode(intent.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED)); }else if("com.android.packageinstaller.ACTION_SILENCE_INSTALL".equals(intent.getAction())){ android.util.Log.e("yanf yim","enter install_app------"); Intent mIntent1 = new Intent(); mIntent1.setAction("com.android.install_app1"); mIntent1.setComponent(new ComponentName("com.android.settings","com.android.settings.SDMountInstallReceiver")); String apkFilePath = intent.getStringExtra("apkFilePath"); mIntent1.putExtra("path_name",apkFilePath); context.sendBroadcast(mIntent1); }
Here is to start the SDMountInstallReceiver under Settings
2. SDMountInstallReceiver under startup settings
2.1 Register first
Code path packages\apps\Settings\AndroidManifest.xml
<receiver android:name=".SDMountInstallReceiver" android:exported="true"> <intent-filter android:priority="1000"> <action android:name="com.android.install_app1"/> <action android:name="com.android.install_app2"/> <action android:name="com.android.install_app3"/> </intent-filter> </receiver> <!--add end-->
2.2 Installed Services
packages\apps\Settings\src\com\android\settings\SDMountInstallReceiver.java
/* ** **Receive broadcast and install apk silently */ package com.android.settings; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import java.io.File; import android.os.Build; import java.io.IOException; import android.content.ContentValues; import android.content.IntentFilter; import android.util.Log; import android.content.pm.PackageManager; import android.os.storage.StorageManager; import android.os.Environment; import android.os.Bundle; import android.os.SystemProperties; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.app.PendingIntent; import android.content.IntentSender; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; // import android.os.RemoteException; import android.util.Log; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.net.Uri; import android.os.Binder; import android.os.Build; import android.util.Log; import androidx.core.content.FileProvider; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class SDMountInstallReceiver extends BroadcastReceiver { private static final String TAG="Install SDMountInstallReceiver"; private static final String PROP_SD_EXTERNAL_PATH = "vold.path.external_sd"; private String apksPath = " "; private Bundle bundleSimple; private PackageManager mPackageManager; @Override public void onReceive(final Context context, Intent intent) { if ("com.android.install_app1".equals(intent.getAction())) { String pathName = intent.getStringExtra("path_name"); android.util.Log.e("xnq", "enter SDMountInstallReceiver------1"); installApk(context, pathName); android.util.Log.e("xnq", "enter SDMountInstallReceiver------2"); } } /** * Show installation * * @param context * @param filePath */ public static synchronized void install(Context context, String filePath) { File apkFile = new File(filePath); Log.e(TAG, "apkPath " + apkFile.getAbsolutePath()); if (!apkFile.exists()) { Log.e(TAG, "apk does not exist!"); return; } Intent installApkIntent = new Intent(); installApkIntent.setAction(Intent.ACTION_VIEW); installApkIntent.addCategory(Intent.CATEGORY_DEFAULT); installApkIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //This is only adapted to 8.0 and requires permissions if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { boolean hasInstallPermission = context.getPackageManager().canRequestPackageInstalls(); if (hasInstallPermission) { //Give apk access rights through FileProvider Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", apkFile); installApkIntent.setDataAndType(uri, "application/vnd.android.package-archive"); installApkIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); if (context.getPackageManager().queryIntentActivities(installApkIntent, 0).size() > 0) { context.startActivity(installApkIntent); } } } } /** * Uninstall apk * * @param context * @param packageName */ public static synchronized void uninstallPackage(Context context, String packageName) { if (!isSystemSign(context)) { Log.e(TAG, "The apk does not have a system signature and the silent installation function cannot be used!"); uninstall(context, packageName); return; } Intent intent = new Intent(context, SDMountInstallReceiver.class); intent.setAction(PackageInstaller.EXTRA_STATUS); //Create uninstall broadcast intent PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); //Get the installer PackageInstaller installer = context.getPackageManager().getPackageInstaller(); //Perform uninstall operation if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //Uninstall the highest version apk installer.uninstall(new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST), pendingIntent.getIntentSender()); } else { //Uninstall apk installer.uninstall(packageName, pendingIntent.getIntentSender()); } } /** * Explicit uninstall * * @param context * @param packageName */ public static synchronized void uninstall(Context context, String packageName) { //Get the URI of the deleted package name Uri uri = Uri.parse("package:" + packageName); Intent intent = new Intent(); //Set the uninstall action we want to perform intent.setAction(Intent.ACTION_DELETE); //Set the obtained URI intent.setData(uri); context.startActivity(intent); } /** * Install apk * @param context * @param filePath */ public static synchronized void installApk (Context context, String filePath){ if (!isSystemSign(context)) { Log.e(TAG, "The apk does not have a system signature and the silent installation function cannot be used!"); //install(context, filePath); return; } File apkFile = new File(filePath); Log.e(TAG, "apkPath " + apkFile.getAbsolutePath()); if (!apkFile.exists()) { Log.e(TAG, "apk does not exist!"); return; } //1. Get the package installer PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); //2. Installation parameters PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL); //Set size sessionParams.setSize(apkFile.length()); //3. Session id int sessionId = createSession(packageInstaller, sessionParams); Log.e(TAG, "sessionId " + sessionId); if (sessionId != -1) { //4. Copy data into session boolean copySuccess = copyInstallFile(packageInstaller, sessionId, filePath); Log.e(TAG, "copySuccess " + copySuccess); if (copySuccess) { //5. Perform installation execInstallCommand(context, packageInstaller, sessionId); } } } /** * Create sessionId * * @param packageInstaller * @param sessionParams * @return */ private static int createSession (PackageInstaller packageInstaller, PackageInstaller.SessionParams sessionParams){ int sessionId = -1; try { //Create sessionId based on sessionParams sessionId = packageInstaller.createSession(sessionParams); } catch (IOException e) { e.printStackTrace(); } return sessionId; } /** * Copy the apk file and write it to PackageInstaller.Session * * @param packageInstaller * @param sessionId * @param apkFilePath * @return */ private static boolean copyInstallFile (PackageInstaller packageInstaller, int sessionId, String apkFilePath){ InputStream in = null; OutputStream out = null; PackageInstaller.Session session = null; boolean success = false; try { File apkFile = new File(apkFilePath); //Get PackageInstaller.Session through sessionId session = packageInstaller.openSession(sessionId); //Open input stream out = session.openWrite("base.apk", 0, apkFile.length()); //Create file stream in = new FileInputStream(apkFile); int total = 0, c; byte[] buffer = new byte[1024 * 1024]; //Read file stream while ((c = in.read(buffer)) != -1) { total + = c; out.write(buffer, 0, c); } //Synchronous Data session.fsync(out); Log.i(TAG, "streamed " + total + " bytes"); success = true; } catch (IOException e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException e) { e.printStackTrace(); } if (session != null) { session.close(); } } return success; } /** * Execute installation * * @param context * @param packageInstaller * @param sessionId * @return */ private static void execInstallCommand (Context context, PackageInstaller packageInstaller, int sessionId){ PackageInstaller.Session session = null; try { //Get PackageInstaller.Session through sessionId session = packageInstaller.openSession(sessionId); //Create a broadcast intent Intent intent = new Intent(context, SDMountInstallReceiver.class); intent.setAction(PackageInstaller.EXTRA_STATUS); //Set broadcast receiver PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); //Execute the installation command, and a broadcast notification will be sent when the installation is completed. session.commit(pendingIntent.getIntentSender()); } catch (Exception e) { e.printStackTrace(); } finally { if (session != null) { session.close(); } } } /** * Determine whether the app has a system signature based on the package name */ private static boolean isSystemSign (Context context){ return context.getPackageManager().checkSignatures(Binder.getCallingUid(), android.os.Process.SYSTEM_UID) == PackageManager.SIGNATURE_MATCH; } }