1 /*
2 * Copyright (C) 2020 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 #include "src/trace_processor/util/profiler_util.h"
18 #include <optional>
19
20 #include "perfetto/ext/base/string_utils.h"
21 #include "src/trace_processor/storage/trace_storage.h"
22
23 namespace perfetto {
24 namespace trace_processor {
25 namespace {
26
27 // Try to extract the package name from a path like:
28 // * /data/app/[packageName]-[randomString]/base.apk
29 // * /data/app/~~[randomStringA]/[packageName]-[randomStringB]/base.apk
30 // The latter is newer (R+), and was added to avoid leaking package names via
31 // mountinfo of incremental apk mounts.
PackageFromApp(base::StringView location)32 std::optional<base::StringView> PackageFromApp(base::StringView location) {
33 location = location.substr(base::StringView("/data/app/").size());
34 size_t start = 0;
35 if (location.at(0) == '~') {
36 size_t slash = location.find('/');
37 if (slash == base::StringView::npos) {
38 return std::nullopt;
39 }
40 start = slash + 1;
41 }
42 size_t end = location.find('/', start + 1);
43 if (end == base::StringView::npos) {
44 return std::nullopt;
45 }
46 location = location.substr(start, end);
47 size_t minus = location.find('-');
48 if (minus == base::StringView::npos) {
49 return std::nullopt;
50 }
51 return location.substr(0, minus);
52 }
53
54 } // namespace
55
PackageFromLocation(TraceStorage * storage,base::StringView location)56 std::optional<std::string> PackageFromLocation(TraceStorage* storage,
57 base::StringView location) {
58 // List of some hardcoded apps that do not follow the scheme used in
59 // PackageFromApp. Ask for yours to be added.
60 //
61 // TODO(b/153632336): Get rid of the hardcoded list of system apps.
62 base::StringView sysui(
63 "/system_ext/priv-app/SystemUIGoogle/SystemUIGoogle.apk");
64 if (location.size() >= sysui.size() &&
65 location.substr(0, sysui.size()) == sysui) {
66 return "com.android.systemui";
67 }
68
69 base::StringView phonesky("/product/priv-app/Phonesky/Phonesky.apk");
70 if (location.size() >= phonesky.size() &&
71 location.substr(0, phonesky.size()) == phonesky) {
72 return "com.android.vending";
73 }
74
75 base::StringView maps("/product/app/Maps/Maps.apk");
76 if (location.size() >= maps.size() &&
77 location.substr(0, maps.size()) == maps) {
78 return "com.google.android.apps.maps";
79 }
80
81 base::StringView launcher(
82 "/system_ext/priv-app/NexusLauncherRelease/NexusLauncherRelease.apk");
83 if (location.size() >= launcher.size() &&
84 location.substr(0, launcher.size()) == launcher) {
85 return "com.google.android.apps.nexuslauncher";
86 }
87
88 base::StringView photos("/product/app/Photos/Photos.apk");
89 if (location.size() >= photos.size() &&
90 location.substr(0, photos.size()) == photos) {
91 return "com.google.android.apps.photos";
92 }
93
94 base::StringView wellbeing(
95 "/product/priv-app/WellbeingPrebuilt/WellbeingPrebuilt.apk");
96 if (location.size() >= wellbeing.size() &&
97 location.substr(0, wellbeing.size()) == wellbeing) {
98 return "com.google.android.apps.wellbeing";
99 }
100
101 if (location.find("DevicePersonalizationPrebuilt") !=
102 base::StringView::npos ||
103 location.find("MatchMaker") != base::StringView::npos) {
104 return "com.google.android.as";
105 }
106
107 if (location.find("DeviceIntelligenceNetworkPrebuilt") !=
108 base::StringView::npos) {
109 return "com.google.android.as.oss";
110 }
111
112 if (location.find("SettingsIntelligenceGooglePrebuilt") !=
113 base::StringView::npos) {
114 return "com.google.android.settings.intelligence";
115 }
116
117 base::StringView gm("/product/app/PrebuiltGmail/PrebuiltGmail.apk");
118 if (location.size() >= gm.size() && location.substr(0, gm.size()) == gm) {
119 return "com.google.android.gm";
120 }
121
122 if (location.find("PrebuiltGmsCore") != base::StringView::npos ||
123 location.find("com.google.android.gms") != base::StringView::npos) {
124 return "com.google.android.gms";
125 }
126
127 base::StringView velvet("/product/priv-app/Velvet/Velvet.apk");
128 if (location.size() >= velvet.size() &&
129 location.substr(0, velvet.size()) == velvet) {
130 return "com.google.android.googlequicksearchbox";
131 }
132
133 base::StringView inputmethod(
134 "/product/app/LatinIMEGooglePrebuilt/LatinIMEGooglePrebuilt.apk");
135 if (location.size() >= inputmethod.size() &&
136 location.substr(0, inputmethod.size()) == inputmethod) {
137 return "com.google.android.inputmethod.latin";
138 }
139
140 base::StringView messaging("/product/app/PrebuiltBugle/PrebuiltBugle.apk");
141 if (location.size() >= messaging.size() &&
142 location.substr(0, messaging.size()) == messaging) {
143 return "com.google.android.apps.messaging";
144 }
145
146 // Deal with paths to /data/app/...
147
148 auto extract_package =
149 [storage](base::StringView path) -> std::optional<std::string> {
150 auto package = PackageFromApp(path);
151 if (!package) {
152 PERFETTO_DLOG("Failed to parse %s", path.ToStdString().c_str());
153 storage->IncrementStats(stats::deobfuscate_location_parse_error);
154 return std::nullopt;
155 }
156 return package->ToStdString();
157 };
158
159 base::StringView data_app("/data/app/");
160 size_t data_app_sz = data_app.size();
161 if (location.substr(0, data_app.size()) == data_app) {
162 return extract_package(location);
163 }
164
165 // Check for in-memory decompressed dexfile, example prefixes:
166 // * "[anon:dalvik-classes.dex extracted in memory from"
167 // * "/dev/ashmem/dalvik-classes.dex extracted in memory from"
168 // The latter form is for older devices (Android P and before).
169 // We cannot hardcode the filename since it could be for example
170 // "classes2.dex" for multidex apks.
171 base::StringView inmem_dex("dex extracted in memory from /data/app/");
172 size_t match_pos = location.find(inmem_dex);
173 if (match_pos != base::StringView::npos) {
174 auto data_app_path =
175 location.substr(match_pos + inmem_dex.size() - data_app_sz);
176 return extract_package(data_app_path);
177 }
178
179 return std::nullopt;
180 }
181
FullyQualifiedDeobfuscatedName(protos::pbzero::ObfuscatedClass::Decoder & cls,protos::pbzero::ObfuscatedMember::Decoder & member)182 std::string FullyQualifiedDeobfuscatedName(
183 protos::pbzero::ObfuscatedClass::Decoder& cls,
184 protos::pbzero::ObfuscatedMember::Decoder& member) {
185 std::string member_deobfuscated_name =
186 member.deobfuscated_name().ToStdString();
187 if (base::Contains(member_deobfuscated_name, '.')) {
188 // Fully qualified name.
189 return member_deobfuscated_name;
190 } else {
191 // Name relative to class.
192 return cls.deobfuscated_name().ToStdString() + "." +
193 member_deobfuscated_name;
194 }
195 }
196
197 } // namespace trace_processor
198 } // namespace perfetto
199