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