Android静默安装的两种方案
一些产品要求APP在升级时能够实现静默安装,而无需弹出安装界面让用户确认。这里提出两种实现方案:
方案一:通过pm命令安装
APP调用『pm』命令实现静默安装,此方案无须修改Android源码,但需要root权限。实现如下:
/**
* Silent install
*
* @param path Package
* @return true: success false: failed
*/
public static boolean installSilent(String path) {
boolean result = false;
BufferedReader es = null;
DataOutputStream os = null;
try {
Process process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
String command = "pm install -r " path "\n";
os.write(command.getBytes(Charset.forName("utf-8")));
os.flush();
os.writeBytes("exit\n");
os.flush();
process.waitFor();
es = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line;
StringBuilder builder = new StringBuilder();
while ((line = es.readLine()) != null) {
builder.append(line);
}
Log.d(TAG, "install msg is " builder.toString());
/* Installation is considered a Failure if the result contains
the Failure character, or a success if it is not.
*/
if (!builder.toString().contains("Failure")) {
result = true;
}
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
} finally {
try {
if (os != null) {
os.close();
}
if (es != null) {
es.close();
}
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
}
}
return result;
}
方案二 修改PackageInstaller源码
如果没有root权限,方案一将无法实现,因此我们通过定制 PackageInstaller 来实现指定包名可以静默安装,并增加Intent参数来指定静默安装还是默认安装。具体修改如下:
diff --git a/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/apps/Pac
old mode 100644
new mode 100755
index 12441b5..cbf8c41
--- a/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
b/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -22,17 22,30 @@ import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.IActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.ProviderInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.content.pm.IPackageInstallObserver;
import android.support.v4.content.FileProvider;
import java.io.File;
import java.lang.reflect.Method;
import java.util.List;
import com.android.internal.annotations.VisibleForTesting;
@@ -43,6 56,8 @@ import com.android.internal.annotations.VisibleForTesting;
public class InstallStart extends Activity {
private static final String LOG_TAG = InstallStart.class.getSimpleName();
private static final String EXTRA_SILENT_INSTALL = "silent_install";
private static final String DOWNLOADS_AUTHORITY = "downloads";
private IActivityManager mIActivityManager;
private IPackageManager mIPackageManager;
@@ -91,40 106,57 @@ public class InstallStart extends Activity {
return;
}
- Intent nextActivity = new Intent(intent);
- nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
-
- // The the installation source as the nextActivity thinks this activity is the source, hence
- // set the originating UID and sourceInfo explicitly
- nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
- nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
- nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
Uri pkgUri = intent.getData();
String path = "";
if (pkgUri != null) {
if (pkgUri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
path = pkgUri.getPath();
} else if (pkgUri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
path = providerUri2Path(this, pkgUri);
}
}
- if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
- nextActivity.setClass(this, PackageInstallerActivity.class);
if (isSilentInstall(intent, path)) {
Log.i(LOG_TAG, "silent install path: " path);
getPackageManager().installPackage(Uri.fromFile(new File(path)),
new PackageInstallObserver(), 2, null);
} else {
- Uri packageUri = intent.getData();
-
- if (packageUri != null && (packageUri.getScheme().equals(ContentResolver.SCHEME_FILE)
- || packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))) {
- // 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)) {
Intent nextActivity = new Intent(intent);
nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
// The the installation source as the nextActivity thinks this activity is the source, hence
// set the originating UID and sourceInfo explicitly
nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
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);
Uri packageUri = intent.getData();
if (packageUri != null && (packageUri.getScheme().equals(ContentResolver.SCHEME_FILE)
|| packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))) {
// 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;
nextActivity = null;
}
}
- }
- if (nextActivity != null) {
- startActivity(nextActivity);
if (nextActivity != null) {
startActivity(nextActivity);
}
}
finish();
}
@@ -247,4 279,94 @@ public class InstallStart extends Activity {
void injectIActivityManager(IActivityManager iActivityManager) {
mIActivityManager = iActivityManager;
}
private static String providerUri2Path(Context context, Uri uri) {
Log.i(LOG_TAG, "providerUri2Path, uri: " uri.toString());
try {
List<PackageInfo> packs = context.getPackageManager()
.getInstalledPackages(PackageManager.GET_PROVIDERS);
if (packs != null) {
for (PackageInfo pack : packs) {
ProviderInfo[] providers = pack.providers;
if (providers != null) {
for (ProviderInfo provider : providers) {
if (provider.authority.equals(uri.getAuthority())) {
Class<FileProvider> fileProviderClass = FileProvider.class;
try {
Method getPathStrategy = fileProviderClass.getDeclaredMethod(
"getPathStrategy", Context.class, String.class);
getPathStrategy.setAccessible(true);
Object invoke = getPathStrategy.invoke(null, context, uri.getAuthority());
if (invoke != null) {
String PathStrategyStringClass = FileProvider.class.getName() "$PathStr
Class<?> PathStrategy = Class.forName(PathStrategyStringClass);
Method getFileForUri = PathStrategy.getDeclaredMethod("getFileForUri", Ur
getFileForUri.setAccessible(true);
Object invoke1 = getFileForUri.invoke(invoke, uri);
if (invoke1 instanceof File) {
return ((File) invoke1).getAbsolutePath();
}
} else {
Log.e(LOG_TAG, "providerUri2Path, invoke is null.");
}
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage());
}
break;
}
}
}
}
} else {
Log.w(LOG_TAG, "providerUri2Path, packs is null.");
}
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage());
}
return "";
}
private boolean isSilentInstall(Intent intent, String path) {
if (!TextUtils.isEmpty(path)) {
if (intent.getBooleanExtra(EXTRA_SILENT_INSTALL, false)) {
Log.i(LOG_TAG, "isSilentInstall, Intent include EXTRA_SILENT_INSTALL.");
return true;
} else {
String value = SystemProperties.get("ro.silentinstallapps", "");
if (!TextUtils.isEmpty(value)) {
if (TextUtils.equals(value, "all")) {
Log.i(LOG_TAG, "isSilentInstall, All.");
return true;
} else {
File sourceFile = new File(path);
PackageParser.Package parsed = PackageUtil.getPackageInfo(this, sourceFile);
if (parsed != null) {
PackageInfo pkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
if (pkgInfo != null) {
if (TextUtils.equals(value, "system")) {
if (TextUtils.equals(pkgInfo.sharedUserId, "android.uid.system")) {
Log.i(LOG_TAG, "isSilentInstall, System.");
return true;
}
} else {
String[] pkgNames = value.split(",");
if (pkgNames != null && pkgNames.length > 0) {
for (String pkgName : pkgNames) {
if (TextUtils.equals(pkgName, pkgInfo.packageName)) {
Log.i(LOG_TAG, "isSilentInstall, Included in the whitelist.");
return true;
}
}
}
}
}
}
}
}
}
} else {
Log.w(LOG_TAG, "isSilentInstall, path is null.");
}
return false;
}
class PackageInstallObserver extends IPackageInstallObserver.Stub {
@Override
public void packageInstalled(String packageName, int returnCode) throws RemoteException {
Log.i(LOG_TAG, packageName " silent installed.");
}
}
}
配置指定包名走静默安装
支持通过属性配置需要静默安装的APP包名,只要是属性配置的包名就走静默安装,其它APP走默认安装。这个操作由系统端配置,APP端按Android标准API调应用安装即可。配置参考:
ro.silentinstallapps=com.ayst.sample1,com.ayst.sample1
注意 :支持同时配置多个包名,包名之间用逗号隔开。
配置全部APP走静默安装
所有APP都走静默安装。
ro.silentinstallapps=all
配置系统APP走静默安装
仅系统uid的APP走静默安装,其它APP走默认安装。
ro.silentinstallapps=system
指定Intent参数走静默安装
通过Intent参数指定是否要静默安装。使用方法如下:
intent.putExtra("silent_install", true); // 静默安装
完整参考:
private static final String EXTRA_SILENT_INSTALL = "silent_install";
public static void install(Context context, String path) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
installO(context, path);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
installN(context, path);
} else {
installOther(context, path);
}
}
/**
* android1.x-6.x
*
* @param context Context
* @param path Package
*/
private static void installOther(Context context, String path) {
Intent install = new Intent(Intent.ACTION_VIEW);
install.setDataAndType(Uri.parse("file://" path),
"application/vnd.android.package-archive");
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
install.putExtra(EXTRA_SILENT_INSTALL, true); // 静默安装
context.startActivity(install);
}
/**
* android7.x
*
* @param context Context
* @param path Package
*/
private static void installN(Context context, String path) {
Uri apkUri = FileProvider.getUriForFile(context, AUTHORITY, new File(path));
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
install.setDataAndType(apkUri, "application/vnd.android.package-archive");
install.putExtra(EXTRA_SILENT_INSTALL, true); // 静默安装
context.startActivity(install);
}
/**
* android8.x
*
* @param context Context
* @param path Package
*/
@RequiresApi(api = Build.VERSION_CODES.O)
private static void installO(Context context, String path) {
boolean isGranted = context.getPackageManager().canRequestPackageInstalls();
if (isGranted) {
installN(context, path);
} else {
Dialog dialog = new AlertDialog.Builder(context.getApplicationContext())
.setTitle("Unknown sources")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface d, int w) {
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
context.startActivity(intent);
}
}).create();
dialog.setCancelable(false);
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
dialog.show();
}
}
https://www.yuque.com/aiyinsitan-dhjkq/android-system/fngm5h
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgkhhhf
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13