美文网首页
Android Log - LOG_VERBOSE

Android Log - LOG_VERBOSE

作者: MelanDawn | 来源:发表于2021-09-12 21:26 被阅读0次

    本文的分析针对蓝牙模块,但具备通用性,其他模块可以参考之。

    1. Overview

    111

    2. LOG_VERBOSE 原理

    2.1 LOG_VEROBSE 实现

    system/bt/gd/os/log.h

    #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开关的开启有两个要求,满足一个要求即可:
      1. 指定的 tag 在显示指定了要开启的 LOG_TAG 列表(或);
      2. 不在指定列表中,但 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_alllogging_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 获取值。

    相关文章

      网友评论

          本文标题:Android Log - LOG_VERBOSE

          本文链接:https://www.haomeiwen.com/subject/qhpawltx.html