Android11.0系统中实现静默安装并启动App以及静默卸载

Android11.0系统中实现静默安装并启动App以及静默卸载

静默安装并启动App以及静默卸载

本文描述Android11中通过修改系统PackageInstaller应用支持静默安装App,并启动安装的App。

修改PackageInstaller

PackageInstaller是系统中专门负责app安装的App,静默安装逻辑添加到此应用中,应用所在路径 /frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/

  1. 添加SilenceInstallManager,路径为 /frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/SilenceInstallManager.java;

package com.android.packageinstaller;

import android.annotation.SuppressLint;
import android.app.PendingIntent;
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.ResolveInfo;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.ArrayMap;
import android.util.Log;

import androidx.annotation.NonNull;

import com.android.internal.content.PackageHelper;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

import android.content.pm.IPackageDeleteObserver;


final class SilenceInstallManager {
    
    
    private static final String TAG = "SilenceInstallManager";

    private static final int MSG_WHAT_INSTALL_FINISH_SUCCESS = 0;
    private static final int MSG_WHAT_INSTALL_FINISH_FAIL = 1;
    private static final int MSG_WHAT_UNINSTALL_COMPLETE = 2;

    private Context mContext;

    @SuppressLint("NewApi")
    private ArrayMap<Integer, InstallAppInfo> InstallAppInfoMap = new ArrayMap<>();


    private static volatile SilenceInstallManager INSTANCE;

    private SilenceInstallManager(Context context) {
    
    
        mContext = context;
    }

    public static SilenceInstallManager getInstance(Context context) {
    
    
        if (null == INSTANCE) {
    
    
            synchronized (SilenceInstallManager.class) {
    
    
                if (null == INSTANCE) {
    
    
                    INSTANCE = new SilenceInstallManager(context.getApplicationContext());
                }
            }
        }
        return INSTANCE;
    }


