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