blob: 3c48ea7b06daba2fb38e6bf3f51cb5894963e682 [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
Terry Wangdbd1dca2020-11-03 17:03:56 -080018import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
Terry Wange201dc02021-04-16 01:03:20 -070019import static android.os.Process.INVALID_UID;
Terry Wangdbd1dca2020-11-03 17:03:56 -080020
Alexander Dorokhine872129a2022-02-22 16:36:36 -080021import static com.android.server.appsearch.util.ServiceImplHelper.invokeCallbackOnError;
22import static com.android.server.appsearch.util.ServiceImplHelper.invokeCallbackOnResult;
23
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -080024import android.annotation.ElapsedRealtimeLong;
sidchhabraa7c8f8a2020-01-16 18:38:17 -080025import android.annotation.NonNull;
Alexander Dorokhinee0587d32021-12-07 17:02:17 -080026import android.annotation.WorkerThread;
Alexander Dorokhine18465842020-01-21 01:08:57 -080027import android.app.appsearch.AppSearchBatchResult;
Terry Wang623e3b02021-02-02 20:27:33 -080028import android.app.appsearch.AppSearchMigrationHelper;
Alexander Dorokhine969f4462020-03-05 15:54:19 -080029import android.app.appsearch.AppSearchResult;
Alexander Dorokhine92ce3532020-10-06 01:39:36 -070030import android.app.appsearch.AppSearchSchema;
Alexander Dorokhinec66d67c2020-10-08 13:44:04 -070031import android.app.appsearch.GenericDocument;
Alexander Dorokhine9795b512021-03-23 22:06:59 -070032import android.app.appsearch.GetSchemaResponse;
Terry Wang26b9e5c2020-10-23 02:05:01 -070033import android.app.appsearch.SearchResultPage;
Alexander Dorokhinec9fc9602020-10-06 01:39:50 -070034import android.app.appsearch.SearchSpec;
Terry Wang623e3b02021-02-02 20:27:33 -080035import android.app.appsearch.SetSchemaResponse;
Cassie Wang8f0df492021-03-24 09:23:18 -070036import android.app.appsearch.StorageInfo;
Terry Wang5b0c5612021-12-23 15:57:19 -080037import android.app.appsearch.VisibilityDocument;
Alexander Dorokhined5e8eda2021-05-04 13:24:47 -070038import android.app.appsearch.aidl.AppSearchResultParcel;
39import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
40import android.app.appsearch.aidl.IAppSearchManager;
Alexander Dorokhinee0587d32021-12-07 17:02:17 -080041import android.app.appsearch.aidl.IAppSearchObserverProxy;
Alexander Dorokhined5e8eda2021-05-04 13:24:47 -070042import android.app.appsearch.aidl.IAppSearchResultCallback;
Terry Wang17876fe2021-10-22 01:08:01 -070043import android.app.appsearch.aidl.DocumentsParcel;
Alexander Dorokhine4d051632021-06-18 10:48:58 -070044import android.app.appsearch.exceptions.AppSearchException;
Alexander Dorokhinee0587d32021-12-07 17:02:17 -080045import android.app.appsearch.observer.ObserverSpec;
Terry Wang70000152022-02-03 02:22:24 -080046import android.content.AttributionSource;
Terry Wang12dc6c02021-03-31 19:26:16 -070047import android.content.BroadcastReceiver;
Terry Wangfebbead2019-10-17 17:05:18 -070048import android.content.Context;
Terry Wang12dc6c02021-03-31 19:26:16 -070049import android.content.Intent;
50import android.content.IntentFilter;
Alexander Dorokhinea5d0d322021-05-10 15:59:31 -070051import android.content.pm.PackageInfo;
Yang Yu0fcd51a2021-04-23 11:25:44 -070052import android.content.pm.PackageManager;
Yang Yu0fcd51a2021-04-23 11:25:44 -070053import android.content.pm.PackageStats;
Alexander Dorokhine270d4f12020-01-15 17:24:35 -080054import android.os.Binder;
Alexander Dorokhine92ce3532020-10-06 01:39:36 -070055import android.os.Bundle;
Terry Wang623e3b02021-02-02 20:27:33 -080056import android.os.ParcelFileDescriptor;
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -080057import android.os.SystemClock;
Cassie Wang0c62d992021-01-15 14:39:30 -080058import android.os.UserHandle;
Cassie Wang9ba9ae12021-02-01 16:39:37 -080059import android.util.ArraySet;
Terry Wangdbd1dca2020-11-03 17:03:56 -080060import android.util.Log;
Terry Wangfebbead2019-10-17 17:05:18 -070061
Alexander Dorokhinea5d0d322021-05-10 15:59:31 -070062import com.android.server.LocalManagerRegistry;
Terry Wangfebbead2019-10-17 17:05:18 -070063import com.android.server.SystemService;
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -080064import com.android.server.appsearch.external.localstorage.stats.CallStats;
Alexander Dorokhine14816e22021-07-12 10:53:24 -070065import com.android.server.appsearch.external.localstorage.stats.OptimizeStats;
Alexander Dorokhine44c04972021-06-22 12:32:15 -070066import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
Alexander Dorokhinee0587d32021-12-07 17:02:17 -080067import com.android.server.appsearch.observer.AppSearchObserverProxy;
Xiaoyu Jin10329472021-06-23 16:50:03 -070068import com.android.server.appsearch.stats.StatsCollector;
Alexander Dorokhine872129a2022-02-22 16:36:36 -080069import com.android.server.appsearch.util.ExecutorManager;
70import com.android.server.appsearch.util.ServiceImplHelper;
Alexander Dorokhine655aec02022-02-07 17:24:01 -080071import com.android.server.appsearch.visibilitystore.FrameworkCallerAccess;
Alexander Dorokhinea5d0d322021-05-10 15:59:31 -070072import com.android.server.usage.StorageStatsManagerLocal;
73import com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter;
Alexander Dorokhinefd07eba2020-01-13 20:22:20 -080074
Alexander Dorokhinec77f4442021-04-14 09:26:06 -070075import com.google.android.icing.proto.PersistType;
76
Terry Wang623e3b02021-02-02 20:27:33 -080077import java.io.DataInputStream;
78import java.io.DataOutputStream;
79import java.io.EOFException;
80import java.io.FileInputStream;
81import java.io.FileOutputStream;
Alexander Dorokhine6a99f942020-12-04 02:57:22 -080082import java.util.ArrayList;
Alexander Dorokhine18465842020-01-21 01:08:57 -080083import java.util.List;
Alexander Dorokhineab789062021-01-11 21:00:00 -080084import java.util.Map;
Alexander Dorokhined18f8842021-01-20 15:26:13 -080085import java.util.Objects;
Cassie Wang9ba9ae12021-02-01 16:39:37 -080086import java.util.Set;
Terry Wange04ceab2021-03-29 19:25:12 -070087import java.util.concurrent.Executor;
Alexander Dorokhine18465842020-01-21 01:08:57 -080088
Alexander Dorokhine5d2bb0b2021-06-24 22:58:46 -070089/**
90 * The main service implementation which contains AppSearch's platform functionality.
Yang Yuc047f7b2021-06-25 12:22:42 -070091 *
Alexander Dorokhine5d2bb0b2021-06-24 22:58:46 -070092 * @hide
93 */
Terry Wangfebbead2019-10-17 17:05:18 -070094public class AppSearchManagerService extends SystemService {
Alexander Dorokhineebd37742020-09-22 15:02:26 -070095 private static final String TAG = "AppSearchManagerService";
Alexander Dorokhine872129a2022-02-22 16:36:36 -080096
97 /**
98 * An executor for system activity not tied to any particular user.
99 *
100 * <p>NOTE: Never call shutdownNow(). AppSearchManagerService persists forever even as
101 * individual users are added and removed -- without this pool the service will be broken. And,
102 * clients waiting for callbacks will never receive anything and will hang.
103 */
104 private static final Executor SHARED_EXECUTOR = ExecutorManager.createDefaultExecutorService();
105
Terry Wang12dc6c02021-03-31 19:26:16 -0700106 private final Context mContext;
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800107 private final ExecutorManager mExecutorManager = new ExecutorManager();
108
Yang Yu0fcd51a2021-04-23 11:25:44 -0700109 private PackageManager mPackageManager;
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800110 private ServiceImplHelper mServiceImplHelper;
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700111 private AppSearchUserInstanceManager mAppSearchUserInstanceManager;
Terry Wangfebbead2019-10-17 17:05:18 -0700112
113 public AppSearchManagerService(Context context) {
114 super(context);
Terry Wang12dc6c02021-03-31 19:26:16 -0700115 mContext = context;
Terry Wangfebbead2019-10-17 17:05:18 -0700116 }
117
118 @Override
119 public void onStart() {
120 publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
Yang Yu0fcd51a2021-04-23 11:25:44 -0700121 mPackageManager = getContext().getPackageManager();
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800122 mServiceImplHelper = new ServiceImplHelper(mContext, mExecutorManager);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700123 mAppSearchUserInstanceManager = AppSearchUserInstanceManager.getInstance();
Terry Wang12dc6c02021-03-31 19:26:16 -0700124 registerReceivers();
Alexander Dorokhinea5d0d322021-05-10 15:59:31 -0700125 LocalManagerRegistry.getManager(StorageStatsManagerLocal.class)
Yang Yu0fcd51a2021-04-23 11:25:44 -0700126 .registerStorageStatsAugmenter(new AppSearchStorageStatsAugmenter(), TAG);
Terry Wang12dc6c02021-03-31 19:26:16 -0700127 }
128
Xiaoyu Jin10329472021-06-23 16:50:03 -0700129 @Override
130 public void onBootPhase(/* @BootPhase */ int phase) {
131 if (phase == PHASE_BOOT_COMPLETED) {
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800132 StatsCollector.getInstance(mContext, SHARED_EXECUTOR);
Xiaoyu Jin10329472021-06-23 16:50:03 -0700133 }
134 }
135
Terry Wang12dc6c02021-03-31 19:26:16 -0700136 private void registerReceivers() {
Alexander Dorokhine5c416772021-06-04 09:05:00 -0700137 mContext.registerReceiverForAllUsers(
138 new UserActionReceiver(),
139 new IntentFilter(Intent.ACTION_USER_REMOVED),
140 /*broadcastPermission=*/ null,
Terry Wang12dc6c02021-03-31 19:26:16 -0700141 /*scheduler=*/ null);
Terry Wange201dc02021-04-16 01:03:20 -0700142
143 //TODO(b/145759910) Add a direct callback when user clears the data instead of relying on
144 // broadcasts
145 IntentFilter packageChangedFilter = new IntentFilter();
146 packageChangedFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
147 packageChangedFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
148 packageChangedFilter.addDataScheme("package");
149 packageChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
Alexander Dorokhine5c416772021-06-04 09:05:00 -0700150 mContext.registerReceiverForAllUsers(
151 new PackageChangedReceiver(),
152 packageChangedFilter,
153 /*broadcastPermission=*/ null,
Terry Wange201dc02021-04-16 01:03:20 -0700154 /*scheduler=*/ null);
Terry Wang12dc6c02021-03-31 19:26:16 -0700155 }
156
157 private class UserActionReceiver extends BroadcastReceiver {
158 @Override
159 public void onReceive(@NonNull Context context, @NonNull Intent intent) {
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700160 Objects.requireNonNull(context);
161 Objects.requireNonNull(intent);
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800162 if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
163 UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
164 if (userHandle == null) {
165 Log.e(TAG,
166 "Extra " + Intent.EXTRA_USER + " is missing in the intent: " + intent);
167 return;
168 }
169 // We can handle user removal the same way as user stopping: shut down the executor
170 // and close icing. The data of AppSearch is saved in the "credential encrypted"
171 // system directory of each user. That directory will be auto-deleted when a user is
172 // removed, so we don't need it handle it specially.
173 onUserStopping(userHandle);
174 } else {
175 Log.e(TAG, "Received unknown intent: " + intent);
Terry Wang12dc6c02021-03-31 19:26:16 -0700176 }
177 }
178 }
179
Terry Wange201dc02021-04-16 01:03:20 -0700180 private class PackageChangedReceiver extends BroadcastReceiver {
181 @Override
182 public void onReceive(@NonNull Context context, @NonNull Intent intent) {
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700183 Objects.requireNonNull(context);
184 Objects.requireNonNull(intent);
185
Terry Wange201dc02021-04-16 01:03:20 -0700186 switch (intent.getAction()) {
187 case Intent.ACTION_PACKAGE_FULLY_REMOVED:
188 case Intent.ACTION_PACKAGE_DATA_CLEARED:
189 String packageName = intent.getData().getSchemeSpecificPart();
190 if (packageName == null) {
191 Log.e(TAG, "Package name is missing in the intent: " + intent);
192 return;
193 }
194 int uid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
195 if (uid == INVALID_UID) {
196 Log.e(TAG, "uid is missing in the intent: " + intent);
197 return;
198 }
199 handlePackageRemoved(packageName, uid);
200 break;
201 default:
202 Log.e(TAG, "Received unknown intent: " + intent);
203 }
204 }
205 }
206
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700207 private void handlePackageRemoved(@NonNull String packageName, int uid) {
208 UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
Terry Wange201dc02021-04-16 01:03:20 -0700209 try {
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800210 if (mServiceImplHelper.isUserLocked(userHandle)) {
211 // We cannot access a locked user's directory and remove package data from it.
Terry Wanga9e6e212021-04-23 15:51:37 -0700212 // We should remove those uninstalled package data when the user is unlocking.
Terry Wange201dc02021-04-16 01:03:20 -0700213 return;
214 }
Terry Wanga9e6e212021-04-23 15:51:37 -0700215 // Only clear the package's data if AppSearch exists for this user.
Xiaoyu Jin8edf1be2022-02-23 01:04:45 +0000216 if (AppSearchModule.getAppSearchDir(userHandle).exists()) {
Cassie Wang84e33a02021-06-18 14:13:31 -0700217 Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700218 AppSearchUserInstance instance =
219 mAppSearchUserInstanceManager.getOrCreateUserInstance(
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800220 userContext,
221 userHandle,
Alexander Dorokhine62f2d752022-04-13 00:42:19 -0700222 FrameworkAppSearchConfig.getInstance(SHARED_EXECUTOR));
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700223 instance.getAppSearchImpl().clearPackageData(packageName);
Alexander Dorokhine801f3212022-02-10 14:04:32 -0800224 dispatchChangeNotifications(instance);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700225 instance.getLogger().removeCachedUidForPackage(packageName);
Terry Wange201dc02021-04-16 01:03:20 -0700226 }
227 } catch (Throwable t) {
228 Log.e(TAG, "Unable to remove data for package: " + packageName, t);
Terry Wang12dc6c02021-03-31 19:26:16 -0700229 }
Terry Wangfebbead2019-10-17 17:05:18 -0700230 }
231
Cassie Wang9ba9ae12021-02-01 16:39:37 -0800232 @Override
Pinyao Tingd5c2ed92021-03-18 14:51:54 -0700233 public void onUserUnlocking(@NonNull TargetUser user) {
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700234 Objects.requireNonNull(user);
Terry Wanga9e6e212021-04-23 15:51:37 -0700235 UserHandle userHandle = user.getUserHandle();
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800236 mServiceImplHelper.setUserIsLocked(userHandle, false);
237 mExecutorManager.getOrCreateUserExecutor(userHandle).execute(() -> {
Terry Wanga9e6e212021-04-23 15:51:37 -0700238 try {
239 // Only clear the package's data if AppSearch exists for this user.
Xiaoyu Jin8edf1be2022-02-23 01:04:45 +0000240 if (AppSearchModule.getAppSearchDir(userHandle).exists()) {
Cassie Wang84e33a02021-06-18 14:13:31 -0700241 Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700242 AppSearchUserInstance instance =
243 mAppSearchUserInstanceManager.getOrCreateUserInstance(
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800244 userContext,
245 userHandle,
Alexander Dorokhine62f2d752022-04-13 00:42:19 -0700246 FrameworkAppSearchConfig.getInstance(SHARED_EXECUTOR));
Cassie Wang84e33a02021-06-18 14:13:31 -0700247 List<PackageInfo> installedPackageInfos = userContext
Terry Wanga9e6e212021-04-23 15:51:37 -0700248 .getPackageManager()
249 .getInstalledPackages(/*flags=*/0);
250 Set<String> packagesToKeep = new ArraySet<>(installedPackageInfos.size());
251 for (int i = 0; i < installedPackageInfos.size(); i++) {
252 packagesToKeep.add(installedPackageInfos.get(i).packageName);
253 }
Terry Wang0be9c302022-01-21 14:36:18 -0800254 packagesToKeep.add(VisibilityStore.VISIBILITY_PACKAGE_NAME);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700255 instance.getAppSearchImpl().prunePackageData(packagesToKeep);
Terry Wanga9e6e212021-04-23 15:51:37 -0700256 }
257 } catch (Throwable t) {
258 Log.e(TAG, "Unable to prune packages for " + user, t);
259 }
260 });
Cassie Wang9ba9ae12021-02-01 16:39:37 -0800261 }
262
Terry Wangde9f3382021-04-28 19:45:07 -0700263 @Override
264 public void onUserStopping(@NonNull TargetUser user) {
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700265 Objects.requireNonNull(user);
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800266 onUserStopping(user.getUserHandle());
Terry Wangde9f3382021-04-28 19:45:07 -0700267 }
268
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800269 private void onUserStopping(@NonNull UserHandle userHandle) {
270 Objects.requireNonNull(userHandle);
271 Log.i(TAG, "Shutting down AppSearch for user " + userHandle);
272 try {
Alexander Dorokhine6edf4122022-02-23 23:03:22 -0800273 mServiceImplHelper.setUserIsLocked(userHandle, true);
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800274 mExecutorManager.shutDownAndRemoveUserExecutor(userHandle);
275 mAppSearchUserInstanceManager.closeAndRemoveUserInstance(userHandle);
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800276 Log.i(TAG, "Removed AppSearchImpl instance for: " + userHandle);
277 } catch (Throwable t) {
278 Log.e(TAG, "Unable to remove data for: " + userHandle, t);
Yang Yu0fcd51a2021-04-23 11:25:44 -0700279 }
280 }
281
Terry Wangfebbead2019-10-17 17:05:18 -0700282 private class Stub extends IAppSearchManager.Stub {
Alexander Dorokhinefd07eba2020-01-13 20:22:20 -0800283 @Override
Alexander Dorokhine969f4462020-03-05 15:54:19 -0800284 public void setSchema(
Terry Wang70000152022-02-03 02:22:24 -0800285 @NonNull AttributionSource callerAttributionSource,
Terry Wang6413aee2020-10-07 03:04:58 -0700286 @NonNull String databaseName,
Alexander Dorokhine92ce3532020-10-06 01:39:36 -0700287 @NonNull List<Bundle> schemaBundles,
Terry Wang5b0c5612021-12-23 15:57:19 -0800288 @NonNull List<Bundle> visibilityBundles,
Alexander Dorokhine969f4462020-03-05 15:54:19 -0800289 boolean forceOverride,
Alexander Dorokhine9795b512021-03-23 22:06:59 -0700290 int schemaVersion,
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700291 @NonNull UserHandle userHandle,
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700292 @ElapsedRealtimeLong long binderCallStartTimeMillis,
Terry Wangdbd1dca2020-11-03 17:03:56 -0800293 @NonNull IAppSearchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -0800294 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700295 Objects.requireNonNull(databaseName);
296 Objects.requireNonNull(schemaBundles);
Terry Wang5b0c5612021-12-23 15:57:19 -0800297 Objects.requireNonNull(visibilityBundles);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700298 Objects.requireNonNull(userHandle);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700299 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700300
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700301 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800302 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
303 callerAttributionSource, userHandle, callback);
304 if (targetUser == null) {
305 return; // Verification failed; verifyIncomingCall triggered callback.
306 }
307 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700308 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700309 AppSearchUserInstance instance = null;
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700310 int operationSuccessCount = 0;
311 int operationFailureCount = 0;
Terry Wange04ceab2021-03-29 19:25:12 -0700312 try {
Terry Wange04ceab2021-03-29 19:25:12 -0700313 List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
314 for (int i = 0; i < schemaBundles.size(); i++) {
315 schemas.add(new AppSearchSchema(schemaBundles.get(i)));
Alexander Dorokhineab789062021-01-11 21:00:00 -0800316 }
Terry Wang5b0c5612021-12-23 15:57:19 -0800317 List<VisibilityDocument> visibilityDocuments =
318 new ArrayList<>(visibilityBundles.size());
319 for (int i = 0; i < visibilityBundles.size(); i++) {
320 visibilityDocuments.add(
321 new VisibilityDocument(visibilityBundles.get(i)));
Terry Wange04ceab2021-03-29 19:25:12 -0700322 }
Cassie Wang2feb34d2021-07-22 18:55:19 +0000323 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
Alexander Dorokhineba42f392021-08-17 15:17:40 -0700324 // TODO(b/173532925): Implement logging for statsBuilder
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700325 SetSchemaResponse setSchemaResponse = instance.getAppSearchImpl().setSchema(
Terry Wang70000152022-02-03 02:22:24 -0800326 callerAttributionSource.getPackageName(),
Terry Wange04ceab2021-03-29 19:25:12 -0700327 databaseName,
328 schemas,
Terry Wange15ea582021-10-21 18:59:21 -0700329 visibilityDocuments,
Terry Wange04ceab2021-03-29 19:25:12 -0700330 forceOverride,
Alexander Dorokhineba42f392021-08-17 15:17:40 -0700331 schemaVersion,
332 /*setSchemaStatsBuilder=*/ null);
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700333 ++operationSuccessCount;
Terry Wange04ceab2021-03-29 19:25:12 -0700334 invokeCallbackOnResult(callback,
335 AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle()));
Terry Wangf4d219b2021-07-01 19:11:39 -0700336
Alexander Dorokhine801f3212022-02-10 14:04:32 -0800337 // Schedule a task to dispatch change notifications. See requirements for where
338 // the method is called documented in the method description.
339 dispatchChangeNotifications(instance);
340
Terry Wangf4d219b2021-07-01 19:11:39 -0700341 // setSchema will sync the schemas in the request to AppSearch, any existing
342 // schemas which is not included in the request will be delete if we force
343 // override incompatible schemas. And all documents of these types will be
344 // deleted as well. We should checkForOptimize for these deletion.
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800345 checkForOptimize(targetUser, instance);
Terry Wange04ceab2021-03-29 19:25:12 -0700346 } catch (Throwable t) {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700347 ++operationFailureCount;
348 statusCode = throwableToFailedResult(t).getResultCode();
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800349 invokeCallbackOnResult(callback, throwableToFailedResult(t));
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700350 } finally {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700351 if (instance != null) {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700352 int estimatedBinderLatencyMillis =
353 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
354 int totalLatencyMillis =
355 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700356 instance.getLogger().logStats(new CallStats.Builder()
Terry Wang70000152022-02-03 02:22:24 -0800357 .setPackageName(callerAttributionSource.getPackageName())
Alexander Dorokhine82a38722021-06-09 16:35:22 -0700358 .setDatabase(databaseName)
359 .setStatusCode(statusCode)
360 .setTotalLatencyMillis(totalLatencyMillis)
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700361 .setCallType(CallStats.CALL_TYPE_SET_SCHEMA)
362 // TODO(b/173532925) check the existing binder call latency chart
363 // is good enough for us:
364 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
365 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
366 .setNumOperationsSucceeded(operationSuccessCount)
Alexander Dorokhine82a38722021-06-09 16:35:22 -0700367 .setNumOperationsFailed(operationFailureCount)
368 .build());
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700369 }
Alexander Dorokhineab789062021-01-11 21:00:00 -0800370 }
Terry Wange04ceab2021-03-29 19:25:12 -0700371 });
Alexander Dorokhine179c8b82020-01-11 00:17:48 -0800372 }
373
374 @Override
Terry Wang83a24932020-12-09 21:00:18 -0800375 public void getSchema(
Terry Wang70000152022-02-03 02:22:24 -0800376 @NonNull AttributionSource callerAttributionSource,
377 @NonNull String targetPackageName,
Terry Wang83a24932020-12-09 21:00:18 -0800378 @NonNull String databaseName,
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700379 @NonNull UserHandle userHandle,
Terry Wang83a24932020-12-09 21:00:18 -0800380 @NonNull IAppSearchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -0800381 Objects.requireNonNull(callerAttributionSource);
382 Objects.requireNonNull(targetPackageName);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700383 Objects.requireNonNull(databaseName);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700384 Objects.requireNonNull(userHandle);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700385 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700386
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800387 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
388 callerAttributionSource, userHandle, callback);
389 if (targetUser == null) {
390 return; // Verification failed; verifyIncomingCall triggered callback.
391 }
392 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Terry Wange04ceab2021-03-29 19:25:12 -0700393 try {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700394 AppSearchUserInstance instance =
Cassie Wang2feb34d2021-07-22 18:55:19 +0000395 mAppSearchUserInstanceManager.getUserInstance(targetUser);
Terry Wang70000152022-02-03 02:22:24 -0800396
397 boolean callerHasSystemAccess = instance.getVisibilityChecker()
398 .doesCallerHaveSystemAccess(callerAttributionSource.getPackageName());
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700399 GetSchemaResponse response =
Alexander Dorokhined41def42022-01-25 15:45:34 -0800400 instance.getAppSearchImpl().getSchema(
Terry Wang70000152022-02-03 02:22:24 -0800401 targetPackageName,
Alexander Dorokhine661ef9a2022-01-28 11:30:45 -0800402 databaseName,
Terry Wang70000152022-02-03 02:22:24 -0800403 new FrameworkCallerAccess(callerAttributionSource,
404 callerHasSystemAccess));
Terry Wange04ceab2021-03-29 19:25:12 -0700405 invokeCallbackOnResult(
406 callback,
407 AppSearchResult.newSuccessfulResult(response.getBundle()));
408 } catch (Throwable t) {
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800409 invokeCallbackOnResult(callback, throwableToFailedResult(t));
Terry Wange04ceab2021-03-29 19:25:12 -0700410 }
411 });
Alexander Dorokhine9795b512021-03-23 22:06:59 -0700412 }
413
414 @Override
415 public void getNamespaces(
Terry Wang70000152022-02-03 02:22:24 -0800416 @NonNull AttributionSource callerAttributionSource,
Alexander Dorokhine9795b512021-03-23 22:06:59 -0700417 @NonNull String databaseName,
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700418 @NonNull UserHandle userHandle,
Alexander Dorokhine9795b512021-03-23 22:06:59 -0700419 @NonNull IAppSearchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -0800420 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700421 Objects.requireNonNull(databaseName);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700422 Objects.requireNonNull(userHandle);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700423 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700424
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800425 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
426 callerAttributionSource, userHandle, callback);
427 if (targetUser == null) {
428 return; // Verification failed; verifyIncomingCall triggered callback.
429 }
430 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Terry Wange04ceab2021-03-29 19:25:12 -0700431 try {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700432 AppSearchUserInstance instance =
Cassie Wang2feb34d2021-07-22 18:55:19 +0000433 mAppSearchUserInstanceManager.getUserInstance(targetUser);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700434 List<String> namespaces =
Terry Wang70000152022-02-03 02:22:24 -0800435 instance.getAppSearchImpl().getNamespaces(
436 callerAttributionSource.getPackageName(), databaseName);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700437 invokeCallbackOnResult(
438 callback, AppSearchResult.newSuccessfulResult(namespaces));
Terry Wange04ceab2021-03-29 19:25:12 -0700439 } catch (Throwable t) {
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800440 invokeCallbackOnResult(callback, throwableToFailedResult(t));
Terry Wange04ceab2021-03-29 19:25:12 -0700441 }
442 });
Terry Wang83a24932020-12-09 21:00:18 -0800443 }
444
445 @Override
Alexander Dorokhine18465842020-01-21 01:08:57 -0800446 public void putDocuments(
Terry Wang70000152022-02-03 02:22:24 -0800447 @NonNull AttributionSource callerAttributionSource,
Terry Wang6413aee2020-10-07 03:04:58 -0700448 @NonNull String databaseName,
Terry Wang17876fe2021-10-22 01:08:01 -0700449 @NonNull DocumentsParcel documentsParcel,
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700450 @NonNull UserHandle userHandle,
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -0800451 @ElapsedRealtimeLong long binderCallStartTimeMillis,
Terry Wangdbd1dca2020-11-03 17:03:56 -0800452 @NonNull IAppSearchBatchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -0800453 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700454 Objects.requireNonNull(databaseName);
Terry Wang17876fe2021-10-22 01:08:01 -0700455 Objects.requireNonNull(documentsParcel);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700456 Objects.requireNonNull(userHandle);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700457 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700458
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700459 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800460 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
461 callerAttributionSource, userHandle, callback);
462 if (targetUser == null) {
463 return; // Verification failed; verifyIncomingCall triggered callback.
464 }
465 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -0800466 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700467 AppSearchUserInstance instance = null;
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -0800468 int operationSuccessCount = 0;
469 int operationFailureCount = 0;
Terry Wange04ceab2021-03-29 19:25:12 -0700470 try {
Terry Wange04ceab2021-03-29 19:25:12 -0700471 AppSearchBatchResult.Builder<String, Void> resultBuilder =
472 new AppSearchBatchResult.Builder<>();
Cassie Wang2feb34d2021-07-22 18:55:19 +0000473 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
Terry Wang17876fe2021-10-22 01:08:01 -0700474 List<GenericDocument> documents = documentsParcel.getDocuments();
475 for (int i = 0; i < documents.size(); i++) {
476 GenericDocument document = documents.get(i);
Terry Wange04ceab2021-03-29 19:25:12 -0700477 try {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700478 instance.getAppSearchImpl().putDocument(
Terry Wang70000152022-02-03 02:22:24 -0800479 callerAttributionSource.getPackageName(),
480 databaseName,
481 document,
Alexander Dorokhinee08ba922022-04-20 11:53:18 -0700482 /*sendChangeNotifications=*/ true,
Terry Wang70000152022-02-03 02:22:24 -0800483 instance.getLogger());
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700484 resultBuilder.setSuccess(document.getId(), /*value=*/ null);
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -0800485 ++operationSuccessCount;
Terry Wange04ceab2021-03-29 19:25:12 -0700486 } catch (Throwable t) {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700487 resultBuilder.setResult(document.getId(), throwableToFailedResult(t));
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -0800488 AppSearchResult<Void> result = throwableToFailedResult(t);
Alexander Dorokhine17f79372021-04-21 11:23:09 -0700489 resultBuilder.setResult(document.getId(), result);
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700490 // Since we can only include one status code in the atom,
491 // for failures, we would just save the one for the last failure
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -0800492 statusCode = result.getResultCode();
493 ++operationFailureCount;
Terry Wange04ceab2021-03-29 19:25:12 -0700494 }
Alexander Dorokhine18465842020-01-21 01:08:57 -0800495 }
Alexander Dorokhinec77f4442021-04-14 09:26:06 -0700496 // Now that the batch has been written. Persist the newly written data.
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700497 instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE);
Terry Wange04ceab2021-03-29 19:25:12 -0700498 invokeCallbackOnResult(callback, resultBuilder.build());
Terry Wangf4d219b2021-07-01 19:11:39 -0700499
Alexander Dorokhinee0587d32021-12-07 17:02:17 -0800500 // Schedule a task to dispatch change notifications. See requirements for where
501 // the method is called documented in the method description.
502 dispatchChangeNotifications(instance);
503
Terry Wangf4d219b2021-07-01 19:11:39 -0700504 // The existing documents with same ID will be deleted, so there may be some
505 // resources that could be released after optimize().
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800506 checkForOptimize(
Terry Wang17876fe2021-10-22 01:08:01 -0700507 targetUser, instance, /*mutateBatchSize=*/ documents.size());
Terry Wange04ceab2021-03-29 19:25:12 -0700508 } catch (Throwable t) {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700509 ++operationFailureCount;
510 statusCode = throwableToFailedResult(t).getResultCode();
Terry Wange04ceab2021-03-29 19:25:12 -0700511 invokeCallbackOnError(callback, t);
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -0800512 } finally {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700513 if (instance != null) {
Xiaoyu Jinc410c4f2021-05-09 14:15:50 -0700514 int estimatedBinderLatencyMillis =
515 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
516 int totalLatencyMillis =
517 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700518 instance.getLogger().logStats(new CallStats.Builder()
Terry Wang70000152022-02-03 02:22:24 -0800519 .setPackageName(callerAttributionSource.getPackageName())
Alexander Dorokhine82a38722021-06-09 16:35:22 -0700520 .setDatabase(databaseName)
521 .setStatusCode(statusCode)
522 .setTotalLatencyMillis(totalLatencyMillis)
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -0800523 .setCallType(CallStats.CALL_TYPE_PUT_DOCUMENTS)
524 // TODO(b/173532925) check the existing binder call latency chart
525 // is good enough for us:
526 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
Xiaoyu Jinc410c4f2021-05-09 14:15:50 -0700527 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -0800528 .setNumOperationsSucceeded(operationSuccessCount)
Alexander Dorokhine82a38722021-06-09 16:35:22 -0700529 .setNumOperationsFailed(operationFailureCount)
530 .build());
Xiaoyu Jinf8cc2012021-03-12 11:00:54 -0800531 }
Alexander Dorokhine18465842020-01-21 01:08:57 -0800532 }
Terry Wange04ceab2021-03-29 19:25:12 -0700533 });
Alexander Dorokhinefd07eba2020-01-13 20:22:20 -0800534 }
Alexander Dorokhine969f4462020-03-05 15:54:19 -0800535
Alexander Dorokhine69a8d9f2020-03-06 10:43:16 -0800536 @Override
Terry Wangf2093072020-11-30 04:47:19 -0800537 public void getDocuments(
Terry Wang70000152022-02-03 02:22:24 -0800538 @NonNull AttributionSource callerAttributionSource,
Alex Saveliev87631932022-01-21 00:48:44 +0000539 @NonNull String targetPackageName,
Terry Wangf2093072020-11-30 04:47:19 -0800540 @NonNull String databaseName,
541 @NonNull String namespace,
Alexander Dorokhine17f79372021-04-21 11:23:09 -0700542 @NonNull List<String> ids,
Alexander Dorokhine87cdd152021-01-20 15:41:25 -0800543 @NonNull Map<String, List<String>> typePropertyPaths,
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700544 @NonNull UserHandle userHandle,
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700545 @ElapsedRealtimeLong long binderCallStartTimeMillis,
Terry Wangdbd1dca2020-11-03 17:03:56 -0800546 @NonNull IAppSearchBatchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -0800547 Objects.requireNonNull(callerAttributionSource);
Alex Saveliev87631932022-01-21 00:48:44 +0000548 Objects.requireNonNull(targetPackageName);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700549 Objects.requireNonNull(databaseName);
550 Objects.requireNonNull(namespace);
Alexander Dorokhine17f79372021-04-21 11:23:09 -0700551 Objects.requireNonNull(ids);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700552 Objects.requireNonNull(typePropertyPaths);
553 Objects.requireNonNull(userHandle);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700554 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700555
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700556 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800557 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
558 callerAttributionSource, userHandle, callback);
559 if (targetUser == null) {
560 return; // Verification failed; verifyIncomingCall triggered callback.
561 }
562 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700563 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700564 AppSearchUserInstance instance = null;
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700565 int operationSuccessCount = 0;
566 int operationFailureCount = 0;
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800567 boolean global =
568 !callerAttributionSource.getPackageName().equals(targetPackageName);
Terry Wange04ceab2021-03-29 19:25:12 -0700569 try {
Terry Wange04ceab2021-03-29 19:25:12 -0700570 AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
571 new AppSearchBatchResult.Builder<>();
Cassie Wang2feb34d2021-07-22 18:55:19 +0000572 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
Alexander Dorokhine17f79372021-04-21 11:23:09 -0700573 for (int i = 0; i < ids.size(); i++) {
574 String id = ids.get(i);
Terry Wange04ceab2021-03-29 19:25:12 -0700575 try {
Alex Saveliev87631932022-01-21 00:48:44 +0000576 GenericDocument document;
577 if (global) {
Terry Wang70000152022-02-03 02:22:24 -0800578 boolean callerHasSystemAccess = instance.getVisibilityChecker()
579 .doesCallerHaveSystemAccess(callerAttributionSource
580 .getPackageName());
Alex Saveliev87631932022-01-21 00:48:44 +0000581 document = instance.getAppSearchImpl().globalGetDocument(
582 targetPackageName,
583 databaseName,
584 namespace,
585 id,
586 typePropertyPaths,
Terry Wang70000152022-02-03 02:22:24 -0800587 new FrameworkCallerAccess(callerAttributionSource,
588 callerHasSystemAccess));
Alex Saveliev87631932022-01-21 00:48:44 +0000589 } else {
590 document = instance.getAppSearchImpl().getDocument(
591 targetPackageName,
592 databaseName,
593 namespace,
594 id,
595 typePropertyPaths);
596 }
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700597 ++operationSuccessCount;
Alexander Dorokhine17f79372021-04-21 11:23:09 -0700598 resultBuilder.setSuccess(id, document.getBundle());
Terry Wange04ceab2021-03-29 19:25:12 -0700599 } catch (Throwable t) {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700600 // Since we can only include one status code in the atom,
601 // for failures, we would just save the one for the last failure
602 AppSearchResult<Bundle> result = throwableToFailedResult(t);
603 resultBuilder.setResult(id, result);
604 statusCode = result.getResultCode();
605 ++operationFailureCount;
Terry Wange04ceab2021-03-29 19:25:12 -0700606 }
Alexander Dorokhinea95f44f2020-03-06 13:53:14 -0800607 }
Terry Wange04ceab2021-03-29 19:25:12 -0700608 invokeCallbackOnResult(callback, resultBuilder.build());
609 } catch (Throwable t) {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700610 ++operationFailureCount;
611 statusCode = throwableToFailedResult(t).getResultCode();
Terry Wange04ceab2021-03-29 19:25:12 -0700612 invokeCallbackOnError(callback, t);
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700613 } finally {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700614 if (instance != null) {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700615 int estimatedBinderLatencyMillis =
616 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
617 int totalLatencyMillis =
618 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
Alex Saveliev6af06242022-02-01 13:50:35 -0800619 int callType = global?
620 CallStats.CALL_TYPE_GLOBAL_GET_DOCUMENT_BY_ID:
621 CallStats.CALL_TYPE_GET_DOCUMENTS;
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700622 instance.getLogger().logStats(new CallStats.Builder()
Alex Saveliev87631932022-01-21 00:48:44 +0000623 .setPackageName(targetPackageName)
Alexander Dorokhine82a38722021-06-09 16:35:22 -0700624 .setDatabase(databaseName)
625 .setStatusCode(statusCode)
626 .setTotalLatencyMillis(totalLatencyMillis)
Alex Saveliev6af06242022-02-01 13:50:35 -0800627 .setCallType(callType)
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700628 // TODO(b/173532925) check the existing binder call latency chart
629 // is good enough for us:
630 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
631 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
632 .setNumOperationsSucceeded(operationSuccessCount)
Alexander Dorokhine82a38722021-06-09 16:35:22 -0700633 .setNumOperationsFailed(operationFailureCount)
634 .build());
Xiaoyu Jinc5a75772021-05-09 20:12:44 -0700635 }
Alexander Dorokhine69a8d9f2020-03-06 10:43:16 -0800636 }
Terry Wange04ceab2021-03-29 19:25:12 -0700637 });
Alexander Dorokhine69a8d9f2020-03-06 10:43:16 -0800638 }
639
sidchhabraa7c8f8a2020-01-16 18:38:17 -0800640 @Override
Alexander Dorokhinee708e182020-03-06 15:30:34 -0800641 public void query(
Terry Wang70000152022-02-03 02:22:24 -0800642 @NonNull AttributionSource callerAttributionSource,
Terry Wang6413aee2020-10-07 03:04:58 -0700643 @NonNull String databaseName,
Alexander Dorokhinec9fc9602020-10-06 01:39:50 -0700644 @NonNull String queryExpression,
645 @NonNull Bundle searchSpecBundle,
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700646 @NonNull UserHandle userHandle,
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700647 @ElapsedRealtimeLong long binderCallStartTimeMillis,
Alexander Dorokhined48f2362020-10-20 17:40:49 -0700648 @NonNull IAppSearchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -0800649 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700650 Objects.requireNonNull(databaseName);
651 Objects.requireNonNull(queryExpression);
652 Objects.requireNonNull(searchSpecBundle);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700653 Objects.requireNonNull(userHandle);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700654 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700655
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700656 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800657 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
658 callerAttributionSource, userHandle, callback);
659 if (targetUser == null) {
660 return; // Verification failed; verifyIncomingCall triggered callback.
661 }
662 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700663 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700664 AppSearchUserInstance instance = null;
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700665 int operationSuccessCount = 0;
666 int operationFailureCount = 0;
Terry Wange04ceab2021-03-29 19:25:12 -0700667 try {
Cassie Wang2feb34d2021-07-22 18:55:19 +0000668 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700669 SearchResultPage searchResultPage = instance.getAppSearchImpl().query(
Terry Wang70000152022-02-03 02:22:24 -0800670 callerAttributionSource.getPackageName(),
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700671 databaseName,
672 queryExpression,
673 new SearchSpec(searchSpecBundle),
674 instance.getLogger());
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700675 ++operationSuccessCount;
Terry Wange04ceab2021-03-29 19:25:12 -0700676 invokeCallbackOnResult(
677 callback,
678 AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
679 } catch (Throwable t) {
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700680 ++operationFailureCount;
681 statusCode = throwableToFailedResult(t).getResultCode();
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800682 invokeCallbackOnResult(callback, throwableToFailedResult(t));
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700683 } finally {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700684 if (instance != null) {
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700685 int estimatedBinderLatencyMillis =
686 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
687 int totalLatencyMillis =
688 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700689 instance.getLogger().logStats(new CallStats.Builder()
Terry Wang70000152022-02-03 02:22:24 -0800690 .setPackageName(callerAttributionSource.getPackageName())
Alexander Dorokhine82a38722021-06-09 16:35:22 -0700691 .setDatabase(databaseName)
692 .setStatusCode(statusCode)
693 .setTotalLatencyMillis(totalLatencyMillis)
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700694 .setCallType(CallStats.CALL_TYPE_SEARCH)
695 // TODO(b/173532925) check the existing binder call latency chart
696 // is good enough for us:
697 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
698 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
699 .setNumOperationsSucceeded(operationSuccessCount)
Alexander Dorokhine82a38722021-06-09 16:35:22 -0700700 .setNumOperationsFailed(operationFailureCount)
701 .build());
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700702 }
Terry Wange04ceab2021-03-29 19:25:12 -0700703 }
704 });
Alexander Dorokhined48f2362020-10-20 17:40:49 -0700705 }
706
Terry Wangf2093072020-11-30 04:47:19 -0800707 @Override
Terry Wangbfbfcac2020-11-06 15:46:44 -0800708 public void globalQuery(
Terry Wang70000152022-02-03 02:22:24 -0800709 @NonNull AttributionSource callerAttributionSource,
Terry Wangbfbfcac2020-11-06 15:46:44 -0800710 @NonNull String queryExpression,
711 @NonNull Bundle searchSpecBundle,
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700712 @NonNull UserHandle userHandle,
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700713 @ElapsedRealtimeLong long binderCallStartTimeMillis,
Terry Wangbfbfcac2020-11-06 15:46:44 -0800714 @NonNull IAppSearchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -0800715 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700716 Objects.requireNonNull(queryExpression);
717 Objects.requireNonNull(searchSpecBundle);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700718 Objects.requireNonNull(userHandle);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700719 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700720
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700721 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800722 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
723 callerAttributionSource, userHandle, callback);
724 if (targetUser == null) {
725 return; // Verification failed; verifyIncomingCall triggered callback.
726 }
727 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700728 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700729 AppSearchUserInstance instance = null;
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700730 int operationSuccessCount = 0;
731 int operationFailureCount = 0;
Terry Wange04ceab2021-03-29 19:25:12 -0700732 try {
Cassie Wang2feb34d2021-07-22 18:55:19 +0000733 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700734
Alexander Dorokhine661ef9a2022-01-28 11:30:45 -0800735 boolean callerHasSystemAccess = instance.getVisibilityChecker()
Terry Wang70000152022-02-03 02:22:24 -0800736 .doesCallerHaveSystemAccess(callerAttributionSource.getPackageName());
737
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700738 SearchResultPage searchResultPage = instance.getAppSearchImpl().globalQuery(
739 queryExpression,
740 new SearchSpec(searchSpecBundle),
Terry Wang70000152022-02-03 02:22:24 -0800741 new FrameworkCallerAccess(callerAttributionSource,
Alexander Dorokhine72f5e372022-02-07 17:23:49 -0800742 callerHasSystemAccess),
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700743 instance.getLogger());
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700744 ++operationSuccessCount;
Terry Wange04ceab2021-03-29 19:25:12 -0700745 invokeCallbackOnResult(
746 callback,
747 AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700748 } catch (Throwable t) {
749 ++operationFailureCount;
750 statusCode = throwableToFailedResult(t).getResultCode();
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800751 invokeCallbackOnResult(callback, throwableToFailedResult(t));
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700752 } finally {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700753 if (instance != null) {
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700754 int estimatedBinderLatencyMillis =
755 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
756 int totalLatencyMillis =
757 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700758 instance.getLogger().logStats(new CallStats.Builder()
Terry Wang70000152022-02-03 02:22:24 -0800759 .setPackageName(callerAttributionSource.getPackageName())
Alexander Dorokhine82a38722021-06-09 16:35:22 -0700760 .setStatusCode(statusCode)
761 .setTotalLatencyMillis(totalLatencyMillis)
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700762 .setCallType(CallStats.CALL_TYPE_GLOBAL_SEARCH)
763 // TODO(b/173532925) check the existing binder call latency chart
764 // is good enough for us:
765 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
766 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
767 .setNumOperationsSucceeded(operationSuccessCount)
Alexander Dorokhine82a38722021-06-09 16:35:22 -0700768 .setNumOperationsFailed(operationFailureCount)
769 .build());
Xiaoyu Jinef66a422021-05-09 17:38:59 -0700770 }
Terry Wange04ceab2021-03-29 19:25:12 -0700771 }
772 });
Terry Wangbfbfcac2020-11-06 15:46:44 -0800773 }
774
Alexander Dorokhined48f2362020-10-20 17:40:49 -0700775 @Override
Cassie Wang0c62d992021-01-15 14:39:30 -0800776 public void getNextPage(
Terry Wang70000152022-02-03 02:22:24 -0800777 @NonNull AttributionSource callerAttributionSource,
Cassie Wang0c62d992021-01-15 14:39:30 -0800778 long nextPageToken,
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700779 @NonNull UserHandle userHandle,
Alexander Dorokhined48f2362020-10-20 17:40:49 -0700780 @NonNull IAppSearchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -0800781 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700782 Objects.requireNonNull(userHandle);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -0700783 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700784
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800785 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
786 callerAttributionSource, userHandle, callback);
787 if (targetUser == null) {
788 return; // Verification failed; verifyIncomingCall triggered callback.
789 }
790 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Terry Wange04ceab2021-03-29 19:25:12 -0700791 try {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700792 AppSearchUserInstance instance =
Cassie Wang2feb34d2021-07-22 18:55:19 +0000793 mAppSearchUserInstanceManager.getUserInstance(targetUser);
Alexander Dorokhineba42f392021-08-17 15:17:40 -0700794 // TODO(b/173532925): Implement logging for statsBuilder
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700795 SearchResultPage searchResultPage =
Alexander Dorokhineba42f392021-08-17 15:17:40 -0700796 instance.getAppSearchImpl().getNextPage(
Terry Wang70000152022-02-03 02:22:24 -0800797 callerAttributionSource.getPackageName(), nextPageToken,
798 /*statsBuilder=*/ null);
Terry Wange04ceab2021-03-29 19:25:12 -0700799 invokeCallbackOnResult(
800 callback,
801 AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
802 } catch (Throwable t) {
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800803 invokeCallbackOnResult(callback, throwableToFailedResult(t));
Terry Wange04ceab2021-03-29 19:25:12 -0700804 }
805 });
Alexander Dorokhined48f2362020-10-20 17:40:49 -0700806 }
807
808 @Override
Terry Wang70000152022-02-03 02:22:24 -0800809 public void invalidateNextPageToken(
810 @NonNull AttributionSource callerAttributionSource,
811 long nextPageToken,
Cassie Wang84e33a02021-06-18 14:13:31 -0700812 @NonNull UserHandle userHandle) {
Terry Wang70000152022-02-03 02:22:24 -0800813 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700814 Objects.requireNonNull(userHandle);
815
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800816 try {
817 UserHandle targetUser = mServiceImplHelper.verifyIncomingCall(
818 callerAttributionSource, userHandle);
819 mExecutorManager.getOrCreateUserExecutor(targetUser).execute(() -> {
820 try {
821 AppSearchUserInstance instance =
822 mAppSearchUserInstanceManager.getUserInstance(targetUser);
823 instance.getAppSearchImpl().invalidateNextPageToken(
824 callerAttributionSource.getPackageName(), nextPageToken);
825 } catch (Throwable t) {
826 Log.e(TAG, "Unable to invalidate the query page token", t);
827 }
828 });
829 } catch (Throwable t) {
830 Log.e(TAG, "Unable to invalidate the query page token", t);
831 }
sidchhabraa7c8f8a2020-01-16 18:38:17 -0800832 }
Alexander Dorokhine969f4462020-03-05 15:54:19 -0800833
Alexander Dorokhinef6c66ae2020-03-09 14:47:25 -0700834 @Override
Terry Wang623e3b02021-02-02 20:27:33 -0800835 public void writeQueryResultsToFile(
Terry Wang70000152022-02-03 02:22:24 -0800836 @NonNull AttributionSource callerAttributionSource,
Terry Wang623e3b02021-02-02 20:27:33 -0800837 @NonNull String databaseName,
838 @NonNull ParcelFileDescriptor fileDescriptor,
839 @NonNull String queryExpression,
840 @NonNull Bundle searchSpecBundle,
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700841 @NonNull UserHandle userHandle,
Terry Wang623e3b02021-02-02 20:27:33 -0800842 @NonNull IAppSearchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -0800843 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700844 Objects.requireNonNull(databaseName);
845 Objects.requireNonNull(fileDescriptor);
846 Objects.requireNonNull(queryExpression);
847 Objects.requireNonNull(searchSpecBundle);
848 Objects.requireNonNull(userHandle);
849 Objects.requireNonNull(callback);
850
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800851 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
852 callerAttributionSource, userHandle, callback);
853 if (targetUser == null) {
854 return; // Verification failed; verifyIncomingCall triggered callback.
855 }
856 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Terry Wange04ceab2021-03-29 19:25:12 -0700857 try {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700858 AppSearchUserInstance instance =
Cassie Wang2feb34d2021-07-22 18:55:19 +0000859 mAppSearchUserInstanceManager.getUserInstance(targetUser);
Terry Wange04ceab2021-03-29 19:25:12 -0700860 // we don't need to append the file. The file is always brand new.
861 try (DataOutputStream outputStream = new DataOutputStream(
862 new FileOutputStream(fileDescriptor.getFileDescriptor()))) {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700863 SearchResultPage searchResultPage = instance.getAppSearchImpl().query(
Terry Wang70000152022-02-03 02:22:24 -0800864 callerAttributionSource.getPackageName(),
Terry Wange04ceab2021-03-29 19:25:12 -0700865 databaseName,
866 queryExpression,
Alexander Dorokhine7cbc4712021-04-27 14:47:39 -0700867 new SearchSpec(searchSpecBundle),
868 /*logger=*/ null);
Terry Wange04ceab2021-03-29 19:25:12 -0700869 while (!searchResultPage.getResults().isEmpty()) {
870 for (int i = 0; i < searchResultPage.getResults().size(); i++) {
871 AppSearchMigrationHelper.writeBundleToOutputStream(
872 outputStream, searchResultPage.getResults().get(i)
873 .getGenericDocument().getBundle());
874 }
Alexander Dorokhineba42f392021-08-17 15:17:40 -0700875 // TODO(b/173532925): Implement logging for statsBuilder
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700876 searchResultPage = instance.getAppSearchImpl().getNextPage(
Terry Wang70000152022-02-03 02:22:24 -0800877 callerAttributionSource.getPackageName(),
Alexander Dorokhineba42f392021-08-17 15:17:40 -0700878 searchResultPage.getNextPageToken(),
879 /*statsBuilder=*/ null);
Terry Wang623e3b02021-02-02 20:27:33 -0800880 }
Terry Wang623e3b02021-02-02 20:27:33 -0800881 }
Terry Wange04ceab2021-03-29 19:25:12 -0700882 invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
883 } catch (Throwable t) {
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800884 invokeCallbackOnResult(callback, throwableToFailedResult(t));
Terry Wang623e3b02021-02-02 20:27:33 -0800885 }
Terry Wange04ceab2021-03-29 19:25:12 -0700886 });
Terry Wang623e3b02021-02-02 20:27:33 -0800887 }
888
889 @Override
890 public void putDocumentsFromFile(
Terry Wang70000152022-02-03 02:22:24 -0800891 @NonNull AttributionSource callerAttributionSource,
Terry Wang623e3b02021-02-02 20:27:33 -0800892 @NonNull String databaseName,
893 @NonNull ParcelFileDescriptor fileDescriptor,
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700894 @NonNull UserHandle userHandle,
Terry Wang623e3b02021-02-02 20:27:33 -0800895 @NonNull IAppSearchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -0800896 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700897 Objects.requireNonNull(databaseName);
898 Objects.requireNonNull(fileDescriptor);
899 Objects.requireNonNull(userHandle);
900 Objects.requireNonNull(callback);
901
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800902 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
903 callerAttributionSource, userHandle, callback);
904 if (targetUser == null) {
905 return; // Verification failed; verifyIncomingCall triggered callback.
906 }
907 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Terry Wange04ceab2021-03-29 19:25:12 -0700908 try {
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700909 AppSearchUserInstance instance =
Cassie Wang2feb34d2021-07-22 18:55:19 +0000910 mAppSearchUserInstanceManager.getUserInstance(targetUser);
Terry Wang623e3b02021-02-02 20:27:33 -0800911
Terry Wange04ceab2021-03-29 19:25:12 -0700912 GenericDocument document;
913 ArrayList<Bundle> migrationFailureBundles = new ArrayList<>();
914 try (DataInputStream inputStream = new DataInputStream(
915 new FileInputStream(fileDescriptor.getFileDescriptor()))) {
916 while (true) {
917 try {
918 document = AppSearchMigrationHelper
919 .readDocumentFromInputStream(inputStream);
920 } catch (EOFException e) {
921 // nothing wrong, we just finish the reading.
922 break;
923 }
924 try {
Alexander Dorokhinee08ba922022-04-20 11:53:18 -0700925 // Per this method's documentation, individual document change
926 // notifications are not dispatched.
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700927 instance.getAppSearchImpl().putDocument(
Terry Wang70000152022-02-03 02:22:24 -0800928 callerAttributionSource.getPackageName(),
929 databaseName,
930 document,
Alexander Dorokhinee08ba922022-04-20 11:53:18 -0700931 /*sendChangeNotifications=*/ false,
Terry Wang70000152022-02-03 02:22:24 -0800932 /*logger=*/ null);
Terry Wange04ceab2021-03-29 19:25:12 -0700933 } catch (Throwable t) {
Alexander Dorokhine17f79372021-04-21 11:23:09 -0700934 migrationFailureBundles.add(new SetSchemaResponse.MigrationFailure(
935 document.getNamespace(),
936 document.getId(),
937 document.getSchemaType(),
938 AppSearchResult.throwableToFailedResult(t))
939 .getBundle());
Terry Wange04ceab2021-03-29 19:25:12 -0700940 }
Terry Wang623e3b02021-02-02 20:27:33 -0800941 }
942 }
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700943 instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL);
Terry Wange04ceab2021-03-29 19:25:12 -0700944 invokeCallbackOnResult(callback,
945 AppSearchResult.newSuccessfulResult(migrationFailureBundles));
946 } catch (Throwable t) {
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800947 invokeCallbackOnResult(callback, throwableToFailedResult(t));
Terry Wang623e3b02021-02-02 20:27:33 -0800948 }
Terry Wange04ceab2021-03-29 19:25:12 -0700949 });
Terry Wang623e3b02021-02-02 20:27:33 -0800950 }
951
952 @Override
Alexander Dorokhined18f8842021-01-20 15:26:13 -0800953 public void reportUsage(
Terry Wang70000152022-02-03 02:22:24 -0800954 @NonNull AttributionSource callerAttributionSource,
955 @NonNull String targetPackageName,
Alexander Dorokhined18f8842021-01-20 15:26:13 -0800956 @NonNull String databaseName,
957 @NonNull String namespace,
Alexander Dorokhine17f79372021-04-21 11:23:09 -0700958 @NonNull String documentId,
Alexander Dorokhined18f8842021-01-20 15:26:13 -0800959 long usageTimeMillis,
Alexander Dorokhine9795b512021-03-23 22:06:59 -0700960 boolean systemUsage,
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700961 @NonNull UserHandle userHandle,
Alexander Dorokhined18f8842021-01-20 15:26:13 -0800962 @NonNull IAppSearchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -0800963 Objects.requireNonNull(callerAttributionSource);
964 Objects.requireNonNull(targetPackageName);
Alexander Dorokhined18f8842021-01-20 15:26:13 -0800965 Objects.requireNonNull(databaseName);
966 Objects.requireNonNull(namespace);
Alexander Dorokhine17f79372021-04-21 11:23:09 -0700967 Objects.requireNonNull(documentId);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700968 Objects.requireNonNull(userHandle);
Alexander Dorokhined18f8842021-01-20 15:26:13 -0800969 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -0700970
Alexander Dorokhine872129a2022-02-22 16:36:36 -0800971 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
972 callerAttributionSource, userHandle, callback);
973 if (targetUser == null) {
974 return; // Verification failed; verifyIncomingCall triggered callback.
975 }
976 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Terry Wange04ceab2021-03-29 19:25:12 -0700977 try {
Terry Wang70000152022-02-03 02:22:24 -0800978 String callingPackageName = callerAttributionSource.getPackageName();
Alexander Dorokhine4d051632021-06-18 10:48:58 -0700979 AppSearchUserInstance instance =
Cassie Wang2feb34d2021-07-22 18:55:19 +0000980 mAppSearchUserInstanceManager.getUserInstance(targetUser);
Terry Wang70000152022-02-03 02:22:24 -0800981 if (systemUsage) {
982 if (!instance.getVisibilityChecker().doesCallerHaveSystemAccess(
983 callerAttributionSource.getPackageName())) {
984 throw new AppSearchException(AppSearchResult.RESULT_SECURITY_ERROR,
985 callingPackageName
986 + " does not have access to report system usage");
987 }
988 } else {
989 if (!callingPackageName.equals(targetPackageName)) {
990 throw new AppSearchException(AppSearchResult.RESULT_SECURITY_ERROR,
991 "Cannot report usage to different package: "
992 + targetPackageName + " from package: "
993 + callingPackageName);
994 }
Terry Wange04ceab2021-03-29 19:25:12 -0700995 }
996
Terry Wang70000152022-02-03 02:22:24 -0800997 instance.getAppSearchImpl().reportUsage(targetPackageName, databaseName,
998 namespace, documentId, usageTimeMillis, systemUsage);
Terry Wange04ceab2021-03-29 19:25:12 -0700999 invokeCallbackOnResult(
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001000 callback, AppSearchResult.newSuccessfulResult(/*value=*/ null));
Terry Wange04ceab2021-03-29 19:25:12 -07001001 } catch (Throwable t) {
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001002 invokeCallbackOnResult(callback, throwableToFailedResult(t));
Alexander Dorokhine9795b512021-03-23 22:06:59 -07001003 }
Terry Wange04ceab2021-03-29 19:25:12 -07001004 });
Alexander Dorokhined18f8842021-01-20 15:26:13 -08001005 }
1006
1007 @Override
Alexander Dorokhine17f79372021-04-21 11:23:09 -07001008 public void removeByDocumentId(
Terry Wang70000152022-02-03 02:22:24 -08001009 @NonNull AttributionSource callerAttributionSource,
Terry Wangf2093072020-11-30 04:47:19 -08001010 @NonNull String databaseName,
1011 @NonNull String namespace,
Alexander Dorokhine17f79372021-04-21 11:23:09 -07001012 @NonNull List<String> ids,
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001013 @NonNull UserHandle userHandle,
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001014 @ElapsedRealtimeLong long binderCallStartTimeMillis,
Terry Wangdbd1dca2020-11-03 17:03:56 -08001015 @NonNull IAppSearchBatchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -08001016 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -07001017 Objects.requireNonNull(databaseName);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001018 Objects.requireNonNull(namespace);
Alexander Dorokhine17f79372021-04-21 11:23:09 -07001019 Objects.requireNonNull(ids);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001020 Objects.requireNonNull(userHandle);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -07001021 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001022
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001023 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001024 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
1025 callerAttributionSource, userHandle, callback);
1026 if (targetUser == null) {
1027 return; // Verification failed; verifyIncomingCall triggered callback.
1028 }
1029 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001030 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001031 AppSearchUserInstance instance = null;
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001032 int operationSuccessCount = 0;
1033 int operationFailureCount = 0;
Terry Wange04ceab2021-03-29 19:25:12 -07001034 try {
Terry Wange04ceab2021-03-29 19:25:12 -07001035 AppSearchBatchResult.Builder<String, Void> resultBuilder =
1036 new AppSearchBatchResult.Builder<>();
Cassie Wang2feb34d2021-07-22 18:55:19 +00001037 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
Alexander Dorokhine17f79372021-04-21 11:23:09 -07001038 for (int i = 0; i < ids.size(); i++) {
1039 String id = ids.get(i);
Terry Wange04ceab2021-03-29 19:25:12 -07001040 try {
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001041 instance.getAppSearchImpl().remove(
Terry Wang70000152022-02-03 02:22:24 -08001042 callerAttributionSource.getPackageName(),
Alexander Dorokhinea4b5bab2021-05-20 14:24:50 -07001043 databaseName,
1044 namespace,
1045 id,
1046 /*removeStatsBuilder=*/ null);
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001047 ++operationSuccessCount;
Alexander Dorokhine17f79372021-04-21 11:23:09 -07001048 resultBuilder.setSuccess(id, /*result= */ null);
Terry Wange04ceab2021-03-29 19:25:12 -07001049 } catch (Throwable t) {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001050 AppSearchResult<Void> result = throwableToFailedResult(t);
1051 resultBuilder.setResult(id, result);
1052 // Since we can only include one status code in the atom,
1053 // for failures, we would just save the one for the last failure
1054 statusCode = result.getResultCode();
1055 ++operationFailureCount;
Terry Wange04ceab2021-03-29 19:25:12 -07001056 }
Alexander Dorokhineff82fba2020-03-09 16:35:24 -07001057 }
Alexander Dorokhinec77f4442021-04-14 09:26:06 -07001058 // Now that the batch has been written. Persist the newly written data.
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001059 instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE);
Terry Wange04ceab2021-03-29 19:25:12 -07001060 invokeCallbackOnResult(callback, resultBuilder.build());
Terry Wangf4d219b2021-07-01 19:11:39 -07001061
Alexander Dorokhinee0587d32021-12-07 17:02:17 -08001062 // Schedule a task to dispatch change notifications. See requirements for where
1063 // the method is called documented in the method description.
1064 dispatchChangeNotifications(instance);
1065
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001066 checkForOptimize(targetUser, instance, ids.size());
Terry Wange04ceab2021-03-29 19:25:12 -07001067 } catch (Throwable t) {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001068 ++operationFailureCount;
1069 statusCode = throwableToFailedResult(t).getResultCode();
Terry Wange04ceab2021-03-29 19:25:12 -07001070 invokeCallbackOnError(callback, t);
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001071 } finally {
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001072 if (instance != null) {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001073 int estimatedBinderLatencyMillis =
1074 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
1075 int totalLatencyMillis =
1076 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001077 instance.getLogger().logStats(new CallStats.Builder()
Terry Wang70000152022-02-03 02:22:24 -08001078 .setPackageName(callerAttributionSource.getPackageName())
Alexander Dorokhine82a38722021-06-09 16:35:22 -07001079 .setDatabase(databaseName)
1080 .setStatusCode(statusCode)
1081 .setTotalLatencyMillis(totalLatencyMillis)
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001082 .setCallType(CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_ID)
1083 // TODO(b/173532925) check the existing binder call latency chart
1084 // is good enough for us:
1085 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
1086 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
1087 .setNumOperationsSucceeded(operationSuccessCount)
Alexander Dorokhine82a38722021-06-09 16:35:22 -07001088 .setNumOperationsFailed(operationFailureCount)
1089 .build());
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001090 }
Alexander Dorokhineff82fba2020-03-09 16:35:24 -07001091 }
Terry Wange04ceab2021-03-29 19:25:12 -07001092 });
Alexander Dorokhineff82fba2020-03-09 16:35:24 -07001093 }
1094
1095 @Override
Terry Wang26b9e5c2020-10-23 02:05:01 -07001096 public void removeByQuery(
Terry Wang70000152022-02-03 02:22:24 -08001097 @NonNull AttributionSource callerAttributionSource,
Terry Wang26b9e5c2020-10-23 02:05:01 -07001098 @NonNull String databaseName,
1099 @NonNull String queryExpression,
1100 @NonNull Bundle searchSpecBundle,
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001101 @NonNull UserHandle userHandle,
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001102 @ElapsedRealtimeLong long binderCallStartTimeMillis,
Alexander Dorokhine178366b2020-10-20 17:40:49 -07001103 @NonNull IAppSearchResultCallback callback) {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001104 // TODO(b/173532925) log CallStats once we have CALL_TYPE_REMOVE_BY_QUERY added
Terry Wang70000152022-02-03 02:22:24 -08001105 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -07001106 Objects.requireNonNull(databaseName);
1107 Objects.requireNonNull(queryExpression);
1108 Objects.requireNonNull(searchSpecBundle);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001109 Objects.requireNonNull(userHandle);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -07001110 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001111
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001112 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001113 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
1114 callerAttributionSource, userHandle, callback);
1115 if (targetUser == null) {
1116 return; // Verification failed; verifyIncomingCall triggered callback.
1117 }
1118 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001119 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001120 AppSearchUserInstance instance = null;
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001121 int operationSuccessCount = 0;
1122 int operationFailureCount = 0;
Terry Wange04ceab2021-03-29 19:25:12 -07001123 try {
Cassie Wang2feb34d2021-07-22 18:55:19 +00001124 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001125 instance.getAppSearchImpl().removeByQuery(
Terry Wang70000152022-02-03 02:22:24 -08001126 callerAttributionSource.getPackageName(),
Terry Wange04ceab2021-03-29 19:25:12 -07001127 databaseName,
1128 queryExpression,
Alexander Dorokhinea4b5bab2021-05-20 14:24:50 -07001129 new SearchSpec(searchSpecBundle),
1130 /*removeStatsBuilder=*/ null);
Alexander Dorokhinec77f4442021-04-14 09:26:06 -07001131 // Now that the batch has been written. Persist the newly written data.
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001132 instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE);
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001133 ++operationSuccessCount;
Terry Wange04ceab2021-03-29 19:25:12 -07001134 invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
Terry Wangf4d219b2021-07-01 19:11:39 -07001135
Alexander Dorokhinee0587d32021-12-07 17:02:17 -08001136 // Schedule a task to dispatch change notifications. See requirements for where
1137 // the method is called documented in the method description.
1138 dispatchChangeNotifications(instance);
1139
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001140 checkForOptimize(targetUser, instance);
Terry Wange04ceab2021-03-29 19:25:12 -07001141 } catch (Throwable t) {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001142 ++operationFailureCount;
1143 statusCode = throwableToFailedResult(t).getResultCode();
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001144 invokeCallbackOnResult(callback, throwableToFailedResult(t));
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001145 } finally {
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001146 if (instance != null) {
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001147 int estimatedBinderLatencyMillis =
1148 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
1149 int totalLatencyMillis =
1150 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001151 instance.getLogger().logStats(new CallStats.Builder()
Terry Wang70000152022-02-03 02:22:24 -08001152 .setPackageName(callerAttributionSource.getPackageName())
Alexander Dorokhine82a38722021-06-09 16:35:22 -07001153 .setDatabase(databaseName)
1154 .setStatusCode(statusCode)
1155 .setTotalLatencyMillis(totalLatencyMillis)
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001156 .setCallType(CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH)
1157 // TODO(b/173532925) check the existing binder call latency chart
1158 // is good enough for us:
1159 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
1160 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
1161 .setNumOperationsSucceeded(operationSuccessCount)
Alexander Dorokhine82a38722021-06-09 16:35:22 -07001162 .setNumOperationsFailed(operationFailureCount)
1163 .build());
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001164 }
Terry Wange04ceab2021-03-29 19:25:12 -07001165 }
1166 });
Alexander Dorokhinef6c66ae2020-03-09 14:47:25 -07001167 }
1168
Terry Wangdbd1dca2020-11-03 17:03:56 -08001169 @Override
Cassie Wang8f0df492021-03-24 09:23:18 -07001170 public void getStorageInfo(
Terry Wang70000152022-02-03 02:22:24 -08001171 @NonNull AttributionSource callerAttributionSource,
Cassie Wang8f0df492021-03-24 09:23:18 -07001172 @NonNull String databaseName,
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001173 @NonNull UserHandle userHandle,
Cassie Wang8f0df492021-03-24 09:23:18 -07001174 @NonNull IAppSearchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -08001175 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -07001176 Objects.requireNonNull(databaseName);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001177 Objects.requireNonNull(userHandle);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -07001178 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001179
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001180 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
1181 callerAttributionSource, userHandle, callback);
1182 if (targetUser == null) {
1183 return; // Verification failed; verifyIncomingCall triggered callback.
1184 }
1185 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Terry Wange04ceab2021-03-29 19:25:12 -07001186 try {
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001187 AppSearchUserInstance instance =
Cassie Wang2feb34d2021-07-22 18:55:19 +00001188 mAppSearchUserInstanceManager.getUserInstance(targetUser);
Terry Wang70000152022-02-03 02:22:24 -08001189 StorageInfo storageInfo = instance.getAppSearchImpl().getStorageInfoForDatabase(
1190 callerAttributionSource.getPackageName(), databaseName);
Terry Wange04ceab2021-03-29 19:25:12 -07001191 Bundle storageInfoBundle = storageInfo.getBundle();
1192 invokeCallbackOnResult(
1193 callback, AppSearchResult.newSuccessfulResult(storageInfoBundle));
1194 } catch (Throwable t) {
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001195 invokeCallbackOnResult(callback, throwableToFailedResult(t));
Terry Wange04ceab2021-03-29 19:25:12 -07001196 }
1197 });
Cassie Wang8f0df492021-03-24 09:23:18 -07001198 }
1199
1200 @Override
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001201 public void persistToDisk(
Terry Wang70000152022-02-03 02:22:24 -08001202 @NonNull AttributionSource callerAttributionSource,
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001203 @NonNull UserHandle userHandle,
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001204 @ElapsedRealtimeLong long binderCallStartTimeMillis) {
Terry Wang70000152022-02-03 02:22:24 -08001205 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001206 Objects.requireNonNull(userHandle);
1207
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001208 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001209 try {
1210 UserHandle targetUser = mServiceImplHelper.verifyIncomingCall(
1211 callerAttributionSource, userHandle);
1212 mExecutorManager.getOrCreateUserExecutor(targetUser).execute(() -> {
1213 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
1214 AppSearchUserInstance instance = null;
1215 int operationSuccessCount = 0;
1216 int operationFailureCount = 0;
1217 try {
1218 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
1219 instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL);
1220 ++operationSuccessCount;
1221 } catch (Throwable t) {
1222 ++operationFailureCount;
1223 statusCode = throwableToFailedResult(t).getResultCode();
1224 Log.e(TAG, "Unable to persist the data to disk", t);
1225 } finally {
1226 if (instance != null) {
1227 int estimatedBinderLatencyMillis =
1228 2 * (int) (totalLatencyStartTimeMillis
1229 - binderCallStartTimeMillis);
1230 int totalLatencyMillis =
1231 (int) (SystemClock.elapsedRealtime()
1232 - totalLatencyStartTimeMillis);
1233 instance.getLogger().logStats(new CallStats.Builder()
1234 .setStatusCode(statusCode)
1235 .setTotalLatencyMillis(totalLatencyMillis)
1236 .setCallType(CallStats.CALL_TYPE_FLUSH)
1237 // TODO(b/173532925) check the existing binder call latency
1238 // chart is good enough for us:
1239 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
1240 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
1241 .setNumOperationsSucceeded(operationSuccessCount)
1242 .setNumOperationsFailed(operationFailureCount)
1243 .build());
1244 }
Xiaoyu Jinc5a75772021-05-09 20:12:44 -07001245 }
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001246 });
1247 } catch (Throwable t) {
1248 Log.e(TAG, "Unable to persist the data to disk", t);
1249 }
Terry Wang2da17852020-12-16 19:59:08 -08001250 }
1251
1252 @Override
Alexander Dorokhine1ff7fdb2022-03-18 14:54:46 -07001253 public AppSearchResultParcel<Void> registerObserverCallback(
Terry Wang70000152022-02-03 02:22:24 -08001254 @NonNull AttributionSource callerAttributionSource,
Alexander Dorokhine661ef9a2022-01-28 11:30:45 -08001255 @NonNull String targetPackageName,
Alexander Dorokhinee0587d32021-12-07 17:02:17 -08001256 @NonNull Bundle observerSpecBundle,
1257 @NonNull UserHandle userHandle,
1258 @NonNull IAppSearchObserverProxy observerProxyStub) {
Terry Wang70000152022-02-03 02:22:24 -08001259 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhine661ef9a2022-01-28 11:30:45 -08001260 Objects.requireNonNull(targetPackageName);
Alexander Dorokhinee0587d32021-12-07 17:02:17 -08001261 Objects.requireNonNull(observerSpecBundle);
1262 Objects.requireNonNull(userHandle);
1263 Objects.requireNonNull(observerProxyStub);
1264
Alexander Dorokhine1ff7fdb2022-03-18 14:54:46 -07001265 // Note: registerObserverCallback is performed on the binder thread, unlike most
1266 // AppSearch APIs
Alexander Dorokhinee0587d32021-12-07 17:02:17 -08001267 try {
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001268 UserHandle targetUser = mServiceImplHelper.verifyIncomingCall(
1269 callerAttributionSource, userHandle);
1270 long callingIdentity = Binder.clearCallingIdentity();
1271 try {
1272 AppSearchUserInstance instance =
1273 mAppSearchUserInstanceManager.getUserInstance(targetUser);
Alexander Dorokhine576a8662022-04-20 12:00:17 -07001274
1275 // Prepare a new ObserverProxy linked to this binder.
1276 AppSearchObserverProxy observerProxy =
1277 new AppSearchObserverProxy(observerProxyStub);
1278
1279 // Watch for client disconnection, unregistering the observer if it happens.
1280 observerProxyStub.asBinder().linkToDeath(
1281 () -> instance.getAppSearchImpl()
1282 .unregisterObserverCallback(targetPackageName, observerProxy),
1283 /*flags=*/ 0);
1284
1285 // Register the observer.
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001286 boolean callerHasSystemAccess = instance.getVisibilityChecker()
1287 .doesCallerHaveSystemAccess(callerAttributionSource.getPackageName());
Alexander Dorokhine1ff7fdb2022-03-18 14:54:46 -07001288 instance.getAppSearchImpl().registerObserverCallback(
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001289 new FrameworkCallerAccess(
1290 callerAttributionSource, callerHasSystemAccess),
1291 targetPackageName,
1292 new ObserverSpec(observerSpecBundle),
1293 mExecutorManager.getOrCreateUserExecutor(targetUser),
1294 new AppSearchObserverProxy(observerProxyStub));
Alexander Dorokhine576a8662022-04-20 12:00:17 -07001295
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001296 return new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null));
1297 } finally {
1298 Binder.restoreCallingIdentity(callingIdentity);
1299 }
Alexander Dorokhinee0587d32021-12-07 17:02:17 -08001300 } catch (Throwable t) {
1301 return new AppSearchResultParcel<>(throwableToFailedResult(t));
Alexander Dorokhinee0587d32021-12-07 17:02:17 -08001302 }
1303 }
1304
1305 @Override
Alexander Dorokhine1ff7fdb2022-03-18 14:54:46 -07001306 public AppSearchResultParcel<Void> unregisterObserverCallback(
Terry Wang70000152022-02-03 02:22:24 -08001307 @NonNull AttributionSource callerAttributionSource,
Alexander Dorokhine04f94d22022-01-24 23:38:50 -08001308 @NonNull String observedPackage,
1309 @NonNull UserHandle userHandle,
1310 @NonNull IAppSearchObserverProxy observerProxyStub) {
Terry Wang70000152022-02-03 02:22:24 -08001311 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhine04f94d22022-01-24 23:38:50 -08001312 Objects.requireNonNull(observedPackage);
1313 Objects.requireNonNull(userHandle);
1314 Objects.requireNonNull(observerProxyStub);
1315
Alexander Dorokhine1ff7fdb2022-03-18 14:54:46 -07001316 // Note: unregisterObserverCallback is performed on the binder thread, unlike most
1317 // AppSearch APIs
Alexander Dorokhine04f94d22022-01-24 23:38:50 -08001318 try {
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001319 UserHandle targetUser = mServiceImplHelper.verifyIncomingCall(
1320 callerAttributionSource, userHandle);
1321 long callingIdentity = Binder.clearCallingIdentity();
1322 try {
1323 AppSearchUserInstance instance =
1324 mAppSearchUserInstanceManager.getUserInstance(targetUser);
Alexander Dorokhine1ff7fdb2022-03-18 14:54:46 -07001325 instance.getAppSearchImpl().unregisterObserverCallback(
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001326 observedPackage,
1327 new AppSearchObserverProxy(observerProxyStub));
1328 return new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null));
1329 } finally {
1330 Binder.restoreCallingIdentity(callingIdentity);
1331 }
Alexander Dorokhine04f94d22022-01-24 23:38:50 -08001332 } catch (Throwable t) {
1333 return new AppSearchResultParcel<>(throwableToFailedResult(t));
Alexander Dorokhine04f94d22022-01-24 23:38:50 -08001334 }
1335 }
1336
1337 @Override
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001338 public void initialize(
Terry Wang70000152022-02-03 02:22:24 -08001339 @NonNull AttributionSource callerAttributionSource,
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001340 @NonNull UserHandle userHandle,
Xiaoyu Jinc410c4f2021-05-09 14:15:50 -07001341 @ElapsedRealtimeLong long binderCallStartTimeMillis,
1342 @NonNull IAppSearchResultCallback callback) {
Terry Wang70000152022-02-03 02:22:24 -08001343 Objects.requireNonNull(callerAttributionSource);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001344 Objects.requireNonNull(userHandle);
Alexander Dorokhine5258ad22021-04-15 10:59:55 -07001345 Objects.requireNonNull(callback);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001346
Xiaoyu Jinc410c4f2021-05-09 14:15:50 -07001347 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001348 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback(
1349 callerAttributionSource, userHandle, callback);
1350 if (targetUser == null) {
1351 return; // Verification failed; verifyIncomingCall triggered callback.
1352 }
1353 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, () -> {
Xiaoyu Jinc410c4f2021-05-09 14:15:50 -07001354 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001355 AppSearchUserInstance instance = null;
Xiaoyu Jinc410c4f2021-05-09 14:15:50 -07001356 int operationSuccessCount = 0;
1357 int operationFailureCount = 0;
Terry Wange04ceab2021-03-29 19:25:12 -07001358 try {
Cassie Wang2feb34d2021-07-22 18:55:19 +00001359 Context targetUserContext = mContext.createContextAsUser(targetUser,
1360 /*flags=*/ 0);
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001361 instance = mAppSearchUserInstanceManager.getOrCreateUserInstance(
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001362 targetUserContext,
1363 targetUser,
Alexander Dorokhine62f2d752022-04-13 00:42:19 -07001364 FrameworkAppSearchConfig.getInstance(SHARED_EXECUTOR));
Xiaoyu Jinc410c4f2021-05-09 14:15:50 -07001365 ++operationSuccessCount;
Terry Wange04ceab2021-03-29 19:25:12 -07001366 invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
1367 } catch (Throwable t) {
Xiaoyu Jinc410c4f2021-05-09 14:15:50 -07001368 ++operationFailureCount;
1369 statusCode = throwableToFailedResult(t).getResultCode();
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001370 invokeCallbackOnResult(callback, throwableToFailedResult(t));
Xiaoyu Jinc410c4f2021-05-09 14:15:50 -07001371 } finally {
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001372 if (instance != null) {
Xiaoyu Jinc410c4f2021-05-09 14:15:50 -07001373 int estimatedBinderLatencyMillis =
1374 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
1375 int totalLatencyMillis =
1376 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001377 instance.getLogger().logStats(new CallStats.Builder()
Alexander Dorokhine82a38722021-06-09 16:35:22 -07001378 .setStatusCode(statusCode)
1379 .setTotalLatencyMillis(totalLatencyMillis)
Xiaoyu Jinc410c4f2021-05-09 14:15:50 -07001380 .setCallType(CallStats.CALL_TYPE_INITIALIZE)
1381 // TODO(b/173532925) check the existing binder call latency chart
1382 // is good enough for us:
1383 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4
1384 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
1385 .setNumOperationsSucceeded(operationSuccessCount)
Alexander Dorokhine82a38722021-06-09 16:35:22 -07001386 .setNumOperationsFailed(operationFailureCount)
1387 .build());
Xiaoyu Jinc410c4f2021-05-09 14:15:50 -07001388 }
Terry Wange04ceab2021-03-29 19:25:12 -07001389 }
1390 });
Terry Wangdbd1dca2020-11-03 17:03:56 -08001391 }
Cassie Wang84e33a02021-06-18 14:13:31 -07001392 }
1393
Yang Yu0fcd51a2021-04-23 11:25:44 -07001394 private class AppSearchStorageStatsAugmenter implements StorageStatsAugmenter {
1395 @Override
Alexander Dorokhinea5d0d322021-05-10 15:59:31 -07001396 public void augmentStatsForPackageForUser(
Yang Yu0fcd51a2021-04-23 11:25:44 -07001397 @NonNull PackageStats stats,
1398 @NonNull String packageName,
Alexander Dorokhinea5d0d322021-05-10 15:59:31 -07001399 @NonNull UserHandle userHandle,
1400 boolean canCallerAccessAllStats) {
Yang Yu0fcd51a2021-04-23 11:25:44 -07001401 Objects.requireNonNull(stats);
1402 Objects.requireNonNull(packageName);
Alexander Dorokhinea5d0d322021-05-10 15:59:31 -07001403 Objects.requireNonNull(userHandle);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001404
Yang Yu0fcd51a2021-04-23 11:25:44 -07001405 try {
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001406 mServiceImplHelper.verifyUserUnlocked(userHandle);
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001407 AppSearchUserInstance instance =
Yang Yuc047f7b2021-06-25 12:22:42 -07001408 mAppSearchUserInstanceManager.getUserInstanceOrNull(userHandle);
1409 if (instance == null) {
1410 // augment storage info from file
1411 UserStorageInfo userStorageInfo =
1412 mAppSearchUserInstanceManager.getOrCreateUserStorageInfoInstance(
1413 userHandle);
1414 stats.dataSize +=
1415 userStorageInfo.getSizeBytesForPackage(packageName);
1416 } else {
1417 stats.dataSize += instance.getAppSearchImpl()
1418 .getStorageInfoForPackage(packageName).getSizeBytes();
1419 }
Yang Yu0fcd51a2021-04-23 11:25:44 -07001420 } catch (Throwable t) {
1421 Log.e(
1422 TAG,
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001423 "Unable to augment storage stats for "
1424 + userHandle
Yang Yu0fcd51a2021-04-23 11:25:44 -07001425 + " packageName "
1426 + packageName,
1427 t);
1428 }
1429 }
1430
1431 @Override
1432 public void augmentStatsForUid(
Alexander Dorokhinea5d0d322021-05-10 15:59:31 -07001433 @NonNull PackageStats stats, int uid, boolean canCallerAccessAllStats) {
Yang Yu0fcd51a2021-04-23 11:25:44 -07001434 Objects.requireNonNull(stats);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001435
1436 UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
Yang Yu0fcd51a2021-04-23 11:25:44 -07001437 try {
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001438 mServiceImplHelper.verifyUserUnlocked(userHandle);
Yang Yu0fcd51a2021-04-23 11:25:44 -07001439 String[] packagesForUid = mPackageManager.getPackagesForUid(uid);
1440 if (packagesForUid == null) {
1441 return;
1442 }
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001443 AppSearchUserInstance instance =
Yang Yuc047f7b2021-06-25 12:22:42 -07001444 mAppSearchUserInstanceManager.getUserInstanceOrNull(userHandle);
1445 if (instance == null) {
1446 // augment storage info from file
1447 UserStorageInfo userStorageInfo =
1448 mAppSearchUserInstanceManager.getOrCreateUserStorageInfoInstance(
1449 userHandle);
1450 for (int i = 0; i < packagesForUid.length; i++) {
1451 stats.dataSize += userStorageInfo.getSizeBytesForPackage(
1452 packagesForUid[i]);
1453 }
1454 } else {
1455 for (int i = 0; i < packagesForUid.length; i++) {
1456 stats.dataSize += instance.getAppSearchImpl()
1457 .getStorageInfoForPackage(packagesForUid[i]).getSizeBytes();
1458 }
Yang Yu0fcd51a2021-04-23 11:25:44 -07001459 }
1460 } catch (Throwable t) {
1461 Log.e(TAG, "Unable to augment storage stats for uid " + uid, t);
1462 }
1463 }
Alexander Dorokhinea5d0d322021-05-10 15:59:31 -07001464
1465 @Override
1466 public void augmentStatsForUser(
1467 @NonNull PackageStats stats, @NonNull UserHandle userHandle) {
1468 // TODO(b/179160886): this implementation could incur many jni calls and a lot of
1469 // in-memory processing from getStorageInfoForPackage. Instead, we can just compute the
1470 // size of the icing dir (or use the overall StorageInfo without interpolating it).
1471 Objects.requireNonNull(stats);
1472 Objects.requireNonNull(userHandle);
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001473
Alexander Dorokhinea5d0d322021-05-10 15:59:31 -07001474 try {
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001475 mServiceImplHelper.verifyUserUnlocked(userHandle);
Alexander Dorokhine4d051632021-06-18 10:48:58 -07001476 AppSearchUserInstance instance =
Yang Yuc047f7b2021-06-25 12:22:42 -07001477 mAppSearchUserInstanceManager.getUserInstanceOrNull(userHandle);
1478 if (instance == null) {
1479 // augment storage info from file
1480 UserStorageInfo userStorageInfo =
1481 mAppSearchUserInstanceManager.getOrCreateUserStorageInfoInstance(
1482 userHandle);
1483 stats.dataSize += userStorageInfo.getTotalSizeBytes();
1484 } else {
1485 List<PackageInfo> packagesForUser = mPackageManager.getInstalledPackagesAsUser(
1486 /*flags=*/0, userHandle.getIdentifier());
1487 if (packagesForUser != null) {
1488 for (int i = 0; i < packagesForUser.size(); i++) {
1489 String packageName = packagesForUser.get(i).packageName;
1490 stats.dataSize += instance.getAppSearchImpl()
1491 .getStorageInfoForPackage(packageName).getSizeBytes();
1492 }
1493 }
Alexander Dorokhinea5d0d322021-05-10 15:59:31 -07001494 }
1495 } catch (Throwable t) {
Alexander Dorokhinebea55602021-05-19 13:59:45 -07001496 Log.e(TAG, "Unable to augment storage stats for " + userHandle, t);
Alexander Dorokhinea5d0d322021-05-10 15:59:31 -07001497 }
1498 }
Yang Yu0fcd51a2021-04-23 11:25:44 -07001499 }
Terry Wangf4d219b2021-07-01 19:11:39 -07001500
Alexander Dorokhinee0587d32021-12-07 17:02:17 -08001501 /**
1502 * Dispatches change notifications if there are any to dispatch.
1503 *
1504 * <p>This method is async; notifications are dispatched onto their own registered executors.
1505 *
1506 * <p>IMPORTANT: You must always call this within the background task that contains the
1507 * operation that mutated the index. If you called it outside of that task, it could start
1508 * before the task completes, causing notifications to be missed.
1509 */
1510 @WorkerThread
1511 private void dispatchChangeNotifications(@NonNull AppSearchUserInstance instance) {
1512 instance.getAppSearchImpl().dispatchAndClearChangeNotifications();
1513 }
1514
1515 @WorkerThread
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001516 private void checkForOptimize(
1517 @NonNull UserHandle targetUser,
1518 @NonNull AppSearchUserInstance instance,
1519 int mutateBatchSize) {
Terry Wang5696a5e2022-03-21 17:11:43 -07001520 if (mServiceImplHelper.isUserLocked(targetUser)) {
1521 // We shouldn't schedule any task to locked user.
1522 return;
1523 }
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001524 mExecutorManager.getOrCreateUserExecutor(targetUser).execute(() -> {
Alexander Dorokhine14816e22021-07-12 10:53:24 -07001525 long totalLatencyStartMillis = SystemClock.elapsedRealtime();
1526 OptimizeStats.Builder builder = new OptimizeStats.Builder();
Terry Wangf4d219b2021-07-01 19:11:39 -07001527 try {
Alexander Dorokhine14816e22021-07-12 10:53:24 -07001528 instance.getAppSearchImpl().checkForOptimize(mutateBatchSize, builder);
Alexander Dorokhinec440dec2022-05-12 09:55:39 -07001529 } catch (Exception e) {
Terry Wangf4d219b2021-07-01 19:11:39 -07001530 Log.w(TAG, "Error occurred when check for optimize", e);
Alexander Dorokhine14816e22021-07-12 10:53:24 -07001531 } finally {
1532 OptimizeStats oStats = builder
1533 .setTotalLatencyMillis(
1534 (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis))
1535 .build();
1536 if (oStats.getOriginalDocumentCount() > 0) {
1537 // see if optimize has been run by checking originalDocumentCount
1538 instance.getLogger().logStats(oStats);
1539 }
Terry Wangf4d219b2021-07-01 19:11:39 -07001540 }
1541 });
1542 }
1543
Alexander Dorokhinee0587d32021-12-07 17:02:17 -08001544 @WorkerThread
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001545 private void checkForOptimize(
1546 @NonNull UserHandle targetUser,
1547 @NonNull AppSearchUserInstance instance) {
Terry Wang5696a5e2022-03-21 17:11:43 -07001548 if (mServiceImplHelper.isUserLocked(targetUser)) {
1549 // We shouldn't schedule any task to locked user.
1550 return;
1551 }
Alexander Dorokhine872129a2022-02-22 16:36:36 -08001552 mExecutorManager.getOrCreateUserExecutor(targetUser).execute(() -> {
Alexander Dorokhine14816e22021-07-12 10:53:24 -07001553 long totalLatencyStartMillis = SystemClock.elapsedRealtime();
1554 OptimizeStats.Builder builder = new OptimizeStats.Builder();
Terry Wangf4d219b2021-07-01 19:11:39 -07001555 try {
Alexander Dorokhine14816e22021-07-12 10:53:24 -07001556 instance.getAppSearchImpl().checkForOptimize(builder);
Alexander Dorokhinec440dec2022-05-12 09:55:39 -07001557 } catch (Exception e) {
Terry Wangf4d219b2021-07-01 19:11:39 -07001558 Log.w(TAG, "Error occurred when check for optimize", e);
Alexander Dorokhine14816e22021-07-12 10:53:24 -07001559 } finally {
1560 OptimizeStats oStats = builder
1561 .setTotalLatencyMillis(
1562 (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis))
1563 .build();
1564 if (oStats.getOriginalDocumentCount() > 0) {
1565 // see if optimize has been run by checking originalDocumentCount
1566 instance.getLogger().logStats(oStats);
1567 }
Terry Wangf4d219b2021-07-01 19:11:39 -07001568 }
1569 });
1570 }
Terry Wangfebbead2019-10-17 17:05:18 -07001571}