本文的分析针对蓝牙模块,但具备通用性,其他模块可以参考之。
1. Overview
111
2. LOG_VERBOSE 原理
2.1 LOG_VEROBSE 实现
#pragma once
#include <cstdlib>
#ifndef LOG_TAG
#define LOG_TAG "bluetooth"
#endif
#if defined(OS_ANDROID) // 1 OS_ANDROID
#include <log/log.h>
#include "common/init_flags.h"
#ifdef FUZZ_TARGET // 2 FUZZ_TARGET
#define LOG_VERBOSE(...)
#define LOG_DEBUG(...)
#define LOG_INFO(...)
#define LOG_WARN(...)
#else // 2 FUZZ_TARGET
#define LOG_VERBOSE(fmt, args...) \
do { \
if (bluetooth::common::InitFlags::IsDebugLoggingEnabledForTag(LOG_TAG)) { \
ALOGV("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args); \
} \
} while (false)
#define LOG_DEBUG(fmt, args...) \
do { \
if (bluetooth::common::InitFlags::IsDebugLoggingEnabledForTag(LOG_TAG)) { \
ALOGD("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args); \
} \
} while (false)
#define LOG_INFO(fmt, args...) ALOGI("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
#define LOG_WARN(fmt, args...) ALOGW("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
#endif // 2 FUZZ_TARGET
#define LOG_ERROR(fmt, args...) ALOGE("%s:%d %s: " fmt, __FILE__, __LINE__, __func__, ##args)
#elif defined (ANDROID_EMULATOR) // 1 ANDROID_EMULATOR
- 由 // 2 可知:若定义了 FUZZ_TARGET ,那么 LOG_VERBOSE 会被关闭,因此这里不能定义 FUZZ_TARGET;
- 由 LOG_VERBOSE 定义可知,其由 ALOGV 实现,因此必须保证 ALOGV 已经开启,开启方式可以参考Android Log - ALOGV;
- 最后,LOG_VERBOSE 实现还依赖于 IsDebugLoggingvEnabledForTag,下面分析该函数,其参数的 LOG_TAG 是 bluetooth。
2.2 IsDebugLoggingvEnabledForTag 实现
分析 IsDebugLoggingvEnabledForTag 的调用流程,是分析其初始化的逆向,因此这里直接分析初始化流程,最终调用到 IsDebugLoggingvEnabledForTag 函数。
2.2.1 IsDebugLoggingvEnabledForTag 初始化流程
packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java
public class AdapterService extends Service {
......
@Override
public void onCreate() {
......
initNative(mUserManager.isGuestUser(), isCommonCriteriaMode(), configCompareResult,
getInitFlags(), isAtvDevice);
......
}
- initNative(...) 将 getInitFlags() 传递到 Native 层,故先分析 getInitFlags() 。
// Boolean flags
private static final String GD_CORE_FLAG = "INIT_gd_core";
private static final String GD_ADVERTISING_FLAG = "INIT_gd_advertising";
private static final String GD_SCANNING_FLAG = "INIT_gd_scanning";
private static final String GD_HCI_FLAG = "INIT_gd_hci";
private static final String GD_CONTROLLER_FLAG = "INIT_gd_controller";
private static final String GD_ACL_FLAG = "INIT_gd_acl";
private static final String GD_L2CAP_FLAG = "INIT_gd_l2cap";
private static final String GD_RUST_FLAG = "INIT_gd_rust";
private static final String GD_LINK_POLICY_FLAG = "INIT_gd_link_policy";
/**
* Logging flags logic (only applies to DEBUG and VERBOSE levels):
* if LOG_TAG in LOGGING_DEBUG_DISABLED_FOR_TAGS_FLAG:
* DO NOT LOG
* else if LOG_TAG in LOGGING_DEBUG_ENABLED_FOR_TAGS_FLAG:
* DO LOG
* else if LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG:
* DO LOG
* else:
* DO NOT LOG
*/
private static final String LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG =
"INIT_logging_debug_enabled_for_all";
// String flags
// Comma separated tags
private static final String LOGGING_DEBUG_ENABLED_FOR_TAGS_FLAG =
"INIT_logging_debug_enabled_for_tags";
private static final String LOGGING_DEBUG_DISABLED_FOR_TAGS_FLAG =
"INIT_logging_debug_disabled_for_tags";
private static final String BTAA_HCI_LOG_FLAG = "INIT_btaa_hci";
private String[] getInitFlags() {
ArrayList<String> initFlags = new ArrayList<>();
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, GD_CORE_FLAG, false)) {
initFlags.add(String.format("%s=%s", GD_CORE_FLAG, "true"));
}
......
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG, false)) {
initFlags.add(String.format("%s=%s", LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG, "true"));
}
// Enabled list 由 ContentProvider 提供
String debugLoggingEnabledTags = DeviceConfig.getString(DeviceConfig.NAMESPACE_BLUETOOTH,
LOGGING_DEBUG_ENABLED_FOR_TAGS_FLAG, "");
if (!debugLoggingEnabledTags.isEmpty()) {
initFlags.add(String.format("%s=%s", LOGGING_DEBUG_ENABLED_FOR_TAGS_FLAG,
debugLoggingEnabledTags));
}
// Disabled list 由 ContentProvider 提供
String debugLoggingDisabledTags = DeviceConfig.getString(DeviceConfig.NAMESPACE_BLUETOOTH,
LOGGING_DEBUG_DISABLED_FOR_TAGS_FLAG, "");
if (!debugLoggingDisabledTags.isEmpty()) {
initFlags.add(String.format("%s=%s", LOGGING_DEBUG_DISABLED_FOR_TAGS_FLAG,
debugLoggingDisabledTags));
}
......
return initFlags.toArray(new String[0]);
}
- getInitFlags() 主要获取一个 key-value 的键值对的集合,该键值对决定 Log 的开关与否。
- DeviceConfig.getXxx() 说明该值由 ContentProvider 决定,具体值是多少、如何设置,后文(2.2.2)分析,这里先分析初始化流程。
static bool initNative(JNIEnv* env, jobject obj, jboolean isGuest,
jboolean isCommonCriteriaMode, int configCompareResult,
jobjectArray initFlags, jboolean isAtvDevice) {
......
int flagCount = env->GetArrayLength(initFlags);
jstring* flagObjs = new jstring[flagCount];
const char** flags = nullptr;
if (flagCount > 0) {
flags = new const char*[flagCount + 1];
flags[flagCount] = nullptr;
}
for (int i = 0; i < flagCount; i++) {
flagObjs[i] = (jstring)env->GetObjectArrayElement(initFlags, i);
flags[i] = env->GetStringUTFChars(flagObjs[i], NULL);
}
int ret = sBluetoothInterface->init(
&sBluetoothCallbacks, isGuest == JNI_TRUE ? 1 : 0,
isCommonCriteriaMode == JNI_TRUE ? 1 : 0, configCompareResult, flags,
isAtvDevice == JNI_TRUE ? 1 : 0);
......
return JNI_TRUE;
}
system/bt/btif/src/bluetooth.cc
static int init(bt_callbacks_t* callbacks, bool start_restricted,
bool is_common_criteria_mode, int config_compare_result,
const char** init_flags, bool is_atv) {
......
bluetooth::common::InitFlags::Load(init_flags);
......
return BT_STATUS_SUCCESS;
}
system/bt/gd/common/init_flags.h
#pragma once
#include <stdexcept>
#include <string>
#include <unordered_map>
#include "src/init_flags.rs.h"
namespace bluetooth {
namespace common {
class InitFlags final {
public:
static void Load(const char** flags);
// 这里要求 tag 在列表中且值为 true,或 debug 全局开关已经打开
inline static bool IsDebugLoggingEnabledForTag(const std::string& tag) {
auto tag_setting = logging_debug_explicit_tag_settings.find(tag);
if (tag_setting != logging_debug_explicit_tag_settings.end()) {
return tag_setting->second;
}
return logging_debug_enabled_for_all;
}
......
private:
......
static bool logging_debug_enabled_for_all;
// save both log allow list and block list in the map to save hashing time
static std::unordered_map<std::string, bool> logging_debug_explicit_tag_settings;
};
} // namespace common
} // namespace bluetooth
- 由 IsDebugLoggingEnabledForTag(...) 函数可知,Log开关的开启有两个要求,满足一个要求即可:
- 指定的 tag 在显示指定了要开启的 LOG_TAG 列表(或);
- 不在指定列表中,但 debug 全局开关已经打开(或)。
system/bt/gd/common/init_flags.cc
namespace bluetooth {
namespace common {
// 默认定义为 false,因此默认的 LOG_VORBOSE 处于关闭状态
bool InitFlags::logging_debug_enabled_for_all = false;
std::unordered_map<std::string, bool> InitFlags::logging_debug_explicit_tag_settings = {};
void InitFlags::Load(const char** flags) {
const char** flags_copy = flags;
// 重置
SetAll(false);
// 对所有的 key-value 字符串进行循环处理。
while (flags != nullptr && *flags != nullptr) {
// 以 = 切割字符串
std::string flag_element = *flags;
auto flag_pair = StringSplit(flag_element, "=", 2);
if (flag_pair.size() != 2) {
flags++;
continue;
}
// 在列表中找到全局 key,并对其赋值
ParseBoolFlag(flag_pair, "INIT_logging_debug_enabled_for_all", &logging_debug_enabled_for_all);
// 在列表中找到 enabled key,将 tag 和 true 添加到集合中;
if ("INIT_logging_debug_enabled_for_tags" == flag_pair[0]) {
auto tags = StringSplit(flag_pair[1], ",");
for (const auto& tag : tags) {
auto setting = logging_debug_explicit_tag_settings.find(tag);
if (setting == logging_debug_explicit_tag_settings.end()) {
logging_debug_explicit_tag_settings.insert_or_assign(tag, true);
}
}
}
// 在列表中找到 disabled key,将 tag 和 false 添加到集合中;
if ("INIT_logging_debug_disabled_for_tags" == flag_pair[0]) {
auto tags = StringSplit(flag_pair[1], ",");
for (const auto& tag : tags) {
logging_debug_explicit_tag_settings.insert_or_assign(tag, false);
}
}
flags++;
}
......
}
void InitFlags::SetAll(bool value) {
logging_debug_enabled_for_all = value;
logging_debug_explicit_tag_settings.clear();
}
- 由以上代码分析可知:logging_debug_enabled_for_all 、logging_debug_explicit_tag_settings 由 AdapterService.java 中传入的 key-value 字符串决定,key-value 值的问题下文详述。
2.2.2 DeviceConfig.getString(...)
frameworks/base/core/java/android/provider/DeviceConfig.java
@SystemApi
@RequiresPermission(READ_DEVICE_CONFIG)
public static String getString(@NonNull String namespace, @NonNull String name,
@Nullable String defaultValue) {
String value = getProperty(namespace, name);
return value != null ? value : defaultValue;
}
@SystemApi
@RequiresPermission(READ_DEVICE_CONFIG)
public static String getProperty(@NonNull String namespace, @NonNull String name) {
return getProperties(namespace, name).getString(name, null);
}
这里分成了两个函数 DeviceConfig.getProperties(...) 和 DeviceConfig.Properties.getString(...),单独分析这两条支线。
2.2.2.1 DeviceConfig.getProperties(...)
@SystemApi
@NonNull
@RequiresPermission(READ_DEVICE_CONFIG)
public static Properties getProperties(@NonNull String namespace, @NonNull String ... names) {
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
return new Properties(namespace,
Settings.Config.getStrings(contentResolver, namespace, Arrays.asList(names)));
}
- 这里的 new Properties(...) 与后部分的 getString(...) 关系更紧密,后续再分析。
public final class Settings {
public static final class Config extends NameValueTable {
public static Map<String, String> getStrings(@NonNull ContentResolver resolver,
@NonNull String namespace, @NonNull List<String> names) {
List<String> compositeNames = new ArrayList<>(names.size());
for (String name : names) {
compositeNames.add(createCompositeName(namespace, name));
}
String prefix = createPrefix(namespace);
ArrayMap<String, String> rawKeyValues = sNameValueCache.getStringsForPrefix(
resolver, prefix, compositeNames);
int size = rawKeyValues.size();
int substringLength = prefix.length();
ArrayMap<String, String> keyValues = new ArrayMap<>(size);
for (int i = 0; i < size; ++i) {
keyValues.put(rawKeyValues.keyAt(i).substring(substringLength),
rawKeyValues.valueAt(i));
}
return keyValues;
}
}
}
- 首先, Config 这个类在 Android 9 的最后一个版本 中不存在,出现在 Android 10 的第一个版本 中;
- 其次,Settings 类中的其他子类 Global、Secure、System 等都可以通过命令读取其中的 key-value,例如: adb shell settings get system <key_name>,那么对于Android 10及以后的设备可以通过类似命令读取 adb shell settings get global <key_name>。(在 Android 11 上测试不行,这里应该有问题)
2.2.2.2 DeviceConfig.Properties.getString(...)
现在回过头来分析
frameworks/base/core/java/android/provider/DeviceConfig.java
public final class DeviceConfig {
@SystemApi
public static class Properties {
private final HashMap<String, String> mMap;
*/
public Properties(@NonNull String namespace, @Nullable Map<String, String> keyValueMap) {
......
mMap = new HashMap();
if (keyValueMap != null) {
mMap.putAll(keyValueMap);
}
}
@Nullable
public String getString(@NonNull String name, @Nullable String defaultValue) {
Preconditions.checkNotNull(name);
String value = mMap.get(name);
return value != null ? value : defaultValue;
}
}
}
读取的值全部存放在构造函数的参数中,传递给了 mMap 参数,因此 getString(...) 函数为指定的 key 获取值。
网友评论