Android 7.1 modify source code to achieve the silent installation app & automatically open application installed after the installation is complete.

The real silent installation does not require any user clicks, the blog said currently most silent installation or need pop-up confirmation screen, asking the user to authorize the installation, I think it should be a real silent installation does not require any user to determine the operation, For example, it is now a lot of applications installed software market is the real silent installation.

The company also wants to achieve a similar market and application installation, we find that, like millet and Huawei google has a personal application market, and this app is not written applications market exchange, for example, I would millet application market app installed Huawei phone above can not be achieved as silent installation like millet phone's built-in applications.

In order not to affect the normal permission checks, we need to modify the system source code on the line, add their own interface to install a

 

Specific changes are as follows:

Modify packages / apps / PackageInstaller / AndroidManifest.xml

Add the following permissions

<uses-permission android:name="android.permission.MANAGE_DOCUMENT" />

 

Create the following receiver 

   <receiver android:name=".SilentInstallerReceiver" android:exported="true" >
            <intent-filter>
                <action android:name="xxx.intent.action.SILENT.INSTALLER" ></action>
                <action android:name="xxx.intent.action.SILENT.INSTALLER.COMMIT"></action>
            </intent-filter>
   </receiver>

Add the following document packages / apps / PackageInstaller / src / com / android / packageinstaller / SilentInstallerReceiver.java

package com.android.packageinstaller;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Log;

