1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
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  */
16 package com.android.server.appsearch.appsindexer;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 import android.app.appsearch.AppSearchBatchResult;
21 import android.app.appsearch.AppSearchResult;
22 import android.app.appsearch.BatchResultCallback;
23 import android.app.appsearch.exceptions.AppSearchException;
24 
25 import java.util.Objects;
26 import java.util.concurrent.CompletableFuture;
27 import java.util.concurrent.ExecutionException;
28 import java.util.concurrent.Executor;
29 import java.util.function.Consumer;
30 
31 /** Contains common methods for converting async methods to sync */
32 public class SyncAppSearchBase {
33     protected final Executor mExecutor;
34 
SyncAppSearchBase(@onNull Executor executor)35     public SyncAppSearchBase(@NonNull Executor executor) {
36         mExecutor = Objects.requireNonNull(executor);
37     }
38 
executeAppSearchResultOperation( Consumer<Consumer<AppSearchResult<T>>> operation)39     protected <T> T executeAppSearchResultOperation(
40             Consumer<Consumer<AppSearchResult<T>>> operation) throws AppSearchException {
41         final CompletableFuture<AppSearchResult<T>> futureResult = new CompletableFuture<>();
42 
43         mExecutor.execute(
44                 () -> {
45                     operation.accept(futureResult::complete);
46                 });
47 
48         try {
49             // TODO(b/275592563): Change to get timeout value from config
50             AppSearchResult<T> result = futureResult.get();
51 
52             if (!result.isSuccess()) {
53                 throw new AppSearchException(result.getResultCode(), result.getErrorMessage());
54             }
55 
56             return result.getResultValue();
57         } catch (InterruptedException e) {
58             Thread.currentThread().interrupt();
59             throw new AppSearchException(
60                     AppSearchResult.RESULT_INTERNAL_ERROR, "Operation was interrupted.", e);
61         } catch (ExecutionException e) {
62             throw new AppSearchException(
63                     AppSearchResult.RESULT_UNKNOWN_ERROR,
64                     "Error executing operation.",
65                     e.getCause());
66         }
67     }
68 
executeAppSearchBatchResultOperation( Consumer<BatchResultCallback<T, V>> operation)69     protected <T, V> AppSearchBatchResult<T, V> executeAppSearchBatchResultOperation(
70             Consumer<BatchResultCallback<T, V>> operation) throws AppSearchException {
71         final CompletableFuture<AppSearchBatchResult<T, V>> futureResult =
72                 new CompletableFuture<>();
73 
74         mExecutor.execute(
75                 () ->
76                         operation.accept(
77                                 new BatchResultCallback<>() {
78                                     @Override
79                                     public void onResult(
80                                             @NonNull AppSearchBatchResult<T, V> value) {
81                                         futureResult.complete(value);
82                                     }
83 
84                                     @Override
85                                     public void onSystemError(@Nullable Throwable throwable) {
86                                         futureResult.completeExceptionally(throwable);
87                                     }
88                                 }));
89 
90         try {
91             // TODO(b/275592563): Change to get timeout value from config
92             return futureResult.get();
93         } catch (InterruptedException e) {
94             Thread.currentThread().interrupt();
95             throw new AppSearchException(
96                     AppSearchResult.RESULT_INTERNAL_ERROR, "Operation was interrupted.", e);
97         } catch (ExecutionException e) {
98             throw new AppSearchException(
99                     AppSearchResult.RESULT_UNKNOWN_ERROR,
100                     "Error executing operation.",
101                     e.getCause());
102         }
103     }
104 }
105