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