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 17 package android.content.pm; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.RequiresPermission; 22 import android.annotation.SdkConstant; 23 import android.annotation.SdkConstant.SdkConstantType; 24 import android.annotation.SystemApi; 25 import android.app.ActivityManager; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentSender; 29 import android.graphics.Bitmap; 30 import android.net.Uri; 31 import android.os.FileBridge; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.os.Parcel; 36 import android.os.ParcelFileDescriptor; 37 import android.os.Parcelable; 38 import android.os.RemoteException; 39 import android.util.ExceptionUtils; 40 41 import com.android.internal.util.IndentingPrintWriter; 42 43 import java.io.Closeable; 44 import java.io.IOException; 45 import java.io.InputStream; 46 import java.io.OutputStream; 47 import java.security.MessageDigest; 48 import java.util.ArrayList; 49 import java.util.Iterator; 50 import java.util.List; 51 52 /** 53 * Offers the ability to install, upgrade, and remove applications on the 54 * device. This includes support for apps packaged either as a single 55 * "monolithic" APK, or apps packaged as multiple "split" APKs. 56 * <p> 57 * An app is delivered for installation through a 58 * {@link PackageInstaller.Session}, which any app can create. Once the session 59 * is created, the installer can stream one or more APKs into place until it 60 * decides to either commit or destroy the session. Committing may require user 61 * intervention to complete the installation. 62 * <p> 63 * Sessions can install brand new apps, upgrade existing apps, or add new splits 64 * into an existing app. 65 * <p> 66 * Apps packaged as multiple split APKs always consist of a single "base" APK 67 * (with a {@code null} split name) and zero or more "split" APKs (with unique 68 * split names). Any subset of these APKs can be installed together, as long as 69 * the following constraints are met: 70 * <ul> 71 * <li>All APKs must have the exact same package name, version code, and signing 72 * certificates. 73 * <li>All APKs must have unique split names. 74 * <li>All installations must contain a single base APK. 75 * </ul> 76 */ 77 public class PackageInstaller { 78 private static final String TAG = "PackageInstaller"; 79 80 /** 81 * Activity Action: Show details about a particular install session. This 82 * may surface actions such as pause, resume, or cancel. 83 * <p> 84 * This should always be scoped to the installer package that owns the 85 * session. Clients should use {@link SessionInfo#createDetailsIntent()} to 86 * build this intent correctly. 87 * <p> 88 * In some cases, a matching Activity may not exist, so ensure you safeguard 89 * against this. 90 * <p> 91 * The session to show details for is defined in {@link #EXTRA_SESSION_ID}. 92 */ 93 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 94 public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS"; 95 96 /** {@hide} */ 97 public static final String 98 ACTION_CONFIRM_PERMISSIONS = "android.content.pm.action.CONFIRM_PERMISSIONS"; 99 100 /** 101 * An integer session ID that an operation is working with. 102 * 103 * @see Intent#getIntExtra(String, int) 104 */ 105 public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID"; 106 107 /** 108 * Package name that an operation is working with. 109 * 110 * @see Intent#getStringExtra(String) 111 */ 112 public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME"; 113 114 /** 115 * Current status of an operation. Will be one of 116 * {@link #STATUS_PENDING_USER_ACTION}, {@link #STATUS_SUCCESS}, 117 * {@link #STATUS_FAILURE}, {@link #STATUS_FAILURE_ABORTED}, 118 * {@link #STATUS_FAILURE_BLOCKED}, {@link #STATUS_FAILURE_CONFLICT}, 119 * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, or 120 * {@link #STATUS_FAILURE_STORAGE}. 121 * <p> 122 * More information about a status may be available through additional 123 * extras; see the individual status documentation for details. 124 * 125 * @see Intent#getIntExtra(String, int) 126 */ 127 public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS"; 128 129 /** 130 * Detailed string representation of the status, including raw details that 131 * are useful for debugging. 132 * 133 * @see Intent#getStringExtra(String) 134 */ 135 public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE"; 136 137 /** 138 * Another package name relevant to a status. This is typically the package 139 * responsible for causing an operation failure. 140 * 141 * @see Intent#getStringExtra(String) 142 */ 143 public static final String 144 EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME"; 145 146 /** 147 * Storage path relevant to a status. 148 * 149 * @see Intent#getStringExtra(String) 150 */ 151 public static final String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH"; 152 153 /** {@hide} */ 154 @Deprecated 155 public static final String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES"; 156 157 /** {@hide} */ 158 public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS"; 159 /** {@hide} */ 160 public static final String EXTRA_LEGACY_BUNDLE = "android.content.pm.extra.LEGACY_BUNDLE"; 161 /** {@hide} */ 162 public static final String EXTRA_CALLBACK = "android.content.pm.extra.CALLBACK"; 163 164 /** 165 * User action is currently required to proceed. You can launch the intent 166 * activity described by {@link Intent#EXTRA_INTENT} to involve the user and 167 * continue. 168 * <p> 169 * You may choose to immediately launch the intent if the user is actively 170 * using your app. Otherwise, you should use a notification to guide the 171 * user back into your app before launching. 172 * 173 * @see Intent#getParcelableExtra(String) 174 */ 175 public static final int STATUS_PENDING_USER_ACTION = -1; 176 177 /** 178 * The operation succeeded. 179 */ 180 public static final int STATUS_SUCCESS = 0; 181 182 /** 183 * The operation failed in a generic way. The system will always try to 184 * provide a more specific failure reason, but in some rare cases this may 185 * be delivered. 186 * 187 * @see #EXTRA_STATUS_MESSAGE 188 */ 189 public static final int STATUS_FAILURE = 1; 190 191 /** 192 * The operation failed because it was blocked. For example, a device policy 193 * may be blocking the operation, a package verifier may have blocked the 194 * operation, or the app may be required for core system operation. 195 * <p> 196 * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the 197 * specific package blocking the install. 198 * 199 * @see #EXTRA_STATUS_MESSAGE 200 * @see #EXTRA_OTHER_PACKAGE_NAME 201 */ 202 public static final int STATUS_FAILURE_BLOCKED = 2; 203 204 /** 205 * The operation failed because it was actively aborted. For example, the 206 * user actively declined requested permissions, or the session was 207 * abandoned. 208 * 209 * @see #EXTRA_STATUS_MESSAGE 210 */ 211 public static final int STATUS_FAILURE_ABORTED = 3; 212 213 /** 214 * The operation failed because one or more of the APKs was invalid. For 215 * example, they might be malformed, corrupt, incorrectly signed, 216 * mismatched, etc. 217 * 218 * @see #EXTRA_STATUS_MESSAGE 219 */ 220 public static final int STATUS_FAILURE_INVALID = 4; 221 222 /** 223 * The operation failed because it conflicts (or is inconsistent with) with 224 * another package already installed on the device. For example, an existing 225 * permission, incompatible certificates, etc. The user may be able to 226 * uninstall another app to fix the issue. 227 * <p> 228 * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the 229 * specific package identified as the cause of the conflict. 230 * 231 * @see #EXTRA_STATUS_MESSAGE 232 * @see #EXTRA_OTHER_PACKAGE_NAME 233 */ 234 public static final int STATUS_FAILURE_CONFLICT = 5; 235 236 /** 237 * The operation failed because of storage issues. For example, the device 238 * may be running low on space, or external media may be unavailable. The 239 * user may be able to help free space or insert different external media. 240 * <p> 241 * The result may also contain {@link #EXTRA_STORAGE_PATH} with the path to 242 * the storage device that caused the failure. 243 * 244 * @see #EXTRA_STATUS_MESSAGE 245 * @see #EXTRA_STORAGE_PATH 246 */ 247 public static final int STATUS_FAILURE_STORAGE = 6; 248 249 /** 250 * The operation failed because it is fundamentally incompatible with this 251 * device. For example, the app may require a hardware feature that doesn't 252 * exist, it may be missing native code for the ABIs supported by the 253 * device, or it requires a newer SDK version, etc. 254 * 255 * @see #EXTRA_STATUS_MESSAGE 256 */ 257 public static final int STATUS_FAILURE_INCOMPATIBLE = 7; 258 259 private final Context mContext; 260 private final PackageManager mPm; 261 private final IPackageInstaller mInstaller; 262 private final int mUserId; 263 private final String mInstallerPackageName; 264 265 private final ArrayList<SessionCallbackDelegate> mDelegates = new ArrayList<>(); 266 267 /** {@hide} */ 268 public PackageInstaller(Context context, PackageManager pm, IPackageInstaller installer, 269 String installerPackageName, int userId) { 270 mContext = context; 271 mPm = pm; 272 mInstaller = installer; 273 mInstallerPackageName = installerPackageName; 274 mUserId = userId; 275 } 276 277 /** 278 * Create a new session using the given parameters, returning a unique ID 279 * that represents the session. Once created, the session can be opened 280 * multiple times across multiple device boots. 281 * <p> 282 * The system may automatically destroy sessions that have not been 283 * finalized (either committed or abandoned) within a reasonable period of 284 * time, typically on the order of a day. 285 * 286 * @throws IOException if parameters were unsatisfiable, such as lack of 287 * disk space or unavailable media. 288 * @throws SecurityException when installation services are unavailable, 289 * such as when called from a restricted user. 290 * @throws IllegalArgumentException when {@link SessionParams} is invalid. 291 * @return positive, non-zero unique ID that represents the created session. 292 * This ID remains consistent across device reboots until the 293 * session is finalized. IDs are not reused during a given boot. 294 */ 295 public int createSession(@NonNull SessionParams params) throws IOException { 296 try { 297 return mInstaller.createSession(params, mInstallerPackageName, mUserId); 298 } catch (RuntimeException e) { 299 ExceptionUtils.maybeUnwrapIOException(e); 300 throw e; 301 } catch (RemoteException e) { 302 throw e.rethrowFromSystemServer(); 303 } 304 } 305 306 /** 307 * Open an existing session to actively perform work. To succeed, the caller 308 * must be the owner of the install session. 309 * 310 * @throws IOException if parameters were unsatisfiable, such as lack of 311 * disk space or unavailable media. 312 * @throws SecurityException when the caller does not own the session, or 313 * the session is invalid. 314 */ 315 public @NonNull Session openSession(int sessionId) throws IOException { 316 try { 317 return new Session(mInstaller.openSession(sessionId)); 318 } catch (RuntimeException e) { 319 ExceptionUtils.maybeUnwrapIOException(e); 320 throw e; 321 } catch (RemoteException e) { 322 throw e.rethrowFromSystemServer(); 323 } 324 } 325 326 /** 327 * Update the icon representing the app being installed in a specific 328 * session. This should be roughly 329 * {@link ActivityManager#getLauncherLargeIconSize()} in both dimensions. 330 * 331 * @throws SecurityException when the caller does not own the session, or 332 * the session is invalid. 333 */ 334 public void updateSessionAppIcon(int sessionId, @Nullable Bitmap appIcon) { 335 try { 336 mInstaller.updateSessionAppIcon(sessionId, appIcon); 337 } catch (RemoteException e) { 338 throw e.rethrowFromSystemServer(); 339 } 340 } 341 342 /** 343 * Update the label representing the app being installed in a specific 344 * session. 345 * 346 * @throws SecurityException when the caller does not own the session, or 347 * the session is invalid. 348 */ 349 public void updateSessionAppLabel(int sessionId, @Nullable CharSequence appLabel) { 350 try { 351 final String val = (appLabel != null) ? appLabel.toString() : null; 352 mInstaller.updateSessionAppLabel(sessionId, val); 353 } catch (RemoteException e) { 354 throw e.rethrowFromSystemServer(); 355 } 356 } 357 358 /** 359 * Completely abandon the given session, destroying all staged data and 360 * rendering it invalid. Abandoned sessions will be reported to 361 * {@link SessionCallback} listeners as failures. This is equivalent to 362 * opening the session and calling {@link Session#abandon()}. 363 * 364 * @throws SecurityException when the caller does not own the session, or 365 * the session is invalid. 366 */ 367 public void abandonSession(int sessionId) { 368 try { 369 mInstaller.abandonSession(sessionId); 370 } catch (RemoteException e) { 371 throw e.rethrowFromSystemServer(); 372 } 373 } 374 375 /** 376 * Return details for a specific session. No special permissions are 377 * required to retrieve these details. 378 * 379 * @return details for the requested session, or {@code null} if the session 380 * does not exist. 381 */ 382 public @Nullable SessionInfo getSessionInfo(int sessionId) { 383 try { 384 return mInstaller.getSessionInfo(sessionId); 385 } catch (RemoteException e) { 386 throw e.rethrowFromSystemServer(); 387 } 388 } 389 390 /** 391 * Return list of all known install sessions, regardless of the installer. 392 */ 393 public @NonNull List<SessionInfo> getAllSessions() { 394 try { 395 return mInstaller.getAllSessions(mUserId).getList(); 396 } catch (RemoteException e) { 397 throw e.rethrowFromSystemServer(); 398 } 399 } 400 401 /** 402 * Return list of all known install sessions owned by the calling app. 403 */ 404 public @NonNull List<SessionInfo> getMySessions() { 405 try { 406 return mInstaller.getMySessions(mInstallerPackageName, mUserId).getList(); 407 } catch (RemoteException e) { 408 throw e.rethrowFromSystemServer(); 409 } 410 } 411 412 /** 413 * Uninstall the given package, removing it completely from the device. This 414 * method is only available to the current "installer of record" for the 415 * package. 416 */ 417 public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) { 418 try { 419 mInstaller.uninstall(packageName, mInstallerPackageName, 0, statusReceiver, mUserId); 420 } catch (RemoteException e) { 421 throw e.rethrowFromSystemServer(); 422 } 423 } 424 425 /** {@hide} */ 426 public void setPermissionsResult(int sessionId, boolean accepted) { 427 try { 428 mInstaller.setPermissionsResult(sessionId, accepted); 429 } catch (RemoteException e) { 430 throw e.rethrowFromSystemServer(); 431 } 432 } 433 434 /** 435 * Events for observing session lifecycle. 436 * <p> 437 * A typical session lifecycle looks like this: 438 * <ul> 439 * <li>An installer creates a session to indicate pending app delivery. All 440 * install details are available at this point. 441 * <li>The installer opens the session to deliver APK data. Note that a 442 * session may be opened and closed multiple times as network connectivity 443 * changes. The installer may deliver periodic progress updates. 444 * <li>The installer commits or abandons the session, resulting in the 445 * session being finished. 446 * </ul> 447 */ 448 public static abstract class SessionCallback { 449 /** 450 * New session has been created. Details about the session can be 451 * obtained from {@link PackageInstaller#getSessionInfo(int)}. 452 */ 453 public abstract void onCreated(int sessionId); 454 455 /** 456 * Badging details for an existing session has changed. For example, the 457 * app icon or label has been updated. 458 */ 459 public abstract void onBadgingChanged(int sessionId); 460 461 /** 462 * Active state for session has been changed. 463 * <p> 464 * A session is considered active whenever there is ongoing forward 465 * progress being made, such as the installer holding an open 466 * {@link Session} instance while streaming data into place, or the 467 * system optimizing code as the result of 468 * {@link Session#commit(IntentSender)}. 469 * <p> 470 * If the installer closes the {@link Session} without committing, the 471 * session is considered inactive until the installer opens the session 472 * again. 473 */ 474 public abstract void onActiveChanged(int sessionId, boolean active); 475 476 /** 477 * Progress for given session has been updated. 478 * <p> 479 * Note that this progress may not directly correspond to the value 480 * reported by 481 * {@link PackageInstaller.Session#setStagingProgress(float)}, as the 482 * system may carve out a portion of the overall progress to represent 483 * its own internal installation work. 484 */ 485 public abstract void onProgressChanged(int sessionId, float progress); 486 487 /** 488 * Session has completely finished, either with success or failure. 489 */ 490 public abstract void onFinished(int sessionId, boolean success); 491 } 492 493 /** {@hide} */ 494 private static class SessionCallbackDelegate extends IPackageInstallerCallback.Stub implements 495 Handler.Callback { 496 private static final int MSG_SESSION_CREATED = 1; 497 private static final int MSG_SESSION_BADGING_CHANGED = 2; 498 private static final int MSG_SESSION_ACTIVE_CHANGED = 3; 499 private static final int MSG_SESSION_PROGRESS_CHANGED = 4; 500 private static final int MSG_SESSION_FINISHED = 5; 501 502 final SessionCallback mCallback; 503 final Handler mHandler; 504 505 public SessionCallbackDelegate(SessionCallback callback, Looper looper) { 506 mCallback = callback; 507 mHandler = new Handler(looper, this); 508 } 509 510 @Override 511 public boolean handleMessage(Message msg) { 512 final int sessionId = msg.arg1; 513 switch (msg.what) { 514 case MSG_SESSION_CREATED: 515 mCallback.onCreated(sessionId); 516 return true; 517 case MSG_SESSION_BADGING_CHANGED: 518 mCallback.onBadgingChanged(sessionId); 519 return true; 520 case MSG_SESSION_ACTIVE_CHANGED: 521 final boolean active = msg.arg2 != 0; 522 mCallback.onActiveChanged(sessionId, active); 523 return true; 524 case MSG_SESSION_PROGRESS_CHANGED: 525 mCallback.onProgressChanged(sessionId, (float) msg.obj); 526 return true; 527 case MSG_SESSION_FINISHED: 528 mCallback.onFinished(sessionId, msg.arg2 != 0); 529 return true; 530 } 531 return false; 532 } 533 534 @Override 535 public void onSessionCreated(int sessionId) { 536 mHandler.obtainMessage(MSG_SESSION_CREATED, sessionId, 0).sendToTarget(); 537 } 538 539 @Override 540 public void onSessionBadgingChanged(int sessionId) { 541 mHandler.obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, 0).sendToTarget(); 542 } 543 544 @Override 545 public void onSessionActiveChanged(int sessionId, boolean active) { 546 mHandler.obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, active ? 1 : 0) 547 .sendToTarget(); 548 } 549 550 @Override 551 public void onSessionProgressChanged(int sessionId, float progress) { 552 mHandler.obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, 0, progress) 553 .sendToTarget(); 554 } 555 556 @Override 557 public void onSessionFinished(int sessionId, boolean success) { 558 mHandler.obtainMessage(MSG_SESSION_FINISHED, sessionId, success ? 1 : 0) 559 .sendToTarget(); 560 } 561 } 562 563 /** {@hide} */ 564 @Deprecated 565 public void addSessionCallback(@NonNull SessionCallback callback) { 566 registerSessionCallback(callback); 567 } 568 569 /** 570 * Register to watch for session lifecycle events. No special permissions 571 * are required to watch for these events. 572 */ 573 public void registerSessionCallback(@NonNull SessionCallback callback) { 574 registerSessionCallback(callback, new Handler()); 575 } 576 577 /** {@hide} */ 578 @Deprecated 579 public void addSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) { 580 registerSessionCallback(callback, handler); 581 } 582 583 /** 584 * Register to watch for session lifecycle events. No special permissions 585 * are required to watch for these events. 586 * 587 * @param handler to dispatch callback events through, otherwise uses 588 * calling thread. 589 */ 590 public void registerSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) { 591 synchronized (mDelegates) { 592 final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback, 593 handler.getLooper()); 594 try { 595 mInstaller.registerCallback(delegate, mUserId); 596 } catch (RemoteException e) { 597 throw e.rethrowFromSystemServer(); 598 } 599 mDelegates.add(delegate); 600 } 601 } 602 603 /** {@hide} */ 604 @Deprecated 605 public void removeSessionCallback(@NonNull SessionCallback callback) { 606 unregisterSessionCallback(callback); 607 } 608 609 /** 610 * Unregister a previously registered callback. 611 */ 612 public void unregisterSessionCallback(@NonNull SessionCallback callback) { 613 synchronized (mDelegates) { 614 for (Iterator<SessionCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) { 615 final SessionCallbackDelegate delegate = i.next(); 616 if (delegate.mCallback == callback) { 617 try { 618 mInstaller.unregisterCallback(delegate); 619 } catch (RemoteException e) { 620 throw e.rethrowFromSystemServer(); 621 } 622 i.remove(); 623 } 624 } 625 } 626 } 627 628 /** 629 * An installation that is being actively staged. For an install to succeed, 630 * all existing and new packages must have identical package names, version 631 * codes, and signing certificates. 632 * <p> 633 * A session may contain any number of split packages. If the application 634 * does not yet exist, this session must include a base package. 635 * <p> 636 * If an APK included in this session is already defined by the existing 637 * installation (for example, the same split name), the APK in this session 638 * will replace the existing APK. 639 */ 640 public static class Session implements Closeable { 641 private IPackageInstallerSession mSession; 642 643 /** {@hide} */ 644 public Session(IPackageInstallerSession session) { 645 mSession = session; 646 } 647 648 /** {@hide} */ 649 @Deprecated 650 public void setProgress(float progress) { 651 setStagingProgress(progress); 652 } 653 654 /** 655 * Set current progress of staging this session. Valid values are 656 * anywhere between 0 and 1. 657 * <p> 658 * Note that this progress may not directly correspond to the value 659 * reported by {@link SessionCallback#onProgressChanged(int, float)}, as 660 * the system may carve out a portion of the overall progress to 661 * represent its own internal installation work. 662 */ 663 public void setStagingProgress(float progress) { 664 try { 665 mSession.setClientProgress(progress); 666 } catch (RemoteException e) { 667 throw e.rethrowFromSystemServer(); 668 } 669 } 670 671 /** {@hide} */ 672 public void addProgress(float progress) { 673 try { 674 mSession.addClientProgress(progress); 675 } catch (RemoteException e) { 676 throw e.rethrowFromSystemServer(); 677 } 678 } 679 680 /** 681 * Open a stream to write an APK file into the session. 682 * <p> 683 * The returned stream will start writing data at the requested offset 684 * in the underlying file, which can be used to resume a partially 685 * written file. If a valid file length is specified, the system will 686 * preallocate the underlying disk space to optimize placement on disk. 687 * It's strongly recommended to provide a valid file length when known. 688 * <p> 689 * You can write data into the returned stream, optionally call 690 * {@link #fsync(OutputStream)} as needed to ensure bytes have been 691 * persisted to disk, and then close when finished. All streams must be 692 * closed before calling {@link #commit(IntentSender)}. 693 * 694 * @param name arbitrary, unique name of your choosing to identify the 695 * APK being written. You can open a file again for 696 * additional writes (such as after a reboot) by using the 697 * same name. This name is only meaningful within the context 698 * of a single install session. 699 * @param offsetBytes offset into the file to begin writing at, or 0 to 700 * start at the beginning of the file. 701 * @param lengthBytes total size of the file being written, used to 702 * preallocate the underlying disk space, or -1 if unknown. 703 * The system may clear various caches as needed to allocate 704 * this space. 705 * @throws IOException if trouble opening the file for writing, such as 706 * lack of disk space or unavailable media. 707 * @throws SecurityException if called after the session has been 708 * committed or abandoned. 709 */ 710 public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes, 711 long lengthBytes) throws IOException { 712 try { 713 final ParcelFileDescriptor clientSocket = mSession.openWrite(name, 714 offsetBytes, lengthBytes); 715 return new FileBridge.FileBridgeOutputStream(clientSocket); 716 } catch (RuntimeException e) { 717 ExceptionUtils.maybeUnwrapIOException(e); 718 throw e; 719 } catch (RemoteException e) { 720 throw e.rethrowFromSystemServer(); 721 } 722 } 723 724 /** 725 * Ensure that any outstanding data for given stream has been committed 726 * to disk. This is only valid for streams returned from 727 * {@link #openWrite(String, long, long)}. 728 */ 729 public void fsync(@NonNull OutputStream out) throws IOException { 730 if (out instanceof FileBridge.FileBridgeOutputStream) { 731 ((FileBridge.FileBridgeOutputStream) out).fsync(); 732 } else { 733 throw new IllegalArgumentException("Unrecognized stream"); 734 } 735 } 736 737 /** 738 * Return all APK names contained in this session. 739 * <p> 740 * This returns all names which have been previously written through 741 * {@link #openWrite(String, long, long)} as part of this session. 742 * 743 * @throws SecurityException if called after the session has been 744 * committed or abandoned. 745 */ 746 public @NonNull String[] getNames() throws IOException { 747 try { 748 return mSession.getNames(); 749 } catch (RuntimeException e) { 750 ExceptionUtils.maybeUnwrapIOException(e); 751 throw e; 752 } catch (RemoteException e) { 753 throw e.rethrowFromSystemServer(); 754 } 755 } 756 757 /** 758 * Open a stream to read an APK file from the session. 759 * <p> 760 * This is only valid for names which have been previously written 761 * through {@link #openWrite(String, long, long)} as part of this 762 * session. For example, this stream may be used to calculate a 763 * {@link MessageDigest} of a written APK before committing. 764 * 765 * @throws SecurityException if called after the session has been 766 * committed or abandoned. 767 */ 768 public @NonNull InputStream openRead(@NonNull String name) throws IOException { 769 try { 770 final ParcelFileDescriptor pfd = mSession.openRead(name); 771 return new ParcelFileDescriptor.AutoCloseInputStream(pfd); 772 } catch (RuntimeException e) { 773 ExceptionUtils.maybeUnwrapIOException(e); 774 throw e; 775 } catch (RemoteException e) { 776 throw e.rethrowFromSystemServer(); 777 } 778 } 779 780 /** 781 * Removes a split. 782 * <p> 783 * Split removals occur prior to adding new APKs. If upgrading a feature 784 * split, it is not expected nor desirable to remove the split prior to 785 * upgrading. 786 * <p> 787 * When split removal is bundled with new APKs, the packageName must be 788 * identical. 789 */ 790 public void removeSplit(@NonNull String splitName) throws IOException { 791 try { 792 mSession.removeSplit(splitName); 793 } catch (RuntimeException e) { 794 ExceptionUtils.maybeUnwrapIOException(e); 795 throw e; 796 } catch (RemoteException e) { 797 throw e.rethrowFromSystemServer(); 798 } 799 } 800 801 /** 802 * Attempt to commit everything staged in this session. This may require 803 * user intervention, and so it may not happen immediately. The final 804 * result of the commit will be reported through the given callback. 805 * <p> 806 * Once this method is called, no additional mutations may be performed 807 * on the session. If the device reboots before the session has been 808 * finalized, you may commit the session again. 809 * 810 * @throws SecurityException if streams opened through 811 * {@link #openWrite(String, long, long)} are still open. 812 */ 813 public void commit(@NonNull IntentSender statusReceiver) { 814 try { 815 mSession.commit(statusReceiver); 816 } catch (RemoteException e) { 817 throw e.rethrowFromSystemServer(); 818 } 819 } 820 821 /** 822 * Release this session object. You can open the session again if it 823 * hasn't been finalized. 824 */ 825 @Override 826 public void close() { 827 try { 828 mSession.close(); 829 } catch (RemoteException e) { 830 throw e.rethrowFromSystemServer(); 831 } 832 } 833 834 /** 835 * Completely abandon this session, destroying all staged data and 836 * rendering it invalid. Abandoned sessions will be reported to 837 * {@link SessionCallback} listeners as failures. This is equivalent to 838 * opening the session and calling {@link Session#abandon()}. 839 */ 840 public void abandon() { 841 try { 842 mSession.abandon(); 843 } catch (RemoteException e) { 844 throw e.rethrowFromSystemServer(); 845 } 846 } 847 } 848 849 /** 850 * Parameters for creating a new {@link PackageInstaller.Session}. 851 */ 852 public static class SessionParams implements Parcelable { 853 854 /** {@hide} */ 855 public static final int MODE_INVALID = -1; 856 857 /** 858 * Mode for an install session whose staged APKs should fully replace any 859 * existing APKs for the target app. 860 */ 861 public static final int MODE_FULL_INSTALL = 1; 862 863 /** 864 * Mode for an install session that should inherit any existing APKs for the 865 * target app, unless they have been explicitly overridden (based on split 866 * name) by the session. For example, this can be used to add one or more 867 * split APKs to an existing installation. 868 * <p> 869 * If there are no existing APKs for the target app, this behaves like 870 * {@link #MODE_FULL_INSTALL}. 871 */ 872 public static final int MODE_INHERIT_EXISTING = 2; 873 874 /** {@hide} */ 875 public static final int UID_UNKNOWN = -1; 876 877 /** {@hide} */ 878 public int mode = MODE_INVALID; 879 /** {@hide} */ 880 public int installFlags; 881 /** {@hide} */ 882 public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY; 883 /** {@hide} */ 884 public long sizeBytes = -1; 885 /** {@hide} */ 886 public String appPackageName; 887 /** {@hide} */ 888 public Bitmap appIcon; 889 /** {@hide} */ 890 public String appLabel; 891 /** {@hide} */ 892 public long appIconLastModified = -1; 893 /** {@hide} */ 894 public Uri originatingUri; 895 /** {@hide} */ 896 public int originatingUid = UID_UNKNOWN; 897 /** {@hide} */ 898 public Uri referrerUri; 899 /** {@hide} */ 900 public String abiOverride; 901 /** {@hide} */ 902 public String volumeUuid; 903 /** {@hide} */ 904 public String[] grantedRuntimePermissions; 905 906 /** 907 * Construct parameters for a new package install session. 908 * 909 * @param mode one of {@link #MODE_FULL_INSTALL} or 910 * {@link #MODE_INHERIT_EXISTING} describing how the session 911 * should interact with an existing app. 912 */ 913 public SessionParams(int mode) { 914 this.mode = mode; 915 } 916 917 /** {@hide} */ 918 public SessionParams(Parcel source) { 919 mode = source.readInt(); 920 installFlags = source.readInt(); 921 installLocation = source.readInt(); 922 sizeBytes = source.readLong(); 923 appPackageName = source.readString(); 924 appIcon = source.readParcelable(null); 925 appLabel = source.readString(); 926 originatingUri = source.readParcelable(null); 927 originatingUid = source.readInt(); 928 referrerUri = source.readParcelable(null); 929 abiOverride = source.readString(); 930 volumeUuid = source.readString(); 931 grantedRuntimePermissions = source.readStringArray(); 932 } 933 934 /** 935 * Provide value of {@link PackageInfo#installLocation}, which may be used 936 * to determine where the app will be staged. Defaults to 937 * {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}. 938 */ 939 public void setInstallLocation(int installLocation) { 940 this.installLocation = installLocation; 941 } 942 943 /** 944 * Optionally indicate the total size (in bytes) of all APKs that will be 945 * delivered in this session. The system may use this to ensure enough disk 946 * space exists before proceeding, or to estimate container size for 947 * installations living on external storage. 948 * 949 * @see PackageInfo#INSTALL_LOCATION_AUTO 950 * @see PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL 951 */ 952 public void setSize(long sizeBytes) { 953 this.sizeBytes = sizeBytes; 954 } 955 956 /** 957 * Optionally set the package name of the app being installed. It's strongly 958 * recommended that you provide this value when known, so that observers can 959 * communicate installing apps to users. 960 * <p> 961 * If the APKs staged in the session aren't consistent with this package 962 * name, the install will fail. Regardless of this value, all APKs in the 963 * app must have the same package name. 964 */ 965 public void setAppPackageName(@Nullable String appPackageName) { 966 this.appPackageName = appPackageName; 967 } 968 969 /** 970 * Optionally set an icon representing the app being installed. This should 971 * be roughly {@link ActivityManager#getLauncherLargeIconSize()} in both 972 * dimensions. 973 */ 974 public void setAppIcon(@Nullable Bitmap appIcon) { 975 this.appIcon = appIcon; 976 } 977 978 /** 979 * Optionally set a label representing the app being installed. 980 */ 981 public void setAppLabel(@Nullable CharSequence appLabel) { 982 this.appLabel = (appLabel != null) ? appLabel.toString() : null; 983 } 984 985 /** 986 * Optionally set the URI where this package was downloaded from. This is 987 * informational and may be used as a signal for anti-malware purposes. 988 * 989 * @see Intent#EXTRA_ORIGINATING_URI 990 */ 991 public void setOriginatingUri(@Nullable Uri originatingUri) { 992 this.originatingUri = originatingUri; 993 } 994 995 /** 996 * Sets the UID that initiated package installation. This is informational 997 * and may be used as a signal for anti-malware purposes. 998 * 999 * @see PackageManager#EXTRA_VERIFICATION_INSTALLER_UID 1000 */ 1001 public void setOriginatingUid(int originatingUid) { 1002 this.originatingUid = originatingUid; 1003 } 1004 1005 /** 1006 * Optionally set the URI that referred you to install this package. This is 1007 * informational and may be used as a signal for anti-malware purposes. 1008 * 1009 * @see Intent#EXTRA_REFERRER 1010 */ 1011 public void setReferrerUri(@Nullable Uri referrerUri) { 1012 this.referrerUri = referrerUri; 1013 } 1014 1015 /** 1016 * Sets which runtime permissions to be granted to the package at installation. 1017 * Using this API requires holding {@link android.Manifest.permission 1018 * #INSTALL_GRANT_RUNTIME_PERMISSIONS} 1019 * 1020 * @param permissions The permissions to grant or null to grant all runtime 1021 * permissions. 1022 * 1023 * @hide 1024 */ 1025 @SystemApi 1026 @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) 1027 public void setGrantedRuntimePermissions(String[] permissions) { 1028 installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS; 1029 this.grantedRuntimePermissions = permissions; 1030 } 1031 1032 /** {@hide} */ 1033 public void setInstallFlagsInternal() { 1034 installFlags |= PackageManager.INSTALL_INTERNAL; 1035 installFlags &= ~PackageManager.INSTALL_EXTERNAL; 1036 } 1037 1038 /** {@hide} */ 1039 @SystemApi 1040 public void setAllowDowngrade(boolean allowDowngrade) { 1041 if (allowDowngrade) { 1042 installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE; 1043 } else { 1044 installFlags &= ~PackageManager.INSTALL_ALLOW_DOWNGRADE; 1045 } 1046 } 1047 1048 /** {@hide} */ 1049 public void setInstallFlagsExternal() { 1050 installFlags |= PackageManager.INSTALL_EXTERNAL; 1051 installFlags &= ~PackageManager.INSTALL_INTERNAL; 1052 } 1053 1054 /** {@hide} */ 1055 public void setInstallFlagsForcePermissionPrompt() { 1056 installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT; 1057 } 1058 1059 /** {@hide} */ 1060 @SystemApi 1061 public void setDontKillApp(boolean dontKillApp) { 1062 if (dontKillApp) { 1063 installFlags |= PackageManager.INSTALL_DONT_KILL_APP; 1064 } else { 1065 installFlags &= ~PackageManager.INSTALL_DONT_KILL_APP; 1066 } 1067 } 1068 1069 /** {@hide} */ 1070 public void dump(IndentingPrintWriter pw) { 1071 pw.printPair("mode", mode); 1072 pw.printHexPair("installFlags", installFlags); 1073 pw.printPair("installLocation", installLocation); 1074 pw.printPair("sizeBytes", sizeBytes); 1075 pw.printPair("appPackageName", appPackageName); 1076 pw.printPair("appIcon", (appIcon != null)); 1077 pw.printPair("appLabel", appLabel); 1078 pw.printPair("originatingUri", originatingUri); 1079 pw.printPair("originatingUid", originatingUid); 1080 pw.printPair("referrerUri", referrerUri); 1081 pw.printPair("abiOverride", abiOverride); 1082 pw.printPair("volumeUuid", volumeUuid); 1083 pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions); 1084 pw.println(); 1085 } 1086 1087 @Override 1088 public int describeContents() { 1089 return 0; 1090 } 1091 1092 @Override 1093 public void writeToParcel(Parcel dest, int flags) { 1094 dest.writeInt(mode); 1095 dest.writeInt(installFlags); 1096 dest.writeInt(installLocation); 1097 dest.writeLong(sizeBytes); 1098 dest.writeString(appPackageName); 1099 dest.writeParcelable(appIcon, flags); 1100 dest.writeString(appLabel); 1101 dest.writeParcelable(originatingUri, flags); 1102 dest.writeInt(originatingUid); 1103 dest.writeParcelable(referrerUri, flags); 1104 dest.writeString(abiOverride); 1105 dest.writeString(volumeUuid); 1106 dest.writeStringArray(grantedRuntimePermissions); 1107 } 1108 1109 public static final Parcelable.Creator<SessionParams> 1110 CREATOR = new Parcelable.Creator<SessionParams>() { 1111 @Override 1112 public SessionParams createFromParcel(Parcel p) { 1113 return new SessionParams(p); 1114 } 1115 1116 @Override 1117 public SessionParams[] newArray(int size) { 1118 return new SessionParams[size]; 1119 } 1120 }; 1121 } 1122 1123 /** 1124 * Details for an active install session. 1125 */ 1126 public static class SessionInfo implements Parcelable { 1127 1128 /** {@hide} */ 1129 public int sessionId; 1130 /** {@hide} */ 1131 public String installerPackageName; 1132 /** {@hide} */ 1133 public String resolvedBaseCodePath; 1134 /** {@hide} */ 1135 public float progress; 1136 /** {@hide} */ 1137 public boolean sealed; 1138 /** {@hide} */ 1139 public boolean active; 1140 1141 /** {@hide} */ 1142 public int mode; 1143 /** {@hide} */ 1144 public long sizeBytes; 1145 /** {@hide} */ 1146 public String appPackageName; 1147 /** {@hide} */ 1148 public Bitmap appIcon; 1149 /** {@hide} */ 1150 public CharSequence appLabel; 1151 1152 /** {@hide} */ 1153 public SessionInfo() { 1154 } 1155 1156 /** {@hide} */ 1157 public SessionInfo(Parcel source) { 1158 sessionId = source.readInt(); 1159 installerPackageName = source.readString(); 1160 resolvedBaseCodePath = source.readString(); 1161 progress = source.readFloat(); 1162 sealed = source.readInt() != 0; 1163 active = source.readInt() != 0; 1164 1165 mode = source.readInt(); 1166 sizeBytes = source.readLong(); 1167 appPackageName = source.readString(); 1168 appIcon = source.readParcelable(null); 1169 appLabel = source.readString(); 1170 } 1171 1172 /** 1173 * Return the ID for this session. 1174 */ 1175 public int getSessionId() { 1176 return sessionId; 1177 } 1178 1179 /** 1180 * Return the package name of the app that owns this session. 1181 */ 1182 public @Nullable String getInstallerPackageName() { 1183 return installerPackageName; 1184 } 1185 1186 /** 1187 * Return current overall progress of this session, between 0 and 1. 1188 * <p> 1189 * Note that this progress may not directly correspond to the value 1190 * reported by 1191 * {@link PackageInstaller.Session#setStagingProgress(float)}, as the 1192 * system may carve out a portion of the overall progress to represent 1193 * its own internal installation work. 1194 */ 1195 public float getProgress() { 1196 return progress; 1197 } 1198 1199 /** 1200 * Return if this session is currently active. 1201 * <p> 1202 * A session is considered active whenever there is ongoing forward 1203 * progress being made, such as the installer holding an open 1204 * {@link Session} instance while streaming data into place, or the 1205 * system optimizing code as the result of 1206 * {@link Session#commit(IntentSender)}. 1207 * <p> 1208 * If the installer closes the {@link Session} without committing, the 1209 * session is considered inactive until the installer opens the session 1210 * again. 1211 */ 1212 public boolean isActive() { 1213 return active; 1214 } 1215 1216 /** {@hide} */ 1217 @Deprecated 1218 public boolean isOpen() { 1219 return isActive(); 1220 } 1221 1222 /** 1223 * Return the package name this session is working with. May be {@code null} 1224 * if unknown. 1225 */ 1226 public @Nullable String getAppPackageName() { 1227 return appPackageName; 1228 } 1229 1230 /** 1231 * Return an icon representing the app being installed. May be {@code null} 1232 * if unavailable. 1233 */ 1234 public @Nullable Bitmap getAppIcon() { 1235 return appIcon; 1236 } 1237 1238 /** 1239 * Return a label representing the app being installed. May be {@code null} 1240 * if unavailable. 1241 */ 1242 public @Nullable CharSequence getAppLabel() { 1243 return appLabel; 1244 } 1245 1246 /** 1247 * Return an Intent that can be started to view details about this install 1248 * session. This may surface actions such as pause, resume, or cancel. 1249 * <p> 1250 * In some cases, a matching Activity may not exist, so ensure you safeguard 1251 * against this. 1252 * 1253 * @see PackageInstaller#ACTION_SESSION_DETAILS 1254 */ 1255 public @Nullable Intent createDetailsIntent() { 1256 final Intent intent = new Intent(PackageInstaller.ACTION_SESSION_DETAILS); 1257 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId); 1258 intent.setPackage(installerPackageName); 1259 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1260 return intent; 1261 } 1262 1263 /** {@hide} */ 1264 @Deprecated 1265 public @Nullable Intent getDetailsIntent() { 1266 return createDetailsIntent(); 1267 } 1268 1269 @Override 1270 public int describeContents() { 1271 return 0; 1272 } 1273 1274 @Override 1275 public void writeToParcel(Parcel dest, int flags) { 1276 dest.writeInt(sessionId); 1277 dest.writeString(installerPackageName); 1278 dest.writeString(resolvedBaseCodePath); 1279 dest.writeFloat(progress); 1280 dest.writeInt(sealed ? 1 : 0); 1281 dest.writeInt(active ? 1 : 0); 1282 1283 dest.writeInt(mode); 1284 dest.writeLong(sizeBytes); 1285 dest.writeString(appPackageName); 1286 dest.writeParcelable(appIcon, flags); 1287 dest.writeString(appLabel != null ? appLabel.toString() : null); 1288 } 1289 1290 public static final Parcelable.Creator<SessionInfo> 1291 CREATOR = new Parcelable.Creator<SessionInfo>() { 1292 @Override 1293 public SessionInfo createFromParcel(Parcel p) { 1294 return new SessionInfo(p); 1295 } 1296 1297 @Override 1298 public SessionInfo[] newArray(int size) { 1299 return new SessionInfo[size]; 1300 } 1301 }; 1302 } 1303 } 1304