PackageInstaller changes based on feedback.
Mostly cosmetic changes from API council feedback. Bug: 16543552 Change-Id: Ic926829b3f77c31f50a899c59b779353daf00d59
This commit is contained in:
@@ -131,7 +131,7 @@ LOCAL_SRC_FILES += \
|
||||
core/java/android/content/pm/IPackageInstallObserver.aidl \
|
||||
core/java/android/content/pm/IPackageInstallObserver2.aidl \
|
||||
core/java/android/content/pm/IPackageInstaller.aidl \
|
||||
core/java/android/content/pm/IPackageInstallerObserver.aidl \
|
||||
core/java/android/content/pm/IPackageInstallerCallback.aidl \
|
||||
core/java/android/content/pm/IPackageInstallerSession.aidl \
|
||||
core/java/android/content/pm/IPackageManager.aidl \
|
||||
core/java/android/content/pm/IPackageMoveObserver.aidl \
|
||||
|
||||
@@ -8454,32 +8454,31 @@ package android.content.pm {
|
||||
|
||||
public class InstallSessionInfo implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public android.graphics.Bitmap getIcon();
|
||||
method public android.graphics.Bitmap getAppIcon();
|
||||
method public java.lang.CharSequence getAppLabel();
|
||||
method public java.lang.String getAppPackageName();
|
||||
method public java.lang.String getInstallerPackageName();
|
||||
method public java.lang.String getPackageName();
|
||||
method public int getProgress();
|
||||
method public float getProgress();
|
||||
method public int getSessionId();
|
||||
method public java.lang.CharSequence getTitle();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
}
|
||||
|
||||
public class InstallSessionParams implements android.os.Parcelable {
|
||||
ctor public InstallSessionParams();
|
||||
ctor public InstallSessionParams(int);
|
||||
method public int describeContents();
|
||||
method public void setDeltaSize(long);
|
||||
method public void setIcon(android.graphics.Bitmap);
|
||||
method public void setAppIcon(android.graphics.Bitmap);
|
||||
method public void setAppLabel(java.lang.CharSequence);
|
||||
method public void setAppPackageName(java.lang.String);
|
||||
method public void setInstallLocation(int);
|
||||
method public void setModeFullInstall();
|
||||
method public void setModeInheritExisting();
|
||||
method public void setOriginatingUri(android.net.Uri);
|
||||
method public void setPackageName(java.lang.String);
|
||||
method public void setProgressMax(int);
|
||||
method public void setReferrerUri(android.net.Uri);
|
||||
method public void setSignatures(android.content.pm.Signature[]);
|
||||
method public void setTitle(java.lang.CharSequence);
|
||||
method public void setSize(long);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
field public static final int MODE_FULL_INSTALL = 1; // 0x1
|
||||
field public static final int MODE_INHERIT_EXISTING = 2; // 0x2
|
||||
}
|
||||
|
||||
public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
|
||||
@@ -8580,42 +8579,47 @@ package android.content.pm {
|
||||
}
|
||||
|
||||
public class PackageInstaller {
|
||||
method public void addSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
|
||||
method public void addSessionCallback(android.content.pm.PackageInstaller.SessionCallback, android.os.Handler);
|
||||
method public int createSession(android.content.pm.InstallSessionParams) throws java.io.IOException;
|
||||
method public java.util.List<android.content.pm.InstallSessionInfo> getActiveSessions();
|
||||
method public java.util.List<android.content.pm.InstallSessionInfo> getAllSessions();
|
||||
method public java.util.List<android.content.pm.InstallSessionInfo> getMySessions();
|
||||
method public android.content.pm.InstallSessionInfo getSessionInfo(int);
|
||||
method public android.content.pm.PackageInstaller.Session openSession(int);
|
||||
method public void registerSessionObserver(android.content.pm.PackageInstaller.SessionObserver);
|
||||
method public void uninstall(java.lang.String, android.content.pm.PackageInstaller.UninstallResultCallback);
|
||||
method public void unregisterSessionObserver(android.content.pm.PackageInstaller.SessionObserver);
|
||||
method public void removeSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
|
||||
method public void uninstall(java.lang.String, android.content.pm.PackageInstaller.UninstallCallback);
|
||||
}
|
||||
|
||||
public static abstract class PackageInstaller.CommitResultCallback {
|
||||
ctor public PackageInstaller.CommitResultCallback();
|
||||
method public abstract void onFailure(java.lang.String);
|
||||
method public void onFailureConflict(java.lang.String, java.lang.String);
|
||||
method public void onFailureIncompatible(java.lang.String);
|
||||
method public void onFailureInvalid(java.lang.String);
|
||||
method public void onFailureStorage(java.lang.String);
|
||||
public static abstract class PackageInstaller.CommitCallback {
|
||||
ctor public PackageInstaller.CommitCallback();
|
||||
method public abstract void onFailure(int, java.lang.String, android.os.Bundle);
|
||||
method public abstract void onSuccess();
|
||||
field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
|
||||
field public static final int FAILURE_CONFLICT = 2; // 0x2
|
||||
field public static final int FAILURE_INCOMPATIBLE = 4; // 0x4
|
||||
field public static final int FAILURE_INVALID = 1; // 0x1
|
||||
field public static final int FAILURE_STORAGE = 3; // 0x3
|
||||
field public static final int FAILURE_UNKNOWN = 0; // 0x0
|
||||
}
|
||||
|
||||
public static class PackageInstaller.Session implements java.io.Closeable {
|
||||
method public void abandon();
|
||||
method public void close();
|
||||
method public void commit(android.content.pm.PackageInstaller.CommitResultCallback);
|
||||
method public void destroy();
|
||||
method public void commit(android.content.pm.PackageInstaller.CommitCallback);
|
||||
method public void fsync(java.io.OutputStream) throws java.io.IOException;
|
||||
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
|
||||
method public void setProgress(int);
|
||||
method public void setProgress(float);
|
||||
}
|
||||
|
||||
public static abstract class PackageInstaller.SessionObserver {
|
||||
ctor public PackageInstaller.SessionObserver();
|
||||
method public abstract void onCreated(android.content.pm.InstallSessionInfo);
|
||||
method public abstract void onFinalized(int, boolean);
|
||||
method public abstract void onProgress(int, int);
|
||||
public static abstract class PackageInstaller.SessionCallback {
|
||||
ctor public PackageInstaller.SessionCallback();
|
||||
method public abstract void onCreated(int);
|
||||
method public abstract void onFinished(int, boolean);
|
||||
method public abstract void onProgressChanged(int, float);
|
||||
}
|
||||
|
||||
public static abstract class PackageInstaller.UninstallResultCallback {
|
||||
ctor public PackageInstaller.UninstallResultCallback();
|
||||
public static abstract class PackageInstaller.UninstallCallback {
|
||||
ctor public PackageInstaller.UninstallCallback();
|
||||
method public abstract void onFailure(java.lang.String);
|
||||
method public abstract void onSuccess();
|
||||
}
|
||||
@@ -8682,7 +8686,6 @@ package android.content.pm {
|
||||
method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
|
||||
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
|
||||
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
|
||||
method public abstract android.content.pm.PackageInstaller getInstaller();
|
||||
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
|
||||
method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
|
||||
method public abstract android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String);
|
||||
@@ -8692,6 +8695,7 @@ package android.content.pm {
|
||||
method public android.content.pm.PackageInfo getPackageArchiveInfo(java.lang.String, int);
|
||||
method public abstract int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
|
||||
method public abstract android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
|
||||
method public abstract android.content.pm.PackageInstaller getPackageInstaller();
|
||||
method public abstract java.lang.String[] getPackagesForUid(int);
|
||||
method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
|
||||
method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
|
||||
@@ -30130,7 +30134,6 @@ package android.test.mock {
|
||||
method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
|
||||
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
|
||||
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
|
||||
method public android.content.pm.PackageInstaller getInstaller();
|
||||
method public java.lang.String getInstallerPackageName(java.lang.String);
|
||||
method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
|
||||
method public android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String);
|
||||
@@ -30139,6 +30142,7 @@ package android.test.mock {
|
||||
method public java.lang.String getNameForUid(int);
|
||||
method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
|
||||
method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
|
||||
method public android.content.pm.PackageInstaller getPackageInstaller();
|
||||
method public java.lang.String[] getPackagesForUid(int);
|
||||
method public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
|
||||
method public android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
|
||||
|
||||
@@ -27,11 +27,12 @@ import android.content.pm.IPackageDataObserver;
|
||||
import android.content.pm.IPackageDeleteObserver;
|
||||
import android.content.pm.IPackageInstaller;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.InstallSessionInfo;
|
||||
import android.content.pm.InstallSessionParams;
|
||||
import android.content.pm.InstrumentationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageInstaller.CommitResultCallback;
|
||||
import android.content.pm.PackageInstaller.CommitCallback;
|
||||
import android.content.pm.PackageItemInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
@@ -157,8 +158,8 @@ public final class Pm {
|
||||
return;
|
||||
}
|
||||
|
||||
if ("install-destroy".equals(op)) {
|
||||
runInstallDestroy();
|
||||
if ("install-abandon".equals(op) || "install-destroy".equals(op)) {
|
||||
runInstallAbandon();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -770,7 +771,7 @@ public final class Pm {
|
||||
}
|
||||
}
|
||||
|
||||
class LocalCommitResultCallback extends CommitResultCallback {
|
||||
class LocalCommitCallback extends CommitCallback {
|
||||
boolean finished;
|
||||
boolean success;
|
||||
String msg;
|
||||
@@ -790,7 +791,7 @@ public final class Pm {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(String msg) {
|
||||
public void onFailure(int failureReason, String msg, Bundle extras) {
|
||||
setResult(false, msg);
|
||||
}
|
||||
}
|
||||
@@ -996,10 +997,9 @@ public final class Pm {
|
||||
private void runInstallCreate() throws RemoteException {
|
||||
String installerPackageName = null;
|
||||
|
||||
final InstallSessionParams params = new InstallSessionParams();
|
||||
final InstallSessionParams params = new InstallSessionParams(
|
||||
InstallSessionParams.MODE_FULL_INSTALL);
|
||||
params.installFlags = PackageManager.INSTALL_ALL_USERS;
|
||||
params.setModeFullInstall();
|
||||
params.setProgressMax(-1);
|
||||
|
||||
String opt;
|
||||
while ((opt = nextOption()) != null) {
|
||||
@@ -1021,11 +1021,9 @@ public final class Pm {
|
||||
} else if (opt.equals("-d")) {
|
||||
params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
|
||||
} else if (opt.equals("-p")) {
|
||||
params.setModeInheritExisting();
|
||||
params.mode = InstallSessionParams.MODE_INHERIT_EXISTING;
|
||||
} else if (opt.equals("-S")) {
|
||||
final long deltaSize = Long.parseLong(nextOptionData());
|
||||
params.setDeltaSize(deltaSize);
|
||||
params.setProgressMax((int) params.deltaSize);
|
||||
params.setSize(Long.parseLong(nextOptionData()));
|
||||
} else if (opt.equals("--abi")) {
|
||||
params.abiOverride = checkAbiArgument(nextOptionData());
|
||||
} else {
|
||||
@@ -1033,7 +1031,7 @@ public final class Pm {
|
||||
}
|
||||
}
|
||||
|
||||
final int sessionId = mInstaller.createSession(installerPackageName, params,
|
||||
final int sessionId = mInstaller.createSession(params, installerPackageName,
|
||||
UserHandle.USER_OWNER);
|
||||
|
||||
// NOTE: adb depends on parsing this string
|
||||
@@ -1080,7 +1078,12 @@ public final class Pm {
|
||||
|
||||
final int n = Streams.copy(in, out);
|
||||
session.fsync(out);
|
||||
session.addProgress(n);
|
||||
|
||||
final InstallSessionInfo info = mInstaller.getSessionInfo(sessionId);
|
||||
if (info.sizeBytes > 0) {
|
||||
final float fraction = ((float) n / (float) info.sizeBytes);
|
||||
session.addProgress(fraction);
|
||||
}
|
||||
|
||||
System.out.println("Success: streamed " + n + " bytes");
|
||||
} finally {
|
||||
@@ -1097,7 +1100,7 @@ public final class Pm {
|
||||
try {
|
||||
session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
|
||||
|
||||
final LocalCommitResultCallback callback = new LocalCommitResultCallback();
|
||||
final LocalCommitCallback callback = new LocalCommitCallback();
|
||||
session.commit(callback);
|
||||
|
||||
synchronized (callback) {
|
||||
@@ -1118,13 +1121,13 @@ public final class Pm {
|
||||
}
|
||||
}
|
||||
|
||||
private void runInstallDestroy() throws RemoteException {
|
||||
private void runInstallAbandon() throws RemoteException {
|
||||
final int sessionId = Integer.parseInt(nextArg());
|
||||
|
||||
PackageInstaller.Session session = null;
|
||||
try {
|
||||
session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
|
||||
session.destroy();
|
||||
session.abandon();
|
||||
System.out.println("Success");
|
||||
} finally {
|
||||
IoUtils.closeQuietly(session);
|
||||
@@ -1743,7 +1746,7 @@ public final class Pm {
|
||||
System.err.println(" pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]");
|
||||
System.err.println(" pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH]");
|
||||
System.err.println(" pm install-commit SESSION_ID");
|
||||
System.err.println(" pm install-destroy SESSION_ID");
|
||||
System.err.println(" pm install-abandon SESSION_ID");
|
||||
System.err.println(" pm uninstall [-k] [--user USER_ID] PACKAGE");
|
||||
System.err.println(" pm set-installer PACKAGE INSTALLER");
|
||||
System.err.println(" pm clear [--user USER_ID] PACKAGE");
|
||||
@@ -1813,7 +1816,7 @@ public final class Pm {
|
||||
System.err.println(" -S: size in bytes of package, required for stdin");
|
||||
System.err.println("");
|
||||
System.err.println("pm install-commit: perform install of fully staged session");
|
||||
System.err.println("pm install-destroy: destroy session");
|
||||
System.err.println("pm install-abandon: abandon session");
|
||||
System.err.println("");
|
||||
System.err.println("pm set-installer: set installer package name");
|
||||
System.err.println("");
|
||||
|
||||
@@ -61,7 +61,10 @@ import android.os.UserManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import dalvik.system.VMRuntime;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
@@ -74,13 +77,20 @@ final class ApplicationPackageManager extends PackageManager {
|
||||
private final static boolean DEBUG = false;
|
||||
private final static boolean DEBUG_ICONS = false;
|
||||
|
||||
UserManager mUserManager;
|
||||
private final Object mLock = new Object();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private UserManager mUserManager;
|
||||
@GuardedBy("mLock")
|
||||
private PackageInstaller mInstaller;
|
||||
|
||||
UserManager getUserManager() {
|
||||
if (mUserManager == null) {
|
||||
mUserManager = UserManager.get(mContext);
|
||||
synchronized (mLock) {
|
||||
if (mUserManager == null) {
|
||||
mUserManager = UserManager.get(mContext);
|
||||
}
|
||||
return mUserManager;
|
||||
}
|
||||
return mUserManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1543,12 +1553,17 @@ final class ApplicationPackageManager extends PackageManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageInstaller getInstaller() {
|
||||
try {
|
||||
return new PackageInstaller(this, mPM.getPackageInstaller(), mContext.getPackageName(),
|
||||
mContext.getUserId());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
public PackageInstaller getPackageInstaller() {
|
||||
synchronized (mLock) {
|
||||
if (mInstaller == null) {
|
||||
try {
|
||||
mInstaller = new PackageInstaller(this, mPM.getPackageInstaller(),
|
||||
mContext.getPackageName(), mContext.getUserId());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
}
|
||||
return mInstaller;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package android.content.pm;
|
||||
|
||||
import android.content.pm.IPackageDeleteObserver;
|
||||
import android.content.pm.IPackageInstallerObserver;
|
||||
import android.content.pm.IPackageInstallerCallback;
|
||||
import android.content.pm.IPackageInstallerSession;
|
||||
import android.content.pm.InstallSessionInfo;
|
||||
import android.content.pm.InstallSessionParams;
|
||||
@@ -25,13 +25,15 @@ import android.os.ParcelFileDescriptor;
|
||||
|
||||
/** {@hide} */
|
||||
interface IPackageInstaller {
|
||||
int createSession(String installerPackageName, in InstallSessionParams params, int userId);
|
||||
int createSession(in InstallSessionParams params, String installerPackageName, int userId);
|
||||
IPackageInstallerSession openSession(int sessionId);
|
||||
|
||||
List<InstallSessionInfo> getSessions(int userId);
|
||||
InstallSessionInfo getSessionInfo(int sessionId);
|
||||
List<InstallSessionInfo> getAllSessions(int userId);
|
||||
List<InstallSessionInfo> getMySessions(String installerPackageName, int userId);
|
||||
|
||||
void registerObserver(IPackageInstallerObserver observer, int userId);
|
||||
void unregisterObserver(IPackageInstallerObserver observer, int userId);
|
||||
void registerCallback(IPackageInstallerCallback callback, int userId);
|
||||
void unregisterCallback(IPackageInstallerCallback callback);
|
||||
|
||||
void uninstall(String packageName, int flags, in IPackageDeleteObserver observer, int userId);
|
||||
void uninstallSplit(String packageName, String splitName, int flags, in IPackageDeleteObserver observer, int userId);
|
||||
|
||||
@@ -16,11 +16,9 @@
|
||||
|
||||
package android.content.pm;
|
||||
|
||||
import android.content.pm.InstallSessionInfo;
|
||||
|
||||
/** {@hide} */
|
||||
oneway interface IPackageInstallerObserver {
|
||||
void onSessionCreated(in InstallSessionInfo info);
|
||||
void onSessionProgress(int sessionId, int progress);
|
||||
oneway interface IPackageInstallerCallback {
|
||||
void onSessionCreated(int sessionId);
|
||||
void onSessionProgressChanged(int sessionId, float progress);
|
||||
void onSessionFinished(int sessionId, boolean success);
|
||||
}
|
||||
@@ -21,11 +21,12 @@ import android.os.ParcelFileDescriptor;
|
||||
|
||||
/** {@hide} */
|
||||
interface IPackageInstallerSession {
|
||||
void setClientProgress(int progress);
|
||||
void addClientProgress(int progress);
|
||||
void setClientProgress(float progress);
|
||||
void addClientProgress(float progress);
|
||||
|
||||
ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes);
|
||||
|
||||
void install(in IPackageInstallObserver2 observer);
|
||||
void destroy();
|
||||
void close();
|
||||
void commit(in IPackageInstallObserver2 observer);
|
||||
void abandon();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.content.pm;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
@@ -30,16 +31,18 @@ public class InstallSessionInfo implements Parcelable {
|
||||
/** {@hide} */
|
||||
public String installerPackageName;
|
||||
/** {@hide} */
|
||||
public int progress;
|
||||
public float progress;
|
||||
|
||||
/** {@hide} */
|
||||
public int mode;
|
||||
/** {@hide} */
|
||||
public String packageName;
|
||||
public long sizeBytes;
|
||||
/** {@hide} */
|
||||
public Bitmap icon;
|
||||
public String appPackageName;
|
||||
/** {@hide} */
|
||||
public CharSequence title;
|
||||
public Bitmap appIcon;
|
||||
/** {@hide} */
|
||||
public CharSequence appLabel;
|
||||
|
||||
/** {@hide} */
|
||||
public InstallSessionInfo() {
|
||||
@@ -49,12 +52,13 @@ public class InstallSessionInfo implements Parcelable {
|
||||
public InstallSessionInfo(Parcel source) {
|
||||
sessionId = source.readInt();
|
||||
installerPackageName = source.readString();
|
||||
progress = source.readInt();
|
||||
progress = source.readFloat();
|
||||
|
||||
mode = source.readInt();
|
||||
packageName = source.readString();
|
||||
icon = source.readParcelable(null);
|
||||
title = source.readString();
|
||||
sizeBytes = source.readLong();
|
||||
appPackageName = source.readString();
|
||||
appIcon = source.readParcelable(null);
|
||||
appLabel = source.readString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,19 +71,19 @@ public class InstallSessionInfo implements Parcelable {
|
||||
/**
|
||||
* Return the package name of the app that owns this session.
|
||||
*/
|
||||
public String getInstallerPackageName() {
|
||||
public @Nullable String getInstallerPackageName() {
|
||||
return installerPackageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return current overall progress of this session, between 0 and 100.
|
||||
* Return current overall progress of this session, between 0 and 1.
|
||||
* <p>
|
||||
* Note that this progress may not directly correspond to the value reported
|
||||
* by {@link PackageInstaller.Session#setProgress(int)}, as the system may
|
||||
* by {@link PackageInstaller.Session#setProgress(float)}, as the system may
|
||||
* carve out a portion of the overall progress to represent its own internal
|
||||
* installation work.
|
||||
*/
|
||||
public int getProgress() {
|
||||
public float getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
@@ -87,24 +91,24 @@ public class InstallSessionInfo implements Parcelable {
|
||||
* Return the package name this session is working with. May be {@code null}
|
||||
* if unknown.
|
||||
*/
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
public @Nullable String getAppPackageName() {
|
||||
return appPackageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an icon representing the app being installed. May be {@code null}
|
||||
* if unavailable.
|
||||
*/
|
||||
public Bitmap getIcon() {
|
||||
return icon;
|
||||
public @Nullable Bitmap getAppIcon() {
|
||||
return appIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a title representing the app being installed. May be {@code null}
|
||||
* Return a label representing the app being installed. May be {@code null}
|
||||
* if unavailable.
|
||||
*/
|
||||
public CharSequence getTitle() {
|
||||
return title;
|
||||
public @Nullable CharSequence getAppLabel() {
|
||||
return appLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,12 +120,13 @@ public class InstallSessionInfo implements Parcelable {
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(sessionId);
|
||||
dest.writeString(installerPackageName);
|
||||
dest.writeInt(progress);
|
||||
dest.writeFloat(progress);
|
||||
|
||||
dest.writeInt(mode);
|
||||
dest.writeString(packageName);
|
||||
dest.writeParcelable(icon, flags);
|
||||
dest.writeString(title != null ? title.toString() : null);
|
||||
dest.writeLong(sizeBytes);
|
||||
dest.writeString(appPackageName);
|
||||
dest.writeParcelable(appIcon, flags);
|
||||
dest.writeString(appLabel != null ? appLabel.toString() : null);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<InstallSessionInfo>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.content.pm;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
@@ -29,17 +30,25 @@ import com.android.internal.util.IndentingPrintWriter;
|
||||
*/
|
||||
public class InstallSessionParams implements Parcelable {
|
||||
|
||||
// TODO: extend to support remaining VerificationParams
|
||||
|
||||
/** {@hide} */
|
||||
public static final int MODE_INVALID = 0;
|
||||
/** {@hide} */
|
||||
/**
|
||||
* Mode for an install session whose staged APKs should fully replace any
|
||||
* existing APKs for the target app.
|
||||
*/
|
||||
public static final int MODE_FULL_INSTALL = 1;
|
||||
/** {@hide} */
|
||||
|
||||
/**
|
||||
* Mode for an install session that should inherit any existing APKs for the
|
||||
* target app, unless they have been explicitly overridden (based on split
|
||||
* name) by the session. For example, this can be used to add one or more
|
||||
* split APKs to an existing installation.
|
||||
* <p>
|
||||
* If there are no existing APKs for the target app, this behaves like
|
||||
* {@link #MODE_FULL_INSTALL}.
|
||||
*/
|
||||
public static final int MODE_INHERIT_EXISTING = 2;
|
||||
|
||||
/** {@hide} */
|
||||
public int mode = MODE_INVALID;
|
||||
public int mode;
|
||||
/** {@hide} */
|
||||
public int installFlags;
|
||||
/** {@hide} */
|
||||
@@ -47,15 +56,13 @@ public class InstallSessionParams implements Parcelable {
|
||||
/** {@hide} */
|
||||
public Signature[] signatures;
|
||||
/** {@hide} */
|
||||
public long deltaSize = -1;
|
||||
public long sizeBytes = -1;
|
||||
/** {@hide} */
|
||||
public int progressMax = 100;
|
||||
public String appPackageName;
|
||||
/** {@hide} */
|
||||
public String packageName;
|
||||
public Bitmap appIcon;
|
||||
/** {@hide} */
|
||||
public Bitmap icon;
|
||||
/** {@hide} */
|
||||
public CharSequence title;
|
||||
public CharSequence appLabel;
|
||||
/** {@hide} */
|
||||
public Uri originatingUri;
|
||||
/** {@hide} */
|
||||
@@ -63,7 +70,15 @@ public class InstallSessionParams implements Parcelable {
|
||||
/** {@hide} */
|
||||
public String abiOverride;
|
||||
|
||||
public InstallSessionParams() {
|
||||
/**
|
||||
* Construct parameters for a new package install session.
|
||||
*
|
||||
* @param mode one of {@link #MODE_FULL_INSTALL} or
|
||||
* {@link #MODE_INHERIT_EXISTING} describing how the session
|
||||
* should interact with an existing app.
|
||||
*/
|
||||
public InstallSessionParams(int mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
@@ -72,33 +87,15 @@ public class InstallSessionParams implements Parcelable {
|
||||
installFlags = source.readInt();
|
||||
installLocation = source.readInt();
|
||||
signatures = (Signature[]) source.readParcelableArray(null);
|
||||
deltaSize = source.readLong();
|
||||
progressMax = source.readInt();
|
||||
packageName = source.readString();
|
||||
icon = source.readParcelable(null);
|
||||
title = source.readString();
|
||||
sizeBytes = source.readLong();
|
||||
appPackageName = source.readString();
|
||||
appIcon = source.readParcelable(null);
|
||||
appLabel = source.readString();
|
||||
originatingUri = source.readParcelable(null);
|
||||
referrerUri = source.readParcelable(null);
|
||||
abiOverride = source.readString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set session mode indicating that it should fully replace any existing
|
||||
* APKs for this application.
|
||||
*/
|
||||
public void setModeFullInstall() {
|
||||
this.mode = MODE_FULL_INSTALL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -109,56 +106,57 @@ public class InstallSessionParams implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Optionally provide a set of certificates for the app being installed.
|
||||
* <p>
|
||||
* If the APKs staged in the session aren't consistent with these
|
||||
* signatures, the install will fail. Regardless of this value, all APKs in
|
||||
* the app must have the same signing certificates.
|
||||
*
|
||||
* @see PackageInfo#signatures
|
||||
*/
|
||||
public void setSignatures(Signature[] signatures) {
|
||||
public void setSignatures(@Nullable 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.
|
||||
* Optionally indicate the total size (in bytes) of all APKs that will be
|
||||
* delivered in this session. The system may use this to ensure enough disk
|
||||
* space exists before proceeding, or to estimate container size for
|
||||
* installations living on external storage.
|
||||
*
|
||||
* @see PackageInstaller.Session#setProgress(int)
|
||||
* @see PackageInfo#INSTALL_LOCATION_AUTO
|
||||
* @see PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL
|
||||
*/
|
||||
public void setProgressMax(int progressMax) {
|
||||
this.progressMax = progressMax;
|
||||
public void setSize(long sizeBytes) {
|
||||
this.sizeBytes = sizeBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally set the package name this session will be working with. It's
|
||||
* strongly recommended that you provide this value when known.
|
||||
* Optionally set the package name of the app being installed. It's strongly
|
||||
* recommended that you provide this value when known, so that observers can
|
||||
* communicate installing apps to users.
|
||||
* <p>
|
||||
* If the APKs staged in the session aren't consistent with this package
|
||||
* name, the install will fail. Regardless of this value, all APKs in the
|
||||
* app must have the same package name.
|
||||
*/
|
||||
public void setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
public void setAppPackageName(@Nullable String appPackageName) {
|
||||
this.appPackageName = appPackageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally set an icon representing the app being installed.
|
||||
* Optionally set an icon representing the app being installed. This should
|
||||
* be at least {@link android.R.dimen#app_icon_size} in both dimensions.
|
||||
*/
|
||||
public void setIcon(Bitmap icon) {
|
||||
this.icon = icon;
|
||||
public void setAppIcon(@Nullable Bitmap appIcon) {
|
||||
this.appIcon = appIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally set a title representing the app being installed.
|
||||
* Optionally set a label representing the app being installed.
|
||||
*/
|
||||
public void setTitle(CharSequence title) {
|
||||
this.title = title;
|
||||
public void setAppLabel(@Nullable CharSequence appLabel) {
|
||||
this.appLabel = appLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,7 +165,7 @@ public class InstallSessionParams implements Parcelable {
|
||||
*
|
||||
* @see Intent#EXTRA_ORIGINATING_URI
|
||||
*/
|
||||
public void setOriginatingUri(Uri originatingUri) {
|
||||
public void setOriginatingUri(@Nullable Uri originatingUri) {
|
||||
this.originatingUri = originatingUri;
|
||||
}
|
||||
|
||||
@@ -177,7 +175,7 @@ public class InstallSessionParams implements Parcelable {
|
||||
*
|
||||
* @see Intent#EXTRA_REFERRER
|
||||
*/
|
||||
public void setReferrerUri(Uri referrerUri) {
|
||||
public void setReferrerUri(@Nullable Uri referrerUri) {
|
||||
this.referrerUri = referrerUri;
|
||||
}
|
||||
|
||||
@@ -187,11 +185,10 @@ public class InstallSessionParams implements Parcelable {
|
||||
pw.printHexPair("installFlags", installFlags);
|
||||
pw.printPair("installLocation", installLocation);
|
||||
pw.printPair("signatures", (signatures != null));
|
||||
pw.printPair("deltaSize", deltaSize);
|
||||
pw.printPair("progressMax", progressMax);
|
||||
pw.printPair("packageName", packageName);
|
||||
pw.printPair("icon", (icon != null));
|
||||
pw.printPair("title", title);
|
||||
pw.printPair("sizeBytes", sizeBytes);
|
||||
pw.printPair("appPackageName", appPackageName);
|
||||
pw.printPair("appIcon", (appIcon != null));
|
||||
pw.printPair("appLabel", appLabel);
|
||||
pw.printPair("originatingUri", originatingUri);
|
||||
pw.printPair("referrerUri", referrerUri);
|
||||
pw.printPair("abiOverride", abiOverride);
|
||||
@@ -209,11 +206,10 @@ public class InstallSessionParams implements Parcelable {
|
||||
dest.writeInt(installFlags);
|
||||
dest.writeInt(installLocation);
|
||||
dest.writeParcelableArray(signatures, flags);
|
||||
dest.writeLong(deltaSize);
|
||||
dest.writeInt(progressMax);
|
||||
dest.writeString(packageName);
|
||||
dest.writeParcelable(icon, flags);
|
||||
dest.writeString(title != null ? title.toString() : null);
|
||||
dest.writeLong(sizeBytes);
|
||||
dest.writeString(appPackageName);
|
||||
dest.writeParcelable(appIcon, flags);
|
||||
dest.writeString(appLabel != null ? appLabel.toString() : null);
|
||||
dest.writeParcelable(originatingUri, flags);
|
||||
dest.writeParcelable(referrerUri, flags);
|
||||
dest.writeString(abiOverride);
|
||||
|
||||
@@ -16,11 +16,16 @@
|
||||
|
||||
package android.content.pm;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.PackageInstallObserver;
|
||||
import android.app.PackageUninstallObserver;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Bundle;
|
||||
import android.os.FileBridge;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ExceptionUtils;
|
||||
@@ -28,6 +33,8 @@ import android.util.ExceptionUtils;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -61,6 +68,8 @@ public class PackageInstaller {
|
||||
private final int mUserId;
|
||||
private final String mInstallerPackageName;
|
||||
|
||||
private final ArrayList<SessionCallbackDelegate> mDelegates = new ArrayList<>();
|
||||
|
||||
/** {@hide} */
|
||||
public PackageInstaller(PackageManager pm, IPackageInstaller installer,
|
||||
String installerPackageName, int userId) {
|
||||
@@ -70,28 +79,6 @@ 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) {
|
||||
return mPm.isPackageAvailable(packageName);
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
public void installAvailablePackage(String packageName, PackageInstallObserver observer) {
|
||||
int returnCode;
|
||||
try {
|
||||
returnCode = mPm.installExistingPackage(packageName);
|
||||
} catch (NameNotFoundException e) {
|
||||
returnCode = PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
|
||||
}
|
||||
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
|
||||
@@ -104,9 +91,9 @@ public class PackageInstaller {
|
||||
* @throws IOException if parameters were unsatisfiable, such as lack of
|
||||
* disk space or unavailable media.
|
||||
*/
|
||||
public int createSession(InstallSessionParams params) throws IOException {
|
||||
public int createSession(@NonNull InstallSessionParams params) throws IOException {
|
||||
try {
|
||||
return mInstaller.createSession(mInstallerPackageName, params, mUserId);
|
||||
return mInstaller.createSession(params, mInstallerPackageName, mUserId);
|
||||
} catch (RuntimeException e) {
|
||||
ExceptionUtils.maybeUnwrapIOException(e);
|
||||
throw e;
|
||||
@@ -116,9 +103,10 @@ public class PackageInstaller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an existing session to actively perform work.
|
||||
* Open an existing session to actively perform work. To succeed, the caller
|
||||
* must be the owner of the install session.
|
||||
*/
|
||||
public Session openSession(int sessionId) {
|
||||
public @NonNull Session openSession(int sessionId) {
|
||||
try {
|
||||
return new Session(mInstaller.openSession(sessionId));
|
||||
} catch (RemoteException e) {
|
||||
@@ -127,13 +115,35 @@ public class PackageInstaller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of all active install sessions on the device.
|
||||
* Return details for a specific session. To succeed, the caller must either
|
||||
* own this session, or be the current home app.
|
||||
*/
|
||||
public List<InstallSessionInfo> getActiveSessions() {
|
||||
// TODO: filter based on caller
|
||||
// TODO: let launcher app see all active sessions
|
||||
public @Nullable InstallSessionInfo getSessionInfo(int sessionId) {
|
||||
try {
|
||||
return mInstaller.getSessions(mUserId);
|
||||
return mInstaller.getSessionInfo(sessionId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of all active install sessions, regardless of the installer.
|
||||
* To succeed, the caller must be the current home app.
|
||||
*/
|
||||
public @NonNull List<InstallSessionInfo> getAllSessions() {
|
||||
try {
|
||||
return mInstaller.getAllSessions(mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of all install sessions owned by the calling app.
|
||||
*/
|
||||
public @NonNull List<InstallSessionInfo> getMySessions() {
|
||||
try {
|
||||
return mInstaller.getMySessions(mInstallerPackageName, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
@@ -144,10 +154,10 @@ public class PackageInstaller {
|
||||
* method is only available to the current "installer of record" for the
|
||||
* package.
|
||||
*/
|
||||
public void uninstall(String packageName, UninstallResultCallback callback) {
|
||||
public void uninstall(@NonNull String packageName, @NonNull UninstallCallback callback) {
|
||||
try {
|
||||
mInstaller.uninstall(packageName, 0,
|
||||
new UninstallResultCallbackDelegate(callback).getBinder(), mUserId);
|
||||
new UninstallCallbackDelegate(callback).getBinder(), mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
@@ -158,10 +168,11 @@ public class PackageInstaller {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void uninstall(String packageName, String splitName, UninstallResultCallback callback) {
|
||||
public void uninstall(@NonNull String packageName, @NonNull String splitName,
|
||||
@NonNull UninstallCallback callback) {
|
||||
try {
|
||||
mInstaller.uninstallSplit(packageName, splitName, 0,
|
||||
new UninstallResultCallbackDelegate(callback).getBinder(), mUserId);
|
||||
new UninstallCallbackDelegate(callback).getBinder(), mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
@@ -170,69 +181,121 @@ public class PackageInstaller {
|
||||
/**
|
||||
* Events for observing session lifecycle.
|
||||
*/
|
||||
public static abstract class SessionObserver {
|
||||
private final IPackageInstallerObserver.Stub mBinder = new IPackageInstallerObserver.Stub() {
|
||||
@Override
|
||||
public void onSessionCreated(InstallSessionInfo info) {
|
||||
SessionObserver.this.onCreated(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionProgress(int sessionId, int progress) {
|
||||
SessionObserver.this.onProgress(sessionId, progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionFinished(int sessionId, boolean success) {
|
||||
SessionObserver.this.onFinalized(sessionId, success);
|
||||
}
|
||||
};
|
||||
|
||||
/** {@hide} */
|
||||
public IPackageInstallerObserver getBinder() {
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
public static abstract class SessionCallback {
|
||||
/**
|
||||
* New session has been created.
|
||||
*/
|
||||
public abstract void onCreated(InstallSessionInfo info);
|
||||
public abstract void onCreated(int sessionId);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* reported by {@link PackageInstaller.Session#setProgress(float)}, 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 onProgressChanged(int sessionId, float progress);
|
||||
|
||||
/**
|
||||
* Session has been finalized, either with success or failure.
|
||||
* Session has completely finished, either with success or failure.
|
||||
*/
|
||||
public abstract void onFinalized(int sessionId, boolean success);
|
||||
public abstract void onFinished(int sessionId, boolean success);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register to watch for session lifecycle events.
|
||||
*/
|
||||
public void registerSessionObserver(SessionObserver observer) {
|
||||
try {
|
||||
mInstaller.registerObserver(observer.getBinder(), mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
/** {@hide} */
|
||||
private static class SessionCallbackDelegate extends IPackageInstallerCallback.Stub implements
|
||||
Handler.Callback {
|
||||
private static final int MSG_SESSION_CREATED = 1;
|
||||
private static final int MSG_SESSION_PROGRESS_CHANGED = 2;
|
||||
private static final int MSG_SESSION_FINISHED = 3;
|
||||
|
||||
final SessionCallback mCallback;
|
||||
final Handler mHandler;
|
||||
|
||||
public SessionCallbackDelegate(SessionCallback callback, Looper looper) {
|
||||
mCallback = callback;
|
||||
mHandler = new Handler(looper, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_SESSION_CREATED:
|
||||
mCallback.onCreated(msg.arg1);
|
||||
return true;
|
||||
case MSG_SESSION_PROGRESS_CHANGED:
|
||||
mCallback.onProgressChanged(msg.arg1, (float) msg.obj);
|
||||
return true;
|
||||
case MSG_SESSION_FINISHED:
|
||||
mCallback.onFinished(msg.arg1, msg.arg2 != 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionCreated(int sessionId) {
|
||||
mHandler.obtainMessage(MSG_SESSION_CREATED, sessionId, 0).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionProgressChanged(int sessionId, float progress) {
|
||||
mHandler.obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, 0, progress)
|
||||
.sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSessionFinished(int sessionId, boolean success) {
|
||||
mHandler.obtainMessage(MSG_SESSION_FINISHED, sessionId, success ? 1 : 0)
|
||||
.sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister an existing observer.
|
||||
* Register to watch for session lifecycle events. To succeed, the caller
|
||||
* must be the current home app.
|
||||
*/
|
||||
public void unregisterSessionObserver(SessionObserver observer) {
|
||||
try {
|
||||
mInstaller.unregisterObserver(observer.getBinder(), mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
public void addSessionCallback(@NonNull SessionCallback callback) {
|
||||
addSessionCallback(callback, new Handler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register to watch for session lifecycle events. To succeed, the caller
|
||||
* must be the current home app.
|
||||
*
|
||||
* @param handler to dispatch callback events through, otherwise uses
|
||||
* calling thread.
|
||||
*/
|
||||
public void addSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
|
||||
synchronized (mDelegates) {
|
||||
final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback,
|
||||
handler.getLooper());
|
||||
try {
|
||||
mInstaller.registerCallback(delegate, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
mDelegates.add(delegate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister an existing callback.
|
||||
*/
|
||||
public void removeSessionCallback(@NonNull SessionCallback callback) {
|
||||
synchronized (mDelegates) {
|
||||
for (Iterator<SessionCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) {
|
||||
final SessionCallbackDelegate delegate = i.next();
|
||||
if (delegate.mCallback == callback) {
|
||||
try {
|
||||
mInstaller.unregisterCallback(delegate);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,9 +307,9 @@ public class PackageInstaller {
|
||||
* A session may contain any number of split packages. If the application
|
||||
* does not yet exist, this session must include a base package.
|
||||
* <p>
|
||||
* If a package included in this session is already defined by the existing
|
||||
* installation (for example, the same split name), the package in this
|
||||
* session will replace the existing package.
|
||||
* If an APK included in this session is already defined by the existing
|
||||
* installation (for example, the same split name), the APK in this session
|
||||
* will replace the existing APK.
|
||||
*/
|
||||
public static class Session implements Closeable {
|
||||
private IPackageInstallerSession mSession;
|
||||
@@ -257,10 +320,9 @@ public class PackageInstaller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current progress. Valid values are anywhere between 0 and
|
||||
* {@link InstallSessionParams#setProgressMax(int)}.
|
||||
* Set current progress. Valid values are anywhere between 0 and 1.
|
||||
*/
|
||||
public void setProgress(int progress) {
|
||||
public void setProgress(float progress) {
|
||||
try {
|
||||
mSession.setClientProgress(progress);
|
||||
} catch (RemoteException e) {
|
||||
@@ -269,7 +331,7 @@ public class PackageInstaller {
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
public void addProgress(int progress) {
|
||||
public void addProgress(float progress) {
|
||||
try {
|
||||
mSession.addClientProgress(progress);
|
||||
} catch (RemoteException e) {
|
||||
@@ -278,15 +340,33 @@ 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 #fsync(OutputStream)} to ensure bytes have been written to
|
||||
* disk.
|
||||
* Open a stream to write an APK file into the session.
|
||||
* <p>
|
||||
* The returned stream will start writing data at the requested offset
|
||||
* in the underlying file, which can be used to resume a partially
|
||||
* written file. If a valid file length is specified, the system will
|
||||
* preallocate the underlying disk space to optimize placement on disk.
|
||||
* It's strongly recommended to provide a valid file length when known.
|
||||
* <p>
|
||||
* You can write data into the returned stream, optionally call
|
||||
* {@link #fsync(OutputStream)} as needed to ensure bytes have been
|
||||
* persisted to disk, and then close when finished. All streams must be
|
||||
* closed before calling {@link #commit(CommitCallback)}.
|
||||
*
|
||||
* @param name arbitrary, unique name of your choosing to identify the
|
||||
* APK being written. You can open a file again for
|
||||
* additional writes (such as after a reboot) by using the
|
||||
* same name. This name is only meaningful within the context
|
||||
* of a single install session.
|
||||
* @param offsetBytes offset into the file to begin writing at, or 0 to
|
||||
* start at the beginning of the file.
|
||||
* @param lengthBytes total size of the file being written, used to
|
||||
* preallocate the underlying disk space, or -1 if unknown.
|
||||
*/
|
||||
public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes)
|
||||
throws IOException {
|
||||
public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
|
||||
long lengthBytes) throws IOException {
|
||||
try {
|
||||
final ParcelFileDescriptor clientSocket = mSession.openWrite(splitName,
|
||||
final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
|
||||
offsetBytes, lengthBytes);
|
||||
return new FileBridge.FileBridgeOutputStream(clientSocket.getFileDescriptor());
|
||||
} catch (RuntimeException e) {
|
||||
@@ -302,7 +382,7 @@ public class PackageInstaller {
|
||||
* to disk. This is only valid for streams returned from
|
||||
* {@link #openWrite(String, long, long)}.
|
||||
*/
|
||||
public void fsync(OutputStream out) throws IOException {
|
||||
public void fsync(@NonNull OutputStream out) throws IOException {
|
||||
if (out instanceof FileBridge.FileBridgeOutputStream) {
|
||||
((FileBridge.FileBridgeOutputStream) out).fsync();
|
||||
} else {
|
||||
@@ -319,9 +399,9 @@ public class PackageInstaller {
|
||||
* on the session. If the device reboots before the session has been
|
||||
* finalized, you may commit the session again.
|
||||
*/
|
||||
public void commit(CommitResultCallback callback) {
|
||||
public void commit(@NonNull CommitCallback callback) {
|
||||
try {
|
||||
mSession.install(new CommitResultCallbackDelegate(callback).getBinder());
|
||||
mSession.commit(new CommitCallbackDelegate(callback).getBinder());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
@@ -333,15 +413,20 @@ public class PackageInstaller {
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
// No resources to release at the moment
|
||||
try {
|
||||
mSession.close();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely destroy this session, rendering it invalid.
|
||||
* Completely abandon this session, destroying all staged data and
|
||||
* rendering it invalid.
|
||||
*/
|
||||
public void destroy() {
|
||||
public void abandon() {
|
||||
try {
|
||||
mSession.destroy();
|
||||
mSession.abandon();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
@@ -351,30 +436,26 @@ public class PackageInstaller {
|
||||
/**
|
||||
* Final result of an uninstall request.
|
||||
*/
|
||||
public static abstract class UninstallResultCallback {
|
||||
public static abstract class UninstallCallback {
|
||||
public abstract void onSuccess();
|
||||
public abstract void onFailure(String msg);
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
private static class UninstallResultCallbackDelegate extends PackageUninstallObserver {
|
||||
private final UninstallResultCallback target;
|
||||
private static class UninstallCallbackDelegate extends PackageUninstallObserver {
|
||||
private final UninstallCallback target;
|
||||
|
||||
public UninstallResultCallbackDelegate(UninstallResultCallback target) {
|
||||
public UninstallCallbackDelegate(UninstallCallback target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUninstallFinished(String basePackageName, int returnCode) {
|
||||
final String msg = null;
|
||||
|
||||
switch (returnCode) {
|
||||
case PackageManager.DELETE_SUCCEEDED: target.onSuccess(); break;
|
||||
case PackageManager.DELETE_FAILED_INTERNAL_ERROR: target.onFailure("DELETE_FAILED_INTERNAL_ERROR: " + msg); break;
|
||||
case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER: target.onFailure("DELETE_FAILED_DEVICE_POLICY_MANAGER: " + msg); break;
|
||||
case PackageManager.DELETE_FAILED_USER_RESTRICTED: target.onFailure("DELETE_FAILED_USER_RESTRICTED: " + msg); break;
|
||||
case PackageManager.DELETE_FAILED_OWNER_BLOCKED: target.onFailure("DELETE_FAILED_OWNER_BLOCKED: " + msg); break;
|
||||
default: target.onFailure(msg); break;
|
||||
if (returnCode == PackageManager.DELETE_SUCCEEDED) {
|
||||
target.onSuccess();
|
||||
} else {
|
||||
final String msg = PackageManager.deleteStatusToString(returnCode);
|
||||
target.onFailure(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -382,16 +463,13 @@ public class PackageInstaller {
|
||||
/**
|
||||
* Final result of a session commit request.
|
||||
*/
|
||||
public static abstract class CommitResultCallback {
|
||||
public abstract void onSuccess();
|
||||
|
||||
public static abstract class CommitCallback {
|
||||
/**
|
||||
* 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.
|
||||
* Generic unknown failure. The system will always try to provide a more
|
||||
* specific failure reason, but in some rare cases this may be
|
||||
* delivered.
|
||||
*/
|
||||
public abstract void onFailure(String msg);
|
||||
public static final int FAILURE_UNKNOWN = 0;
|
||||
|
||||
/**
|
||||
* One or more of the APKs included in the session was invalid. For
|
||||
@@ -399,23 +477,19 @@ public class PackageInstaller {
|
||||
* mismatched, etc. The installer may want to try downloading and
|
||||
* installing again.
|
||||
*/
|
||||
public void onFailureInvalid(String msg) {
|
||||
onFailure(msg);
|
||||
}
|
||||
public static final int FAILURE_INVALID = 1;
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
* <p>
|
||||
* The extras bundle may contain {@link #EXTRA_PACKAGE_NAME} if one
|
||||
* specific package was identified as the cause of the conflict. If
|
||||
* unknown, or multiple packages, the extra may be {@code null}.
|
||||
*/
|
||||
public void onFailureConflict(String msg, String otherPackageName) {
|
||||
onFailure(msg);
|
||||
}
|
||||
public static final int FAILURE_CONFLICT = 2;
|
||||
|
||||
/**
|
||||
* This install session failed due to storage issues. For example,
|
||||
@@ -423,9 +497,7 @@ public class PackageInstaller {
|
||||
* media may be unavailable. The user may be able to help free space
|
||||
* or insert the correct media.
|
||||
*/
|
||||
public void onFailureStorage(String msg) {
|
||||
onFailure(msg);
|
||||
}
|
||||
public static final int FAILURE_STORAGE = 3;
|
||||
|
||||
/**
|
||||
* This install session is fundamentally incompatible with this
|
||||
@@ -434,66 +506,37 @@ public class PackageInstaller {
|
||||
* ABI, or it requires a newer SDK version, etc. This install would
|
||||
* never succeed.
|
||||
*/
|
||||
public void onFailureIncompatible(String msg) {
|
||||
onFailure(msg);
|
||||
}
|
||||
public static final int FAILURE_INCOMPATIBLE = 4;
|
||||
|
||||
public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
|
||||
|
||||
public abstract void onSuccess();
|
||||
public abstract void onFailure(int failureReason, String msg, Bundle extras);
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
private static class CommitResultCallbackDelegate extends PackageInstallObserver {
|
||||
private final CommitResultCallback target;
|
||||
private static class CommitCallbackDelegate extends PackageInstallObserver {
|
||||
private final CommitCallback target;
|
||||
|
||||
public CommitResultCallbackDelegate(CommitResultCallback target) {
|
||||
public CommitCallbackDelegate(CommitCallback target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packageInstalled(String basePackageName, Bundle extras, int returnCode,
|
||||
String msg) {
|
||||
final String otherPackage = null;
|
||||
if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
|
||||
target.onSuccess();
|
||||
} else {
|
||||
final int failureReason = PackageManager.installStatusToFailureReason(returnCode);
|
||||
msg = PackageManager.installStatusToString(returnCode) + ": " + msg;
|
||||
|
||||
switch (returnCode) {
|
||||
case PackageManager.INSTALL_SUCCEEDED: target.onSuccess(); break;
|
||||
case PackageManager.INSTALL_FAILED_ALREADY_EXISTS: target.onFailureConflict("INSTALL_FAILED_ALREADY_EXISTS: " + msg, otherPackage); break;
|
||||
case PackageManager.INSTALL_FAILED_INVALID_APK: target.onFailureInvalid("INSTALL_FAILED_INVALID_APK: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_INVALID_URI: target.onFailureInvalid("INSTALL_FAILED_INVALID_URI: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE: target.onFailureStorage("INSTALL_FAILED_INSUFFICIENT_STORAGE: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE: target.onFailureConflict("INSTALL_FAILED_DUPLICATE_PACKAGE: " + msg, otherPackage); break;
|
||||
case PackageManager.INSTALL_FAILED_NO_SHARED_USER: target.onFailureConflict("INSTALL_FAILED_NO_SHARED_USER: " + msg, otherPackage); break;
|
||||
case PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE: target.onFailureConflict("INSTALL_FAILED_UPDATE_INCOMPATIBLE: " + msg, otherPackage); break;
|
||||
case PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: target.onFailureConflict("INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: " + msg, otherPackage); break;
|
||||
case PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY: target.onFailureIncompatible("INSTALL_FAILED_MISSING_SHARED_LIBRARY: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE: target.onFailureConflict("INSTALL_FAILED_REPLACE_COULDNT_DELETE: " + msg, otherPackage); break;
|
||||
case PackageManager.INSTALL_FAILED_DEXOPT: target.onFailureInvalid("INSTALL_FAILED_DEXOPT: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_OLDER_SDK: target.onFailureIncompatible("INSTALL_FAILED_OLDER_SDK: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER: target.onFailureConflict("INSTALL_FAILED_CONFLICTING_PROVIDER: " + msg, otherPackage); break;
|
||||
case PackageManager.INSTALL_FAILED_NEWER_SDK: target.onFailureIncompatible("INSTALL_FAILED_NEWER_SDK: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_TEST_ONLY: target.onFailureInvalid("INSTALL_FAILED_TEST_ONLY: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: target.onFailureIncompatible("INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_MISSING_FEATURE: target.onFailureIncompatible("INSTALL_FAILED_MISSING_FEATURE: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_CONTAINER_ERROR: target.onFailureStorage("INSTALL_FAILED_CONTAINER_ERROR: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION: target.onFailureStorage("INSTALL_FAILED_INVALID_INSTALL_LOCATION: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE: target.onFailureStorage("INSTALL_FAILED_MEDIA_UNAVAILABLE: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_VERIFICATION_TIMEOUT: target.onFailure("INSTALL_FAILED_VERIFICATION_TIMEOUT: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE: target.onFailure("INSTALL_FAILED_VERIFICATION_FAILURE: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_PACKAGE_CHANGED: target.onFailureInvalid("INSTALL_FAILED_PACKAGE_CHANGED: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_UID_CHANGED: target.onFailureInvalid("INSTALL_FAILED_UID_CHANGED: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE: target.onFailureInvalid("INSTALL_FAILED_VERSION_DOWNGRADE: " + msg); break;
|
||||
case PackageManager.INSTALL_PARSE_FAILED_NOT_APK: target.onFailureInvalid("INSTALL_PARSE_FAILED_NOT_APK: " + msg); break;
|
||||
case PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST: target.onFailureInvalid("INSTALL_PARSE_FAILED_BAD_MANIFEST: " + msg); break;
|
||||
case PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: target.onFailureInvalid("INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: " + msg); break;
|
||||
case PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES: target.onFailureInvalid("INSTALL_PARSE_FAILED_NO_CERTIFICATES: " + msg); break;
|
||||
case PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: target.onFailureInvalid("INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: " + msg); break;
|
||||
case PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: target.onFailureInvalid("INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: " + msg); break;
|
||||
case PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: target.onFailureInvalid("INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: " + msg); break;
|
||||
case PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: target.onFailureInvalid("INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: " + msg); break;
|
||||
case PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: target.onFailureInvalid("INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: " + msg); break;
|
||||
case PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY: target.onFailureInvalid("INSTALL_PARSE_FAILED_MANIFEST_EMPTY: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_INTERNAL_ERROR: target.onFailure("INSTALL_FAILED_INTERNAL_ERROR: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_USER_RESTRICTED: target.onFailureIncompatible("INSTALL_FAILED_USER_RESTRICTED: " + msg); break;
|
||||
case PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION: target.onFailureConflict("INSTALL_FAILED_DUPLICATE_PERMISSION: " + msg, otherPackage); break;
|
||||
case PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS: target.onFailureInvalid("INSTALL_FAILED_NO_MATCHING_ABIS: " + msg); break;
|
||||
default: target.onFailure(msg); break;
|
||||
if (extras != null) {
|
||||
extras.putString(CommitCallback.EXTRA_PACKAGE_NAME,
|
||||
extras.getString(PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE));
|
||||
}
|
||||
|
||||
target.onFailure(failureReason, msg, extras);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.content.pm;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SdkConstant;
|
||||
import android.annotation.SdkConstant.SdkConstantType;
|
||||
import android.annotation.SystemApi;
|
||||
@@ -26,6 +27,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.PackageInstaller.CommitCallback;
|
||||
import android.content.pm.PackageParser.PackageParserException;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
@@ -3723,7 +3725,7 @@ public abstract class PackageManager {
|
||||
* Return interface that offers the ability to install, upgrade, and remove
|
||||
* applications on the device.
|
||||
*/
|
||||
public abstract PackageInstaller getInstaller();
|
||||
public abstract @NonNull PackageInstaller getPackageInstaller();
|
||||
|
||||
/**
|
||||
* Returns the data directory for a particular user and package, given the uid of the package.
|
||||
@@ -3772,4 +3774,109 @@ public abstract class PackageManager {
|
||||
|
||||
/** {@hide} */
|
||||
public abstract boolean isPackageAvailable(String packageName);
|
||||
|
||||
/** {@hide} */
|
||||
public static String installStatusToString(int status) {
|
||||
switch (status) {
|
||||
case INSTALL_SUCCEEDED: return "INSTALL_SUCCEEDED";
|
||||
case INSTALL_FAILED_ALREADY_EXISTS: return "INSTALL_FAILED_ALREADY_EXISTS";
|
||||
case INSTALL_FAILED_INVALID_APK: return "INSTALL_FAILED_INVALID_APK";
|
||||
case INSTALL_FAILED_INVALID_URI: return "INSTALL_FAILED_INVALID_URI";
|
||||
case INSTALL_FAILED_INSUFFICIENT_STORAGE: return "INSTALL_FAILED_INSUFFICIENT_STORAGE";
|
||||
case INSTALL_FAILED_DUPLICATE_PACKAGE: return "INSTALL_FAILED_DUPLICATE_PACKAGE";
|
||||
case INSTALL_FAILED_NO_SHARED_USER: return "INSTALL_FAILED_NO_SHARED_USER";
|
||||
case INSTALL_FAILED_UPDATE_INCOMPATIBLE: return "INSTALL_FAILED_UPDATE_INCOMPATIBLE";
|
||||
case INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: return "INSTALL_FAILED_SHARED_USER_INCOMPATIBLE";
|
||||
case INSTALL_FAILED_MISSING_SHARED_LIBRARY: return "INSTALL_FAILED_MISSING_SHARED_LIBRARY";
|
||||
case INSTALL_FAILED_REPLACE_COULDNT_DELETE: return "INSTALL_FAILED_REPLACE_COULDNT_DELETE";
|
||||
case INSTALL_FAILED_DEXOPT: return "INSTALL_FAILED_DEXOPT";
|
||||
case INSTALL_FAILED_OLDER_SDK: return "INSTALL_FAILED_OLDER_SDK";
|
||||
case INSTALL_FAILED_CONFLICTING_PROVIDER: return "INSTALL_FAILED_CONFLICTING_PROVIDER";
|
||||
case INSTALL_FAILED_NEWER_SDK: return "INSTALL_FAILED_NEWER_SDK";
|
||||
case INSTALL_FAILED_TEST_ONLY: return "INSTALL_FAILED_TEST_ONLY";
|
||||
case INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: return "INSTALL_FAILED_CPU_ABI_INCOMPATIBLE";
|
||||
case INSTALL_FAILED_MISSING_FEATURE: return "INSTALL_FAILED_MISSING_FEATURE";
|
||||
case INSTALL_FAILED_CONTAINER_ERROR: return "INSTALL_FAILED_CONTAINER_ERROR";
|
||||
case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return "INSTALL_FAILED_INVALID_INSTALL_LOCATION";
|
||||
case INSTALL_FAILED_MEDIA_UNAVAILABLE: return "INSTALL_FAILED_MEDIA_UNAVAILABLE";
|
||||
case INSTALL_FAILED_VERIFICATION_TIMEOUT: return "INSTALL_FAILED_VERIFICATION_TIMEOUT";
|
||||
case INSTALL_FAILED_VERIFICATION_FAILURE: return "INSTALL_FAILED_VERIFICATION_FAILURE";
|
||||
case INSTALL_FAILED_PACKAGE_CHANGED: return "INSTALL_FAILED_PACKAGE_CHANGED";
|
||||
case INSTALL_FAILED_UID_CHANGED: return "INSTALL_FAILED_UID_CHANGED";
|
||||
case INSTALL_FAILED_VERSION_DOWNGRADE: return "INSTALL_FAILED_VERSION_DOWNGRADE";
|
||||
case INSTALL_PARSE_FAILED_NOT_APK: return "INSTALL_PARSE_FAILED_NOT_APK";
|
||||
case INSTALL_PARSE_FAILED_BAD_MANIFEST: return "INSTALL_PARSE_FAILED_BAD_MANIFEST";
|
||||
case INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: return "INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION";
|
||||
case INSTALL_PARSE_FAILED_NO_CERTIFICATES: return "INSTALL_PARSE_FAILED_NO_CERTIFICATES";
|
||||
case INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: return "INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES";
|
||||
case INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: return "INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING";
|
||||
case INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: return "INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME";
|
||||
case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return "INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID";
|
||||
case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return "INSTALL_PARSE_FAILED_MANIFEST_MALFORMED";
|
||||
case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return "INSTALL_PARSE_FAILED_MANIFEST_EMPTY";
|
||||
case INSTALL_FAILED_INTERNAL_ERROR: return "INSTALL_FAILED_INTERNAL_ERROR";
|
||||
case INSTALL_FAILED_USER_RESTRICTED: return "INSTALL_FAILED_USER_RESTRICTED";
|
||||
case INSTALL_FAILED_DUPLICATE_PERMISSION: return "INSTALL_FAILED_DUPLICATE_PERMISSION";
|
||||
case INSTALL_FAILED_NO_MATCHING_ABIS: return "INSTALL_FAILED_NO_MATCHING_ABIS";
|
||||
default: return Integer.toString(status);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
public static int installStatusToFailureReason(int status) {
|
||||
switch (status) {
|
||||
case INSTALL_FAILED_ALREADY_EXISTS: return CommitCallback.FAILURE_CONFLICT;
|
||||
case INSTALL_FAILED_INVALID_APK: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_FAILED_INVALID_URI: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_FAILED_INSUFFICIENT_STORAGE: return CommitCallback.FAILURE_STORAGE;
|
||||
case INSTALL_FAILED_DUPLICATE_PACKAGE: return CommitCallback.FAILURE_CONFLICT;
|
||||
case INSTALL_FAILED_NO_SHARED_USER: return CommitCallback.FAILURE_CONFLICT;
|
||||
case INSTALL_FAILED_UPDATE_INCOMPATIBLE: return CommitCallback.FAILURE_CONFLICT;
|
||||
case INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: return CommitCallback.FAILURE_CONFLICT;
|
||||
case INSTALL_FAILED_MISSING_SHARED_LIBRARY: return CommitCallback.FAILURE_INCOMPATIBLE;
|
||||
case INSTALL_FAILED_REPLACE_COULDNT_DELETE: return CommitCallback.FAILURE_CONFLICT;
|
||||
case INSTALL_FAILED_DEXOPT: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_FAILED_OLDER_SDK: return CommitCallback.FAILURE_INCOMPATIBLE;
|
||||
case INSTALL_FAILED_CONFLICTING_PROVIDER: return CommitCallback.FAILURE_CONFLICT;
|
||||
case INSTALL_FAILED_NEWER_SDK: return CommitCallback.FAILURE_INCOMPATIBLE;
|
||||
case INSTALL_FAILED_TEST_ONLY: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: return CommitCallback.FAILURE_INCOMPATIBLE;
|
||||
case INSTALL_FAILED_MISSING_FEATURE: return CommitCallback.FAILURE_INCOMPATIBLE;
|
||||
case INSTALL_FAILED_CONTAINER_ERROR: return CommitCallback.FAILURE_STORAGE;
|
||||
case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return CommitCallback.FAILURE_STORAGE;
|
||||
case INSTALL_FAILED_MEDIA_UNAVAILABLE: return CommitCallback.FAILURE_STORAGE;
|
||||
case INSTALL_FAILED_VERIFICATION_TIMEOUT: return CommitCallback.FAILURE_UNKNOWN;
|
||||
case INSTALL_FAILED_VERIFICATION_FAILURE: return CommitCallback.FAILURE_UNKNOWN;
|
||||
case INSTALL_FAILED_PACKAGE_CHANGED: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_FAILED_UID_CHANGED: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_FAILED_VERSION_DOWNGRADE: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_PARSE_FAILED_NOT_APK: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_PARSE_FAILED_BAD_MANIFEST: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_PARSE_FAILED_NO_CERTIFICATES: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return CommitCallback.FAILURE_INVALID;
|
||||
case INSTALL_FAILED_INTERNAL_ERROR: return CommitCallback.FAILURE_UNKNOWN;
|
||||
case INSTALL_FAILED_USER_RESTRICTED: return CommitCallback.FAILURE_INCOMPATIBLE;
|
||||
case INSTALL_FAILED_DUPLICATE_PERMISSION: return CommitCallback.FAILURE_CONFLICT;
|
||||
case INSTALL_FAILED_NO_MATCHING_ABIS: return CommitCallback.FAILURE_INCOMPATIBLE;
|
||||
default: return CommitCallback.FAILURE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
public static String deleteStatusToString(int status) {
|
||||
switch (status) {
|
||||
case DELETE_SUCCEEDED: return "DELETE_SUCCEEDED";
|
||||
case DELETE_FAILED_INTERNAL_ERROR: return "DELETE_FAILED_INTERNAL_ERROR";
|
||||
case DELETE_FAILED_DEVICE_POLICY_MANAGER: return "DELETE_FAILED_DEVICE_POLICY_MANAGER";
|
||||
case DELETE_FAILED_USER_RESTRICTED: return "DELETE_FAILED_USER_RESTRICTED";
|
||||
case DELETE_FAILED_OWNER_BLOCKED: return "DELETE_FAILED_OWNER_BLOCKED";
|
||||
default: return Integer.toString(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2798,6 +2798,13 @@
|
||||
android:description="@string/permdesc_createMediaProjection"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- @SystemApi Allows an application to read install sessions
|
||||
@hide This is not a third-party API (intended for system apps). -->
|
||||
<permission android:name="android.permission.READ_INSTALL_SESSIONS"
|
||||
android:label="@string/permlab_readInstallSessions"
|
||||
android:description="@string/permdesc_readInstallSessions"
|
||||
android:protectionLevel="signature|system" />
|
||||
|
||||
<!-- The system process is explicitly the only one allowed to launch the
|
||||
confirmation UI for full backup/restore -->
|
||||
<uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
|
||||
|
||||
@@ -3872,6 +3872,11 @@
|
||||
<!-- Description of an application permission that lets it create media projection sessions. -->
|
||||
<string name="permdesc_createMediaProjection">Allows an application to create media projection sessions. These sessions can provide applications the ability to capture display and audio contents. Should never be needed by normal apps.</string>
|
||||
|
||||
<!-- Title of an application permission that lets it read install sessions. -->
|
||||
<string name="permlab_readInstallSessions">Read install sessions</string>
|
||||
<!-- Description of an application permission that lets it read install sessions. -->
|
||||
<string name="permdesc_readInstallSessions">Allows an application to read install sessions. This allows it to see details about active package installations.</string>
|
||||
|
||||
<!-- Shown in the tutorial for tap twice for zoom control. -->
|
||||
<string name="tutorial_double_tap_to_zoom_message_short">Touch twice for zoom control</string>
|
||||
|
||||
|
||||
@@ -24,10 +24,11 @@ import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.IPackageDeleteObserver;
|
||||
import android.content.pm.IPackageInstaller;
|
||||
import android.content.pm.IPackageInstallerObserver;
|
||||
import android.content.pm.IPackageInstallerCallback;
|
||||
import android.content.pm.IPackageInstallerSession;
|
||||
import android.content.pm.InstallSessionInfo;
|
||||
import android.content.pm.InstallSessionParams;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Binder;
|
||||
import android.os.FileUtils;
|
||||
import android.os.HandlerThread;
|
||||
@@ -54,6 +55,7 @@ import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class PackageInstallerService extends IPackageInstaller.Stub {
|
||||
private static final String TAG = "PackageInstaller";
|
||||
@@ -80,7 +82,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
||||
@GuardedBy("mSessions")
|
||||
private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>();
|
||||
|
||||
private RemoteCallbackList<IPackageInstallerObserver> mObservers = new RemoteCallbackList<>();
|
||||
private RemoteCallbackList<IPackageInstallerCallback> mCallbacks = new RemoteCallbackList<>();
|
||||
|
||||
private static final FilenameFilter sStageFilter = new FilenameFilter() {
|
||||
@Override
|
||||
@@ -152,8 +154,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int createSession(String installerPackageName, InstallSessionParams params,
|
||||
int userId) {
|
||||
public int createSession(InstallSessionParams params, String installerPackageName, int userId) {
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
mPm.enforceCrossUserPermission(callingUid, userId, true, "createSession");
|
||||
|
||||
@@ -172,14 +173,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
||||
params.installFlags |= INSTALL_REPLACE_EXISTING;
|
||||
}
|
||||
|
||||
if (params.mode == InstallSessionParams.MODE_INVALID) {
|
||||
throw new IllegalArgumentException("Params must have valid mode set");
|
||||
switch (params.mode) {
|
||||
case InstallSessionParams.MODE_FULL_INSTALL:
|
||||
case InstallSessionParams.MODE_INHERIT_EXISTING:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Params must have valid mode set");
|
||||
}
|
||||
|
||||
// Sanity check that install could fit
|
||||
if (params.deltaSize > 0) {
|
||||
if (params.sizeBytes > 0) {
|
||||
try {
|
||||
mPm.freeStorage(params.deltaSize);
|
||||
mPm.freeStorage(params.sizeBytes);
|
||||
} catch (IOException e) {
|
||||
throw ExceptionUtils.wrap(e);
|
||||
}
|
||||
@@ -248,8 +253,22 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InstallSessionInfo> getSessions(int userId) {
|
||||
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getSessions");
|
||||
public InstallSessionInfo getSessionInfo(int sessionId) {
|
||||
synchronized (mSessions) {
|
||||
final PackageInstallerSession session = mSessions.get(sessionId);
|
||||
final boolean isOwner = (session != null)
|
||||
&& (session.installerUid == Binder.getCallingUid());
|
||||
if (!isOwner) {
|
||||
enforceCallerCanReadSessions();
|
||||
}
|
||||
return session != null ? session.generateInfo() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InstallSessionInfo> getAllSessions(int userId) {
|
||||
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getAllSessions");
|
||||
enforceCallerCanReadSessions();
|
||||
|
||||
final List<InstallSessionInfo> result = new ArrayList<>();
|
||||
synchronized (mSessions) {
|
||||
@@ -263,10 +282,30 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InstallSessionInfo> getMySessions(String installerPackageName, int userId) {
|
||||
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getMySessions");
|
||||
mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
|
||||
|
||||
final List<InstallSessionInfo> result = new ArrayList<>();
|
||||
synchronized (mSessions) {
|
||||
for (int i = 0; i < mSessions.size(); i++) {
|
||||
final PackageInstallerSession session = mSessions.valueAt(i);
|
||||
if (Objects.equals(session.installerPackageName, installerPackageName)
|
||||
&& session.userId == userId) {
|
||||
result.add(session.generateInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninstall(String packageName, int flags, IPackageDeleteObserver observer,
|
||||
int userId) {
|
||||
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstall");
|
||||
|
||||
// TODO: enforce installer of record or permission
|
||||
mPm.deletePackageAsUser(packageName, observer, userId, flags);
|
||||
}
|
||||
|
||||
@@ -280,17 +319,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerObserver(IPackageInstallerObserver observer, int userId) {
|
||||
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "registerObserver");
|
||||
public void registerCallback(IPackageInstallerCallback callback, int userId) {
|
||||
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "registerCallback");
|
||||
enforceCallerCanReadSessions();
|
||||
|
||||
// TODO: consider restricting to active launcher app only
|
||||
mObservers.register(observer, new UserHandle(userId));
|
||||
mCallbacks.register(callback, new UserHandle(userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterObserver(IPackageInstallerObserver observer, int userId) {
|
||||
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "unregisterObserver");
|
||||
mObservers.unregister(observer);
|
||||
public void unregisterCallback(IPackageInstallerCallback callback) {
|
||||
mCallbacks.unregister(callback);
|
||||
}
|
||||
|
||||
private int getSessionUserId(int sessionId) {
|
||||
@@ -299,52 +337,68 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private void notifySessionCreated(InstallSessionInfo info) {
|
||||
final int userId = getSessionUserId(info.sessionId);
|
||||
final int n = mObservers.beginBroadcast();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final IPackageInstallerObserver observer = mObservers.getBroadcastItem(i);
|
||||
final UserHandle user = (UserHandle) mObservers.getBroadcastCookie(i);
|
||||
if (userId == user.getIdentifier()) {
|
||||
try {
|
||||
observer.onSessionCreated(info);
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
}
|
||||
/**
|
||||
* We allow those with permission, or the current home app.
|
||||
*/
|
||||
private void enforceCallerCanReadSessions() {
|
||||
final boolean hasPermission = (mContext.checkCallingOrSelfPermission(
|
||||
android.Manifest.permission.READ_INSTALL_SESSIONS)
|
||||
== PackageManager.PERMISSION_GRANTED);
|
||||
final boolean isHomeApp = mPm.checkCallerIsHomeApp();
|
||||
if (hasPermission || isHomeApp) {
|
||||
return;
|
||||
} else {
|
||||
throw new SecurityException("Caller must be current home app to read install sessions");
|
||||
}
|
||||
mObservers.finishBroadcast();
|
||||
}
|
||||
|
||||
private void notifySessionProgress(int sessionId, int progress) {
|
||||
final int userId = getSessionUserId(sessionId);
|
||||
final int n = mObservers.beginBroadcast();
|
||||
private void notifySessionCreated(InstallSessionInfo info) {
|
||||
final int userId = getSessionUserId(info.sessionId);
|
||||
final int n = mCallbacks.beginBroadcast();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final IPackageInstallerObserver observer = mObservers.getBroadcastItem(i);
|
||||
final UserHandle user = (UserHandle) mObservers.getBroadcastCookie(i);
|
||||
final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
|
||||
final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
|
||||
// TODO: dispatch notifications for slave profiles
|
||||
if (userId == user.getIdentifier()) {
|
||||
try {
|
||||
observer.onSessionProgress(sessionId, progress);
|
||||
callback.onSessionCreated(info.sessionId);
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
mObservers.finishBroadcast();
|
||||
mCallbacks.finishBroadcast();
|
||||
}
|
||||
|
||||
private void notifySessionProgressChanged(int sessionId, float progress) {
|
||||
final int userId = getSessionUserId(sessionId);
|
||||
final int n = mCallbacks.beginBroadcast();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
|
||||
final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
|
||||
if (userId == user.getIdentifier()) {
|
||||
try {
|
||||
callback.onSessionProgressChanged(sessionId, progress);
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
mCallbacks.finishBroadcast();
|
||||
}
|
||||
|
||||
private void notifySessionFinished(int sessionId, boolean success) {
|
||||
final int userId = getSessionUserId(sessionId);
|
||||
final int n = mObservers.beginBroadcast();
|
||||
final int n = mCallbacks.beginBroadcast();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final IPackageInstallerObserver observer = mObservers.getBroadcastItem(i);
|
||||
final UserHandle user = (UserHandle) mObservers.getBroadcastCookie(i);
|
||||
final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
|
||||
final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
|
||||
if (userId == user.getIdentifier()) {
|
||||
try {
|
||||
observer.onSessionFinished(sessionId, success);
|
||||
callback.onSessionFinished(sessionId, success);
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
mObservers.finishBroadcast();
|
||||
mCallbacks.finishBroadcast();
|
||||
}
|
||||
|
||||
void dump(IndentingPrintWriter pw) {
|
||||
@@ -374,8 +428,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
||||
}
|
||||
|
||||
class Callback {
|
||||
public void onSessionProgress(PackageInstallerSession session, int progress) {
|
||||
notifySessionProgress(session.sessionId, progress);
|
||||
public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
|
||||
notifySessionProgressChanged(session.sessionId, progress);
|
||||
}
|
||||
|
||||
public void onSessionFinished(PackageInstallerSession session, boolean success) {
|
||||
|
||||
@@ -71,6 +71,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
// TODO: enforce INSTALL_ALLOW_DOWNGRADE
|
||||
// TODO: handle INSTALL_EXTERNAL, INSTALL_INTERNAL
|
||||
|
||||
// TODO: treat INHERIT_EXISTING as installExistingPackage()
|
||||
|
||||
private final PackageInstallerService.Callback mCallback;
|
||||
private final PackageManagerService mPm;
|
||||
private final Handler mHandler;
|
||||
@@ -84,7 +86,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
public final long createdMillis;
|
||||
public final File sessionStageDir;
|
||||
|
||||
private static final int MSG_INSTALL = 0;
|
||||
private static final int MSG_COMMIT = 0;
|
||||
|
||||
private Handler.Callback mHandlerCallback = new Handler.Callback() {
|
||||
@Override
|
||||
@@ -95,7 +97,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
|
||||
try {
|
||||
installLocked();
|
||||
commitLocked();
|
||||
} catch (PackageManagerException e) {
|
||||
Slog.e(TAG, "Install failed: " + e);
|
||||
destroyInternal();
|
||||
@@ -114,8 +116,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
private int mClientProgress;
|
||||
private int mProgress = 0;
|
||||
private float mClientProgress;
|
||||
private float mProgress = 0;
|
||||
|
||||
private String mPackageName;
|
||||
private int mVersionCode;
|
||||
@@ -168,23 +170,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
info.progress = mProgress;
|
||||
|
||||
info.mode = params.mode;
|
||||
info.packageName = params.packageName;
|
||||
info.icon = params.icon;
|
||||
info.title = params.title;
|
||||
info.sizeBytes = params.sizeBytes;
|
||||
info.appPackageName = params.appPackageName;
|
||||
info.appIcon = params.appIcon;
|
||||
info.appLabel = params.appLabel;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientProgress(int progress) {
|
||||
public void setClientProgress(float progress) {
|
||||
mClientProgress = progress;
|
||||
mProgress = MathUtils.constrain(
|
||||
(int) (((float) mClientProgress) / ((float) params.progressMax)) * 80, 0, 80);
|
||||
mCallback.onSessionProgress(this, mProgress);
|
||||
mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f);
|
||||
mCallback.onSessionProgressChanged(this, mProgress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClientProgress(int progress) {
|
||||
public void addClientProgress(float progress) {
|
||||
setClientProgress(mClientProgress + progress);
|
||||
}
|
||||
|
||||
@@ -250,12 +252,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void install(IPackageInstallObserver2 observer) {
|
||||
public void commit(IPackageInstallObserver2 observer) {
|
||||
Preconditions.checkNotNull(observer);
|
||||
mHandler.obtainMessage(MSG_INSTALL, observer).sendToTarget();
|
||||
mHandler.obtainMessage(MSG_COMMIT, observer).sendToTarget();
|
||||
}
|
||||
|
||||
private void installLocked() throws PackageManagerException {
|
||||
private void commitLocked() throws PackageManagerException {
|
||||
if (mInvalid) {
|
||||
throw new PackageManagerException(INSTALL_FAILED_ALREADY_EXISTS, "Invalid session");
|
||||
}
|
||||
@@ -295,7 +297,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
|
||||
// TODO: surface more granular state from dexopt
|
||||
mCallback.onSessionProgress(this, 90);
|
||||
mCallback.onSessionProgressChanged(this, 0.9f);
|
||||
|
||||
// TODO: for ASEC based applications, grow and stream in packages
|
||||
|
||||
@@ -458,7 +460,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
public void close() {
|
||||
// Currently ignored
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abandon() {
|
||||
try {
|
||||
destroyInternal();
|
||||
} finally {
|
||||
|
||||
@@ -11686,6 +11686,47 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
preferred.activityInfo.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if calling UID is the current home app. This handles both the case
|
||||
* where the user has selected a specific home app, and where there is only
|
||||
* one home app.
|
||||
*/
|
||||
public boolean checkCallerIsHomeApp() {
|
||||
final Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.addCategory(Intent.CATEGORY_HOME);
|
||||
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int callingUserId = UserHandle.getCallingUserId();
|
||||
final List<ResolveInfo> allHomes = queryIntentActivities(intent, null, 0, callingUserId);
|
||||
final ResolveInfo preferredHome = findPreferredActivity(intent, null, 0, allHomes, 0, true,
|
||||
false, false, callingUserId);
|
||||
|
||||
if (preferredHome != null) {
|
||||
if (callingUid == preferredHome.activityInfo.applicationInfo.uid) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
for (ResolveInfo info : allHomes) {
|
||||
if (callingUid == info.activityInfo.applicationInfo.uid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforce that calling UID is the current home app. This handles both the
|
||||
* case where the user has selected a specific home app, and where there is
|
||||
* only one home app.
|
||||
*/
|
||||
public void enforceCallerIsHomeApp() {
|
||||
if (!checkCallerIsHomeApp()) {
|
||||
throw new SecurityException("Caller is not currently selected home app");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEnabledSetting(String appPackageName,
|
||||
int newState, int flags, int userId, String callingPackage) {
|
||||
|
||||
@@ -750,7 +750,7 @@ public class MockPackageManager extends PackageManager {
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
public PackageInstaller getInstaller() {
|
||||
public PackageInstaller getPackageInstaller() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user