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