blob: 79a5448f5c07b54aff429034de5e52b6a4464adf [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/trace_processor/importers/proto/profiler_util.h"
#include <optional>
#include "perfetto/ext/base/string_utils.h"
#include "src/trace_processor/storage/trace_storage.h"
namespace perfetto {
namespace trace_processor {
namespace {
// Try to extract the package name from a path like:
// * /data/app/[packageName]-[randomString]/base.apk
// * /data/app/~~[randomStringA]/[packageName]-[randomStringB]/base.apk
// The latter is newer (R+), and was added to avoid leaking package names via
// mountinfo of incremental apk mounts.
std::optional<base::StringView> PackageFromApp(base::StringView location) {
location = location.substr(base::StringView("/data/app/").size());
size_t start = 0;
if (location.at(0) == '~') {
size_t slash = location.find('/');
if (slash == base::StringView::npos) {
return std::nullopt;
}
start = slash + 1;
}
size_t end = location.find('/', start + 1);
if (end == base::StringView::npos) {
return std::nullopt;
}
location = location.substr(start, end);
size_t minus = location.find('-');
if (minus == base::StringView::npos) {
return std::nullopt;
}
return location.substr(0, minus);
}
} // namespace
std::optional<std::string> PackageFromLocation(TraceStorage* storage,
base::StringView location) {
// List of some hardcoded apps that do not follow the scheme used in
// PackageFromApp. Ask for yours to be added.
//
// TODO(b/153632336): Get rid of the hardcoded list of system apps.
base::StringView sysui(
"/system_ext/priv-app/SystemUIGoogle/SystemUIGoogle.apk");
if (location.size() >= sysui.size() &&
location.substr(0, sysui.size()) == sysui) {
return "com.android.systemui";
}
base::StringView phonesky("/product/priv-app/Phonesky/Phonesky.apk");
if (location.size() >= phonesky.size() &&
location.substr(0, phonesky.size()) == phonesky) {
return "com.android.vending";
}
base::StringView maps("/product/app/Maps/Maps.apk");
if (location.size() >= maps.size() &&
location.substr(0, maps.size()) == maps) {
return "com.google.android.apps.maps";
}
base::StringView launcher(
"/system_ext/priv-app/NexusLauncherRelease/NexusLauncherRelease.apk");
if (location.size() >= launcher.size() &&
location.substr(0, launcher.size()) == launcher) {
return "com.google.android.apps.nexuslauncher";
}
base::StringView photos("/product/app/Photos/Photos.apk");
if (location.size() >= photos.size() &&
location.substr(0, photos.size()) == photos) {
return "com.google.android.apps.photos";
}
base::StringView wellbeing(
"/product/priv-app/WellbeingPrebuilt/WellbeingPrebuilt.apk");
if (location.size() >= wellbeing.size() &&
location.substr(0, wellbeing.size()) == wellbeing) {
return "com.google.android.apps.wellbeing";
}
if (location.find("DevicePersonalizationPrebuilt") !=
base::StringView::npos ||
location.find("MatchMaker") != base::StringView::npos) {
return "com.google.android.as";
}
if (location.find("DeviceIntelligenceNetworkPrebuilt") !=
base::StringView::npos) {
return "com.google.android.as.oss";
}
if (location.find("SettingsIntelligenceGooglePrebuilt") !=
base::StringView::npos) {
return "com.google.android.settings.intelligence";
}
base::StringView gm("/product/app/PrebuiltGmail/PrebuiltGmail.apk");
if (location.size() >= gm.size() && location.substr(0, gm.size()) == gm) {
return "com.google.android.gm";
}
if (location.find("PrebuiltGmsCore") != base::StringView::npos ||
location.find("com.google.android.gms") != base::StringView::npos) {
return "com.google.android.gms";
}
base::StringView velvet("/product/priv-app/Velvet/Velvet.apk");
if (location.size() >= velvet.size() &&
location.substr(0, velvet.size()) == velvet) {
return "com.google.android.googlequicksearchbox";
}
base::StringView inputmethod(
"/product/app/LatinIMEGooglePrebuilt/LatinIMEGooglePrebuilt.apk");
if (location.size() >= inputmethod.size() &&
location.substr(0, inputmethod.size()) == inputmethod) {
return "com.google.android.inputmethod.latin";
}
base::StringView messaging("/product/app/PrebuiltBugle/PrebuiltBugle.apk");
if (location.size() >= messaging.size() &&
location.substr(0, messaging.size()) == messaging) {
return "com.google.android.apps.messaging";
}
// Deal with paths to /data/app/...
auto extract_package =
[storage](base::StringView path) -> std::optional<std::string> {
auto package = PackageFromApp(path);
if (!package) {
PERFETTO_DLOG("Failed to parse %s", path.ToStdString().c_str());
storage->IncrementStats(stats::deobfuscate_location_parse_error);
return std::nullopt;
}
return package->ToStdString();
};
base::StringView data_app("/data/app/");
size_t data_app_sz = data_app.size();
if (location.substr(0, data_app.size()) == data_app) {
return extract_package(location);
}
// Check for in-memory decompressed dexfile, example prefixes:
// * "[anon:dalvik-classes.dex extracted in memory from"
// * "/dev/ashmem/dalvik-classes.dex extracted in memory from"
// The latter form is for older devices (Android P and before).
// We cannot hardcode the filename since it could be for example
// "classes2.dex" for multidex apks.
base::StringView inmem_dex("dex extracted in memory from /data/app/");
size_t match_pos = location.find(inmem_dex);
if (match_pos != base::StringView::npos) {
auto data_app_path =
location.substr(match_pos + inmem_dex.size() - data_app_sz);
return extract_package(data_app_path);
}
return std::nullopt;
}
std::string FullyQualifiedDeobfuscatedName(
protos::pbzero::ObfuscatedClass::Decoder& cls,
protos::pbzero::ObfuscatedMember::Decoder& member) {
std::string member_deobfuscated_name =
member.deobfuscated_name().ToStdString();
if (base::Contains(member_deobfuscated_name, '.')) {
// Fully qualified name.
return member_deobfuscated_name;
} else {
// Name relative to class.
return cls.deobfuscated_name().ToStdString() + "." +
member_deobfuscated_name;
}
}
} // namespace trace_processor
} // namespace perfetto