1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.content.pm;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.annotation.RequiresPermission;
22import android.annotation.SdkConstant;
23import android.annotation.SdkConstant.SdkConstantType;
24import android.annotation.SystemApi;
25import android.app.ActivityManager;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentSender;
29import android.graphics.Bitmap;
30import android.net.Uri;
31import android.os.FileBridge;
32import android.os.Handler;
33import android.os.Looper;
34import android.os.Message;
35import android.os.Parcel;
36import android.os.ParcelFileDescriptor;
37import android.os.Parcelable;
38import android.os.RemoteException;
39import android.util.ExceptionUtils;
40
41import com.android.internal.util.IndentingPrintWriter;
42
43import java.io.Closeable;
44import java.io.IOException;
45import java.io.InputStream;
46import java.io.OutputStream;
47import java.security.MessageDigest;
48import java.util.ArrayList;
49import java.util.Iterator;
50import java.util.List;
51
52/**
53 * Offers the ability to install, upgrade, and remove applications on the
54 * device. This includes support for apps packaged either as a single
55 * "monolithic" APK, or apps packaged as multiple "split" APKs.
56 * <p>
57 * An app is delivered for installation through a
58 * {@link PackageInstaller.Session}, which any app can create. Once the session
59 * is created, the installer can stream one or more APKs into place until it
60 * decides to either commit or destroy the session. Committing may require user
61 * intervention to complete the installation.
62 * <p>
63 * Sessions can install brand new apps, upgrade existing apps, or add new splits
64 * into an existing app.
65 * <p>
66 * Apps packaged as multiple split APKs always consist of a single "base" APK
67 * (with a {@code null} split name) and zero or more "split" APKs (with unique
68 * split names). Any subset of these APKs can be installed together, as long as
69 * the following constraints are met:
70 * <ul>
71 * <li>All APKs must have the exact same package name, version code, and signing
72 * certificates.
73 * <li>All APKs must have unique split names.
74 * <li>All installations must contain a single base APK.
75 * </ul>
76 */
77public class PackageInstaller {
78    private static final String TAG = "PackageInstaller";
79
80    /**
81     * Activity Action: Show details about a particular install session. This
82     * may surface actions such as pause, resume, or cancel.
83     * <p>
84     * This should always be scoped to the installer package that owns the
85     * session. Clients should use {@link SessionInfo#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} */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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} */
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         */
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         */
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         */
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         */
485        public abstract void onProgressChanged(int sessionId, float progress);
486
487        /**
488         * Session has completely finished, either with success or failure.
489         */
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
505        public SessionCallbackDelegate(SessionCallback callback, Looper looper) {
506            mCallback = callback;
507            mHandler = new Handler(looper, this);
508        }
509
510        @Override
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
535        public void onSessionCreated(int sessionId) {
536            mHandler.obtainMessage(MSG_SESSION_CREATED, sessionId, 0).sendToTarget();
537        }
538
539        @Override
540        public void onSessionBadgingChanged(int sessionId) {
541            mHandler.obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, 0).sendToTarget();
542        }
543
544        @Override
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
551        public void onSessionProgressChanged(int sessionId, float progress) {
552            mHandler.obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, 0, progress)
553                    .sendToTarget();
554        }
555
556        @Override
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
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     */
573    public void registerSessionCallback(@NonNull SessionCallback callback) {
574        registerSessionCallback(callback, new Handler());
575    }
576
577    /** {@hide} */
578    @Deprecated
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     */
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
605    public void removeSessionCallback(@NonNull SessionCallback callback) {
606        unregisterSessionCallback(callback);
607    }
608
609    /**
610     * Unregister a previously registered callback.
611     */
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} */
644        public Session(IPackageInstallerSession session) {
645            mSession = session;
646        }
647
648        /** {@hide} */
649        @Deprecated
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         */
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} */
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         */
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         */
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         */
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         */
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         */
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         */
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
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         */
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         */
913        public SessionParams(int mode) {
914            this.mode = mode;
915        }
916
917        /** {@hide} */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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)
1027        public void setGrantedRuntimePermissions(String[] permissions) {
1028            installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
1029            this.grantedRuntimePermissions = permissions;
1030        }
1031
1032        /** {@hide} */
1033        public void setInstallFlagsInternal() {
1034            installFlags |= PackageManager.INSTALL_INTERNAL;
1035            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
1036        }
1037
1038        /** {@hide} */
1039        @SystemApi
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} */
1049        public void setInstallFlagsExternal() {
1050            installFlags |= PackageManager.INSTALL_EXTERNAL;
1051            installFlags &= ~PackageManager.INSTALL_INTERNAL;
1052        }
1053
1054        /** {@hide} */
1055        public void setInstallFlagsForcePermissionPrompt() {
1056            installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
1057        }
1058
1059        /** {@hide} */
1060        @SystemApi
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} */
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
1088        public int describeContents() {
1089            return 0;
1090        }
1091
1092        @Override
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} */
1153        public SessionInfo() {
1154        }
1155
1156        /** {@hide} */
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         */
1175        public int getSessionId() {
1176            return sessionId;
1177        }
1178
1179        /**
1180         * Return the package name of the app that owns this session.
1181         */
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         */
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         */
1212        public boolean isActive() {
1213            return active;
1214        }
1215
1216        /** {@hide} */
1217        @Deprecated
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         */
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         */
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         */
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         */
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
1265        public @Nullable Intent getDetailsIntent() {
1266            return createDetailsIntent();
1267        }
1268
1269        @Override
1270        public int describeContents() {
1271            return 0;
1272        }
1273
1274        @Override
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