[stdlib]: Add an android service module

Added a detailed service bindings table. We connect the client process
making the binding to the server process getting bound using the binder
txns orchestrated through the system_server.

We also special case bindings from system_server itself.

Test: tools/diff_test_trace_processor.py out/android/trace_processor_shell --name-filter '.*service_binding.*'
Change-Id: I9180ab33ac7cefee7fa2e2559b0e7fc819354043
diff --git a/Android.bp b/Android.bp
index 9611c03..03a8547 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11968,6 +11968,7 @@
         "src/trace_processor/perfetto_sql/stdlib/android/network_packets.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/process_metadata.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/screenshots.sql",
+        "src/trace_processor/perfetto_sql/stdlib/android/services.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/slices.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/startup/startup_events.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/startup/startups.sql",
diff --git a/BUILD b/BUILD
index 70ad1f7..dde29af 100644
--- a/BUILD
+++ b/BUILD
@@ -2272,6 +2272,7 @@
         "src/trace_processor/perfetto_sql/stdlib/android/network_packets.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/process_metadata.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/screenshots.sql",
+        "src/trace_processor/perfetto_sql/stdlib/android/services.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/slices.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/statsd.sql",
         "src/trace_processor/perfetto_sql/stdlib/android/thread.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
index ffd1350..62ba544 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/android/BUILD.gn
@@ -33,6 +33,7 @@
     "network_packets.sql",
     "process_metadata.sql",
     "screenshots.sql",
+    "services.sql",
     "slices.sql",
     "statsd.sql",
     "thread.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/services.sql b/src/trace_processor/perfetto_sql/stdlib/android/services.sql
new file mode 100644
index 0000000..2a4afd3
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/services.sql
@@ -0,0 +1,155 @@
+--
+-- Copyright 2024 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
+--
+--     https://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 PERFETTO MODULE common.slices;
+INCLUDE PERFETTO MODULE android.binder;
+INCLUDE PERFETTO MODULE graphs.search;
+
+-- Details of all Service#onBind dispatched events.
+CREATE PERFETTO VIEW _bind_dispatch
+AS
+WITH
+  next_sibling AS MATERIALIZED (
+    SELECT *
+    FROM
+      graph_next_sibling!(
+          (
+            SELECT id AS node_id, parent_id AS node_parent_id, ts AS sort_key
+            FROM slice
+            WHERE dur = 0
+          )
+      )
+  ),
+  service AS (
+    SELECT
+      next_slice.id,
+      next_slice.ts,
+      next_slice.dur,
+      next_slice.name,
+      slice.utid,
+      slice.name AS bind_seq_name
+    FROM next_sibling
+    JOIN thread_slice slice
+      ON slice.id = next_sibling.node_id
+    JOIN slice next_slice
+      ON next_slice.id = next_sibling.next_node_id
+  )
+  SELECT
+  id,
+  ts,
+  dur,
+  utid,
+  CAST(STR_SPLIT(STR_SPLIT(bind_seq_name, 'bindSeq=', 1), ' ', 0) AS INT) AS bind_seq
+FROM service
+WHERE bind_seq_name GLOB 'requestServiceBinding*' AND name = 'binder transaction async';
+
+-- Details of all Service#onBind received events.
+CREATE PERFETTO VIEW _bind_receive
+AS
+SELECT
+  id,
+  ts,
+  dur,
+  track_id,
+  REPLACE(STR_SPLIT(STR_SPLIT(name, 'token=', 1), ' ', 0), 'ServiceRecord{', '') AS token,
+  STR_SPLIT(STR_SPLIT(name, 'act=', 1), ' ', 0) AS act,
+  STR_SPLIT(STR_SPLIT(name, 'cmp=', 1), ' ', 0) AS cmp,
+  STR_SPLIT(STR_SPLIT(name, 'flg=', 1), ' ', 0) AS flg,
+  CAST(STR_SPLIT(STR_SPLIT(name, 'bindSeq=', 1), '}', 0) AS INT) AS bind_seq
+FROM slice
+WHERE name GLOB 'serviceBind:*';
+
+-- All service bindings from client app to server app.
+CREATE PERFETTO TABLE android_service_bindings(
+  -- OOM score of client process making the binding.
+  client_oom_score INT,
+  -- Name of client process making the binding.
+  client_process STRING,
+  -- Name of client thread making the binding.
+  client_thread STRING,
+  -- Pid of client process making the binding.
+  client_pid INT,
+  -- Tid of client process making the binding.
+  client_tid INT,
+  -- Upid of client process making the binding.
+  client_upid INT,
+  -- Utid of client thread making the binding.
+  client_utid INT,
+  -- Timestamp the client process made the request.
+  client_ts INT,
+  -- Duration of the client binding request.
+  client_dur INT,
+  -- OOM score of server process getting bound to.
+  server_oom_score INT,
+  -- Name of server process getting bound to
+  server_process STRING,
+  -- Name of server thread getting bound to.
+  server_thread STRING,
+  -- Pid of server process getting bound to.
+  server_pid INT,
+  -- Tid of server process getting bound to.
+  server_tid INT,
+  -- Upid of server process getting bound to.
+  server_upid INT,
+  -- Utid of server process getting bound to.
+  server_utid INT,
+  -- Timestamp the server process got bound to.
+  server_ts INT,
+  -- Duration of the server process handling the binding.
+  server_dur INT,
+  -- Unique binder identifier for the Service binding.
+  token STRING,
+  -- Intent action name for the service binding.
+  act STRING,
+  -- Intent component name for the service binding.
+  cmp STRING,
+  -- Intent flag for the service binding.
+  flg STRING,
+  -- Monotonically increasing id for the service binding.
+  bind_seq INT)
+AS
+SELECT
+  COALESCE(client_binder.client_oom_score, server_binder.client_oom_score) AS client_oom_score,
+  COALESCE(client_binder.client_process, server_binder.client_process) AS client_process,
+  COALESCE(client_binder.client_thread, server_binder.client_thread) AS client_thread,
+  COALESCE(client_binder.client_pid, server_binder.client_pid) AS client_pid,
+  COALESCE(client_binder.client_tid, server_binder.client_tid) AS client_tid,
+  COALESCE(client_binder.client_upid, server_binder.client_upid) AS client_upid,
+  COALESCE(client_binder.client_utid, server_binder.client_utid) AS client_utid,
+  COALESCE(client_binder.client_ts, server_binder.client_ts) AS client_ts,
+  COALESCE(client_binder.client_dur, server_binder.client_dur) AS client_dur,
+  server_binder.server_oom_score,
+  server_binder.server_process,
+  server_binder.server_thread,
+  server_binder.server_pid,
+  server_binder.server_tid,
+  server_binder.server_upid,
+  server_binder.server_utid,
+  receive.ts AS server_ts,
+  receive.dur AS server_dur,
+  receive.token,
+  receive.act,
+  receive.cmp,
+  receive.flg,
+  receive.bind_seq
+FROM _bind_dispatch dispatch
+JOIN _bind_receive receive
+  ON dispatch.bind_seq = receive.bind_seq
+LEFT JOIN android_binder_txns server_binder
+  ON server_binder.binder_txn_id = dispatch.id
+LEFT JOIN ancestor_slice(dispatch.id) anc ON anc.depth = 0
+LEFT JOIN android_binder_txns client_binder
+  ON client_binder.server_ts = anc.ts AND dispatch.utid = client_binder.server_utid;
diff --git a/src/trace_processor/perfetto_sql/stdlib/graphs/search.sql b/src/trace_processor/perfetto_sql/stdlib/graphs/search.sql
index 05cec80..6d9bf00 100644
--- a/src/trace_processor/perfetto_sql/stdlib/graphs/search.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/graphs/search.sql
@@ -67,3 +67,35 @@
     $start_node_id
   ) dt
 );
