静默安装并启动App以及静默卸载
本文描述Android11中通过修改系统PackageInstaller应用支持静默安装App,并启动安装的App。
修改PackageInstaller
PackageInstaller是系统中专门负责app安装的App,静默安装逻辑添加到此应用中,应用所在路径 /frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/文章来源:https://www.toymoban.com/news/detail-773259.html
- 添加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);
}
}
}
- 添加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);
}
}
}
- 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();
}
}
//********省略代码******
- 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();
}
//********省略代码******
- 修改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);
}
}
//********省略代码******
- 修改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>
//********省略代码******
- 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;文章来源地址https://www.toymoban.com/news/detail-773259.html
//********省略代码******
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请求静默安装及卸载
- 在开发的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);
}
}
}
- 在开发的app中使用下面方法请求卸载
public boolean requestSilenceUninstall(Context context, String packageName) {
if (null == packageName || "".equals(packageName)) {
return false;
}
try {
PackageInfo packinfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
String[] list = packinfo.requestedPermissions;
boolean hasPermission = false;
if (null != list) {
for (int i = 0; i < list.length; i++) {
if (Manifest.permission.QUERY_ALL_PACKAGES.equals(list[i])) {
hasPermission = true;
break;
}
}
}
if (!hasPermission) {
throw new RuntimeException("need permission " + Manifest.permission.QUERY_ALL_PACKAGES);
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
if (!checkExistForApp(context, packageName)) {
return false;
}
Intent intent = new Intent(Intent.ACTION_DELETE);
intent.setData(Uri.parse("package:" + packageName));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("allowed_Background", true);
intent.putExtra("silence_install", true);
context.startActivity(intent);
return true;
}
到了这里,关于Android11.0系统中实现静默安装并启动App以及静默卸载的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!