Merge "Remove impl of cloudsearch api" into tm-qpr-dev

This commit is contained in:
Hui Wu
2022-09-22 22:40:13 +00:00
committed by Android (Google) Code Review
19 changed files with 104 additions and 1454 deletions

View File

@@ -29,8 +29,6 @@ import android.app.ambientcontext.AmbientContextManager;
import android.app.ambientcontext.IAmbientContextManager;
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;
@@ -1231,17 +1229,6 @@ public final class SystemServiceRegistry {
}
});
registerService(Context.CLOUDSEARCH_SERVICE, CloudSearchManager.class,
new CachedServiceFetcher<CloudSearchManager>() {
@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<AppPredictionManager>() {
@Override

View File

@@ -15,17 +15,15 @@
*/
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.
*
@@ -41,7 +39,7 @@ public class CloudSearchManager {
/**
* Invoked by receiving app with the result of the search.
*
* @param request original request for the search.
* @param request original request for the search.
* @param response search result.
*/
void onSearchSucceeded(@NonNull SearchRequest request, @NonNull SearchResponse response);
@@ -51,17 +49,15 @@ public class CloudSearchManager {
* 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 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;
public CloudSearchManager() {
}
/**
@@ -69,10 +65,9 @@ public class CloudSearchManager {
* 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 request request to be searched.
* @param callbackExecutor where the callback is invoked.
* @param callback invoked when the result is available.
*
* @param callback invoked when the result is available.
* @hide
*/
@SystemApi
@@ -80,49 +75,8 @@ public class CloudSearchManager {
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));
}
callbackExecutor.execute(
() -> callback.onSearchFailed(request,
new SearchResponse.Builder(SearchResponse.SEARCH_STATUS_UNKNOWN).build()));
}
}

View File

@@ -1,33 +0,0 @@
/**
* 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);
}

View File

@@ -1,31 +0,0 @@
/**
* 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);
}

View File

@@ -1,19 +0,0 @@
/**
* 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;

View File

@@ -15,8 +15,6 @@
*/
package android.app.cloudsearch;
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
@@ -28,7 +26,6 @@ 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.
@@ -38,36 +35,6 @@ import java.util.Objects;
@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.
@@ -76,121 +43,83 @@ public final class SearchRequest implements Parcelable {
*/
@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.
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 =
"android.app.cloudsearch.IS_PRESUBMIT_SUGGESTION";
/** The target search provider list of package names(separated by ;), String value expected.
/**
* 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 =
"android.app.cloudsearch.SEARCH_PROVIDER_FILTER";
@NonNull
private Bundle mSearchConstraints;
/** Auto set by system servier, and the caller cannot set it.
*
* The caller's package name.
*
*/
@NonNull
private String mCallerPackageName;
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();
this.mCallerPackageName = in.readString();
}
private SearchRequest(String query, int resultOffset, int resultNumber, float maxLatencyMillis,
Bundle searchConstraints, String callerPackageName) {
mQuery = query;
mResultOffset = resultOffset;
mResultNumber = resultNumber;
mMaxLatencyMillis = maxLatencyMillis;
mSearchConstraints = searchConstraints;
mCallerPackageName = callerPackageName;
private SearchRequest() {
}
/** Returns the original query. */
@NonNull
public String getQuery() {
return mQuery;
return "";
}
/** Returns the result offset. */
public int getResultOffset() {
return mResultOffset;
return 0;
}
/** Returns the expected number of search results. */
public int getResultNumber() {
return mResultNumber;
return 0;
}
/** Returns the maximum latency requirement. */
public float getMaxLatencyMillis() {
return mMaxLatencyMillis;
return 0;
}
/** Returns the search constraints. */
@NonNull
public Bundle getSearchConstraints() {
return mSearchConstraints;
return Bundle.EMPTY;
}
/** Gets the caller's package name. */
@NonNull
public String getCallerPackageName() {
return mCallerPackageName;
return "";
}
/** Returns the search request id, which is used to identify the request. */
@NonNull
public String getRequestId() {
if (mId == null || mId.length() == 0) {
mId = String.valueOf(toString().hashCode());
}
return mId;
return "";
}
/** Sets the caller, and this will be set by the system server.
/**
* Sets the caller, and this will be set by the system server.
*
* @hide
*/
public void setCallerPackageName(@NonNull String callerPackageName) {
this.mCallerPackageName = callerPackageName;
}
private SearchRequest(Builder b) {
mQuery = requireNonNull(b.mQuery);
mResultOffset = b.mResultOffset;
mResultNumber = b.mResultNumber;
mMaxLatencyMillis = b.mMaxLatencyMillis;
mSearchConstraints = requireNonNull(b.mSearchConstraints);
mCallerPackageName = requireNonNull(b.mCallerPackageName);
}
/**
* @see Creator
*
*/
@NonNull
public static final Creator<SearchRequest> CREATOR = new Creator<SearchRequest>() {
@Override
public SearchRequest createFromParcel(Parcel p) {
return new SearchRequest(p);
return new SearchRequest();
}
@Override
@@ -201,13 +130,6 @@ public final class SearchRequest implements Parcelable {
@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());
dest.writeString(this.mCallerPackageName);
}
@Override
@@ -217,44 +139,17 @@ public final class SearchRequest implements Parcelable {
@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)
&& Objects.equals(mCallerPackageName, that.mCallerPackageName);
return false;
}
@Override
public String toString() {
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);
}
return String.format("SearchRequest: {query:%s,offset:%d;number:%d;max_latency:%f;"
+ "is_presubmit:%b;search_provider:%s;callerPackageName:%s}", mQuery,
mResultOffset, mResultNumber, mMaxLatencyMillis, isPresubmit, searchProvider,
mCallerPackageName);
return "";
}
@Override
public int hashCode() {
return Objects.hash(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis,
mSearchConstraints, mCallerPackageName);
return 0;
}
/**
@@ -264,87 +159,62 @@ public final class SearchRequest implements Parcelable {
*/
@SystemApi
public static final class Builder {
private String mQuery;
private int mResultOffset;
private int mResultNumber;
private float mMaxLatencyMillis;
private Bundle mSearchConstraints;
private String mCallerPackageName;
/**
*
* @param query the query for search.
*
* @hide
*/
@SystemApi
public Builder(@NonNull String query) {
mQuery = query;
mResultOffset = 0;
mResultNumber = 10;
mMaxLatencyMillis = 200;
mSearchConstraints = Bundle.EMPTY;
mCallerPackageName = "DEFAULT_CALLER";
}
/** 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. */
/**
* 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;
}
/** Sets the caller, and this will be set by the system server.
/**
* Sets the caller, and this will be set by the system server.
*
* @hide
*/
@NonNull
@TestApi
public Builder setCallerPackageName(@NonNull String callerPackageName) {
this.mCallerPackageName = callerPackageName;
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, mCallerPackageName);
return new SearchRequest();
}
}
}

