blob: 1dfde528e69b930c9b7e05f1b64ebfc64d2086a0 [file] [log] [blame]
Terry Wangfebbead2019-10-17 17:05:18 -07001/*
sidchhabraa7c8f8a2020-01-16 18:38:17 -08002 * Copyright (C) 2020 The Android Open Source Project
Terry Wangfebbead2019-10-17 17:05:18 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.server.appsearch;
17
sidchhabraa7c8f8a2020-01-16 18:38:17 -080018import android.annotation.NonNull;
Alexander Dorokhine18465842020-01-21 01:08:57 -080019import android.app.appsearch.AppSearchBatchResult;
Alexander Dorokhine969f4462020-03-05 15:54:19 -080020import android.app.appsearch.AppSearchResult;
Alexander Dorokhine92ce3532020-10-06 01:39:36 -070021import android.app.appsearch.AppSearchSchema;
Alexander Dorokhinec66d67c2020-10-08 13:44:04 -070022import android.app.appsearch.GenericDocument;
Terry Wangfebbead2019-10-17 17:05:18 -070023import android.app.appsearch.IAppSearchManager;
Terry Wang26b9e5c2020-10-23 02:05:01 -070024import android.app.appsearch.SearchResultPage;
Alexander Dorokhinec9fc9602020-10-06 01:39:50 -070025import android.app.appsearch.SearchSpec;
Alexander Dorokhineebd37742020-09-22 15:02:26 -070026import android.app.appsearch.exceptions.AppSearchException;
Terry Wangfebbead2019-10-17 17:05:18 -070027import android.content.Context;
Alexander Dorokhine270d4f12020-01-15 17:24:35 -080028import android.os.Binder;
Alexander Dorokhine92ce3532020-10-06 01:39:36 -070029import android.os.Bundle;
Alexander Dorokhine270d4f12020-01-15 17:24:35 -080030import android.os.UserHandle;
Terry Wang26b9e5c2020-10-23 02:05:01 -070031import android.util.ArraySet;
Terry Wangfebbead2019-10-17 17:05:18 -070032
Alexander Dorokhinefd07eba2020-01-13 20:22:20 -080033import com.android.internal.infra.AndroidFuture;
Alexander Dorokhine270d4f12020-01-15 17:24:35 -080034import com.android.internal.util.Preconditions;
Terry Wangfebbead2019-10-17 17:05:18 -070035import com.android.server.SystemService;
Alexander Dorokhinef660d8f2020-10-29 22:37:00 -070036import com.android.server.appsearch.external.localstorage.AppSearchImpl;
Alexander Dorokhinefd07eba2020-01-13 20:22:20 -080037
Alexander Dorokhine969f4462020-03-05 15:54:19 -080038import java.io.IOException;
Alexander Dorokhine18465842020-01-21 01:08:57 -080039import java.util.List;
Terry Wang26b9e5c2020-10-23 02:05:01 -070040import java.util.Set;
Alexander Dorokhine18465842020-01-21 01:08:57 -080041
Terry Wangfebbead2019-10-17 17:05:18 -070042/**
43 * TODO(b/142567528): add comments when implement this class
44 */
45public class AppSearchManagerService extends SystemService {
Alexander Dorokhineebd37742020-09-22 15:02:26 -070046 private static final String TAG = "AppSearchManagerService";
Terry Wang6413aee2020-10-07 03:04:58 -070047 private static final char CALLING_NAME_DATABASE_DELIMITER = '$';
Terry Wangfebbead2019-10-17 17:05:18 -070048
49 public AppSearchManagerService(Context context) {
50 super(context);
51 }
52
53 @Override
54 public void onStart() {
55 publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
56 }
57
58 private class Stub extends IAppSearchManager.Stub {
Alexander Dorokhinefd07eba2020-01-13 20:22:20 -080059 @Override
Alexander Dorokhine969f4462020-03-05 15:54:19 -080060 public void setSchema(
Terry Wang6413aee2020-10-07 03:04:58 -070061 @NonNull String databaseName,
Alexander Dorokhine92ce3532020-10-06 01:39:36 -070062 @NonNull List<Bundle> schemaBundles,
Alexander Dorokhine969f4462020-03-05 15:54:19 -080063 boolean forceOverride,
64 @NonNull AndroidFuture<AppSearchResult> callback) {
Terry Wang26b9e5c2020-10-23 02:05:01 -070065 Preconditions.checkNotNull(databaseName);
Alexander Dorokhine92ce3532020-10-06 01:39:36 -070066 Preconditions.checkNotNull(schemaBundles);
Alexander Dorokhine270d4f12020-01-15 17:24:35 -080067 Preconditions.checkNotNull(callback);
68 int callingUid = Binder.getCallingUidOrThrow();
69 int callingUserId = UserHandle.getUserId(callingUid);
Jeff Sharkeyea5328c2020-10-06 11:18:09 -060070 final long callingIdentity = Binder.clearCallingIdentity();
Alexander Dorokhinefd07eba2020-01-13 20:22:20 -080071 try {
Terry Wang26b9e5c2020-10-23 02:05:01 -070072 Set<AppSearchSchema> schemas = new ArraySet<>(schemaBundles.size());
Alexander Dorokhine92ce3532020-10-06 01:39:36 -070073 for (int i = 0; i < schemaBundles.size(); i++) {
Terry Wang26b9e5c2020-10-23 02:05:01 -070074 schemas.add(new AppSearchSchema(schemaBundles.get(i)));
Alexander Dorokhine92ce3532020-10-06 01:39:36 -070075 }
Alexander Dorokhine270d4f12020-01-15 17:24:35 -080076 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
Terry Wang6413aee2020-10-07 03:04:58 -070077 databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
Terry Wang26b9e5c2020-10-23 02:05:01 -070078 impl.setSchema(databaseName, schemas, forceOverride);
Alexander Dorokhinec66d67c2020-10-08 13:44:04 -070079 callback.complete(AppSearchResult.newSuccessfulResult(/*result=*/ null));
Alexander Dorokhine179c8b82020-01-11 00:17:48 -080080 } catch (Throwable t) {
Alexander Dorokhine969f4462020-03-05 15:54:19 -080081 callback.complete(throwableToFailedResult(t));
Alexander Dorokhine270d4f12020-01-15 17:24:35 -080082 } finally {
83 Binder.restoreCallingIdentity(callingIdentity);
Alexander Dorokhine179c8b82020-01-11 00:17:48 -080084 }
85 }
86
87 @Override
Alexander Dorokhine18465842020-01-21 01:08:57 -080088 public void putDocuments(
Terry Wang6413aee2020-10-07 03:04:58 -070089 @NonNull String databaseName,
Alexander Dorokhinec66d67c2020-10-08 13:44:04 -070090 @NonNull List<Bundle> documentBundles,
Alexander Dorokhine969f4462020-03-05 15:54:19 -080091 @NonNull AndroidFuture<AppSearchBatchResult> callback) {
Terry Wang26b9e5c2020-10-23 02:05:01 -070092 Preconditions.checkNotNull(databaseName);
Alexander Dorokhinec66d67c2020-10-08 13:44:04 -070093 Preconditions.checkNotNull(documentBundles);
Alexander Dorokhinecc223452020-01-19 03:00:28 -080094 Preconditions.checkNotNull(callback);
95 int callingUid = Binder.getCallingUidOrThrow();
96 int callingUserId = UserHandle.getUserId(callingUid);
Jeff Sharkeyea5328c2020-10-06 11:18:09 -060097 final long callingIdentity = Binder.clearCallingIdentity();
Alexander Dorokhine179c8b82020-01-11 00:17:48 -080098 try {
Alexander Dorokhinecc223452020-01-19 03:00:28 -080099 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
Terry Wang6413aee2020-10-07 03:04:58 -0700100 databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
Alexander Dorokhine18465842020-01-21 01:08:57 -0800101 AppSearchBatchResult.Builder<String, Void> resultBuilder =
Alexander Dorokhine969f4462020-03-05 15:54:19 -0800102 new AppSearchBatchResult.Builder<>();
Alexander Dorokhinec66d67c2020-10-08 13:44:04 -0700103 for (int i = 0; i < documentBundles.size(); i++) {
104 GenericDocument document = new GenericDocument(documentBundles.get(i));
Alexander Dorokhine18465842020-01-21 01:08:57 -0800105 try {
Terry Wang26b9e5c2020-10-23 02:05:01 -0700106 impl.putDocument(databaseName, document);
Alexander Dorokhinec66d67c2020-10-08 13:44:04 -0700107 resultBuilder.setSuccess(document.getUri(), /*result=*/ null);
Alexander Dorokhine18465842020-01-21 01:08:57 -0800108 } catch (Throwable t) {
Alexander Dorokhine969f4462020-03-05 15:54:19 -0800109 resultBuilder.setResult(document.getUri(), throwableToFailedResult(t));
Alexander Dorokhine18465842020-01-21 01:08:57 -0800110 }
111 }
112 callback.complete(resultBuilder.build());
Alexander Dorokhinefd07eba2020-01-13 20:22:20 -0800113 } catch (Throwable t) {
114 callback.completeExceptionally(t);
Alexander Dorokhinecc223452020-01-19 03:00:28 -0800115 } finally {
116 Binder.restoreCallingIdentity(callingIdentity);
Alexander Dorokhinefd07eba2020-01-13 20:22:20 -0800117 }
118 }
Alexander Dorokhine969f4462020-03-05 15:54:19 -0800119
Alexander Dorokhine69a8d9f2020-03-06 10:43:16 -0800120 @Override
Terry Wang6413aee2020-10-07 03:04:58 -0700121 public void getDocuments(@NonNull String databaseName, @NonNull String namespace,
Alexander Dorokhinea95f44f2020-03-06 13:53:14 -0800122 @NonNull List<String> uris, @NonNull AndroidFuture<AppSearchBatchResult> callback) {
Terry Wang26b9e5c2020-10-23 02:05:01 -0700123 Preconditions.checkNotNull(databaseName);
124 Preconditions.checkNotNull(namespace);
Alexander Dorokhine69a8d9f2020-03-06 10:43:16 -0800125 Preconditions.checkNotNull(uris);
126 Preconditions.checkNotNull(callback);
127 int callingUid = Binder.getCallingUidOrThrow();
128 int callingUserId = UserHandle.getUserId(callingUid);
Jeff Sharkeyea5328c2020-10-06 11:18:09 -0600129 final long callingIdentity = Binder.clearCallingIdentity();
Alexander Dorokhine69a8d9f2020-03-06 10:43:16 -0800130 try {
131 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
Terry Wang6413aee2020-10-07 03:04:58 -0700132 databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
Alexander Dorokhinec66d67c2020-10-08 13:44:04 -0700133 AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
Alexander Dorokhinea95f44f2020-03-06 13:53:14 -0800134 new AppSearchBatchResult.Builder<>();
135 for (int i = 0; i < uris.size(); i++) {
136 String uri = uris.get(i);
137 try {
Terry Wang26b9e5c2020-10-23 02:05:01 -0700138 GenericDocument document = impl.getDocument(databaseName, namespace, uri);
139 resultBuilder.setSuccess(uri, document.getBundle());
Alexander Dorokhinea95f44f2020-03-06 13:53:14 -0800140 } catch (Throwable t) {
141 resultBuilder.setResult(uri, throwableToFailedResult(t));
142 }
Alexander Dorokhine69a8d9f2020-03-06 10:43:16 -0800143 }
Alexander Dorokhinea95f44f2020-03-06 13:53:14 -0800144 callback.complete(resultBuilder.build());
Alexander Dorokhine69a8d9f2020-03-06 10:43:16 -0800145 } catch (Throwable t) {
146 callback.completeExceptionally(t);
147 } finally {
148 Binder.restoreCallingIdentity(callingIdentity);
149 }
150 }
151
sidchhabraa7c8f8a2020-01-16 18:38:17 -0800152 // TODO(sidchhabra): Do this in a threadpool.
Terry Wang26b9e5c2020-10-23 02:05:01 -0700153 // TODO(b/162450968) handle pagination after getNextPage and SearchResults is ready.
sidchhabraa7c8f8a2020-01-16 18:38:17 -0800154 @Override
Alexander Dorokhinee708e182020-03-06 15:30:34 -0800155 public void query(
Terry Wang6413aee2020-10-07 03:04:58 -0700156 @NonNull String databaseName,
Alexander Dorokhinec9fc9602020-10-06 01:39:50 -0700157 @NonNull String queryExpression,
158 @NonNull Bundle searchSpecBundle,
Alexander Dorokhinee708e182020-03-06 15:30:34 -0800159 @NonNull AndroidFuture<AppSearchResult> callback) {
Terry Wang26b9e5c2020-10-23 02:05:01 -0700160 Preconditions.checkNotNull(databaseName);
Alexander Dorokhinec9fc9602020-10-06 01:39:50 -0700161 Preconditions.checkNotNull(queryExpression);
162 Preconditions.checkNotNull(searchSpecBundle);
Alexander Dorokhinee708e182020-03-06 15:30:34 -0800163 Preconditions.checkNotNull(callback);
164 int callingUid = Binder.getCallingUidOrThrow();
165 int callingUserId = UserHandle.getUserId(callingUid);
Jeff Sharkeyea5328c2020-10-06 11:18:09 -0600166 final long callingIdentity = Binder.clearCallingIdentity();
sidchhabraa7c8f8a2020-01-16 18:38:17 -0800167 try {
Alexander Dorokhinee708e182020-03-06 15:30:34 -0800168 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
Terry Wang6413aee2020-10-07 03:04:58 -0700169 databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
Terry Wang26b9e5c2020-10-23 02:05:01 -0700170 SearchResultPage searchResultPage = impl.query(
Alexander Dorokhinec9fc9602020-10-06 01:39:50 -0700171 databaseName,
Terry Wang26b9e5c2020-10-23 02:05:01 -0700172 queryExpression,
173 new SearchSpec(searchSpecBundle));
174 callback.complete(
175 AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
Alexander Dorokhinee708e182020-03-06 15:30:34 -0800176 } catch (Throwable t) {
177 callback.complete(throwableToFailedResult(t));
178 } finally {
179 Binder.restoreCallingIdentity(callingIdentity);
sidchhabraa7c8f8a2020-01-16 18:38:17 -0800180 }
sidchhabraa7c8f8a2020-01-16 18:38:17 -0800181 }
Alexander Dorokhine969f4462020-03-05 15:54:19 -0800182
Alexander Dorokhinef6c66ae2020-03-09 14:47:25 -0700183 @Override
Terry Wang26b9e5c2020-10-23 02:05:01 -0700184 public void removeByUri(@NonNull String databaseName, @NonNull String namespace,
Terry Wang6413aee2020-10-07 03:04:58 -0700185 List<String> uris, AndroidFuture<AppSearchBatchResult> callback) {
Terry Wang26b9e5c2020-10-23 02:05:01 -0700186 Preconditions.checkNotNull(databaseName);
187 Preconditions.checkNotNull(namespace);
Alexander Dorokhineff82fba2020-03-09 16:35:24 -0700188 Preconditions.checkNotNull(uris);
189 Preconditions.checkNotNull(callback);
190 int callingUid = Binder.getCallingUidOrThrow();
191 int callingUserId = UserHandle.getUserId(callingUid);
Jeff Sharkeyea5328c2020-10-06 11:18:09 -0600192 final long callingIdentity = Binder.clearCallingIdentity();
Alexander Dorokhineff82fba2020-03-09 16:35:24 -0700193 try {
194 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
Terry Wang6413aee2020-10-07 03:04:58 -0700195 databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
Alexander Dorokhineff82fba2020-03-09 16:35:24 -0700196 AppSearchBatchResult.Builder<String, Void> resultBuilder =
197 new AppSearchBatchResult.Builder<>();
198 for (int i = 0; i < uris.size(); i++) {
199 String uri = uris.get(i);
200 try {
Terry Wang6413aee2020-10-07 03:04:58 -0700201 impl.remove(databaseName, namespace, uri);
Alexander Dorokhinec66d67c2020-10-08 13:44:04 -0700202 resultBuilder.setSuccess(uri, /*result= */null);
Alexander Dorokhineff82fba2020-03-09 16:35:24 -0700203 } catch (Throwable t) {
204 resultBuilder.setResult(uri, throwableToFailedResult(t));
205 }
206 }
207 callback.complete(resultBuilder.build());
208 } catch (Throwable t) {
209 callback.completeExceptionally(t);
210 } finally {
211 Binder.restoreCallingIdentity(callingIdentity);
212 }
213 }
214
215 @Override
Terry Wang26b9e5c2020-10-23 02:05:01 -0700216 public void removeByQuery(
217 @NonNull String databaseName,
218 @NonNull String queryExpression,
219 @NonNull Bundle searchSpecBundle,
Terry Wang6413aee2020-10-07 03:04:58 -0700220 @NonNull AndroidFuture<AppSearchResult> callback) {
Terry Wang26b9e5c2020-10-23 02:05:01 -0700221 Preconditions.checkNotNull(databaseName);
222 Preconditions.checkNotNull(queryExpression);
223 Preconditions.checkNotNull(searchSpecBundle);
Alexander Dorokhinef6c66ae2020-03-09 14:47:25 -0700224 Preconditions.checkNotNull(callback);
225 int callingUid = Binder.getCallingUidOrThrow();
226 int callingUserId = UserHandle.getUserId(callingUid);
Jeff Sharkeyea5328c2020-10-06 11:18:09 -0600227 final long callingIdentity = Binder.clearCallingIdentity();
Alexander Dorokhinef6c66ae2020-03-09 14:47:25 -0700228 try {
229 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
Terry Wang6413aee2020-10-07 03:04:58 -0700230 databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
Terry Wang26b9e5c2020-10-23 02:05:01 -0700231 impl.removeByQuery(databaseName, queryExpression, new SearchSpec(searchSpecBundle));
232 callback.complete(AppSearchResult.newSuccessfulResult(/*result= */null));
Alexander Dorokhinef6c66ae2020-03-09 14:47:25 -0700233 } catch (Throwable t) {
234 callback.complete(throwableToFailedResult(t));
235 } finally {
236 Binder.restoreCallingIdentity(callingIdentity);
237 }
238 }
239
Alexander Dorokhineebd37742020-09-22 15:02:26 -0700240 /**
Terry Wang6413aee2020-10-07 03:04:58 -0700241 * Rewrites the database name by adding a prefix of unique name for the given uid.
Alexander Dorokhineebd37742020-09-22 15:02:26 -0700242 *
243 * <p>The current implementation returns the package name of the app with this uid in a
244 * format like {@code com.example.package} or {@code com.example.sharedname:5678}.
245 */
246 @NonNull
Terry Wang6413aee2020-10-07 03:04:58 -0700247 private String rewriteDatabaseNameWithUid(String databaseName, int callingUid) {
Alexander Dorokhineebd37742020-09-22 15:02:26 -0700248 // For regular apps, this call will return the package name. If callingUid is an
249 // android:sharedUserId, this value may be another type of name and have a :uid suffix.
250 String callingUidName = getContext().getPackageManager().getNameForUid(callingUid);
251 if (callingUidName == null) {
252 // Not sure how this is possible --- maybe app was uninstalled?
253 throw new IllegalStateException(
254 "Failed to look up package name for uid " + callingUid);
255 }
Terry Wang6413aee2020-10-07 03:04:58 -0700256 return callingUidName + CALLING_NAME_DATABASE_DELIMITER + databaseName;
Alexander Dorokhineebd37742020-09-22 15:02:26 -0700257 }
258
Alexander Dorokhine969f4462020-03-05 15:54:19 -0800259 private <ValueType> AppSearchResult<ValueType> throwableToFailedResult(
260 @NonNull Throwable t) {
261 if (t instanceof AppSearchException) {
262 return ((AppSearchException) t).toAppSearchResult();
263 }
264
265 @AppSearchResult.ResultCode int resultCode;
266 if (t instanceof IllegalStateException) {
267 resultCode = AppSearchResult.RESULT_INTERNAL_ERROR;
268 } else if (t instanceof IllegalArgumentException) {
269 resultCode = AppSearchResult.RESULT_INVALID_ARGUMENT;
270 } else if (t instanceof IOException) {
271 resultCode = AppSearchResult.RESULT_IO_ERROR;
272 } else {
273 resultCode = AppSearchResult.RESULT_UNKNOWN_ERROR;
274 }
275 return AppSearchResult.newFailedResult(resultCode, t.getMessage());
276 }
Terry Wangfebbead2019-10-17 17:05:18 -0700277 }
278}