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