1 /*
2  * Copyright 2021 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 
17 package com.android.server.appsearch.external.localstorage.stats;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.appsearch.AppSearchResult;
23 import android.app.appsearch.annotation.CanIgnoreReturnValue;
24 import android.util.ArraySet;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 import java.util.Arrays;
31 import java.util.Objects;
32 import java.util.Set;
33 
34 /**
35  * A class for setting basic information to log for all function calls.
36  *
37  * <p>This class can set which stats to log for both batch and non-batch {@link
38  * android.app.appsearch.AppSearchSession} calls.
39  *
40  * <p>Some function calls may have their own detailed stats class like {@link PutDocumentStats}.
41  * However, {@link CallStats} can still be used along with the detailed stats class for easy
42  * aggregation/analysis with other function calls.
43  *
44  * @hide
45  */
46 public class CallStats {
47     /** Call types. */
48     @IntDef(
49             value = {
50                 CALL_TYPE_UNKNOWN,
51                 CALL_TYPE_INITIALIZE,
52                 CALL_TYPE_SET_SCHEMA,
53                 CALL_TYPE_PUT_DOCUMENTS,
54                 CALL_TYPE_GET_DOCUMENTS,
55                 CALL_TYPE_REMOVE_DOCUMENTS_BY_ID,
56                 CALL_TYPE_PUT_DOCUMENT,
57                 CALL_TYPE_GET_DOCUMENT,
58                 CALL_TYPE_REMOVE_DOCUMENT_BY_ID,
59                 CALL_TYPE_SEARCH,
60                 CALL_TYPE_OPTIMIZE,
61                 CALL_TYPE_FLUSH,
62                 CALL_TYPE_GLOBAL_SEARCH,
63                 CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH,
64                 CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH,
65                 CALL_TYPE_GLOBAL_GET_DOCUMENT_BY_ID,
66                 CALL_TYPE_SCHEMA_MIGRATION,
67                 CALL_TYPE_GLOBAL_GET_SCHEMA,
68                 CALL_TYPE_GET_SCHEMA,
69                 CALL_TYPE_GET_NAMESPACES,
70                 CALL_TYPE_GET_NEXT_PAGE,
71                 CALL_TYPE_INVALIDATE_NEXT_PAGE_TOKEN,
72                 CALL_TYPE_WRITE_SEARCH_RESULTS_TO_FILE,
73                 CALL_TYPE_PUT_DOCUMENTS_FROM_FILE,
74                 CALL_TYPE_SEARCH_SUGGESTION,
75                 CALL_TYPE_REPORT_SYSTEM_USAGE,
76                 CALL_TYPE_REPORT_USAGE,
77                 CALL_TYPE_GET_STORAGE_INFO,
78                 CALL_TYPE_REGISTER_OBSERVER_CALLBACK,
79                 CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK,
80                 CALL_TYPE_GLOBAL_GET_NEXT_PAGE,
81                 CALL_TYPE_EXECUTE_APP_FUNCTION
82             })
83     @Retention(RetentionPolicy.SOURCE)
84     public @interface CallType {}
85 
86     public static final int CALL_TYPE_UNKNOWN = 0;
87     public static final int CALL_TYPE_INITIALIZE = 1;
88     public static final int CALL_TYPE_SET_SCHEMA = 2;
89     public static final int CALL_TYPE_PUT_DOCUMENTS = 3;
90     public static final int CALL_TYPE_GET_DOCUMENTS = 4;
91     public static final int CALL_TYPE_REMOVE_DOCUMENTS_BY_ID = 5;
92     public static final int CALL_TYPE_PUT_DOCUMENT = 6;
93     public static final int CALL_TYPE_GET_DOCUMENT = 7;
94     public static final int CALL_TYPE_REMOVE_DOCUMENT_BY_ID = 8;
95     public static final int CALL_TYPE_SEARCH = 9;
96     public static final int CALL_TYPE_OPTIMIZE = 10;
97     public static final int CALL_TYPE_FLUSH = 11;
98     public static final int CALL_TYPE_GLOBAL_SEARCH = 12;
99     public static final int CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH = 13;
100     public static final int CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH = 14;
101     public static final int CALL_TYPE_GLOBAL_GET_DOCUMENT_BY_ID = 15;
102     public static final int CALL_TYPE_SCHEMA_MIGRATION = 16;
103     public static final int CALL_TYPE_GLOBAL_GET_SCHEMA = 17;
104     public static final int CALL_TYPE_GET_SCHEMA = 18;
105     public static final int CALL_TYPE_GET_NAMESPACES = 19;
106     public static final int CALL_TYPE_GET_NEXT_PAGE = 20;
107     public static final int CALL_TYPE_INVALIDATE_NEXT_PAGE_TOKEN = 21;
108     public static final int CALL_TYPE_WRITE_SEARCH_RESULTS_TO_FILE = 22;
109     public static final int CALL_TYPE_PUT_DOCUMENTS_FROM_FILE = 23;
110     public static final int CALL_TYPE_SEARCH_SUGGESTION = 24;
111     public static final int CALL_TYPE_REPORT_SYSTEM_USAGE = 25;
112     public static final int CALL_TYPE_REPORT_USAGE = 26;
113     public static final int CALL_TYPE_GET_STORAGE_INFO = 27;
114     public static final int CALL_TYPE_REGISTER_OBSERVER_CALLBACK = 28;
115     public static final int CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK = 29;
116     public static final int CALL_TYPE_GLOBAL_GET_NEXT_PAGE = 30;
117     public static final int CALL_TYPE_EXECUTE_APP_FUNCTION = 31;
118 
119     // These strings are for the subset of call types that correspond to an AppSearchManager API
120     private static final String CALL_TYPE_STRING_INITIALIZE = "initialize";
121     private static final String CALL_TYPE_STRING_SET_SCHEMA = "localSetSchema";
122     private static final String CALL_TYPE_STRING_PUT_DOCUMENTS = "localPutDocuments";
123     private static final String CALL_TYPE_STRING_GET_DOCUMENTS = "localGetDocuments";
124     private static final String CALL_TYPE_STRING_REMOVE_DOCUMENTS_BY_ID = "localRemoveByDocumentId";
125     private static final String CALL_TYPE_STRING_SEARCH = "localSearch";
126     private static final String CALL_TYPE_STRING_FLUSH = "flush";
127     private static final String CALL_TYPE_STRING_GLOBAL_SEARCH = "globalSearch";
128     private static final String CALL_TYPE_STRING_REMOVE_DOCUMENTS_BY_SEARCH = "localRemoveBySearch";
129     private static final String CALL_TYPE_STRING_GLOBAL_GET_DOCUMENT_BY_ID = "globalGetDocuments";
130     private static final String CALL_TYPE_STRING_GLOBAL_GET_SCHEMA = "globalGetSchema";
131     private static final String CALL_TYPE_STRING_GET_SCHEMA = "localGetSchema";
132     private static final String CALL_TYPE_STRING_GET_NAMESPACES = "localGetNamespaces";
133     private static final String CALL_TYPE_STRING_GET_NEXT_PAGE = "localGetNextPage";
134     private static final String CALL_TYPE_STRING_INVALIDATE_NEXT_PAGE_TOKEN =
135             "invalidateNextPageToken";
136     private static final String CALL_TYPE_STRING_WRITE_SEARCH_RESULTS_TO_FILE =
137             "localWriteSearchResultsToFile";
138     private static final String CALL_TYPE_STRING_PUT_DOCUMENTS_FROM_FILE =
139             "localPutDocumentsFromFile";
140     private static final String CALL_TYPE_STRING_SEARCH_SUGGESTION = "localSearchSuggestion";
141     private static final String CALL_TYPE_STRING_REPORT_SYSTEM_USAGE = "globalReportUsage";
142     private static final String CALL_TYPE_STRING_REPORT_USAGE = "localReportUsage";
143     private static final String CALL_TYPE_STRING_GET_STORAGE_INFO = "localGetStorageInfo";
144     private static final String CALL_TYPE_STRING_REGISTER_OBSERVER_CALLBACK =
145             "globalRegisterObserverCallback";
146     private static final String CALL_TYPE_STRING_UNREGISTER_OBSERVER_CALLBACK =
147             "globalUnregisterObserverCallback";
148     private static final String CALL_TYPE_STRING_GLOBAL_GET_NEXT_PAGE = "globalGetNextPage";
149     private static final String CALL_TYPE_STRING_EXECUTE_APP_FUNCTION = "executeAppFunction";
150 
151     @Nullable private final String mPackageName;
152     @Nullable private final String mDatabase;
153 
154     /**
155      * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal
156      * state.
157      */
158     @AppSearchResult.ResultCode private final int mStatusCode;
159 
160     private final int mTotalLatencyMillis;
161 
162     @CallType private final int mCallType;
163     private final int mEstimatedBinderLatencyMillis;
164     private final int mNumOperationsSucceeded;
165     private final int mNumOperationsFailed;
166 
CallStats(@onNull Builder builder)167     CallStats(@NonNull Builder builder) {
168         Objects.requireNonNull(builder);
169         mPackageName = builder.mPackageName;
170         mDatabase = builder.mDatabase;
171         mStatusCode = builder.mStatusCode;
172         mTotalLatencyMillis = builder.mTotalLatencyMillis;
173         mCallType = builder.mCallType;
174         mEstimatedBinderLatencyMillis = builder.mEstimatedBinderLatencyMillis;
175         mNumOperationsSucceeded = builder.mNumOperationsSucceeded;
176         mNumOperationsFailed = builder.mNumOperationsFailed;
177     }
178 
179     /** Returns calling package name. */
180     @Nullable
getPackageName()181     public String getPackageName() {
182         return mPackageName;
183     }
184 
185     /** Returns calling database name. */
186     @Nullable
getDatabase()187     public String getDatabase() {
188         return mDatabase;
189     }
190 
191     /** Returns status code for this api call. */
192     @AppSearchResult.ResultCode
getStatusCode()193     public int getStatusCode() {
194         return mStatusCode;
195     }
196 
197     /** Returns total latency of this api call in millis. */
getTotalLatencyMillis()198     public int getTotalLatencyMillis() {
199         return mTotalLatencyMillis;
200     }
201 
202     /** Returns type of the call. */
203     @CallType
getCallType()204     public int getCallType() {
205         return mCallType;
206     }
207 
208     /** Returns estimated binder latency, in milliseconds */
getEstimatedBinderLatencyMillis()209     public int getEstimatedBinderLatencyMillis() {
210         return mEstimatedBinderLatencyMillis;
211     }
212 
213     /**
214      * Returns number of operations succeeded.
215      *
216      * <p>For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total
217      * number of individual successful put operations. In this case, how many documents are
218      * successfully indexed.
219      *
220      * <p>For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema}, the
221      * sum of {@link CallStats#getNumOperationsSucceeded()} and {@link
222      * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation.
223      */
getNumOperationsSucceeded()224     public int getNumOperationsSucceeded() {
225         return mNumOperationsSucceeded;
226     }
227 
228     /**
229      * Returns number of operations failed.
230      *
231      * <p>For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total
232      * number of individual failed put operations. In this case, how many documents are failed to be
233      * indexed.
234      *
235      * <p>For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema}, the
236      * sum of {@link CallStats#getNumOperationsSucceeded()} and {@link
237      * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation.
238      */
getNumOperationsFailed()239     public int getNumOperationsFailed() {
240         return mNumOperationsFailed;
241     }
242 
243     /** Builder for {@link CallStats}. */
244     public static class Builder {
245         @Nullable String mPackageName;
246         @Nullable String mDatabase;
247         @AppSearchResult.ResultCode int mStatusCode;
248         int mTotalLatencyMillis;
249         @CallType int mCallType;
250         int mEstimatedBinderLatencyMillis;
251         int mNumOperationsSucceeded;
252         int mNumOperationsFailed;
253 
254         /** Sets the PackageName used by the session. */
255         @CanIgnoreReturnValue
256         @NonNull
setPackageName(@ullable String packageName)257         public Builder setPackageName(@Nullable String packageName) {
258             mPackageName = packageName;
259             return this;
260         }
261 
262         /** Sets the database used by the session. */
263         @CanIgnoreReturnValue
264         @NonNull
setDatabase(@ullable String database)265         public Builder setDatabase(@Nullable String database) {
266             mDatabase = database;
267             return this;
268         }
269 
270         /** Sets the status code. */
271         @CanIgnoreReturnValue
272         @NonNull
setStatusCode(@ppSearchResult.ResultCode int statusCode)273         public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
274             mStatusCode = statusCode;
275             return this;
276         }
277 
278         /** Sets total latency in millis. */
279         @CanIgnoreReturnValue
280         @NonNull
setTotalLatencyMillis(int totalLatencyMillis)281         public Builder setTotalLatencyMillis(int totalLatencyMillis) {
282             mTotalLatencyMillis = totalLatencyMillis;
283             return this;
284         }
285 
286         /** Sets type of the call. */
287         @CanIgnoreReturnValue
288         @NonNull
setCallType(@allType int callType)289         public Builder setCallType(@CallType int callType) {
290             mCallType = callType;
291             return this;
292         }
293 
294         /** Sets estimated binder latency, in milliseconds. */
295         @CanIgnoreReturnValue
296         @NonNull
setEstimatedBinderLatencyMillis(int estimatedBinderLatencyMillis)297         public Builder setEstimatedBinderLatencyMillis(int estimatedBinderLatencyMillis) {
298             mEstimatedBinderLatencyMillis = estimatedBinderLatencyMillis;
299             return this;
300         }
301 
302         /**
303          * Sets number of operations succeeded.
304          *
305          * <p>For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total
306          * number of individual successful put operations. In this case, how many documents are
307          * successfully indexed.
308          *
309          * <p>For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema},
310          * the sum of {@link CallStats#getNumOperationsSucceeded()} and {@link
311          * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation.
312          */
313         @CanIgnoreReturnValue
314         @NonNull
setNumOperationsSucceeded(int numOperationsSucceeded)315         public Builder setNumOperationsSucceeded(int numOperationsSucceeded) {
316             mNumOperationsSucceeded = numOperationsSucceeded;
317             return this;
318         }
319 
320         /**
321          * Sets number of operations failed.
322          *
323          * <p>For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total
324          * number of individual failed put operations. In this case, how many documents are failed
325          * to be indexed.
326          *
327          * <p>For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema},
328          * the sum of {@link CallStats#getNumOperationsSucceeded()} and {@link
329          * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation.
330          */
331         @CanIgnoreReturnValue
332         @NonNull
setNumOperationsFailed(int numOperationsFailed)333         public Builder setNumOperationsFailed(int numOperationsFailed) {
334             mNumOperationsFailed = numOperationsFailed;
335             return this;
336         }
337 
338         /** Creates {@link CallStats} object from {@link Builder} instance. */
339         @NonNull
build()340         public CallStats build() {
341             return new CallStats(/* builder= */ this);
342         }
343     }
344 
345     /**
346      * Returns the {@link CallStats.CallType} represented by the given AppSearchManager API name. If
347      * an unknown name is provided, {@link CallStats.CallType#CALL_TYPE_UNKNOWN} is returned.
348      */
349     @CallType
getApiCallTypeFromName(@onNull String name)350     public static int getApiCallTypeFromName(@NonNull String name) {
351         switch (name) {
352             case CALL_TYPE_STRING_INITIALIZE:
353                 return CALL_TYPE_INITIALIZE;
354             case CALL_TYPE_STRING_SET_SCHEMA:
355                 return CALL_TYPE_SET_SCHEMA;
356             case CALL_TYPE_STRING_PUT_DOCUMENTS:
357                 return CALL_TYPE_PUT_DOCUMENTS;
358             case CALL_TYPE_STRING_GET_DOCUMENTS:
359                 return CALL_TYPE_GET_DOCUMENTS;
360             case CALL_TYPE_STRING_REMOVE_DOCUMENTS_BY_ID:
361                 return CALL_TYPE_REMOVE_DOCUMENTS_BY_ID;
362             case CALL_TYPE_STRING_SEARCH:
363                 return CALL_TYPE_SEARCH;
364             case CALL_TYPE_STRING_FLUSH:
365                 return CALL_TYPE_FLUSH;
366             case CALL_TYPE_STRING_GLOBAL_SEARCH:
367                 return CALL_TYPE_GLOBAL_SEARCH;
368             case CALL_TYPE_STRING_REMOVE_DOCUMENTS_BY_SEARCH:
369                 return CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH;
370             case CALL_TYPE_STRING_GLOBAL_GET_DOCUMENT_BY_ID:
371                 return CALL_TYPE_GLOBAL_GET_DOCUMENT_BY_ID;
372             case CALL_TYPE_STRING_GLOBAL_GET_SCHEMA:
373                 return CALL_TYPE_GLOBAL_GET_SCHEMA;
374             case CALL_TYPE_STRING_GET_SCHEMA:
375                 return CALL_TYPE_GET_SCHEMA;
376             case CALL_TYPE_STRING_GET_NAMESPACES:
377                 return CALL_TYPE_GET_NAMESPACES;
378             case CALL_TYPE_STRING_GET_NEXT_PAGE:
379                 return CALL_TYPE_GET_NEXT_PAGE;
380             case CALL_TYPE_STRING_INVALIDATE_NEXT_PAGE_TOKEN:
381                 return CALL_TYPE_INVALIDATE_NEXT_PAGE_TOKEN;
382             case CALL_TYPE_STRING_WRITE_SEARCH_RESULTS_TO_FILE:
383                 return CALL_TYPE_WRITE_SEARCH_RESULTS_TO_FILE;
384             case CALL_TYPE_STRING_PUT_DOCUMENTS_FROM_FILE:
385                 return CALL_TYPE_PUT_DOCUMENTS_FROM_FILE;
386             case CALL_TYPE_STRING_SEARCH_SUGGESTION:
387                 return CALL_TYPE_SEARCH_SUGGESTION;
388             case CALL_TYPE_STRING_REPORT_SYSTEM_USAGE:
389                 return CALL_TYPE_REPORT_SYSTEM_USAGE;
390             case CALL_TYPE_STRING_REPORT_USAGE:
391                 return CALL_TYPE_REPORT_USAGE;
392             case CALL_TYPE_STRING_GET_STORAGE_INFO:
393                 return CALL_TYPE_GET_STORAGE_INFO;
394             case CALL_TYPE_STRING_REGISTER_OBSERVER_CALLBACK:
395                 return CALL_TYPE_REGISTER_OBSERVER_CALLBACK;
396             case CALL_TYPE_STRING_UNREGISTER_OBSERVER_CALLBACK:
397                 return CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK;
398             case CALL_TYPE_STRING_GLOBAL_GET_NEXT_PAGE:
399                 return CALL_TYPE_GLOBAL_GET_NEXT_PAGE;
400             case CALL_TYPE_STRING_EXECUTE_APP_FUNCTION:
401                 return CALL_TYPE_EXECUTE_APP_FUNCTION;
402             default:
403                 return CALL_TYPE_UNKNOWN;
404         }
405     }
406 
407     /** Returns the set of all {@link CallStats.CallType} that map to an AppSearchManager API. */
408     @VisibleForTesting
409     @NonNull
getAllApiCallTypes()410     public static Set<Integer> getAllApiCallTypes() {
411         return new ArraySet<>(
412                 Arrays.asList(
413                         CALL_TYPE_INITIALIZE,
414                         CALL_TYPE_SET_SCHEMA,
415                         CALL_TYPE_PUT_DOCUMENTS,
416                         CALL_TYPE_GET_DOCUMENTS,
417                         CALL_TYPE_REMOVE_DOCUMENTS_BY_ID,
418                         CALL_TYPE_SEARCH,
419                         CALL_TYPE_FLUSH,
420                         CALL_TYPE_GLOBAL_SEARCH,
421                         CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH,
422                         CALL_TYPE_GLOBAL_GET_DOCUMENT_BY_ID,
423                         CALL_TYPE_GLOBAL_GET_SCHEMA,
424                         CALL_TYPE_GET_SCHEMA,
425                         CALL_TYPE_GET_NAMESPACES,
426                         CALL_TYPE_GET_NEXT_PAGE,
427                         CALL_TYPE_INVALIDATE_NEXT_PAGE_TOKEN,
428                         CALL_TYPE_WRITE_SEARCH_RESULTS_TO_FILE,
429                         CALL_TYPE_PUT_DOCUMENTS_FROM_FILE,
430                         CALL_TYPE_SEARCH_SUGGESTION,
431                         CALL_TYPE_REPORT_SYSTEM_USAGE,
432                         CALL_TYPE_REPORT_USAGE,
433                         CALL_TYPE_GET_STORAGE_INFO,
434                         CALL_TYPE_REGISTER_OBSERVER_CALLBACK,
435                         CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK,
436                         CALL_TYPE_GLOBAL_GET_NEXT_PAGE,
437                         CALL_TYPE_EXECUTE_APP_FUNCTION));
438     }
439 }
440