    @SuppressLint("NewApi")
    private PackageInstaller.SessionCallback mSessionCallback = new PackageInstaller.SessionCallback() {
    
    
        @Override
        public void onCreated(int sessionId) {
    
    
            Log.d(TAG, "onCreated---->" + sessionId);
        }

        @Override
        public void onBadgingChanged(int sessionId) {
    
    
//            Log.w(TAG, "SilenceInstallReceiver onBadgingChanged---->" + sessionId);
        }

        @Override
        public void onActiveChanged(int sessionId, boolean active) {
    
    
//            Log.w(TAG, "SilenceInstallReceiver onActiveChanged---->" + sessionId + "  active--->" + active);
        }

        @Override
        public void onProgressChanged(int sessionId, float progress) {
    
    
//            Log.w(TAG, "SilenceInstallReceiver onProgressChanged---->" + sessionId + "  progress--->" + progress);
        }

        @Override
        public void onFinished(int sessionId, boolean success) {
    
    
            Log.d(TAG, "onFinished---->" + sessionId + "  success--->" + success);
            Message msg = Message.obtain();
            msg.what = MSG_WHAT_INSTALL_FINISH_SUCCESS;
            msg.arg1 = sessionId;
            msg.obj = success;
            mHandler.sendMessage(msg);
        }
    };

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
    
    
        @Override
        public void dispatchMessage(@NonNull Message msg) {
    
    
            mContext.getPackageManager().getPackageInstaller().unregisterSessionCallback(mSessionCallback);
            if (msg.what == MSG_WHAT_INSTALL_FINISH_SUCCESS) {
    
    
                boolean result = (boolean) msg.obj;
                int sessionId = msg.arg1;
                InstallAppInfo info = InstallAppInfoMap.remove(sessionId);
                if (result) {
    
    
                    Log.d(TAG, "install success");
                    if (null != info) {
    
    
                        if (info.isLaunch && null != info.info && null != info.info.packageName && !"".equals(info.info.packageName)) {
    
    
                            launchApp(info.info.packageName);
                        }

                        File f = new File(info.filePath);
                        if (f.exists()) {
    
    
                            f.delete();
                        }
                    }
                } else {
    
    
                    Log.d(TAG, "install fail");
                }
            } else if (msg.what == MSG_WHAT_INSTALL_FINISH_FAIL) {
    
    
                int sessionId = msg.arg1;
                if (sessionId != -1) {
    
    
                    InstallAppInfoMap.remove(sessionId);
                }
                Log.d(TAG, "install fail");
            } else if (msg.what == MSG_WHAT_UNINSTALL_COMPLETE) {
    
    
                Log.d(TAG, "uninstall complete--->" + msg.arg1);
                if (msg.arg1 == PackageManager.DELETE_SUCCEEDED) {
    
    
                    Log.d(TAG, "delete succeeded");
                } else {
    
    
                    Log.d(TAG, "delete fail");
                }
            }
        }
    };

    public void silenceInstall(String appFilePath, boolean launch) {
    
    
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
    
    
            mContext.getPackageManager().getPackageInstaller().registerSessionCallback(mSessionCallback);

            PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
            params.setInstallAsInstantApp(false);
            params.setInstallReason(PackageManager.INSTALL_REASON_USER);

            File file = new File(appFilePath);
            if (!file.exists()) {
    
    
                sendFailMsg(-1);
                return;
            }

            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());
            }

            try {
    
    
                PackageInfo mPkgInfo = PackageUtil.getPackageInfo(mContext, file, PackageManager.GET_PERMISSIONS);
                int mSessionId = mContext.getPackageManager().getPackageInstaller().createSession(params);
                InstallAppInfo installAppInfo = new InstallAppInfo(mSessionId, appFilePath, mPkgInfo, launch);
                InstallAppInfoMap.put(mSessionId, installAppInfo);

                InstallingAsyncTask mInstallingTask = new InstallingAsyncTask(mContext, appFilePath, mSessionId);
                mInstallingTask.execute();
            } catch (IOException e) {
    
    
                e.printStackTrace();
                sendFailMsg(-1);
            }
        }
    }

    private void sendFailMsg(int sessionId) {
    
    
        Message msg = Message.obtain();
        msg.what = MSG_WHAT_INSTALL_FINISH_FAIL;
        msg.arg1 = sessionId;
        mHandler.sendMessage(msg);
    }


    @SuppressLint("NewApi")
    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
            PackageInstaller.Session> {
    
    

        private Context mContext;
        private String mAppPath;
        private int mSessionId;

        public InstallingAsyncTask(Context context, String appPath, int sessionId) {
    
    
            mContext = context;
            mAppPath = appPath;
            mSessionId = sessionId;
        }

        @Override
        protected PackageInstaller.Session doInBackground(Void... params) {
    
    
            PackageInstaller.Session session;
            try {
    
    
                session = mContext.getPackageManager().getPackageInstaller().openSession(mSessionId);
            } catch (IOException e) {
    
    
                return null;
            }

            session.setStagingProgress(0);

            try {
    
    
                File file = new File(mAppPath);

                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(TAG, "Could not write package", e);
                session.close();
                return null;
            }
        }

        @Override
        protected void onPostExecute(PackageInstaller.Session session) {
            if (session != null) {
                Intent broadcastIntent = new Intent();
                PendingIntent pendingIntent = PendingIntent.getBroadcast(
                        mContext,
                        1,
                        broadcastIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT);

                session.commit(pendingIntent.getIntentSender());

                session.close();

                Log.d(TAG, "send install PendingIntent----->");
            } else {
    
    
                mContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
                sendFailMsg(mSessionId);

                File f = new File(mAppPath);
                if (f.exists()) {
    
    
                    f.delete();
                }

                Log.e(TAG, "copy fail delete file----->");
            }

            mContext = null;
            mAppPath = "";
            mSessionId = -1;
        }
    }


    private class InstallAppInfo {
    
    
        private int sessionId;
        private String filePath;
        private PackageInfo info;
        private boolean isLaunch;

        public InstallAppInfo(int sessionId, String filePath, PackageInfo info, boolean isLaunch) {
    
    
            this.sessionId = sessionId;
            this.filePath = filePath;
            this.info = info;
            this.isLaunch = isLaunch;
        }

        public int getSessionId() {
    
    
            return sessionId;
        }

        public String getFilePath() {
    
    
            return filePath;
        }

        public PackageInfo getInfo() {
    
    
            return info;
        }

        public boolean isLaunch() {
    
    
            return isLaunch;
        }
    }


    private void launchApp(String appPackageName) {
    
    
        Intent mLaunchIntent = mContext.getPackageManager().getLaunchIntentForPackage(appPackageName);
        if (mLaunchIntent != null) {
    
    
            List<ResolveInfo> list = mContext.getPackageManager().queryIntentActivities(mLaunchIntent, 0);
            if (list != null && list.size() > 0) {
    
    
                Log.d(TAG, "launch app--->");
                mContext.startActivity(mLaunchIntent);
            }
        }
    }


    public void silenceUninstall(String packageName) {
    
    
        Log.i(TAG, "silenceUninstall--->" + packageName);
        PackageDeleteObserver observer = new PackageDeleteObserver();
        mContext.getPackageManager().deletePackage(packageName, observer, PackageManager.DELETE_ALL_USERS);
    }

    private class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
    
    
        public void packageDeleted(String packageName, int returnCode) {
    
    
            Message msg = mHandler.obtainMessage(MSG_WHAT_UNINSTALL_COMPLETE);
            msg.arg1 = returnCode;
            msg.obj = packageName;
            mHandler.sendMessage(msg);
        }
    }
}

  1. 添加SilenceInstallReceiver,路径为 /frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/SilenceInstallReceiver.java;

