Add libperfetto_android_internal.so library to access HAL/VNDK

This CL introduces a new shared library, meant to be used
only in in-android-tree builds, to access Android internal
HAL services via hwbinder (and in future maybe other sw
binder interfaces).
The concrete use case is battery tracing (see CLs based
op top of this).
The reasons for splitting off a dedicated .so are twofold:
1) Keep non-NDK dependencies isolated in a standalone target
   and avoid polluting the codebase with #ifdefs.
2) Avoid the memory impact of pulling the various VNDK .so(s)
   unless necessary.

Using hwbinder requires pulling 6 (or more) .so(s), which adds
150 KB of private dirty memory at load time due to linker
relocations.
The plan is to dlopen() libperfetto_android_internal.so
only when required and unload it when unneeded.

Bug: 113076327
Change-Id: I55b76d3c6637fb3604afff6b27870418e4831aa1
diff --git a/Android.bp b/Android.bp
index 147384b..3bf34dd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -302,6 +302,29 @@
   ],
 }
 
+// GN target: //:libperfetto_android_internal
+cc_library_shared {
+  name: "libperfetto_android_internal",
+  srcs: [
+    "src/android_internal/health_hal.cc",
+  ],
+  shared_libs: [
+    "android.hardware.health@2.0",
+    "libbase",
+    "libhidlbase",
+    "libhidltransport",
+    "libhwbinder",
+    "liblog",
+    "libutils",
+  ],
+  defaults: [
+    "perfetto_defaults",
+  ],
+  static_libs: [
+    "libhealthhalutils",
+  ],
+}
+
 // GN target: //:perfetto
 cc_binary {
   name: "perfetto",
@@ -2573,6 +2596,9 @@
   defaults: [
     "perfetto_defaults",
   ],
+  required: [
+    "libperfetto_android_internal",
+  ],
 }
 
 // These targets are appended to the autogenerated Android.bp by tools/gen_android_bp.
diff --git a/BUILD.gn b/BUILD.gn
index 307f3aa..773c50b 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -238,7 +238,14 @@
         "protos/perfetto/trace:lite",
       ]
     }
