Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ec9bad20 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Allow badging updates to install sessions.

For the system restore use-case, an installer may need to enqueue
their sessions quickly before badging details, like icons, have been
downloaded.  This change relaxes to allow an installer to update
their session badging after the session has been created.  Notify
observers when badging changes.

Rename callback registration methods to match style guide.  Relax
constraint that observers are home app.  Fix bug around internal
progress reporting.

Bug: 17376797, 17389236, 17334199
Change-Id: I5fb88508baea2f08e89a1504fcf5ef972afad4a7
parent 5ef33984
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -8637,15 +8637,17 @@ package android.content.pm {
  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";
@@ -8677,6 +8679,7 @@ package android.content.pm {
  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);
+7 −0
Original line number Diff line number Diff line
@@ -22,14 +22,21 @@ import android.content.pm.IPackageInstallerSession;
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);

+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.content.pm;
/** {@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);
+70 −15
Original line number Diff line number Diff line
@@ -312,6 +312,32 @@ public class PackageInstaller {
        }
    }

    /**
     * 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 @@ public class PackageInstaller {
    }

    /**
     * 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 @@ public class PackageInstaller {

    /**
     * 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();
@@ -405,6 +429,12 @@ public class PackageInstaller {
         */
        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 @@ public class PackageInstaller {
    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 @@ public class PackageInstaller {
                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;
@@ -476,6 +510,11 @@ public class PackageInstaller {
            mHandler.obtainMessage(MSG_SESSION_CREATED, sessionId, 0).sendToTarget();
        }

        @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 @@ public class PackageInstaller {
        }
    }

    /** {@hide} */
    @Deprecated
    public void addSessionCallback(@NonNull SessionCallback callback) {
        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 addSessionCallback(@NonNull SessionCallback callback) {
        addSessionCallback(callback, new Handler());
    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. To succeed, the caller
     * must be the current home app.
     * 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 @@ public class PackageInstaller {
        }
    }

    /** {@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();
+45 −29
Original line number Diff line number Diff line
@@ -47,12 +47,12 @@ import android.content.pm.IPackageInstaller;
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;
@@ -580,6 +580,30 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
        return sessionId;
    }

    @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) {
@@ -681,9 +705,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
    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 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
    @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 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
    @Override
    public void registerCallback(IPackageInstallerCallback callback, int userId) {
        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "registerCallback");
        enforceCallerCanReadSessions();

        mCallbacks.register(callback, userId);
    }

@@ -787,21 +805,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
        }
    }

    /**
     * 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub {

    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 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
                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 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
            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 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
    }

    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);
        }
Loading