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 package com.android.server.appsearch.external.localstorage.stats;
17 
18 import android.annotation.IntDef;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.appsearch.AppSearchResult;
22 import android.app.appsearch.SearchSpec;
23 
24 import com.android.internal.util.Preconditions;
25 
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 import java.util.Objects;
29 
30 /**
31  * Class holds detailed stats for {@link android.app.appsearch.AppSearchSession#search(String,
32  * SearchSpec)}
33  *
34  * @hide
35  */
36 public final class SearchStats {
37     @IntDef(
38             value = {
39                 // Searches apps' own documents.
40                 VISIBILITY_SCOPE_LOCAL,
41                 // Searches the global documents. Including platform surfaceable and 3p-access.
42                 VISIBILITY_SCOPE_GLOBAL,
43                 VISIBILITY_SCOPE_UNKNOWN,
44                 // TODO(b/173532925) Add THIRD_PARTY_ACCESS once we can distinguish platform
45                 //  surfaceable from 3p access(right both of them are categorized as
46                 //  VISIBILITY_SCOPE_GLOBAL)
47             })
48     @Retention(RetentionPolicy.SOURCE)
49     public @interface VisibilityScope {}
50 
51     // Searches apps' own documents.
52     public static final int VISIBILITY_SCOPE_LOCAL = 1;
53     // Searches the global documents. Including platform surfaceable and 3p-access.
54     public static final int VISIBILITY_SCOPE_GLOBAL = 2;
55     public static final int VISIBILITY_SCOPE_UNKNOWN = 3;
56 
57     // TODO(b/173532925): Add a field searchType to indicate where the search is used(normal
58     //  query vs in removeByQuery vs during migration)
59 
60     @NonNull private final String mPackageName;
61     @Nullable private final String mDatabase;
62     /**
63      * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal
64      * state.
65      */
66     @AppSearchResult.ResultCode private final int mStatusCode;
67 
68     private final int mTotalLatencyMillis;
69     /** Time used to rewrite the search spec. */
70     private final int mRewriteSearchSpecLatencyMillis;
71     /** Time used to rewrite the search results. */
72     private final int mRewriteSearchResultLatencyMillis;
73     /** Defines the scope the query is searching over */
74     @VisibilityScope private final int mVisibilityScope;
75     /** Overall time used for the native function call. */
76     private final int mNativeLatencyMillis;
77     /** Number of terms in the query string. */
78     private final int mNativeNumTerms;
79     /** Length of the query string. */
80     private final int mNativeQueryLength;
81     /** Number of namespaces filtered. */
82     private final int mNativeNumNamespacesFiltered;
83     /** Number of schema types filtered. */
84     private final int mNativeNumSchemaTypesFiltered;
85     /** The requested number of results in one page. */
86     private final int mNativeRequestedPageSize;
87     /** The actual number of results returned in the current page. */
88     private final int mNativeNumResultsReturnedCurrentPage;
89     /**
90      * Whether the function call is querying the first page. If it's not, Icing will fetch the
91      * results from cache so that some steps may be skipped.
92      */
93     private final boolean mNativeIsFirstPage;
94     /**
95      * Time used to parse the query, including 2 parts: tokenizing and transforming tokens into an
96      * iterator tree.
97      */
98     private final int mNativeParseQueryLatencyMillis;
99     /** Strategy of scoring and ranking. */
100     @SearchSpec.RankingStrategy private final int mNativeRankingStrategy;
101     /** Number of documents scored. */
102     private final int mNativeNumDocumentsScored;
103     /** Time used to score the raw results. */
104     private final int mNativeScoringLatencyMillis;
105     /** Time used to rank the scored results. */
106     private final int mNativeRankingLatencyMillis;
107     /**
108      * Time used to fetch the document protos. Note that it includes the time to snippet if {@link
109      * SearchStats#mNativeNumResultsWithSnippets} is greater than 0.
110      */
111     private final int mNativeDocumentRetrievingLatencyMillis;
112     /** How many snippets are calculated. */
113     private final int mNativeNumResultsWithSnippets;
114 
SearchStats(@onNull Builder builder)115     SearchStats(@NonNull Builder builder) {
116         Objects.requireNonNull(builder);
117         mPackageName = builder.mPackageName;
118         mDatabase = builder.mDatabase;
119         mStatusCode = builder.mStatusCode;
120         mTotalLatencyMillis = builder.mTotalLatencyMillis;
121         mRewriteSearchSpecLatencyMillis = builder.mRewriteSearchSpecLatencyMillis;
122         mRewriteSearchResultLatencyMillis = builder.mRewriteSearchResultLatencyMillis;
123         mVisibilityScope = builder.mVisibilityScope;
124         mNativeLatencyMillis = builder.mNativeLatencyMillis;
125         mNativeNumTerms = builder.mNativeNumTerms;
126         mNativeQueryLength = builder.mNativeQueryLength;
127         mNativeNumNamespacesFiltered = builder.mNativeNumNamespacesFiltered;
128         mNativeNumSchemaTypesFiltered = builder.mNativeNumSchemaTypesFiltered;
129         mNativeRequestedPageSize = builder.mNativeRequestedPageSize;
130         mNativeNumResultsReturnedCurrentPage = builder.mNativeNumResultsReturnedCurrentPage;
131         mNativeIsFirstPage = builder.mNativeIsFirstPage;
132         mNativeParseQueryLatencyMillis = builder.mNativeParseQueryLatencyMillis;
133         mNativeRankingStrategy = builder.mNativeRankingStrategy;
134         mNativeNumDocumentsScored = builder.mNativeNumDocumentsScored;
135         mNativeScoringLatencyMillis = builder.mNativeScoringLatencyMillis;
136         mNativeRankingLatencyMillis = builder.mNativeRankingLatencyMillis;
137         mNativeNumResultsWithSnippets = builder.mNativeNumResultsWithSnippets;
138         mNativeDocumentRetrievingLatencyMillis = builder.mNativeDocumentRetrievingLatencyMillis;
139     }
140 
141     /** Returns the package name of the session. */
142     @NonNull
getPackageName()143     public String getPackageName() {
144         return mPackageName;
145     }
146 
147     /**
148      * Returns the database name of the session.
149      *
150      * @return database name used by the session. {@code null} if and only if it is a global
151      *     search(visibilityScope is {@link SearchStats#VISIBILITY_SCOPE_GLOBAL}).
152      */
153     @Nullable
getDatabase()154     public String getDatabase() {
155         return mDatabase;
156     }
157 
158     /** Returns status of the search. */
159     @AppSearchResult.ResultCode
getStatusCode()160     public int getStatusCode() {
161         return mStatusCode;
162     }
163 
164     /** Returns the total latency of the search. */
getTotalLatencyMillis()165     public int getTotalLatencyMillis() {
166         return mTotalLatencyMillis;
167     }
168 
169     /** Returns how much time spent on rewriting the {@link SearchSpec}. */
getRewriteSearchSpecLatencyMillis()170     public int getRewriteSearchSpecLatencyMillis() {
171         return mRewriteSearchSpecLatencyMillis;
172     }
173 
174     /** Returns how much time spent on rewriting the {@link android.app.appsearch.SearchResult}. */
getRewriteSearchResultLatencyMillis()175     public int getRewriteSearchResultLatencyMillis() {
176         return mRewriteSearchResultLatencyMillis;
177     }
178 
179     /** Returns the visibility scope of the search. */
180     @VisibilityScope
getVisibilityScope()181     public int getVisibilityScope() {
182         return mVisibilityScope;
183     }
184 
185     /** Returns how much time spent on the native calls. */
getNativeLatencyMillis()186     public int getNativeLatencyMillis() {
187         return mNativeLatencyMillis;
188     }
189 
190     /** Returns number of terms in the search string. */
getTermCount()191     public int getTermCount() {
192         return mNativeNumTerms;
193     }
194 
195     /** Returns the length of the search string. */
getQueryLength()196     public int getQueryLength() {
197         return mNativeQueryLength;
198     }
199 
200     /** Returns number of namespaces filtered. */
getFilteredNamespaceCount()201     public int getFilteredNamespaceCount() {
202         return mNativeNumNamespacesFiltered;
203     }
204 
205     /** Returns number of schema types filtered. */
getFilteredSchemaTypeCount()206     public int getFilteredSchemaTypeCount() {
207         return mNativeNumSchemaTypesFiltered;
208     }
209 
210     /** Returns the requested number of results in one page. */
getRequestedPageSize()211     public int getRequestedPageSize() {
212         return mNativeRequestedPageSize;
213     }
214 
215     /** Returns the actual number of results returned in the current page. */
getCurrentPageReturnedResultCount()216     public int getCurrentPageReturnedResultCount() {
217         return mNativeNumResultsReturnedCurrentPage;
218     }
219 
220     // TODO(b/185184738) Make it an integer to show how many pages having been returned.
221     /** Returns whether the function call is querying the first page. */
isFirstPage()222     public boolean isFirstPage() {
223         return mNativeIsFirstPage;
224     }
225 
226     /**
227      * Returns time used to parse the query, including 2 parts: tokenizing and transforming tokens
228      * into an iterator tree.
229      */
getParseQueryLatencyMillis()230     public int getParseQueryLatencyMillis() {
231         return mNativeParseQueryLatencyMillis;
232     }
233 
234     /** Returns strategy of scoring and ranking. */
235     @SearchSpec.RankingStrategy
getRankingStrategy()236     public int getRankingStrategy() {
237         return mNativeRankingStrategy;
238     }
239 
240     /** Returns number of documents scored. */
getScoredDocumentCount()241     public int getScoredDocumentCount() {
242         return mNativeNumDocumentsScored;
243     }
244 
245     /** Returns time used to score the raw results. */
getScoringLatencyMillis()246     public int getScoringLatencyMillis() {
247         return mNativeScoringLatencyMillis;
248     }
249 
250     /** Returns time used to rank the scored results. */
getRankingLatencyMillis()251     public int getRankingLatencyMillis() {
252         return mNativeRankingLatencyMillis;
253     }
254 
255     /**
256      * Returns time used to fetch the document protos. Note that it includes the time to snippet if
257      * {@link SearchStats#mNativeNumResultsWithSnippets} is not zero.
258      */
getDocumentRetrievingLatencyMillis()259     public int getDocumentRetrievingLatencyMillis() {
260         return mNativeDocumentRetrievingLatencyMillis;
261     }
262 
263     /** Returns the number of the results in the page returned were snippeted. */
getResultWithSnippetsCount()264     public int getResultWithSnippetsCount() {
265         return mNativeNumResultsWithSnippets;
266     }
267 
268     /** Builder for {@link SearchStats} */
269     public static class Builder {
270         @NonNull final String mPackageName;
271         @Nullable String mDatabase;
272         @AppSearchResult.ResultCode int mStatusCode;
273         int mTotalLatencyMillis;
274         int mRewriteSearchSpecLatencyMillis;
275         int mRewriteSearchResultLatencyMillis;
276         int mVisibilityScope;
277         int mNativeLatencyMillis;
278         int mNativeNumTerms;
279         int mNativeQueryLength;
280         int mNativeNumNamespacesFiltered;
281         int mNativeNumSchemaTypesFiltered;
282         int mNativeRequestedPageSize;
283         int mNativeNumResultsReturnedCurrentPage;
284         boolean mNativeIsFirstPage;
285         int mNativeParseQueryLatencyMillis;
286         int mNativeRankingStrategy;
287         int mNativeNumDocumentsScored;
288         int mNativeScoringLatencyMillis;
289         int mNativeRankingLatencyMillis;
290         int mNativeNumResultsWithSnippets;
291         int mNativeDocumentRetrievingLatencyMillis;
292 
293         /**
294          * Constructor
295          *
296          * @param visibilityScope scope for the corresponding search.
297          * @param packageName name of the calling package.
298          */
Builder(@isibilityScope int visibilityScope, @NonNull String packageName)299         public Builder(@VisibilityScope int visibilityScope, @NonNull String packageName) {
300             mVisibilityScope = visibilityScope;
301             mPackageName = Objects.requireNonNull(packageName);
302         }
303 
304         /** Sets the database used by the session. */
305         @NonNull
setDatabase(@onNull String database)306         public Builder setDatabase(@NonNull String database) {
307             mDatabase = Objects.requireNonNull(database);
308             return this;
309         }
310 
311         /** Sets the status of the search. */
312         @NonNull
setStatusCode(@ppSearchResult.ResultCode int statusCode)313         public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
314             mStatusCode = statusCode;
315             return this;
316         }
317 
318         /** Sets total latency for the search. */
319         @NonNull
setTotalLatencyMillis(int totalLatencyMillis)320         public Builder setTotalLatencyMillis(int totalLatencyMillis) {
321             mTotalLatencyMillis = totalLatencyMillis;
322             return this;
323         }
324 
325         /** Sets time used to rewrite the search spec. */
326         @NonNull
setRewriteSearchSpecLatencyMillis(int rewriteSearchSpecLatencyMillis)327         public Builder setRewriteSearchSpecLatencyMillis(int rewriteSearchSpecLatencyMillis) {
328             mRewriteSearchSpecLatencyMillis = rewriteSearchSpecLatencyMillis;
329             return this;
330         }
331 
332         /** Sets time used to rewrite the search results. */
333         @NonNull
setRewriteSearchResultLatencyMillis(int rewriteSearchResultLatencyMillis)334         public Builder setRewriteSearchResultLatencyMillis(int rewriteSearchResultLatencyMillis) {
335             mRewriteSearchResultLatencyMillis = rewriteSearchResultLatencyMillis;
336             return this;
337         }
338 
339         /** Sets overall time used for the native function calls. */
340         @NonNull
setNativeLatencyMillis(int nativeLatencyMillis)341         public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
342             mNativeLatencyMillis = nativeLatencyMillis;
343             return this;
344         }
345 
346         /** Sets number of terms in the search string. */
347         @NonNull
setTermCount(int termCount)348         public Builder setTermCount(int termCount) {
349             mNativeNumTerms = termCount;
350             return this;
351         }
352 
353         /** Sets length of the search string. */
354         @NonNull
setQueryLength(int queryLength)355         public Builder setQueryLength(int queryLength) {
356             mNativeQueryLength = queryLength;
357             return this;
358         }
359 
360         /** Sets number of namespaces filtered. */
361         @NonNull
setFilteredNamespaceCount(int filteredNamespaceCount)362         public Builder setFilteredNamespaceCount(int filteredNamespaceCount) {
363             mNativeNumNamespacesFiltered = filteredNamespaceCount;
364             return this;
365         }
366 
367         /** Sets number of schema types filtered. */
368         @NonNull
setFilteredSchemaTypeCount(int filteredSchemaTypeCount)369         public Builder setFilteredSchemaTypeCount(int filteredSchemaTypeCount) {
370             mNativeNumSchemaTypesFiltered = filteredSchemaTypeCount;
371             return this;
372         }
373 
374         /** Sets the requested number of results in one page. */
375         @NonNull
setRequestedPageSize(int requestedPageSize)376         public Builder setRequestedPageSize(int requestedPageSize) {
377             mNativeRequestedPageSize = requestedPageSize;
378             return this;
379         }
380 
381         /** Sets the actual number of results returned in the current page. */
382         @NonNull
setCurrentPageReturnedResultCount(int currentPageReturnedResultCount)383         public Builder setCurrentPageReturnedResultCount(int currentPageReturnedResultCount) {
384             mNativeNumResultsReturnedCurrentPage = currentPageReturnedResultCount;
385             return this;
386         }
387 
388         /**
389          * Sets whether the function call is querying the first page. If it's not, Icing will fetch
390          * the results from cache so that some steps may be skipped.
391          */
392         @NonNull
setIsFirstPage(boolean nativeIsFirstPage)393         public Builder setIsFirstPage(boolean nativeIsFirstPage) {
394             mNativeIsFirstPage = nativeIsFirstPage;
395             return this;
396         }
397 
398         /**
399          * Sets time used to parse the query, including 2 parts: tokenizing and transforming tokens
400          * into an iterator tree.
401          */
402         @NonNull
setParseQueryLatencyMillis(int parseQueryLatencyMillis)403         public Builder setParseQueryLatencyMillis(int parseQueryLatencyMillis) {
404             mNativeParseQueryLatencyMillis = parseQueryLatencyMillis;
405             return this;
406         }
407 
408         /** Sets strategy of scoring and ranking. */
409         @NonNull
setRankingStrategy(@earchSpec.RankingStrategy int rankingStrategy)410         public Builder setRankingStrategy(@SearchSpec.RankingStrategy int rankingStrategy) {
411             mNativeRankingStrategy = rankingStrategy;
412             return this;
413         }
414 
415         /** Sets number of documents scored. */
416         @NonNull
setScoredDocumentCount(int scoredDocumentCount)417         public Builder setScoredDocumentCount(int scoredDocumentCount) {
418             mNativeNumDocumentsScored = scoredDocumentCount;
419             return this;
420         }
421 
422         /** Sets time used to score the raw results. */
423         @NonNull
setScoringLatencyMillis(int scoringLatencyMillis)424         public Builder setScoringLatencyMillis(int scoringLatencyMillis) {
425             mNativeScoringLatencyMillis = scoringLatencyMillis;
426             return this;
427         }
428 
429         /** Sets time used to rank the scored results. */
430         @NonNull
setRankingLatencyMillis(int rankingLatencyMillis)431         public Builder setRankingLatencyMillis(int rankingLatencyMillis) {
432             mNativeRankingLatencyMillis = rankingLatencyMillis;
433             return this;
434         }
435 
436         /** Sets time used to fetch the document protos. */
437         @NonNull
setDocumentRetrievingLatencyMillis(int documentRetrievingLatencyMillis)438         public Builder setDocumentRetrievingLatencyMillis(int documentRetrievingLatencyMillis) {
439             mNativeDocumentRetrievingLatencyMillis = documentRetrievingLatencyMillis;
440             return this;
441         }
442 
443         /** Sets how many snippets are calculated. */
444         @NonNull
setResultWithSnippetsCount(int resultWithSnippetsCount)445         public Builder setResultWithSnippetsCount(int resultWithSnippetsCount) {
446             mNativeNumResultsWithSnippets = resultWithSnippetsCount;
447             return this;
448         }
449 
450         /**
451          * Constructs a new {@link SearchStats} from the contents of this {@link
452          * SearchStats.Builder}.
453          */
454         @NonNull
build()455         public SearchStats build() {
456             if (mDatabase == null) {
457                 Preconditions.checkState(
458                         mVisibilityScope != SearchStats.VISIBILITY_SCOPE_LOCAL,
459                         "database can not be null if visibilityScope is local.");
460             }
461 
462             return new SearchStats(/* builder= */ this);
463         }
464     }
465 }
466