View File

@@ -1,19 +0,0 @@
/**
* 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;

View File

@@ -15,8 +15,6 @@
*/
package android.app.cloudsearch;
import static java.util.Objects.requireNonNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -25,7 +23,6 @@ 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.
@@ -37,77 +34,53 @@ 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 {}
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<SearchResult> 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<SearchResult> searchResults) {
mStatusCode = statusCode;
mSource = source;
mSearchResults = searchResults;
private SearchResponse() {
}
/** Gets the search status code. */
public int getStatusCode() {
return mStatusCode;
return SEARCH_STATUS_UNKNOWN;
}
/** Gets the search provider package name. */
@NonNull
public String getSource() {
return mSource;
return "";
}
/** Gets the search results, which can be empty. */
@NonNull
public List<SearchResult> getSearchResults() {
return mSearchResults;
return new ArrayList<SearchResult>();
}
/** Sets the search provider, and this will be set by the system server.
/**
* 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<SearchResponse> CREATOR = new Creator<SearchResponse>() {
@NonNull
public static final Creator<SearchResponse> CREATOR = new Creator<SearchResponse>() {
@Override
public SearchResponse createFromParcel(Parcel p) {
return new SearchResponse(p);
return new SearchResponse();
}
@Override
@@ -118,9 +91,6 @@ public final class SearchResponse implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(this.mStatusCode);
dest.writeString(this.mSource);
dest.writeTypedList(this.mSearchResults);
}
@Override
@@ -130,23 +100,12 @@ public final class SearchResponse implements Parcelable {
@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);
return false;
}
@Override
public int hashCode() {
return Objects.hash(mStatusCode, mSource, mSearchResults);
return 0;
}
/**
@@ -156,59 +115,40 @@ public final class SearchResponse implements Parcelable {
*/
@SystemApi
public static final class Builder {
private int mStatusCode;
private String mSource;
private List<SearchResult> 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<SearchResult>();
}
/** 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.
/**
* 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<SearchResult> 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);
return new SearchResponse();
}
}
}

View File

@@ -1,19 +0,0 @@
/**
* 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;

View File

@@ -15,8 +15,6 @@
*/
package android.app.cloudsearch;
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.StringDef;
import android.annotation.SuppressLint;
@@ -27,7 +25,6 @@ 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.
@@ -37,17 +34,6 @@ import java.util.Objects;
@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.
@@ -56,28 +42,30 @@ public final class SearchResult implements Parcelable {
*/
@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_ACTION_APP_CARD,
EXTRAINFO_ACTION_INSTALL_BUTTON,
EXTRAINFO_APP_PACKAGE_NAME,
EXTRAINFO_APP_INSTALL_COUNT,
EXTRAINFO_WEB_URL,
EXTRAINFO_WEB_ICON})
public @interface SearchResultExtraInfoKey {}
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_ACTION_APP_CARD,
EXTRAINFO_ACTION_INSTALL_BUTTON,
EXTRAINFO_APP_PACKAGE_NAME,
EXTRAINFO_APP_INSTALL_COUNT,
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 = "android.app.cloudsearch.APP_DOMAIN_URL";
/** This App icon, android.graphics.drawable.Icon expected. */
@@ -90,7 +78,8 @@ public final class SearchResult implements Parcelable {
/** This App developer's name, Double value expected. */
public static final String EXTRAINFO_APP_STAR_RATING =
"android.app.cloudsearch.APP_STAR_RATING";
/** This App's IARC rating, String value expected.
/**
* 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.
@@ -142,62 +131,40 @@ public final class SearchResult implements Parcelable {
/** Web content's domain icon, android.graphics.drawable.Icon expected. */
public static final String EXTRAINFO_WEB_ICON = "android.app.cloudsearch.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;
private SearchResult() {
}
/** Gets the search result title. */
@NonNull
public String getTitle() {
return mTitle;
return "";
}
/** Gets the search result snippet. */
@NonNull
public String getSnippet() {
return mSnippet;
return "";
}
/** Gets the ranking score provided by the original search provider. */
public float getScore() {
return mScore;
return 0;
}
/** 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);
return Bundle.EMPTY;
}
/**
*
* @see Creator
*
*/
@NonNull public static final Creator<SearchResult> CREATOR = new Creator<SearchResult>() {
@NonNull
public static final Creator<SearchResult> CREATOR = new Creator<SearchResult>() {
@Override
public SearchResult createFromParcel(Parcel p) {
return new SearchResult(p);
return new SearchResult();
}
@Override
@@ -208,10 +175,6 @@ public final class SearchResult implements Parcelable {
@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
@@ -221,24 +184,12 @@ public final class SearchResult implements Parcelable {
@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);
return false;
}
@Override
public int hashCode() {
return Objects.hash(mTitle, mSnippet, mScore, mExtraInfos);
return 0;
}
/**
@@ -248,63 +199,43 @@ public final class SearchResult implements Parcelable {
*/
@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 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);
return new SearchResult();
}
}
}

