1#!/usr/bin/env python3
2#
3# Copyright (C) 2021 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import logging
18import os
19import time
20
21from simpleperf_utils import remove
22from . test_utils import TestBase, TestHelper
23
24
25class TestApiProfiler(TestBase):
26    def run_api_test(self, package_name, apk_name, expected_reports, min_android_version: int):
27        adb = TestHelper.adb
28        if TestHelper.android_version < min_android_version:
29            logging.info('skip this test on Android < %s.' % min_android_version)
30            return
31        # step 1: Install and run the app.
32        apk_path = TestHelper.testdata_path(apk_name)
33        adb.run(['uninstall', package_name])
34        adb.check_run(['install', '-t', apk_path])
35        # Without sleep, the activity may be killed by post install intent ACTION_PACKAGE_CHANGED.
36        time.sleep(3)
37        # step 2: Prepare profiling.
38        self.run_cmd(['api_profiler.py', 'prepare', '-p', package_name, '-d', '1'])
39        if TestHelper.android_version >= 13:
40            # Enable perf_harden to check if profile_app_uid property works.
41            adb.set_property('security.perf_harden', '1')
42        adb.check_run(['shell', 'am', 'start', '-n', package_name + '/.MainActivity'])
43        # step 3: Wait until the app exits.
44        time.sleep(4)
45        while True:
46            result = adb.run(['shell', 'pidof', package_name])
47            if not result:
48                break
49            time.sleep(1)
50        # step 4: Collect recording data.
51        remove('simpleperf_data')
52        self.run_cmd(['api_profiler.py', 'collect', '-p', package_name, '-o', 'simpleperf_data'])
53        # step 5: Check recording data.
54        names = os.listdir('simpleperf_data')
55        self.assertGreater(len(names), 0)
56        for name in names:
57            path = os.path.join('simpleperf_data', name)
58            remove('report.txt')
59            self.run_cmd(['report.py', '-g', '-o', 'report.txt', '-i', path])
60            self.check_strings_in_file('report.txt', expected_reports)
61        # step 6: Clean up.
62        adb.check_run(['uninstall', package_name])
63
64    def run_cpp_api_test(self, apk_name, min_android_version):
65        self.run_api_test('simpleperf.demo.cpp_api', apk_name, ['BusyThreadFunc'],
66                          min_android_version)
67
68    def test_cpp_api_on_a_debuggable_app(self):
69        # The source code of the apk is in simpleperf/demo/CppApi.
70        self.run_cpp_api_test('cpp_api-debuggable.apk', 7)
71
72    def test_cpp_api_on_a_profileable_app(self):
73        # a release apk with <profileable android:shell="true" />
74        self.run_cpp_api_test('cpp_api-profileable.apk', 10)
75
76    def run_java_api_test(self, apk_name, min_android_version):
77        self.run_api_test('simpleperf.demo.java_api', apk_name,
78                          ['simpleperf.demo.java_api.MainActivity', 'java.lang.Thread.run'],
79                          min_android_version)
80
81    def test_java_api_on_a_debuggable_app(self):
82        # The source code of the apk is in simpleperf/demo/JavaApi.
83        self.run_java_api_test('java_api-debuggable.apk', 9)
84
85    def test_java_api_on_a_profileable_app(self):
86        # a release apk with <profileable android:shell="true" />
87        self.run_java_api_test('java_api-profileable.apk', 10)
88