1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "./test-app.h"
18
19 #include <fcntl.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24
25 #include <iostream>
26 #include <string>
27
28 #include "./string-utils.h"
29
30 namespace shell_as {
31
32 // Returns a pointer to bytes of the test app APK along with the length in bytes
33 // of the APK.
34 //
35 // This function is defined by the shell-as-test-app-apk-cpp genrule.
36 void GetTestApk(uint8_t **apk, size_t *length);
37
38 namespace {
39
40 // The staging path for the test app APK.
41 const char kTestAppApkStagingPath[] = "/data/local/tmp/shell-as-test-app.apk";
42
43 // Writes the test app to a staging location and then installs the APK via the
44 // 'pm' utility. The app is granted runtime permissions on installation. Returns
45 // true if the app is installed successfully.
InstallTestApp()46 bool InstallTestApp() {
47 uint8_t *apk = nullptr;
48 size_t apk_size = 0;
49 GetTestApk(&apk, &apk_size);
50
51 int staging_file = open(kTestAppApkStagingPath, O_WRONLY | O_CREAT | O_TRUNC,
52 S_IRUSR | S_IWUSR);
53 if (staging_file == -1) {
54 std::cerr << "Unable to open staging APK path." << std::endl;
55 return false;
56 }
57
58 size_t bytes_written = write(staging_file, apk, apk_size);
59 close(staging_file);
60 if (bytes_written != apk_size) {
61 std::cerr << "Unable to write entire test app APK." << std::endl;
62 return false;
63 }
64
65 const char cmd_template[] = "pm install -g %s > /dev/null 2> /dev/null";
66 char system_cmd[sizeof(cmd_template) + sizeof(kTestAppApkStagingPath) + 1] =
67 {};
68 sprintf(system_cmd, cmd_template, kTestAppApkStagingPath);
69 return system(system_cmd) == 0;
70 }
71
72 // Uninstalls the test app if it is installed. This method is a no-op if the app
73 // is not installed.
UninstallTestApp()74 void UninstallTestApp() {
75 system(
76 "pm uninstall com.android.google.tools.security.shell_as"
77 " > /dev/null 2> /dev/null");
78 }
79
80 // Starts the main activity of the test app. This is necessary as some aspects
81 // of the security context can only be inferred from a running process.
StartTestApp()82 bool StartTestApp() {
83 return system(
84 "am start-activity "
85 "com.android.google.tools.security.shell_as/"
86 ".MainActivity"
87 " > /dev/null 2> /dev/null") == 0;
88 }
89
90 // Obtain the process ID of the test app and returns true if it is running.
91 // Returns false otherwise.
GetTestAppProcessId(pid_t * test_app_pid)92 bool GetTestAppProcessId(pid_t *test_app_pid) {
93 FILE *pgrep = popen(
94 "pgrep -f "
95 "com.android.google.tools.security.shell_as",
96 "r");
97 if (!pgrep) {
98 std::cerr << "Unable to execute pgrep." << std::endl;
99 return false;
100 }
101
102 char pgrep_output[128];
103 memset(pgrep_output, 0, sizeof(pgrep_output));
104 int bytes_read = fread(pgrep_output, 1, sizeof(pgrep_output) - 1, pgrep);
105 pclose(pgrep);
106 if (bytes_read <= 0) {
107 // Unable to find the process. This may happen if the app is still starting
108 // up.
109 return false;
110 }
111 return StringToUInt32(pgrep_output, (uint32_t *)test_app_pid);
112 }
113 } // namespace
114
SetupAndStartTestApp(pid_t * test_app_pid)115 bool SetupAndStartTestApp(pid_t *test_app_pid) {
116 UninstallTestApp();
117
118 if (!InstallTestApp()) {
119 std::cerr << "Unable to install test app." << std::endl;
120 return false;
121 }
122
123 if (!StartTestApp()) {
124 std::cerr << "Unable to start and obtain test app PID." << std::endl;
125 return false;
126 }
127
128 for (int i = 0; i < 5; i++) {
129 if (GetTestAppProcessId(test_app_pid)) {
130 return true;
131 }
132 sleep(1);
133 }
134 return false;
135 }
136 } // namespace shell_as
137