From b0017b593f94734e308c2f397c244afdaafa3873 Mon Sep 17 00:00:00 2001 From: Hui Wu Date: Mon, 24 Jan 2022 14:08:44 -0800 Subject: [PATCH] CloudSearch API: data class declarations and interface definitions. CTS-Coverage-Bug: 210528288 Bug: 210528288 Test: RBE Built redfin-eng and tested on a physical device Change-Id: I640be77dbf05633b7a17ada73ea261dbd0027697 --- core/api/system-current.txt | 106 ++++++ core/api/system-lint-baseline.txt | 226 ++++++------- .../android/app/SystemServiceRegistry.java | 13 + .../app/cloudsearch/CloudSearchManager.java | 128 ++++++++ .../app/cloudsearch/ICloudSearchManager.aidl | 33 ++ .../ICloudSearchManagerCallback.aidl | 31 ++ .../app/cloudsearch/SearchRequest.aidl | 19 ++ .../app/cloudsearch/SearchRequest.java | 303 ++++++++++++++++++ .../app/cloudsearch/SearchResponse.aidl | 19 ++ .../app/cloudsearch/SearchResponse.java | 214 +++++++++++++ .../android/app/cloudsearch/SearchResult.aidl | 19 ++ .../android/app/cloudsearch/SearchResult.java | 285 ++++++++++++++++ core/java/android/content/Context.java | 14 + .../cloudsearch/CloudSearchService.java | 141 ++++++++ .../cloudsearch/ICloudSearchService.aidl | 28 ++ core/res/AndroidManifest.xml | 5 + core/res/res/values/config.xml | 9 + core/res/res/values/symbols.xml | 1 + 18 files changed, 1483 insertions(+), 111 deletions(-) create mode 100644 core/java/android/app/cloudsearch/CloudSearchManager.java create mode 100644 core/java/android/app/cloudsearch/ICloudSearchManager.aidl create mode 100644 core/java/android/app/cloudsearch/ICloudSearchManagerCallback.aidl create mode 100644 core/java/android/app/cloudsearch/SearchRequest.aidl create mode 100644 core/java/android/app/cloudsearch/SearchRequest.java create mode 100644 core/java/android/app/cloudsearch/SearchResponse.aidl create mode 100644 core/java/android/app/cloudsearch/SearchResponse.java create mode 100644 core/java/android/app/cloudsearch/SearchResult.aidl create mode 100644 core/java/android/app/cloudsearch/SearchResult.java create mode 100644 core/java/android/service/cloudsearch/CloudSearchService.java create mode 100644 core/java/android/service/cloudsearch/ICloudSearchService.aidl diff --git a/core/api/system-current.txt b/core/api/system-current.txt index f9d802b16a791..af46d2483354f 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -157,6 +157,7 @@ package android { field public static final String MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED = "android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"; field public static final String MANAGE_CARRIER_OEM_UNLOCK_STATE = "android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE"; field public static final String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES"; + field public static final String MANAGE_CLOUDSEARCH = "android.permission.MANAGE_CLOUDSEARCH"; field public static final String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE"; field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS"; field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING"; @@ -1578,6 +1579,99 @@ package android.app.backup { } +package android.app.cloudsearch { + + public class CloudSearchManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_CLOUDSEARCH) public void search(@NonNull android.app.cloudsearch.SearchRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.cloudsearch.CloudSearchManager.CallBack); + } + + public static interface CloudSearchManager.CallBack { + method public void onSearchFailed(@NonNull android.app.cloudsearch.SearchRequest, @NonNull android.app.cloudsearch.SearchResponse); + method public void onSearchSucceeded(@NonNull android.app.cloudsearch.SearchRequest, @NonNull android.app.cloudsearch.SearchResponse); + } + + public final class SearchRequest implements android.os.Parcelable { + method public int describeContents(); + method public float getMaxLatencyMillis(); + method @NonNull public String getQuery(); + method @NonNull public String getRequestId(); + method public int getResultNumber(); + method public int getResultOffset(); + method @NonNull public android.os.Bundle getSearchConstraints(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final String CONSTRAINT_IS_PRESUBMIT_SUGGESTION = "IS_PRESUBMIT_SUGGESTION"; + field public static final String CONSTRAINT_SEARCH_PROVIDER_FILTER = "SEARCH_PROVIDER_FILTER"; + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class SearchRequest.Builder { + ctor public SearchRequest.Builder(@NonNull String); + method @NonNull public android.app.cloudsearch.SearchRequest build(); + method @NonNull public android.app.cloudsearch.SearchRequest.Builder setMaxLatencyMillis(float); + method @NonNull public android.app.cloudsearch.SearchRequest.Builder setQuery(@NonNull String); + method @NonNull public android.app.cloudsearch.SearchRequest.Builder setResultNumber(int); + method @NonNull public android.app.cloudsearch.SearchRequest.Builder setResultOffset(int); + method @NonNull public android.app.cloudsearch.SearchRequest.Builder setSearchConstraints(@Nullable android.os.Bundle); + } + + public final class SearchResponse implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List getSearchResults(); + method @NonNull public String getSource(); + method public int getStatusCode(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final int SEARCH_STATUS_NO_INTERNET = 2; // 0x2 + field public static final int SEARCH_STATUS_OK = 0; // 0x0 + field public static final int SEARCH_STATUS_TIME_OUT = 1; // 0x1 + field public static final int SEARCH_STATUS_UNKNOWN = -1; // 0xffffffff + } + + public static final class SearchResponse.Builder { + ctor public SearchResponse.Builder(int); + method @NonNull public android.app.cloudsearch.SearchResponse build(); + method @NonNull public android.app.cloudsearch.SearchResponse.Builder setSearchResults(@NonNull java.util.List); + method @NonNull public android.app.cloudsearch.SearchResponse.Builder setStatusCode(int); + } + + public final class SearchResult implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.os.Bundle getExtraInfos(); + method public float getScore(); + method @NonNull public String getSnippet(); + method @NonNull public String getTitle(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final String EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING = "ACTION_BUTTON_IMAGE"; + field public static final String EXTRAINFO_ACTION_BUTTON_TEXT_PREREGISTERING = "ACTION_BUTTON_TEXT"; + field public static final String EXTRAINFO_APP_BADGES = "APP_BADGES"; + field public static final String EXTRAINFO_APP_CONTAINS_ADS_DISCLAIMER = "APP_CONTAINS_ADS_DISCLAIMER"; + field public static final String EXTRAINFO_APP_CONTAINS_IAP_DISCLAIMER = "APP_CONTAINS_IAP_DISCLAIMER"; + field public static final String EXTRAINFO_APP_DEVELOPER_NAME = "APP_DEVELOPER_NAME"; + field public static final String EXTRAINFO_APP_DOMAIN_URL = "APP_DOMAIN_URL"; + field public static final String EXTRAINFO_APP_IARC = "APP_IARC"; + field public static final String EXTRAINFO_APP_ICON = "APP_ICON"; + field public static final String EXTRAINFO_APP_REVIEW_COUNT = "APP_REVIEW_COUNT"; + field public static final String EXTRAINFO_APP_SIZE_BYTES = "APP_SIZE_BYTES"; + field public static final String EXTRAINFO_APP_STAR_RATING = "APP_STAR_RATING"; + field public static final String EXTRAINFO_LONG_DESCRIPTION = "LONG_DESCRIPTION"; + field public static final String EXTRAINFO_SCREENSHOTS = "SCREENSHOTS"; + field public static final String EXTRAINFO_SHORT_DESCRIPTION = "SHORT_DESCRIPTION"; + field public static final String EXTRAINFO_WEB_ICON = "WEB_ICON"; + field public static final String EXTRAINFO_WEB_URL = "WEB_URL"; + } + + public static final class SearchResult.Builder { + ctor public SearchResult.Builder(@NonNull String, @NonNull android.os.Bundle); + method @NonNull public android.app.cloudsearch.SearchResult build(); + method @NonNull public android.app.cloudsearch.SearchResult.Builder setExtraInfos(@NonNull android.os.Bundle); + method @NonNull public android.app.cloudsearch.SearchResult.Builder setScore(float); + method @NonNull public android.app.cloudsearch.SearchResult.Builder setSnippet(@NonNull String); + method @NonNull public android.app.cloudsearch.SearchResult.Builder setTitle(@NonNull String); + } + +} + package android.app.compat { public final class CompatChanges { @@ -2840,6 +2934,7 @@ package android.content { field public static final String BATTERY_STATS_SERVICE = "batterystats"; field @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000 field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000 + field public static final String CLOUDSEARCH_SERVICE = "cloudsearch_service"; field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; field public static final String ETHERNET_SERVICE = "ethernet"; @@ -10866,6 +10961,17 @@ package android.service.carrier { } +package android.service.cloudsearch { + + public abstract class CloudSearchService extends android.app.Service { + ctor public CloudSearchService(); + method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); + method public abstract void onSearch(@NonNull android.app.cloudsearch.SearchRequest); + method public final void returnResults(@NonNull String, @NonNull android.app.cloudsearch.SearchResponse); + } + +} + package android.service.contentcapture { public final class ActivityEvent implements android.os.Parcelable { diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index 3d756bafa292b..dbb427467a680 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -1,6 +1,6 @@ // Baseline format: 1.0 ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions(): - + BuilderSetStyle: android.net.IpSecTransform.Builder#buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex): @@ -12,23 +12,23 @@ ExecutorRegistration: android.media.MediaPlayer#setOnRtpRxNoticeListener(android GenericException: android.app.prediction.AppPredictor#finalize(): - + GenericException: android.hardware.location.ContextHubClient#finalize(): - + GenericException: android.net.IpSecManager.IpSecTunnelInterface#finalize(): - + GenericException: android.service.autofill.augmented.FillWindow#finalize(): - + IntentBuilderName: android.app.search.SearchAction#getIntent(): - + IntentBuilderName: android.app.smartspace.SmartspaceAction#getIntent(): Methods creating an Intent should be named `createIntent()`, was `getIntent` KotlinKeyword: android.app.Notification#when: - + MissingGetterMatchingBuilder: android.os.NewUserRequest.Builder#setAdmin(): @@ -46,49 +46,49 @@ MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#set MissingNullability: android.media.soundtrigger.SoundTriggerDetectionService#onUnbind(android.content.Intent) parameter #0: - + MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #0: - + MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #1: - + MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #2: - + MissingNullability: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context) parameter #0: - + MissingNullability: android.provider.ContactsContract.MetadataSync#CONTENT_URI: - + MissingNullability: android.provider.ContactsContract.MetadataSync#METADATA_AUTHORITY_URI: - + MissingNullability: android.provider.ContactsContract.MetadataSyncState#CONTENT_URI: - + MissingNullability: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #0: - + MissingNullability: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #1: - + MissingNullability: android.service.autofill.augmented.AugmentedAutofillService#onUnbind(android.content.Intent) parameter #0: - + MissingNullability: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0: - + MissingNullability: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1: - + MissingNullability: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #2: - + MissingNullability: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context) parameter #0: - + MissingNullability: android.telephony.NetworkService#onUnbind(android.content.Intent) parameter #0: - + MissingNullability: android.telephony.SubscriptionPlan.Builder#createRecurringDaily(java.time.ZonedDateTime) parameter #0: - + MissingNullability: android.telephony.SubscriptionPlan.Builder#createRecurringMonthly(java.time.ZonedDateTime) parameter #0: - + MissingNullability: android.telephony.SubscriptionPlan.Builder#createRecurringWeekly(java.time.ZonedDateTime) parameter #0: - + MissingNullability: android.telephony.data.DataService#onUnbind(android.content.Intent) parameter #0: - + MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String): - + MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String) parameter #0: - + NoSettingsProvider: android.provider.Settings.Secure#FAST_PAIR_SCAN_ENABLED: @@ -100,11 +100,11 @@ OnNameExpected: android.service.smartspace.SmartspaceService#notifySmartspaceEve ProtectedMember: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context): - + ProtectedMember: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]): - + ProtectedMember: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context): - + RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimAmount(): @@ -118,103 +118,103 @@ RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimmingAmount(f SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean): - + SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, String[]): - + SamShouldBeLast: android.accounts.AccountManager#confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#editProperties(String, android.app.Activity, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#getAuthToken(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#getAuthToken(android.accounts.Account, String, android.os.Bundle, boolean, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#getAuthToken(android.accounts.Account, String, boolean, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#getAuthTokenByFeatures(String, String, String[], android.app.Activity, android.os.Bundle, android.os.Bundle, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#isCredentialsUpdateSuggested(android.accounts.Account, String, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#renameAccount(android.accounts.Account, String, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#startAddAccountSession(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#startUpdateCredentialsSession(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.accounts.AccountManager#updateCredentials(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback, android.os.Handler): - + SamShouldBeLast: android.app.AlarmManager#set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler): - + SamShouldBeLast: android.app.AlarmManager#setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler): - + SamShouldBeLast: android.app.AlarmManager#setWindow(int, long, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler): - + SamShouldBeLast: android.app.WallpaperInfo#dump(android.util.Printer, String): - + SamShouldBeLast: android.app.WallpaperManager#addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler): - + SamShouldBeLast: android.app.admin.DevicePolicyManager#installSystemUpdate(android.content.ComponentName, android.net.Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback): - + SamShouldBeLast: android.content.IntentFilter#dump(android.util.Printer, String): - + SamShouldBeLast: android.content.pm.ApplicationInfo#dump(android.util.Printer, String): - + SamShouldBeLast: android.content.pm.PackageItemInfo#dumpBack(android.util.Printer, String): - + SamShouldBeLast: android.content.pm.PackageItemInfo#dumpFront(android.util.Printer, String): - + SamShouldBeLast: android.content.pm.ResolveInfo#dump(android.util.Printer, String): - + SamShouldBeLast: android.location.Location#dump(android.util.Printer, String): - + SamShouldBeLast: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler): - + SamShouldBeLast: android.location.LocationManager#registerGnssMeasurementsCallback(java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback): - + SamShouldBeLast: android.location.LocationManager#registerGnssNavigationMessageCallback(java.util.concurrent.Executor, android.location.GnssNavigationMessage.Callback): - + SamShouldBeLast: android.location.LocationManager#registerGnssStatusCallback(java.util.concurrent.Executor, android.location.GnssStatus.Callback): - + SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, android.location.LocationListener, android.os.Looper): - + SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, java.util.concurrent.Executor, android.location.LocationListener): - + SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, java.util.concurrent.Executor, android.location.LocationListener): - + SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, android.location.LocationListener, android.os.Looper): - + SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, java.util.concurrent.Executor, android.location.LocationListener): - + SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(String, android.location.LocationListener, android.os.Looper): - + SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(android.location.Criteria, android.location.LocationListener, android.os.Looper): - + SamShouldBeLast: android.media.AudioFocusRequest.Builder#setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler): - + SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int): - + SamShouldBeLast: android.media.AudioRecord#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): - + SamShouldBeLast: android.media.AudioRecord#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback): - + SamShouldBeLast: android.media.AudioRecordingMonitor#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback): - + SamShouldBeLast: android.media.AudioRouting#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): - + SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): - + SamShouldBeLast: android.media.MediaPlayer#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaPlayer.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.MediaPlayer#setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler): @@ -228,74 +228,78 @@ SamShouldBeLast: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.cont SamShouldBeLast: android.media.MediaPlayer#setOnSubtitleDataListener(android.media.MediaPlayer.OnSubtitleDataListener, android.os.Handler): SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaPlayer.setOnSubtitleDataListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.MediaRecorder#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): - + SamShouldBeLast: android.media.MediaRecorder#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback): - + SamShouldBeLast: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName): - + SamShouldBeLast: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler): - + SamShouldBeLast: android.media.session.MediaSessionManager#addOnSession2TokensChangedListener(android.media.session.MediaSessionManager.OnSession2TokensChangedListener, android.os.Handler): - + SamShouldBeLast: android.media.session.MediaSessionManager#registerCallback(java.util.concurrent.Executor, android.media.session.MediaSessionManager.Callback): - + SamShouldBeLast: android.nfc.NfcAdapter#enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle): - + SamShouldBeLast: android.nfc.NfcAdapter#ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler): - + SamShouldBeLast: android.nfc.NfcAdapter#setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity): - + SamShouldBeLast: android.nfc.NfcAdapter#setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...): - + SamShouldBeLast: android.nfc.NfcAdapter#setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...): - + SamShouldBeLast: android.os.Binder#attachInterface(android.os.IInterface, String): - + SamShouldBeLast: android.os.Binder#linkToDeath(android.os.IBinder.DeathRecipient, int): - + SamShouldBeLast: android.os.Binder#unlinkToDeath(android.os.IBinder.DeathRecipient, int): - + SamShouldBeLast: android.os.Handler#dump(android.util.Printer, String): - + SamShouldBeLast: android.os.Handler#postAtTime(Runnable, Object, long): - + SamShouldBeLast: android.os.Handler#postAtTime(Runnable, long): - + SamShouldBeLast: android.os.Handler#postDelayed(Runnable, Object, long): - + SamShouldBeLast: android.os.Handler#postDelayed(Runnable, long): - + SamShouldBeLast: android.os.Handler#removeCallbacks(Runnable, Object): - + SamShouldBeLast: android.os.IBinder#linkToDeath(android.os.IBinder.DeathRecipient, int): - + SamShouldBeLast: android.os.IBinder#unlinkToDeath(android.os.IBinder.DeathRecipient, int): - + SamShouldBeLast: android.os.RecoverySystem#verifyPackage(java.io.File, android.os.RecoverySystem.ProgressListener, java.io.File): - + SamShouldBeLast: android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, String[], java.security.Principal[], String, int, String): SAM-compatible parameters (such as parameter 2, "response", in android.security.KeyChain.choosePrivateKeyAlias) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, String[], java.security.Principal[], android.net.Uri, String): SAM-compatible parameters (such as parameter 2, "response", in android.security.KeyChain.choosePrivateKeyAlias) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.view.View#postDelayed(Runnable, long): - + SamShouldBeLast: android.view.View#postOnAnimationDelayed(Runnable, long): - + SamShouldBeLast: android.view.View#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long): - + SamShouldBeLast: android.view.Window#addOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener, android.os.Handler): - + SamShouldBeLast: android.view.accessibility.AccessibilityManager#addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, android.os.Handler): - + SamShouldBeLast: android.view.accessibility.AccessibilityManager#addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, android.os.Handler): - + SamShouldBeLast: android.webkit.WebChromeClient#onShowFileChooser(android.webkit.WebView, android.webkit.ValueCallback, android.webkit.WebChromeClient.FileChooserParams): + +ServiceName: android.content.Context#CLOUDSEARCH_SERVICE: + Inconsistent service value; expected `cloudsearch`, was `cloudsearch_service` (Note: Do not change the name of already released services, which will break tools using `adb shell dumpsys`. Instead add `@SuppressLint("ServiceName"))` + UserHandleName: android.app.search.SearchAction.Builder#setUserHandle(android.os.UserHandle): Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `setUserHandle` UserHandleName: android.app.search.SearchTarget.Builder#setUserHandle(android.os.UserHandle): - + UserHandleName: android.app.smartspace.SmartspaceAction.Builder#setUserHandle(android.os.UserHandle): Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `setUserHandle` diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 7f8e46edf5941..c17d1a3687bee 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -28,6 +28,8 @@ import android.app.ambientcontext.AmbientContextManager; import android.app.ambientcontext.IAmbientContextEventObserver; import android.app.appsearch.AppSearchManagerFrameworkInitializer; import android.app.blob.BlobStoreManagerFrameworkInitializer; +import android.app.cloudsearch.CloudSearchManager; +import android.app.cloudsearch.ICloudSearchManager; import android.app.contentsuggestions.ContentSuggestionsManager; import android.app.contentsuggestions.IContentSuggestionsManager; import android.app.job.JobSchedulerFrameworkInitializer; @@ -1248,6 +1250,17 @@ public final class SystemServiceRegistry { } }); + registerService(Context.CLOUDSEARCH_SERVICE, CloudSearchManager.class, + new CachedServiceFetcher() { + @Override + public CloudSearchManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.CLOUDSEARCH_SERVICE); + return b == null ? null : + new CloudSearchManager(ICloudSearchManager.Stub.asInterface(b)); + } + }); + registerService(Context.APP_PREDICTION_SERVICE, AppPredictionManager.class, new CachedServiceFetcher() { @Override diff --git a/core/java/android/app/cloudsearch/CloudSearchManager.java b/core/java/android/app/cloudsearch/CloudSearchManager.java new file mode 100644 index 0000000000000..471e423db458b --- /dev/null +++ b/core/java/android/app/cloudsearch/CloudSearchManager.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.cloudsearch; + +import static java.util.Objects.requireNonNull; + +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; +import android.os.RemoteException; + +import java.util.concurrent.Executor; +/** + * A {@link CloudSearchManager} is the class having all the information passed to search providers. + * + * @hide + */ +@SystemApi +@SystemService(Context.CLOUDSEARCH_SERVICE) +public class CloudSearchManager { + /** + * CallBack Interface for this API. + */ + public interface CallBack { + /** + * Invoked by receiving app with the result of the search. + * + * @param request original request for the search. + * @param response search result. + */ + void onSearchSucceeded(@NonNull SearchRequest request, @NonNull SearchResponse response); + + /** + * Invoked when the search is not successful. + * Each failure is recorded. The client may receive a failure from one provider and + * subsequently receive successful searches from other providers + * + * @param request original request for the search. + * @param response search result. + */ + void onSearchFailed(@NonNull SearchRequest request, @NonNull SearchResponse response); + } + + private final ICloudSearchManager mService; + + /** @hide **/ + public CloudSearchManager(@NonNull ICloudSearchManager service) { + mService = service; + } + + /** + * Execute an {@link android.app.cloudsearch.SearchRequest} from the given parameters + * to the designated cloud lookup services. After the lookup is done, the given + * callback will be invoked by the system with the result or lack thereof. + * + * @param request request to be searched. + * @param callbackExecutor where the callback is invoked. + * @param callback invoked when the result is available. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_CLOUDSEARCH) + public void search(@NonNull SearchRequest request, + @NonNull @CallbackExecutor Executor callbackExecutor, + @NonNull CallBack callback) { + try { + mService.search( + requireNonNull(request), + new CallBackWrapper( + requireNonNull(request), + requireNonNull(callback), + requireNonNull(callbackExecutor))); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + private final class CallBackWrapper extends + ICloudSearchManagerCallback.Stub { + @NonNull + private final SearchRequest mSearchRequest; + + @NonNull + private final CallBack mCallback; + + @NonNull + private final Executor mCallbackExecutor; + + CallBackWrapper( + SearchRequest searchRequest, + CallBack callback, + Executor callbackExecutor) { + mSearchRequest = searchRequest; + mCallback = callback; + mCallbackExecutor = callbackExecutor; + } + + + @Override + public void onSearchSucceeded(SearchResponse searchResponse) { + mCallbackExecutor.execute( + () -> mCallback.onSearchSucceeded(mSearchRequest, searchResponse)); + } + + @Override + public void onSearchFailed(SearchResponse searchResponse) { + mCallbackExecutor.execute( + () -> mCallback.onSearchFailed(mSearchRequest, searchResponse)); + } + } +} diff --git a/core/java/android/app/cloudsearch/ICloudSearchManager.aidl b/core/java/android/app/cloudsearch/ICloudSearchManager.aidl new file mode 100644 index 0000000000000..18f8fc4761918 --- /dev/null +++ b/core/java/android/app/cloudsearch/ICloudSearchManager.aidl @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.cloudsearch; + +import android.app.cloudsearch.SearchRequest; +import android.app.cloudsearch.SearchResponse; +import android.app.cloudsearch.ICloudSearchManagerCallback; + +/** + * Used by {@link CloudSearchManager} to tell system server to do search. + * + * @hide + */ +oneway interface ICloudSearchManager { + void search(in SearchRequest request, in ICloudSearchManagerCallback callBack); + + void returnResults(in IBinder token, in String requestId, + in SearchResponse response); +} diff --git a/core/java/android/app/cloudsearch/ICloudSearchManagerCallback.aidl b/core/java/android/app/cloudsearch/ICloudSearchManagerCallback.aidl new file mode 100644 index 0000000000000..84771dd4a19bd --- /dev/null +++ b/core/java/android/app/cloudsearch/ICloudSearchManagerCallback.aidl @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.cloudsearch; + +import android.app.cloudsearch.SearchResponse; + + +/** + * Callback used by system server to notify invoker of {@link CloudSearchManager} of the result + * + * @hide + */ +oneway interface ICloudSearchManagerCallback { + void onSearchSucceeded(in SearchResponse response); + + void onSearchFailed(in SearchResponse response); +} diff --git a/core/java/android/app/cloudsearch/SearchRequest.aidl b/core/java/android/app/cloudsearch/SearchRequest.aidl new file mode 100644 index 0000000000000..9f2cdb888bdf4 --- /dev/null +++ b/core/java/android/app/cloudsearch/SearchRequest.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.cloudsearch; + +parcelable SearchRequest; diff --git a/core/java/android/app/cloudsearch/SearchRequest.java b/core/java/android/app/cloudsearch/SearchRequest.java new file mode 100644 index 0000000000000..0c5c30c23d7bc --- /dev/null +++ b/core/java/android/app/cloudsearch/SearchRequest.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.cloudsearch; + +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StringDef; +import android.annotation.SystemApi; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * A {@link SearchRequest} is the data class having all the information passed to search providers. + * + * @hide + */ +@SystemApi +public final class SearchRequest implements Parcelable { + + /** + * Query for search. + */ + @NonNull + private final String mQuery; + + /** + * Expected result offset for pagination. + * + * The default value is 0. + */ + private final int mResultOffset; + + /** + * Expected search result number. + * + * The default value is 10. + */ + private final int mResultNumber; + + /** + * The max acceptable latency. + * + * The default value is 200 milliseconds. + */ + private final float mMaxLatencyMillis; + + @Nullable + private String mId = null; + + /** + * List of public static KEYS for the Bundle to mSearchConstraints. mSearchConstraints + * contains various constraints specifying the search intent. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = {"CONSTRAINT_"}, + value = {CONSTRAINT_IS_PRESUBMIT_SUGGESTION, + CONSTRAINT_SEARCH_PROVIDER_FILTER}) + public @interface SearchConstraintKey {} + /** If this is a presubmit suggestion, Boolean value expected. + * presubmit is the input before the user finishes the entire query, i.e. push "ENTER" or + * "SEARCH" button. After the user finishes the entire query, the behavior is postsubmit. + */ + public static final String CONSTRAINT_IS_PRESUBMIT_SUGGESTION = "IS_PRESUBMIT_SUGGESTION"; + /** The target search provider list of package names(separated by ;), String value expected. + * If this is not provided or its value is empty, then no filter will be applied. + */ + public static final String CONSTRAINT_SEARCH_PROVIDER_FILTER = "SEARCH_PROVIDER_FILTER"; + + @NonNull + private Bundle mSearchConstraints; + + private SearchRequest(Parcel in) { + this.mQuery = in.readString(); + this.mResultOffset = in.readInt(); + this.mResultNumber = in.readInt(); + this.mMaxLatencyMillis = in.readFloat(); + this.mSearchConstraints = in.readBundle(); + this.mId = in.readString(); + } + + private SearchRequest(String query, int resultOffset, int resultNumber, float maxLatencyMillis, + Bundle searchConstraints) { + mQuery = query; + mResultOffset = resultOffset; + mResultNumber = resultNumber; + mMaxLatencyMillis = maxLatencyMillis; + mSearchConstraints = searchConstraints; + } + + /** Returns the original query. */ + @NonNull + public String getQuery() { + return mQuery; + } + + /** Returns the result offset. */ + public int getResultOffset() { + return mResultOffset; + } + + /** Returns the expected number of search results. */ + public int getResultNumber() { + return mResultNumber; + } + + /** Returns the maximum latency requirement. */ + public float getMaxLatencyMillis() { + return mMaxLatencyMillis; + } + + /** Returns the search constraints. */ + @NonNull + public Bundle getSearchConstraints() { + return mSearchConstraints; + } + + /** Returns the search request id, which is used to identify the request. */ + @NonNull + public String getRequestId() { + if (mId == null || mId.length() == 0) { + boolean isPresubmit = + mSearchConstraints.containsKey(CONSTRAINT_IS_PRESUBMIT_SUGGESTION) + && mSearchConstraints.getBoolean(CONSTRAINT_IS_PRESUBMIT_SUGGESTION); + + String searchProvider = "EMPTY"; + if (mSearchConstraints.containsKey(CONSTRAINT_SEARCH_PROVIDER_FILTER)) { + searchProvider = mSearchConstraints.getString(CONSTRAINT_SEARCH_PROVIDER_FILTER); + } + + String rawContent = String.format("%s\t%d\t%d\t%f\t%b\t%s", + mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis, + isPresubmit, searchProvider); + + mId = String.valueOf(rawContent.hashCode()); + } + + return mId; + } + + private SearchRequest(Builder b) { + mQuery = requireNonNull(b.mQuery); + mResultOffset = b.mResultOffset; + mResultNumber = b.mResultNumber; + mMaxLatencyMillis = b.mMaxLatencyMillis; + mSearchConstraints = requireNonNull(b.mSearchConstraints); + } + + /** + * @see Creator + * + */ + @NonNull + public static final Creator CREATOR = new Creator() { + @Override + public SearchRequest createFromParcel(Parcel p) { + return new SearchRequest(p); + } + + @Override + public SearchRequest[] newArray(int size) { + return new SearchRequest[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(this.mQuery); + dest.writeInt(this.mResultOffset); + dest.writeInt(this.mResultNumber); + dest.writeFloat(this.mMaxLatencyMillis); + dest.writeBundle(this.mSearchConstraints); + dest.writeString(getRequestId()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + SearchRequest that = (SearchRequest) obj; + return Objects.equals(mQuery, that.mQuery) + && mResultOffset == that.mResultOffset + && mResultNumber == that.mResultNumber + && mMaxLatencyMillis == that.mMaxLatencyMillis + && Objects.equals(mSearchConstraints, that.mSearchConstraints); + } + + @Override + public int hashCode() { + return Objects.hash(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis, + mSearchConstraints); + } + + /** + * The builder for {@link SearchRequest}. + * + * @hide + */ + @SystemApi + public static final class Builder { + private String mQuery; + private int mResultOffset; + private int mResultNumber; + private float mMaxLatencyMillis; + private Bundle mSearchConstraints; + + /** + * + * @param query the query for search. + * + * @hide + */ + @SystemApi + public Builder(@NonNull String query) { + mQuery = query; + + mResultOffset = 0; + mResultNumber = 10; + mMaxLatencyMillis = 200; + mSearchConstraints = Bundle.EMPTY; + } + + /** Sets the input query. */ + @NonNull + public Builder setQuery(@NonNull String query) { + this.mQuery = query; + return this; + } + + /** Sets the search result offset. */ + @NonNull + public Builder setResultOffset(int resultOffset) { + this.mResultOffset = resultOffset; + return this; + } + + /** Sets the expected number of search result. */ + @NonNull + public Builder setResultNumber(int resultNumber) { + this.mResultNumber = resultNumber; + return this; + } + + /** Sets the maximum acceptable search latency. */ + @NonNull + public Builder setMaxLatencyMillis(float maxLatencyMillis) { + this.mMaxLatencyMillis = maxLatencyMillis; + return this; + } + + /** Sets the search constraints, such as the user location, the search type(presubmit or + * postsubmit), and the target search providers. */ + @NonNull + public Builder setSearchConstraints(@Nullable Bundle searchConstraints) { + this.mSearchConstraints = searchConstraints; + return this; + } + + /** Builds a SearchRequest based-on the given params. */ + @NonNull + public SearchRequest build() { + if (mQuery == null || mResultOffset < 0 || mResultNumber < 1 || mMaxLatencyMillis < 0 + || mSearchConstraints == null) { + throw new IllegalStateException("Please make sure all required args are valid."); + } + + return new SearchRequest(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis, + mSearchConstraints); + } + } +} diff --git a/core/java/android/app/cloudsearch/SearchResponse.aidl b/core/java/android/app/cloudsearch/SearchResponse.aidl new file mode 100644 index 0000000000000..2064d112c6b93 --- /dev/null +++ b/core/java/android/app/cloudsearch/SearchResponse.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.cloudsearch; + +parcelable SearchResponse; \ No newline at end of file diff --git a/core/java/android/app/cloudsearch/SearchResponse.java b/core/java/android/app/cloudsearch/SearchResponse.java new file mode 100644 index 0000000000000..607bd561d3310 --- /dev/null +++ b/core/java/android/app/cloudsearch/SearchResponse.java @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.cloudsearch; + +import static java.util.Objects.requireNonNull; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * A {@link SearchResponse} includes search results and associated meta information. + * + * @hide + */ +@SystemApi +public final class SearchResponse implements Parcelable { + /** @hide */ + @IntDef(prefix = {"SEARCH_STATUS_"}, + value = {SEARCH_STATUS_UNKNOWN, + SEARCH_STATUS_OK, + SEARCH_STATUS_TIME_OUT, + SEARCH_STATUS_NO_INTERNET}) + public @interface SearchStatusCode {} + public static final int SEARCH_STATUS_UNKNOWN = -1; + public static final int SEARCH_STATUS_OK = 0; + public static final int SEARCH_STATUS_TIME_OUT = 1; + public static final int SEARCH_STATUS_NO_INTERNET = 2; + + private final int mStatusCode; + + /** Auto set by system servier, and the provider cannot set it. */ + @NonNull + private String mSource; + + @NonNull + private final List mSearchResults; + + private SearchResponse(Parcel in) { + this.mStatusCode = in.readInt(); + this.mSource = in.readString(); + this.mSearchResults = in.createTypedArrayList(SearchResult.CREATOR); + } + + private SearchResponse(@SearchStatusCode int statusCode, String source, + List searchResults) { + mStatusCode = statusCode; + mSource = source; + mSearchResults = searchResults; + } + + /** Gets the search status code. */ + public int getStatusCode() { + return mStatusCode; + } + + /** Gets the search provider package name. */ + @NonNull + public String getSource() { + return mSource; + } + + /** Gets the search results, which can be empty. */ + @NonNull + public List getSearchResults() { + return mSearchResults; + } + + /** Sets the search provider, and this will be set by the system server. + * + * @hide + */ + public void setSource(@NonNull String source) { + this.mSource = source; + } + + private SearchResponse(Builder b) { + mStatusCode = b.mStatusCode; + mSource = requireNonNull(b.mSource); + mSearchResults = requireNonNull(b.mSearchResults); + } + + /** + * + * @see Creator + * + */ + @NonNull public static final Creator CREATOR = new Creator() { + @Override + public SearchResponse createFromParcel(Parcel p) { + return new SearchResponse(p); + } + + @Override + public SearchResponse[] newArray(int size) { + return new SearchResponse[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(this.mStatusCode); + dest.writeString(this.mSource); + dest.writeTypedList(this.mSearchResults); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + SearchResponse that = (SearchResponse) obj; + return mStatusCode == that.mStatusCode + && Objects.equals(mSource, that.mSource) + && Objects.equals(mSearchResults, that.mSearchResults); + } + + @Override + public int hashCode() { + return Objects.hash(mStatusCode, mSource, mSearchResults); + } + + /** + * Builder constructing SearchResponse. + * + * @hide + */ + @SystemApi + public static final class Builder { + private int mStatusCode; + private String mSource; + private List mSearchResults; + + /** + * + * @param statusCode the search status code. + * + * @hide + */ + @SystemApi + public Builder(@SearchStatusCode int statusCode) { + mStatusCode = statusCode; + + /** Init with a default value. */ + mSource = "DEFAULT"; + + mSearchResults = new ArrayList(); + } + + /** Sets the search status code. */ + @NonNull + public Builder setStatusCode(@SearchStatusCode int statusCode) { + this.mStatusCode = statusCode; + return this; + } + + /** Sets the search provider, and this will be set by the system server. + * + * @hide + */ + @NonNull + public Builder setSource(@NonNull String source) { + this.mSource = source; + return this; + } + + /** Sets the search results. */ + @NonNull + public Builder setSearchResults(@NonNull List searchResults) { + this.mSearchResults = searchResults; + return this; + } + + /** Builds a SearchResponse based-on the given parameters. */ + @NonNull + public SearchResponse build() { + if (mStatusCode < SEARCH_STATUS_UNKNOWN || mStatusCode > SEARCH_STATUS_NO_INTERNET + || mSearchResults == null) { + throw new IllegalStateException("Please make sure all @NonNull args are assigned."); + } + + return new SearchResponse(mStatusCode, mSource, mSearchResults); + } + } +} diff --git a/core/java/android/app/cloudsearch/SearchResult.aidl b/core/java/android/app/cloudsearch/SearchResult.aidl new file mode 100644 index 0000000000000..daebfbf3307fc --- /dev/null +++ b/core/java/android/app/cloudsearch/SearchResult.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.cloudsearch; + +parcelable SearchResult; \ No newline at end of file diff --git a/core/java/android/app/cloudsearch/SearchResult.java b/core/java/android/app/cloudsearch/SearchResult.java new file mode 100644 index 0000000000000..060931bdd4374 --- /dev/null +++ b/core/java/android/app/cloudsearch/SearchResult.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.cloudsearch; + +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; +import android.annotation.StringDef; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * A {@link SearchResult} includes all the information for one result item. + * + * @hide + */ +@SystemApi +public final class SearchResult implements Parcelable { + + /** Short content best describing the result item. */ + @NonNull + private final String mTitle; + + /** Matched contents in the result item. */ + @NonNull + private final String mSnippet; + + /** Ranking Score provided by the search provider. */ + private final float mScore; + + /** + * List of public static KEYS for Bundles in mExtraInfos. + * mExtraInfos contains various information specified for different data types. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = {"EXTRAINFO_"}, + value = {EXTRAINFO_APP_DOMAIN_URL, + EXTRAINFO_APP_ICON, + EXTRAINFO_APP_DEVELOPER_NAME, + EXTRAINFO_APP_SIZE_BYTES, + EXTRAINFO_APP_STAR_RATING, + EXTRAINFO_APP_IARC, + EXTRAINFO_APP_REVIEW_COUNT, + EXTRAINFO_APP_CONTAINS_ADS_DISCLAIMER, + EXTRAINFO_APP_CONTAINS_IAP_DISCLAIMER, + EXTRAINFO_SHORT_DESCRIPTION, + EXTRAINFO_LONG_DESCRIPTION, + EXTRAINFO_SCREENSHOTS, + EXTRAINFO_APP_BADGES, + EXTRAINFO_ACTION_BUTTON_TEXT_PREREGISTERING, + EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING, + EXTRAINFO_WEB_URL, + EXTRAINFO_WEB_ICON}) + public @interface SearchResultExtraInfoKey {} + /** This App developer website's domain URL, String value expected. */ + public static final String EXTRAINFO_APP_DOMAIN_URL = "APP_DOMAIN_URL"; + /** This App result's ICON URL, String value expected. */ + public static final String EXTRAINFO_APP_ICON = "APP_ICON"; + /** This App developer's name, String value expected. */ + public static final String EXTRAINFO_APP_DEVELOPER_NAME = "APP_DEVELOPER_NAME"; + /** This App's pkg size in bytes, Double value expected. */ + public static final String EXTRAINFO_APP_SIZE_BYTES = "APP_SIZE_BYTES"; + /** This App developer's name, Double value expected. */ + public static final String EXTRAINFO_APP_STAR_RATING = "APP_STAR_RATING"; + /** This App's IARC rating, String value expected. + * IARC (International Age Rating Coalition) is partnered globally with major + * content rating organizations to provide a centralized and one-stop-shop for + * rating content on a global scale. + */ + public static final String EXTRAINFO_APP_IARC = "APP_IARC"; + /** This App's review count, Double value expected. */ + public static final String EXTRAINFO_APP_REVIEW_COUNT = "APP_REVIEW_COUNT"; + /** If this App contains the Ads Disclaimer, Boolean value expected. */ + public static final String EXTRAINFO_APP_CONTAINS_ADS_DISCLAIMER = + "APP_CONTAINS_ADS_DISCLAIMER"; + /** If this App contains the IAP Disclaimer, Boolean value expected. */ + public static final String EXTRAINFO_APP_CONTAINS_IAP_DISCLAIMER = + "APP_CONTAINS_IAP_DISCLAIMER"; + /** This App's short description, String value expected. */ + public static final String EXTRAINFO_SHORT_DESCRIPTION = "SHORT_DESCRIPTION"; + /** This App's long description, String value expected. */ + public static final String EXTRAINFO_LONG_DESCRIPTION = "LONG_DESCRIPTION"; + /** This App's screenshots, List value expected. */ + public static final String EXTRAINFO_SCREENSHOTS = "SCREENSHOTS"; + /** Editor's choices for this App, ArrayList value expected. */ + public static final String EXTRAINFO_APP_BADGES = "APP_BADGES"; + /** Pre-registration game's action button text, String value expected. */ + @SuppressLint("IntentName") + public static final String EXTRAINFO_ACTION_BUTTON_TEXT_PREREGISTERING = "ACTION_BUTTON_TEXT"; + /** Pre-registration game's action button image, ImageLoadingBundle value expected. */ + @SuppressLint("IntentName") + public static final String EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING = "ACTION_BUTTON_IMAGE"; + /** Web content's URL, String value expected. */ + public static final String EXTRAINFO_WEB_URL = "WEB_URL"; + /** Web content's domain icon URL, String value expected. */ + public static final String EXTRAINFO_WEB_ICON = "WEB_ICON"; + + @NonNull + private Bundle mExtraInfos; + + private SearchResult(Parcel in) { + this.mTitle = in.readString(); + this.mSnippet = in.readString(); + this.mScore = in.readFloat(); + this.mExtraInfos = in.readBundle(); + } + + private SearchResult(String title, String snippet, float score, Bundle extraInfos) { + mTitle = title; + mSnippet = snippet; + mScore = score; + mExtraInfos = extraInfos; + } + + /** Gets the search result title. */ + @NonNull + public String getTitle() { + return mTitle; + } + + /** Gets the search result snippet. */ + @NonNull + public String getSnippet() { + return mSnippet; + } + + /** Gets the ranking score provided by the original search provider. */ + public float getScore() { + return mScore; + } + + /** Gets the extra information associated with the search result. */ + @NonNull + public Bundle getExtraInfos() { + return mExtraInfos; + } + + private SearchResult(Builder b) { + mTitle = requireNonNull(b.mTitle); + mSnippet = requireNonNull(b.mSnippet); + mScore = b.mScore; + mExtraInfos = requireNonNull(b.mExtraInfos); + } + + /** + * + * @see Creator + * + */ + @NonNull public static final Creator CREATOR = new Creator() { + @Override + public SearchResult createFromParcel(Parcel p) { + return new SearchResult(p); + } + + @Override + public SearchResult[] newArray(int size) { + return new SearchResult[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(this.mTitle); + dest.writeString(this.mSnippet); + dest.writeFloat(this.mScore); + dest.writeBundle(this.mExtraInfos); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + SearchResult that = (SearchResult) obj; + return Objects.equals(mTitle, that.mTitle) + && Objects.equals(mSnippet, that.mSnippet) + && mScore == that.mScore + && Objects.equals(mExtraInfos, that.mExtraInfos); + } + + @Override + public int hashCode() { + return Objects.hash(mTitle, mSnippet, mScore, mExtraInfos); + } + + /** + * Builder constructing SearchResult. + * + * @hide + */ + @SystemApi + public static final class Builder { + private String mTitle; + private String mSnippet; + private float mScore; + private Bundle mExtraInfos; + + /** + * + * @param title the title to the search result. + * @param extraInfos the extra infos associated with the search result. + * + * @hide + */ + @SystemApi + public Builder(@NonNull String title, @NonNull Bundle extraInfos) { + mTitle = title; + mExtraInfos = extraInfos; + + mSnippet = ""; + mScore = 0; + } + + /** Sets the title to the search result. */ + @NonNull + public Builder setTitle(@NonNull String title) { + this.mTitle = title; + return this; + } + + /** Sets the snippet to the search result. */ + @NonNull + public Builder setSnippet(@NonNull String snippet) { + this.mSnippet = snippet; + return this; + } + + /** Sets the ranking score to the search result. */ + @NonNull + public Builder setScore(float score) { + this.mScore = score; + return this; + } + + /** Adds extra information to the search result for rendering in the UI. */ + @NonNull + public Builder setExtraInfos(@NonNull Bundle extraInfos) { + this.mExtraInfos = extraInfos; + return this; + } + + /** Builds a SearchResult based-on the given parameters. */ + @NonNull + public SearchResult build() { + if (mTitle == null || mExtraInfos == null || mSnippet == null) { + throw new IllegalStateException("Please make sure all required args are assigned."); + } + + return new SearchResult(mTitle, mSnippet, mScore, mExtraInfos); + } + } +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ce2efcf4ac7f5..b8b96b7248ade 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4978,6 +4978,20 @@ public abstract class Context { @SystemApi public static final String SMARTSPACE_SERVICE = "smartspace"; + /** + * Used for getting the cloudsearch service. + * + *

NOTE: this service is optional; callers of + * {@code Context.getSystemServiceName(CLOUDSEARCH_SERVICE)} should check for {@code null}. + * + * @hide + * @see #getSystemService(String) + */ + // TODO(216507592): Change cloudsearch_service to cloudsearch. + @SystemApi + @SuppressLint("ServiceName") + public static final String CLOUDSEARCH_SERVICE = "cloudsearch_service"; + /** * Use with {@link #getSystemService(String)} to access the * {@link com.android.server.voiceinteraction.SoundTriggerService}. diff --git a/core/java/android/service/cloudsearch/CloudSearchService.java b/core/java/android/service/cloudsearch/CloudSearchService.java new file mode 100644 index 0000000000000..5efa1acf8ffa7 --- /dev/null +++ b/core/java/android/service/cloudsearch/CloudSearchService.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.service.cloudsearch; + +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + +import android.annotation.CallSuper; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.app.Service; +import android.app.cloudsearch.ICloudSearchManager; +import android.app.cloudsearch.SearchRequest; +import android.app.cloudsearch.SearchResponse; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.service.cloudsearch.ICloudSearchService.Stub; +import android.util.Log; +import android.util.Slog; + +/** + * A service for returning search results from cloud services in response to an on device query. + * + *

To extend this class, you must declare the service in your manifest file with + * the {@link android.Manifest.permission#MANAGE_CLOUDSEARCH} permission + * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:

+ *
+ *  
+ *  
+ *       
+ *           
+ *               
+ *           
+ *       
+ *
+ *      
+ *
+ *  
+ * 
+ * + * @hide + */ +@SystemApi +public abstract class CloudSearchService extends Service { + + /** + * The {@link Intent} that must be declared as handled by the service. + * + *

The service must also require the {@link android.permission#MANAGE_CLOUDSEARCH} + * permission. + * + * @hide + */ + public static final String SERVICE_INTERFACE = + "android.service.cloudsearch.CloudSearchService"; + private static final boolean DEBUG = false; + private static final String TAG = "CloudSearchService"; + private Handler mHandler; + private ICloudSearchManager mService; + + private final android.service.cloudsearch.ICloudSearchService mInterface = new Stub() { + @Override + public void onSearch(SearchRequest request) { + mHandler.sendMessage( + obtainMessage(CloudSearchService::onSearch, + CloudSearchService.this, request)); + } + }; + + @CallSuper + @Override + public void onCreate() { + super.onCreate(); + if (DEBUG) { + Log.d(TAG, "onCreate CloudSearchService"); + } + mHandler = new Handler(Looper.getMainLooper(), null, true); + + IBinder b = ServiceManager.getService(Context.CLOUDSEARCH_SERVICE); + mService = android.app.cloudsearch.ICloudSearchManager.Stub.asInterface(b); + } + + /** + * onSearch receives the input request, retrievals the search provider's own + * corpus and returns the search response through returnResults below. + * + *@param request the search request passed from the client. + * + */ + public abstract void onSearch(@NonNull SearchRequest request); + + /** + * returnResults returnes the response and its associated requestId, where + * requestIs is generated by request through getRequestId(). + * + *@param requestId the request ID got from request.getRequestId(). + *@param response the search response returned from the search provider. + * + */ + public final void returnResults(@NonNull String requestId, + @NonNull SearchResponse response) { + try { + mService.returnResults(mInterface.asBinder(), requestId, response); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + @NonNull + public final IBinder onBind(@NonNull Intent intent) { + if (DEBUG) { + Log.d(TAG, "onBind CloudSearchService"); + } + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return mInterface.asBinder(); + } + Slog.w(TAG, "Tried to bind to wrong intent (should be " + + SERVICE_INTERFACE + ": " + intent); + return null; + } +} diff --git a/core/java/android/service/cloudsearch/ICloudSearchService.aidl b/core/java/android/service/cloudsearch/ICloudSearchService.aidl new file mode 100644 index 0000000000000..104bf99f1537c --- /dev/null +++ b/core/java/android/service/cloudsearch/ICloudSearchService.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.cloudsearch; + +import android.app.cloudsearch.SearchRequest; + +/** + * Interface from the system to CloudSearch service. + * + * @hide + */ +oneway interface ICloudSearchService { + void onSearch(in SearchRequest request); +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 3a842ee6e7f3f..77789bcdc114e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5727,6 +5727,11 @@ + + + + + +