A, requirements,
Recently, I need to adapt an old project SDK version of Android 9.0. There is a silent install API that works fine on Android 5.1 but has a lot of problems on Android 9.0.
- Question 1:
IPackageInstallObserver was referenced as the application callback interface on older versions, and has been replaced by IPackageInstallObserver2 on Android9.0. We can actually customize the callback interface without using the system-defined IPackageInstallObserver.
- Question 2:
Install the app in Android 5.1 by calling PackageManager&installPackage. However, the installPackage method has been replaced by Packagemanager and InstallpacakageasUser since Android7.0 (API24), and the installation mechanism has changed since Android 9.0 (API28). Android 9.0 does not provide a single API for installing applications and installPacakageAsUser no longer exists.
Second, the principle of
- Sdk_api < API24: Directly call PackageManager$installPackage to implement application installation.
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);
}
Copy the code
- Sdk_api >= API24: Directly call Packagemanager and InstallpacakageasUser to install the application
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());
}
Copy the code
- Sdk_api >=28: After Android 9.0, use PackageInstaller to install the application.
The PackageInstaller.Session sends APK data to the PackageManagerService(PMS). The PMS receives the data and broadcasts the installation result to the upper-layer application.
Three, implementation,
Step 1: Register the broadcast of installation results:
public class SilencePackageHandleService extends ILakalaPackageHandler.Stub{
private static final String BROADCAST_SENDER_PERMISSION = "android.permission.INSTALL_PACKAGES";
// Declare a broadcast receiver
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;
}
If statusCode == 0, the installation is successful. If no, the installation fails
observer2.onPackageInstalled(packageName, statusCode, null.null);
} catch(RemoteException e) { e.printStackTrace(); }}};public SilencePackageHandleService(Context context) {
ServiceManager.addService("lakala_packageHandler".this.asBinder()); .// Register a broadcast
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.install");
mContext.registerReceiver(mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null); }}Copy the code
Step 2: create 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;
}
// Enable thread passing APK
mInstallHandler.post(new Runnable() {
@Override
public void run(a) {
try {
doInstall(pm, params);
} catch(RemoteException e) { e.printStackTrace(); }}}); }Copy the code
Step 3: Pass APK data
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.com MIT must close the data stream before otherwise an error will be reported
in.close();
out = null;
in = null;
Intent broadcastIntent = new Intent("com.install"); // Create an intent to broadcast the end of the installation
PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext,
sessionId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(pendingIntent.getIntentSender()); // Submit data to complete the installation
} 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(); }}Copy the code
4. Reference links
- www.jianshu.com/p/ec642ff5f…
- www.jianshu.com/p/27766c5a9…