package com.android.packageinstaller;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;


public class SilenceInstallReceiver extends BroadcastReceiver {
    
    
    public static final String SILENCE_INSTALL_APP = "com.android.packageinstaller.ACTION_SILENCE_INSTALL";
    public static final String SILENCE_INSTALL_KEY = "silence_install";
    public static final String IS_LAUNCH_KEY = "is_launch";
    public static final String APP_URI_KEY = "app_uri";


    @Override
    public void onReceive(Context context, Intent intent) {
    
    
        if (SILENCE_INSTALL_APP.equals(intent.getAction())) {
    
    
            Uri uri = intent.getParcelableExtra(APP_URI_KEY);
            boolean isLaunch = intent.getBooleanExtra(IS_LAUNCH_KEY, false);
            SilenceInstallManager.getInstance(context).silenceInstall(uri.getPath(), isLaunch);
        }

    }

}

  1. InstallStart是PackageInstaller程序安装app的入口activity,修改InstallStart,添加静默安装逻辑分支,路径为 /frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java;
//********省略代码******

  protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    

        //********省略代码******
        if (isSessionInstall) {
    
    
            nextActivity.setClass(this, PackageInstallerActivity.class);
        } else {
    
    
            Uri packageUri = intent.getData();

            if (packageUri != null && packageUri.getScheme().equals(
                    ContentResolver.SCHEME_CONTENT)) {
    
    
                // [IMPORTANT] This path is deprecated, but should still work. Only necessary
                // features should be added.

                // Copy file to prevent it from being changed underneath this process
                nextActivity.setClass(this, InstallStaging.class);
            } else if (packageUri != null && packageUri.getScheme().equals(
                    PackageInstallerActivity.SCHEME_PACKAGE)) {
    
    
                nextActivity.setClass(this, PackageInstallerActivity.class);
            } else {
    
    
                Intent result = new Intent();
                result.putExtra(Intent.EXTRA_INSTALL_RESULT,
                        PackageManager.INSTALL_FAILED_INVALID_URI);
                setResult(RESULT_FIRST_USER, result);

                nextActivity = null;
            }
        }

       //安装请求中如果带静默安装标识,执行静默安装操作
        if (intent.getBooleanExtra(SilenceInstallReceiver.SILENCE_INSTALL_KEY, false)) {
    
    
            StagingAsyncAppTask mStagingTask = new StagingAsyncAppTask(intent.getBooleanExtra(SilenceInstallReceiver.IS_LAUNCH_KEY, false));
            mStagingTask.execute(getIntent().getData());
            return;
        }

        if (nextActivity != null) {
    
    
            startActivity(nextActivity);
        }
        finish();
}


    @SuppressLint("NewApi")
    private final class StagingAsyncAppTask extends AsyncTask<Uri, Void, File> {
    
    

        private boolean mIsLaunch;

        public StagingAsyncAppTask(boolean isLaunch){
    
    
            mIsLaunch = isLaunch;
        }


        @Override
        protected File doInBackground(Uri... params) {
    
    
            Log.d(LOG_TAG, "copy file from user app start");
            if (params == null || params.length <= 0) {
    
    
                return null;
            }
            Uri packageUri = params[0];
            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
    
    
                // Despite the comments in ContentResolver#openInputStream the returned stream can
                // be null.
                if (in == null) {
    
    
                    return null;
                }

                File mStagedFile = TemporaryFileManager.getStagedFile(InstallStart.this);

                try (OutputStream out = new FileOutputStream(mStagedFile)) {
    
    
                    byte[] buffer = new byte[1024 * 1024];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) >= 0) {
    
    
                        // Be nice and respond to a cancellation
                        out.write(buffer, 0, bytesRead);
                    }
                }
                return mStagedFile;
            } catch (IOException | SecurityException | IllegalStateException e) {
    
    
                Log.w(LOG_TAG, "Error staging apk from content URI", e);
            }
            return null;
        }

        @Override
        protected void onPostExecute(File installFile) {
    
    
            if (null != installFile) {
    
    
                // Now start the installation again from a file
                Log.d(LOG_TAG, "copy file from user app finish");

                Intent installIntent = new Intent(SilenceInstallReceiver.SILENCE_INSTALL_APP);
                installIntent.putExtra(SilenceInstallReceiver.APP_URI_KEY, Uri.fromFile(installFile));
                installIntent.putExtra(SilenceInstallReceiver.IS_LAUNCH_KEY, mIsLaunch);
                installIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                installIntent.setPackage("com.android.packageinstaller");
                sendBroadcast(installIntent);

                Log.d(LOG_TAG, "send to install");
            } else {
    
    
                Log.d(LOG_TAG, "copy file from user app fail");
            }

            finish();
        }
    }

