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.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.RequiresPermission;
22 import android.annotation.SdkConstant;
23 import android.annotation.SdkConstant.SdkConstantType;
24 import android.annotation.SystemApi;
25 import android.app.ActivityManager;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentSender;
29 import android.graphics.Bitmap;
30 import android.net.Uri;
31 import android.os.FileBridge;
32 import android.os.Handler;
33 import android.os.Looper;
34 import android.os.Message;
35 import android.os.Parcel;
36 import android.os.ParcelFileDescriptor;
37 import android.os.Parcelable;
38 import android.os.RemoteException;
39 import android.util.ExceptionUtils;
40 
41 import com.android.internal.util.IndentingPrintWriter;
42 
43 import java.io.Closeable;
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.io.OutputStream;
47 import java.security.MessageDigest;
48 import java.util.ArrayList;
49 import java.util.Iterator;
50 import 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  */
77 public 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#createDetailsIntent()} 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} */
PackageInstaller(Context context, PackageManager pm, IPackageInstaller installer, String installerPackageName, int userId)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      * @throws SecurityException when installation services are unavailable,
289      *             such as when called from a restricted user.
290      * @throws IllegalArgumentException when {@link SessionParams} is invalid.
291      * @return positive, non-zero unique ID that represents the created session.
292      *         This ID remains consistent across device reboots until the
293      *         session is finalized. IDs are not reused during a given boot.
294      */
createSession(@onNull SessionParams params)295     public int createSession(@NonNull SessionParams params) throws IOException {
296         try {
297             return mInstaller.createSession(params, mInstallerPackageName, mUserId);
298         } catch (RuntimeException e) {
299             ExceptionUtils.maybeUnwrapIOException(e);
300             throw e;
301         } catch (RemoteException e) {
302             throw e.rethrowFromSystemServer();
303         }
304     }
305 
306     /**
307      * Open an existing session to actively perform work. To succeed, the caller
308      * must be the owner of the install session.
309      *
310      * @throws IOException if parameters were unsatisfiable, such as lack of
311      *             disk space or unavailable media.
312      * @throws SecurityException when the caller does not own the session, or
313      *             the session is invalid.
314      */
openSession(int sessionId)315     public @NonNull Session openSession(int sessionId) throws IOException {
316         try {
317             return new Session(mInstaller.openSession(sessionId));
318         } catch (RuntimeException e) {
319             ExceptionUtils.maybeUnwrapIOException(e);
320             throw e;
321         } catch (RemoteException e) {
322             throw e.rethrowFromSystemServer();
323         }
324     }
325 
326     /**
327      * Update the icon representing the app being installed in a specific
328      * session. This should be roughly
329      * {@link ActivityManager#getLauncherLargeIconSize()} in both dimensions.
330      *
331      * @throws SecurityException when the caller does not own the session, or
332      *             the session is invalid.
333      */
updateSessionAppIcon(int sessionId, @Nullable Bitmap appIcon)334     public void updateSessionAppIcon(int sessionId, @Nullable Bitmap appIcon) {
335         try {
336             mInstaller.updateSessionAppIcon(sessionId, appIcon);
337         } catch (RemoteException e) {
338             throw e.rethrowFromSystemServer();
339         }
340     }
341 
342     /**
343      * Update the label representing the app being installed in a specific
344      * session.
345      *
346      * @throws SecurityException when the caller does not own the session, or
347      *             the session is invalid.
348      */
updateSessionAppLabel(int sessionId, @Nullable CharSequence appLabel)349     public void updateSessionAppLabel(int sessionId, @Nullable CharSequence appLabel) {
350         try {
351             final String val = (appLabel != null) ? appLabel.toString() : null;
352             mInstaller.updateSessionAppLabel(sessionId, val);
353         } catch (RemoteException e) {
354             throw e.rethrowFromSystemServer();
355         }
356     }
357 
358     /**
359      * Completely abandon the given session, destroying all staged data and
360      * rendering it invalid. Abandoned sessions will be reported to
361      * {@link SessionCallback} listeners as failures. This is equivalent to
362      * opening the session and calling {@link Session#abandon()}.
363      *
364      * @throws SecurityException when the caller does not own the session, or
365      *             the session is invalid.
366      */
abandonSession(int sessionId)367     public void abandonSession(int sessionId) {
368         try {
369             mInstaller.abandonSession(sessionId);
370         } catch (RemoteException e) {
371             throw e.rethrowFromSystemServer();
372         }
373     }
374 
375     /**
376      * Return details for a specific session. No special permissions are
377      * required to retrieve these details.
378      *
379      * @return details for the requested session, or {@code null} if the session
380      *         does not exist.
381      */
getSessionInfo(int sessionId)382     public @Nullable SessionInfo getSessionInfo(int sessionId) {
383         try {
384             return mInstaller.getSessionInfo(sessionId);
385         } catch (RemoteException e) {
386             throw e.rethrowFromSystemServer();
387         }
388     }
389 
390     /**
391      * Return list of all known install sessions, regardless of the installer.
392      */
getAllSessions()393     public @NonNull List<SessionInfo> getAllSessions() {
394         try {
395             return mInstaller.getAllSessions(mUserId).getList();
396         } catch (RemoteException e) {
397             throw e.rethrowFromSystemServer();
398         }
399     }
400 
401     /**
402      * Return list of all known install sessions owned by the calling app.
403      */
getMySessions()404     public @NonNull List<SessionInfo> getMySessions() {
405         try {
406             return mInstaller.getMySessions(mInstallerPackageName, mUserId).getList();
407         } catch (RemoteException e) {
408             throw e.rethrowFromSystemServer();
409         }
410     }
411 
412     /**
413      * Uninstall the given package, removing it completely from the device. This
414      * method is only available to the current "installer of record" for the
415      * package.
416      */
uninstall(@onNull String packageName, @NonNull IntentSender statusReceiver)417     public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) {
418         try {
419             mInstaller.uninstall(packageName, mInstallerPackageName, 0, statusReceiver, mUserId);
420         } catch (RemoteException e) {
421             throw e.rethrowFromSystemServer();
422         }
423     }
424 
425     /** {@hide} */
setPermissionsResult(int sessionId, boolean accepted)426     public void setPermissionsResult(int sessionId, boolean accepted) {
427         try {
428             mInstaller.setPermissionsResult(sessionId, accepted);
429         } catch (RemoteException e) {
430             throw e.rethrowFromSystemServer();
431         }
432     }
433 
434     /**
435      * Events for observing session lifecycle.
436      * <p>
437      * A typical session lifecycle looks like this:
438      * <ul>
439      * <li>An installer creates a session to indicate pending app delivery. All
440      * install details are available at this point.
441      * <li>The installer opens the session to deliver APK data. Note that a
442      * session may be opened and closed multiple times as network connectivity
443      * changes. The installer may deliver periodic progress updates.
444      * <li>The installer commits or abandons the session, resulting in the
445      * session being finished.
446      * </ul>
447      */
448     public static abstract class SessionCallback {
449         /**
450          * New session has been created. Details about the session can be
451          * obtained from {@link PackageInstaller#getSessionInfo(int)}.
452          */
onCreated(int sessionId)453         public abstract void onCreated(int sessionId);
454 
455         /**
456          * Badging details for an existing session has changed. For example, the
457          * app icon or label has been updated.
458          */
onBadgingChanged(int sessionId)459         public abstract void onBadgingChanged(int sessionId);
460 
461         /**
462          * Active state for session has been changed.
463          * <p>
464          * A session is considered active whenever there is ongoing forward
465          * progress being made, such as the installer holding an open
466          * {@link Session} instance while streaming data into place, or the
467          * system optimizing code as the result of
468          * {@link Session#commit(IntentSender)}.
469          * <p>
470          * If the installer closes the {@link Session} without committing, the
471          * session is considered inactive until the installer opens the session
472          * again.
473          */
onActiveChanged(int sessionId, boolean active)474         public abstract void onActiveChanged(int sessionId, boolean active);
475 
476         /**
477          * Progress for given session has been updated.
478          * <p>
479          * Note that this progress may not directly correspond to the value
480          * reported by
481          * {@link PackageInstaller.Session#setStagingProgress(float)}, as the
482          * system may carve out a portion of the overall progress to represent
483          * its own internal installation work.
484          */
onProgressChanged(int sessionId, float progress)485         public abstract void onProgressChanged(int sessionId, float progress);
486 
487         /**
488          * Session has completely finished, either with success or failure.
489          */
onFinished(int sessionId, boolean success)490         public abstract void onFinished(int sessionId, boolean success);
491     }
492 
493     /** {@hide} */
494     private static class SessionCallbackDelegate extends IPackageInstallerCallback.Stub implements
495             Handler.Callback {
496         private static final int MSG_SESSION_CREATED = 1;
497         private static final int MSG_SESSION_BADGING_CHANGED = 2;
498         private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
499         private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
500         private static final int MSG_SESSION_FINISHED = 5;
501 
502         final SessionCallback mCallback;
503         final Handler mHandler;
504 
SessionCallbackDelegate(SessionCallback callback, Looper looper)505         public SessionCallbackDelegate(SessionCallback callback, Looper looper) {
506             mCallback = callback;
507             mHandler = new Handler(looper, this);
508         }
509 
510         @Override
handleMessage(Message msg)511         public boolean handleMessage(Message msg) {
512             final int sessionId = msg.arg1;
513             switch (msg.what) {
514                 case MSG_SESSION_CREATED:
515                     mCallback.onCreated(sessionId);
516                     return true;
517                 case MSG_SESSION_BADGING_CHANGED:
518                     mCallback.onBadgingChanged(sessionId);
519                     return true;
520                 case MSG_SESSION_ACTIVE_CHANGED:
521                     final boolean active = msg.arg2 != 0;
522                     mCallback.onActiveChanged(sessionId, active);
523                     return true;
524                 case MSG_SESSION_PROGRESS_CHANGED:
525                     mCallback.onProgressChanged(sessionId, (float) msg.obj);
526                     return true;
527                 case MSG_SESSION_FINISHED:
528                     mCallback.onFinished(sessionId, msg.arg2 != 0);
529                     return true;
530             }
531             return false;
532         }
533 
534         @Override
onSessionCreated(int sessionId)535         public void onSessionCreated(int sessionId) {
536             mHandler.obtainMessage(MSG_SESSION_CREATED, sessionId, 0).sendToTarget();
537         }
538 
539         @Override
onSessionBadgingChanged(int sessionId)540         public void onSessionBadgingChanged(int sessionId) {
541             mHandler.obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, 0).sendToTarget();
542         }
543 
544         @Override
onSessionActiveChanged(int sessionId, boolean active)545         public void onSessionActiveChanged(int sessionId, boolean active) {
546             mHandler.obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, active ? 1 : 0)
547                     .sendToTarget();
548         }
549 
550         @Override
onSessionProgressChanged(int sessionId, float progress)551         public void onSessionProgressChanged(int sessionId, float progress) {
552             mHandler.obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, 0, progress)
553                     .sendToTarget();
554         }
555 
556         @Override
onSessionFinished(int sessionId, boolean success)557         public void onSessionFinished(int sessionId, boolean success) {
558             mHandler.obtainMessage(MSG_SESSION_FINISHED, sessionId, success ? 1 : 0)
559                     .sendToTarget();
560         }
561     }
562 
563     /** {@hide} */
564     @Deprecated
addSessionCallback(@onNull SessionCallback callback)565     public void addSessionCallback(@NonNull SessionCallback callback) {
566         registerSessionCallback(callback);
567     }
568 
569     /**
570      * Register to watch for session lifecycle events. No special permissions
571      * are required to watch for these events.
572      */
registerSessionCallback(@onNull SessionCallback callback)573     public void registerSessionCallback(@NonNull SessionCallback callback) {
574         registerSessionCallback(callback, new Handler());
575     }
576 
577     /** {@hide} */
578     @Deprecated
addSessionCallback(@onNull SessionCallback callback, @NonNull Handler handler)579     public void addSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
580         registerSessionCallback(callback, handler);
581     }
582 
583     /**
584      * Register to watch for session lifecycle events. No special permissions
585      * are required to watch for these events.
586      *
587      * @param handler to dispatch callback events through, otherwise uses
588      *            calling thread.
589      */
registerSessionCallback(@onNull SessionCallback callback, @NonNull Handler handler)590     public void registerSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
591         synchronized (mDelegates) {
592             final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback,
593                     handler.getLooper());
594             try {
595                 mInstaller.registerCallback(delegate, mUserId);
596             } catch (RemoteException e) {
597                 throw e.rethrowFromSystemServer();
598             }
599             mDelegates.add(delegate);
600         }
601     }
602 
603     /** {@hide} */
604     @Deprecated
removeSessionCallback(@onNull SessionCallback callback)605     public void removeSessionCallback(@NonNull SessionCallback callback) {
606         unregisterSessionCallback(callback);
607     }
608 
609     /**
610      * Unregister a previously registered callback.
611      */
unregisterSessionCallback(@onNull SessionCallback callback)612     public void unregisterSessionCallback(@NonNull SessionCallback callback) {
613         synchronized (mDelegates) {
614             for (Iterator<SessionCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) {
615                 final SessionCallbackDelegate delegate = i.next();
616                 if (delegate.mCallback == callback) {
617                     try {
618                         mInstaller.unregisterCallback(delegate);
619                     } catch (RemoteException e) {
620                         throw e.rethrowFromSystemServer();
621                     }
622                     i.remove();
623                 }
624             }
625         }
626     }
627 
628     /**
629      * An installation that is being actively staged. For an install to succeed,
630      * all existing and new packages must have identical package names, version
631      * codes, and signing certificates.
632      * <p>
633      * A session may contain any number of split packages. If the application
634      * does not yet exist, this session must include a base package.
635      * <p>
636      * If an APK included in this session is already defined by the existing
637      * installation (for example, the same split name), the APK in this session
638      * will replace the existing APK.
639      */
640     public static class Session implements Closeable {
641         private IPackageInstallerSession mSession;
642 
643         /** {@hide} */
Session(IPackageInstallerSession session)644         public Session(IPackageInstallerSession session) {
645             mSession = session;
646         }
647 
648         /** {@hide} */
649         @Deprecated
setProgress(float progress)650         public void setProgress(float progress) {
651             setStagingProgress(progress);
652         }
653 
654         /**
655          * Set current progress of staging this session. Valid values are
656          * anywhere between 0 and 1.
657          * <p>
658          * Note that this progress may not directly correspond to the value
659          * reported by {@link SessionCallback#onProgressChanged(int, float)}, as
660          * the system may carve out a portion of the overall progress to
661          * represent its own internal installation work.
662          */
setStagingProgress(float progress)663         public void setStagingProgress(float progress) {
664             try {
665                 mSession.setClientProgress(progress);
666             } catch (RemoteException e) {
667                 throw e.rethrowFromSystemServer();
668             }
669         }
670 
671         /** {@hide} */
addProgress(float progress)672         public void addProgress(float progress) {
673             try {
674                 mSession.addClientProgress(progress);
675             } catch (RemoteException e) {
676                 throw e.rethrowFromSystemServer();
677             }
678         }
679 
680         /**
681          * Open a stream to write an APK file into the session.
682          * <p>
683          * The returned stream will start writing data at the requested offset
684          * in the underlying file, which can be used to resume a partially
685          * written file. If a valid file length is specified, the system will
686          * preallocate the underlying disk space to optimize placement on disk.
687          * It's strongly recommended to provide a valid file length when known.
688          * <p>
689          * You can write data into the returned stream, optionally call
690          * {@link #fsync(OutputStream)} as needed to ensure bytes have been
691          * persisted to disk, and then close when finished. All streams must be
692          * closed before calling {@link #commit(IntentSender)}.
693          *
694          * @param name arbitrary, unique name of your choosing to identify the
695          *            APK being written. You can open a file again for
696          *            additional writes (such as after a reboot) by using the
697          *            same name. This name is only meaningful within the context
698          *            of a single install session.
699          * @param offsetBytes offset into the file to begin writing at, or 0 to
700          *            start at the beginning of the file.
701          * @param lengthBytes total size of the file being written, used to
702          *            preallocate the underlying disk space, or -1 if unknown.
703          *            The system may clear various caches as needed to allocate
704          *            this space.
705          * @throws IOException if trouble opening the file for writing, such as
706          *             lack of disk space or unavailable media.
707          * @throws SecurityException if called after the session has been
708          *             committed or abandoned.
709          */
openWrite(@onNull String name, long offsetBytes, long lengthBytes)710         public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
711                 long lengthBytes) throws IOException {
712             try {
713                 final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
714                         offsetBytes, lengthBytes);
715                 return new FileBridge.FileBridgeOutputStream(clientSocket);
716             } catch (RuntimeException e) {
717                 ExceptionUtils.maybeUnwrapIOException(e);
718                 throw e;
719             } catch (RemoteException e) {
720                 throw e.rethrowFromSystemServer();
721             }
722         }
723 
724         /**
725          * Ensure that any outstanding data for given stream has been committed
726          * to disk. This is only valid for streams returned from
727          * {@link #openWrite(String, long, long)}.
728          */
fsync(@onNull OutputStream out)729         public void fsync(@NonNull OutputStream out) throws IOException {
730             if (out instanceof FileBridge.FileBridgeOutputStream) {
731                 ((FileBridge.FileBridgeOutputStream) out).fsync();
732             } else {
733                 throw new IllegalArgumentException("Unrecognized stream");
734             }
735         }
736 
737         /**
738          * Return all APK names contained in this session.
739          * <p>
740          * This returns all names which have been previously written through
741          * {@link #openWrite(String, long, long)} as part of this session.
742          *
743          * @throws SecurityException if called after the session has been
744          *             committed or abandoned.
745          */
getNames()746         public @NonNull String[] getNames() throws IOException {
747             try {
748                 return mSession.getNames();
749             } catch (RuntimeException e) {
750                 ExceptionUtils.maybeUnwrapIOException(e);
751                 throw e;
752             } catch (RemoteException e) {
753                 throw e.rethrowFromSystemServer();
754             }
755         }
756 
757         /**
758          * Open a stream to read an APK file from the session.
759          * <p>
760          * This is only valid for names which have been previously written
761          * through {@link #openWrite(String, long, long)} as part of this
762          * session. For example, this stream may be used to calculate a
763          * {@link MessageDigest} of a written APK before committing.
764          *
765          * @throws SecurityException if called after the session has been
766          *             committed or abandoned.
767          */
openRead(@onNull String name)768         public @NonNull InputStream openRead(@NonNull String name) throws IOException {
769             try {
770                 final ParcelFileDescriptor pfd = mSession.openRead(name);
771                 return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
772             } catch (RuntimeException e) {
773                 ExceptionUtils.maybeUnwrapIOException(e);
774                 throw e;
775             } catch (RemoteException e) {
776                 throw e.rethrowFromSystemServer();
777             }
778         }
779 
780         /**
781          * Removes a split.
782          * <p>
783          * Split removals occur prior to adding new APKs. If upgrading a feature
784          * split, it is not expected nor desirable to remove the split prior to
785          * upgrading.
786          * <p>
787          * When split removal is bundled with new APKs, the packageName must be
788          * identical.
789          */
removeSplit(@onNull String splitName)790         public void removeSplit(@NonNull String splitName) throws IOException {
791             try {
792                 mSession.removeSplit(splitName);
793             } catch (RuntimeException e) {
794                 ExceptionUtils.maybeUnwrapIOException(e);
795                 throw e;
796             } catch (RemoteException e) {
797                 throw e.rethrowFromSystemServer();
798             }
799         }
800 
801         /**
802          * Attempt to commit everything staged in this session. This may require
803          * user intervention, and so it may not happen immediately. The final
804          * result of the commit will be reported through the given callback.
805          * <p>
806          * Once this method is called, no additional mutations may be performed
807          * on the session. If the device reboots before the session has been
808          * finalized, you may commit the session again.
809          *
810          * @throws SecurityException if streams opened through
811          *             {@link #openWrite(String, long, long)} are still open.
812          */
commit(@onNull IntentSender statusReceiver)813         public void commit(@NonNull IntentSender statusReceiver) {
814             try {
815                 mSession.commit(statusReceiver);
816             } catch (RemoteException e) {
817                 throw e.rethrowFromSystemServer();
818             }
819         }
820 
821         /**
822          * Release this session object. You can open the session again if it
823          * hasn't been finalized.
824          */
825         @Override
close()826         public void close() {
827             try {
828                 mSession.close();
829             } catch (RemoteException e) {
830                 throw e.rethrowFromSystemServer();
831             }
832         }
833 
834         /**
835          * Completely abandon this session, destroying all staged data and
836          * rendering it invalid. Abandoned sessions will be reported to
837          * {@link SessionCallback} listeners as failures. This is equivalent to
838          * opening the session and calling {@link Session#abandon()}.
839          */
abandon()840         public void abandon() {
841             try {
842                 mSession.abandon();
843             } catch (RemoteException e) {
844                 throw e.rethrowFromSystemServer();
845             }
846         }
847     }
848 
849     /**
850      * Parameters for creating a new {@link PackageInstaller.Session}.
851      */
852     public static class SessionParams implements Parcelable {
853 
854         /** {@hide} */
855         public static final int MODE_INVALID = -1;
856 
857         /**
858          * Mode for an install session whose staged APKs should fully replace any
859          * existing APKs for the target app.
860          */
861         public static final int MODE_FULL_INSTALL = 1;
862 
863         /**
864          * Mode for an install session that should inherit any existing APKs for the
865          * target app, unless they have been explicitly overridden (based on split
866          * name) by the session. For example, this can be used to add one or more
867          * split APKs to an existing installation.
868          * <p>
869          * If there are no existing APKs for the target app, this behaves like
870          * {@link #MODE_FULL_INSTALL}.
871          */
872         public static final int MODE_INHERIT_EXISTING = 2;
873 
874         /** {@hide} */
875         public static final int UID_UNKNOWN = -1;
876 
877         /** {@hide} */
878         public int mode = MODE_INVALID;
879         /** {@hide} */
880         public int installFlags;
881         /** {@hide} */
882         public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
883         /** {@hide} */
884         public long sizeBytes = -1;
885         /** {@hide} */
886         public String appPackageName;
887         /** {@hide} */
888         public Bitmap appIcon;
889         /** {@hide} */
890         public String appLabel;
891         /** {@hide} */
892         public long appIconLastModified = -1;
893         /** {@hide} */
894         public Uri originatingUri;
895         /** {@hide} */
896         public int originatingUid = UID_UNKNOWN;
897         /** {@hide} */
898         public Uri referrerUri;
899         /** {@hide} */
900         public String abiOverride;
901         /** {@hide} */
902         public String volumeUuid;
903         /** {@hide} */
904         public String[] grantedRuntimePermissions;
905 
906         /**
907          * Construct parameters for a new package install session.
908          *
909          * @param mode one of {@link #MODE_FULL_INSTALL} or
910          *            {@link #MODE_INHERIT_EXISTING} describing how the session
911          *            should interact with an existing app.
912          */
SessionParams(int mode)913         public SessionParams(int mode) {
914             this.mode = mode;
915         }
916 
917         /** {@hide} */
SessionParams(Parcel source)918         public SessionParams(Parcel source) {
919             mode = source.readInt();
920             installFlags = source.readInt();
921             installLocation = source.readInt();
922             sizeBytes = source.readLong();
923             appPackageName = source.readString();
924             appIcon = source.readParcelable(null);
925             appLabel = source.readString();
926             originatingUri = source.readParcelable(null);
927             originatingUid = source.readInt();
928             referrerUri = source.readParcelable(null);
929             abiOverride = source.readString();
930             volumeUuid = source.readString();
931             grantedRuntimePermissions = source.readStringArray();
932         }
933 
934         /**
935          * Provide value of {@link PackageInfo#installLocation}, which may be used
936          * to determine where the app will be staged. Defaults to
937          * {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}.
938          */
setInstallLocation(int installLocation)939         public void setInstallLocation(int installLocation) {
940             this.installLocation = installLocation;
941         }
942 
943         /**
944          * Optionally indicate the total size (in bytes) of all APKs that will be
945          * delivered in this session. The system may use this to ensure enough disk
946          * space exists before proceeding, or to estimate container size for
947          * installations living on external storage.
948          *
949          * @see PackageInfo#INSTALL_LOCATION_AUTO
950          * @see PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL
951          */
setSize(long sizeBytes)952         public void setSize(long sizeBytes) {
953             this.sizeBytes = sizeBytes;
954         }
955 
956         /**
957          * Optionally set the package name of the app being installed. It's strongly
958          * recommended that you provide this value when known, so that observers can
959          * communicate installing apps to users.
960          * <p>
961          * If the APKs staged in the session aren't consistent with this package
962          * name, the install will fail. Regardless of this value, all APKs in the
963          * app must have the same package name.
964          */
setAppPackageName(@ullable String appPackageName)965         public void setAppPackageName(@Nullable String appPackageName) {
966             this.appPackageName = appPackageName;
967         }
968 
969         /**
970          * Optionally set an icon representing the app being installed. This should
971          * be roughly {@link ActivityManager#getLauncherLargeIconSize()} in both
972          * dimensions.
973          */
setAppIcon(@ullable Bitmap appIcon)974         public void setAppIcon(@Nullable Bitmap appIcon) {
975             this.appIcon = appIcon;
976         }
977 
978         /**
979          * Optionally set a label representing the app being installed.
980          */
setAppLabel(@ullable CharSequence appLabel)981         public void setAppLabel(@Nullable CharSequence appLabel) {
982             this.appLabel = (appLabel != null) ? appLabel.toString() : null;
983         }
984 
985         /**
986          * Optionally set the URI where this package was downloaded from. This is
987          * informational and may be used as a signal for anti-malware purposes.
988          *
989          * @see Intent#EXTRA_ORIGINATING_URI
990          */
setOriginatingUri(@ullable Uri originatingUri)991         public void setOriginatingUri(@Nullable Uri originatingUri) {
992             this.originatingUri = originatingUri;
993         }
994 
995         /**
996          * Sets the UID that initiated package installation. This is informational
997          * and may be used as a signal for anti-malware purposes.
998          *
999          * @see PackageManager#EXTRA_VERIFICATION_INSTALLER_UID
1000          */
setOriginatingUid(int originatingUid)1001         public void setOriginatingUid(int originatingUid) {
1002             this.originatingUid = originatingUid;
1003         }
1004 
1005         /**
1006          * Optionally set the URI that referred you to install this package. This is
1007          * informational and may be used as a signal for anti-malware purposes.
1008          *
1009          * @see Intent#EXTRA_REFERRER
1010          */
setReferrerUri(@ullable Uri referrerUri)1011         public void setReferrerUri(@Nullable Uri referrerUri) {
1012             this.referrerUri = referrerUri;
1013         }
1014 
1015         /**
1016          * Sets which runtime permissions to be granted to the package at installation.
1017          * Using this API requires holding {@link android.Manifest.permission
1018          * #INSTALL_GRANT_RUNTIME_PERMISSIONS}
1019          *
1020          * @param permissions The permissions to grant or null to grant all runtime
1021          *     permissions.
1022          *
1023          * @hide
1024          */
1025         @SystemApi
1026         @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS)
setGrantedRuntimePermissions(String[] permissions)1027         public void setGrantedRuntimePermissions(String[] permissions) {
1028             installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
1029             this.grantedRuntimePermissions = permissions;
1030         }
1031 
1032         /** {@hide} */
setInstallFlagsInternal()1033         public void setInstallFlagsInternal() {
1034             installFlags |= PackageManager.INSTALL_INTERNAL;
1035             installFlags &= ~PackageManager.INSTALL_EXTERNAL;
1036         }
1037 
1038         /** {@hide} */
1039         @SystemApi
setAllowDowngrade(boolean allowDowngrade)1040         public void setAllowDowngrade(boolean allowDowngrade) {
1041             if (allowDowngrade) {
1042                 installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
1043             } else {
1044                 installFlags &= ~PackageManager.INSTALL_ALLOW_DOWNGRADE;
1045             }
1046         }
1047 
1048         /** {@hide} */
setInstallFlagsExternal()1049         public void setInstallFlagsExternal() {
1050             installFlags |= PackageManager.INSTALL_EXTERNAL;
1051             installFlags &= ~PackageManager.INSTALL_INTERNAL;
1052         }
1053 
1054         /** {@hide} */
setInstallFlagsForcePermissionPrompt()1055         public void setInstallFlagsForcePermissionPrompt() {
1056             installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
1057         }
1058 
1059         /** {@hide} */
1060         @SystemApi
setDontKillApp(boolean dontKillApp)1061         public void setDontKillApp(boolean dontKillApp) {
1062             if (dontKillApp) {
1063                 installFlags |= PackageManager.INSTALL_DONT_KILL_APP;
1064             } else {
1065                 installFlags &= ~PackageManager.INSTALL_DONT_KILL_APP;
1066             }
1067         }
1068 
1069         /** {@hide} */
dump(IndentingPrintWriter pw)1070         public void dump(IndentingPrintWriter pw) {
1071             pw.printPair("mode", mode);
1072             pw.printHexPair("installFlags", installFlags);
1073             pw.printPair("installLocation", installLocation);
1074             pw.printPair("sizeBytes", sizeBytes);
1075             pw.printPair("appPackageName", appPackageName);
1076             pw.printPair("appIcon", (appIcon != null));
1077             pw.printPair("appLabel", appLabel);
1078             pw.printPair("originatingUri", originatingUri);
1079             pw.printPair("originatingUid", originatingUid);
1080             pw.printPair("referrerUri", referrerUri);
1081             pw.printPair("abiOverride", abiOverride);
1082             pw.printPair("volumeUuid", volumeUuid);
1083             pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions);
1084             pw.println();
1085         }
1086 
1087         @Override
describeContents()1088         public int describeContents() {
1089             return 0;
1090         }
1091 
1092         @Override
writeToParcel(Parcel dest, int flags)1093         public void writeToParcel(Parcel dest, int flags) {
1094             dest.writeInt(mode);
1095             dest.writeInt(installFlags);
1096             dest.writeInt(installLocation);
1097             dest.writeLong(sizeBytes);
1098             dest.writeString(appPackageName);
1099             dest.writeParcelable(appIcon, flags);
1100             dest.writeString(appLabel);
1101             dest.writeParcelable(originatingUri, flags);
1102             dest.writeInt(originatingUid);
1103             dest.writeParcelable(referrerUri, flags);
1104             dest.writeString(abiOverride);
1105             dest.writeString(volumeUuid);
1106             dest.writeStringArray(grantedRuntimePermissions);
1107         }
1108 
1109         public static final Parcelable.Creator<SessionParams>
1110                 CREATOR = new Parcelable.Creator<SessionParams>() {
1111                     @Override
1112                     public SessionParams createFromParcel(Parcel p) {
1113                         return new SessionParams(p);
1114                     }
1115 
1116                     @Override
1117                     public SessionParams[] newArray(int size) {
1118                         return new SessionParams[size];
1119                     }
1120                 };
1121     }
1122 
1123     /**
1124      * Details for an active install session.
1125      */
1126     public static class SessionInfo implements Parcelable {
1127 
1128         /** {@hide} */
1129         public int sessionId;
1130         /** {@hide} */
1131         public String installerPackageName;
1132         /** {@hide} */
1133         public String resolvedBaseCodePath;
1134         /** {@hide} */
1135         public float progress;
1136         /** {@hide} */
1137         public boolean sealed;
1138         /** {@hide} */
1139         public boolean active;
1140 
1141         /** {@hide} */
1142         public int mode;
1143         /** {@hide} */
1144         public long sizeBytes;
1145         /** {@hide} */
1146         public String appPackageName;
1147         /** {@hide} */
1148         public Bitmap appIcon;
1149         /** {@hide} */
1150         public CharSequence appLabel;
1151 
1152         /** {@hide} */
SessionInfo()1153         public SessionInfo() {
1154         }
1155 
1156         /** {@hide} */
SessionInfo(Parcel source)1157         public SessionInfo(Parcel source) {
1158             sessionId = source.readInt();
1159             installerPackageName = source.readString();
1160             resolvedBaseCodePath = source.readString();
1161             progress = source.readFloat();
1162             sealed = source.readInt() != 0;
1163             active = source.readInt() != 0;
1164 
1165             mode = source.readInt();
1166             sizeBytes = source.readLong();
1167             appPackageName = source.readString();
1168             appIcon = source.readParcelable(null);
1169             appLabel = source.readString();
1170         }
1171 
1172         /**
1173          * Return the ID for this session.
1174          */
getSessionId()1175         public int getSessionId() {
1176             return sessionId;
1177         }
1178 
1179         /**
1180          * Return the package name of the app that owns this session.
1181          */
getInstallerPackageName()1182         public @Nullable String getInstallerPackageName() {
1183             return installerPackageName;
1184         }
1185 
1186         /**
1187          * Return current overall progress of this session, between 0 and 1.
1188          * <p>
1189          * Note that this progress may not directly correspond to the value
1190          * reported by
1191          * {@link PackageInstaller.Session#setStagingProgress(float)}, as the
1192          * system may carve out a portion of the overall progress to represent
1193          * its own internal installation work.
1194          */
getProgress()1195         public float getProgress() {
1196             return progress;
1197         }
1198 
1199         /**
1200          * Return if this session is currently active.
1201          * <p>
1202          * A session is considered active whenever there is ongoing forward
1203          * progress being made, such as the installer holding an open
1204          * {@link Session} instance while streaming data into place, or the
1205          * system optimizing code as the result of
1206          * {@link Session#commit(IntentSender)}.
1207          * <p>
1208          * If the installer closes the {@link Session} without committing, the
1209          * session is considered inactive until the installer opens the session
1210          * again.
1211          */
isActive()1212         public boolean isActive() {
1213             return active;
1214         }
1215 
1216         /** {@hide} */
1217         @Deprecated
isOpen()1218         public boolean isOpen() {
1219             return isActive();
1220         }
1221 
1222         /**
1223          * Return the package name this session is working with. May be {@code null}
1224          * if unknown.
1225          */
getAppPackageName()1226         public @Nullable String getAppPackageName() {
1227             return appPackageName;
1228         }
1229 
1230         /**
1231          * Return an icon representing the app being installed. May be {@code null}
1232          * if unavailable.
1233          */
getAppIcon()1234         public @Nullable Bitmap getAppIcon() {
1235             return appIcon;
1236         }
1237 
1238         /**
1239          * Return a label representing the app being installed. May be {@code null}
1240          * if unavailable.
1241          */
getAppLabel()1242         public @Nullable CharSequence getAppLabel() {
1243             return appLabel;
1244         }
1245 
1246         /**
1247          * Return an Intent that can be started to view details about this install
1248          * session. This may surface actions such as pause, resume, or cancel.
1249          * <p>
1250          * In some cases, a matching Activity may not exist, so ensure you safeguard
1251          * against this.
1252          *
1253          * @see PackageInstaller#ACTION_SESSION_DETAILS
1254          */
createDetailsIntent()1255         public @Nullable Intent createDetailsIntent() {
1256             final Intent intent = new Intent(PackageInstaller.ACTION_SESSION_DETAILS);
1257             intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
1258             intent.setPackage(installerPackageName);
1259             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1260             return intent;
1261         }
1262 
1263         /** {@hide} */
1264         @Deprecated
getDetailsIntent()1265         public @Nullable Intent getDetailsIntent() {
1266             return createDetailsIntent();
1267         }
1268 
1269         @Override
describeContents()1270         public int describeContents() {
1271             return 0;
1272         }
1273 
1274         @Override
writeToParcel(Parcel dest, int flags)1275         public void writeToParcel(Parcel dest, int flags) {
1276             dest.writeInt(sessionId);
1277             dest.writeString(installerPackageName);
1278             dest.writeString(resolvedBaseCodePath);
1279             dest.writeFloat(progress);
1280             dest.writeInt(sealed ? 1 : 0);
1281             dest.writeInt(active ? 1 : 0);
1282 
1283             dest.writeInt(mode);
1284             dest.writeLong(sizeBytes);
1285             dest.writeString(appPackageName);
1286             dest.writeParcelable(appIcon, flags);
1287             dest.writeString(appLabel != null ? appLabel.toString() : null);
1288         }
1289 
1290         public static final Parcelable.Creator<SessionInfo>
1291                 CREATOR = new Parcelable.Creator<SessionInfo>() {
1292                     @Override
1293                     public SessionInfo createFromParcel(Parcel p) {
1294                         return new SessionInfo(p);
1295                     }
1296 
1297                     @Override
1298                     public SessionInfo[] newArray(int size) {
1299                         return new SessionInfo[size];
1300                     }
1301                 };
1302     }
1303 }
1304