--
-- Copyright 2019 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 android.user_list;

-- Count packages by package UID.
CREATE PERFETTO TABLE _uid_package_count AS
SELECT uid, count(1) AS cnt FROM package_list GROUP BY 1;

-- Account for android-specific functionality (sdk sandbox, pcc) that is not encoded in
-- the canonical app_id concept but is still required to understand the package
-- see system/core/libcutils/include/private/android_filesystem_config.h
CREATE PERFETTO FUNCTION _app_id_to_base_package(app_id LONG)
RETURNS LONG
AS
SELECT
  CASE
    -- regular app_id [10000, 20000)
    WHEN $app_id < 20000 THEN $app_id
    -- sdk sandbox [20000, 30000)
    WHEN $app_id < 30000 THEN $app_id - 10000
    -- pcc [30000, 40000)
    WHEN $app_id < 40000 THEN $app_id - 20000
    ELSE $app_id
  END;

CREATE PERFETTO FUNCTION _android_package_for_process(
  uid LONG,
  uid_count LONG,
  process_name STRING
)
RETURNS TABLE(package_name STRING, version_code LONG, debuggable BOOL)
AS
WITH
  min_distance AS (
    SELECT
      -- SQLite allows omitting the group-by for the MIN: the other columns
      -- will match the row with the minimum value.
      min(length($process_name) - length(package_name)),
      package_name,
      version_code,
      debuggable
    FROM package_list
    WHERE
      (($uid = uid
      AND (
      -- Either a unique match or process name is a prefix the package name
      $uid_count = 1
      OR $process_name GLOB package_name || '*'))
      OR (
      -- isolated processes can only be matched based on the name
      $uid >= 90000
      AND $uid < 100000
      AND str_split($process_name, ':', 0) GLOB package_name || '*'))
  )
SELECT package_name, version_code, debuggable FROM min_distance;

-- Table containing the upids of all kernel tasks.
CREATE PERFETTO TABLE _android_kernel_tasks(
  -- upid of the kernel task
  upid LONG
)
AS
SELECT a.upid
FROM process AS a
LEFT JOIN process AS b
  ON a.parent_upid = b.upid
WHERE
  b.name = 'kthreadd'
  OR a.pid = 0
  OR b.pid = 0
ORDER BY
  1;

-- Returns true if the process is a kernel task.
CREATE PERFETTO FUNCTION android_is_kernel_task(
  -- Queried process
  upid LONG
)
-- True for kernel tasks
RETURNS BOOL
AS
SELECT EXISTS (SELECT TRUE FROM _android_kernel_tasks WHERE upid = $upid);

-- Data about packages running on the process.
CREATE PERFETTO TABLE android_process_metadata(
  -- Process upid.
  upid JOINID(process.id),
  -- Process pid.
  pid LONG,
  -- Process name.
  process_name STRING,
  -- Android app UID.
  uid LONG,
  -- Whether the UID is shared by multiple packages.
  shared_uid BOOL,
  -- Android user id for multi-user devices
  user_id LONG,
  -- Type of the Android user (e.g., HEADLESS, SECONDARY)
  user_type STRING,
  -- Name of the packages running in this process.
  package_name STRING,
  -- Package version code.
  version_code LONG,
  -- Whether package is debuggable.
  debuggable LONG,
  -- Whether the task is kernel or not
  is_kernel_task BOOL
)
AS
SELECT
  process.upid,
  process.pid,
  -- workaround for b/169226092: the bug has been fixed it Android T, but
  -- we support ingesting traces from older Android versions.
  CASE
    -- cmdline gets rewritten after fork, if these are still there we must
    -- have seen a racy capture.
    WHEN length(process.name) = 15
    AND (process.cmdline IN ('zygote', 'zygote64', '<pre-initialized>')
    OR process.cmdline GLOB '*' || process.name) THEN process.cmdline
    ELSE process.name
  END AS process_name,
  process.android_appid AS uid,
  process.android_user_id AS user_id,
  user.type AS user_type,
  CASE WHEN _uid_package_count.cnt > 1 THEN TRUE ELSE NULL END AS shared_uid,
  plist.package_name,
  plist.version_code,
  plist.debuggable,
  android_is_kernel_task(process.upid) AS is_kernel_task
FROM process
LEFT JOIN _uid_package_count
  ON _app_id_to_base_package(process.android_appid) = _uid_package_count.uid
LEFT JOIN _android_package_for_process(
  _app_id_to_base_package(process.android_appid),
  _uid_package_count.cnt,
  process.name
) AS plist
LEFT JOIN android_user_list AS user
  ON process.android_user_id = user.android_user_id
ORDER BY
  upid;