//********省略代码******
  1. UninstallerActivity是PackageInstaller程序卸载app的入口activity,修改UninstallerActivity,添加静默卸载逻辑分支,路径为 /frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java;

//********省略代码******
public void onCreate(Bundle icicle){
    
    
         //********省略代码******
        
        //添加静默卸载逻辑
        if (intent.getBooleanExtra(SilenceInstallReceiver.SILENCE_INSTALL_KEY, false)) {
    
    
            SilenceInstallManager.getInstance(this).silenceUninstall(mPackageName);
            return;
        }

        showConfirmationDialog();
}

//********省略代码******
  1. 修改PackageInstallerApplication,路径为 /frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerApplication.java;
//********省略代码******

public class PackageInstallerApplication extends Application {
    
    
    @Override
    public void onCreate() {
    
    
        super.onCreate();
        PackageItemInfo.forceSafeLabels();

		//添加管理类初始化
        SilenceInstallManager.getInstance(this);
    }
}

//********省略代码******
  1. 修改AndroidManifest.xml,路径为 /frameworks/base/packages/PackageInstaller/AndroidManifest.xml;
//********省略代码******

//添加后台启动activity权限
<uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />

//********省略代码******

<receiver android:name=".SilenceInstallReceiver"
            android:exported="false">
            <intent-filter android:priority="1">
                <action android:name="com.android.packageinstaller.ACTION_SILENCE_INSTALL" />
            </intent-filter>
        </receiver>

//********省略代码******
  1. Android10开始,系统限制在没有用户交互的情况下在后台启动Activity,会输出如下信息

Background activity start [callingPackage: com.android.packageinstaller; callingUid: 10069; isCallingUidForeground: false; callingUidHasAnyVisibleWindow: false; callingUidProcState: CACHED_EMPTY; isCallingUidPersistentSystemProcess: false; realCallingUid: 10069; isRealCallingUidForeground: false; realCallingUidHasAnyVisibleWindow: false; realCallingUidProcState: CACHED_EMPTY; isRealCallingUidPersistentSystemProcess: false; originatingPendingIntent: null; isBgStartWhitelisted: false; intent: Intent {
    
     act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.qiyi.video cmp=com.qiyi.video/.WelcomeActivity }; callerApp: ProcessRecord{
    
    ba2b2ca 2471:com.android.packageinstaller/u0a69}]

Abort background activity starts from 10069

当开发的App切换到后台后,就无法进行应用安装请求,ActivityStarter.java中的shouldAbortBackgroundActivityStart()方法在判断是否能够启动activity,在这里添加修改逻辑处理,路径为 /frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java;

//********省略代码******

boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
            final String callingPackage, int realCallingUid, int realCallingPid,
            WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
            boolean allowBackgroundActivityStart, Intent intent) {
    
    

//********省略代码******

        // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
        if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
    
    
            Slog.w(TAG, "Background activity start for " + callingPackage
                    + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
            return false;
        }
        
        //添加判断逻辑,通过allowed_Background标识决定是能够启动
        if (intent.getBooleanExtra("allowed_Background", false)) {
    
    
            Slog.w(TAG, "allowed_Background.");
            return false;
        }
    
       //********省略代码******
}

//********省略代码******

第三方App请求静默安装

  1. 在开发的app中使用下面方法请求安装

    private void installTest() {
    
    
        String appPath = getExternalFilesDir(null).getAbsolutePath() + File.separator + "aiqiyi.apk";

        File appFile = new File(appPath);
        if (!appFile.exists()) {
    
    
            showToast("请在" + getExternalFilesDir(null).getAbsolutePath() + File.separator + "目录中放置升级文件");
            return;
        }

        Uri uri = FileProvider.getUriForFile(getApplicationContext(), getPackageName() + ".file_provider", appFile);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
            Intent installApkIntent = new Intent();
            installApkIntent.setAction(Intent.ACTION_VIEW);
            installApkIntent.addCategory(Intent.CATEGORY_DEFAULT);
            installApkIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            installApkIntent.setDataAndType(uri, "application/vnd.android.package-archive");
            installApkIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            //设置静默安装标识
            installApkIntent.putExtra("silence_install", true);
            //设置安装完成是否启动标识
            installApkIntent.putExtra("is_launch", true);
            //设置后台中启动activity标识
            installApkIntent.putExtra("allowed_Background", true);
            if (getPackageManager().queryIntentActivities(installApkIntent, 0).size() > 0) {
    
    
                startActivity(installApkIntent);
            }
        }
    }

猜你喜欢

转载自blog.csdn.net/yjz_0314/article/details/132605391