View File

@@ -15,25 +15,14 @@
*/
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.
@@ -72,39 +61,18 @@ public abstract class CloudSearchService extends Service {
*/
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.
*
* @param request the search request passed from the client.
*/
public abstract void onSearch(@NonNull SearchRequest request);
@@ -112,30 +80,16 @@ public abstract class CloudSearchService extends Service {
* 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.
*
* @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();
}
@NonNull SearchResponse response) {
}
@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;
}
}

View File

@@ -1,28 +0,0 @@
/*
* 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);
}

View File

@@ -89,7 +89,6 @@ filegroup {
":services.autofill-sources",
":services.backup-sources",
":backuplib-sources",
":services.cloudsearch-sources",
":services.companion-sources",
":services.contentcapture-sources",
":services.contentsuggestions-sources",
@@ -144,7 +143,6 @@ java_library {
"services.appwidget",
"services.autofill",
"services.backup",
"services.cloudsearch",
"services.companion",
"services.contentcapture",
"services.contentsuggestions",

View File

@@ -1,22 +0,0 @@
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
}
filegroup {
name: "services.cloudsearch-sources",
srcs: ["java/**/*.java"],
path: "java",
visibility: ["//frameworks/base/services"],
}
java_library_static {
name: "services.cloudsearch",
defaults: ["platform_service_defaults"],
srcs: [":services.cloudsearch-sources"],
libs: ["services.core"],
}

