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

Commit 876a4ab9 authored by Danning Chen's avatar Danning Chen Committed by Android (Google) Code Review
Browse files

Merge "Adds ShortcutChangeCallback in LauncherApps - Service side"

parents 25a92a3d b754b7ac
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -95,9 +95,9 @@ interface ILauncherApps {

    void registerShortcutChangeCallback(String callingPackage, long changedSince,
            String packageName, in List shortcutIds, in List<LocusId> locusIds,
            in ComponentName componentName, int flags, in IShortcutChangeCallback callback,
            int callbackId);
    void unregisterShortcutChangeCallback(String callingPackage, int callbackId);
            in ComponentName componentName, int flags, in IShortcutChangeCallback callback);
    void unregisterShortcutChangeCallback(String callingPackage,
            in IShortcutChangeCallback callback);

    void cacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
            in UserHandle user);
+10 −13
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.content.pm;

import static android.Manifest.permission;

import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -161,7 +162,7 @@ public class LauncherApps {
    private final List<CallbackMessageHandler> mCallbacks = new ArrayList<>();
    private final List<SessionCallbackDelegate> mDelegates = new ArrayList<>();

    private final Map<Integer, Pair<Executor, ShortcutChangeCallback>>
    private final Map<ShortcutChangeCallback, Pair<Executor, IShortcutChangeCallback>>
            mShortcutChangeCallbacks = new HashMap<>();

    /**
@@ -549,8 +550,8 @@ public class LauncherApps {
            android.content.pm.IShortcutChangeCallback.Stub {
        private final WeakReference<Pair<Executor, ShortcutChangeCallback>> mRemoteReferences;

        ShortcutChangeCallbackProxy(Pair<Executor, ShortcutChangeCallback> remoteReferences) {
            mRemoteReferences = new WeakReference<>(remoteReferences);
        ShortcutChangeCallbackProxy(Executor executor, ShortcutChangeCallback callback) {
            mRemoteReferences = new WeakReference<>(new Pair<>(executor, callback));
        }

        @Override
@@ -1753,14 +1754,12 @@ public class LauncherApps {
        Objects.requireNonNull(executor, "Executor cannot be null");

        synchronized (mShortcutChangeCallbacks) {
            final int callbackId = callback.hashCode();
            final Pair<Executor, ShortcutChangeCallback> state = new Pair<>(executor, callback);
            mShortcutChangeCallbacks.put(callbackId, state);
            IShortcutChangeCallback proxy = new ShortcutChangeCallbackProxy(executor, callback);
            mShortcutChangeCallbacks.put(callback, new Pair<>(executor, proxy));
            try {
                mService.registerShortcutChangeCallback(mContext.getPackageName(),
                        query.mChangedSince, query.mPackage, query.mShortcutIds, query.mLocusIds,
                        query.mActivity, query.mQueryFlags, new ShortcutChangeCallbackProxy(state),
                        callbackId);
                        query.mActivity, query.mQueryFlags, proxy);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
@@ -1779,12 +1778,10 @@ public class LauncherApps {
        Objects.requireNonNull(callback, "Callback cannot be null");

        synchronized (mShortcutChangeCallbacks) {
            final int callbackId = callback.hashCode();
            if (mShortcutChangeCallbacks.containsKey(callbackId)) {
                mShortcutChangeCallbacks.remove(callbackId);
            if (mShortcutChangeCallbacks.containsKey(callback)) {
                IShortcutChangeCallback proxy = mShortcutChangeCallbacks.remove(callback).second;
                try {
                    mService.unregisterShortcutChangeCallback(mContext.getPackageName(),
                            callbackId);
                    mService.unregisterShortcutChangeCallback(mContext.getPackageName(), proxy);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
+3 −0
Original line number Diff line number Diff line
@@ -65,6 +65,9 @@ public abstract class ShortcutServiceInternal {

    public abstract void addListener(@NonNull ShortcutChangeListener listener);

    public abstract void addShortcutChangeCallback(
            @NonNull LauncherApps.ShortcutChangeCallback callback);

    public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
            @NonNull String packageName, @NonNull String shortcutId, int userId);

+180 −3
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -134,6 +135,8 @@ public class LauncherAppsService extends SystemService {

        private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();

        private final ShortcutChangeHandler mShortcutChangeHandler;

        private final Handler mCallbackHandler;

        private PackageInstallerService mPackageInstallerService;
@@ -153,6 +156,8 @@ public class LauncherAppsService extends SystemService {
            mShortcutServiceInternal = Objects.requireNonNull(
                    LocalServices.getService(ShortcutServiceInternal.class));
            mShortcutServiceInternal.addListener(mPackageMonitor);
            mShortcutChangeHandler = new ShortcutChangeHandler(mUserManagerInternal);
            mShortcutServiceInternal.addShortcutChangeCallback(mShortcutChangeHandler);
            mCallbackHandler = BackgroundThread.getHandler();
            mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
        }
@@ -720,12 +725,37 @@ public class LauncherAppsService extends SystemService {
        @Override
        public void registerShortcutChangeCallback(String callingPackage, long changedSince,
                String packageName, List shortcutIds, List<LocusId> locusIds,
                ComponentName componentName, int flags, IShortcutChangeCallback callback,
                int callbackId) {
                ComponentName componentName, int flags, IShortcutChangeCallback callback) {
            ensureShortcutPermission(callingPackage);

            if (shortcutIds != null && packageName == null) {
                throw new IllegalArgumentException(
                        "To query by shortcut ID, package name must also be set");
            }
            if (locusIds != null && packageName == null) {
                throw new IllegalArgumentException(
                        "To query by locus ID, package name must also be set");
            }

            UserHandle user = UserHandle.of(injectCallingUserId());
            if (mContext.checkCallingOrSelfPermission(
                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
                    == PackageManager.PERMISSION_GRANTED) {
                user = null;
            }

            // TODO: When ShortcutQueryWrapper (ag/10323729) is available, pass that directly.
            ShortcutChangeHandler.QueryInfo query = new ShortcutChangeHandler.QueryInfo(
                    changedSince, packageName, shortcutIds, locusIds, componentName, flags, user);
            mShortcutChangeHandler.addShortcutChangeCallback(callback, query);
        }

        @Override
        public void unregisterShortcutChangeCallback(String callingPackage, int callbackId) {
        public void unregisterShortcutChangeCallback(String callingPackage,
                IShortcutChangeCallback callback) {
            ensureShortcutPermission(callingPackage);

            mShortcutChangeHandler.removeShortcutChangeCallback(callback);
        }

        @Override
@@ -1005,6 +1035,153 @@ public class LauncherAppsService extends SystemService {
            mCallbackHandler.post(r);
        }

        public static class ShortcutChangeHandler implements LauncherApps.ShortcutChangeCallback {

            static class QueryInfo {
                final long mChangedSince;
                final String mPackage;
                final List<String> mShortcutIds;
                final List<LocusId> mLocusIds;
                final ComponentName mActivity;
                final int mQueryFlags;
                final UserHandle mCallbackUser;

                QueryInfo(long changedSince, String packageName, List<String> shortcutIds,
                        List<LocusId> locusIds, ComponentName activity, int flags,
                        UserHandle callbackUser) {
                    mChangedSince = changedSince;
                    mPackage = packageName;
                    mShortcutIds = shortcutIds;
                    mLocusIds = locusIds;
                    mActivity = activity;
                    mQueryFlags = flags;
                    mCallbackUser = callbackUser;
                }
            }

            private final UserManagerInternal mUserManagerInternal;

            ShortcutChangeHandler(UserManagerInternal userManager) {
                mUserManagerInternal = userManager;
            }

            private final RemoteCallbackList<IShortcutChangeCallback> mCallbacks =
                    new RemoteCallbackList<>();

            public synchronized void addShortcutChangeCallback(IShortcutChangeCallback callback,
                    QueryInfo query) {
                mCallbacks.unregister(callback);
                mCallbacks.register(callback, query);
            }

            public synchronized void removeShortcutChangeCallback(
                    IShortcutChangeCallback callback) {
                mCallbacks.unregister(callback);
            }

            @Override
            public void onShortcutsAddedOrUpdated(String packageName, List<ShortcutInfo> shortcuts,
                    UserHandle user) {
                onShortcutEvent(packageName, shortcuts, user, false);
            }

            @Override
            public void onShortcutsRemoved(String packageName, List<ShortcutInfo> shortcuts,
                    UserHandle user) {
                onShortcutEvent(packageName, shortcuts, user, true);
            }

            private void onShortcutEvent(String packageName,
                    List<ShortcutInfo> shortcuts, UserHandle user, boolean shortcutsRemoved) {
                int count = mCallbacks.beginBroadcast();

                for (int i = 0; i < count; i++) {
                    final IShortcutChangeCallback callback = mCallbacks.getBroadcastItem(i);
                    final QueryInfo query = (QueryInfo) mCallbacks.getBroadcastCookie(i);

                    if (query.mCallbackUser != null && !hasUserAccess(query.mCallbackUser, user)) {
                        // Callback owner does not have access to the shortcuts' user.
                        continue;
                    }

                    // Filter the list by query, if any matches exists, send via callback.
                    List<ShortcutInfo> matchedList =
                            filterShortcutsByQuery(packageName, shortcuts, query);
                    if (!CollectionUtils.isEmpty(matchedList)) {
                        try {
                            if (shortcutsRemoved) {
                                callback.onShortcutsRemoved(packageName, matchedList, user);
                            } else {
                                callback.onShortcutsAddedOrUpdated(packageName, matchedList, user);
                            }
                        } catch (RemoteException e) {
                            // The RemoteCallbackList will take care of removing the dead object.
                        }
                    }
                }

                mCallbacks.finishBroadcast();
            }

            public static List<ShortcutInfo> filterShortcutsByQuery(String packageName,
                    List<ShortcutInfo> shortcuts, QueryInfo query) {
                if (query.mPackage != null && query.mPackage != packageName) {
                    return null;
                }

                List<ShortcutInfo> matches = new ArrayList<>();

                final boolean matchDynamic =
                        (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) != 0;
                final boolean matchPinned =
                        (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0;
                final boolean matchManifest =
                        (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0;
                final boolean matchCached =
                        (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_CACHED) != 0;
                final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0)
                        | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
                        | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
                        | (matchCached ? ShortcutInfo.FLAG_CACHED : 0);

                for (int i = 0; i < shortcuts.size(); i++) {
                    final ShortcutInfo si = shortcuts.get(i);

                    if (query.mActivity != null && !query.mActivity.equals(si.getActivity())) {
                        continue;
                    }

                    if (query.mChangedSince != 0
                            && query.mChangedSince > si.getLastChangedTimestamp()) {
                        continue;
                    }

                    if (query.mShortcutIds != null && !query.mShortcutIds.contains(si.getId())) {
                        continue;
                    }

                    if (query.mLocusIds != null && !query.mLocusIds.contains(si.getLocusId())) {
                        continue;
                    }

                    if ((shortcutFlags & si.getFlags()) != 0) {
                        matches.add(si);
                    }
                }

                return matches;
            }

            private boolean hasUserAccess(UserHandle callbackUser, UserHandle shortcutUser) {
                final int callbackUserId = callbackUser.getIdentifier();
                final int shortcutUserId = shortcutUser.getIdentifier();

                if (shortcutUser == callbackUser) return true;
                return mUserManagerInternal.isProfileAccessible(callbackUserId, shortcutUserId,
                        null, false);
            }
        }

        private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {

            // TODO Simplify with lambdas.
+25 −9
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.ShortcutService.DumpFilter;
@@ -365,9 +366,12 @@ class ShortcutPackage extends ShortcutPackageItem {

    /**
     * Remove all shortcuts that aren't pinned, cached nor dynamic.
     *
     * @return List of removed shortcuts.
     */
    private void removeOrphans() {
    private List<ShortcutInfo> removeOrphans() {
        ArrayList<String> removeList = null; // Lazily initialize.
        List<ShortcutInfo> removedShortcuts = null;

        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
            final ShortcutInfo si = mShortcuts.valueAt(i);
@@ -376,20 +380,26 @@ class ShortcutPackage extends ShortcutPackageItem {

            if (removeList == null) {
                removeList = new ArrayList<>();
                removedShortcuts = new ArrayList<>();
            }
            removeList.add(si.getId());
            removedShortcuts.add(si);
        }
        if (removeList != null) {
            for (int i = removeList.size() - 1; i >= 0; i--) {
                forceDeleteShortcutInner(removeList.get(i));
            }
        }

        return removedShortcuts;
    }

    /**
     * Remove all dynamic shortcuts.
     *
     * @return List of shortcuts that actually got removed.
     */
    public void deleteAllDynamicShortcuts(boolean ignoreInvisible) {
    public List<ShortcutInfo> deleteAllDynamicShortcuts(boolean ignoreInvisible) {
        final long now = mShortcutUser.mService.injectCurrentTimeMillis();

        boolean changed = false;
@@ -404,8 +414,9 @@ class ShortcutPackage extends ShortcutPackageItem {
            }
        }
        if (changed) {
            removeOrphans();
            return removeOrphans();
        }
        return null;
    }

    /**
@@ -1028,7 +1039,8 @@ class ShortcutPackage extends ShortcutPackageItem {
        s.verifyStates();

        // This will send a notification to the launcher, and also save .
        s.packageShortcutsChanged(getPackageName(), getPackageUserId());
        // TODO: List changed and removed manifest shortcuts and pass to packageShortcutsChanged()
        s.packageShortcutsChanged(getPackageName(), getPackageUserId(), null, null);
        return true; // true means changed.
    }

@@ -1299,15 +1311,14 @@ class ShortcutPackage extends ShortcutPackageItem {
     */
    public void resolveResourceStrings() {
        final ShortcutService s = mShortcutUser.mService;
        boolean changed = false;

        List<ShortcutInfo> changedShortcuts = null;

        Resources publisherRes = null;
        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
            final ShortcutInfo si = mShortcuts.valueAt(i);

            if (si.hasStringResources()) {
                changed = true;

                if (publisherRes == null) {
                    publisherRes = getPackageResources();
                    if (publisherRes == null) {
@@ -1317,10 +1328,15 @@ class ShortcutPackage extends ShortcutPackageItem {

                si.resolveResourceStrings(publisherRes);
                si.setTimestamp(s.injectCurrentTimeMillis());

                if (changedShortcuts == null) {
                    changedShortcuts = new ArrayList<>(1);
                }
                changedShortcuts.add(si);
            }
        if (changed) {
            s.packageShortcutsChanged(getPackageName(), getPackageUserId());
        }
        if (!CollectionUtils.isEmpty(changedShortcuts)) {
            s.packageShortcutsChanged(getPackageName(), getPackageUserId(), changedShortcuts, null);
        }
    }

Loading