PackageInstaller.java revision da96e137bcc8191c584ada7b5de31eaae92f244f
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.content.pm;
18
19import android.app.PackageInstallObserver;
20import android.app.PackageUninstallObserver;
21import android.content.pm.PackageManager.NameNotFoundException;
22import android.os.Bundle;
23import android.os.FileBridge;
24import android.os.ParcelFileDescriptor;
25import android.os.RemoteException;
26import android.util.ExceptionUtils;
27
28import java.io.Closeable;
29import java.io.IOException;
30import java.io.OutputStream;
31import java.util.List;
32
33/**
34 * Offers the ability to install, upgrade, and remove applications on the
35 * device. This includes support for apps packaged either as a single
36 * "monolithic" APK, or apps packaged as multiple "split" APKs.
37 * <p>
38 * An app is delivered for installation through a
39 * {@link PackageInstaller.Session}, which any app can create. Once the session
40 * is created, the installer can stream one or more APKs into place until it
41 * decides to either commit or destroy the session. Committing may require user
42 * intervention to complete the installation.
43 * <p>
44 * Sessions can install brand new apps, upgrade existing apps, or add new splits
45 * into an existing app.
46 * <p>
47 * Apps packaged as multiple split APKs always consist of a single "base" APK
48 * (with a {@code null} split name) and zero or more "split" APKs (with unique
49 * split names). Any subset of these APKs can be installed together, as long as
50 * the following constraints are met:
51 * <ul>
52 * <li>All APKs must have the exact same package name, version code, and signing
53 * certificates.
54 * <li>All APKs must have unique split names.
55 * <li>All installations must contain a single base APK.
56 * </ul>
57 */
58public class PackageInstaller {
59    private final PackageManager mPm;
60    private final IPackageInstaller mInstaller;
61    private final int mUserId;
62    private final String mInstallerPackageName;
63
64    /** {@hide} */
65    public PackageInstaller(PackageManager pm, IPackageInstaller installer,
66            String installerPackageName, int userId) {
67        mPm = pm;
68        mInstaller = installer;
69        mInstallerPackageName = installerPackageName;
70        mUserId = userId;
71    }
72
73    /**
74     * Quickly test if the given package is already available on the device.
75     * This is typically used in multi-user scenarios where another user on the
76     * device has already installed the package.
77     *
78     * @hide
79     */
80    public boolean isPackageAvailable(String packageName) {
81        return mPm.isPackageAvailable(packageName);
82    }
83
84    /** {@hide} */
85    public void installAvailablePackage(String packageName, PackageInstallObserver observer) {
86        int returnCode;
87        try {
88            returnCode = mPm.installExistingPackage(packageName);
89        } catch (NameNotFoundException e) {
90            returnCode = PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
91        }
92        observer.packageInstalled(packageName, null, returnCode);
93    }
94
95    /**
96     * Create a new session using the given parameters, returning a unique ID
97     * that represents the session. Once created, the session can be opened
98     * multiple times across multiple device boots.
99     * <p>
100     * The system may automatically destroy sessions that have not been
101     * finalized (either committed or abandoned) within a reasonable period of
102     * time, typically on the order of a day.
103     *
104     * @throws IOException if parameters were unsatisfiable, such as lack of
105     *             disk space or unavailable media.
106     */
107    public int createSession(InstallSessionParams params) throws IOException {
108        try {
109            return mInstaller.createSession(mInstallerPackageName, params, mUserId);
110        } catch (RuntimeException e) {
111            ExceptionUtils.maybeUnwrapIOException(e);
112            throw e;
113        } catch (RemoteException e) {
114            throw e.rethrowAsRuntimeException();
115        }
116    }
117
118    /**
119     * Open an existing session to actively perform work.
120     */
121    public Session openSession(int sessionId) {
122        try {
123            return new Session(mInstaller.openSession(sessionId));
124        } catch (RemoteException e) {
125            throw e.rethrowAsRuntimeException();
126        }
127    }
128
129    /**
130     * Return list of all active install sessions on the device.
131     */
132    public List<InstallSessionInfo> getActiveSessions() {
133        // TODO: filter based on caller
134        // TODO: let launcher app see all active sessions
135        try {
136            return mInstaller.getSessions(mUserId);
137        } catch (RemoteException e) {
138            throw e.rethrowAsRuntimeException();
139        }
140    }
141
142    /**
143     * Uninstall the given package, removing it completely from the device. This
144     * method is only available to the current "installer of record" for the
145     * package.
146     */
147    public void uninstall(String packageName, UninstallResultCallback callback) {
148        try {
149            mInstaller.uninstall(packageName, 0,
150                    new UninstallResultCallbackDelegate(callback).getBinder(), mUserId);
151        } catch (RemoteException e) {
152            throw e.rethrowAsRuntimeException();
153        }
154    }
155
156    /**
157     * Uninstall only a specific split from the given package.
158     *
159     * @hide
160     */
161    public void uninstall(String packageName, String splitName, UninstallResultCallback callback) {
162        try {
163            mInstaller.uninstallSplit(packageName, splitName, 0,
164                    new UninstallResultCallbackDelegate(callback).getBinder(), mUserId);
165        } catch (RemoteException e) {
166            throw e.rethrowAsRuntimeException();
167        }
168    }
169
170    /**
171     * Events for observing session lifecycle.
172     */
173    public static abstract class SessionObserver {
174        private final IPackageInstallerObserver.Stub mBinder = new IPackageInstallerObserver.Stub() {
175            @Override
176            public void onSessionCreated(InstallSessionInfo info) {
177                SessionObserver.this.onCreated(info);
178            }
179
180            @Override
181            public void onSessionProgress(int sessionId, int progress) {
182                SessionObserver.this.onProgress(sessionId, progress);
183            }
184
185            @Override
186            public void onSessionFinished(int sessionId, boolean success) {
187                SessionObserver.this.onFinalized(sessionId, success);
188            }
189        };
190
191        /** {@hide} */
192        public IPackageInstallerObserver getBinder() {
193            return mBinder;
194        }
195
196        /**
197         * New session has been created.
198         */
199        public abstract void onCreated(InstallSessionInfo info);
200
201        /**
202         * Progress for given session has been updated.
203         * <p>
204         * Note that this progress may not directly correspond to the value
205         * reported by {@link PackageInstaller.Session#setProgress(int)}, as the
206         * system may carve out a portion of the overall progress to represent
207         * its own internal installation work.
208         */
209        public abstract void onProgress(int sessionId, int progress);
210
211        /**
212         * Session has been finalized, either with success or failure.
213         */
214        public abstract void onFinalized(int sessionId, boolean success);
215    }
216
217    /**
218     * Register to watch for session lifecycle events.
219     */
220    public void registerSessionObserver(SessionObserver observer) {
221        try {
222            mInstaller.registerObserver(observer.getBinder(), mUserId);
223        } catch (RemoteException e) {
224            throw e.rethrowAsRuntimeException();
225        }
226    }
227
228    /**
229     * Unregister an existing observer.
230     */
231    public void unregisterSessionObserver(SessionObserver observer) {
232        try {
233            mInstaller.unregisterObserver(observer.getBinder(), mUserId);
234        } catch (RemoteException e) {
235            throw e.rethrowAsRuntimeException();
236        }
237    }
238
239    /**
240     * An installation that is being actively staged. For an install to succeed,
241     * all existing and new packages must have identical package names, version
242     * codes, and signing certificates.
243     * <p>
244     * A session may contain any number of split packages. If the application
245     * does not yet exist, this session must include a base package.
246     * <p>
247     * If a package included in this session is already defined by the existing
248     * installation (for example, the same split name), the package in this
249     * session will replace the existing package.
250     */
251    public static class Session implements Closeable {
252        private IPackageInstallerSession mSession;
253
254        /** {@hide} */
255        public Session(IPackageInstallerSession session) {
256            mSession = session;
257        }
258
259        /**
260         * Set current progress. Valid values are anywhere between 0 and
261         * {@link InstallSessionParams#setProgressMax(int)}.
262         */
263        public void setProgress(int progress) {
264            try {
265                mSession.setClientProgress(progress);
266            } catch (RemoteException e) {
267                throw e.rethrowAsRuntimeException();
268            }
269        }
270
271        /** {@hide} */
272        public void addProgress(int progress) {
273            try {
274                mSession.addClientProgress(progress);
275            } catch (RemoteException e) {
276                throw e.rethrowAsRuntimeException();
277            }
278        }
279
280        /**
281         * Open an APK file for writing, starting at the given offset. You can
282         * then stream data into the file, periodically calling
283         * {@link #fsync(OutputStream)} to ensure bytes have been written to
284         * disk.
285         */
286        public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes)
287                throws IOException {
288            try {
289                final ParcelFileDescriptor clientSocket = mSession.openWrite(splitName,
290                        offsetBytes, lengthBytes);
291                return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor());
292            } catch (RuntimeException e) {
293                ExceptionUtils.maybeUnwrapIOException(e);
294                throw e;
295            } catch (RemoteException e) {
296                throw new IOException(e);
297            }
298        }
299
300        /**
301         * Ensure that any outstanding data for given stream has been committed
302         * to disk. This is only valid for streams returned from
303         * {@link #openWrite(String, long, long)}.
304         */
305        public void fsync(OutputStream out) throws IOException {
306            if (out instanceof FileBridge.FileBridgeOutputStream) {
307                ((FileBridge.FileBridgeOutputStream) out).fsync();
308            } else {
309                throw new IllegalArgumentException("Unrecognized stream");
310            }
311        }
312
313        /**
314         * Attempt to commit everything staged in this session. This may require
315         * user intervention, and so it may not happen immediately. The final
316         * result of the commit will be reported through the given callback.
317         * <p>
318         * Once this method is called, no additional mutations may be performed
319         * on the session. If the device reboots before the session has been
320         * finalized, you may commit the session again.
321         */
322        public void commit(CommitResultCallback callback) {
323            try {
324                mSession.install(new CommitResultCallbackDelegate(callback).getBinder());
325            } catch (RemoteException e) {
326                throw e.rethrowAsRuntimeException();
327            }
328        }
329
330        /**
331         * Release this session object. You can open the session again if it
332         * hasn't been finalized.
333         */
334        @Override
335        public void close() {
336            // No resources to release at the moment
337        }
338
339        /**
340         * Completely destroy this session, rendering it invalid.
341         */
342        public void destroy() {
343            try {
344                mSession.destroy();
345            } catch (RemoteException e) {
346                throw e.rethrowAsRuntimeException();
347            }
348        }
349    }
350
351    /**
352     * Final result of an uninstall request.
353     */
354    public static abstract class UninstallResultCallback {
355        public abstract void onSuccess();
356        public abstract void onFailure(String msg);
357    }
358
359    /** {@hide} */
360    private static class UninstallResultCallbackDelegate extends PackageUninstallObserver {
361        private final UninstallResultCallback target;
362
363        public UninstallResultCallbackDelegate(UninstallResultCallback target) {
364            this.target = target;
365        }
366
367        @Override
368        public void onUninstallFinished(String basePackageName, int returnCode) {
369            final String msg = null;
370
371            switch (returnCode) {
372                case PackageManager.DELETE_SUCCEEDED: target.onSuccess(); break;
373                case PackageManager.DELETE_FAILED_INTERNAL_ERROR: target.onFailure("DELETE_FAILED_INTERNAL_ERROR: " + msg); break;
374                case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER: target.onFailure("DELETE_FAILED_DEVICE_POLICY_MANAGER: " + msg); break;
375                case PackageManager.DELETE_FAILED_USER_RESTRICTED: target.onFailure("DELETE_FAILED_USER_RESTRICTED: " + msg); break;
376                case PackageManager.DELETE_FAILED_OWNER_BLOCKED: target.onFailure("DELETE_FAILED_OWNER_BLOCKED: " + msg); break;
377                default: target.onFailure(msg); break;
378            }
379        }
380    }
381
382    /**
383     * Final result of a session commit request.
384     */
385    public static abstract class CommitResultCallback {
386        public abstract void onSuccess();
387
388        /**
389         * Generic failure occurred. You can override methods (such as
390         * {@link #onFailureInvalid(String)}) to handle more specific categories
391         * of failure. By default, those specific categories all flow into this
392         * generic failure.
393         */
394        public abstract void onFailure(String msg);
395
396        /**
397         * One or more of the APKs included in the session was invalid. For
398         * example, they might be malformed, corrupt, incorrectly signed,
399         * mismatched, etc. The installer may want to try downloading and
400         * installing again.
401         */
402        public void onFailureInvalid(String msg) {
403            onFailure(msg);
404        }
405
406        /**
407         * This install session conflicts (or is inconsistent with) with another
408         * package already installed on the device. For example, an existing
409         * permission, incompatible certificates, etc. The user may be able to
410         * uninstall another app to fix the issue.
411         *
412         * @param otherPackageName if one specific package was identified as the
413         *            cause of the conflict, it's named here. If unknown, or
414         *            multiple packages, this may be {@code null}.
415         */
416        public void onFailureConflict(String msg, String otherPackageName) {
417            onFailure(msg);
418        }
419
420        /**
421         * This install session failed due to storage issues. For example,
422         * the device may be running low on space, or the required external
423         * media may be unavailable. The user may be able to help free space
424         * or insert the correct media.
425         */
426        public void onFailureStorage(String msg) {
427            onFailure(msg);
428        }
429
430        /**
431         * This install session is fundamentally incompatible with this
432         * device. For example, the package may require a hardware feature
433         * that doesn't exist, it may be missing native code for the device
434         * ABI, or it requires a newer SDK version, etc. This install would
435         * never succeed.
436         */
437        public void onFailureIncompatible(String msg) {
438            onFailure(msg);
439        }
440    }
441
442    /** {@hide} */
443    private static class CommitResultCallbackDelegate extends PackageInstallObserver {
444        private final CommitResultCallback target;
445
446        public CommitResultCallbackDelegate(CommitResultCallback target) {
447            this.target = target;
448        }
449
450        @Override
451        public void packageInstalled(String basePackageName, Bundle extras, int returnCode,
452                String msg) {
453            final String otherPackage = null;
454
455            switch (returnCode) {
456                case PackageManager.INSTALL_SUCCEEDED: target.onSuccess(); break;
457                case PackageManager.INSTALL_FAILED_ALREADY_EXISTS: target.onFailureConflict("INSTALL_FAILED_ALREADY_EXISTS: " + msg, otherPackage); break;
458                case PackageManager.INSTALL_FAILED_INVALID_APK: target.onFailureInvalid("INSTALL_FAILED_INVALID_APK: " + msg); break;
459                case PackageManager.INSTALL_FAILED_INVALID_URI: target.onFailureInvalid("INSTALL_FAILED_INVALID_URI: " + msg); break;
460                case PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE: target.onFailureStorage("INSTALL_FAILED_INSUFFICIENT_STORAGE: " + msg); break;
461                case PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE: target.onFailureConflict("INSTALL_FAILED_DUPLICATE_PACKAGE: " + msg, otherPackage); break;
462                case PackageManager.INSTALL_FAILED_NO_SHARED_USER: target.onFailureConflict("INSTALL_FAILED_NO_SHARED_USER: " + msg, otherPackage); break;
463                case PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE: target.onFailureConflict("INSTALL_FAILED_UPDATE_INCOMPATIBLE: " + msg, otherPackage); break;
464                case PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: target.onFailureConflict("INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: " + msg, otherPackage); break;
465                case PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY: target.onFailureIncompatible("INSTALL_FAILED_MISSING_SHARED_LIBRARY: " + msg); break;
466                case PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE: target.onFailureConflict("INSTALL_FAILED_REPLACE_COULDNT_DELETE: " + msg, otherPackage); break;
467                case PackageManager.INSTALL_FAILED_DEXOPT: target.onFailureInvalid("INSTALL_FAILED_DEXOPT: " + msg); break;
468                case PackageManager.INSTALL_FAILED_OLDER_SDK: target.onFailureIncompatible("INSTALL_FAILED_OLDER_SDK: " + msg); break;
469                case PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER: target.onFailureConflict("INSTALL_FAILED_CONFLICTING_PROVIDER: " + msg, otherPackage); break;
470                case PackageManager.INSTALL_FAILED_NEWER_SDK: target.onFailureIncompatible("INSTALL_FAILED_NEWER_SDK: " + msg); break;
471                case PackageManager.INSTALL_FAILED_TEST_ONLY: target.onFailureInvalid("INSTALL_FAILED_TEST_ONLY: " + msg); break;
472                case PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: target.onFailureIncompatible("INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: " + msg); break;
473                case PackageManager.INSTALL_FAILED_MISSING_FEATURE: target.onFailureIncompatible("INSTALL_FAILED_MISSING_FEATURE: " + msg); break;
474                case PackageManager.INSTALL_FAILED_CONTAINER_ERROR: target.onFailureStorage("INSTALL_FAILED_CONTAINER_ERROR: " + msg); break;
475                case PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION: target.onFailureStorage("INSTALL_FAILED_INVALID_INSTALL_LOCATION: " + msg); break;
476                case PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE: target.onFailureStorage("INSTALL_FAILED_MEDIA_UNAVAILABLE: " + msg); break;
477                case PackageManager.INSTALL_FAILED_VERIFICATION_TIMEOUT: target.onFailure("INSTALL_FAILED_VERIFICATION_TIMEOUT: " + msg); break;
478                case PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE: target.onFailure("INSTALL_FAILED_VERIFICATION_FAILURE: " + msg); break;
479                case PackageManager.INSTALL_FAILED_PACKAGE_CHANGED: target.onFailureInvalid("INSTALL_FAILED_PACKAGE_CHANGED: " + msg); break;
480                case PackageManager.INSTALL_FAILED_UID_CHANGED: target.onFailureInvalid("INSTALL_FAILED_UID_CHANGED: " + msg); break;
481                case PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE: target.onFailureInvalid("INSTALL_FAILED_VERSION_DOWNGRADE: " + msg); break;
482                case PackageManager.INSTALL_PARSE_FAILED_NOT_APK: target.onFailureInvalid("INSTALL_PARSE_FAILED_NOT_APK: " + msg); break;
483                case PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST: target.onFailureInvalid("INSTALL_PARSE_FAILED_BAD_MANIFEST: " + msg); break;
484                case PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: target.onFailureInvalid("INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: " + msg); break;
485                case PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES: target.onFailureInvalid("INSTALL_PARSE_FAILED_NO_CERTIFICATES: " + msg); break;
486                case PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: target.onFailureInvalid("INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: " + msg); break;
487                case PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: target.onFailureInvalid("INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: " + msg); break;
488                case PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: target.onFailureInvalid("INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: " + msg); break;
489                case PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: target.onFailureInvalid("INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: " + msg); break;
490                case PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: target.onFailureInvalid("INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: " + msg); break;
491                case PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY: target.onFailureInvalid("INSTALL_PARSE_FAILED_MANIFEST_EMPTY: " + msg); break;
492                case PackageManager.INSTALL_FAILED_INTERNAL_ERROR: target.onFailure("INSTALL_FAILED_INTERNAL_ERROR: " + msg); break;
493                case PackageManager.INSTALL_FAILED_USER_RESTRICTED: target.onFailureIncompatible("INSTALL_FAILED_USER_RESTRICTED: " + msg); break;
494                case PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION: target.onFailureConflict("INSTALL_FAILED_DUPLICATE_PERMISSION: " + msg, otherPackage); break;
495                case PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS: target.onFailureInvalid("INSTALL_FAILED_NO_MATCHING_ABIS: " + msg); break;
496                default: target.onFailure(msg); break;
497            }
498        }
499    }
500}
501