View File

@@ -1,202 +0,0 @@
/*
* 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 com.android.server.cloudsearch;
import static android.Manifest.permission.MANAGE_CLOUDSEARCH;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.content.Context.CLOUDSEARCH_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.cloudsearch.ICloudSearchManager;
import android.app.cloudsearch.ICloudSearchManagerCallback;
import android.app.cloudsearch.SearchRequest;
import android.app.cloudsearch.SearchResponse;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.util.Slog;
import com.android.internal.R;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
* A service used to return cloudsearch targets given a query.
*/
public class CloudSearchManagerService extends
AbstractMasterSystemService<CloudSearchManagerService, CloudSearchPerUserService> {
private static final String TAG = CloudSearchManagerService.class.getSimpleName();
private static final boolean DEBUG = false;
private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
private final Context mContext;
public CloudSearchManagerService(Context context) {
super(context, new FrameworkResourcesServiceNameResolver(context,
R.array.config_defaultCloudSearchServices, true), null,
PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mContext = context;
}
@Override
protected CloudSearchPerUserService newServiceLocked(int resolvedUserId, boolean disabled) {
return new CloudSearchPerUserService(this, mLock, resolvedUserId, "");
}
@Override
protected List<CloudSearchPerUserService> newServiceListLocked(int resolvedUserId,
boolean disabled, String[] serviceNames) {
if (serviceNames == null) {
return new ArrayList<>();
}
List<CloudSearchPerUserService> serviceList =
new ArrayList<>(serviceNames.length);
for (int i = 0; i < serviceNames.length; i++) {
if (serviceNames[i] == null) {
continue;
}
serviceList.add(new CloudSearchPerUserService(this, mLock, resolvedUserId,
serviceNames[i]));
}
return serviceList;
}
@Override
public void onStart() {
publishBinderService(CLOUDSEARCH_SERVICE, new CloudSearchManagerStub());
}
@Override
protected void enforceCallingPermissionForManagement() {
getContext().enforceCallingPermission(MANAGE_CLOUDSEARCH, TAG);
}
@Override // from AbstractMasterSystemService
protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
final CloudSearchPerUserService service = peekServiceForUserLocked(userId);
if (service != null) {
service.onPackageUpdatedLocked();
}
}
@Override // from AbstractMasterSystemService
protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
final CloudSearchPerUserService service = peekServiceForUserLocked(userId);
if (service != null) {
service.onPackageRestartedLocked();
}
}
@Override
protected int getMaximumTemporaryServiceDurationMs() {
return MAX_TEMP_SERVICE_DURATION_MS;
}
private class CloudSearchManagerStub extends ICloudSearchManager.Stub {
@Override
public void search(@NonNull SearchRequest searchRequest,
@NonNull ICloudSearchManagerCallback callBack) {
searchRequest.setCallerPackageName(
mContext.getPackageManager().getNameForUid(Binder.getCallingUid()));
runForUser("search", (service) -> {
synchronized (service.mLock) {
service.onSearchLocked(searchRequest, callBack);
}
});
}
@Override
public void returnResults(IBinder token, String requestId, SearchResponse response) {
runForUser("returnResults", (service) -> {
synchronized (service.mLock) {
service.onReturnResultsLocked(token, requestId, response);
}
});
}
public void destroy(@NonNull SearchRequest searchRequest) {
runForUser("destroyCloudSearchSession", (service) -> {
synchronized (service.mLock) {
service.onDestroyLocked(searchRequest.getRequestId());
}
});
}
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@Nullable FileDescriptor err,
@NonNull String[] args, @Nullable ShellCallback callback,
@NonNull ResultReceiver resultReceiver) {
new CloudSearchManagerServiceShellCommand(CloudSearchManagerService.this)
.exec(this, in, out, err, args, callback, resultReceiver);
}
private void runForUser(@NonNull final String func,
@NonNull final Consumer<CloudSearchPerUserService> c) {
ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
Binder.getCallingUserHandle().getIdentifier(), false, ALLOW_NON_FULL,
null, null);
if (DEBUG) {
Slog.d(TAG, "runForUser:" + func + " from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
}
Context ctx = getContext();
if (!(ctx.checkCallingPermission(MANAGE_CLOUDSEARCH) == PERMISSION_GRANTED
|| mServiceNameResolver.isTemporary(userId)
|| mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
String msg = "Permission Denial: Cannot call " + func + " from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
final List<CloudSearchPerUserService> services =
getServiceListForUserLocked(userId);
for (int i = 0; i < services.size(); i++) {
c.accept(services.get(i));
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
}

View File

@@ -1,89 +0,0 @@
/*
* 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 com.android.server.cloudsearch;
import android.annotation.NonNull;
import android.os.ShellCommand;
import java.io.PrintWriter;
/**
* The shell command implementation for the CloudSearchManagerService.
*/
public class CloudSearchManagerServiceShellCommand extends ShellCommand {
private static final String TAG =
CloudSearchManagerServiceShellCommand.class.getSimpleName();
private final CloudSearchManagerService mService;
public CloudSearchManagerServiceShellCommand(@NonNull CloudSearchManagerService service) {
mService = service;
}
@Override
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
final PrintWriter pw = getOutPrintWriter();
switch (cmd) {
case "set": {
final String what = getNextArgRequired();
switch (what) {
case "temporary-service": {
final int userId = Integer.parseInt(getNextArgRequired());
String serviceName = getNextArg();
if (serviceName == null) {
mService.resetTemporaryService(userId);
pw.println("CloudSearchService temporarily reset. ");
return 0;
}
final int duration = Integer.parseInt(getNextArgRequired());
String[] services = serviceName.split(";");
if (services.length == 0) {
return 0;
} else {
mService.setTemporaryServices(userId, services, duration);
}
pw.println("CloudSearchService temporarily set to " + serviceName
+ " for " + duration + "ms");
break;
}
}
}
break;
default:
return handleDefaultCommands(cmd);
}
return 0;
}
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter()) {
pw.println("CloudSearchManagerService commands:");
pw.println(" help");
pw.println(" Prints this help text.");
pw.println("");
pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]");
pw.println(" Temporarily (for DURATION ms) changes the service implemtation.");
pw.println(" To reset, call with just the USER_ID argument.");
pw.println("");
}
}
}