import com.android.internal.content.PackageHelper;
import com.android.packageinstaller.permission.utils.IoUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class SilentInstallerReceiver extends BroadcastReceiver {

    private static final String TAG = "SilentInstallerReceiver";
    private static final String BROADCAST_ACTION = "xxx.intent.action.SILENT.INSTALLER.COMMIT";
    private static final String PACKAGENAME = "slient_name";
    private static final String INSTLLPATH = "instll_path";
    private static final String SCHEME_FILE = "file";
    private static final String SCHEME_CONTENT = "content";
    private static final String SCHEME_PACKAGE = "package";

    private Context mContext;
    private Uri mPackageURI;
    private Intent mLaunchIntent;
    private HandlerThread mInstallThread;
    private Handler mInstallHandler;

    private AsyncTask<Uri, Void, File> mStagingAsynTask;
    private PackageManager mPm;
    private PackageInfo mPkgInfo;

    @Override
    public void onReceive(Context context, Intent intent) {

        mContext = context;
        boolean wasSetUp = false;
        mInstallThread = new HandlerThread("InstallThread");
        mInstallThread.start();
        mInstallHandler = new Handler(mInstallThread.getLooper());

        mPm = context.getPackageManager();
        String instllpath = intent.getStringExtra(INSTLLPATH);

        // TODO this is  lunch APP
        if (!BROADCAST_ACTION.equals(intent.getAction())) {
            if (TextUtils.isEmpty(instllpath)) {

                return;
            }
            mPackageURI = Uri.parse(instllpath);
            if (mPackageURI.getScheme() == null) {
                instllpath = "file://" + instllpath;
                mPackageURI = Uri.parse(instllpath);
            }
            Log.d(TAG, "  mPackageURI =  " + mPackageURI + " ,  intent getAction =   " + intent.getAction());
            wasSetUp = processPackageUri(mPackageURI);
        }


        if (BROADCAST_ACTION.equals(intent.getAction())) {
            Log.d(TAG, " install app , so  return !!!  ");
            onPackageInstalled(intent);
            return;
        }

        if (mPackageURI == null) {
            Log.d(TAG, " mPackageURI is null , so  return !!!  ");
            return;
        }

        if (!wasSetUp) {
            Log.d(TAG, " wasSetUp is false, return!!!   ");
            return;
        }

        initiateInstall();
    }


    private void initiateInstall() {
        String pkgName = mPkgInfo.packageName;
        // Check if there is already a package on the device with this name
        // but it has been renamed to something else.
        String[] oldName = mPm.canonicalToCurrentPackageNames(new String[]{pkgName});
        if (oldName != null && oldName.length > 0 && oldName[0] != null) {
            pkgName = oldName[0];
            mPkgInfo.packageName = pkgName;
            mPkgInfo.applicationInfo.packageName = pkgName;
        }
        Log.d(TAG, " cdownloaded app uri=  " + mPackageURI);
        startSilentInstaller();
    }


    int getInstallFlags(String packageName) {
        PackageManager pm = mContext.getPackageManager();
        try {
            PackageInfo pi =
                    pm.getPackageInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
            if (pi != null) {
                return PackageManager.INSTALL_REPLACE_EXISTING;
            }
        } catch (PackageManager.NameNotFoundException e) {
        }
        return 0;
    }


    void startSilentInstaller() {
        final PackageManager pm = mContext.getPackageManager();
        final int installFlags = getInstallFlags(mPkgInfo.applicationInfo.packageName);

        if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
            Log.w(TAG, "Replacing package:" + mPkgInfo.applicationInfo.packageName);
        }
        Log.d(TAG, " starSilentInstaller    +  packageName  " + mPkgInfo.applicationInfo.packageName);
        final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        File file = new File(mPackageURI.getPath());

        try {
            PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
            params.setAppPackageName(pkg.packageName);
            params.setInstallLocation(pkg.installLocation);
            params.setSize(
                    PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
        } catch (PackageParser.PackageParserException e) {
            Log.e(TAG, "Cannot parse package " + file + ". Assuming defaults.");
            Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size.");
            params.setSize(file.length());
        } catch (IOException e) {
            Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size.");
            params.setSize(file.length());
        }

        mInstallHandler.post(new Runnable() {
            @Override
            public void run() {
                doPackageStage(pm, params);
            }
        });
    }


    private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
        final PackageInstaller packageInstaller = pm.getPackageInstaller();
        PackageInstaller.Session session = null;
        try {
            final String packageLocation = mPackageURI.getPath();
            final File file = new File(packageLocation);
            final int sessionId = packageInstaller.createSession(params);
            final byte[] buffer = new byte[65536];

            session = packageInstaller.openSession(sessionId);

            final InputStream in = new FileInputStream(file);
            final long sizeBytes = file.length();
            final OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes);
            try {
                int c;
                while ((c = in.read(buffer)) != -1) {
                    out.write(buffer, 0, c);
                    if (sizeBytes > 0) {
                        final float fraction = ((float) c / (float) sizeBytes);
                        session.addProgress(fraction);
                    }
                }
                session.fsync(out);
            } finally {
                IoUtils.closeQuietly(in);
                IoUtils.closeQuietly(out);
            }

            // TODO this is  lunch APP  Broast
            // Create a PendingIntent and use it to generate the IntentSender
            Intent broadcastIntent = new Intent(BROADCAST_ACTION);
            broadcastIntent.putExtra(PACKAGENAME, mPkgInfo.applicationInfo.packageName);

            PendingIntent pendingIntent = PendingIntent.getBroadcast(
                    mContext /*context*/,
                    sessionId,
                    broadcastIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT);

            session.commit(pendingIntent.getIntentSender());

        } catch (IOException e) {

        } finally {
            IoUtils.closeQuietly(session);
        }

    }


    /**
     * TODO this is  lunch APP  Receiver Broast
     * after app installed ,open the install app
     *
     * @param intent get insalled app name {@param BROADCAST_ACTION}
     */

    void onPackageInstalled(Intent intent) {
        Log.d(TAG, " install app is finishing, start luncher install app");
        mLaunchIntent = mContext.getPackageManager().getLaunchIntentForPackage(
                intent.getStringExtra(PACKAGENAME));
        if (mLaunchIntent == null) return;
        mLaunchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mContext.startActivity(mLaunchIntent);

    }

    /**
     * delete the apk
     */
    private void clearCachedApkIfNeededAndFinish() {
        if ("file".equals(mPackageURI.getScheme()) && mPackageURI.getPath() != null
                && mPackageURI.getPath().startsWith(mContext.getCacheDir().toString())) {
            File file = new File(mPackageURI.getPath());
            file.delete();
        }
    }

    /**
     * Parse the Uri and set up the installer for this package.
     *
     * @param packageUri The URI to parse
     * @return {@code true} iff the installer could be set up
     */
    private boolean processPackageUri(final Uri packageUri) {
        mPackageURI = packageUri;

        final String scheme = packageUri.getScheme();
        Log.d(TAG, " scheme  =  " + scheme + "  packageUri  " + packageUri);
        switch (scheme) {
            case SCHEME_PACKAGE: {
                try {
                    mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(),
                            PackageManager.GET_PERMISSIONS
                                    | PackageManager.GET_UNINSTALLED_PACKAGES);
                } catch (PackageManager.NameNotFoundException e) {
                }
                if (mPkgInfo == null) {
                    Log.w(TAG, "Requested package " + packageUri.getScheme()
                            + " not available. Discontinuing installation");
                    return false;
                }
            }
            break;

            case SCHEME_FILE: {
                File sourceFile = new File(packageUri.getPath());
                PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
                // Check for parse errors
                if (parsed == null) {
                    Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
                    return false;
                }
                mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
                        PackageManager.GET_PERMISSIONS, 0, 0, null,
                        new PackageUserState());
            }
            break;

            case SCHEME_CONTENT: {
                mStagingAsynTask = new SilentInstallerReceiver.StagingAsyncTask();
                mStagingAsynTask.execute(packageUri);
                return false;
            }

            default: {
                Log.w(TAG, "Unsupported scheme " + scheme);
                clearCachedApkIfNeededAndFinish();
                return false;
            }
        }
        return true;
    }


    private final class StagingAsyncTask extends AsyncTask<Uri, Void, File> {

        @Override
        protected void onPreExecute() {
            // TODO  nothing to do
        }

        @Override
        protected File doInBackground(Uri... params) {
            if (params == null || params.length <= 0) {
                return null;
            }
            Uri packageUri = params[0];
            File sourceFile = null;
            try {
                sourceFile = File.createTempFile("package", ".apk", mContext.getApplicationContext().getCacheDir());
                try (
                        InputStream in = mContext.getApplicationContext().getContentResolver().openInputStream(packageUri);
                        OutputStream out = (in != null) ? new FileOutputStream(
                                sourceFile) : null;
                ) {
                    // Despite the comments in ContentResolver#openInputStream
                    // the returned stream can be null.
                    if (in == null) {
                        return null;
                    }
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) >= 0) {
                        // Be nice and respond to a cancellation
                        if (isCancelled()) {
                            return null;
                        }
                        out.write(buffer, 0, bytesRead);
                    }
                }
            } catch (IOException ioe) {
                Log.w(TAG, "Error staging apk from content URI", ioe);
                if (sourceFile != null) {
                    sourceFile.delete();
                }
            }
            return sourceFile;
        }

        @Override
        protected void onPostExecute(File file) {
            if (file == null) {
                return;
            }
            Uri fileUri = Uri.fromFile(file);

            boolean wasSetUp = processPackageUri(fileUri);
            if (wasSetUp) {
                initiateInstall();
            }
        }

        @Override
        protected void onCancelled(File file) {
            // TODO  nothing to do
        }
    }

}

Once created recompile PackageInstaller apk push to restart the phone inside the phone.

Adb broadcast transmission using a silent installation verification operation, when the downloaded file is good, can be issued as follows.

adb shell am broadcast -a xxx.intent.action.SILENT.INSTALLER --es instllpath "apk路径"

Xu execute this command wait 5-10 seconds, waiting time and the phone's performance relationship, the better the performance, the shorter wait. After the installation is complete, pull up directly to the apk, no action is required,

If you do not want to be drawn up after the installation is complete, the following code injection commented out.

<action android:name="xxx.intent.action.SILENT.INSTALLER.COMMIT"></action>

 

Published 25 original articles · won praise 13 · views 10000 +

Guess you like

Origin blog.csdn.net/ChaoLi_Chen/article/details/103576575