Home | History | Annotate | Download | only in pm
      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 
     17 package android.content.pm;
     18 
     19 import android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.annotation.SdkConstant;
     22 import android.annotation.SdkConstant.SdkConstantType;
     23 import android.app.ActivityManager;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentSender;
     27 import android.graphics.Bitmap;
     28 import android.net.Uri;
     29 import android.os.FileBridge;
     30 import android.os.Handler;
     31 import android.os.Looper;
     32 import android.os.Message;
     33 import android.os.Parcel;
     34 import android.os.ParcelFileDescriptor;
     35 import android.os.Parcelable;
     36 import android.os.RemoteException;
     37 import android.util.ExceptionUtils;
     38 import android.util.Log;
     39 
     40 import com.android.internal.util.IndentingPrintWriter;
     41 
     42 import java.io.Closeable;
     43 import java.io.IOException;
     44 import java.io.InputStream;
     45 import java.io.OutputStream;
     46 import java.security.MessageDigest;
     47 import java.util.ArrayList;
     48 import java.util.Collections;
     49 import java.util.Iterator;
     50 import 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  */
     77 public 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#createDetailsIntent()} 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      * @throws SecurityException when installation services are unavailable,
    289      *             such as when called from a restricted user.
    290      * @throws IllegalArgumentException when {@link SessionParams} is invalid.
    291      * @return positive, non-zero unique ID that represents the created session.
    292      *         This ID remains consistent across device reboots until the
    293      *         session is finalized. IDs are not reused during a given boot.
    294      */
    295     public int createSession(@NonNull SessionParams params) throws IOException {
    296         try {
    297             return mInstaller.createSession(params, mInstallerPackageName, mUserId);
    298         } catch (RuntimeException e) {
    299             ExceptionUtils.maybeUnwrapIOException(e);
    300             throw e;
    301         } catch (RemoteException e) {
    302             throw e.rethrowAsRuntimeException();
    303         }
    304     }
    305 
    306     /**
    307      * Open an existing session to actively perform work. To succeed, the caller
    308      * must be the owner of the install session.
    309      *
    310      * @throws IOException if parameters were unsatisfiable, such as lack of
    311      *             disk space or unavailable media.
    312      * @throws SecurityException when the caller does not own the session, or
    313      *             the session is invalid.
    314      */
    315     public @NonNull Session openSession(int sessionId) throws IOException {
    316         try {
    317             return new Session(mInstaller.openSession(sessionId));
    318         } catch (RuntimeException e) {
    319             ExceptionUtils.maybeUnwrapIOException(e);
    320             throw e;
    321         } catch (RemoteException e) {
    322             throw e.rethrowAsRuntimeException();
    323         }
    324     }
    325 
    326     /**
    327      * Update the icon representing the app being installed in a specific
    328      * session. This should be roughly
    329      * {@link ActivityManager#getLauncherLargeIconSize()} in both dimensions.
    330      *
    331      * @throws SecurityException when the caller does not own the session, or
    332      *             the session is invalid.
    333      */
    334     public void updateSessionAppIcon(int sessionId, @Nullable Bitmap appIcon) {
    335         try {
    336             mInstaller.updateSessionAppIcon(sessionId, appIcon);
    337         } catch (RemoteException e) {
    338             throw e.rethrowAsRuntimeException();
    339         }
    340     }
    341 
    342     /**
    343      * Update the label representing the app being installed in a specific
    344      * session.
    345      *
    346      * @throws SecurityException when the caller does not own the session, or
    347      *             the session is invalid.
    348      */
    349     public void updateSessionAppLabel(int sessionId, @Nullable CharSequence appLabel) {
    350         try {
    351             final String val = (appLabel != null) ? appLabel.toString() : null;
    352             mInstaller.updateSessionAppLabel(sessionId, val);
    353         } catch (RemoteException e) {
    354             throw e.rethrowAsRuntimeException();
    355         }
    356     }
    357 
    358     /**
    359      * Completely abandon the given session, destroying all staged data and
    360      * rendering it invalid. Abandoned sessions will be reported to
    361      * {@link SessionCallback} listeners as failures. This is equivalent to
    362      * opening the session and calling {@link Session#abandon()}.
    363      *
    364      * @throws SecurityException when the caller does not own the session, or
    365      *             the session is invalid.
    366      */
    367     public void abandonSession(int sessionId) {
    368         try {
    369             mInstaller.abandonSession(sessionId);
    370         } catch (RemoteException e) {
    371             throw e.rethrowAsRuntimeException();
    372         }
    373     }
    374 
    375     /**
    376      * Return details for a specific session. No special permissions are
    377      * required to retrieve these details.
    378      *
    379      * @return details for the requested session, or {@code null} if the session
    380      *         does not exist.
    381      */
    382     public @Nullable SessionInfo getSessionInfo(int sessionId) {
    383         try {
    384             return mInstaller.getSessionInfo(sessionId);
    385         } catch (RemoteException e) {
    386             throw e.rethrowAsRuntimeException();
    387         }
    388     }
    389 
    390     /**
    391      * Return list of all known install sessions, regardless of the installer.
    392      */
    393     public @NonNull List<SessionInfo> getAllSessions() {
    394         final ApplicationInfo info = mContext.getApplicationInfo();
    395         if ("com.google.android.googlequicksearchbox".equals(info.packageName)
    396                 && info.versionCode <= 300400110) {
    397             Log.d(TAG, "Ignoring callback request from old prebuilt");
    398             return Collections.EMPTY_LIST;
    399         }
    400 
    401         try {
    402             return mInstaller.getAllSessions(mUserId).getList();
    403         } catch (RemoteException e) {
    404             throw e.rethrowAsRuntimeException();
    405         }
    406     }
    407 
    408     /**
    409      * Return list of all known install sessions owned by the calling app.
    410      */
    411     public @NonNull List<SessionInfo> getMySessions() {
    412         try {
    413             return mInstaller.getMySessions(mInstallerPackageName, mUserId).getList();
    414         } catch (RemoteException e) {
    415             throw e.rethrowAsRuntimeException();
    416         }
    417     }
    418 
    419     /**
    420      * Uninstall the given package, removing it completely from the device. This
    421      * method is only available to the current "installer of record" for the
    422      * package.
    423      */
    424     public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) {
    425         try {
    426             mInstaller.uninstall(packageName, 0, statusReceiver, mUserId);
    427         } catch (RemoteException e) {
    428             throw e.rethrowAsRuntimeException();
    429         }
    430     }
    431 
    432     /** {@hide} */
    433     public void setPermissionsResult(int sessionId, boolean accepted) {
    434         try {
    435             mInstaller.setPermissionsResult(sessionId, accepted);
    436         } catch (RemoteException e) {
    437             throw e.rethrowAsRuntimeException();
    438         }
    439     }
    440 
    441     /**
    442      * Events for observing session lifecycle.
    443      * <p>
    444      * A typical session lifecycle looks like this:
    445      * <ul>
    446      * <li>An installer creates a session to indicate pending app delivery. All
    447      * install details are available at this point.
    448      * <li>The installer opens the session to deliver APK data. Note that a
    449      * session may be opened and closed multiple times as network connectivity
    450      * changes. The installer may deliver periodic progress updates.
    451      * <li>The installer commits or abandons the session, resulting in the
    452      * session being finished.
    453      * </ul>
    454      */
    455     public static abstract class SessionCallback {
    456         /**
    457          * New session has been created. Details about the session can be
    458          * obtained from {@link PackageInstaller#getSessionInfo(int)}.
    459          */
    460         public abstract void onCreated(int sessionId);
    461 
    462         /**
    463          * Badging details for an existing session has changed. For example, the
    464          * app icon or label has been updated.
    465          */
    466         public abstract void onBadgingChanged(int sessionId);
    467 
    468         /**
    469          * Active state for session has been changed.
    470          * <p>
    471          * A session is considered active whenever there is ongoing forward
    472          * progress being made, such as the installer holding an open
    473          * {@link Session} instance while streaming data into place, or the
    474          * system optimizing code as the result of
    475          * {@link Session#commit(IntentSender)}.
    476          * <p>
    477          * If the installer closes the {@link Session} without committing, the
    478          * session is considered inactive until the installer opens the session
    479          * again.
    480          */
    481         public abstract void onActiveChanged(int sessionId, boolean active);
    482 
    483         /**
    484          * Progress for given session has been updated.
    485          * <p>
    486          * Note that this progress may not directly correspond to the value
    487          * reported by
    488          * {@link PackageInstaller.Session#setStagingProgress(float)}, as the
    489          * system may carve out a portion of the overall progress to represent
    490          * its own internal installation work.
    491          */
    492         public abstract void onProgressChanged(int sessionId, float progress);
    493 
    494         /**
    495          * Session has completely finished, either with success or failure.
    496          */
    497         public abstract void onFinished(int sessionId, boolean success);
    498     }
    499 
    500     /** {@hide} */
    501     private static class SessionCallbackDelegate extends IPackageInstallerCallback.Stub implements
    502             Handler.Callback {
    503         private static final int MSG_SESSION_CREATED = 1;
    504         private static final int MSG_SESSION_BADGING_CHANGED = 2;
    505         private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
    506         private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
    507         private static final int MSG_SESSION_FINISHED = 5;
    508 
    509         final SessionCallback mCallback;
    510         final Handler mHandler;
    511 
    512         public SessionCallbackDelegate(SessionCallback callback, Looper looper) {
    513             mCallback = callback;
    514             mHandler = new Handler(looper, this);
    515         }
    516 
    517         @Override
    518         public boolean handleMessage(Message msg) {
    519             final int sessionId = msg.arg1;
    520             switch (msg.what) {
    521                 case MSG_SESSION_CREATED:
    522                     mCallback.onCreated(sessionId);
    523                     return true;
    524                 case MSG_SESSION_BADGING_CHANGED:
    525                     mCallback.onBadgingChanged(sessionId);
    526                     return true;
    527                 case MSG_SESSION_ACTIVE_CHANGED:
    528                     final boolean active = msg.arg2 != 0;
    529                     mCallback.onActiveChanged(sessionId, active);
    530                     return true;
    531                 case MSG_SESSION_PROGRESS_CHANGED:
    532                     mCallback.onProgressChanged(sessionId, (float) msg.obj);
    533                     return true;
    534                 case MSG_SESSION_FINISHED:
    535                     mCallback.onFinished(sessionId, msg.arg2 != 0);
    536                     return true;
    537             }
    538             return false;
    539         }
    540 
    541         @Override
    542         public void onSessionCreated(int sessionId) {
    543             mHandler.obtainMessage(MSG_SESSION_CREATED, sessionId, 0).sendToTarget();
    544         }
    545 
    546         @Override
    547         public void onSessionBadgingChanged(int sessionId) {
    548             mHandler.obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, 0).sendToTarget();
    549         }
    550 
    551         @Override
    552         public void onSessionActiveChanged(int sessionId, boolean active) {
    553             mHandler.obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, active ? 1 : 0)
    554                     .sendToTarget();
    555         }
    556 
    557         @Override
    558         public void onSessionProgressChanged(int sessionId, float progress) {
    559             mHandler.obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, 0, progress)
    560                     .sendToTarget();
    561         }
    562 
    563         @Override
    564         public void onSessionFinished(int sessionId, boolean success) {
    565             mHandler.obtainMessage(MSG_SESSION_FINISHED, sessionId, success ? 1 : 0)
    566                     .sendToTarget();
    567         }
    568     }
    569 
    570     /** {@hide} */
    571     @Deprecated
    572     public void addSessionCallback(@NonNull SessionCallback callback) {
    573         registerSessionCallback(callback);
    574     }
    575 
    576     /**
    577      * Register to watch for session lifecycle events. No special permissions
    578      * are required to watch for these events.
    579      */
    580     public void registerSessionCallback(@NonNull SessionCallback callback) {
    581         registerSessionCallback(callback, new Handler());
    582     }
    583 
    584     /** {@hide} */
    585     @Deprecated
    586     public void addSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
    587         registerSessionCallback(callback, handler);
    588     }
    589 
    590     /**
    591      * Register to watch for session lifecycle events. No special permissions
    592      * are required to watch for these events.
    593      *
    594      * @param handler to dispatch callback events through, otherwise uses
    595      *            calling thread.
    596      */
    597     public void registerSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
    598         // TODO: remove this temporary guard once we have new prebuilts
    599         final ApplicationInfo info = mContext.getApplicationInfo();
    600         if ("com.google.android.googlequicksearchbox".equals(info.packageName)
    601                 && info.versionCode <= 300400110) {
    602             Log.d(TAG, "Ignoring callback request from old prebuilt");
    603             return;
    604         }
    605 
    606         synchronized (mDelegates) {
    607             final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback,
    608                     handler.getLooper());
    609             try {
    610                 mInstaller.registerCallback(delegate, mUserId);
    611             } catch (RemoteException e) {
    612                 throw e.rethrowAsRuntimeException();
    613             }
    614             mDelegates.add(delegate);
    615         }
    616     }
    617 
    618     /** {@hide} */
    619     @Deprecated
    620     public void removeSessionCallback(@NonNull SessionCallback callback) {
    621         unregisterSessionCallback(callback);
    622     }
    623 
    624     /**
    625      * Unregister a previously registered callback.
    626      */
    627     public void unregisterSessionCallback(@NonNull SessionCallback callback) {
    628         synchronized (mDelegates) {
    629             for (Iterator<SessionCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) {
    630                 final SessionCallbackDelegate delegate = i.next();
    631                 if (delegate.mCallback == callback) {
    632                     try {
    633                         mInstaller.unregisterCallback(delegate);
    634                     } catch (RemoteException e) {
    635                         throw e.rethrowAsRuntimeException();
    636                     }
    637                     i.remove();
    638                 }
    639             }
    640         }
    641     }
    642 
    643     /**
    644      * An installation that is being actively staged. For an install to succeed,
    645      * all existing and new packages must have identical package names, version
    646      * codes, and signing certificates.
    647      * <p>
    648      * A session may contain any number of split packages. If the application
    649      * does not yet exist, this session must include a base package.
    650      * <p>
    651      * If an APK included in this session is already defined by the existing
    652      * installation (for example, the same split name), the APK in this session
    653      * will replace the existing APK.
    654      */
    655     public static class Session implements Closeable {
    656         private IPackageInstallerSession mSession;
    657 
    658         /** {@hide} */
    659         public Session(IPackageInstallerSession session) {
    660             mSession = session;
    661         }
    662 
    663         /** {@hide} */
    664         @Deprecated
    665         public void setProgress(float progress) {
    666             setStagingProgress(progress);
    667         }
    668 
    669         /**
    670          * Set current progress of staging this session. Valid values are
    671          * anywhere between 0 and 1.
    672          * <p>
    673          * Note that this progress may not directly correspond to the value
    674          * reported by {@link SessionCallback#onProgressChanged(int, float)}, as
    675          * the system may carve out a portion of the overall progress to
    676          * represent its own internal installation work.
    677          */
    678         public void setStagingProgress(float progress) {
    679             try {
    680                 mSession.setClientProgress(progress);
    681             } catch (RemoteException e) {
    682                 throw e.rethrowAsRuntimeException();
    683             }
    684         }
    685 
    686         /** {@hide} */
    687         public void addProgress(float progress) {
    688             try {
    689                 mSession.addClientProgress(progress);
    690             } catch (RemoteException e) {
    691                 throw e.rethrowAsRuntimeException();
    692             }
    693         }
    694 
    695         /**
    696          * Open a stream to write an APK file into the session.
    697          * <p>
    698          * The returned stream will start writing data at the requested offset
    699          * in the underlying file, which can be used to resume a partially
    700          * written file. If a valid file length is specified, the system will
    701          * preallocate the underlying disk space to optimize placement on disk.
    702          * It's strongly recommended to provide a valid file length when known.
    703          * <p>
    704          * You can write data into the returned stream, optionally call
    705          * {@link #fsync(OutputStream)} as needed to ensure bytes have been
    706          * persisted to disk, and then close when finished. All streams must be
    707          * closed before calling {@link #commit(IntentSender)}.
    708          *
    709          * @param name arbitrary, unique name of your choosing to identify the
    710          *            APK being written. You can open a file again for
    711          *            additional writes (such as after a reboot) by using the
    712          *            same name. This name is only meaningful within the context
    713          *            of a single install session.
    714          * @param offsetBytes offset into the file to begin writing at, or 0 to
    715          *            start at the beginning of the file.
    716          * @param lengthBytes total size of the file being written, used to
    717          *            preallocate the underlying disk space, or -1 if unknown.
    718          *            The system may clear various caches as needed to allocate
    719          *            this space.
    720          * @throws IOException if trouble opening the file for writing, such as
    721          *             lack of disk space or unavailable media.
    722          * @throws SecurityException if called after the session has been
    723          *             committed or abandoned.
    724          */
    725         public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
    726                 long lengthBytes) throws IOException {
    727             try {
    728                 final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
    729                         offsetBytes, lengthBytes);
    730                 return new FileBridge.FileBridgeOutputStream(clientSocket);
    731             } catch (RuntimeException e) {
    732                 ExceptionUtils.maybeUnwrapIOException(e);
    733                 throw e;
    734             } catch (RemoteException e) {
    735                 throw e.rethrowAsRuntimeException();
    736             }
    737         }
    738 
    739         /**
    740          * Ensure that any outstanding data for given stream has been committed
    741          * to disk. This is only valid for streams returned from
    742          * {@link #openWrite(String, long, long)}.
    743          */
    744         public void fsync(@NonNull OutputStream out) throws IOException {
    745             if (out instanceof FileBridge.FileBridgeOutputStream) {
    746                 ((FileBridge.FileBridgeOutputStream) out).fsync();
    747             } else {
    748                 throw new IllegalArgumentException("Unrecognized stream");
    749             }
    750         }
    751 
    752         /**
    753          * Return all APK names contained in this session.
    754          * <p>
    755          * This returns all names which have been previously written through
    756          * {@link #openWrite(String, long, long)} as part of this session.
    757          *
    758          * @throws SecurityException if called after the session has been
    759          *             committed or abandoned.
    760          */
    761         public @NonNull String[] getNames() throws IOException {
    762             try {
    763                 return mSession.getNames();
    764             } catch (RuntimeException e) {
    765                 ExceptionUtils.maybeUnwrapIOException(e);
    766                 throw e;
    767             } catch (RemoteException e) {
    768                 throw e.rethrowAsRuntimeException();
    769             }
    770         }
    771 
    772         /**
    773          * Open a stream to read an APK file from the session.
    774          * <p>
    775          * This is only valid for names which have been previously written
    776          * through {@link #openWrite(String, long, long)} as part of this
    777          * session. For example, this stream may be used to calculate a
    778          * {@link MessageDigest} of a written APK before committing.
    779          *
    780          * @throws SecurityException if called after the session has been
    781          *             committed or abandoned.
    782          */
    783         public @NonNull InputStream openRead(@NonNull String name) throws IOException {
    784             try {
    785                 final ParcelFileDescriptor pfd = mSession.openRead(name);
    786                 return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
    787             } catch (RuntimeException e) {
    788                 ExceptionUtils.maybeUnwrapIOException(e);
    789                 throw e;
    790             } catch (RemoteException e) {
    791                 throw e.rethrowAsRuntimeException();
    792             }
    793         }
    794 
    795         /**
    796          * Attempt to commit everything staged in this session. This may require
    797          * user intervention, and so it may not happen immediately. The final
    798          * result of the commit will be reported through the given callback.
    799          * <p>
    800          * Once this method is called, no additional mutations may be performed
    801          * on the session. If the device reboots before the session has been
    802          * finalized, you may commit the session again.
    803          *
    804          * @throws SecurityException if streams opened through
    805          *             {@link #openWrite(String, long, long)} are still open.
    806          */
    807         public void commit(@NonNull IntentSender statusReceiver) {
    808             try {
    809                 mSession.commit(statusReceiver);
    810             } catch (RemoteException e) {
    811                 throw e.rethrowAsRuntimeException();
    812             }
    813         }
    814 
    815         /**
    816          * Release this session object. You can open the session again if it
    817          * hasn't been finalized.
    818          */
    819         @Override
    820         public void close() {
    821             try {
    822                 mSession.close();
    823             } catch (RemoteException e) {
    824                 throw e.rethrowAsRuntimeException();
    825             }
    826         }
    827 
    828         /**
    829          * Completely abandon this session, destroying all staged data and
    830          * rendering it invalid. Abandoned sessions will be reported to
    831          * {@link SessionCallback} listeners as failures. This is equivalent to
    832          * opening the session and calling {@link Session#abandon()}.
    833          */
    834         public void abandon() {
    835             try {
    836                 mSession.abandon();
    837             } catch (RemoteException e) {
    838                 throw e.rethrowAsRuntimeException();
    839             }
    840         }
    841     }
    842 
    843     /**
    844      * Parameters for creating a new {@link PackageInstaller.Session}.
    845      */
    846     public static class SessionParams implements Parcelable {
    847 
    848         /** {@hide} */
    849         public static final int MODE_INVALID = -1;
    850 
    851         /**
    852          * Mode for an install session whose staged APKs should fully replace any
    853          * existing APKs for the target app.
    854          */
    855         public static final int MODE_FULL_INSTALL = 1;
    856 
    857         /**
    858          * Mode for an install session that should inherit any existing APKs for the
    859          * target app, unless they have been explicitly overridden (based on split
    860          * name) by the session. For example, this can be used to add one or more
    861          * split APKs to an existing installation.
    862          * <p>
    863          * If there are no existing APKs for the target app, this behaves like
    864          * {@link #MODE_FULL_INSTALL}.
    865          */
    866         public static final int MODE_INHERIT_EXISTING = 2;
    867 
    868         /** {@hide} */
    869         public int mode = MODE_INVALID;
    870         /** {@hide} */
    871         public int installFlags;
    872         /** {@hide} */
    873         public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
    874         /** {@hide} */
    875         public long sizeBytes = -1;
    876         /** {@hide} */
    877         public String appPackageName;
    878         /** {@hide} */
    879         public Bitmap appIcon;
    880         /** {@hide} */
    881         public String appLabel;
    882         /** {@hide} */
    883         public long appIconLastModified = -1;
    884         /** {@hide} */
    885         public Uri originatingUri;
    886         /** {@hide} */
    887         public Uri referrerUri;
    888         /** {@hide} */
    889         public String abiOverride;
    890 
    891         /**
    892          * Construct parameters for a new package install session.
    893          *
    894          * @param mode one of {@link #MODE_FULL_INSTALL} or
    895          *            {@link #MODE_INHERIT_EXISTING} describing how the session
    896          *            should interact with an existing app.
    897          */
    898         public SessionParams(int mode) {
    899             this.mode = mode;
    900         }
    901 
    902         /** {@hide} */
    903         public SessionParams(Parcel source) {
    904             mode = source.readInt();
    905             installFlags = source.readInt();
    906             installLocation = source.readInt();
    907             sizeBytes = source.readLong();
    908             appPackageName = source.readString();
    909             appIcon = source.readParcelable(null);
    910             appLabel = source.readString();
    911             originatingUri = source.readParcelable(null);
    912             referrerUri = source.readParcelable(null);
    913             abiOverride = source.readString();
    914         }
    915 
    916         /**
    917          * Provide value of {@link PackageInfo#installLocation}, which may be used
    918          * to determine where the app will be staged. Defaults to
    919          * {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}.
    920          */
    921         public void setInstallLocation(int installLocation) {
    922             this.installLocation = installLocation;
    923         }
    924 
    925         /**
    926          * Optionally indicate the total size (in bytes) of all APKs that will be
    927          * delivered in this session. The system may use this to ensure enough disk
    928          * space exists before proceeding, or to estimate container size for
    929          * installations living on external storage.
    930          *
    931          * @see PackageInfo#INSTALL_LOCATION_AUTO
    932          * @see PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL
    933          */
    934         public void setSize(long sizeBytes) {
    935             this.sizeBytes = sizeBytes;
    936         }
    937 
    938         /**
    939          * Optionally set the package name of the app being installed. It's strongly
    940          * recommended that you provide this value when known, so that observers can
    941          * communicate installing apps to users.
    942          * <p>
    943          * If the APKs staged in the session aren't consistent with this package
    944          * name, the install will fail. Regardless of this value, all APKs in the
    945          * app must have the same package name.
    946          */
    947         public void setAppPackageName(@Nullable String appPackageName) {
    948             this.appPackageName = appPackageName;
    949         }
    950 
    951         /**
    952          * Optionally set an icon representing the app being installed. This should
    953          * be roughly {@link ActivityManager#getLauncherLargeIconSize()} in both
    954          * dimensions.
    955          */
    956         public void setAppIcon(@Nullable Bitmap appIcon) {
    957             this.appIcon = appIcon;
    958         }
    959 
    960         /**
    961          * Optionally set a label representing the app being installed.
    962          */
    963         public void setAppLabel(@Nullable CharSequence appLabel) {
    964             this.appLabel = (appLabel != null) ? appLabel.toString() : null;
    965         }
    966 
    967         /**
    968          * Optionally set the URI where this package was downloaded from. Used for
    969          * verification purposes.
    970          *
    971          * @see Intent#EXTRA_ORIGINATING_URI
    972          */
    973         public void setOriginatingUri(@Nullable Uri originatingUri) {
    974             this.originatingUri = originatingUri;
    975         }
    976 
    977         /**
    978          * Optionally set the URI that referred you to install this package. Used
    979          * for verification purposes.
    980          *
    981          * @see Intent#EXTRA_REFERRER
    982          */
    983         public void setReferrerUri(@Nullable Uri referrerUri) {
    984             this.referrerUri = referrerUri;
    985         }
    986 
    987         /** {@hide} */
    988         public void setInstallFlagsInternal() {
    989             installFlags |= PackageManager.INSTALL_INTERNAL;
    990             installFlags &= ~PackageManager.INSTALL_EXTERNAL;
    991         }
    992 
    993         /** {@hide} */
    994         public void setInstallFlagsExternal() {
    995             installFlags |= PackageManager.INSTALL_EXTERNAL;
    996             installFlags &= ~PackageManager.INSTALL_INTERNAL;
    997         }
    998 
    999         /** {@hide} */
   1000         public void dump(IndentingPrintWriter pw) {
   1001             pw.printPair("mode", mode);
   1002             pw.printHexPair("installFlags", installFlags);
   1003             pw.printPair("installLocation", installLocation);
   1004             pw.printPair("sizeBytes", sizeBytes);
   1005             pw.printPair("appPackageName", appPackageName);
   1006             pw.printPair("appIcon", (appIcon != null));
   1007             pw.printPair("appLabel", appLabel);
   1008             pw.printPair("originatingUri", originatingUri);
   1009             pw.printPair("referrerUri", referrerUri);
   1010             pw.printPair("abiOverride", abiOverride);
   1011             pw.println();
   1012         }
   1013 
   1014         @Override
   1015         public int describeContents() {
   1016             return 0;
   1017         }
   1018 
   1019         @Override
   1020         public void writeToParcel(Parcel dest, int flags) {
   1021             dest.writeInt(mode);
   1022             dest.writeInt(installFlags);
   1023             dest.writeInt(installLocation);
   1024             dest.writeLong(sizeBytes);
   1025             dest.writeString(appPackageName);
   1026             dest.writeParcelable(appIcon, flags);
   1027             dest.writeString(appLabel);
   1028             dest.writeParcelable(originatingUri, flags);
   1029             dest.writeParcelable(referrerUri, flags);
   1030             dest.writeString(abiOverride);
   1031         }
   1032 
   1033         public static final Parcelable.Creator<SessionParams>
   1034                 CREATOR = new Parcelable.Creator<SessionParams>() {
   1035                     @Override
   1036                     public SessionParams createFromParcel(Parcel p) {
   1037                         return new SessionParams(p);
   1038                     }
   1039 
   1040                     @Override
   1041                     public SessionParams[] newArray(int size) {
   1042                         return new SessionParams[size];
   1043                     }
   1044                 };
   1045     }
   1046 
   1047     /**
   1048      * Details for an active install session.
   1049      */
   1050     public static class SessionInfo implements Parcelable {
   1051 
   1052         /** {@hide} */
   1053         public int sessionId;
   1054         /** {@hide} */
   1055         public String installerPackageName;
   1056         /** {@hide} */
   1057         public String resolvedBaseCodePath;
   1058         /** {@hide} */
   1059         public float progress;
   1060         /** {@hide} */
   1061         public boolean sealed;
   1062         /** {@hide} */
   1063         public boolean active;
   1064 
   1065         /** {@hide} */
   1066         public int mode;
   1067         /** {@hide} */
   1068         public long sizeBytes;
   1069         /** {@hide} */
   1070         public String appPackageName;
   1071         /** {@hide} */
   1072         public Bitmap appIcon;
   1073         /** {@hide} */
   1074         public CharSequence appLabel;
   1075 
   1076         /** {@hide} */
   1077         public SessionInfo() {
   1078         }
   1079 
   1080         /** {@hide} */
   1081         public SessionInfo(Parcel source) {
   1082             sessionId = source.readInt();
   1083             installerPackageName = source.readString();
   1084             resolvedBaseCodePath = source.readString();
   1085             progress = source.readFloat();
   1086             sealed = source.readInt() != 0;
   1087             active = source.readInt() != 0;
   1088 
   1089             mode = source.readInt();
   1090             sizeBytes = source.readLong();
   1091             appPackageName = source.readString();
   1092             appIcon = source.readParcelable(null);
   1093             appLabel = source.readString();
   1094         }
   1095 
   1096         /**
   1097          * Return the ID for this session.
   1098          */
   1099         public int getSessionId() {
   1100             return sessionId;
   1101         }
   1102 
   1103         /**
   1104          * Return the package name of the app that owns this session.
   1105          */
   1106         public @Nullable String getInstallerPackageName() {
   1107             return installerPackageName;
   1108         }
   1109 
   1110         /**
   1111          * Return current overall progress of this session, between 0 and 1.
   1112          * <p>
   1113          * Note that this progress may not directly correspond to the value
   1114          * reported by
   1115          * {@link PackageInstaller.Session#setStagingProgress(float)}, as the
   1116          * system may carve out a portion of the overall progress to represent
   1117          * its own internal installation work.
   1118          */
   1119         public float getProgress() {
   1120             return progress;
   1121         }
   1122 
   1123         /**
   1124          * Return if this session is currently active.
   1125          * <p>
   1126          * A session is considered active whenever there is ongoing forward
   1127          * progress being made, such as the installer holding an open
   1128          * {@link Session} instance while streaming data into place, or the
   1129          * system optimizing code as the result of
   1130          * {@link Session#commit(IntentSender)}.
   1131          * <p>
   1132          * If the installer closes the {@link Session} without committing, the
   1133          * session is considered inactive until the installer opens the session
   1134          * again.
   1135          */
   1136         public boolean isActive() {
   1137             return active;
   1138         }
   1139 
   1140         /** {@hide} */
   1141         @Deprecated
   1142         public boolean isOpen() {
   1143             return isActive();
   1144         }
   1145 
   1146         /**
   1147          * Return the package name this session is working with. May be {@code null}
   1148          * if unknown.
   1149          */
   1150         public @Nullable String getAppPackageName() {
   1151             return appPackageName;
   1152         }
   1153 
   1154         /**
   1155          * Return an icon representing the app being installed. May be {@code null}
   1156          * if unavailable.
   1157          */
   1158         public @Nullable Bitmap getAppIcon() {
   1159             return appIcon;
   1160         }
   1161 
   1162         /**
   1163          * Return a label representing the app being installed. May be {@code null}
   1164          * if unavailable.
   1165          */
   1166         public @Nullable CharSequence getAppLabel() {
   1167             return appLabel;
   1168         }
   1169 
   1170         /**
   1171          * Return an Intent that can be started to view details about this install
   1172          * session. This may surface actions such as pause, resume, or cancel.
   1173          * <p>
   1174          * In some cases, a matching Activity may not exist, so ensure you safeguard
   1175          * against this.
   1176          *
   1177          * @see PackageInstaller#ACTION_SESSION_DETAILS
   1178          */
   1179         public @Nullable Intent createDetailsIntent() {
   1180             final Intent intent = new Intent(PackageInstaller.ACTION_SESSION_DETAILS);
   1181             intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
   1182             intent.setPackage(installerPackageName);
   1183             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1184             return intent;
   1185         }
   1186 
   1187         /** {@hide} */
   1188         @Deprecated
   1189         public @Nullable Intent getDetailsIntent() {
   1190             return createDetailsIntent();
   1191         }
   1192 
   1193         @Override
   1194         public int describeContents() {
   1195             return 0;
   1196         }
   1197 
   1198         @Override
   1199         public void writeToParcel(Parcel dest, int flags) {
   1200             dest.writeInt(sessionId);
   1201             dest.writeString(installerPackageName);
   1202             dest.writeString(resolvedBaseCodePath);
   1203             dest.writeFloat(progress);
   1204             dest.writeInt(sealed ? 1 : 0);
   1205             dest.writeInt(active ? 1 : 0);
   1206 
   1207             dest.writeInt(mode);
   1208             dest.writeLong(sizeBytes);
   1209             dest.writeString(appPackageName);
   1210             dest.writeParcelable(appIcon, flags);
   1211             dest.writeString(appLabel != null ? appLabel.toString() : null);
   1212         }
   1213 
   1214         public static final Parcelable.Creator<SessionInfo>
   1215                 CREATOR = new Parcelable.Creator<SessionInfo>() {
   1216                     @Override
   1217                     public SessionInfo createFromParcel(Parcel p) {
   1218                         return new SessionInfo(p);
   1219                     }
   1220 
   1221                     @Override
   1222                     public SessionInfo[] newArray(int size) {
   1223                         return new SessionInfo[size];
   1224                     }
   1225                 };
   1226     }
   1227 }
   1228