View File

@@ -1,401 +0,0 @@
/*
* 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 com.android.server.cloudsearch;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
import android.app.cloudsearch.ICloudSearchManagerCallback;
import android.app.cloudsearch.SearchRequest;
import android.app.cloudsearch.SearchResponse;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.service.cloudsearch.CloudSearchService;
import android.service.cloudsearch.ICloudSearchService;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AbstractRemoteService;
import com.android.server.CircularQueue;
import com.android.server.infra.AbstractPerUserSystemService;
/**
* Per-user instance of {@link CloudSearchManagerService}.
*/
public class CloudSearchPerUserService extends
AbstractPerUserSystemService<CloudSearchPerUserService, CloudSearchManagerService>
implements RemoteCloudSearchService.RemoteCloudSearchServiceCallbacks {
private static final String TAG = CloudSearchPerUserService.class.getSimpleName();
private static final int QUEUE_SIZE = 10;
@GuardedBy("mLock")
private final CircularQueue<String, CloudSearchCallbackInfo> mCallbackQueue =
new CircularQueue<>(QUEUE_SIZE);
private final String mServiceName;
private final ComponentName mRemoteComponentName;
@Nullable
@GuardedBy("mLock")
private RemoteCloudSearchService mRemoteService;
/**
* When {@code true}, remote service died but service state is kept so it's restored after
* the system re-binds to it.
*/
@GuardedBy("mLock")
private boolean mZombie;
protected CloudSearchPerUserService(CloudSearchManagerService master,
Object lock, int userId, String serviceName) {
super(master, lock, userId);
mServiceName = serviceName;
mRemoteComponentName = ComponentName.unflattenFromString(mServiceName);
}
@Override // from PerUserSystemService
protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
throws NameNotFoundException {
ServiceInfo si;
try {
si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
PackageManager.GET_META_DATA, mUserId);
} catch (RemoteException e) {
throw new NameNotFoundException("Could not get service for " + serviceComponent);
}
// TODO(b/177858728): must check that either the service is from a system component,
// or it matches a service set by shell cmd (so it can be used on CTS tests and when
// OEMs are implementing the real service and also verify the proper permissions
return si;
}
@GuardedBy("mLock")
@Override // from PerUserSystemService
protected boolean updateLocked(boolean disabled) {
final boolean enabledChanged = super.updateLocked(disabled);
if (enabledChanged) {
if (isEnabledLocked()) {
// Send the pending sessions over to the service
resurrectSessionsLocked();
} else {
// Clear the remote service for the next call
updateRemoteServiceLocked();
}
}
return enabledChanged;
}
/**
* Notifies the service of a new cloudsearch session.
*/
@GuardedBy("mLock")
public void onSearchLocked(@NonNull SearchRequest searchRequest,
@NonNull ICloudSearchManagerCallback callback) {
if (mRemoteComponentName == null) {
return;
}
String filterList = searchRequest.getSearchConstraints().containsKey(
SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER)
? searchRequest.getSearchConstraints().getString(
SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER) : "";
String remoteServicePackageName = mRemoteComponentName.getPackageName();
// By default, all providers are marked as wanted.
boolean wantedProvider = true;
if (filterList.length() > 0) {
// If providers are specified by the client,
wantedProvider = false;
String[] providersSpecified = filterList.split(";");
for (int i = 0; i < providersSpecified.length; i++) {
if (providersSpecified[i].equals(remoteServicePackageName)) {
wantedProvider = true;
break;
}
}
}
// If the provider was not requested by the Client, the request will not be sent to the
// provider.
if (!wantedProvider) {
// TODO(216520546) Send a failure callback to the client.
return;
}
final boolean serviceExists = resolveService(searchRequest,
s -> s.onSearch(searchRequest));
String requestId = searchRequest.getRequestId();
if (serviceExists && !mCallbackQueue.containsKey(requestId)) {
final CloudSearchCallbackInfo sessionInfo = new CloudSearchCallbackInfo(
requestId, searchRequest, callback, callback.asBinder(), () -> {
synchronized (mLock) {
onDestroyLocked(requestId);
}
});
if (sessionInfo.linkToDeath()) {
CloudSearchCallbackInfo removedInfo = mCallbackQueue.put(requestId, sessionInfo);
if (removedInfo != null) {
removedInfo.destroy();
}
} else {
// destroy the session if calling process is already dead
onDestroyLocked(requestId);
}
}
}
/**
* Used to return results back to the clients.
*/
@GuardedBy("mLock")
public void onReturnResultsLocked(@NonNull IBinder token,
@NonNull String requestId,
@NonNull SearchResponse response) {
if (mRemoteService == null) {
return;
}
ICloudSearchService serviceInterface = mRemoteService.getServiceInterface();
if (serviceInterface == null || token != serviceInterface.asBinder()) {
return;
}
if (mCallbackQueue.containsKey(requestId)) {
response.setSource(mServiceName);
final CloudSearchCallbackInfo sessionInfo = mCallbackQueue.getElement(requestId);
try {
if (response.getStatusCode() == SearchResponse.SEARCH_STATUS_OK) {
sessionInfo.mCallback.onSearchSucceeded(response);
} else {
sessionInfo.mCallback.onSearchFailed(response);
}
} catch (RemoteException e) {
if (mMaster.debug) {
Slog.e(TAG, "Exception in posting results");
e.printStackTrace();
}
onDestroyLocked(requestId);
}
}
}
/**
* Notifies the server about the end of an existing cloudsearch session.
*/
@GuardedBy("mLock")
public void onDestroyLocked(@NonNull String requestId) {
if (isDebug()) {
Slog.d(TAG, "onDestroyLocked(): requestId=" + requestId);
}
final CloudSearchCallbackInfo sessionInfo = mCallbackQueue.removeElement(requestId);
if (sessionInfo != null) {
sessionInfo.destroy();
}
}
@Override
public void onFailureOrTimeout(boolean timedOut) {
if (isDebug()) {
Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut);
}
// Do nothing, we are just proxying to the cloudsearch service
}
@Override
public void onConnectedStateChanged(boolean connected) {
if (isDebug()) {
Slog.d(TAG, "onConnectedStateChanged(): connected=" + connected);
}
if (connected) {
synchronized (mLock) {
if (mZombie) {
// Validation check - shouldn't happen
if (mRemoteService == null) {
Slog.w(TAG, "Cannot resurrect sessions because remote service is null");
return;
}
mZombie = false;
resurrectSessionsLocked();
}
}
}
}
@Override
public void onServiceDied(RemoteCloudSearchService service) {
if (isDebug()) {
Slog.w(TAG, "onServiceDied(): service=" + service);
}
synchronized (mLock) {
mZombie = true;
}
updateRemoteServiceLocked();
}
@GuardedBy("mLock")
private void updateRemoteServiceLocked() {
if (mRemoteService != null) {
mRemoteService.destroy();
mRemoteService = null;
}
}
void onPackageUpdatedLocked() {
if (isDebug()) {
Slog.v(TAG, "onPackageUpdatedLocked()");
}
destroyAndRebindRemoteService();
}
void onPackageRestartedLocked() {
if (isDebug()) {
Slog.v(TAG, "onPackageRestartedLocked()");
}
destroyAndRebindRemoteService();
}
private void destroyAndRebindRemoteService() {
if (mRemoteService == null) {
return;
}
if (isDebug()) {
Slog.d(TAG, "Destroying the old remote service.");
}
mRemoteService.destroy();
mRemoteService = null;
synchronized (mLock) {
mZombie = true;
}
mRemoteService = getRemoteServiceLocked();
if (mRemoteService != null) {
if (isDebug()) {
Slog.d(TAG, "Rebinding to the new remote service.");
}
mRemoteService.reconnect();
}
}
/**
* Called after the remote service connected, it's used to restore state from a 'zombie'
* service (i.e., after it died).
*/
private void resurrectSessionsLocked() {
final int numCallbacks = mCallbackQueue.size();
if (isDebug()) {
Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on "
+ numCallbacks + " requests.");
}
for (CloudSearchCallbackInfo callbackInfo : mCallbackQueue.values()) {
callbackInfo.resurrectSessionLocked(this, callbackInfo.mToken);
}
}
@GuardedBy("mLock")
@Nullable
protected boolean resolveService(
@NonNull final SearchRequest requestId,
@NonNull final AbstractRemoteService.AsyncRequest<ICloudSearchService> cb) {
final RemoteCloudSearchService service = getRemoteServiceLocked();
if (service != null) {
service.executeOnResolvedService(cb);
}
return service != null;
}
@GuardedBy("mLock")
@Nullable
private RemoteCloudSearchService getRemoteServiceLocked() {
if (mRemoteService == null) {
final String serviceName = getComponentNameForMultipleLocked(mServiceName);
if (serviceName == null) {
if (mMaster.verbose) {
Slog.v(TAG, "getRemoteServiceLocked(): not set");
}
return null;
}
ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
mRemoteService = new RemoteCloudSearchService(getContext(),
CloudSearchService.SERVICE_INTERFACE, serviceComponent, mUserId, this,
mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
}
return mRemoteService;
}
private static final class CloudSearchCallbackInfo {
private static final boolean DEBUG = false; // Do not submit with true
@NonNull
final IBinder mToken;
@NonNull
final IBinder.DeathRecipient mDeathRecipient;
@NonNull
private final String mRequestId;
@NonNull
private final SearchRequest mSearchRequest;
private final ICloudSearchManagerCallback mCallback;
CloudSearchCallbackInfo(
@NonNull final String id,
@NonNull final SearchRequest request,
@NonNull final ICloudSearchManagerCallback callback,
@NonNull final IBinder token,
@NonNull final IBinder.DeathRecipient deathRecipient) {
if (DEBUG) {
Slog.d(TAG, "Creating CloudSearchSessionInfo for session Id=" + id);
}
mRequestId = id;
mSearchRequest = request;
mCallback = callback;
mToken = token;
mDeathRecipient = deathRecipient;
}
boolean linkToDeath() {
try {
mToken.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
if (DEBUG) {
Slog.w(TAG, "Caller is dead before session can be started, requestId: "
+ mRequestId);
}
return false;
}
return true;
}
void destroy() {
if (DEBUG) {
Slog.d(TAG, "Removing callback for Request Id=" + mRequestId);
}
if (mToken != null) {
mToken.unlinkToDeath(mDeathRecipient, 0);
}
mCallback.asBinder().unlinkToDeath(mDeathRecipient, 0);
}
void resurrectSessionLocked(CloudSearchPerUserService service, IBinder token) {
if (DEBUG) {
Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
+ ") for request Id=" + mRequestId);
}
service.onSearchLocked(mSearchRequest, mCallback);
}
}
}

