Merge "Allow badging updates to install sessions." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index fb52648d..dab4f64 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8681,15 +8681,17 @@
public class PackageInstaller {
method public void abandonSession(int);
- 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.PackageInstaller.SessionParams) throws java.io.IOException;
method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllSessions();
method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions();
method public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
method public android.content.pm.PackageInstaller.Session openSession(int);
- method public void removeSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
+ method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
+ method public void registerSessionCallback(android.content.pm.PackageInstaller.SessionCallback, android.os.Handler);
method public void uninstall(java.lang.String, android.content.IntentSender);
+ method public void unregisterSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
+ method public void updateSessionAppIcon(int, android.graphics.Bitmap);
+ method public void updateSessionAppLabel(int, java.lang.CharSequence);
field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
field public static final java.lang.String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
@@ -8721,6 +8723,7 @@
public static abstract class PackageInstaller.SessionCallback {
ctor public PackageInstaller.SessionCallback();
+ method public abstract void onBadgingChanged(int);
method public abstract void onClosed(int);
method public abstract void onCreated(int);
method public abstract void onFinished(int, boolean);
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index c6c0ff6..6daefc8 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -22,14 +22,21 @@
import android.content.pm.PackageInstaller;
import android.content.IntentSender;
+import android.graphics.Bitmap;
+
/** {@hide} */
interface IPackageInstaller {
int createSession(in PackageInstaller.SessionParams params, String installerPackageName, int userId);
+
+ void updateSessionAppIcon(int sessionId, in Bitmap appIcon);
+ void updateSessionAppLabel(int sessionId, String appLabel);
+
void abandonSession(int sessionId);
IPackageInstallerSession openSession(int sessionId);
PackageInstaller.SessionInfo getSessionInfo(int sessionId);
+
List<PackageInstaller.SessionInfo> getAllSessions(int userId);
List<PackageInstaller.SessionInfo> getMySessions(String installerPackageName, int userId);
diff --git a/core/java/android/content/pm/IPackageInstallerCallback.aidl b/core/java/android/content/pm/IPackageInstallerCallback.aidl
index 39ae1a0..fe98ee7 100644
--- a/core/java/android/content/pm/IPackageInstallerCallback.aidl
+++ b/core/java/android/content/pm/IPackageInstallerCallback.aidl
@@ -19,6 +19,7 @@
/** {@hide} */
oneway interface IPackageInstallerCallback {
void onSessionCreated(int sessionId);
+ void onSessionBadgingChanged(int sessionId);
void onSessionOpened(int sessionId);
void onSessionProgressChanged(int sessionId, float progress);
void onSessionClosed(int sessionId);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 44e24b1..7c34a65 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -312,6 +312,32 @@
}
}
+ /**
+ * Update the icon representing the app being installed in a specific
+ * session. This should be roughly
+ * {@link ActivityManager#getLauncherLargeIconSize()} in both dimensions.
+ */
+ public void updateSessionAppIcon(int sessionId, @Nullable Bitmap appIcon) {
+ try {
+ mInstaller.updateSessionAppIcon(sessionId, appIcon);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Update the label representing the app being installed in a specific
+ * session.
+ */
+ public void updateSessionAppLabel(int sessionId, @Nullable CharSequence appLabel) {
+ try {
+ final String val = (appLabel != null) ? appLabel.toString() : null;
+ mInstaller.updateSessionAppLabel(sessionId, val);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
public void abandonSession(int sessionId) {
try {
mInstaller.abandonSession(sessionId);
@@ -321,8 +347,7 @@
}
/**
- * Return details for a specific session. To succeed, the caller must either
- * own this session, or be the current home app.
+ * Return details for a specific session.
*/
public @Nullable SessionInfo getSessionInfo(int sessionId) {
try {
@@ -334,7 +359,6 @@
/**
* Return list of all active install sessions, regardless of the installer.
- * To succeed, the caller must be the current home app.
*/
public @NonNull List<SessionInfo> getAllSessions() {
final ApplicationInfo info = mContext.getApplicationInfo();
@@ -406,6 +430,12 @@
public abstract void onCreated(int sessionId);
/**
+ * Badging details for an existing session has changed. For example, the
+ * app icon or label has been updated.
+ */
+ public abstract void onBadgingChanged(int sessionId);
+
+ /**
* Session has been opened. A session is usually opened when the
* installer is actively writing data.
*/
@@ -436,10 +466,11 @@
private static class SessionCallbackDelegate extends IPackageInstallerCallback.Stub implements
Handler.Callback {
private static final int MSG_SESSION_CREATED = 1;
- private static final int MSG_SESSION_OPENED = 2;
- private static final int MSG_SESSION_PROGRESS_CHANGED = 3;
- private static final int MSG_SESSION_CLOSED = 4;
- private static final int MSG_SESSION_FINISHED = 5;
+ private static final int MSG_SESSION_BADGING_CHANGED = 2;
+ private static final int MSG_SESSION_OPENED = 3;
+ private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
+ private static final int MSG_SESSION_CLOSED = 5;
+ private static final int MSG_SESSION_FINISHED = 6;
final SessionCallback mCallback;
final Handler mHandler;
@@ -455,6 +486,9 @@
case MSG_SESSION_CREATED:
mCallback.onCreated(msg.arg1);
return true;
+ case MSG_SESSION_BADGING_CHANGED:
+ mCallback.onBadgingChanged(msg.arg1);
+ return true;
case MSG_SESSION_OPENED:
mCallback.onOpened(msg.arg1);
return true;
@@ -477,6 +511,11 @@
}
@Override
+ public void onSessionBadgingChanged(int sessionId) {
+ mHandler.obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, 0).sendToTarget();
+ }
+
+ @Override
public void onSessionOpened(int sessionId) {
mHandler.obtainMessage(MSG_SESSION_OPENED, sessionId, 0).sendToTarget();
}
@@ -499,22 +538,32 @@
}
}
- /**
- * Register to watch for session lifecycle events. To succeed, the caller
- * must be the current home app.
- */
+ /** {@hide} */
+ @Deprecated
public void addSessionCallback(@NonNull SessionCallback callback) {
- addSessionCallback(callback, new Handler());
+ registerSessionCallback(callback);
}
/**
- * Register to watch for session lifecycle events. To succeed, the caller
- * must be the current home app.
+ * Register to watch for session lifecycle events.
+ */
+ public void registerSessionCallback(@NonNull SessionCallback callback) {
+ registerSessionCallback(callback, new Handler());
+ }
+
+ /** {@hide} */
+ @Deprecated
+ public void addSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
+ registerSessionCallback(callback, handler);
+ }
+
+ /**
+ * Register to watch for session lifecycle events.
*
* @param handler to dispatch callback events through, otherwise uses
* calling thread.
*/
- public void addSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
+ public void registerSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
// TODO: remove this temporary guard once we have new prebuilts
final ApplicationInfo info = mContext.getApplicationInfo();
if ("com.google.android.googlequicksearchbox".equals(info.packageName)
@@ -535,10 +584,16 @@
}
}
+ /** {@hide} */
+ @Deprecated
+ public void removeSessionCallback(@NonNull SessionCallback callback) {
+ unregisterSessionCallback(callback);
+ }
+
/**
* Unregister an existing callback.
*/
- public void removeSessionCallback(@NonNull SessionCallback callback) {
+ public void unregisterSessionCallback(@NonNull SessionCallback callback) {
synchronized (mDelegates) {
for (Iterator<SessionCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) {
final SessionCallbackDelegate delegate = i.next();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 3f7a607..9a00923 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -47,12 +47,12 @@
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageInstallerSession;
import android.content.pm.PackageInstaller;
-import android.content.pm.PackageParser;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
@@ -581,6 +581,30 @@
}
@Override
+ public void updateSessionAppIcon(int sessionId, Bitmap appIcon) {
+ synchronized (mSessions) {
+ final PackageInstallerSession session = mSessions.get(sessionId);
+ if (session == null || !isCallingUidOwner(session)) {
+ throw new SecurityException("Caller has no access to session " + sessionId);
+ }
+ session.params.appIcon = appIcon;
+ mInternalCallback.onSessionBadgingChanged(session);
+ }
+ }
+
+ @Override
+ public void updateSessionAppLabel(int sessionId, String appLabel) {
+ synchronized (mSessions) {
+ final PackageInstallerSession session = mSessions.get(sessionId);
+ if (session == null || !isCallingUidOwner(session)) {
+ throw new SecurityException("Caller has no access to session " + sessionId);
+ }
+ session.params.appLabel = appLabel;
+ mInternalCallback.onSessionBadgingChanged(session);
+ }
+ }
+
+ @Override
public void abandonSession(int sessionId) {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
@@ -681,9 +705,6 @@
public SessionInfo getSessionInfo(int sessionId) {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
- if (!isCallingUidOwner(session)) {
- enforceCallerCanReadSessions();
- }
return session != null ? session.generateInfo() : null;
}
}
@@ -691,7 +712,6 @@
@Override
public List<SessionInfo> getAllSessions(int userId) {
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getAllSessions");
- enforceCallerCanReadSessions();
final List<SessionInfo> result = new ArrayList<>();
synchronized (mSessions) {
@@ -755,8 +775,6 @@
@Override
public void registerCallback(IPackageInstallerCallback callback, int userId) {
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "registerCallback");
- enforceCallerCanReadSessions();
-
mCallbacks.register(callback, userId);
}
@@ -787,21 +805,6 @@
}
}
- /**
- * 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");
- }
- }
-
static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
private final Context mContext;
private final IntentSender mTarget;
@@ -893,10 +896,11 @@
private static class Callbacks extends Handler {
private static final int MSG_SESSION_CREATED = 1;
- private static final int MSG_SESSION_OPENED = 2;
- private static final int MSG_SESSION_PROGRESS_CHANGED = 3;
- private static final int MSG_SESSION_CLOSED = 4;
- private static final int MSG_SESSION_FINISHED = 5;
+ private static final int MSG_SESSION_BADGING_CHANGED = 2;
+ private static final int MSG_SESSION_OPENED = 3;
+ private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
+ private static final int MSG_SESSION_CLOSED = 5;
+ private static final int MSG_SESSION_FINISHED = 6;
private final RemoteCallbackList<IPackageInstallerCallback>
mCallbacks = new RemoteCallbackList<>();
@@ -938,6 +942,9 @@
case MSG_SESSION_CREATED:
callback.onSessionCreated(sessionId);
break;
+ case MSG_SESSION_BADGING_CHANGED:
+ callback.onSessionBadgingChanged(sessionId);
+ break;
case MSG_SESSION_OPENED:
callback.onSessionOpened(sessionId);
break;
@@ -957,6 +964,10 @@
obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget();
}
+ private void notifySessionBadgingChanged(int sessionId, int userId) {
+ obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();
+ }
+
private void notifySessionOpened(int sessionId, int userId) {
obtainMessage(MSG_SESSION_OPENED, sessionId, userId).sendToTarget();
}
@@ -1006,14 +1017,19 @@
}
class InternalCallback {
- public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
- mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
+ public void onSessionBadgingChanged(PackageInstallerSession session) {
+ mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
+ writeSessionsAsync();
}
public void onSessionOpened(PackageInstallerSession session) {
mCallbacks.notifySessionOpened(session.sessionId, session.userId);
}
+ public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
+ mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
+ }
+
public void onSessionClosed(PackageInstallerSession session) {
mCallbacks.notifySessionClosed(session.sessionId, session.userId);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5264fc4..85ff54e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -294,7 +294,9 @@
}
private void computeProgressLocked() {
- mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f);
+ if (mProgress <= 0.8f) {
+ mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f);
+ }
}
private void maybePublishProgress() {
@@ -485,7 +487,8 @@
}
// TODO: surface more granular state from dexopt
- mCallback.onSessionProgressChanged(this, 0.9f);
+ mProgress = 0.9f;
+ maybePublishProgress();
// Unpack native libraries
extractNativeLibraries(mResolvedStageDir, params.abiOverride);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a4d4914..d2a627e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11746,47 +11746,6 @@
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) {