MTK Android12 silent installation interface

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 mInstallResults = new 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;
    }

}