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