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