/* * Copyright (C) 2023 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 * * http://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 "./test-app.h" #include #include #include #include #include #include #include #include "./string-utils.h" namespace shell_as { // Returns a pointer to bytes of the test app APK along with the length in bytes // of the APK. // // This function is defined by the shell-as-test-app-apk-cpp genrule. void GetTestApk(uint8_t **apk, size_t *length); namespace { // The staging path for the test app APK. const char kTestAppApkStagingPath[] = "/data/local/tmp/shell-as-test-app.apk"; // Writes the test app to a staging location and then installs the APK via the // 'pm' utility. The app is granted runtime permissions on installation. Returns // true if the app is installed successfully. bool InstallTestApp() { uint8_t *apk = nullptr; size_t apk_size = 0; GetTestApk(&apk, &apk_size); int staging_file = open(kTestAppApkStagingPath, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (staging_file == -1) { std::cerr << "Unable to open staging APK path." << std::endl; return false; } size_t bytes_written = write(staging_file, apk, apk_size); close(staging_file); if (bytes_written != apk_size) { std::cerr << "Unable to write entire test app APK." << std::endl; return false; } const char cmd_template[] = "pm install -g %s > /dev/null 2> /dev/null"; char system_cmd[sizeof(cmd_template) + sizeof(kTestAppApkStagingPath) + 1] = {}; sprintf(system_cmd, cmd_template, kTestAppApkStagingPath); return system(system_cmd) == 0; } // Uninstalls the test app if it is installed. This method is a no-op if the app // is not installed. void UninstallTestApp() { system( "pm uninstall com.android.google.tools.security.shell_as" " > /dev/null 2> /dev/null"); } // Starts the main activity of the test app. This is necessary as some aspects // of the security context can only be inferred from a running process. bool StartTestApp() { return system( "am start-activity " "com.android.google.tools.security.shell_as/" ".MainActivity" " > /dev/null 2> /dev/null") == 0; } // Obtain the process ID of the test app and returns true if it is running. // Returns false otherwise. bool GetTestAppProcessId(pid_t *test_app_pid) { FILE *pgrep = popen( "pgrep -f " "com.android.google.tools.security.shell_as", "r"); if (!pgrep) { std::cerr << "Unable to execute pgrep." << std::endl; return false; } char pgrep_output[128]; memset(pgrep_output, 0, sizeof(pgrep_output)); int bytes_read = fread(pgrep_output, 1, sizeof(pgrep_output) - 1, pgrep); pclose(pgrep); if (bytes_read <= 0) { // Unable to find the process. This may happen if the app is still starting // up. return false; } return StringToUInt32(pgrep_output, (uint32_t *)test_app_pid); } } // namespace bool SetupAndStartTestApp(pid_t *test_app_pid) { UninstallTestApp(); if (!InstallTestApp()) { std::cerr << "Unable to install test app." << std::endl; return false; } if (!StartTestApp()) { std::cerr << "Unable to start and obtain test app PID." << std::endl; return false; } for (int i = 0; i < 5; i++) { if (GetTestAppProcessId(test_app_pid)) { return true; } sleep(1); } return false; } } // namespace shell_as