+
+-- Computes the next sibling node in a directed graph. The next node under a parent node
+-- is determined by on the |sort_key|, which should be unique for every node under a parent.
+-- The order of the next sibling is undefined if the |sort_key| is not unique.
+--
+-- Example usage:
+--
+-- -- Compute the next sibling:
+-- SELECT *
+-- FROM graph_next_sibling!(
+--   (
+--     SELECT
+--       id AS node_id,
+--       parent_id AS node_parent_id,
+--       ts AS sort_key
+--     FROM slice
+--   )
+-- );
+-- ```
+CREATE PERFETTO MACRO graph_next_sibling(
+  -- A table/view/subquery corresponding to a directed graph for which to find the next sibling.
+  -- This table must have the columns "node_id", "node_parent_id" and "sort_key".
+  graph_table TableOrSubquery
+)
+-- The returned table has the schema (node_id UINT32, next_node_id UINT32).
+-- |node_id| is the id of the node from the input graph and |next_node_id|
+-- is the id of the node which is its next sibling.
+RETURNS TableOrSubquery AS
+(
+  SELECT node_id, lead(node_id) OVER (PARTITION BY node_parent_id ORDER BY sort_key) AS next_node_id
+    FROM $graph_table
+);
diff --git a/test/trace_processor/diff_tests/stdlib/android/tests.py b/test/trace_processor/diff_tests/stdlib/android/tests.py
index 402fdcc..63a92ca 100644
--- a/test/trace_processor/diff_tests/stdlib/android/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/android/tests.py
@@ -1160,4 +1160,47 @@
         out=Csv("""
         "pid","ts","dur"
         8361,588092720937,576298685
+        """))
+
+  def test_service_bindings(self):
+    return DiffTestBlueprint(
+        trace=DataPath('post_boot_trace.atr'),
+        query="""
+        INCLUDE PERFETTO MODULE android.services;
+        SELECT
+        client_oom_score,
+        client_process,
+        client_thread,
+        client_pid,
+        client_tid,
+        client_ts,
+        client_dur,
+        server_oom_score,
+        server_process,
+        server_thread,
+        server_tid,
+        server_pid,
+        server_ts,
+        server_dur,
+        token,
+        act,
+        cmp,
+        flg,
+        bind_seq
+        FROM android_service_bindings
+        ORDER BY client_tid, client_ts
+        LIMIT 10
+      """,
+        out=Csv("""
+        "client_oom_score","client_process","client_thread","client_pid","client_tid","client_ts","client_dur","server_oom_score","server_process","server_thread","server_tid","server_pid","server_ts","server_dur","token","act","cmp","flg","bind_seq"
+        -900,"system_server","system_server",7288,7288,577830735575,0,0,"android.ext.services","binder:7732_3",7764,7732,577866081720,9755069,"android.os.BinderProxy@a0dc800","android.service.notification.NotificationAssistantService","android.ext.services/.notification.Assistant","[NULL]",21
+        -900,"system_server","eduling.default",7288,7366,579204777498,0,0,"com.android.dialer","binder:8075_2",8097,8075,579207718770,13090141,"android.os.BinderProxy@9a28fdf","[NULL]","com.android.dialer/com.android.voicemail.impl.StatusCheckJobService","0x4",29
+        -900,"system_server","eduling.default",7288,7366,580022869386,0,0,"com.android.imsserviceentitlement","binder:8647_1",8667,8647,580027477378,1982139,"android.os.BinderProxy@27f8e83","[NULL]","com.android.imsserviceentitlement/.fcm.FcmRegistrationService","0x4",35
+        -900,"system_server","StorageManagerS",7288,7397,587754918358,0,-700,"com.android.providers.media.module","binder:8294_1",8327,8294,587757305854,2691423,"android.os.BinderProxy@73b68b5","[NULL]","com.android.providers.media.module/com.android.providers.media.fuse.ExternalStorageServiceImpl","[NULL]",44
+        -800,"com.android.systemui","ndroid.systemui",7493,7493,572995972978,8071106,-800,"com.android.systemui","binder:7493_4",7682,7493,573131280194,17181314,"android.os.BinderProxy@1c2ac60","android.service.wallpaper.WallpaperService","com.android.systemui/.wallpapers.ImageWallpaper","[NULL]",14
+        -800,"com.android.systemui","ndroid.systemui",7493,7493,572995972978,8071106,-800,"com.android.systemui","binder:7493_4",7682,7493,577000518511,6977972,"android.os.BinderProxy@b18137","[NULL]","com.android.systemui/.keyguard.KeyguardService","0x100",15
+        -800,"com.android.networkstack.process","rkstack.process",7610,7610,571078334504,7552850,-800,"com.android.networkstack.process","binder:7610_1",7633,7610,571090652307,74610898,"android.os.BinderProxy@ee1090b","android.net.INetworkStackConnector","com.android.networkstack/com.android.server.NetworkStackService","[NULL]",2
+        -800,"com.android.networkstack.process","rkstack.process",7610,7610,571078334504,7552850,-800,"com.android.networkstack.process","binder:7610_1",7633,7610,571489537275,1570460,"android.os.BinderProxy@a0dc800","android.net.ITetheringConnector","com.android.networkstack.tethering/.TetheringService","[NULL]",3
+        0,"com.android.bluetooth","droid.bluetooth",7639,7639,571248973750,9874358,-700,"com.android.bluetooth","binder:7639_2",7672,7639,571871169647,6460322,"android.os.BinderProxy@7482132","android.bluetooth.IBluetooth","com.android.bluetooth/.btservice.AdapterService","[NULL]",4
+        -700,"com.android.bluetooth","droid.bluetooth",7639,7639,572342110044,4874276,-700,"com.android.bluetooth","binder:7639_2",7672,7639,572466393291,1404185,"android.os.BinderProxy@ce5a6fc","android.media.browse.MediaBrowserService","com.android.bluetooth/.avrcpcontroller.BluetoothMediaBrowserService","[NULL]",10
       """))
diff --git a/test/trace_processor/diff_tests/stdlib/graphs/search_tests.py b/test/trace_processor/diff_tests/stdlib/graphs/search_tests.py
index 093b021..1e3b96b 100644
--- a/test/trace_processor/diff_tests/stdlib/graphs/search_tests.py
+++ b/test/trace_processor/diff_tests/stdlib/graphs/search_tests.py
@@ -131,3 +131,25 @@
           "L","D"
           "R","[NULL]"
         """))
+
+  def test_next_sibling(self):
+    return DiffTestBlueprint(
+        trace=DataPath('counters.json'),
+        query="""
+          INCLUDE PERFETTO MODULE graphs.search;
+
+          CREATE PERFETTO TABLE foo AS
+          SELECT 1 AS node_id, 0 AS node_parent_id, 1 AS sort_key
+          UNION ALL
+          SELECT 2 AS node_id, 1 AS node_parent_id, 2 AS sort_key
+          UNION ALL
+          SELECT 3 AS node_id, 1 AS node_parent_id, 1 AS sort_key;
+
+          SELECT * FROM graph_next_sibling!(foo);
+        """,
+        out=Csv("""
+        "node_id","next_node_id"
+        1,"[NULL]"
+        3,2
+        2,"[NULL]"
+        """))