-  }
+
+    shared_library("libperfetto_android_internal") {
+      deps = [
+        "gn:default_deps",
+        "src/android_internal",
+      ]
+    }
+  }  # if (perfetto_build_with_android)
 }  # if (perfetto_build_standalone || perfetto_build_with_android)
 
 if (perfetto_build_with_embedder) {
diff --git a/src/android_internal/BUILD.gn b/src/android_internal/BUILD.gn
new file mode 100644
index 0000000..0930238
--- /dev/null
+++ b/src/android_internal/BUILD.gn
@@ -0,0 +1,56 @@
+# Copyright (C) 2018 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.
+
+import("../../gn/perfetto.gni")
+
+source_set("headers") {
+  deps = [
+    "../../gn:default_deps",
+  ]
+  sources = [
+    "health_hal.h",
+  ]
+}
+
+# This target proxies calls to Android internal libraries that are not part of
+# the NDK. See README.md.
+source_set("android_internal") {
+  visibility = [ "//:libperfetto_android_internal" ]
+  deps = [
+    ":headers",
+    "../../gn:default_deps",
+  ]
+  if (perfetto_build_with_android) {
+    sources = [
+      "health_hal.cc",
+    ]
+    libs = [
+      "android.hardware.health@2.0",
+      "base",
+      "log",
+      "hwbinder",
+      "hidlbase",
+      "hidltransport",
+      "utils",
+    ]
+  }
+
+  # This target should never depend on any other perfetto target to avoid ODR
+  # violation by doubly linking code in two .so(s) loaded in the same exe.
+  assert_no_deps = [
+    "//src/base/*",
+    "//src/tracing/*",
+    "//include/*",
+  ]
+}
diff --git a/src/android_internal/README.md b/src/android_internal/README.md
new file mode 100644
index 0000000..4cd2102
--- /dev/null
+++ b/src/android_internal/README.md
@@ -0,0 +1,35 @@
+This directory contains code that accesses Android (hw)binder interfaces
+and is dynamically loaded and used by traced_probes.
+The code in this directory is built as a separate .so library and can depend on
+on Android internals.
+
+Block diagram:
+
+```
++---------------+       +---------------------------------+
+| traced_probes |- - -> | libperfetto_android_internal.so |
++---------------+  ^    +---------------+-----------------+
+                   |                    |
+                   |                    | [  Non-NDK libraries ]
+                   |                    +-> libbase.so
+                   |                    +-> libutils.so
+                   |                    +-> libhidltransport.so
+                   |                    +-> libhwbinder.so
+                   |                    +-> android.hardware.xxx@2.0
+                   |
+                   + dynamically loaded on first use via dlopen()
+```
+
+The major reason for using a separate .so() and introducing the shared library
+layer is avoiding the cost of linker relocations (~150 KB private dirty)
+required for loading the graph of binder-related libraries.
+
+The general structure and rules for code in this directory is as-follows:
+- Targets herein defined must be leaf targets. Dependencies to perfetto targets
+  (e.g. base) are not allowed, as doing that would create ODR violations.
+- Headers (e.g. health_hal.h) must have a plain old C interface (to avoid
+  dealing with name mangling) and should not expose neither android internal
+  structure/types nor struct/types defined in perfetto headers outside of this
+  directory.
+- Dependencies to Android internal headers are allowed only in .cc files, not
+  in headers.
diff --git a/src/android_internal/health_hal.cc b/src/android_internal/health_hal.cc
new file mode 100644
index 0000000..0d8ad05
--- /dev/null
+++ b/src/android_internal/health_hal.cc
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 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/android_internal/health_hal.h"
+
+#include <android/hardware/health/2.0/IHealth.h>
+#include <healthhalutils/HealthHalUtils.h>
+
+namespace perfetto {
+namespace android_internal {
+
+using ::android::hardware::health::V2_0::IHealth;
+using ::android::hardware::health::V2_0::Result;
+
+namespace {
+
+android::sp<IHealth> g_svc;
+
+void ResetService() {
+  g_svc = ::android::hardware::health::V2_0::get_health_service();
+}
+
+}  // namespace
+
+bool GetBatteryCounter(BatteryCounter counter, int64_t* value) {
+  *value = 0;
+  if (!g_svc)
+    ResetService();
+
+  if (!g_svc)
+    return false;
+
+  // The Android VNDK documentation states that for blocking services, the
+  // caller blocks until the reply is received and the callback is called inline
+  // in the same thread.
+  // See https://source.android.com/devices/architecture/hidl/threading .
+
+  Result res;
+  switch (counter) {
+    case BatteryCounter::kUnspecified:
+      res = Result::NOT_FOUND;
+      break;
+
+    case BatteryCounter::kCharge:
+      g_svc->getChargeCounter([&res, value](Result hal_res, int32_t hal_value) {
+        res = hal_res;
+        *value = hal_value;
+      });
+      break;
+
+    case BatteryCounter::kCapacityPercent:
+      g_svc->getCapacity([&res, value](Result hal_res, int64_t hal_value) {
+        res = hal_res;
+        *value = hal_value;
+      });
+      break;
+
+    case BatteryCounter::kCurrent:
+      g_svc->getCurrentNow([&res, value](Result hal_res, int32_t hal_value) {
+        res = hal_res;
+        *value = hal_value;
+      });
+      break;
+
+    case BatteryCounter::kCurrentAvg:
+      g_svc->getCurrentAverage(
+          [&res, value](Result hal_res, int32_t hal_value) {
+            res = hal_res;
+            *value = hal_value;
+          });
+      break;
+  }  // switch(counter)
+
+  if (res == Result::CALLBACK_DIED)
+    g_svc.clear();
+
+  return res == Result::SUCCESS;
+}
+
+}  // namespace android_internal
+}  // namespace perfetto
diff --git a/src/android_internal/health_hal.h b/src/android_internal/health_hal.h
new file mode 100644
index 0000000..5f489af
--- /dev/null
+++ b/src/android_internal/health_hal.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef SRC_ANDROID_INTERNAL_HEALTH_HAL_H_
+#define SRC_ANDROID_INTERNAL_HEALTH_HAL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+// This header declares proxy functions defined in
+// libperfetto_android_internal.so that allow traced_probes to access internal
+// android functions (e.g., hwbinder).
+// Do not add any include to either perfetto headers or android headers. See
+// README.md for more.
+
+namespace perfetto {
+namespace android_internal {
+
+enum class BatteryCounter {
+  kUnspecified = 0,
+  kCharge,
+  kCapacityPercent,
+  kCurrent,
+  kCurrentAvg,
+};
+
+extern "C" {
+
+// Thse functions are not thread safe unless specified otherwise.
+
+bool __attribute__((visibility("default")))
+GetBatteryCounter(BatteryCounter, int64_t*);
+
+}  // extern "C"
+
+}  // namespace android_internal
+}  // namespace perfetto
+
+#endif  // SRC_ANDROID_INTERNAL_HEALTH_HAL_H_
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index f0762d6..dd8b467 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -37,6 +37,7 @@
 # Default targets to translate to the blueprint file.
 default_targets = [
     '//:libperfetto',
+    '//:libperfetto_android_internal',
     '//:perfetto_integrationtests',
     '//:perfetto_trace_protos',
     '//:perfetto_unittests',
@@ -71,8 +72,13 @@
 
 # Shared libraries which are directly translated to Android system equivalents.
 library_whitelist = [
+    'android.hardware.health@2.0',
     'android',
+    'base',
     'binder',
+    'hidlbase',
+    'hidltransport',
+    'hwbinder',
     'log',
     'services',
     'utils',
@@ -99,8 +105,14 @@
 
 # Additional arguments to apply to Android.bp rules.
 additional_args = {
-    "heapprofd_client": [
-        ("include_dirs", ["bionic/libc"]),
+    'heapprofd_client': [
+        ('include_dirs', ['bionic/libc']),
+    ],
+    'traced_probes': [
+      ('required', ['libperfetto_android_internal']),
+    ],
+    'libperfetto_android_internal': [
+      ('static_libs', ['libhealthhalutils']),
     ],
 }
 
@@ -335,7 +347,9 @@
     # equivalents.
     target = desc[dep_name]
     for lib in target.get('libs', []):
-        android_lib = 'lib' + lib
+        # Generally library names sould be mangled as 'libXXX', unless they are
+        # HAL libraries (e.g., android.hardware.health@2.0).
+        android_lib = lib if '@' in lib else 'lib' + lib
         if lib in library_whitelist and not android_lib in module.shared_libs:
             module.shared_libs.append(android_lib)
 
diff --git a/tools/tmux b/tools/tmux
index ff12b42..e3d7a82 100755
--- a/tools/tmux
+++ b/tools/tmux
@@ -30,10 +30,15 @@
   return $?
 }
 
+function is_mac {
+  ! test -d /proc
+  return $?
+}
+
 function reset_tracing {
   if is_android $OUT; then
     adb shell 'echo 0 > /d/tracing/tracing_on'
-  else
+  elif ! is_mac; then
     if [ ! -w /sys/kernel/debug ]; then
       echo "debugfs not accessible, try sudo chown -R $USER /sys/kernel/debug"
       sudo chown -R $USER /sys/kernel/debug
@@ -93,6 +98,8 @@
 
 if is_android $OUT ; then
   DIR=/data/local/tmp
+elif is_mac; then
+  DIR=$(mktemp -d $TMPDIR/perfetto.XXXXXX)
 else
   DIR=$(mktemp -p $TMPDIR -d perfetto.XXXXXX)
 fi
@@ -113,6 +120,9 @@
 if ! is_monolithic $OUT; then
   PREFIX="$PREFIX LD_LIBRARY_PATH=$DIR"
   push $OUT/libperfetto.so
+  if is_android $OUT; then
+    push $OUT/libperfetto_android_internal.so
+  fi
 fi
 
 CONFIG_DEVICE_PATH=$CONFIG