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