diff options
7 files changed, 191 insertions, 44 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index dd606774b770..666d0aa3954a 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9187,6 +9187,14 @@ package android.app.blob { } +package android.app.contextualsearch { + + @FlaggedApi("android.app.contextualsearch.flags.self_invocation") public final class ContextualSearchManager { + method @FlaggedApi("android.app.contextualsearch.flags.self_invocation") public void startContextualSearch(); + } + +} + package android.app.jank { @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public final class AppJankStats { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index ab824119d643..354c16b889d8 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -7,7 +7,7 @@ package android { field public static final String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO"; field public static final String ACCESS_BROADCAST_RESPONSE_STATS = "android.permission.ACCESS_BROADCAST_RESPONSE_STATS"; field public static final String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM"; - field @FlaggedApi("android.app.contextualsearch.flags.enable_service") public static final String ACCESS_CONTEXTUAL_SEARCH = "android.permission.ACCESS_CONTEXTUAL_SEARCH"; + field public static final String ACCESS_CONTEXTUAL_SEARCH = "android.permission.ACCESS_CONTEXTUAL_SEARCH"; field public static final String ACCESS_CONTEXT_HUB = "android.permission.ACCESS_CONTEXT_HUB"; field public static final String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES"; field @FlaggedApi("android.permission.flags.fine_power_monitor_permission") public static final String ACCESS_FINE_POWER_MONITORS = "android.permission.ACCESS_FINE_POWER_MONITORS"; @@ -2231,7 +2231,7 @@ package android.app.contextualsearch { field @NonNull public static final android.os.Parcelable.Creator<android.app.contextualsearch.CallbackToken> CREATOR; } - public final class ContextualSearchManager { + @FlaggedApi("android.app.contextualsearch.flags.self_invocation") public final class ContextualSearchManager { method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXTUAL_SEARCH) public void startContextualSearch(int); field public static final String ACTION_LAUNCH_CONTEXTUAL_SEARCH = "android.app.contextualsearch.action.LAUNCH_CONTEXTUAL_SEARCH"; field public static final int ENTRYPOINT_LONG_PRESS_HOME = 2; // 0x2 diff --git a/core/java/android/app/contextualsearch/ContextualSearchManager.java b/core/java/android/app/contextualsearch/ContextualSearchManager.java index 2ce431dcb32d..4e5fa6bac951 100644 --- a/core/java/android/app/contextualsearch/ContextualSearchManager.java +++ b/core/java/android/app/contextualsearch/ContextualSearchManager.java @@ -32,6 +32,9 @@ import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; /** * {@link ContextualSearchManager} is a system service to facilitate contextual search experience on @@ -39,10 +42,8 @@ import java.lang.annotation.RetentionPolicy; * <p> * This class lets a caller start contextual search by calling {@link #startContextualSearch} * method. - * - * @hide */ -@SystemApi +@FlaggedApi(Flags.FLAG_SELF_INVOCATION) public final class ContextualSearchManager { /** @@ -50,7 +51,9 @@ public final class ContextualSearchManager { * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH. * * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH + * @hide */ + @SystemApi public static final String EXTRA_ENTRYPOINT = "android.app.contextualsearch.extra.ENTRYPOINT"; @@ -60,7 +63,9 @@ public final class ContextualSearchManager { * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH. * * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH + * @hide */ + @SystemApi public static final String EXTRA_FLAG_SECURE_FOUND = "android.app.contextualsearch.extra.FLAG_SECURE_FOUND"; @@ -69,7 +74,9 @@ public final class ContextualSearchManager { * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH. * * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH + * @hide */ + @SystemApi public static final String EXTRA_SCREENSHOT = "android.app.contextualsearch.extra.SCREENSHOT"; @@ -79,7 +86,9 @@ public final class ContextualSearchManager { * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH. * * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH + * @hide */ + @SystemApi public static final String EXTRA_IS_MANAGED_PROFILE_VISIBLE = "android.app.contextualsearch.extra.IS_MANAGED_PROFILE_VISIBLE"; @@ -89,7 +98,9 @@ public final class ContextualSearchManager { * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH. * * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH + * @hide */ + @SystemApi public static final String EXTRA_VISIBLE_PACKAGE_NAMES = "android.app.contextualsearch.extra.VISIBLE_PACKAGE_NAMES"; @@ -98,10 +109,9 @@ public final class ContextualSearchManager { * {@link SystemClock#uptimeMillis()}. * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH. * - * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH - * * TODO: un-hide in W * + * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH * @hide */ public static final String EXTRA_INVOCATION_TIME_MS = @@ -113,7 +123,9 @@ public final class ContextualSearchManager { * Only supposed to be used with ACTION_LAUNCH_CONTEXTUAL_SEARCH. * * @see #ACTION_LAUNCH_CONTEXTUAL_SEARCH + * @hide */ + @SystemApi public static final String EXTRA_TOKEN = "android.app.contextualsearch.extra.TOKEN"; /** @@ -132,7 +144,10 @@ public final class ContextualSearchManager { * experience must add this intent filter action to the activity it wants to be launched. * <br> * <b>Note</b> This activity must not be exported. + * + * @hide */ + @SystemApi public static final String ACTION_LAUNCH_CONTEXTUAL_SEARCH = "android.app.contextualsearch.action.LAUNCH_CONTEXTUAL_SEARCH"; @@ -144,23 +159,63 @@ public final class ContextualSearchManager { public static final String FEATURE_CONTEXTUAL_SEARCH = "com.google.android.feature.CONTEXTUAL_SEARCH"; - /** Entrypoint to be used when a user long presses on the nav handle. */ + /** + * Entrypoint to be used when a user long presses on the nav handle. + * + * @hide + */ + @SystemApi public static final int ENTRYPOINT_LONG_PRESS_NAV_HANDLE = 1; - /** Entrypoint to be used when a user long presses on the home button. */ + + /** Entrypoint to be used when a user long presses on the home button. + * + * @hide + */ + @SystemApi public static final int ENTRYPOINT_LONG_PRESS_HOME = 2; - /** Entrypoint to be used when a user long presses on the overview button. */ + + /** Entrypoint to be used when a user long presses on the overview button. + * + * @hide + */ + @SystemApi public static final int ENTRYPOINT_LONG_PRESS_OVERVIEW = 3; - /** Entrypoint to be used when a user presses the action button in overview. */ + + /** + * Entrypoint to be used when a user presses the action button in overview. + * + * @hide + */ + @SystemApi public static final int ENTRYPOINT_OVERVIEW_ACTION = 4; - /** Entrypoint to be used when a user presses the context menu button in overview. */ + + /** + * Entrypoint to be used when a user presses the context menu button in overview. + * + * @hide + */ + @SystemApi public static final int ENTRYPOINT_OVERVIEW_MENU = 5; - /** Entrypoint to be used by system actions like TalkBack, Accessibility etc. */ + + /** + * Entrypoint to be used by system actions like TalkBack, Accessibility etc. + * + * @hide + */ + @SystemApi public static final int ENTRYPOINT_SYSTEM_ACTION = 9; - /** Entrypoint to be used when a user long presses on the meta key. */ + + /** + * Entrypoint to be used when a user long presses on the meta key. + * + * @hide + */ + @SystemApi public static final int ENTRYPOINT_LONG_PRESS_META = 10; + /** * The {@link Entrypoint} annotation is used to standardize the entrypoints supported by - * {@link #startContextualSearch} method. + * {@link #startContextualSearch(int entrypoint)} method. * * @hide */ @@ -174,8 +229,18 @@ public final class ContextualSearchManager { ENTRYPOINT_LONG_PRESS_META }) @Retention(RetentionPolicy.SOURCE) - public @interface Entrypoint { - } + public @interface Entrypoint {} + + private static final Set<Integer> VALID_ENTRYPOINT_VALUES = new HashSet<>(Arrays.asList( + ENTRYPOINT_LONG_PRESS_NAV_HANDLE, + ENTRYPOINT_LONG_PRESS_HOME, + ENTRYPOINT_LONG_PRESS_OVERVIEW, + ENTRYPOINT_OVERVIEW_ACTION, + ENTRYPOINT_OVERVIEW_MENU, + ENTRYPOINT_SYSTEM_ACTION, + ENTRYPOINT_LONG_PRESS_META + )); + private static final String TAG = ContextualSearchManager.class.getSimpleName(); private static final boolean DEBUG = false; @@ -189,7 +254,7 @@ public final class ContextualSearchManager { } /** - * Used to start contextual search. + * Used to start contextual search for a given system entrypoint. * <p> * When {@link #startContextualSearch} is called, the system server does the following: * <ul> @@ -202,9 +267,15 @@ public final class ContextualSearchManager { * </p> * * @param entrypoint the invocation entrypoint + * + * @hide */ @RequiresPermission(ACCESS_CONTEXTUAL_SEARCH) + @SystemApi public void startContextualSearch(@Entrypoint int entrypoint) { + if (!VALID_ENTRYPOINT_VALUES.contains(entrypoint)) { + throw new IllegalArgumentException("Invalid entrypoint: " + entrypoint); + } if (DEBUG) Log.d(TAG, "startContextualSearch for entrypoint: " + entrypoint); try { mService.startContextualSearch(entrypoint); @@ -213,4 +284,22 @@ public final class ContextualSearchManager { e.rethrowFromSystemServer(); } } + + /** + * Used to start contextual search from within an app. + * + * <p>System apps should use the available System APIs rather than this method. + * + * @throws SecurityException if the caller does not have a foreground Activity. + */ + @FlaggedApi(Flags.FLAG_SELF_INVOCATION) + public void startContextualSearch() { + if (DEBUG) Log.d(TAG, "startContextualSearch from app"); + try { + mService.startContextualSearchForForegroundApp(); + } catch (RemoteException e) { + if (DEBUG) Log.d(TAG, "Failed to startContextualSearch", e); + e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/contextualsearch/IContextualSearchManager.aidl b/core/java/android/app/contextualsearch/IContextualSearchManager.aidl index 9b0b8b775971..8789daab3afe 100644 --- a/core/java/android/app/contextualsearch/IContextualSearchManager.aidl +++ b/core/java/android/app/contextualsearch/IContextualSearchManager.aidl @@ -4,7 +4,8 @@ import android.app.contextualsearch.IContextualSearchCallback; /** * @hide */ -oneway interface IContextualSearchManager { - void startContextualSearch(int entrypoint); - void getContextualSearchState(in IBinder token, in IContextualSearchCallback callback); +interface IContextualSearchManager { + void startContextualSearchForForegroundApp(); + oneway void startContextualSearch(int entrypoint); + oneway void getContextualSearchState(in IBinder token, in IContextualSearchCallback callback); } diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig index d81ec1e8b883..bc1f7cea7fce 100644 --- a/core/java/android/app/contextualsearch/flags.aconfig +++ b/core/java/android/app/contextualsearch/flags.aconfig @@ -39,3 +39,11 @@ flag { description: "Add audio playing status to the contextual search invocation intent." bug: "372935419" } + +flag { + name: "self_invocation" + namespace: "sysui_integrations" + description: "Enable apps to self-invoke Contextual Search." + bug: "368653769" + is_exported: true +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c8ad7dae28d4..75492da69beb 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -7688,12 +7688,12 @@ <permission android:name="android.permission.ACCESS_SMARTSPACE" android:protectionLevel="signature|privileged|development" /> - <!-- @SystemApi Allows an application to start a contextual search. - @FlaggedApi("android.app.contextualsearch.flags.enable_service") - @hide <p>Not for use by third-party applications.</p> --> + <!-- @SystemApi Allows a system application to start a contextual search. + Other applications can start a contextual search only if they have a + foreground activity. + @hide <p>Not for use by third-party applications.</p> --> <permission android:name="android.permission.ACCESS_CONTEXTUAL_SEARCH" - android:protectionLevel="signature|privileged" - android:featureFlag="android.app.contextualsearch.flags.enable_service"/> + android:protectionLevel="signature|privileged" /> <!-- @SystemApi Allows an application to manage the wallpaper effects generation service. diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java index 89c9d690a82c..700a1624f7d4 100644 --- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java +++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java @@ -34,6 +34,8 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUC import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManagerInternal; @@ -69,7 +71,6 @@ import android.provider.Settings; import android.util.Log; import android.util.Slog; import android.view.IWindowManager; -import android.window.ScreenCapture; import android.window.ScreenCapture.ScreenshotHardwareBuffer; import com.android.internal.R; @@ -86,7 +87,6 @@ import java.io.FileDescriptor; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.Set; public class ContextualSearchManagerService extends SystemService { private static final String TAG = ContextualSearchManagerService.class.getSimpleName(); @@ -95,9 +95,20 @@ public class ContextualSearchManagerService extends SystemService { private static final int MSG_INVALIDATE_TOKEN = 1; private static final int MAX_TOKEN_VALID_DURATION_MS = 1_000 * 60 * 10; // 10 minutes + /** + * Below are internal entrypoints not supported by the + * {@link ContextualSearchManager#startContextualSearch(int entrypoint)} method. + * + * <p>These values should be negative to avoid conflicting with the system entrypoints. + */ + + /** Entrypoint to be used when a foreground app invokes Contextual Search. */ + private static final int INTERNAL_ENTRYPOINT_APP = -1; + private static final boolean DEBUG = false; private final Context mContext; + private final ActivityManagerInternal mActivityManagerInternal; private final ActivityTaskManagerInternal mAtmInternal; private final PackageManagerInternal mPackageManager; private final WindowManagerInternal mWmInternal; @@ -162,6 +173,8 @@ public class ContextualSearchManagerService extends SystemService { super(context); if (DEBUG) Log.d(TAG, "ContextualSearchManagerService created"); mContext = context; + mActivityManagerInternal = Objects.requireNonNull( + LocalServices.getService(ActivityManagerInternal.class)); mAtmInternal = Objects.requireNonNull( LocalServices.getService(ActivityTaskManagerInternal.class)); mPackageManager = LocalServices.getService(PackageManagerInternal.class); @@ -391,6 +404,20 @@ public class ContextualSearchManagerService extends SystemService { } } + private void enforceForegroundApp(@NonNull final String func) { + final int callingUid = Binder.getCallingUid(); + final String callingPackage = mPackageManager.getNameForUid(Binder.getCallingUid()); + if (mActivityManagerInternal.getUidProcessState(callingUid) + > ActivityManager.PROCESS_STATE_TOP) { + // The calling process must be displaying an activity in foreground to + // trigger contextual search. + String msg = "Permission Denial: Cannot call " + func + " from pid=" + + Binder.getCallingPid() + ", uid=" + callingUid + + ", package=" + callingPackage + " without a foreground activity."; + throw new SecurityException(msg); + } + } + private void enforceOverridingPermission(@NonNull final String func) { if (!(Binder.getCallingUid() == Process.SHELL_UID || Binder.getCallingUid() == Process.ROOT_UID @@ -448,29 +475,43 @@ public class ContextualSearchManagerService extends SystemService { } @Override + public void startContextualSearchForForegroundApp() { + synchronized (this) { + if (DEBUG) { + Log.d(TAG, "Starting contextual search from: " + + mPackageManager.getNameForUid(Binder.getCallingUid())); + } + enforceForegroundApp("startContextualSearchForForegroundApp"); + startContextualSearchInternal(INTERNAL_ENTRYPOINT_APP); + } + } + + @Override public void startContextualSearch(int entrypoint) { synchronized (this) { if (DEBUG) Log.d(TAG, "startContextualSearch entrypoint: " + entrypoint); enforcePermission("startContextualSearch"); - final int callingUserId = Binder.getCallingUserHandle().getIdentifier(); - - mAssistDataRequester.cancel(); - // Creates a new CallbackToken at mToken and an expiration handler. - issueToken(); - // We get the launch intent with the system server's identity because the system - // server has READ_FRAME_BUFFER permission to get the screenshot and because only - // the system server can invoke non-exported activities. - Binder.withCleanCallingIdentity(() -> { - Intent launchIntent = - getContextualSearchIntent(entrypoint, callingUserId, mToken); - if (launchIntent != null) { - int result = invokeContextualSearchIntent(launchIntent, callingUserId); - if (DEBUG) Log.d(TAG, "Launch result: " + result); - } - }); + startContextualSearchInternal(entrypoint); } } + private void startContextualSearchInternal(int entrypoint) { + final int callingUserId = Binder.getCallingUserHandle().getIdentifier(); + mAssistDataRequester.cancel(); + // Creates a new CallbackToken at mToken and an expiration handler. + issueToken(); + // We get the launch intent with the system server's identity because the system + // server has READ_FRAME_BUFFER permission to get the screenshot and because only + // the system server can invoke non-exported activities. + Binder.withCleanCallingIdentity(() -> { + Intent launchIntent = getContextualSearchIntent(entrypoint, callingUserId, mToken); + if (launchIntent != null) { + int result = invokeContextualSearchIntent(launchIntent, callingUserId); + if (DEBUG) Log.d(TAG, "Launch result: " + result); + } + }); + } + @Override public void getContextualSearchState( @NonNull IBinder token, |