summaryrefslogtreecommitdiffstats
path: root/core/java/android/content
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2014-07-14 22:44:30 -0700
committerJeff Sharkey <jsharkey@android.com>2014-07-14 23:06:52 -0700
commit6c833e07a05c48ca60ee4d72421bf8b1e78dc710 (patch)
treef16f3c2a12dea6fd5131d82d007fd75cd61fb915 /core/java/android/content
parent5c6a8e322227354acbded5c49f44c0b289021bf5 (diff)
downloadframeworks_base-6c833e07a05c48ca60ee4d72421bf8b1e78dc710.zip
frameworks_base-6c833e07a05c48ca60ee4d72421bf8b1e78dc710.tar.gz
frameworks_base-6c833e07a05c48ca60ee4d72421bf8b1e78dc710.tar.bz2
Public API for PackageInstaller!
Flesh out documentation and finalize first cut of API. Also surface installLocation and splitNames through PackageInfo. Bug: 14975160, 15348430 Change-Id: Ic27696d20ed06e508aa3526218e9cb20835af6a0
Diffstat (limited to 'core/java/android/content')
-rw-r--r--core/java/android/content/pm/InstallSessionInfo.java68
-rw-r--r--core/java/android/content/pm/InstallSessionParams.java83
-rw-r--r--core/java/android/content/pm/PackageInfo.java32
-rw-r--r--core/java/android/content/pm/PackageInstaller.java157
-rw-r--r--core/java/android/content/pm/PackageManager.java10
-rw-r--r--core/java/android/content/pm/PackageParser.java1
6 files changed, 306 insertions, 45 deletions
diff --git a/core/java/android/content/pm/InstallSessionInfo.java b/core/java/android/content/pm/InstallSessionInfo.java
index 45606d1..3336727 100644
--- a/core/java/android/content/pm/InstallSessionInfo.java
+++ b/core/java/android/content/pm/InstallSessionInfo.java
@@ -20,15 +20,25 @@ import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
-/** {@hide} */
+/**
+ * Details for an active install session.
+ */
public class InstallSessionInfo implements Parcelable {
+
+ /** {@hide} */
public int sessionId;
+ /** {@hide} */
public String installerPackageName;
+ /** {@hide} */
public int progress;
- public boolean fullInstall;
+ /** {@hide} */
+ public int mode;
+ /** {@hide} */
public String packageName;
+ /** {@hide} */
public Bitmap icon;
+ /** {@hide} */
public CharSequence title;
/** {@hide} */
@@ -41,12 +51,62 @@ public class InstallSessionInfo implements Parcelable {
installerPackageName = source.readString();
progress = source.readInt();
- fullInstall = source.readInt() != 0;
+ mode = source.readInt();
packageName = source.readString();
icon = source.readParcelable(null);
title = source.readString();
}
+ /**
+ * Return the ID for this session.
+ */
+ public int getSessionId() {
+ return sessionId;
+ }
+
+ /**
+ * Return the package name of the app that owns this session.
+ */
+ public String getInstallerPackageName() {
+ return installerPackageName;
+ }
+
+ /**
+ * Return current overall progress of this session, between 0 and 100.
+ * <p>
+ * Note that this progress may not directly correspond to the value reported
+ * by {@link PackageInstaller.Session#setProgress(int)}, as the system may
+ * carve out a portion of the overall progress to represent its own internal
+ * installation work.
+ */
+ public int getProgress() {
+ return progress;
+ }
+
+ /**
+ * Return the package name this session is working with. May be {@code null}
+ * if unknown.
+ */
+ public String getPackageName() {
+ return packageName;
+ }
+
+ /**
+ * Return an icon representing the app being installed. May be {@code null}
+ * if unavailable.
+ */
+ public Bitmap getIcon() {
+ return icon;
+ }
+
+ /**
+ * Return a title representing the app being installed. May be {@code null}
+ * if unavailable.
+ */
+ public CharSequence getTitle() {
+ return title;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -58,7 +118,7 @@ public class InstallSessionInfo implements Parcelable {
dest.writeString(installerPackageName);
dest.writeInt(progress);
- dest.writeInt(fullInstall ? 1 : 0);
+ dest.writeInt(mode);
dest.writeString(packageName);
dest.writeParcelable(icon, flags);
dest.writeString(title != null ? title.toString() : null);
diff --git a/core/java/android/content/pm/InstallSessionParams.java b/core/java/android/content/pm/InstallSessionParams.java
index 43e3314..e039699 100644
--- a/core/java/android/content/pm/InstallSessionParams.java
+++ b/core/java/android/content/pm/InstallSessionParams.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Parcel;
@@ -23,13 +24,22 @@ import android.os.Parcelable;
import com.android.internal.util.IndentingPrintWriter;
-/** {@hide} */
+/**
+ * Parameters for creating a new {@link PackageInstaller.Session}.
+ */
public class InstallSessionParams implements Parcelable {
// TODO: extend to support remaining VerificationParams
/** {@hide} */
- public boolean fullInstall;
+ public static final int MODE_INVALID = 0;
+ /** {@hide} */
+ public static final int MODE_FULL_INSTALL = 1;
+ /** {@hide} */
+ public static final int MODE_INHERIT_EXISTING = 2;
+
+ /** {@hide} */
+ public int mode = MODE_INVALID;
/** {@hide} */
public int installFlags;
/** {@hide} */
@@ -58,7 +68,7 @@ public class InstallSessionParams implements Parcelable {
/** {@hide} */
public InstallSessionParams(Parcel source) {
- fullInstall = source.readInt() != 0;
+ mode = source.readInt();
installFlags = source.readInt();
installLocation = source.readInt();
signatures = (Signature[]) source.readParcelableArray(null);
@@ -72,53 +82,108 @@ public class InstallSessionParams implements Parcelable {
abiOverride = source.readString();
}
- public void setFullInstall(boolean fullInstall) {
- this.fullInstall = fullInstall;
+ /**
+ * Set session mode indicating that it should fully replace any existing
+ * APKs for this application.
+ */
+ public void setModeFullInstall() {
+ this.mode = MODE_FULL_INSTALL;
}
- public void setInstallFlags(int installFlags) {
- this.installFlags = installFlags;
+ /**
+ * Set session mode indicating that it should inherit any existing APKs for
+ * this application, unless they are explicitly overridden (by split name)
+ * in the session.
+ */
+ public void setModeInheritExisting() {
+ this.mode = MODE_INHERIT_EXISTING;
}
+ /**
+ * Provide value of {@link PackageInfo#installLocation}, which may be used
+ * to determine where the app will be staged. Defaults to
+ * {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}.
+ */
public void setInstallLocation(int installLocation) {
this.installLocation = installLocation;
}
+ /**
+ * Optionally provide the required value of {@link PackageInfo#signatures}.
+ * This can be used to assert that all staged APKs have been signed with
+ * this set of specific certificates. Regardless of this value, all APKs in
+ * the application must have the same signing certificates.
+ */
public void setSignatures(Signature[] signatures) {
this.signatures = signatures;
}
+ /**
+ * Indicate the expected growth in disk usage resulting from this session.
+ * This may be used to ensure enough disk space exists before proceeding, or
+ * to estimate container size for installations living on external storage.
+ * <p>
+ * This value should only reflect APK sizes.
+ */
public void setDeltaSize(long deltaSize) {
this.deltaSize = deltaSize;
}
+ /**
+ * Set the maximum progress for this session, used for normalization
+ * purposes.
+ *
+ * @see PackageInstaller.Session#setProgress(int)
+ */
public void setProgressMax(int progressMax) {
this.progressMax = progressMax;
}
+ /**
+ * Optionally set the package name this session will be working with. It's
+ * strongly recommended that you provide this value when known.
+ */
public void setPackageName(String packageName) {
this.packageName = packageName;
}
+ /**
+ * Optionally set an icon representing the app being installed.
+ */
public void setIcon(Bitmap icon) {
this.icon = icon;
}
+ /**
+ * Optionally set a title representing the app being installed.
+ */
public void setTitle(CharSequence title) {
this.title = title;
}
+ /**
+ * Optionally set the URI where this package was downloaded from. Used for
+ * verification purposes.
+ *
+ * @see Intent#EXTRA_ORIGINATING_URI
+ */
public void setOriginatingUri(Uri originatingUri) {
this.originatingUri = originatingUri;
}
+ /**
+ * Optionally set the URI that referred you to install this package. Used
+ * for verification purposes.
+ *
+ * @see Intent#EXTRA_REFERRER
+ */
public void setReferrerUri(Uri referrerUri) {
this.referrerUri = referrerUri;
}
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
- pw.printPair("fullInstall", fullInstall);
+ pw.printPair("mode", mode);
pw.printHexPair("installFlags", installFlags);
pw.printPair("installLocation", installLocation);
pw.printPair("signatures", (signatures != null));
@@ -140,7 +205,7 @@ public class InstallSessionParams implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(fullInstall ? 1 : 0);
+ dest.writeInt(mode);
dest.writeInt(installFlags);
dest.writeInt(installLocation);
dest.writeParcelableArray(signatures, flags);
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index ef0c4d5..8f0c249 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -31,6 +31,11 @@ public class PackageInfo implements Parcelable {
public String packageName;
/**
+ * The names of any installed split APKs for this package.
+ */
+ public String[] splitNames;
+
+ /**
* The version number of this package, as specified by the &lt;manifest&gt;
* tag's {@link android.R.styleable#AndroidManifest_versionCode versionCode}
* attribute.
@@ -190,24 +195,25 @@ public class PackageInfo implements Parcelable {
* @hide
*/
public static final int INSTALL_LOCATION_UNSPECIFIED = -1;
+
/**
- * Constant corresponding to <code>auto</code> in
- * the {@link android.R.attr#installLocation} attribute.
- * @hide
+ * Constant corresponding to <code>auto</code> in the
+ * {@link android.R.attr#installLocation} attribute.
*/
public static final int INSTALL_LOCATION_AUTO = 0;
+
/**
- * Constant corresponding to <code>internalOnly</code> in
- * the {@link android.R.attr#installLocation} attribute.
- * @hide
+ * Constant corresponding to <code>internalOnly</code> in the
+ * {@link android.R.attr#installLocation} attribute.
*/
public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1;
+
/**
- * Constant corresponding to <code>preferExternal</code> in
- * the {@link android.R.attr#installLocation} attribute.
- * @hide
+ * Constant corresponding to <code>preferExternal</code> in the
+ * {@link android.R.attr#installLocation} attribute.
*/
public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2;
+
/**
* Flag for {@link #requiredForProfile}
* The application will always be installed for a restricted profile.
@@ -222,12 +228,10 @@ public class PackageInfo implements Parcelable {
public static final int MANAGED_PROFILE = 2;
/**
- * The install location requested by the activity. From the
+ * The install location requested by the package. From the
* {@link android.R.attr#installLocation} attribute, one of
- * {@link #INSTALL_LOCATION_AUTO},
- * {@link #INSTALL_LOCATION_INTERNAL_ONLY},
+ * {@link #INSTALL_LOCATION_AUTO}, {@link #INSTALL_LOCATION_INTERNAL_ONLY},
* {@link #INSTALL_LOCATION_PREFER_EXTERNAL}
- * @hide
*/
public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY;
@@ -269,6 +273,7 @@ public class PackageInfo implements Parcelable {
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(packageName);
+ dest.writeStringArray(splitNames);
dest.writeInt(versionCode);
dest.writeString(versionName);
dest.writeString(sharedUserId);
@@ -314,6 +319,7 @@ public class PackageInfo implements Parcelable {
private PackageInfo(Parcel source) {
packageName = source.readString();
+ splitNames = source.readStringArray();
versionCode = source.readInt();
versionName = source.readString();
sharedUserId = source.readString();
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 401be06..348a7ad 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -30,7 +30,31 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
-/** {@hide} */
+/**
+ * Offers the ability to install, upgrade, and remove applications on the
+ * device. This includes support for apps packaged either as a single
+ * "monolithic" APK, or apps packaged as multiple "split" APKs.
+ * <p>
+ * An app is delivered for installation through a
+ * {@link PackageInstaller.Session}, which any app can create. Once the session
+ * is created, the installer can stream one or more APKs into place until it
+ * decides to either commit or destroy the session. Committing may require user
+ * intervention to complete the installation.
+ * <p>
+ * Sessions can install brand new apps, upgrade existing apps, or add new splits
+ * onto an existing app.
+ * <p>
+ * Apps packaged into multiple split APKs always consist of a single "base" APK
+ * (with a {@code null} split name) and zero or more "split" APKs (with unique
+ * split names). Any subset of these APKs can be installed together, as long as
+ * the following constraints are met:
+ * <ul>
+ * <li>All APKs must have the exact same package name, version code, and signing
+ * certificates.
+ * <li>All installations must contain a single base APK.
+ * <li>All APKs must have unique split names.
+ * </ul>
+ */
public class PackageInstaller {
private final PackageManager mPm;
private final IPackageInstaller mInstaller;
@@ -46,16 +70,18 @@ public class PackageInstaller {
mUserId = userId;
}
+ /**
+ * Quickly test if the given package is already available on the device.
+ * This is typically used in multi-user scenarios where another user on the
+ * device has already installed the package.
+ *
+ * @hide
+ */
public boolean isPackageAvailable(String packageName) {
- try {
- final ApplicationInfo info = mPm.getApplicationInfo(packageName,
- PackageManager.GET_UNINSTALLED_PACKAGES);
- return ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0);
- } catch (NameNotFoundException e) {
- return false;
- }
+ return mPm.isPackageAvailable(packageName);
}
+ /** {@hide} */
public void installAvailablePackage(String packageName, PackageInstallObserver observer) {
int returnCode;
try {
@@ -66,6 +92,18 @@ public class PackageInstaller {
observer.packageInstalled(packageName, null, returnCode);
}
+ /**
+ * Create a new session using the given parameters, returning a unique ID
+ * that represents the session. Once created, the session can be opened
+ * multiple times across multiple device boots.
+ * <p>
+ * The system may automatically destroy sessions that have not been
+ * finalized (either committed or abandoned) within a reasonable period of
+ * time, typically on the order of a day.
+ *
+ * @throws IOException if parameters were unsatisfiable, such as lack of
+ * disk space or unavailable media.
+ */
public int createSession(InstallSessionParams params) throws IOException {
try {
return mInstaller.createSession(mInstallerPackageName, params, mUserId);
@@ -77,6 +115,9 @@ public class PackageInstaller {
}
}
+ /**
+ * Open an existing session to actively perform work.
+ */
public Session openSession(int sessionId) {
try {
return new Session(mInstaller.openSession(sessionId));
@@ -85,7 +126,10 @@ public class PackageInstaller {
}
}
- public List<InstallSessionInfo> getSessions() {
+ /**
+ * Return list of all active install sessions on the device.
+ */
+ public List<InstallSessionInfo> getActiveSessions() {
// TODO: filter based on caller
// TODO: let launcher app see all active sessions
try {
@@ -95,6 +139,11 @@ public class PackageInstaller {
}
}
+ /**
+ * Uninstall the given package, removing it completely from the device. This
+ * method is only available to the current "installer of record" for the
+ * package.
+ */
public void uninstall(String packageName, UninstallResultCallback callback) {
try {
mInstaller.uninstall(packageName, 0,
@@ -104,6 +153,11 @@ public class PackageInstaller {
}
}
+ /**
+ * Uninstall only a specific split from the given package.
+ *
+ * @hide
+ */
public void uninstall(String packageName, String splitName, UninstallResultCallback callback) {
try {
mInstaller.uninstallSplit(packageName, splitName, 0,
@@ -113,6 +167,9 @@ public class PackageInstaller {
}
}
+ /**
+ * Events for observing session lifecycle.
+ */
public static abstract class SessionObserver {
private final IPackageInstallerObserver.Stub mBinder = new IPackageInstallerObserver.Stub() {
@Override
@@ -127,7 +184,7 @@ public class PackageInstaller {
@Override
public void onSessionFinished(int sessionId, boolean success) {
- SessionObserver.this.onFinished(sessionId, success);
+ SessionObserver.this.onFinalized(sessionId, success);
}
};
@@ -136,11 +193,30 @@ public class PackageInstaller {
return mBinder;
}
+ /**
+ * New session has been created.
+ */
public abstract void onCreated(InstallSessionInfo info);
+
+ /**
+ * Progress for given session has been updated.
+ * <p>
+ * Note that this progress may not directly correspond to the value
+ * reported by {@link PackageInstaller.Session#setProgress(int)}, as the
+ * system may carve out a portion of the overall progress to represent
+ * its own internal installation work.
+ */
public abstract void onProgress(int sessionId, int progress);
- public abstract void onFinished(int sessionId, boolean success);
+
+ /**
+ * Session has been finalized, either with success or failure.
+ */
+ public abstract void onFinalized(int sessionId, boolean success);
}
+ /**
+ * Register to watch for session lifecycle events.
+ */
public void registerSessionObserver(SessionObserver observer) {
try {
mInstaller.registerObserver(observer.getBinder(), mUserId);
@@ -149,6 +225,9 @@ public class PackageInstaller {
}
}
+ /**
+ * Unregister an existing observer.
+ */
public void unregisterSessionObserver(SessionObserver observer) {
try {
mInstaller.unregisterObserver(observer.getBinder(), mUserId);
@@ -177,6 +256,10 @@ public class PackageInstaller {
mSession = session;
}
+ /**
+ * Set current progress. Valid values are anywhere between 0 and
+ * {@link InstallSessionParams#setProgressMax(int)}.
+ */
public void setProgress(int progress) {
try {
mSession.setClientProgress(progress);
@@ -197,7 +280,7 @@ public class PackageInstaller {
/**
* Open an APK file for writing, starting at the given offset. You can
* then stream data into the file, periodically calling
- * {@link OutputStream#flush()} to ensure bytes have been written to
+ * {@link #fsync(OutputStream)} to ensure bytes have been written to
* disk.
*/
public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes)
@@ -214,6 +297,11 @@ public class PackageInstaller {
}
}
+ /**
+ * Ensure that any outstanding data for given stream has been committed
+ * to disk. This is only valid for streams returned from
+ * {@link #openWrite(String, long, long)}.
+ */
public void fsync(OutputStream out) throws IOException {
if (out instanceof FileBridge.FileBridgeOutputStream) {
((FileBridge.FileBridgeOutputStream) out).fsync();
@@ -222,6 +310,15 @@ public class PackageInstaller {
}
}
+ /**
+ * Attempt to commit everything staged in this session. This may require
+ * user intervention, and so it may not happen immediately. The final
+ * result of the commit will be reported through the given callback.
+ * <p>
+ * Once this method is called, no additional mutations may be performed
+ * on the session. If the device reboots before the session has been
+ * finalized, you may commit the session again.
+ */
public void commit(CommitResultCallback callback) {
try {
mSession.install(new CommitResultCallbackDelegate(callback).getBinder());
@@ -230,11 +327,18 @@ public class PackageInstaller {
}
}
+ /**
+ * Release this session object. You can open the session again if it
+ * hasn't been finalized.
+ */
@Override
public void close() {
// No resources to release at the moment
}
+ /**
+ * Completely destroy this session, rendering it invalid.
+ */
public void destroy() {
try {
mSession.destroy();
@@ -244,11 +348,15 @@ public class PackageInstaller {
}
}
+ /**
+ * Final result of an uninstall request.
+ */
public static abstract class UninstallResultCallback {
public abstract void onSuccess();
public abstract void onFailure(String msg);
}
+ /** {@hide} */
private static class UninstallResultCallbackDelegate extends PackageUninstallObserver {
private final UninstallResultCallback target;
@@ -271,8 +379,18 @@ public class PackageInstaller {
}
}
+ /**
+ * Final result of a session commit request.
+ */
public static abstract class CommitResultCallback {
public abstract void onSuccess();
+
+ /**
+ * Generic failure occurred. You can override methods (such as
+ * {@link #onFailureInvalid(String)}) to handle more specific categories
+ * of failure. By default, those specific categories all flow into this
+ * generic failure.
+ */
public abstract void onFailure(String msg);
/**
@@ -286,12 +404,16 @@ public class PackageInstaller {
}
/**
- * This install session conflicts (or is inconsistent with) with
- * another package already installed on the device. For example, an
- * existing permission, incompatible certificates, etc. The user may
- * be able to uninstall another app to fix the issue.
+ * This install session conflicts (or is inconsistent with) with another
+ * package already installed on the device. For example, an existing
+ * permission, incompatible certificates, etc. The user may be able to
+ * uninstall another app to fix the issue.
+ *
+ * @param otherPackageName if one specific package was identified as the
+ * cause of the conflict, it's named here. If unknown, or
+ * multiple packages, this may be {@code null}.
*/
- public void onFailureConflict(String msg, String packageName) {
+ public void onFailureConflict(String msg, String otherPackageName) {
onFailure(msg);
}
@@ -317,6 +439,7 @@ public class PackageInstaller {
}
}
+ /** {@hide} */
private static class CommitResultCallbackDelegate extends PackageInstallObserver {
private final CommitResultCallback target;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 03d4701..04ca4a3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3629,8 +3629,11 @@ public abstract class PackageManager {
*/
public abstract VerifierDeviceIdentity getVerifierDeviceIdentity();
- /** {@hide} */
- public abstract PackageInstaller getPackageInstaller();
+ /**
+ * Return interface that offers the ability to install, upgrade, and remove
+ * applications on the device.
+ */
+ public abstract PackageInstaller getInstaller();
/**
* Returns the data directory for a particular user and package, given the uid of the package.
@@ -3676,4 +3679,7 @@ public abstract class PackageManager {
* @hide
*/
public abstract Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo);
+
+ /** {@hide} */
+ public abstract boolean isPackageAvailable(String packageName);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index d92a90d..db2da42 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -380,6 +380,7 @@ public class PackageParser {
}
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
+ pi.splitNames = p.splitNames;
pi.versionCode = p.mVersionCode;
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;