• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.appfunctions;
18 
19 import static android.app.appfunctions.AppFunctionRuntimeMetadata.PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID;
20 import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_INDEXER_PACKAGE;
21 import static android.app.appfunctions.AppFunctionStaticMetadataHelper.PROPERTY_FUNCTION_ID;
22 
23 import android.Manifest;
24 import android.annotation.BinderThread;
25 import android.annotation.RequiresPermission;
26 import android.annotation.NonNull;
27 import android.content.Context;
28 import android.content.pm.UserInfo;
29 import android.os.UserManager;
30 import android.util.IndentingPrintWriter;
31 import android.app.appfunctions.AppFunctionRuntimeMetadata;
32 import android.app.appfunctions.AppFunctionStaticMetadataHelper;
33 import android.app.appsearch.AppSearchManager;
34 import android.app.appsearch.AppSearchManager.SearchContext;
35 import android.app.appsearch.JoinSpec;
36 import android.app.appsearch.GenericDocument;
37 import android.app.appsearch.SearchResult;
38 import android.app.appsearch.SearchSpec;
39 
40 import java.io.PrintWriter;
41 import java.lang.reflect.Array;
42 import java.util.Arrays;
43 import java.util.List;
44 import java.util.Set;
45 
46 public final class AppFunctionDumpHelper {
47     private static final String TAG = AppFunctionDumpHelper.class.getSimpleName();
48 
AppFunctionDumpHelper()49     private AppFunctionDumpHelper() {}
50 
51     /** Dumps the state of all app functions for all users. */
52     @BinderThread
53     @RequiresPermission(
54             anyOf = {Manifest.permission.CREATE_USERS, Manifest.permission.MANAGE_USERS})
dumpAppFunctionsState(@onNull Context context, @NonNull PrintWriter w)55     public static void dumpAppFunctionsState(@NonNull Context context, @NonNull PrintWriter w) {
56         UserManager userManager = context.getSystemService(UserManager.class);
57         if (userManager == null) {
58             w.println("Couldn't retrieve UserManager.");
59             return;
60         }
61 
62         IndentingPrintWriter pw = new IndentingPrintWriter(w);
63 
64         List<UserInfo> userInfos = userManager.getAliveUsers();
65         for (UserInfo userInfo : userInfos) {
66             pw.println(
67                     "AppFunction state for user " + userInfo.getUserHandle().getIdentifier() + ":");
68             pw.increaseIndent();
69             dumpAppFunctionsStateForUser(
70                     context.createContextAsUser(userInfo.getUserHandle(), /* flags= */ 0), pw);
71             pw.decreaseIndent();
72         }
73     }
74 
dumpAppFunctionsStateForUser( @onNull Context context, @NonNull IndentingPrintWriter pw)75     private static void dumpAppFunctionsStateForUser(
76             @NonNull Context context, @NonNull IndentingPrintWriter pw) {
77         AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
78         if (appSearchManager == null) {
79             pw.println("Couldn't retrieve AppSearchManager.");
80             return;
81         }
82 
83         try (FutureGlobalSearchSession searchSession =
84                 new FutureGlobalSearchSession(appSearchManager, Runnable::run)) {
85             pw.println();
86 
87             try (FutureSearchResults futureSearchResults =
88                     searchSession.search("", buildAppFunctionMetadataSearchSpec()).get(); ) {
89                 List<SearchResult> searchResultsList;
90                 do {
91                     searchResultsList = futureSearchResults.getNextPage().get();
92                     for (SearchResult searchResult : searchResultsList) {
93                         dumpAppFunctionMetadata(pw, searchResult);
94                     }
95                 } while (!searchResultsList.isEmpty());
96             }
97 
98         } catch (Exception e) {
99             pw.println("Failed to dump AppFunction state: " + e);
100         }
101     }
102 
buildAppFunctionMetadataSearchSpec()103     private static SearchSpec buildAppFunctionMetadataSearchSpec() {
104         SearchSpec runtimeMetadataSearchSpec =
105                 new SearchSpec.Builder()
106                         .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
107                         .addFilterSchemas(AppFunctionRuntimeMetadata.RUNTIME_SCHEMA_TYPE)
108                         .build();
109         JoinSpec joinSpec =
110                 new JoinSpec.Builder(PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID)
111                         .setNestedSearch(/* queryExpression= */ "", runtimeMetadataSearchSpec)
112                         .build();
113 
114         return new SearchSpec.Builder()
115                 .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
116                 .addFilterSchemas(AppFunctionStaticMetadataHelper.STATIC_SCHEMA_TYPE)
117                 .setJoinSpec(joinSpec)
118                 .build();
119     }
120 
dumpAppFunctionMetadata( IndentingPrintWriter pw, SearchResult joinedSearchResult)121     private static void dumpAppFunctionMetadata(
122             IndentingPrintWriter pw, SearchResult joinedSearchResult) {
123         pw.println(
124                 "AppFunctionMetadata for: "
125                         + joinedSearchResult
126                                 .getGenericDocument()
127                                 .getPropertyString(PROPERTY_FUNCTION_ID));
128         pw.increaseIndent();
129 
130         pw.println("Static Metadata:");
131         pw.increaseIndent();
132         writeGenericDocumentProperties(pw, joinedSearchResult.getGenericDocument());
133         pw.decreaseIndent();
134 
135         pw.println("Runtime Metadata:");
136         pw.increaseIndent();
137         if (!joinedSearchResult.getJoinedResults().isEmpty()) {
138             writeGenericDocumentProperties(
139                     pw, joinedSearchResult.getJoinedResults().getFirst().getGenericDocument());
140         } else {
141             pw.println("No runtime metadata found.");
142         }
143         pw.decreaseIndent();
144 
145         pw.decreaseIndent();
146     }
147 
writeGenericDocumentProperties( IndentingPrintWriter pw, GenericDocument genericDocument)148     private static void writeGenericDocumentProperties(
149             IndentingPrintWriter pw, GenericDocument genericDocument) {
150         Set<String> propertyNames = genericDocument.getPropertyNames();
151         pw.println("{");
152         pw.increaseIndent();
153         for (String propertyName : propertyNames) {
154             Object propertyValue = genericDocument.getProperty(propertyName);
155             pw.print("\"" + propertyName + "\"" + ": [");
156 
157             if (propertyValue instanceof GenericDocument[]) {
158                 GenericDocument[] documentValues = (GenericDocument[]) propertyValue;
159                 for (int i = 0; i < documentValues.length; i++) {
160                     GenericDocument documentValue = documentValues[i];
161                     writeGenericDocumentProperties(pw, documentValue);
162                     if (i != documentValues.length - 1) {
163                         pw.print(", ");
164                     }
165                     pw.println();
166                 }
167             } else {
168                 int propertyArrLength = Array.getLength(propertyValue);
169                 for (int i = 0; i < propertyArrLength; i++) {
170                     Object propertyElement = Array.get(propertyValue, i);
171                     if (propertyElement instanceof String) {
172                         pw.print("\"" + propertyElement + "\"");
173                     } else if (propertyElement instanceof byte[]) {
174                         pw.print(Arrays.toString((byte[]) propertyElement));
175                     } else if (propertyElement != null) {
176                         pw.print(propertyElement.toString());
177                     }
178                     if (i != propertyArrLength - 1) {
179                         pw.print(", ");
180                     }
181                 }
182             }
183             pw.println("]");
184         }
185         pw.decreaseIndent();
186         pw.println("}");
187     }
188 }
189