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