blob: 32830053fae2d469579b115a8ea61a1f2fd0faa0 [file] [log] [blame]
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.content.pm;
18
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -070019import android.annotation.NonNull;
20import android.annotation.Nullable;
Svet Ganov7121e182015-07-13 22:38:12 -070021import android.annotation.RequiresPermission;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070022import android.annotation.SdkConstant;
23import android.annotation.SdkConstant.SdkConstantType;
Svet Ganov7121e182015-07-13 22:38:12 -070024import android.annotation.SystemApi;
Jeff Sharkeya0907432014-08-15 10:23:11 -070025import android.app.ActivityManager;
26import android.content.Context;
Jeff Sharkeyfbd0e9f2014-08-06 16:34:34 -070027import android.content.Intent;
Jeff Sharkeya0907432014-08-15 10:23:11 -070028import android.content.IntentSender;
29import android.graphics.Bitmap;
30import android.net.Uri;
Jeff Sharkey78cc3402014-05-22 10:52:49 -070031import android.os.FileBridge;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -070032import android.os.Handler;
33import android.os.Looper;
34import android.os.Message;
Jeff Sharkeya0907432014-08-15 10:23:11 -070035import android.os.Parcel;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070036import android.os.ParcelFileDescriptor;
Jeff Sharkeya0907432014-08-15 10:23:11 -070037import android.os.Parcelable;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070038import android.os.RemoteException;
Jeff Sharkeya1031142014-07-12 18:09:46 -070039import android.util.ExceptionUtils;
Jeff Sharkeya0907432014-08-15 10:23:11 -070040import android.util.Log;
41
42import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070043
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070044import java.io.Closeable;
Jeff Sharkeya1031142014-07-12 18:09:46 -070045import java.io.IOException;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070046import java.io.InputStream;
Jeff Sharkey78cc3402014-05-22 10:52:49 -070047import java.io.OutputStream;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070048import java.security.MessageDigest;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -070049import java.util.ArrayList;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -070050import java.util.Collections;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -070051import java.util.Iterator;
Jeff Sharkeybb580672014-07-10 12:10:25 -070052import java.util.List;
Jeff Sharkey78cc3402014-05-22 10:52:49 -070053
Jeff Sharkey6c833e02014-07-14 22:44:30 -070054/**
55 * Offers the ability to install, upgrade, and remove applications on the
56 * device. This includes support for apps packaged either as a single
57 * "monolithic" APK, or apps packaged as multiple "split" APKs.
58 * <p>
59 * An app is delivered for installation through a
60 * {@link PackageInstaller.Session}, which any app can create. Once the session
61 * is created, the installer can stream one or more APKs into place until it
62 * decides to either commit or destroy the session. Committing may require user
63 * intervention to complete the installation.
64 * <p>
65 * Sessions can install brand new apps, upgrade existing apps, or add new splits
Jeff Sharkeyda96e132014-07-15 14:54:09 -070066 * into an existing app.
Jeff Sharkey6c833e02014-07-14 22:44:30 -070067 * <p>
Jeff Sharkeyda96e132014-07-15 14:54:09 -070068 * Apps packaged as multiple split APKs always consist of a single "base" APK
Jeff Sharkey6c833e02014-07-14 22:44:30 -070069 * (with a {@code null} split name) and zero or more "split" APKs (with unique
70 * split names). Any subset of these APKs can be installed together, as long as
71 * the following constraints are met:
72 * <ul>
73 * <li>All APKs must have the exact same package name, version code, and signing
74 * certificates.
Jeff Sharkey6c833e02014-07-14 22:44:30 -070075 * <li>All APKs must have unique split names.
Jeff Sharkeyda96e132014-07-15 14:54:09 -070076 * <li>All installations must contain a single base APK.
Jeff Sharkey6c833e02014-07-14 22:44:30 -070077 * </ul>
78 */
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070079public class PackageInstaller {
Jeff Sharkeya0907432014-08-15 10:23:11 -070080 private static final String TAG = "PackageInstaller";
81
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070082 /**
83 * Activity Action: Show details about a particular install session. This
84 * may surface actions such as pause, resume, or cancel.
85 * <p>
86 * This should always be scoped to the installer package that owns the
Jeff Sharkeyde742312014-09-15 14:04:56 -070087 * session. Clients should use {@link SessionInfo#createDetailsIntent()} to
Jeff Sharkeya0907432014-08-15 10:23:11 -070088 * build this intent correctly.
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070089 * <p>
90 * In some cases, a matching Activity may not exist, so ensure you safeguard
91 * against this.
Jeff Sharkey941a8ba2014-08-20 16:26:32 -070092 * <p>
93 * The session to show details for is defined in {@link #EXTRA_SESSION_ID}.
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070094 */
95 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
96 public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
97
Jeff Sharkey7328a1b2014-08-07 14:01:43 -070098 /** {@hide} */
99 public static final String
100 ACTION_CONFIRM_PERMISSIONS = "android.content.pm.action.CONFIRM_PERMISSIONS";
101
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700102 /**
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700103 * An integer session ID that an operation is working with.
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700104 *
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700105 * @see Intent#getIntExtra(String, int)
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700106 */
107 public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
108
Jeff Sharkeya0907432014-08-15 10:23:11 -0700109 /**
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700110 * Package name that an operation is working with.
Jeff Sharkeya0907432014-08-15 10:23:11 -0700111 *
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700112 * @see Intent#getStringExtra(String)
Jeff Sharkeya0907432014-08-15 10:23:11 -0700113 */
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700114 public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
115
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700116 /**
117 * Current status of an operation. Will be one of
118 * {@link #STATUS_PENDING_USER_ACTION}, {@link #STATUS_SUCCESS},
119 * {@link #STATUS_FAILURE}, {@link #STATUS_FAILURE_ABORTED},
120 * {@link #STATUS_FAILURE_BLOCKED}, {@link #STATUS_FAILURE_CONFLICT},
121 * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, or
122 * {@link #STATUS_FAILURE_STORAGE}.
123 * <p>
124 * More information about a status may be available through additional
125 * extras; see the individual status documentation for details.
126 *
127 * @see Intent#getIntExtra(String, int)
128 */
129 public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
130
131 /**
132 * Detailed string representation of the status, including raw details that
133 * are useful for debugging.
134 *
135 * @see Intent#getStringExtra(String)
136 */
137 public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
138
139 /**
140 * Another package name relevant to a status. This is typically the package
141 * responsible for causing an operation failure.
142 *
143 * @see Intent#getStringExtra(String)
144 */
145 public static final String
146 EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
147
148 /**
149 * Storage path relevant to a status.
150 *
151 * @see Intent#getStringExtra(String)
152 */
153 public static final String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
154
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700155 /** {@hide} */
156 @Deprecated
Jeff Sharkeya0907432014-08-15 10:23:11 -0700157 public static final String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES";
158
159 /** {@hide} */
160 public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
161 /** {@hide} */
162 public static final String EXTRA_LEGACY_BUNDLE = "android.content.pm.extra.LEGACY_BUNDLE";
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700163 /** {@hide} */
164 public static final String EXTRA_CALLBACK = "android.content.pm.extra.CALLBACK";
165
Jeff Sharkeya0907432014-08-15 10:23:11 -0700166 /**
167 * User action is currently required to proceed. You can launch the intent
168 * activity described by {@link Intent#EXTRA_INTENT} to involve the user and
169 * continue.
170 * <p>
171 * You may choose to immediately launch the intent if the user is actively
172 * using your app. Otherwise, you should use a notification to guide the
173 * user back into your app before launching.
174 *
175 * @see Intent#getParcelableExtra(String)
176 */
Jeff Sharkey742e7902014-08-16 19:09:13 -0700177 public static final int STATUS_PENDING_USER_ACTION = -1;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700178
179 /**
180 * The operation succeeded.
181 */
182 public static final int STATUS_SUCCESS = 0;
183
184 /**
185 * The operation failed in a generic way. The system will always try to
186 * provide a more specific failure reason, but in some rare cases this may
187 * be delivered.
188 *
189 * @see #EXTRA_STATUS_MESSAGE
190 */
191 public static final int STATUS_FAILURE = 1;
192
193 /**
194 * The operation failed because it was blocked. For example, a device policy
195 * may be blocking the operation, a package verifier may have blocked the
196 * operation, or the app may be required for core system operation.
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700197 * <p>
198 * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
199 * specific package blocking the install.
Jeff Sharkeya0907432014-08-15 10:23:11 -0700200 *
201 * @see #EXTRA_STATUS_MESSAGE
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700202 * @see #EXTRA_OTHER_PACKAGE_NAME
Jeff Sharkeya0907432014-08-15 10:23:11 -0700203 */
Jeff Sharkey742e7902014-08-16 19:09:13 -0700204 public static final int STATUS_FAILURE_BLOCKED = 2;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700205
206 /**
207 * The operation failed because it was actively aborted. For example, the
208 * user actively declined requested permissions, or the session was
209 * abandoned.
210 *
211 * @see #EXTRA_STATUS_MESSAGE
212 */
Jeff Sharkey742e7902014-08-16 19:09:13 -0700213 public static final int STATUS_FAILURE_ABORTED = 3;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700214
215 /**
216 * The operation failed because one or more of the APKs was invalid. For
217 * example, they might be malformed, corrupt, incorrectly signed,
218 * mismatched, etc.
219 *
220 * @see #EXTRA_STATUS_MESSAGE
221 */
Jeff Sharkey742e7902014-08-16 19:09:13 -0700222 public static final int STATUS_FAILURE_INVALID = 4;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700223
224 /**
225 * The operation failed because it conflicts (or is inconsistent with) with
226 * another package already installed on the device. For example, an existing
227 * permission, incompatible certificates, etc. The user may be able to
228 * uninstall another app to fix the issue.
229 * <p>
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700230 * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700231 * specific package identified as the cause of the conflict.
Jeff Sharkeya0907432014-08-15 10:23:11 -0700232 *
233 * @see #EXTRA_STATUS_MESSAGE
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700234 * @see #EXTRA_OTHER_PACKAGE_NAME
Jeff Sharkeya0907432014-08-15 10:23:11 -0700235 */
Jeff Sharkey742e7902014-08-16 19:09:13 -0700236 public static final int STATUS_FAILURE_CONFLICT = 5;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700237
238 /**
239 * The operation failed because of storage issues. For example, the device
240 * may be running low on space, or external media may be unavailable. The
241 * user may be able to help free space or insert different external media.
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700242 * <p>
243 * The result may also contain {@link #EXTRA_STORAGE_PATH} with the path to
244 * the storage device that caused the failure.
Jeff Sharkeya0907432014-08-15 10:23:11 -0700245 *
246 * @see #EXTRA_STATUS_MESSAGE
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700247 * @see #EXTRA_STORAGE_PATH
Jeff Sharkeya0907432014-08-15 10:23:11 -0700248 */
Jeff Sharkey742e7902014-08-16 19:09:13 -0700249 public static final int STATUS_FAILURE_STORAGE = 6;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700250
251 /**
252 * The operation failed because it is fundamentally incompatible with this
253 * device. For example, the app may require a hardware feature that doesn't
254 * exist, it may be missing native code for the ABIs supported by the
255 * device, or it requires a newer SDK version, etc.
256 *
257 * @see #EXTRA_STATUS_MESSAGE
258 */
Jeff Sharkey742e7902014-08-16 19:09:13 -0700259 public static final int STATUS_FAILURE_INCOMPATIBLE = 7;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700260
261 private final Context mContext;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700262 private final PackageManager mPm;
263 private final IPackageInstaller mInstaller;
264 private final int mUserId;
265 private final String mInstallerPackageName;
266
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700267 private final ArrayList<SessionCallbackDelegate> mDelegates = new ArrayList<>();
268
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700269 /** {@hide} */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700270 public PackageInstaller(Context context, PackageManager pm, IPackageInstaller installer,
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700271 String installerPackageName, int userId) {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700272 mContext = context;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700273 mPm = pm;
274 mInstaller = installer;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700275 mInstallerPackageName = installerPackageName;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700276 mUserId = userId;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700277 }
278
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700279 /**
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700280 * Create a new session using the given parameters, returning a unique ID
281 * that represents the session. Once created, the session can be opened
282 * multiple times across multiple device boots.
283 * <p>
284 * The system may automatically destroy sessions that have not been
285 * finalized (either committed or abandoned) within a reasonable period of
286 * time, typically on the order of a day.
287 *
288 * @throws IOException if parameters were unsatisfiable, such as lack of
289 * disk space or unavailable media.
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700290 * @throws SecurityException when installation services are unavailable,
291 * such as when called from a restricted user.
292 * @throws IllegalArgumentException when {@link SessionParams} is invalid.
Jeff Sharkeyf174c6e2014-08-05 10:42:27 -0700293 * @return positive, non-zero unique ID that represents the created session.
294 * This ID remains consistent across device reboots until the
295 * session is finalized. IDs are not reused during a given boot.
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700296 */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700297 public int createSession(@NonNull SessionParams params) throws IOException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700298 try {
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700299 return mInstaller.createSession(params, mInstallerPackageName, mUserId);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700300 } catch (RuntimeException e) {
301 ExceptionUtils.maybeUnwrapIOException(e);
302 throw e;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700303 } catch (RemoteException e) {
304 throw e.rethrowAsRuntimeException();
305 }
306 }
307
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700308 /**
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700309 * Open an existing session to actively perform work. To succeed, the caller
310 * must be the owner of the install session.
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700311 *
312 * @throws IOException if parameters were unsatisfiable, such as lack of
313 * disk space or unavailable media.
314 * @throws SecurityException when the caller does not own the session, or
315 * the session is invalid.
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700316 */
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700317 public @NonNull Session openSession(int sessionId) throws IOException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700318 try {
319 return new Session(mInstaller.openSession(sessionId));
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700320 } catch (RuntimeException e) {
321 ExceptionUtils.maybeUnwrapIOException(e);
322 throw e;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700323 } catch (RemoteException e) {
324 throw e.rethrowAsRuntimeException();
325 }
326 }
327
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700328 /**
329 * Update the icon representing the app being installed in a specific
330 * session. This should be roughly
331 * {@link ActivityManager#getLauncherLargeIconSize()} in both dimensions.
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700332 *
333 * @throws SecurityException when the caller does not own the session, or
334 * the session is invalid.
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700335 */
336 public void updateSessionAppIcon(int sessionId, @Nullable Bitmap appIcon) {
337 try {
338 mInstaller.updateSessionAppIcon(sessionId, appIcon);
339 } catch (RemoteException e) {
340 throw e.rethrowAsRuntimeException();
341 }
342 }
343
344 /**
345 * Update the label representing the app being installed in a specific
346 * session.
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700347 *
348 * @throws SecurityException when the caller does not own the session, or
349 * the session is invalid.
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700350 */
351 public void updateSessionAppLabel(int sessionId, @Nullable CharSequence appLabel) {
352 try {
353 final String val = (appLabel != null) ? appLabel.toString() : null;
354 mInstaller.updateSessionAppLabel(sessionId, val);
355 } catch (RemoteException e) {
356 throw e.rethrowAsRuntimeException();
357 }
358 }
359
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700360 /**
361 * Completely abandon the given session, destroying all staged data and
362 * rendering it invalid. Abandoned sessions will be reported to
363 * {@link SessionCallback} listeners as failures. This is equivalent to
364 * opening the session and calling {@link Session#abandon()}.
365 *
366 * @throws SecurityException when the caller does not own the session, or
367 * the session is invalid.
368 */
Jeff Sharkey381d94b2014-08-24 14:45:56 -0700369 public void abandonSession(int sessionId) {
370 try {
371 mInstaller.abandonSession(sessionId);
372 } catch (RemoteException e) {
373 throw e.rethrowAsRuntimeException();
374 }
375 }
376
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700377 /**
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700378 * Return details for a specific session. No special permissions are
379 * required to retrieve these details.
380 *
381 * @return details for the requested session, or {@code null} if the session
382 * does not exist.
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700383 */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700384 public @Nullable SessionInfo getSessionInfo(int sessionId) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700385 try {
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700386 return mInstaller.getSessionInfo(sessionId);
387 } catch (RemoteException e) {
388 throw e.rethrowAsRuntimeException();
389 }
390 }
391
392 /**
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700393 * Return list of all known install sessions, regardless of the installer.
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700394 */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700395 public @NonNull List<SessionInfo> getAllSessions() {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700396 final ApplicationInfo info = mContext.getApplicationInfo();
397 if ("com.google.android.googlequicksearchbox".equals(info.packageName)
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700398 && info.versionCode <= 300400110) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700399 Log.d(TAG, "Ignoring callback request from old prebuilt");
400 return Collections.EMPTY_LIST;
401 }
402
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700403 try {
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700404 return mInstaller.getAllSessions(mUserId).getList();
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700405 } catch (RemoteException e) {
406 throw e.rethrowAsRuntimeException();
407 }
408 }
409
410 /**
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700411 * Return list of all known install sessions owned by the calling app.
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700412 */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700413 public @NonNull List<SessionInfo> getMySessions() {
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700414 try {
Jeff Sharkey97d47ed2014-10-15 09:19:47 -0700415 return mInstaller.getMySessions(mInstallerPackageName, mUserId).getList();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700416 } catch (RemoteException e) {
417 throw e.rethrowAsRuntimeException();
418 }
419 }
420
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700421 /**
422 * Uninstall the given package, removing it completely from the device. This
423 * method is only available to the current "installer of record" for the
424 * package.
425 */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700426 public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700427 try {
Benjamin Franz39fb7fd2015-02-18 16:11:18 +0000428 mInstaller.uninstall(packageName, mInstallerPackageName, 0, statusReceiver, mUserId);
Jeff Sharkeybb580672014-07-10 12:10:25 -0700429 } catch (RemoteException e) {
430 throw e.rethrowAsRuntimeException();
431 }
432 }
433
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700434 /** {@hide} */
435 public void setPermissionsResult(int sessionId, boolean accepted) {
436 try {
437 mInstaller.setPermissionsResult(sessionId, accepted);
438 } catch (RemoteException e) {
439 throw e.rethrowAsRuntimeException();
440 }
441 }
442
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700443 /**
444 * Events for observing session lifecycle.
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700445 * <p>
446 * A typical session lifecycle looks like this:
447 * <ul>
448 * <li>An installer creates a session to indicate pending app delivery. All
449 * install details are available at this point.
450 * <li>The installer opens the session to deliver APK data. Note that a
451 * session may be opened and closed multiple times as network connectivity
452 * changes. The installer may deliver periodic progress updates.
453 * <li>The installer commits or abandons the session, resulting in the
454 * session being finished.
455 * </ul>
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700456 */
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700457 public static abstract class SessionCallback {
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700458 /**
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700459 * New session has been created. Details about the session can be
460 * obtained from {@link PackageInstaller#getSessionInfo(int)}.
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700461 */
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700462 public abstract void onCreated(int sessionId);
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700463
464 /**
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700465 * Badging details for an existing session has changed. For example, the
466 * app icon or label has been updated.
467 */
468 public abstract void onBadgingChanged(int sessionId);
469
470 /**
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700471 * Active state for session has been changed.
472 * <p>
473 * A session is considered active whenever there is ongoing forward
474 * progress being made, such as the installer holding an open
475 * {@link Session} instance while streaming data into place, or the
476 * system optimizing code as the result of
477 * {@link Session#commit(IntentSender)}.
478 * <p>
479 * If the installer closes the {@link Session} without committing, the
480 * session is considered inactive until the installer opens the session
481 * again.
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700482 */
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700483 public abstract void onActiveChanged(int sessionId, boolean active);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700484
485 /**
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700486 * Progress for given session has been updated.
487 * <p>
488 * Note that this progress may not directly correspond to the value
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700489 * reported by
490 * {@link PackageInstaller.Session#setStagingProgress(float)}, as the
491 * system may carve out a portion of the overall progress to represent
492 * its own internal installation work.
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700493 */
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700494 public abstract void onProgressChanged(int sessionId, float progress);
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700495
496 /**
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700497 * Session has completely finished, either with success or failure.
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700498 */
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700499 public abstract void onFinished(int sessionId, boolean success);
Jeff Sharkeybb580672014-07-10 12:10:25 -0700500 }
501
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700502 /** {@hide} */
503 private static class SessionCallbackDelegate extends IPackageInstallerCallback.Stub implements
504 Handler.Callback {
505 private static final int MSG_SESSION_CREATED = 1;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700506 private static final int MSG_SESSION_BADGING_CHANGED = 2;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700507 private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700508 private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700509 private static final int MSG_SESSION_FINISHED = 5;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700510
511 final SessionCallback mCallback;
512 final Handler mHandler;
513
514 public SessionCallbackDelegate(SessionCallback callback, Looper looper) {
515 mCallback = callback;
516 mHandler = new Handler(looper, this);
517 }
518
519 @Override
520 public boolean handleMessage(Message msg) {
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700521 final int sessionId = msg.arg1;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700522 switch (msg.what) {
523 case MSG_SESSION_CREATED:
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700524 mCallback.onCreated(sessionId);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700525 return true;
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700526 case MSG_SESSION_BADGING_CHANGED:
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700527 mCallback.onBadgingChanged(sessionId);
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700528 return true;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700529 case MSG_SESSION_ACTIVE_CHANGED:
530 final boolean active = msg.arg2 != 0;
531 mCallback.onActiveChanged(sessionId, active);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700532 return true;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700533 case MSG_SESSION_PROGRESS_CHANGED:
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700534 mCallback.onProgressChanged(sessionId, (float) msg.obj);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700535 return true;
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700536 case MSG_SESSION_FINISHED:
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700537 mCallback.onFinished(sessionId, msg.arg2 != 0);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700538 return true;
539 }
540 return false;
541 }
542
543 @Override
544 public void onSessionCreated(int sessionId) {
545 mHandler.obtainMessage(MSG_SESSION_CREATED, sessionId, 0).sendToTarget();
546 }
547
548 @Override
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700549 public void onSessionBadgingChanged(int sessionId) {
550 mHandler.obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, 0).sendToTarget();
551 }
552
553 @Override
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700554 public void onSessionActiveChanged(int sessionId, boolean active) {
555 mHandler.obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, active ? 1 : 0)
556 .sendToTarget();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700557 }
558
559 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700560 public void onSessionProgressChanged(int sessionId, float progress) {
561 mHandler.obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, 0, progress)
562 .sendToTarget();
563 }
564
565 @Override
566 public void onSessionFinished(int sessionId, boolean success) {
567 mHandler.obtainMessage(MSG_SESSION_FINISHED, sessionId, success ? 1 : 0)
568 .sendToTarget();
Jeff Sharkeybb580672014-07-10 12:10:25 -0700569 }
570 }
571
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700572 /** {@hide} */
573 @Deprecated
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700574 public void addSessionCallback(@NonNull SessionCallback callback) {
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700575 registerSessionCallback(callback);
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700576 }
577
578 /**
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700579 * Register to watch for session lifecycle events. No special permissions
580 * are required to watch for these events.
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700581 */
582 public void registerSessionCallback(@NonNull SessionCallback callback) {
583 registerSessionCallback(callback, new Handler());
584 }
585
586 /** {@hide} */
587 @Deprecated
588 public void addSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
589 registerSessionCallback(callback, handler);
590 }
591
592 /**
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700593 * Register to watch for session lifecycle events. No special permissions
594 * are required to watch for these events.
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700595 *
596 * @param handler to dispatch callback events through, otherwise uses
597 * calling thread.
598 */
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700599 public void registerSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700600 // TODO: remove this temporary guard once we have new prebuilts
601 final ApplicationInfo info = mContext.getApplicationInfo();
602 if ("com.google.android.googlequicksearchbox".equals(info.packageName)
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700603 && info.versionCode <= 300400110) {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700604 Log.d(TAG, "Ignoring callback request from old prebuilt");
605 return;
606 }
607
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700608 synchronized (mDelegates) {
609 final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback,
610 handler.getLooper());
611 try {
612 mInstaller.registerCallback(delegate, mUserId);
613 } catch (RemoteException e) {
614 throw e.rethrowAsRuntimeException();
615 }
616 mDelegates.add(delegate);
617 }
618 }
619
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700620 /** {@hide} */
621 @Deprecated
622 public void removeSessionCallback(@NonNull SessionCallback callback) {
623 unregisterSessionCallback(callback);
624 }
625
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700626 /**
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700627 * Unregister a previously registered callback.
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700628 */
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700629 public void unregisterSessionCallback(@NonNull SessionCallback callback) {
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700630 synchronized (mDelegates) {
631 for (Iterator<SessionCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) {
632 final SessionCallbackDelegate delegate = i.next();
633 if (delegate.mCallback == callback) {
634 try {
635 mInstaller.unregisterCallback(delegate);
636 } catch (RemoteException e) {
637 throw e.rethrowAsRuntimeException();
638 }
639 i.remove();
640 }
641 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700642 }
643 }
644
645 /**
646 * An installation that is being actively staged. For an install to succeed,
647 * all existing and new packages must have identical package names, version
648 * codes, and signing certificates.
649 * <p>
650 * A session may contain any number of split packages. If the application
651 * does not yet exist, this session must include a base package.
652 * <p>
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700653 * If an APK included in this session is already defined by the existing
654 * installation (for example, the same split name), the APK in this session
655 * will replace the existing APK.
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700656 */
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700657 public static class Session implements Closeable {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700658 private IPackageInstallerSession mSession;
659
660 /** {@hide} */
661 public Session(IPackageInstallerSession session) {
662 mSession = session;
663 }
664
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700665 /** {@hide} */
666 @Deprecated
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700667 public void setProgress(float progress) {
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700668 setStagingProgress(progress);
669 }
670
671 /**
672 * Set current progress of staging this session. Valid values are
673 * anywhere between 0 and 1.
674 * <p>
675 * Note that this progress may not directly correspond to the value
676 * reported by {@link SessionCallback#onProgressChanged(int, float)}, as
677 * the system may carve out a portion of the overall progress to
678 * represent its own internal installation work.
679 */
680 public void setStagingProgress(float progress) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700681 try {
Jeff Sharkeya1031142014-07-12 18:09:46 -0700682 mSession.setClientProgress(progress);
683 } catch (RemoteException e) {
684 throw e.rethrowAsRuntimeException();
685 }
686 }
687
688 /** {@hide} */
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700689 public void addProgress(float progress) {
Jeff Sharkeya1031142014-07-12 18:09:46 -0700690 try {
691 mSession.addClientProgress(progress);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700692 } catch (RemoteException e) {
693 throw e.rethrowAsRuntimeException();
694 }
695 }
696
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700697 /**
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700698 * Open a stream to write an APK file into the session.
699 * <p>
700 * The returned stream will start writing data at the requested offset
701 * in the underlying file, which can be used to resume a partially
702 * written file. If a valid file length is specified, the system will
703 * preallocate the underlying disk space to optimize placement on disk.
704 * It's strongly recommended to provide a valid file length when known.
705 * <p>
706 * You can write data into the returned stream, optionally call
707 * {@link #fsync(OutputStream)} as needed to ensure bytes have been
708 * persisted to disk, and then close when finished. All streams must be
Jeff Sharkeya0907432014-08-15 10:23:11 -0700709 * closed before calling {@link #commit(IntentSender)}.
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700710 *
711 * @param name arbitrary, unique name of your choosing to identify the
712 * APK being written. You can open a file again for
713 * additional writes (such as after a reboot) by using the
714 * same name. This name is only meaningful within the context
715 * of a single install session.
716 * @param offsetBytes offset into the file to begin writing at, or 0 to
717 * start at the beginning of the file.
718 * @param lengthBytes total size of the file being written, used to
719 * preallocate the underlying disk space, or -1 if unknown.
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700720 * The system may clear various caches as needed to allocate
721 * this space.
722 * @throws IOException if trouble opening the file for writing, such as
723 * lack of disk space or unavailable media.
724 * @throws SecurityException if called after the session has been
725 * committed or abandoned.
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700726 */
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700727 public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
728 long lengthBytes) throws IOException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700729 try {
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700730 final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700731 offsetBytes, lengthBytes);
Jeff Sharkey9a1507a2014-08-28 20:38:10 -0700732 return new FileBridge.FileBridgeOutputStream(clientSocket);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700733 } catch (RuntimeException e) {
734 ExceptionUtils.maybeUnwrapIOException(e);
735 throw e;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700736 } catch (RemoteException e) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700737 throw e.rethrowAsRuntimeException();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700738 }
739 }
740
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700741 /**
742 * Ensure that any outstanding data for given stream has been committed
743 * to disk. This is only valid for streams returned from
744 * {@link #openWrite(String, long, long)}.
745 */
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700746 public void fsync(@NonNull OutputStream out) throws IOException {
Jeff Sharkeya1031142014-07-12 18:09:46 -0700747 if (out instanceof FileBridge.FileBridgeOutputStream) {
748 ((FileBridge.FileBridgeOutputStream) out).fsync();
749 } else {
750 throw new IllegalArgumentException("Unrecognized stream");
751 }
752 }
753
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700754 /**
Jeff Sharkeya0907432014-08-15 10:23:11 -0700755 * Return all APK names contained in this session.
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700756 * <p>
757 * This returns all names which have been previously written through
758 * {@link #openWrite(String, long, long)} as part of this session.
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700759 *
760 * @throws SecurityException if called after the session has been
761 * committed or abandoned.
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700762 */
Jeff Sharkey742e7902014-08-16 19:09:13 -0700763 public @NonNull String[] getNames() throws IOException {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700764 try {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700765 return mSession.getNames();
Jeff Sharkey742e7902014-08-16 19:09:13 -0700766 } catch (RuntimeException e) {
767 ExceptionUtils.maybeUnwrapIOException(e);
768 throw e;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700769 } catch (RemoteException e) {
770 throw e.rethrowAsRuntimeException();
771 }
772 }
773
774 /**
775 * Open a stream to read an APK file from the session.
776 * <p>
777 * This is only valid for names which have been previously written
778 * through {@link #openWrite(String, long, long)} as part of this
779 * session. For example, this stream may be used to calculate a
780 * {@link MessageDigest} of a written APK before committing.
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700781 *
782 * @throws SecurityException if called after the session has been
783 * committed or abandoned.
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700784 */
785 public @NonNull InputStream openRead(@NonNull String name) throws IOException {
786 try {
787 final ParcelFileDescriptor pfd = mSession.openRead(name);
788 return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
789 } catch (RuntimeException e) {
790 ExceptionUtils.maybeUnwrapIOException(e);
791 throw e;
792 } catch (RemoteException e) {
793 throw e.rethrowAsRuntimeException();
794 }
795 }
796
797 /**
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700798 * Attempt to commit everything staged in this session. This may require
799 * user intervention, and so it may not happen immediately. The final
800 * result of the commit will be reported through the given callback.
801 * <p>
802 * Once this method is called, no additional mutations may be performed
803 * on the session. If the device reboots before the session has been
804 * finalized, you may commit the session again.
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700805 *
806 * @throws SecurityException if streams opened through
807 * {@link #openWrite(String, long, long)} are still open.
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700808 */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700809 public void commit(@NonNull IntentSender statusReceiver) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700810 try {
Jeff Sharkeya0907432014-08-15 10:23:11 -0700811 mSession.commit(statusReceiver);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700812 } catch (RemoteException e) {
813 throw e.rethrowAsRuntimeException();
814 }
815 }
816
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700817 /**
818 * Release this session object. You can open the session again if it
819 * hasn't been finalized.
820 */
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700821 @Override
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700822 public void close() {
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700823 try {
824 mSession.close();
825 } catch (RemoteException e) {
826 throw e.rethrowAsRuntimeException();
827 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700828 }
829
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700830 /**
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700831 * Completely abandon this session, destroying all staged data and
Jeff Sharkey77d218e2014-09-06 12:20:37 -0700832 * rendering it invalid. Abandoned sessions will be reported to
833 * {@link SessionCallback} listeners as failures. This is equivalent to
834 * opening the session and calling {@link Session#abandon()}.
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700835 */
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700836 public void abandon() {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700837 try {
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700838 mSession.abandon();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700839 } catch (RemoteException e) {
840 throw e.rethrowAsRuntimeException();
841 }
842 }
843 }
Jeff Sharkeybb580672014-07-10 12:10:25 -0700844
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700845 /**
Jeff Sharkeya0907432014-08-15 10:23:11 -0700846 * Parameters for creating a new {@link PackageInstaller.Session}.
Jeff Sharkey6c833e02014-07-14 22:44:30 -0700847 */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700848 public static class SessionParams implements Parcelable {
849
850 /** {@hide} */
851 public static final int MODE_INVALID = -1;
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700852
853 /**
Jeff Sharkeya0907432014-08-15 10:23:11 -0700854 * Mode for an install session whose staged APKs should fully replace any
855 * existing APKs for the target app.
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700856 */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700857 public static final int MODE_FULL_INSTALL = 1;
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700858
859 /**
Jeff Sharkeya0907432014-08-15 10:23:11 -0700860 * Mode for an install session that should inherit any existing APKs for the
861 * target app, unless they have been explicitly overridden (based on split
862 * name) by the session. For example, this can be used to add one or more
863 * split APKs to an existing installation.
Jeff Sharkeyfbd0e9f2014-08-06 16:34:34 -0700864 * <p>
Jeff Sharkeya0907432014-08-15 10:23:11 -0700865 * If there are no existing APKs for the target app, this behaves like
866 * {@link #MODE_FULL_INSTALL}.
Jeff Sharkeyfbd0e9f2014-08-06 16:34:34 -0700867 */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700868 public static final int MODE_INHERIT_EXISTING = 2;
Jeff Sharkeyfbd0e9f2014-08-06 16:34:34 -0700869
Jeff Sharkeya0907432014-08-15 10:23:11 -0700870 /** {@hide} */
Todd Kennedya1d12cf2015-09-29 15:43:00 -0700871 public static final int UID_UNKNOWN = -1;
872
873 /** {@hide} */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700874 public int mode = MODE_INVALID;
875 /** {@hide} */
876 public int installFlags;
877 /** {@hide} */
878 public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
879 /** {@hide} */
880 public long sizeBytes = -1;
881 /** {@hide} */
882 public String appPackageName;
883 /** {@hide} */
884 public Bitmap appIcon;
885 /** {@hide} */
886 public String appLabel;
887 /** {@hide} */
Jeff Sharkey02bd7842014-10-06 15:14:27 -0700888 public long appIconLastModified = -1;
889 /** {@hide} */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700890 public Uri originatingUri;
891 /** {@hide} */
Todd Kennedya1d12cf2015-09-29 15:43:00 -0700892 public int originatingUid = UID_UNKNOWN;
893 /** {@hide} */
Jeff Sharkeya0907432014-08-15 10:23:11 -0700894 public Uri referrerUri;
895 /** {@hide} */
896 public String abiOverride;
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700897 /** {@hide} */
898 public String volumeUuid;
Svet Ganov7121e182015-07-13 22:38:12 -0700899 /** {@hide} */
900 public String[] grantedRuntimePermissions;
Jeff Sharkeybb580672014-07-10 12:10:25 -0700901
Jeff Sharkeya0907432014-08-15 10:23:11 -0700902 /**
903 * Construct parameters for a new package install session.
904 *
905 * @param mode one of {@link #MODE_FULL_INSTALL} or
906 * {@link #MODE_INHERIT_EXISTING} describing how the session
907 * should interact with an existing app.
908 */
909 public SessionParams(int mode) {
910 this.mode = mode;
911 }
Jeff Sharkeybb580672014-07-10 12:10:25 -0700912
Jeff Sharkeya0907432014-08-15 10:23:11 -0700913 /** {@hide} */
914 public SessionParams(Parcel source) {
915 mode = source.readInt();
916 installFlags = source.readInt();
917 installLocation = source.readInt();
918 sizeBytes = source.readLong();
919 appPackageName = source.readString();
920 appIcon = source.readParcelable(null);
921 appLabel = source.readString();
922 originatingUri = source.readParcelable(null);
Todd Kennedya1d12cf2015-09-29 15:43:00 -0700923 originatingUid = source.readInt();
Jeff Sharkeya0907432014-08-15 10:23:11 -0700924 referrerUri = source.readParcelable(null);
925 abiOverride = source.readString();
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -0700926 volumeUuid = source.readString();
Svet Ganov7121e182015-07-13 22:38:12 -0700927 grantedRuntimePermissions = source.readStringArray();
Jeff Sharkeya0907432014-08-15 10:23:11 -0700928 }
929
930 /**
931 * Provide value of {@link PackageInfo#installLocation}, which may be used
932 * to determine where the app will be staged. Defaults to
933 * {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}.
934 */
935 public void setInstallLocation(int installLocation) {
936 this.installLocation = installLocation;
937 }
938
939 /**
940 * Optionally indicate the total size (in bytes) of all APKs that will be
941 * delivered in this session. The system may use this to ensure enough disk
942 * space exists before proceeding, or to estimate container size for
943 * installations living on external storage.
944 *
945 * @see PackageInfo#INSTALL_LOCATION_AUTO
946 * @see PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL
947 */
948 public void setSize(long sizeBytes) {
949 this.sizeBytes = sizeBytes;
950 }
951
952 /**
953 * Optionally set the package name of the app being installed. It's strongly
954 * recommended that you provide this value when known, so that observers can
955 * communicate installing apps to users.
956 * <p>
957 * If the APKs staged in the session aren't consistent with this package
958 * name, the install will fail. Regardless of this value, all APKs in the
959 * app must have the same package name.
960 */
961 public void setAppPackageName(@Nullable String appPackageName) {
962 this.appPackageName = appPackageName;
963 }
964
965 /**
966 * Optionally set an icon representing the app being installed. This should
967 * be roughly {@link ActivityManager#getLauncherLargeIconSize()} in both
968 * dimensions.
969 */
970 public void setAppIcon(@Nullable Bitmap appIcon) {
971 this.appIcon = appIcon;
972 }
973
974 /**
975 * Optionally set a label representing the app being installed.
976 */
977 public void setAppLabel(@Nullable CharSequence appLabel) {
978 this.appLabel = (appLabel != null) ? appLabel.toString() : null;
979 }
980
981 /**
982 * Optionally set the URI where this package was downloaded from. Used for
983 * verification purposes.
984 *
985 * @see Intent#EXTRA_ORIGINATING_URI
986 */
987 public void setOriginatingUri(@Nullable Uri originatingUri) {
988 this.originatingUri = originatingUri;
989 }
990
991 /**
Todd Kennedya1d12cf2015-09-29 15:43:00 -0700992 * Sets the UID that initiated package installation. Used for verification purposes.
993 *
994 * @see PackageManager#EXTRA_VERIFICATION_INSTALLER_UID
995 */
996 public void setOriginatingUid(int originatingUid) {
997 this.originatingUid = originatingUid;
998 }
999
1000 /**
Jeff Sharkeya0907432014-08-15 10:23:11 -07001001 * Optionally set the URI that referred you to install this package. Used
1002 * for verification purposes.
1003 *
1004 * @see Intent#EXTRA_REFERRER
1005 */
1006 public void setReferrerUri(@Nullable Uri referrerUri) {
1007 this.referrerUri = referrerUri;
1008 }
1009
Svet Ganov7121e182015-07-13 22:38:12 -07001010 /**
1011 * Sets which runtime permissions to be granted to the package at installation.
1012 * Using this API requires holding {@link android.Manifest.permission
1013 * #INSTALL_GRANT_RUNTIME_PERMISSIONS}
1014 *
1015 * @param permissions The permissions to grant or null to grant all runtime
1016 * permissions.
1017 *
1018 * @hide
1019 */
1020 @SystemApi
1021 @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS)
1022 public void setGrantedRuntimePermissions(String[] permissions) {
1023 installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
1024 this.grantedRuntimePermissions = permissions;
1025 }
1026
Jeff Sharkeya0907432014-08-15 10:23:11 -07001027 /** {@hide} */
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001028 public void setInstallFlagsInternal() {
1029 installFlags |= PackageManager.INSTALL_INTERNAL;
1030 installFlags &= ~PackageManager.INSTALL_EXTERNAL;
1031 }
1032
1033 /** {@hide} */
1034 public void setInstallFlagsExternal() {
1035 installFlags |= PackageManager.INSTALL_EXTERNAL;
1036 installFlags &= ~PackageManager.INSTALL_INTERNAL;
1037 }
1038
1039 /** {@hide} */
Todd Kennedya1d12cf2015-09-29 15:43:00 -07001040 public void setInstallFlagsForcePermissionPrompt() {
1041 installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
1042 }
1043
1044 /** {@hide} */
Jeff Sharkeya0907432014-08-15 10:23:11 -07001045 public void dump(IndentingPrintWriter pw) {
1046 pw.printPair("mode", mode);
1047 pw.printHexPair("installFlags", installFlags);
1048 pw.printPair("installLocation", installLocation);
1049 pw.printPair("sizeBytes", sizeBytes);
1050 pw.printPair("appPackageName", appPackageName);
1051 pw.printPair("appIcon", (appIcon != null));
1052 pw.printPair("appLabel", appLabel);
1053 pw.printPair("originatingUri", originatingUri);
Todd Kennedya1d12cf2015-09-29 15:43:00 -07001054 pw.printPair("originatingUid", originatingUid);
Jeff Sharkeya0907432014-08-15 10:23:11 -07001055 pw.printPair("referrerUri", referrerUri);
1056 pw.printPair("abiOverride", abiOverride);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -07001057 pw.printPair("volumeUuid", volumeUuid);
Svet Ganov7121e182015-07-13 22:38:12 -07001058 pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions);
Jeff Sharkeya0907432014-08-15 10:23:11 -07001059 pw.println();
Jeff Sharkeybb580672014-07-10 12:10:25 -07001060 }
1061
1062 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -07001063 public int describeContents() {
1064 return 0;
Jeff Sharkeyfbd0e9f2014-08-06 16:34:34 -07001065 }
1066
1067 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -07001068 public void writeToParcel(Parcel dest, int flags) {
1069 dest.writeInt(mode);
1070 dest.writeInt(installFlags);
1071 dest.writeInt(installLocation);
1072 dest.writeLong(sizeBytes);
1073 dest.writeString(appPackageName);
1074 dest.writeParcelable(appIcon, flags);
1075 dest.writeString(appLabel);
1076 dest.writeParcelable(originatingUri, flags);
Todd Kennedya1d12cf2015-09-29 15:43:00 -07001077 dest.writeInt(originatingUid);
Jeff Sharkeya0907432014-08-15 10:23:11 -07001078 dest.writeParcelable(referrerUri, flags);
1079 dest.writeString(abiOverride);
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -07001080 dest.writeString(volumeUuid);
Svet Ganov7121e182015-07-13 22:38:12 -07001081 dest.writeStringArray(grantedRuntimePermissions);
Jeff Sharkeybb580672014-07-10 12:10:25 -07001082 }
Jeff Sharkeya0907432014-08-15 10:23:11 -07001083
1084 public static final Parcelable.Creator<SessionParams>
1085 CREATOR = new Parcelable.Creator<SessionParams>() {
1086 @Override
1087 public SessionParams createFromParcel(Parcel p) {
1088 return new SessionParams(p);
1089 }
1090
1091 @Override
1092 public SessionParams[] newArray(int size) {
1093 return new SessionParams[size];
1094 }
1095 };
Jeff Sharkeybb580672014-07-10 12:10:25 -07001096 }
1097
Jeff Sharkey6c833e02014-07-14 22:44:30 -07001098 /**
Jeff Sharkeya0907432014-08-15 10:23:11 -07001099 * Details for an active install session.
Jeff Sharkey6c833e02014-07-14 22:44:30 -07001100 */
Jeff Sharkeya0907432014-08-15 10:23:11 -07001101 public static class SessionInfo implements Parcelable {
1102
1103 /** {@hide} */
1104 public int sessionId;
1105 /** {@hide} */
1106 public String installerPackageName;
1107 /** {@hide} */
1108 public String resolvedBaseCodePath;
1109 /** {@hide} */
1110 public float progress;
1111 /** {@hide} */
1112 public boolean sealed;
1113 /** {@hide} */
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001114 public boolean active;
Jeff Sharkeya0907432014-08-15 10:23:11 -07001115
1116 /** {@hide} */
1117 public int mode;
1118 /** {@hide} */
1119 public long sizeBytes;
1120 /** {@hide} */
1121 public String appPackageName;
1122 /** {@hide} */
1123 public Bitmap appIcon;
1124 /** {@hide} */
1125 public CharSequence appLabel;
1126
1127 /** {@hide} */
1128 public SessionInfo() {
1129 }
1130
1131 /** {@hide} */
1132 public SessionInfo(Parcel source) {
1133 sessionId = source.readInt();
1134 installerPackageName = source.readString();
1135 resolvedBaseCodePath = source.readString();
1136 progress = source.readFloat();
1137 sealed = source.readInt() != 0;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001138 active = source.readInt() != 0;
Jeff Sharkeya0907432014-08-15 10:23:11 -07001139
1140 mode = source.readInt();
1141 sizeBytes = source.readLong();
1142 appPackageName = source.readString();
1143 appIcon = source.readParcelable(null);
1144 appLabel = source.readString();
1145 }
Jeff Sharkeybb580672014-07-10 12:10:25 -07001146
1147 /**
Jeff Sharkeya0907432014-08-15 10:23:11 -07001148 * Return the ID for this session.
Jeff Sharkeybb580672014-07-10 12:10:25 -07001149 */
Jeff Sharkeya0907432014-08-15 10:23:11 -07001150 public int getSessionId() {
1151 return sessionId;
1152 }
Jeff Sharkeybb580672014-07-10 12:10:25 -07001153
1154 /**
Jeff Sharkeya0907432014-08-15 10:23:11 -07001155 * Return the package name of the app that owns this session.
1156 */
1157 public @Nullable String getInstallerPackageName() {
1158 return installerPackageName;
1159 }
1160
1161 /**
1162 * Return current overall progress of this session, between 0 and 1.
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -07001163 * <p>
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001164 * Note that this progress may not directly correspond to the value
1165 * reported by
1166 * {@link PackageInstaller.Session#setStagingProgress(float)}, as the
1167 * system may carve out a portion of the overall progress to represent
1168 * its own internal installation work.
Jeff Sharkeybb580672014-07-10 12:10:25 -07001169 */
Jeff Sharkeya0907432014-08-15 10:23:11 -07001170 public float getProgress() {
1171 return progress;
1172 }
Jeff Sharkeybb580672014-07-10 12:10:25 -07001173
1174 /**
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001175 * Return if this session is currently active.
1176 * <p>
1177 * A session is considered active whenever there is ongoing forward
1178 * progress being made, such as the installer holding an open
1179 * {@link Session} instance while streaming data into place, or the
1180 * system optimizing code as the result of
1181 * {@link Session#commit(IntentSender)}.
1182 * <p>
1183 * If the installer closes the {@link Session} without committing, the
1184 * session is considered inactive until the installer opens the session
1185 * again.
Jeff Sharkeybb580672014-07-10 12:10:25 -07001186 */
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001187 public boolean isActive() {
1188 return active;
1189 }
1190
1191 /** {@hide} */
1192 @Deprecated
Jeff Sharkeya0907432014-08-15 10:23:11 -07001193 public boolean isOpen() {
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001194 return isActive();
Jeff Sharkeya0907432014-08-15 10:23:11 -07001195 }
Jeff Sharkeybb580672014-07-10 12:10:25 -07001196
1197 /**
Jeff Sharkeya0907432014-08-15 10:23:11 -07001198 * Return the package name this session is working with. May be {@code null}
1199 * if unknown.
Jeff Sharkeybb580672014-07-10 12:10:25 -07001200 */
Jeff Sharkeya0907432014-08-15 10:23:11 -07001201 public @Nullable String getAppPackageName() {
1202 return appPackageName;
1203 }
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -07001204
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001205 /**
Jeff Sharkeya0907432014-08-15 10:23:11 -07001206 * Return an icon representing the app being installed. May be {@code null}
1207 * if unavailable.
1208 */
1209 public @Nullable Bitmap getAppIcon() {
1210 return appIcon;
1211 }
1212
1213 /**
1214 * Return a label representing the app being installed. May be {@code null}
1215 * if unavailable.
1216 */
1217 public @Nullable CharSequence getAppLabel() {
1218 return appLabel;
1219 }
1220
1221 /**
1222 * Return an Intent that can be started to view details about this install
1223 * session. This may surface actions such as pause, resume, or cancel.
1224 * <p>
1225 * In some cases, a matching Activity may not exist, so ensure you safeguard
1226 * against this.
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001227 *
Jeff Sharkeya0907432014-08-15 10:23:11 -07001228 * @see PackageInstaller#ACTION_SESSION_DETAILS
Jeff Sharkey7328a1b2014-08-07 14:01:43 -07001229 */
Jeff Sharkeyde742312014-09-15 14:04:56 -07001230 public @Nullable Intent createDetailsIntent() {
Jeff Sharkeya0907432014-08-15 10:23:11 -07001231 final Intent intent = new Intent(PackageInstaller.ACTION_SESSION_DETAILS);
1232 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
1233 intent.setPackage(installerPackageName);
1234 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1235 return intent;
Jeff Sharkeybb580672014-07-10 12:10:25 -07001236 }
1237
Jeff Sharkeyde742312014-09-15 14:04:56 -07001238 /** {@hide} */
1239 @Deprecated
1240 public @Nullable Intent getDetailsIntent() {
1241 return createDetailsIntent();
1242 }
1243
Jeff Sharkeybb580672014-07-10 12:10:25 -07001244 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -07001245 public int describeContents() {
1246 return 0;
Jeff Sharkeyfbd0e9f2014-08-06 16:34:34 -07001247 }
1248
1249 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -07001250 public void writeToParcel(Parcel dest, int flags) {
1251 dest.writeInt(sessionId);
1252 dest.writeString(installerPackageName);
1253 dest.writeString(resolvedBaseCodePath);
1254 dest.writeFloat(progress);
1255 dest.writeInt(sealed ? 1 : 0);
Jeff Sharkeybc7bce32014-09-05 15:53:05 -07001256 dest.writeInt(active ? 1 : 0);
Jeff Sharkeybb580672014-07-10 12:10:25 -07001257
Jeff Sharkeya0907432014-08-15 10:23:11 -07001258 dest.writeInt(mode);
1259 dest.writeLong(sizeBytes);
1260 dest.writeString(appPackageName);
1261 dest.writeParcelable(appIcon, flags);
1262 dest.writeString(appLabel != null ? appLabel.toString() : null);
Jeff Sharkeybb580672014-07-10 12:10:25 -07001263 }
Jeff Sharkeya0907432014-08-15 10:23:11 -07001264
1265 public static final Parcelable.Creator<SessionInfo>
1266 CREATOR = new Parcelable.Creator<SessionInfo>() {
1267 @Override
1268 public SessionInfo createFromParcel(Parcel p) {
1269 return new SessionInfo(p);
1270 }
1271
1272 @Override
1273 public SessionInfo[] newArray(int size) {
1274 return new SessionInfo[size];
1275 }
1276 };
Jeff Sharkeybb580672014-07-10 12:10:25 -07001277 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001278}