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