blob: 84fedcab6f2b648240e520d34a32c9b39b444fc2 [file] [log] [blame]
/*
* 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 <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>
#include <string>
#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