View File

@@ -1,113 +0,0 @@
/*
* 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 com.android.server.cloudsearch;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
import android.service.cloudsearch.ICloudSearchService;
import android.text.format.DateUtils;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
/**
* Proxy to the {@link android.service.cloudsearch.CloudSearchService} implementation in another
* process.
*/
public class RemoteCloudSearchService extends
AbstractMultiplePendingRequestsRemoteService<RemoteCloudSearchService,
ICloudSearchService> {
private static final String TAG = "RemoteCloudSearchService";
private static final long TIMEOUT_IDLE_BOUND_TIMEOUT_MS = 10 * DateUtils.MINUTE_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
private final RemoteCloudSearchServiceCallbacks mCallback;
public RemoteCloudSearchService(Context context, String serviceInterface,
ComponentName componentName, int userId,
RemoteCloudSearchServiceCallbacks callback, boolean bindInstantServiceAllowed,
boolean verbose) {
super(context, serviceInterface, componentName, userId, callback,
context.getMainThreadHandler(),
bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
verbose, /* initialCapacity= */ 1);
mCallback = callback;
}
@Override
protected ICloudSearchService getServiceInterface(IBinder service) {
return ICloudSearchService.Stub.asInterface(service);
}
@Override
protected long getTimeoutIdleBindMillis() {
return TIMEOUT_IDLE_BOUND_TIMEOUT_MS;
}
@Override
protected long getRemoteRequestMillis() {
return TIMEOUT_REMOTE_REQUEST_MILLIS;
}
/**
* Schedules a request to bind to the remote service.
*/
public void reconnect() {
super.scheduleBind();
}
/**
* Schedule async request on remote service.
*/
public void scheduleOnResolvedService(@NonNull AsyncRequest<ICloudSearchService> request) {
scheduleAsyncRequest(request);
}
/**
* Execute async request on remote service immediately instead of sending it to Handler queue.
*/
public void executeOnResolvedService(@NonNull AsyncRequest<ICloudSearchService> request) {
executeAsyncRequest(request);
}
/**
* Failure callback
*/
public interface RemoteCloudSearchServiceCallbacks
extends VultureCallback<RemoteCloudSearchService> {
/**
* Notifies a the failure or timeout of a remote call.
*/
void onFailureOrTimeout(boolean timedOut);
/**
* Notifies change in connected state of the remote service.
*/
void onConnectedStateChanged(boolean connected);
}
@Override // from AbstractRemoteService
protected void handleOnConnectedStateChanged(boolean connected) {
if (mCallback != null) {
mCallback.onConnectedStateChanged(connected);
}
}
}

View File

@@ -378,8 +378,6 @@ public final class SystemServer implements Dumpable {
"com.android.server.searchui.SearchUiManagerService";
private static final String SMARTSPACE_MANAGER_SERVICE_CLASS =
"com.android.server.smartspace.SmartspaceManagerService";
private static final String CLOUDSEARCH_MANAGER_SERVICE_CLASS =
"com.android.server.cloudsearch.CloudSearchManagerService";
private static final String DEVICE_IDLE_CONTROLLER_CLASS =
"com.android.server.DeviceIdleController";
private static final String BLOB_STORE_MANAGER_SERVICE_CLASS =
@@ -1886,12 +1884,6 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(SMARTSPACE_MANAGER_SERVICE_CLASS);
t.traceEnd();
// CloudSearch manager service
// TODO: add deviceHasConfigString(context, R.string.config_defaultCloudSearchServices)
t.traceBegin("StartCloudSearchService");
mSystemServiceManager.startService(CLOUDSEARCH_MANAGER_SERVICE_CLASS);
t.traceEnd();
t.traceBegin("InitConnectivityModuleConnector");
try {
ConnectivityModuleConnector.getInstance().init(context);