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

Commit 24912ef3 authored by Pinyao Ting's avatar Pinyao Ting
Browse files

Integrate LauncherApps API with AppSearch

- getShortcutIntent and startShortcut now additionally checks AppSearch
if specifed shortcuts cannot be found in memory.
- getShortcutIconFd and getShortcutIconUri now additionally checks
AppSearch if specified shortcuts cannot be found in memory.
- getShortcutIconResId is unchanged since the api is deprecated.
- Introduced ShortcutQuery.FLAG_GET_PERSISTED_DATA, when used in
getShortcuts, it additionally performs the check in AppSearch.

Bug: 151359749
Test: atest CtsShortcutManagerTestCases
Change-Id: I0e56e3d0261fc0a3cf0ab4c5decbbd59ac3d7d29
parent 00276585
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2839,6 +2839,7 @@ package android.content.pm {
  }
  public static class LauncherApps.ShortcutQuery {
    field public static final int FLAG_GET_PERSISTED_DATA = 4096; // 0x1000
    field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
  }
+4 −0
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.os.ParcelFileDescriptor;

import com.android.internal.infra.AndroidFuture;

import java.util.List;

/**
@@ -73,6 +75,8 @@ interface ILauncherApps {

    ParceledListSlice getShortcuts(String callingPackage, in ShortcutQueryWrapper query,
            in UserHandle user);
    void getShortcutsAsync(String callingPackage, in ShortcutQueryWrapper query,
            in UserHandle user, in AndroidFuture<List<ShortcutInfo>> cb);
    void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
            in UserHandle user);
    boolean startShortcut(String callingPackage, String packageName, String featureId, String id,
+35 −2
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ import android.util.Log;
import android.util.Pair;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.function.pooled.PooledLambda;

import java.io.FileNotFoundException;
@@ -84,6 +85,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;

/**
@@ -439,6 +441,17 @@ public class LauncherApps {
         */
        public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;

        /**
         * Includes shortcuts from persistence layer in the search result.
         *
         * <p>The caller should make the query on a worker thread since accessing persistence layer
         * is considered asynchronous.
         *
         * @hide
         */
        @SystemApi
        public static final int FLAG_GET_PERSISTED_DATA = 1 << 12;

        /**
         * Populate the persons field in the result. See {@link ShortcutInfo#getPersons()}.
         *
@@ -459,6 +472,7 @@ public class LauncherApps {
                FLAG_MATCH_PINNED_BY_ANY_LAUNCHER,
                FLAG_GET_KEY_FIELDS_ONLY,
                FLAG_GET_PERSONS_DATA,
                FLAG_GET_PERSISTED_DATA
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface QueryFlags {}
@@ -1137,6 +1151,9 @@ public class LauncherApps {
            @NonNull UserHandle user) {
        logErrorForInvalidProfileAccess(user);
        try {
            if ((query.mQueryFlags & ShortcutQuery.FLAG_GET_PERSISTED_DATA) != 0) {
                return getShortcutsBlocked(query, user);
            }
            // Note this is the only case we need to update the disabled message for shortcuts
            // that weren't restored.
            // The restore problem messages are only shown by the user, and publishers will never
@@ -1151,6 +1168,22 @@ public class LauncherApps {
        }
    }

    private List<ShortcutInfo> getShortcutsBlocked(@NonNull ShortcutQuery query,
            @NonNull UserHandle user) {
        logErrorForInvalidProfileAccess(user);
        final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
        future.thenApply(this::maybeUpdateDisabledMessage);
        try {
            mService.getShortcutsAsync(mContext.getPackageName(),
                            new ShortcutQueryWrapper(query), user, future);
            return future.get();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @hide // No longer used.  Use getShortcuts() instead.  Kept for unit tests.
     */
+38 −0
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import android.content.pm.LauncherApps.ShortcutQuery;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;

import com.android.internal.infra.AndroidFuture;

import java.util.List;

/**
@@ -50,6 +52,19 @@ public abstract class ShortcutServiceInternal {
            @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
            @ShortcutQuery.QueryFlags int flags, int userId, int callingPid, int callingUid);

    /**
     * Retrieves shortcuts asynchronously. Query will go through persistence layer (thus making the
     * call async) if querying by shortcutIds in a specific package; otherwise it's effectively the
     * same as calling {@link #getShortcuts}.
     */
    public abstract void
            getShortcutsAsync(int launcherUserId,
            @NonNull String callingPackage, long changedSince,
            @Nullable String packageName, @Nullable List<String> shortcutIds,
            @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
            @ShortcutQuery.QueryFlags int flags, int userId, int callingPid, int callingUid,
            AndroidFuture<List<ShortcutInfo>> cb);

    public abstract boolean
            isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
            @NonNull String packageName, @NonNull String id, int userId);
