PackageInstaller.java revision f174c6e6de6ba863179401aa7b3d55d91ceed707
13a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey/*
23a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * Copyright (C) 2014 The Android Open Source Project
33a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey *
43a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
53a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * you may not use this file except in compliance with the License.
63a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * You may obtain a copy of the License at
73a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey *
83a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
93a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey *
103a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
113a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
123a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * See the License for the specific language governing permissions and
143a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey * limitations under the License.
153a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey */
163a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
173a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeypackage android.content.pm;
183a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkeyimport android.annotation.NonNull;
2016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkeyimport android.annotation.Nullable;
211cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkeyimport android.annotation.SdkConstant;
221cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkeyimport android.annotation.SdkConstant.SdkConstantType;
233a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.app.PackageInstallObserver;
243a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.app.PackageUninstallObserver;
25bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkeyimport android.os.Bundle;
2678cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport android.os.FileBridge;
2716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkeyimport android.os.Handler;
2816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkeyimport android.os.Looper;
2916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkeyimport android.os.Message;
303a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.os.ParcelFileDescriptor;
313a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeyimport android.os.RemoteException;
32a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkeyimport android.util.ExceptionUtils;
333a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
34ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkeyimport java.io.Closeable;
35a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkeyimport java.io.IOException;
361cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkeyimport java.io.InputStream;
3778cc340c2de873d6995c283b777476f7237d690fJeff Sharkeyimport java.io.OutputStream;
381cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkeyimport java.security.MessageDigest;
3916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkeyimport java.util.ArrayList;
4016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkeyimport java.util.Iterator;
41bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkeyimport java.util.List;
4278cc340c2de873d6995c283b777476f7237d690fJeff Sharkey
436c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey/**
446c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * Offers the ability to install, upgrade, and remove applications on the
456c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * device. This includes support for apps packaged either as a single
466c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * "monolithic" APK, or apps packaged as multiple "split" APKs.
476c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * <p>
486c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * An app is delivered for installation through a
496c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * {@link PackageInstaller.Session}, which any app can create. Once the session
506c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * is created, the installer can stream one or more APKs into place until it
516c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * decides to either commit or destroy the session. Committing may require user
526c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * intervention to complete the installation.
536c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * <p>
546c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * Sessions can install brand new apps, upgrade existing apps, or add new splits
55da96e137bcc8191c584ada7b5de31eaae92f244fJeff Sharkey * into an existing app.
566c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * <p>
57da96e137bcc8191c584ada7b5de31eaae92f244fJeff Sharkey * Apps packaged as multiple split APKs always consist of a single "base" APK
586c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * (with a {@code null} split name) and zero or more "split" APKs (with unique
596c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * split names). Any subset of these APKs can be installed together, as long as
606c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * the following constraints are met:
616c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * <ul>
626c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * <li>All APKs must have the exact same package name, version code, and signing
636c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * certificates.
646c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * <li>All APKs must have unique split names.
65da96e137bcc8191c584ada7b5de31eaae92f244fJeff Sharkey * <li>All installations must contain a single base APK.
666c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey * </ul>
676c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey */
683a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkeypublic class PackageInstaller {
691cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey    /**
701cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * Activity Action: Show details about a particular install session. This
711cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * may surface actions such as pause, resume, or cancel.
721cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * <p>
731cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * This should always be scoped to the installer package that owns the
741cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * session. Clients should use {@link InstallSessionInfo#getDetailsIntent()}
751cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * to build this intent correctly.
761cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * <p>
771cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * In some cases, a matching Activity may not exist, so ensure you safeguard
781cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * against this.
791cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     */
801cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
811cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey    public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
821cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey
831cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey    /**
841cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * An integer session ID.
851cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     *
861cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * @see #ACTION_SESSION_DETAILS
871cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     */
881cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey    public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
891cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey
903a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private final PackageManager mPm;
913a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private final IPackageInstaller mInstaller;
923a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private final int mUserId;
933a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    private final String mInstallerPackageName;
943a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
9516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    private final ArrayList<SessionCallbackDelegate> mDelegates = new ArrayList<>();
9616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
973a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    /** {@hide} */
98ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    public PackageInstaller(PackageManager pm, IPackageInstaller installer,
99ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey            String installerPackageName, int userId) {
1003a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        mPm = pm;
1013a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        mInstaller = installer;
1023a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        mInstallerPackageName = installerPackageName;
103ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        mUserId = userId;
1043a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
1053a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1066c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    /**
1076c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * Create a new session using the given parameters, returning a unique ID
1086c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * that represents the session. Once created, the session can be opened
1096c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * multiple times across multiple device boots.
1106c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * <p>
1116c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * The system may automatically destroy sessions that have not been
1126c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * finalized (either committed or abandoned) within a reasonable period of
1136c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * time, typically on the order of a day.
1146c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     *
1156c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * @throws IOException if parameters were unsatisfiable, such as lack of
1166c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     *             disk space or unavailable media.
117f174c6e6de6ba863179401aa7b3d55d91ceed707Jeff Sharkey     * @return positive, non-zero unique ID that represents the created session.
118f174c6e6de6ba863179401aa7b3d55d91ceed707Jeff Sharkey     *         This ID remains consistent across device reboots until the
119f174c6e6de6ba863179401aa7b3d55d91ceed707Jeff Sharkey     *         session is finalized. IDs are not reused during a given boot.
1206c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     */
12116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public int createSession(@NonNull InstallSessionParams params) throws IOException {
1223a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        try {
12316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            return mInstaller.createSession(params, mInstallerPackageName, mUserId);
124a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        } catch (RuntimeException e) {
125a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            ExceptionUtils.maybeUnwrapIOException(e);
126a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            throw e;
1273a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        } catch (RemoteException e) {
1283a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            throw e.rethrowAsRuntimeException();
1293a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
1303a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
1313a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1326c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    /**
13316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * Open an existing session to actively perform work. To succeed, the caller
13416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * must be the owner of the install session.
1356c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     */
13616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public @NonNull Session openSession(int sessionId) {
1373a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        try {
1383a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            return new Session(mInstaller.openSession(sessionId));
1393a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        } catch (RemoteException e) {
1403a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            throw e.rethrowAsRuntimeException();
1413a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
1423a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
1433a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1446c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    /**
14516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * Return details for a specific session. To succeed, the caller must either
14616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * own this session, or be the current home app.
1476c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     */
14816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public @Nullable InstallSessionInfo getSessionInfo(int sessionId) {
1493a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        try {
15016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            return mInstaller.getSessionInfo(sessionId);
15116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        } catch (RemoteException e) {
15216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            throw e.rethrowAsRuntimeException();
15316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        }
15416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    }
15516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
15616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    /**
15716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * Return list of all active install sessions, regardless of the installer.
15816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * To succeed, the caller must be the current home app.
15916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     */
16016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public @NonNull List<InstallSessionInfo> getAllSessions() {
16116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        try {
16216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            return mInstaller.getAllSessions(mUserId);
16316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        } catch (RemoteException e) {
16416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            throw e.rethrowAsRuntimeException();
16516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        }
16616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    }
16716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
16816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    /**
16916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * Return list of all install sessions owned by the calling app.
17016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     */
17116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public @NonNull List<InstallSessionInfo> getMySessions() {
17216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        try {
17316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            return mInstaller.getMySessions(mInstallerPackageName, mUserId);
1743a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        } catch (RemoteException e) {
1753a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            throw e.rethrowAsRuntimeException();
1763a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
1773a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
1783a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1796c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    /**
1806c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * Uninstall the given package, removing it completely from the device. This
1816c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * method is only available to the current "installer of record" for the
1826c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * package.
1836c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     */
18416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public void uninstall(@NonNull String packageName, @NonNull UninstallCallback callback) {
1853a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        try {
186bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey            mInstaller.uninstall(packageName, 0,
18716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    new UninstallCallbackDelegate(callback).getBinder(), mUserId);
1883a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        } catch (RemoteException e) {
1893a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            throw e.rethrowAsRuntimeException();
1903a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
1913a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
1923a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
1936c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    /**
1946c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * Uninstall only a specific split from the given package.
1956c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     *
1966c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * @hide
1976c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     */
19816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public void uninstall(@NonNull String packageName, @NonNull String splitName,
19916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            @NonNull UninstallCallback callback) {
2003a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        try {
201bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey            mInstaller.uninstallSplit(packageName, splitName, 0,
20216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    new UninstallCallbackDelegate(callback).getBinder(), mUserId);
203bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        } catch (RemoteException e) {
204bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey            throw e.rethrowAsRuntimeException();
205bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        }
206bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey    }
207bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
2086c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    /**
2096c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * Events for observing session lifecycle.
2101cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * <p>
2111cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * A typical session lifecycle looks like this:
2121cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * <ul>
2131cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * <li>An installer creates a session to indicate pending app delivery. All
2141cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * install details are available at this point.
2151cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * <li>The installer opens the session to deliver APK data. Note that a
2161cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * session may be opened and closed multiple times as network connectivity
2171cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * changes. The installer may deliver periodic progress updates.
2181cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * <li>The installer commits or abandons the session, resulting in the
2191cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * session being finished.
2201cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey     * </ul>
2216c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     */
22216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public static abstract class SessionCallback {
2236c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey        /**
2241cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * New session has been created. Details about the session can be
2251cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * obtained from {@link PackageInstaller#getSessionInfo(int)}.
2266c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         */
22716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public abstract void onCreated(int sessionId);
2286c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey
2296c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey        /**
2301cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * Session has been opened. A session is usually opened when the
2311cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * installer is actively writing data.
2321cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         */
2331cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        public abstract void onOpened(int sessionId);
2341cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey
2351cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        /**
2366c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * Progress for given session has been updated.
2376c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * <p>
2386c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * Note that this progress may not directly correspond to the value
23916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * reported by {@link PackageInstaller.Session#setProgress(float)}, as
24016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * the system may carve out a portion of the overall progress to
24116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * represent its own internal installation work.
2426c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         */
24316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public abstract void onProgressChanged(int sessionId, float progress);
2446c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey
2456c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey        /**
2461cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * Session has been closed.
2471cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         */
2481cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        public abstract void onClosed(int sessionId);
2491cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey
2501cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        /**
25116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * Session has completely finished, either with success or failure.
2526c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         */
25316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public abstract void onFinished(int sessionId, boolean success);
25416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    }
25516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
25616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    /** {@hide} */
25716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    private static class SessionCallbackDelegate extends IPackageInstallerCallback.Stub implements
25816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            Handler.Callback {
25916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        private static final int MSG_SESSION_CREATED = 1;
2601cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        private static final int MSG_SESSION_OPENED = 2;
2611cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        private static final int MSG_SESSION_PROGRESS_CHANGED = 3;
2621cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        private static final int MSG_SESSION_CLOSED = 4;
2631cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        private static final int MSG_SESSION_FINISHED = 5;
26416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
26516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        final SessionCallback mCallback;
26616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        final Handler mHandler;
26716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
26816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public SessionCallbackDelegate(SessionCallback callback, Looper looper) {
26916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            mCallback = callback;
27016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            mHandler = new Handler(looper, this);
27116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        }
27216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
27316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        @Override
27416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public boolean handleMessage(Message msg) {
27516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            switch (msg.what) {
27616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                case MSG_SESSION_CREATED:
27716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    mCallback.onCreated(msg.arg1);
27816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    return true;
2791cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                case MSG_SESSION_OPENED:
2801cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                    mCallback.onOpened(msg.arg1);
2811cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                    return true;
28216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                case MSG_SESSION_PROGRESS_CHANGED:
28316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    mCallback.onProgressChanged(msg.arg1, (float) msg.obj);
28416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    return true;
2851cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                case MSG_SESSION_CLOSED:
2861cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                    mCallback.onClosed(msg.arg1);
2871cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                    return true;
28816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                case MSG_SESSION_FINISHED:
28916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    mCallback.onFinished(msg.arg1, msg.arg2 != 0);
29016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    return true;
29116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            }
29216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            return false;
29316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        }
29416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
29516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        @Override
29616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public void onSessionCreated(int sessionId) {
29716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            mHandler.obtainMessage(MSG_SESSION_CREATED, sessionId, 0).sendToTarget();
29816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        }
29916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
30016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        @Override
3011cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        public void onSessionOpened(int sessionId) {
3021cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey            mHandler.obtainMessage(MSG_SESSION_OPENED, sessionId, 0).sendToTarget();
3031cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        }
3041cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey
3051cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        @Override
30616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public void onSessionProgressChanged(int sessionId, float progress) {
30716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            mHandler.obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, 0, progress)
30816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    .sendToTarget();
30916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        }
31016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
31116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        @Override
3121cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        public void onSessionClosed(int sessionId) {
3131cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey            mHandler.obtainMessage(MSG_SESSION_CLOSED, sessionId, 0).sendToTarget();
3141cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        }
3151cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey
3161cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        @Override
31716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public void onSessionFinished(int sessionId, boolean success) {
31816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            mHandler.obtainMessage(MSG_SESSION_FINISHED, sessionId, success ? 1 : 0)
31916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    .sendToTarget();
32016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        }
321bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey    }
322bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
3236c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    /**
32416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * Register to watch for session lifecycle events. To succeed, the caller
32516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * must be the current home app.
3266c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     */
32716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public void addSessionCallback(@NonNull SessionCallback callback) {
32816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        addSessionCallback(callback, new Handler());
32916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    }
33016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
33116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    /**
33216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * Register to watch for session lifecycle events. To succeed, the caller
33316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * must be the current home app.
33416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     *
33516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * @param handler to dispatch callback events through, otherwise uses
33616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     *            calling thread.
33716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     */
33816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public void addSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
33916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        synchronized (mDelegates) {
34016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback,
34116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    handler.getLooper());
34216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            try {
34316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                mInstaller.registerCallback(delegate, mUserId);
34416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            } catch (RemoteException e) {
34516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                throw e.rethrowAsRuntimeException();
34616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            }
34716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            mDelegates.add(delegate);
348bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        }
349bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey    }
350bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
3516c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    /**
35216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * Unregister an existing callback.
3536c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     */
35416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public void removeSessionCallback(@NonNull SessionCallback callback) {
35516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        synchronized (mDelegates) {
35616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            for (Iterator<SessionCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) {
35716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                final SessionCallbackDelegate delegate = i.next();
35816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                if (delegate.mCallback == callback) {
35916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    try {
36016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                        mInstaller.unregisterCallback(delegate);
36116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    } catch (RemoteException e) {
36216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                        throw e.rethrowAsRuntimeException();
36316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    }
36416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    i.remove();
36516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                }
36616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            }
3673a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
3683a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
3693a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
3703a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    /**
3713a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey     * An installation that is being actively staged. For an install to succeed,
3723a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey     * all existing and new packages must have identical package names, version
3733a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey     * codes, and signing certificates.
3743a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey     * <p>
3753a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey     * A session may contain any number of split packages. If the application
3763a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey     * does not yet exist, this session must include a base package.
3773a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey     * <p>
37816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * If an APK included in this session is already defined by the existing
37916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * installation (for example, the same split name), the APK in this session
38016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey     * will replace the existing APK.
3813a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey     */
382ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey    public static class Session implements Closeable {
3833a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        private IPackageInstallerSession mSession;
3843a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
3853a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        /** {@hide} */
3863a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        public Session(IPackageInstallerSession session) {
3873a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            mSession = session;
3883a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
3893a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
3906c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey        /**
39116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * Set current progress. Valid values are anywhere between 0 and 1.
3926c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         */
39316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public void setProgress(float progress) {
3943a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            try {
395a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                mSession.setClientProgress(progress);
396a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            } catch (RemoteException e) {
397a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                throw e.rethrowAsRuntimeException();
398a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            }
399a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        }
400a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
401a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        /** {@hide} */
40216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public void addProgress(float progress) {
403a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            try {
404a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                mSession.addClientProgress(progress);
4053a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            } catch (RemoteException e) {
4063a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                throw e.rethrowAsRuntimeException();
4073a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            }
4083a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
4093a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
41078cc340c2de873d6995c283b777476f7237d690fJeff Sharkey        /**
41116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * Open a stream to write an APK file into the session.
41216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * <p>
41316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * The returned stream will start writing data at the requested offset
41416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * in the underlying file, which can be used to resume a partially
41516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * written file. If a valid file length is specified, the system will
41616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * preallocate the underlying disk space to optimize placement on disk.
41716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * It's strongly recommended to provide a valid file length when known.
41816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * <p>
41916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * You can write data into the returned stream, optionally call
42016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * {@link #fsync(OutputStream)} as needed to ensure bytes have been
42116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * persisted to disk, and then close when finished. All streams must be
42216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * closed before calling {@link #commit(CommitCallback)}.
42316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         *
42416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * @param name arbitrary, unique name of your choosing to identify the
42516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         *            APK being written. You can open a file again for
42616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         *            additional writes (such as after a reboot) by using the
42716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         *            same name. This name is only meaningful within the context
42816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         *            of a single install session.
42916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * @param offsetBytes offset into the file to begin writing at, or 0 to
43016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         *            start at the beginning of the file.
43116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * @param lengthBytes total size of the file being written, used to
43216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         *            preallocate the underlying disk space, or -1 if unknown.
43378cc340c2de873d6995c283b777476f7237d690fJeff Sharkey         */
43416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
43516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                long lengthBytes) throws IOException {
4363a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            try {
43716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
43878cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                        offsetBytes, lengthBytes);
43978cc340c2de873d6995c283b777476f7237d690fJeff Sharkey                return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor());
440a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            } catch (RuntimeException e) {
441a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                ExceptionUtils.maybeUnwrapIOException(e);
442a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                throw e;
4433a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            } catch (RemoteException e) {
4441cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                throw e.rethrowAsRuntimeException();
4453a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            }
4463a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
4473a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
4486c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey        /**
4496c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * Ensure that any outstanding data for given stream has been committed
4506c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * to disk. This is only valid for streams returned from
4516c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * {@link #openWrite(String, long, long)}.
4526c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         */
45316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public void fsync(@NonNull OutputStream out) throws IOException {
454a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            if (out instanceof FileBridge.FileBridgeOutputStream) {
455a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                ((FileBridge.FileBridgeOutputStream) out).fsync();
456a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            } else {
457a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey                throw new IllegalArgumentException("Unrecognized stream");
458a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey            }
459a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey        }
460a10311434778ea1be1621c2251c0c8c2966f337bJeff Sharkey
4616c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey        /**
4621cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * List all APK names contained in this session.
4631cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * <p>
4641cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * This returns all names which have been previously written through
4651cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * {@link #openWrite(String, long, long)} as part of this session.
4661cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         */
4671cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        public @NonNull String[] list() {
4681cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey            try {
4691cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                return mSession.list();
4701cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey            } catch (RemoteException e) {
4711cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                throw e.rethrowAsRuntimeException();
4721cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey            }
4731cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        }
4741cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey
4751cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        /**
4761cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * Open a stream to read an APK file from the session.
4771cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * <p>
4781cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * This is only valid for names which have been previously written
4791cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * through {@link #openWrite(String, long, long)} as part of this
4801cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * session. For example, this stream may be used to calculate a
4811cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         * {@link MessageDigest} of a written APK before committing.
4821cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey         */
4831cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        public @NonNull InputStream openRead(@NonNull String name) throws IOException {
4841cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey            try {
4851cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                final ParcelFileDescriptor pfd = mSession.openRead(name);
4861cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
4871cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey            } catch (RuntimeException e) {
4881cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                ExceptionUtils.maybeUnwrapIOException(e);
4891cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                throw e;
4901cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey            } catch (RemoteException e) {
4911cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey                throw e.rethrowAsRuntimeException();
4921cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey            }
4931cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        }
4941cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey
4951cb2d0d4bba387665128c62c342e59103ea4be26Jeff Sharkey        /**
4966c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * Attempt to commit everything staged in this session. This may require
4976c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * user intervention, and so it may not happen immediately. The final
4986c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * result of the commit will be reported through the given callback.
4996c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * <p>
5006c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * Once this method is called, no additional mutations may be performed
5016c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * on the session. If the device reboots before the session has been
5026c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * finalized, you may commit the session again.
5036c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         */
50416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public void commit(@NonNull CommitCallback callback) {
5053a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            try {
50616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                mSession.commit(new CommitCallbackDelegate(callback).getBinder());
5073a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            } catch (RemoteException e) {
5083a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                throw e.rethrowAsRuntimeException();
5093a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            }
5103a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
5113a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
5126c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey        /**
5136c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * Release this session object. You can open the session again if it
5146c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * hasn't been finalized.
5156c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         */
516ec55ef0934b8e0d1bb705434947de817f7be57f1Jeff Sharkey        @Override
5173a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        public void close() {
51816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            try {
51916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                mSession.close();
52016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            } catch (RemoteException e) {
52116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                throw e.rethrowAsRuntimeException();
52216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            }
5233a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
5243a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey
5256c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey        /**
52616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * Completely abandon this session, destroying all staged data and
52716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * rendering it invalid.
5286c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         */
52916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public void abandon() {
5303a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            try {
53116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                mSession.abandon();
5323a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            } catch (RemoteException e) {
5333a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey                throw e.rethrowAsRuntimeException();
5343a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey            }
5353a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey        }
5363a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey    }
537bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
5386c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    /**
5396c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * Final result of an uninstall request.
5406c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     */
54116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public static abstract class UninstallCallback {
542bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        public abstract void onSuccess();
543bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        public abstract void onFailure(String msg);
544bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey    }
545bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
5466c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    /** {@hide} */
54716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    private static class UninstallCallbackDelegate extends PackageUninstallObserver {
54816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        private final UninstallCallback target;
549bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
55016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public UninstallCallbackDelegate(UninstallCallback target) {
551bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey            this.target = target;
552bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        }
553bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
554bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        @Override
555bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        public void onUninstallFinished(String basePackageName, int returnCode) {
55616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            if (returnCode == PackageManager.DELETE_SUCCEEDED) {
55716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                target.onSuccess();
55816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            } else {
55916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                final String msg = PackageManager.deleteStatusToString(returnCode);
56016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                target.onFailure(msg);
561bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey            }
562bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        }
563bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey    }
564bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
5656c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    /**
5666c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     * Final result of a session commit request.
5676c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey     */
56816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    public static abstract class CommitCallback {
5696c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey        /**
57016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * Generic unknown failure. The system will always try to provide a more
57116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * specific failure reason, but in some rare cases this may be
57216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * delivered.
5736c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         */
57416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public static final int FAILURE_UNKNOWN = 0;
575bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
576bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        /**
577bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * One or more of the APKs included in the session was invalid. For
578bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * example, they might be malformed, corrupt, incorrectly signed,
579bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * mismatched, etc. The installer may want to try downloading and
580bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * installing again.
581bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         */
58216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public static final int FAILURE_INVALID = 1;
583bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
584bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        /**
5856c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * This install session conflicts (or is inconsistent with) with another
5866c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * package already installed on the device. For example, an existing
5876c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * permission, incompatible certificates, etc. The user may be able to
5886c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey         * uninstall another app to fix the issue.
58916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * <p>
59016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * The extras bundle may contain {@link #EXTRA_PACKAGE_NAME} if one
59116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * specific package was identified as the cause of the conflict. If
59216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey         * unknown, or multiple packages, the extra may be {@code null}.
593bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         */
59416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public static final int FAILURE_CONFLICT = 2;
595bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
596bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        /**
597bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * This install session failed due to storage issues. For example,
598bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * the device may be running low on space, or the required external
599bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * media may be unavailable. The user may be able to help free space
600bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * or insert the correct media.
601bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         */
60216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public static final int FAILURE_STORAGE = 3;
603bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
604bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        /**
605bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * This install session is fundamentally incompatible with this
606bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * device. For example, the package may require a hardware feature
607bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * that doesn't exist, it may be missing native code for the device
608bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * ABI, or it requires a newer SDK version, etc. This install would
609bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         * never succeed.
610bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey         */
61116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public static final int FAILURE_INCOMPATIBLE = 4;
61216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
61316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
61416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
61516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public abstract void onSuccess();
61616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public abstract void onFailure(int failureReason, String msg, Bundle extras);
617bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey    }
618bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
6196c833e07a05c48ca60ee4d72421bf8b1e78dc710Jeff Sharkey    /** {@hide} */
62016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey    private static class CommitCallbackDelegate extends PackageInstallObserver {
62116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        private final CommitCallback target;
622bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
62316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey        public CommitCallbackDelegate(CommitCallback target) {
624bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey            this.target = target;
625bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        }
626bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey
627bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        @Override
628bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        public void packageInstalled(String basePackageName, Bundle extras, int returnCode,
629bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey                String msg) {
63016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
63116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                target.onSuccess();
63216c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey            } else {
63316c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                final int failureReason = PackageManager.installStatusToFailureReason(returnCode);
63416c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                msg = PackageManager.installStatusToString(returnCode) + ": " + msg;
63516c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
63616c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                if (extras != null) {
63716c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                    extras.putString(CommitCallback.EXTRA_PACKAGE_NAME,
63816c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                            extras.getString(PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE));
63916c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                }
64016c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey
64116c8e3f49497b6046972ae650772f65768366be8Jeff Sharkey                target.onFailure(failureReason, msg, extras);
642bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey            }
643bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey        }
644bb580670350b76fa2fcc5ee873f99b7970759cbfJeff Sharkey    }
6453a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey}
646