Android 9.0 静默安装实现
2020-05-06 本文已影响0人
红色晨光的烂漫
一、需求
最近需要对一个旧项目的SDK做版本Android 9.0的版本适配。其中有个静默安装的API在Android 5.1上可以正常运行但是在Android 9.0上出现了很多问题。
-
问题1:
旧版本上引用了IPackageInstallObserver作为应用回调接口,然后在Android9.0上该回调接口已经被IPackageInstallObserver2替代。事实上我们可以自定义回调接口并不需要使用系统定义的IPackageInstallObserver。 -
问题2:
在Android 5.1中安装应用直接调用PackageManager&installPackage即可实现,然而从Android7.0(api24)开始installPackage方法已经被PackageManager&installPacakageAsUser替换,而到了Android 9.0(api28)整改安装机制都发生变化。Android 9.0之后并没有提供一个单一的api来实现应用的安装installPacakageAsUser也已经不存在了。
二、原理
- sdk_api<api24:直接调用PackageManager$installPackage实现应用安装。
public static void installPackage(Context ctx, String filePath, IPackageInstallObserver observer)
throws RemoteException {
PackageManager mPm = ctx.getPackageManager();
Uri packageURI = Uri.fromFile(new File(filePath));
mPm.installPackage(packageURI, observer, PackageManager.INSTALL_REPLACE_EXISTING, null);
}
- sdk_api>=api24:直接调用PackageManager&installPacakageAsUser实现应用安装
public static void installPackage(Context mContext, String filePath, IPackageInstallObserver2 observer)
throws RemoteException {
PackageManager mPm = ctx.getPackageManager();
File apkFile = new File(filePath)
mPm.installPackageAsUser(Uri.fromFile(apkFile).getPath(), observer, 2, apkFile.getName(), mContext.getUserId());
}
- sdk_api>=28:Android 9.0以后使用PackageInstaller进行应用安装。
通过PackageInstaller.Session将APK数据传递给PackageManagerService(PMS),PMS接收数据进行安装,安装完后将安装结果通过广播发送到上层应用。
三、实现
第一步:注册安装结果的广播:
public class SilencePackageHandleService extends ILakalaPackageHandler.Stub{
private static final String BROADCAST_SENDER_PERMISSION = "android.permission.INSTALL_PACKAGES";
// 声明广播接收器
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final int statusCode = intent.getIntExtra(
PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
Log.d(TAG, "install status code:" + statusCode);
try {
String packageName = null;
if (pkg != null) {
packageName = pkg.packageName;
}
// 回调安装结果,statusCode == 0:安装成功,否正安装失败
observer2.onPackageInstalled(packageName, statusCode, null, null);
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
public SilencePackageHandleService(Context context) {
ServiceManager.addService("lakala_packageHandler", this.asBinder());
...
// 注册广播
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.install");
mContext.registerReceiver(mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null);
}
}
第二步:创建PackageInstaller.SessionParams
private void startInstall(String filePath) throws RemoteException {
...
final PackageManager pm = mContext.getPackageManager();
mPackageURI = Uri.fromFile(new File(filePath));
final File sourceFile = new File(filePath);
Log.d(TAG, "zxc startInstall: sourceFile:" + filePath);
final PackageInstaller.SessionParams params = new PackageInstaller
.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.originatingUid = UID_UNKNOWN;
try {
params.setInstallLocation(PackageParser.parsePackageLite(sourceFile, 0).installLocation);
pkg = PackageParser.parsePackageLite(sourceFile, 0);
} catch (PackageParser.PackageParserException e) {
Log.e(TAG, "zxc startInstall: error...");
e.printStackTrace();
observer2.onPackageInstalled(null, PackageInstaller.STATUS_FAILURE, null, null);
return;
}
// 开启线程传递apk
mInstallHandler.post(new Runnable() {
@Override
public void run() {
try {
doInstall(pm, params);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
第三步:传递apk数据
private void doInstall(PackageManager pm, PackageInstaller.SessionParams params) throws RemoteException {
PackageInstaller packageInstaller = pm.getPackageInstaller();
int sessionId = 0;
String packageLocation = mPackageURI.getPath();
File file = new File(packageLocation);
byte[] buffer = new byte[65536];
InputStream in = null;
OutputStream out = null;
try {
sessionId = packageInstaller.createSession(params);
session = packageInstaller.openSession(sessionId);
in = new FileInputStream(file);
long sizeBytes = file.length();
out = session.openWrite("PackageInstaller", 0, sizeBytes);
session.setStagingProgress(0);
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);
out.close(); // session.commit之前一定要关闭数据流否则会报错
in.close();
out = null;
in = null;
Intent broadcastIntent = new Intent("com.install"); // 新建intent用于安装结束发送广播
PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext,
sessionId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(pendingIntent.getIntentSender()); // 提交数据 完成安装
} catch (IOException e) {
e.printStackTrace();
observer2.onPackageInstalled(pkg.packageName, PackageInstaller.STATUS_FAILURE, null, null);
} catch (Exception e) {
e.printStackTrace();
observer2.onPackageInstalled(pkg.packageName, PackageInstaller.STATUS_FAILURE, null, null);
} finally {
try {
if (in != null) in.close();
if (out != null) out.close();
} catch (IOException e) {
e.printStackTrace();
}
session.close();
}
}