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