PackageInstaller.java revision da96e137bcc8191c584ada7b5de31eaae92f244f
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.content.pm; 18 19import android.app.PackageInstallObserver; 20import android.app.PackageUninstallObserver; 21import android.content.pm.PackageManager.NameNotFoundException; 22import android.os.Bundle; 23import android.os.FileBridge; 24import android.os.ParcelFileDescriptor; 25import android.os.RemoteException; 26import android.util.ExceptionUtils; 27 28import java.io.Closeable; 29import java.io.IOException; 30import java.io.OutputStream; 31import java.util.List; 32 33/** 34 * Offers the ability to install, upgrade, and remove applications on the 35 * device. This includes support for apps packaged either as a single 36 * "monolithic" APK, or apps packaged as multiple "split" APKs. 37 * <p> 38 * An app is delivered for installation through a 39 * {@link PackageInstaller.Session}, which any app can create. Once the session 40 * is created, the installer can stream one or more APKs into place until it 41 * decides to either commit or destroy the session. Committing may require user 42 * intervention to complete the installation. 43 * <p> 44 * Sessions can install brand new apps, upgrade existing apps, or add new splits 45 * into an existing app. 46 * <p> 47 * Apps packaged as multiple split APKs always consist of a single "base" APK 48 * (with a {@code null} split name) and zero or more "split" APKs (with unique 49 * split names). Any subset of these APKs can be installed together, as long as 50 * the following constraints are met: 51 * <ul> 52 * <li>All APKs must have the exact same package name, version code, and signing 53 * certificates. 54 * <li>All APKs must have unique split names. 55 * <li>All installations must contain a single base APK. 56 * </ul> 57 */ 58public class PackageInstaller { 59 private final PackageManager mPm; 60 private final IPackageInstaller mInstaller; 61 private final int mUserId; 62 private final String mInstallerPackageName; 63 64 /** {@hide} */ 65 public PackageInstaller(PackageManager pm, IPackageInstaller installer, 66 String installerPackageName, int userId) { 67 mPm = pm; 68 mInstaller = installer; 69 mInstallerPackageName = installerPackageName; 70 mUserId = userId; 71 } 72 73 /** 74 * Quickly test if the given package is already available on the device. 75 * This is typically used in multi-user scenarios where another user on the 76 * device has already installed the package. 77 * 78 * @hide 79 */ 80 public boolean isPackageAvailable(String packageName) { 81 return mPm.isPackageAvailable(packageName); 82 } 83 84 /** {@hide} */ 85 public void installAvailablePackage(String packageName, PackageInstallObserver observer) { 86 int returnCode; 87 try { 88 returnCode = mPm.installExistingPackage(packageName); 89 } catch (NameNotFoundException e) { 90 returnCode = PackageManager.INSTALL_FAILED_PACKAGE_CHANGED; 91 } 92 observer.packageInstalled(packageName, null, returnCode); 93 } 94 95 /** 96 * Create a new session using the given parameters, returning a unique ID 97 * that represents the session. Once created, the session can be opened 98 * multiple times across multiple device boots. 99 * <p> 100 * The system may automatically destroy sessions that have not been 101 * finalized (either committed or abandoned) within a reasonable period of 102 * time, typically on the order of a day. 103 * 104 * @throws IOException if parameters were unsatisfiable, such as lack of 105 * disk space or unavailable media. 106 */ 107 public int createSession(InstallSessionParams params) throws IOException { 108 try { 109 return mInstaller.createSession(mInstallerPackageName, params, mUserId); 110 } catch (RuntimeException e) { 111 ExceptionUtils.maybeUnwrapIOException(e); 112 throw e; 113 } catch (RemoteException e) { 114 throw e.rethrowAsRuntimeException(); 115 } 116 } 117 118 /** 119 * Open an existing session to actively perform work. 120 */ 121 public Session openSession(int sessionId) { 122 try { 123 return new Session(mInstaller.openSession(sessionId)); 124 } catch (RemoteException e) { 125 throw e.rethrowAsRuntimeException(); 126 } 127 } 128 129 /** 130 * Return list of all active install sessions on the device. 131 */ 132 public List<InstallSessionInfo> getActiveSessions() { 133 // TODO: filter based on caller 134 // TODO: let launcher app see all active sessions 135 try { 136 return mInstaller.getSessions(mUserId); 137 } catch (RemoteException e) { 138 throw e.rethrowAsRuntimeException(); 139 } 140 } 141 142 /** 143 * Uninstall the given package, removing it completely from the device. This 144 * method is only available to the current "installer of record" for the 145 * package. 146 */ 147 public void uninstall(String packageName, UninstallResultCallback callback) { 148 try { 149 mInstaller.uninstall(packageName, 0, 150 new UninstallResultCallbackDelegate(callback).getBinder(), mUserId); 151 } catch (RemoteException e) { 152 throw e.rethrowAsRuntimeException(); 153 } 154 } 155 156 /** 157 * Uninstall only a specific split from the given package. 158 * 159 * @hide 160 */ 161 public void uninstall(String packageName, String splitName, UninstallResultCallback callback) { 162 try { 163 mInstaller.uninstallSplit(packageName, splitName, 0, 164 new UninstallResultCallbackDelegate(callback).getBinder(), mUserId); 165 } catch (RemoteException e) { 166 throw e.rethrowAsRuntimeException(); 167 } 168 } 169 170 /** 171 * Events for observing session lifecycle. 172 */ 173 public static abstract class SessionObserver { 174 private final IPackageInstallerObserver.Stub mBinder = new IPackageInstallerObserver.Stub() { 175 @Override 176 public void onSessionCreated(InstallSessionInfo info) { 177 SessionObserver.this.onCreated(info); 178 } 179 180 @Override 181 public void onSessionProgress(int sessionId, int progress) { 182 SessionObserver.this.onProgress(sessionId, progress); 183 } 184 185 @Override 186 public void onSessionFinished(int sessionId, boolean success) { 187 SessionObserver.this.onFinalized(sessionId, success); 188 } 189 }; 190 191 /** {@hide} */ 192 public IPackageInstallerObserver getBinder() { 193 return mBinder; 194 } 195 196 /** 197 * New session has been created. 198 */ 199 public abstract void onCreated(InstallSessionInfo info); 200 201 /** 202 * Progress for given session has been updated. 203 * <p> 204 * Note that this progress may not directly correspond to the value 205 * reported by {@link PackageInstaller.Session#setProgress(int)}, as the 206 * system may carve out a portion of the overall progress to represent 207 * its own internal installation work. 208 */ 209 public abstract void onProgress(int sessionId, int progress); 210 211 /** 212 * Session has been finalized, either with success or failure. 213 */ 214 public abstract void onFinalized(int sessionId, boolean success); 215 } 216 217 /** 218 * Register to watch for session lifecycle events. 219 */ 220 public void registerSessionObserver(SessionObserver observer) { 221 try { 222 mInstaller.registerObserver(observer.getBinder(), mUserId); 223 } catch (RemoteException e) { 224 throw e.rethrowAsRuntimeException(); 225 } 226 } 227 228 /** 229 * Unregister an existing observer. 230 */ 231 public void unregisterSessionObserver(SessionObserver observer) { 232 try { 233 mInstaller.unregisterObserver(observer.getBinder(), mUserId); 234 } catch (RemoteException e) { 235 throw e.rethrowAsRuntimeException(); 236 } 237 } 238 239 /** 240 * An installation that is being actively staged. For an install to succeed, 241 * all existing and new packages must have identical package names, version 242 * codes, and signing certificates. 243 * <p> 244 * A session may contain any number of split packages. If the application 245 * does not yet exist, this session must include a base package. 246 * <p> 247 * If a package included in this session is already defined by the existing 248 * installation (for example, the same split name), the package in this 249 * session will replace the existing package. 250 */ 251 public static class Session implements Closeable { 252 private IPackageInstallerSession mSession; 253 254 /** {@hide} */ 255 public Session(IPackageInstallerSession session) { 256 mSession = session; 257 } 258 259 /** 260 * Set current progress. Valid values are anywhere between 0 and 261 * {@link InstallSessionParams#setProgressMax(int)}. 262 */ 263 public void setProgress(int progress) { 264 try { 265 mSession.setClientProgress(progress); 266 } catch (RemoteException e) { 267 throw e.rethrowAsRuntimeException(); 268 } 269 } 270 271 /** {@hide} */ 272 public void addProgress(int progress) { 273 try { 274 mSession.addClientProgress(progress); 275 } catch (RemoteException e) { 276 throw e.rethrowAsRuntimeException(); 277 } 278 } 279 280 /** 281 * Open an APK file for writing, starting at the given offset. You can 282 * then stream data into the file, periodically calling 283 * {@link #fsync(OutputStream)} to ensure bytes have been written to 284 * disk. 285 */ 286 public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes) 287 throws IOException { 288 try { 289 final ParcelFileDescriptor clientSocket = mSession.openWrite(splitName, 290 offsetBytes, lengthBytes); 291 return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor()); 292 } catch (RuntimeException e) { 293 ExceptionUtils.maybeUnwrapIOException(e); 294 throw e; 295 } catch (RemoteException e) { 296 throw new IOException(e); 297 } 298 } 299 300 /** 301 * Ensure that any outstanding data for given stream has been committed 302 * to disk. This is only valid for streams returned from 303 * {@link #openWrite(String, long, long)}. 304 */ 305 public void fsync(OutputStream out) throws IOException { 306 if (out instanceof FileBridge.FileBridgeOutputStream) { 307 ((FileBridge.FileBridgeOutputStream) out).fsync(); 308 } else { 309 throw new IllegalArgumentException("Unrecognized stream"); 310 } 311 } 312 313 /** 314 * Attempt to commit everything staged in this session. This may require 315 * user intervention, and so it may not happen immediately. The final 316 * result of the commit will be reported through the given callback. 317 * <p> 318 * Once this method is called, no additional mutations may be performed 319 * on the session. If the device reboots before the session has been 320 * finalized, you may commit the session again. 321 */ 322 public void commit(CommitResultCallback callback) { 323 try { 324 mSession.install(new CommitResultCallbackDelegate(callback).getBinder()); 325 } catch (RemoteException e) { 326 throw e.rethrowAsRuntimeException(); 327 } 328 } 329 330 /** 331 * Release this session object. You can open the session again if it 332 * hasn't been finalized. 333 */ 334 @Override 335 public void close() { 336 // No resources to release at the moment 337 } 338 339 /** 340 * Completely destroy this session, rendering it invalid. 341 */ 342 public void destroy() { 343 try { 344 mSession.destroy(); 345 } catch (RemoteException e) { 346 throw e.rethrowAsRuntimeException(); 347 } 348 } 349 } 350 351 /** 352 * Final result of an uninstall request. 353 */ 354 public static abstract class UninstallResultCallback { 355 public abstract void onSuccess(); 356 public abstract void onFailure(String msg); 357 } 358 359 /** {@hide} */ 360 private static class UninstallResultCallbackDelegate extends PackageUninstallObserver { 361 private final UninstallResultCallback target; 362 363 public UninstallResultCallbackDelegate(UninstallResultCallback target) { 364 this.target = target; 365 } 366 367 @Override 368 public void onUninstallFinished(String basePackageName, int returnCode) { 369 final String msg = null; 370 371 switch (returnCode) { 372 case PackageManager.DELETE_SUCCEEDED: target.onSuccess(); break; 373 case PackageManager.DELETE_FAILED_INTERNAL_ERROR: target.onFailure("DELETE_FAILED_INTERNAL_ERROR: " + msg); break; 374 case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER: target.onFailure("DELETE_FAILED_DEVICE_POLICY_MANAGER: " + msg); break; 375 case PackageManager.DELETE_FAILED_USER_RESTRICTED: target.onFailure("DELETE_FAILED_USER_RESTRICTED: " + msg); break; 376 case PackageManager.DELETE_FAILED_OWNER_BLOCKED: target.onFailure("DELETE_FAILED_OWNER_BLOCKED: " + msg); break; 377 default: target.onFailure(msg); break; 378 } 379 } 380 } 381 382 /** 383 * Final result of a session commit request. 384 */ 385 public static abstract class CommitResultCallback { 386 public abstract void onSuccess(); 387 388 /** 389 * Generic failure occurred. You can override methods (such as 390 * {@link #onFailureInvalid(String)}) to handle more specific categories 391 * of failure. By default, those specific categories all flow into this 392 * generic failure. 393 */ 394 public abstract void onFailure(String msg); 395 396 /** 397 * One or more of the APKs included in the session was invalid. For 398 * example, they might be malformed, corrupt, incorrectly signed, 399 * mismatched, etc. The installer may want to try downloading and 400 * installing again. 401 */ 402 public void onFailureInvalid(String msg) { 403 onFailure(msg); 404 } 405 406 /** 407 * This install session conflicts (or is inconsistent with) with another 408 * package already installed on the device. For example, an existing 409 * permission, incompatible certificates, etc. The user may be able to 410 * uninstall another app to fix the issue. 411 * 412 * @param otherPackageName if one specific package was identified as the 413 * cause of the conflict, it's named here. If unknown, or 414 * multiple packages, this may be {@code null}. 415 */ 416 public void onFailureConflict(String msg, String otherPackageName) { 417 onFailure(msg); 418 } 419 420 /** 421 * This install session failed due to storage issues. For example, 422 * the device may be running low on space, or the required external 423 * media may be unavailable. The user may be able to help free space 424 * or insert the correct media. 425 */ 426 public void onFailureStorage(String msg) { 427 onFailure(msg); 428 } 429 430 /** 431 * This install session is fundamentally incompatible with this 432 * device. For example, the package may require a hardware feature 433 * that doesn't exist, it may be missing native code for the device 434 * ABI, or it requires a newer SDK version, etc. This install would 435 * never succeed. 436 */ 437 public void onFailureIncompatible(String msg) { 438 onFailure(msg); 439 } 440 } 441 442 /** {@hide} */ 443 private static class CommitResultCallbackDelegate extends PackageInstallObserver { 444 private final CommitResultCallback target; 445 446 public CommitResultCallbackDelegate(CommitResultCallback target) { 447 this.target = target; 448 } 449 450 @Override 451 public void packageInstalled(String basePackageName, Bundle extras, int returnCode, 452 String msg) { 453 final String otherPackage = null; 454 455 switch (returnCode) { 456 case PackageManager.INSTALL_SUCCEEDED: target.onSuccess(); break; 457 case PackageManager.INSTALL_FAILED_ALREADY_EXISTS: target.onFailureConflict("INSTALL_FAILED_ALREADY_EXISTS: " + msg, otherPackage); break; 458 case PackageManager.INSTALL_FAILED_INVALID_APK: target.onFailureInvalid("INSTALL_FAILED_INVALID_APK: " + msg); break; 459 case PackageManager.INSTALL_FAILED_INVALID_URI: target.onFailureInvalid("INSTALL_FAILED_INVALID_URI: " + msg); break; 460 case PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE: target.onFailureStorage("INSTALL_FAILED_INSUFFICIENT_STORAGE: " + msg); break; 461 case PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE: target.onFailureConflict("INSTALL_FAILED_DUPLICATE_PACKAGE: " + msg, otherPackage); break; 462 case PackageManager.INSTALL_FAILED_NO_SHARED_USER: target.onFailureConflict("INSTALL_FAILED_NO_SHARED_USER: " + msg, otherPackage); break; 463 case PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE: target.onFailureConflict("INSTALL_FAILED_UPDATE_INCOMPATIBLE: " + msg, otherPackage); break; 464 case PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: target.onFailureConflict("INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: " + msg, otherPackage); break; 465 case PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY: target.onFailureIncompatible("INSTALL_FAILED_MISSING_SHARED_LIBRARY: " + msg); break; 466 case PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE: target.onFailureConflict("INSTALL_FAILED_REPLACE_COULDNT_DELETE: " + msg, otherPackage); break; 467 case PackageManager.INSTALL_FAILED_DEXOPT: target.onFailureInvalid("INSTALL_FAILED_DEXOPT: " + msg); break; 468 case PackageManager.INSTALL_FAILED_OLDER_SDK: target.onFailureIncompatible("INSTALL_FAILED_OLDER_SDK: " + msg); break; 469 case PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER: target.onFailureConflict("INSTALL_FAILED_CONFLICTING_PROVIDER: " + msg, otherPackage); break; 470 case PackageManager.INSTALL_FAILED_NEWER_SDK: target.onFailureIncompatible("INSTALL_FAILED_NEWER_SDK: " + msg); break; 471 case PackageManager.INSTALL_FAILED_TEST_ONLY: target.onFailureInvalid("INSTALL_FAILED_TEST_ONLY: " + msg); break; 472 case PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: target.onFailureIncompatible("INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: " + msg); break; 473 case PackageManager.INSTALL_FAILED_MISSING_FEATURE: target.onFailureIncompatible("INSTALL_FAILED_MISSING_FEATURE: " + msg); break; 474 case PackageManager.INSTALL_FAILED_CONTAINER_ERROR: target.onFailureStorage("INSTALL_FAILED_CONTAINER_ERROR: " + msg); break; 475 case PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION: target.onFailureStorage("INSTALL_FAILED_INVALID_INSTALL_LOCATION: " + msg); break; 476 case PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE: target.onFailureStorage("INSTALL_FAILED_MEDIA_UNAVAILABLE: " + msg); break; 477 case PackageManager.INSTALL_FAILED_VERIFICATION_TIMEOUT: target.onFailure("INSTALL_FAILED_VERIFICATION_TIMEOUT: " + msg); break; 478 case PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE: target.onFailure("INSTALL_FAILED_VERIFICATION_FAILURE: " + msg); break; 479 case PackageManager.INSTALL_FAILED_PACKAGE_CHANGED: target.onFailureInvalid("INSTALL_FAILED_PACKAGE_CHANGED: " + msg); break; 480 case PackageManager.INSTALL_FAILED_UID_CHANGED: target.onFailureInvalid("INSTALL_FAILED_UID_CHANGED: " + msg); break; 481 case PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE: target.onFailureInvalid("INSTALL_FAILED_VERSION_DOWNGRADE: " + msg); break; 482 case PackageManager.INSTALL_PARSE_FAILED_NOT_APK: target.onFailureInvalid("INSTALL_PARSE_FAILED_NOT_APK: " + msg); break; 483 case PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST: target.onFailureInvalid("INSTALL_PARSE_FAILED_BAD_MANIFEST: " + msg); break; 484 case PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: target.onFailureInvalid("INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: " + msg); break; 485 case PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES: target.onFailureInvalid("INSTALL_PARSE_FAILED_NO_CERTIFICATES: " + msg); break; 486 case PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: target.onFailureInvalid("INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: " + msg); break; 487 case PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: target.onFailureInvalid("INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: " + msg); break; 488 case PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: target.onFailureInvalid("INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: " + msg); break; 489 case PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: target.onFailureInvalid("INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: " + msg); break; 490 case PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: target.onFailureInvalid("INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: " + msg); break; 491 case PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY: target.onFailureInvalid("INSTALL_PARSE_FAILED_MANIFEST_EMPTY: " + msg); break; 492 case PackageManager.INSTALL_FAILED_INTERNAL_ERROR: target.onFailure("INSTALL_FAILED_INTERNAL_ERROR: " + msg); break; 493 case PackageManager.INSTALL_FAILED_USER_RESTRICTED: target.onFailureIncompatible("INSTALL_FAILED_USER_RESTRICTED: " + msg); break; 494 case PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION: target.onFailureConflict("INSTALL_FAILED_DUPLICATE_PERMISSION: " + msg, otherPackage); break; 495 case PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS: target.onFailureInvalid("INSTALL_FAILED_NO_MATCHING_ABIS: " + msg); break; 496 default: target.onFailure(msg); break; 497 } 498 } 499 } 500} 501