@@ -63,6 +78,14 @@ public abstract class ShortcutServiceInternal {
            @NonNull String packageName, @NonNull String shortcutId, int userId,
            int callingPid, int callingUid);

    /**
     * Retrieves the intents from a specified shortcut asynchronously.
     */
    public abstract void createShortcutIntentsAsync(
            int launcherUserId, @NonNull String callingPackage,
            @NonNull String packageName, @NonNull String shortcutId, int userId,
            int callingPid, int callingUid, @NonNull AndroidFuture<Intent[]> cb);

    public abstract void addListener(@NonNull ShortcutChangeListener listener);

    public abstract void addShortcutChangeCallback(
@@ -82,6 +105,13 @@ public abstract class ShortcutServiceInternal {
            @NonNull String callingPackage,
            @NonNull String packageName, @NonNull String shortcutId, int userId);

    /**
     * Retrieves a file descriptor from the icon in a specified shortcut asynchronously.
     */
    public abstract void getShortcutIconFdAsync(int launcherUserId, @NonNull String callingPackage,
            @NonNull String packageName, @NonNull String shortcutId, int userId,
            @NonNull AndroidFuture<ParcelFileDescriptor> cb);

    public abstract boolean hasShortcutHostPermission(int launcherUserId,
            @NonNull String callingPackage, int callingPid, int callingUid);

@@ -117,6 +147,14 @@ public abstract class ShortcutServiceInternal {
    public abstract String getShortcutIconUri(int launcherUserId, @NonNull String launcherPackage,
            @NonNull String packageName, @NonNull String shortcutId, int userId);

    /**
     * Retrieves the icon Uri of the shortcut asynchronously, and grants Uri read permission to the
     * caller.
     */
    public abstract void getShortcutIconUriAsync(int launcherUserId,
            @NonNull String launcherPackage, @NonNull String packageName,
            @NonNull String shortcutId, int userId, @NonNull AndroidFuture<String> cb);

    public abstract boolean isSharingShortcut(int callingUserId, @NonNull String callingPackage,
            @NonNull String packageName, @NonNull String shortcutId, int userId,
            @NonNull IntentFilter filter);
+72 −10
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -103,6 +104,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;

/**
 * Service that manages requests and callbacks for launchers that support
@@ -728,9 +730,16 @@ public class LauncherAppsService extends SystemService {
                return null;
            }

            final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
                    getCallingUserId(), callingPackage, packageName, shortcutId,
                    user.getIdentifier(), injectBinderCallingPid(), injectBinderCallingUid());
            final AndroidFuture<Intent[]> ret = new AndroidFuture<>();
            Intent[] intents;
            mShortcutServiceInternal.createShortcutIntentsAsync(getCallingUserId(),
                    callingPackage, packageName, shortcutId, user.getIdentifier(),
                    injectBinderCallingPid(), injectBinderCallingUid(), ret);
            try {
                intents = ret.get();
            } catch (InterruptedException | ExecutionException e) {
                return null;
            }
            if (intents == null || intents.length == 0) {
                return null;
            }
@@ -900,6 +909,40 @@ public class LauncherAppsService extends SystemService {
                            injectBinderCallingPid(), injectBinderCallingUid()));
        }

        @Override
        public void getShortcutsAsync(@NonNull final String callingPackage,
                @NonNull final ShortcutQueryWrapper query, @NonNull final UserHandle targetUser,
                @NonNull final AndroidFuture<List<ShortcutInfo>> cb) {
            ensureShortcutPermission(callingPackage);
            if (!canAccessProfile(targetUser.getIdentifier(), "Cannot get shortcuts")) {
                cb.complete(Collections.EMPTY_LIST);
                return;
            }

            final long changedSince = query.getChangedSince();
            final String packageName = query.getPackage();
            final List<String> shortcutIds = query.getShortcutIds();
            final List<LocusId> locusIds = query.getLocusIds();
            final ComponentName componentName = query.getActivity();
            final int flags = query.getQueryFlags();
            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");
            }
            if ((query.getQueryFlags() & ShortcutQuery.FLAG_GET_PERSONS_DATA) != 0) {
                ensureStrictAccessShortcutsPermission(callingPackage);
            }

            mShortcutServiceInternal.getShortcutsAsync(getCallingUserId(),
                    callingPackage, changedSince, packageName, shortcutIds, locusIds,
                    componentName, flags, targetUser.getIdentifier(),
                    injectBinderCallingPid(), injectBinderCallingUid(), cb);
        }

        @Override
        public void registerShortcutChangeCallback(@NonNull final String callingPackage,
                @NonNull final ShortcutQueryWrapper query,
@@ -991,8 +1034,14 @@ public class LauncherAppsService extends SystemService {
                return null;
            }

            return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
                    callingPackage, packageName, id, targetUserId);
            final AndroidFuture<ParcelFileDescriptor> ret = new AndroidFuture<>();
            mShortcutServiceInternal.getShortcutIconFdAsync(getCallingUserId(),
                    callingPackage, packageName, id, targetUserId, ret);
            try {
                return ret.get();
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
@@ -1003,8 +1052,14 @@ public class LauncherAppsService extends SystemService {
                return null;
            }

            return mShortcutServiceInternal.getShortcutIconUri(getCallingUserId(), callingPackage,
                    packageName, shortcutId, userId);
            final AndroidFuture<String> ret = new AndroidFuture<>();
            mShortcutServiceInternal.getShortcutIconUriAsync(getCallingUserId(), callingPackage,
                    packageName, shortcutId, userId, ret);
            try {
                return ret.get();
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
@@ -1037,9 +1092,16 @@ public class LauncherAppsService extends SystemService {
                ensureShortcutPermission(callerUid, callerPid, callingPackage);
            }

            final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
                    callingUserId, callingPackage, packageName, shortcutId, targetUserId,
                    callerPid, callerUid);
            final AndroidFuture<Intent[]> ret = new AndroidFuture<>();
            Intent[] intents;
            mShortcutServiceInternal.createShortcutIntentsAsync(getCallingUserId(), callingPackage,
                    packageName, shortcutId, targetUserId,
                    injectBinderCallingPid(), injectBinderCallingUid(), ret);
            try {
                intents = ret.get();
            } catch (InterruptedException | ExecutionException e) {
                return false;
            }
            if (intents == null || intents.length == 0) {
                return false;
            }
Loading