• 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.xts.apimapper.helper;
18 
19 import android.util.Log;
20 
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.TreeMap;
26 
27 /**
28  * A class to record and dump Android API method calls.
29  */
30 public final class DeviceMethodCallStats {
31 
DeviceMethodCallStats()32     public DeviceMethodCallStats() {}
33 
34     // A map to record the data of called class -> called method -> called method description
35     // -> called times.
36     private final Map<String, Map<String, Map<String, Integer>>> mStats = new TreeMap<>();
37 
38     /** Clear all records. */
clear()39     public void clear() {
40         mStats.clear();
41     }
42 
43     /** Record the given method call. */
onMethodCalled(String className, String methodName, String methodDesc)44     public void onMethodCalled(String className, String methodName, String methodDesc) {
45         var classStats = mStats.computeIfAbsent(className, k -> new TreeMap<>());
46         var methodStats = classStats.computeIfAbsent(methodName, k -> new TreeMap<>());
47         int count = methodStats.getOrDefault(methodDesc, 0);
48         methodStats.put(methodDesc, count + 1);
49     }
50 
51     /**
52      * Dump all API calls to the output stream. Each API call is formatted as a csv record
53      * containing a prefix, a test class name, a test method name, a called class, a called method,
54      * a called method description, and a called times.
55      */
dump(String tag, String prefix, String testClassName, String testMethodName)56     public void dump(String tag, String prefix, String testClassName, String testMethodName) {
57         String logPrefix = String.format(
58                 "%s %s:%s",
59                 prefix,
60                 testClassName,
61                 removeMethodParameters(testMethodName));
62         List<String> classes = new ArrayList<>(mStats.keySet());
63         Collections.sort(classes);
64 
65         for (String className : classes) {
66             Map<String, Map<String, Integer>> methodStats = mStats.get(className);
67             List<String> methodNames = new ArrayList<>(methodStats.keySet());
68 
69             for (String methodName : methodNames) {
70                 Map<String, Integer> methodDescStats = methodStats.get(methodName);
71                 List<String> methodDescs = new ArrayList<>(methodDescStats.keySet());
72 
73                 for (String methodDesc : methodDescs) {
74                     String logMessage = String.format(
75                             "%s:%s:%s:%s:%s",
76                             logPrefix,
77                             getPackageClass(className),
78                             methodName,
79                             methodDesc,
80                             methodDescStats.get(methodDesc)
81 
82                     );
83                     Log.i(tag, logMessage);
84                 }
85             }
86         }
87     }
88 
89     /**
90      * Split package name and class name from the given string.
91      *
92      * @return a csv style string with the format "{packageName}:{className}"
93      */
getPackageClass(String packageClass)94     public static String getPackageClass(String packageClass) {
95         int lastDot = packageClass.lastIndexOf('.');
96         if (lastDot < 0) {
97             return ":" + packageClass;
98         } else {
99             return packageClass.substring(0, lastDot) + ":"
100                     + packageClass.substring(lastDot + 1);
101         }
102     }
103 
removeMethodParameters(String methodName)104     private static String removeMethodParameters(String methodName) {
105         int pos = methodName.indexOf('[');
106         if (pos < 0) {
107             return methodName;
108         }
109         return methodName.substring(0, pos);
110     }
111 }
112