libdvr: Add performance API to platform library.
- Add dvrPerformanceSetSchedulerPolicy API. Only this API is exposed
through the DVR platform library, the older API will be deprecated.
- Add permission checks to all performanced APIs.
- Allow services with android.permission.RESTRICTED_VR_ACCESS to change
scheduler policy for VR apps.
- Minor updates to use the updated PDX service API. The older API will
be deprecated soon.
- Add tests for permission checks and policy API.
Most of the tests are automatic however, there is one manual step for
testing the android.permission.RESTRICTED_VR_ACCESS (e.g. trusted uid)
check. Because there is no reliable way to determine the UID of VrCore
from the gtest, instead the gtest looks for an env var named
GTEST_TRUSTED_UID; if this is set the value is used as a uid and the
trusted uid tests are enabled.
Bug: 62468109
Test: 'GTEST_TRUSTED_UID=<VrCore UID> performance_service_tests' passes.
Change-Id: I9047b298a015a69535b655a299ca26c179e2d57d
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 4041d6b..7fe9825 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -31,6 +31,7 @@
"dvr_configuration_data.cpp",
"dvr_display_manager.cpp",
"dvr_hardware_composer_client.cpp",
+ "dvr_performance.cpp",
"dvr_surface.cpp",
"dvr_vsync.cpp",
]
@@ -45,6 +46,7 @@
"libvr_hwc-impl",
"libvr_hwc-binder",
"libgrallocusage",
+ "libperformance",
"libpdx_default_transport",
]
diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp
index dc31917..7d4e2d1 100644
--- a/libs/vr/libdvr/dvr_api.cpp
+++ b/libs/vr/libdvr/dvr_api.cpp
@@ -10,6 +10,7 @@
#include <dvr/dvr_buffer_queue.h>
#include <dvr/dvr_configuration_data.h>
#include <dvr/dvr_display_manager.h>
+#include <dvr/dvr_performance.h>
#include <dvr/dvr_surface.h>
#include <dvr/dvr_vsync.h>
diff --git a/libs/vr/libdvr/dvr_performance.cpp b/libs/vr/libdvr/dvr_performance.cpp
new file mode 100644
index 0000000..599101f
--- /dev/null
+++ b/libs/vr/libdvr/dvr_performance.cpp
@@ -0,0 +1,18 @@
+#include "include/dvr/dvr_performance.h"
+
+#include <private/dvr/performance_client.h>
+
+using android::dvr::PerformanceClient;
+
+extern "C" {
+
+int dvrPerformanceSetSchedulerPolicy(pid_t task_id,
+ const char* scheduler_policy) {
+ int error;
+ if (auto client = PerformanceClient::Create(&error))
+ return client->SetSchedulerPolicy(task_id, scheduler_policy);
+ else
+ return error;
+}
+
+} // extern "C"
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index 4b530b2..ca41e52 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
+#include <unistd.h>
#include <cstdio>
#include <dvr/dvr_display_types.h>
@@ -320,6 +321,10 @@
size_t layer_index,
size_t index);
+// dvr_performance.h
+typedef int (*DvrPerformanceSetSchedulerPolicyPtr)(
+ pid_t task_id, const char* scheduler_policy);
+
// The buffer metadata that an Android Surface (a.k.a. ANativeWindow)
// will populate. A DvrWriteBufferQueue must be created with this metadata iff
// ANativeWindow access is needed. Please do not remove, modify, or reorder
diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
index 64e5e71..09bae84 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -151,3 +151,6 @@
// Read the native display metrics from the hardware composer
DVR_V1_API_ENTRY(GetNativeDisplayMetrics);
+
+// Performance
+DVR_V1_API_ENTRY(PerformanceSetSchedulerPolicy);
diff --git a/libs/vr/libdvr/include/dvr/dvr_performance.h b/libs/vr/libdvr/include/dvr/dvr_performance.h
new file mode 100644
index 0000000..5df35ad
--- /dev/null
+++ b/libs/vr/libdvr/include/dvr/dvr_performance.h
@@ -0,0 +1,26 @@
+#ifndef ANDROID_DVR_PERFORMANCE_H_
+#define ANDROID_DVR_PERFORMANCE_H_
+
+#include <stddef.h>
+#include <unistd.h>
+
+__BEGIN_DECLS
+
+/// Sets the scheduler policy for a task.
+///
+/// Sets the scheduler policy for a task to the class described by a semantic
+/// string.
+///
+/// Supported policies are device-specific.
+///
+/// @param task_id The task id of task to set the policy for. When task_id is 0
+/// the current task id is substituted.
+/// @param scheduler_policy NULL-terminated ASCII string containing the desired
+/// scheduler policy.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrPerformanceSetSchedulerPolicy(pid_t task_id, const char* scheduler_policy);
+
+__END_DECLS
+
+#endif // ANDROID_DVR_PERFORMANCE_H_
+
diff --git a/libs/vr/libperformance/include/dvr/performance_client_api.h b/libs/vr/libperformance/include/dvr/performance_client_api.h
index 2216e38..9d617cb 100644
--- a/libs/vr/libperformance/include/dvr/performance_client_api.h
+++ b/libs/vr/libperformance/include/dvr/performance_client_api.h
@@ -8,6 +8,20 @@
extern "C" {
#endif
+/// Sets the scheduler policy for a task.
+///
+/// Sets the scheduler policy for a task to the class described by a semantic
+/// string.
+///
+/// Supported policies are device-specific.
+///
+/// @param task_id The task id of task to set the policy for. When task_id is 0
+/// the current task id is substituted.
+/// @param scheduler_policy NULL-terminated ASCII string containing the desired
+/// scheduler policy.
+/// @returns Returns 0 on success or a negative errno error code on error.
+int dvrSetSchedulerPolicy(pid_t task_id, const char* scheduler_policy);
+
/// Sets the CPU partition for a task.
///
/// Sets the CPU partition for a task to the partition described by a CPU
diff --git a/libs/vr/libperformance/include/private/dvr/performance_client.h b/libs/vr/libperformance/include/private/dvr/performance_client.h
index a61c6b2..3bd90dc 100644
--- a/libs/vr/libperformance/include/private/dvr/performance_client.h
+++ b/libs/vr/libperformance/include/private/dvr/performance_client.h
@@ -14,6 +14,10 @@
class PerformanceClient : public pdx::ClientBase<PerformanceClient> {
public:
+ int SetSchedulerPolicy(pid_t task_id, const std::string& scheduler_policy);
+ int SetSchedulerPolicy(pid_t task_id, const char* scheduler_policy);
+
+ // TODO(eieio): Consider deprecating this API.
int SetCpuPartition(pid_t task_id, const std::string& partition);
int SetCpuPartition(pid_t task_id, const char* partition);
int SetSchedulerClass(pid_t task_id, const std::string& scheduler_class);
diff --git a/libs/vr/libperformance/include/private/dvr/performance_rpc.h b/libs/vr/libperformance/include/private/dvr/performance_rpc.h
index 73bdaa7..d57bbe8 100644
--- a/libs/vr/libperformance/include/private/dvr/performance_rpc.h
+++ b/libs/vr/libperformance/include/private/dvr/performance_rpc.h
@@ -21,14 +21,17 @@
kOpSetCpuPartition = 0,
kOpSetSchedulerClass,
kOpGetCpuPartition,
+ kOpSetSchedulerPolicy,
};
// Methods.
PDX_REMOTE_METHOD(SetCpuPartition, kOpSetCpuPartition,
- int(pid_t, const std::string&));
+ void(pid_t, const std::string&));
PDX_REMOTE_METHOD(SetSchedulerClass, kOpSetSchedulerClass,
- int(pid_t, const std::string&));
+ void(pid_t, const std::string&));
PDX_REMOTE_METHOD(GetCpuPartition, kOpGetCpuPartition, std::string(pid_t));
+ PDX_REMOTE_METHOD(SetSchedulerPolicy, kOpSetSchedulerPolicy,
+ void(pid_t, const std::string&));
};
} // namespace dvr
diff --git a/libs/vr/libperformance/performance_client.cpp b/libs/vr/libperformance/performance_client.cpp
index 2124162..bf3726e 100644
--- a/libs/vr/libperformance/performance_client.cpp
+++ b/libs/vr/libperformance/performance_client.cpp
@@ -37,6 +37,26 @@
task_id, WrapString(partition)));
}
+int PerformanceClient::SetSchedulerPolicy(pid_t task_id,
+ const std::string& scheduler_policy) {
+ if (task_id == 0)
+ task_id = gettid();
+
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<PerformanceRPC::SetSchedulerPolicy>(task_id,
+ scheduler_policy));
+}
+
+int PerformanceClient::SetSchedulerPolicy(pid_t task_id,
+ const char* scheduler_policy) {
+ if (task_id == 0)
+ task_id = gettid();
+
+ return ReturnStatusOrError(
+ InvokeRemoteMethod<PerformanceRPC::SetSchedulerPolicy>(
+ task_id, WrapString(scheduler_policy)));
+}
+
int PerformanceClient::SetSchedulerClass(pid_t task_id,
const std::string& scheduler_class) {
if (task_id == 0)
@@ -101,6 +121,15 @@
return error;
}
+extern "C" int dvrSetSchedulerPolicy(pid_t task_id,
+ const char* scheduler_policy) {
+ int error;
+ if (auto client = android::dvr::PerformanceClient::Create(&error))
+ return client->SetSchedulerPolicy(task_id, scheduler_policy);
+ else
+ return error;
+}
+
extern "C" int dvrSetSchedulerClass(pid_t task_id,
const char* scheduler_class) {
int error;
diff --git a/services/vr/performanced/Android.mk b/services/vr/performanced/Android.mk
index 6256e90..dbc66f1 100644
--- a/services/vr/performanced/Android.mk
+++ b/services/vr/performanced/Android.mk
@@ -23,8 +23,10 @@
staticLibraries := \
libperformance \
libpdx_default_transport \
+ libvr_manager
sharedLibraries := \
+ libbinder \
libbase \
libcutils \
liblog \
diff --git a/services/vr/performanced/cpu_set.cpp b/services/vr/performanced/cpu_set.cpp
index 1a3723f..1a7264c 100644
--- a/services/vr/performanced/cpu_set.cpp
+++ b/services/vr/performanced/cpu_set.cpp
@@ -15,6 +15,9 @@
#include "task.h"
#include "unique_file.h"
+using android::pdx::ErrorStatus;
+using android::pdx::Status;
+
namespace {
constexpr int kDirectoryFlags = O_RDONLY | O_DIRECTORY | O_CLOEXEC;
@@ -176,11 +179,11 @@
task_id, task.name().c_str(), target_set.c_str(),
task.thread_group_id(), task.parent_process_id());
- const int ret = target->AttachTask(task_id);
- ALOGW_IF(ret < 0 && ret != -EINVAL,
+ auto status = target->AttachTask(task_id);
+ ALOGW_IF(!status && status.error() != EINVAL,
"CpuSetManager::MoveUnboundTasks: Failed to attach task_id=%d "
"to cpuset=%s: %s",
- task_id, target_set.c_str(), strerror(-ret));
+ task_id, target_set.c_str(), status.GetErrorMessage().c_str());
} else {
ALOGD_IF(TRACE,
"CpuSet::MoveUnboundTasks: Skipping task_id=%d name=%s cpus=%s.",
@@ -233,7 +236,7 @@
return fp;
}
-int CpuSet::AttachTask(pid_t task_id) const {
+Status<void> CpuSet::AttachTask(pid_t task_id) const {
auto file = OpenFile("tasks", O_RDWR);
if (file.get() >= 0) {
std::ostringstream stream;
@@ -241,11 +244,15 @@
std::string value = stream.str();
const bool ret = base::WriteStringToFd(value, file.get());
- return !ret ? -errno : 0;
+ if (!ret)
+ return ErrorStatus(errno);
+ else
+ return {};
} else {
+ const int error = errno;
ALOGE("CpuSet::AttachTask: Failed to open %s/tasks: %s", path_.c_str(),
- strerror(errno));
- return -errno;
+ strerror(error));
+ return ErrorStatus(error);
}
}
diff --git a/services/vr/performanced/cpu_set.h b/services/vr/performanced/cpu_set.h
index fcf280a..6879272 100644
--- a/services/vr/performanced/cpu_set.h
+++ b/services/vr/performanced/cpu_set.h
@@ -11,6 +11,8 @@
#include <android-base/unique_fd.h>
+#include <pdx/status.h>
+
#include "unique_file.h"
namespace android {
@@ -28,7 +30,7 @@
std::string GetCpuList() const;
- int AttachTask(pid_t task_id) const;
+ pdx::Status<void> AttachTask(pid_t task_id) const;
std::vector<pid_t> GetTasks() const;
private:
diff --git a/services/vr/performanced/performance_service.cpp b/services/vr/performanced/performance_service.cpp
index 955c661..3f7009a 100644
--- a/services/vr/performanced/performance_service.cpp
+++ b/services/vr/performanced/performance_service.cpp
@@ -8,14 +8,20 @@
#include <pdx/rpc/argument_encoder.h>
#include <pdx/rpc/message_buffer.h>
#include <pdx/rpc/remote_method.h>
+#include <private/android_filesystem_config.h>
#include <private/dvr/performance_rpc.h>
+#include <private/dvr/trusted_uids.h>
#include "task.h"
// This prctl is only available in Android kernels.
#define PR_SET_TIMERSLACK_PID 41
+using android::dvr::IsTrustedUid;
+using android::dvr::Task;
+using android::pdx::ErrorStatus;
using android::pdx::Message;
+using android::pdx::Status;
using android::pdx::rpc::DispatchRemoteMethod;
using android::pdx::default_transport::Endpoint;
@@ -26,6 +32,68 @@
constexpr unsigned long kTimerSlackForegroundNs = 50000;
constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
+// Expands the given parameter pack expression using an initializer list to
+// guarantee ordering and a comma expression to guarantee even void expressions
+// are valid elements of the initializer list.
+#define EXPAND_PACK(...) \
+ std::initializer_list<int> { (__VA_ARGS__, 0)... }
+
+// Returns true if the sender's euid matches any of the uids in |UIDs|.
+template <uid_t... UIDs>
+struct UserId {
+ static bool Check(const Message& sender, const Task&) {
+ const uid_t uid = sender.GetEffectiveUserId();
+ bool allow = false;
+ EXPAND_PACK(allow |= (uid == UIDs));
+ return allow;
+ }
+};
+
+// Returns true if the sender's egid matches any of the gids in |GIDs|.
+template <gid_t... GIDs>
+struct GroupId {
+ static bool Check(const Message& sender, const Task&) {
+ const gid_t gid = sender.GetEffectiveGroupId();
+ bool allow = false;
+ EXPAND_PACK(allow |= (gid == GIDs));
+ return allow;
+ }
+};
+
+// Returns true if the sender's euid is trusted according to VR manager service.
+struct Trusted {
+ static bool Check(const Message& sender, const Task&) {
+ return IsTrustedUid(sender.GetEffectiveUserId(), false);
+ }
+};
+
+// Returns returns true if the task belongs to the sending process.
+struct SameProcess {
+ static bool Check(const Message& sender, const Task& task) {
+ return sender.GetProcessId() == task.thread_group_id();
+ }
+};
+
+// Returns true if any of the checks in |Allows| pass, false otherwise.
+template <typename... Allows>
+struct CheckOr {
+ static bool Check(const Message& sender, const Task& task) {
+ bool allow = false;
+ EXPAND_PACK(allow |= Allows::Check(sender, task));
+ return allow;
+ }
+};
+
+// Returns true if all of the checks in |Allows| pass, false otherwise.
+template <typename... Allows>
+struct CheckAnd {
+ static bool Check(const Message& sender, const Task& task) {
+ bool allow = true;
+ EXPAND_PACK(allow &= Allows::Check(sender, task));
+ return allow;
+ }
+};
+
} // anonymous namespace
namespace android {
@@ -48,43 +116,79 @@
const int fifo_low = sched_fifo_min_priority_;
const int fifo_medium = sched_fifo_min_priority_ + fifo_range / 5;
- // TODO(eieio): Make this configurable on the command line.
+ // TODO(eieio): Make this configurable on the command line or config file.
cpuset_.MoveUnboundTasks("/kernel");
+ // TODO(eieio): Replace this witha device-specific config file. This is just a
+ // hack for now to put some form of permission logic in place while a longer
+ // term solution is developed.
+ using AllowRootSystem =
+ CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM>,
+ GroupId<AID_SYSTEM>>>;
+ using AllowRootSystemGraphics =
+ CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_GRAPHICS>,
+ GroupId<AID_SYSTEM, AID_GRAPHICS>>>;
+ using AllowRootSystemAudio =
+ CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_AUDIO>,
+ GroupId<AID_SYSTEM, AID_AUDIO>>>;
+ using AllowRootSystemTrusted = CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>,
+ GroupId<AID_SYSTEM>>;
+
+ partition_permission_check_ = AllowRootSystemTrusted::Check;
+
// Setup the scheduler classes.
+ // TODO(eieio): Replace this with a device-specific config file.
scheduler_classes_ = {
{"audio:low",
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
- .priority = fifo_medium}},
+ .priority = fifo_medium,
+ .permission_check = AllowRootSystemAudio::Check}},
{"audio:high",
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
- .priority = fifo_medium + 3}},
+ .priority = fifo_medium + 3,
+ .permission_check = AllowRootSystemAudio::Check}},
{"graphics",
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
- .priority = fifo_medium}},
+ .priority = fifo_medium,
+ .permission_check = AllowRootSystemGraphics::Check}},
{"graphics:low",
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
- .priority = fifo_medium}},
+ .priority = fifo_medium,
+ .permission_check = AllowRootSystemGraphics::Check}},
{"graphics:high",
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
- .priority = fifo_medium + 2}},
+ .priority = fifo_medium + 2,
+ .permission_check = AllowRootSystemGraphics::Check}},
{"sensors",
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
- .priority = fifo_low}},
+ .priority = fifo_low,
+ .permission_check = AllowRootSystem::Check}},
{"sensors:low",
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
- .priority = fifo_low}},
+ .priority = fifo_low,
+ .permission_check = AllowRootSystem::Check}},
{"sensors:high",
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
- .priority = fifo_low + 1}},
+ .priority = fifo_low + 1,
+ .permission_check = AllowRootSystem::Check}},
+ {"vr:system:arp",
+ {.timer_slack = kTimerSlackForegroundNs,
+ .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+ .priority = fifo_medium + 2,
+ .permission_check = AllowRootSystemTrusted::Check}},
+ {"vr:app:render",
+ {.timer_slack = kTimerSlackForegroundNs,
+ .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
+ .priority = fifo_medium + 1,
+ .permission_check = AllowRootSystemTrusted::Check}},
{"normal",
{.timer_slack = kTimerSlackForegroundNs,
.scheduler_policy = SCHED_NORMAL,
@@ -113,67 +217,94 @@
return cpuset_.DumpState();
}
-int PerformanceService::OnSetCpuPartition(Message& message, pid_t task_id,
- const std::string& partition) {
+Status<void> PerformanceService::OnSetSchedulerPolicy(
+ Message& message, pid_t task_id, const std::string& scheduler_policy) {
+ // Forward to scheduler class handler for now. In the future this method will
+ // subsume the others by unifying both scheduler class and cpu partiton into a
+ // single policy concept.
+ ALOGI(
+ "PerformanceService::OnSetSchedulerPolicy: task_id=%d "
+ "scheduler_policy=%s",
+ task_id, scheduler_policy.c_str());
+ return OnSetSchedulerClass(message, task_id, scheduler_policy);
+}
+
+Status<void> PerformanceService::OnSetCpuPartition(
+ Message& message, pid_t task_id, const std::string& partition) {
Task task(task_id);
if (!task || task.thread_group_id() != message.GetProcessId())
- return -EINVAL;
+ return ErrorStatus(EINVAL);
+
+ // Temporary permission check.
+ // TODO(eieio): Replace this with a configuration file.
+ if (partition_permission_check_ &&
+ !partition_permission_check_(message, task)) {
+ return ErrorStatus(EINVAL);
+ }
auto target_set = cpuset_.Lookup(partition);
if (!target_set)
- return -ENOENT;
+ return ErrorStatus(ENOENT);
- const auto attach_error = target_set->AttachTask(task_id);
- if (attach_error)
- return attach_error;
+ auto attach_status = target_set->AttachTask(task_id);
+ if (!attach_status)
+ return attach_status;
- return 0;
+ return {};
}
-int PerformanceService::OnSetSchedulerClass(
+Status<void> PerformanceService::OnSetSchedulerClass(
Message& message, pid_t task_id, const std::string& scheduler_class) {
- // Make sure the task id is valid and belongs to the sending process.
Task task(task_id);
- if (!task || task.thread_group_id() != message.GetProcessId())
- return -EINVAL;
+ if (!task)
+ return ErrorStatus(EINVAL);
- struct sched_param param;
-
- // TODO(eieio): Apply rules based on the requesting process. Applications are
- // only allowed one audio thread that runs at SCHED_FIFO. System services can
- // have more than one.
auto search = scheduler_classes_.find(scheduler_class);
if (search != scheduler_classes_.end()) {
auto config = search->second;
+
+ // Make sure the sending process is allowed to make the requested change to
+ // this task.
+ if (!config.IsAllowed(message, task))
+ return ErrorStatus(EINVAL);
+
+ struct sched_param param;
param.sched_priority = config.priority;
+
sched_setscheduler(task_id, config.scheduler_policy, ¶m);
prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id);
ALOGI("PerformanceService::OnSetSchedulerClass: Set task=%d to class=%s.",
task_id, scheduler_class.c_str());
- return 0;
+ return {};
} else {
ALOGE(
"PerformanceService::OnSetSchedulerClass: Invalid class=%s requested "
"by task=%d.",
scheduler_class.c_str(), task_id);
- return -EINVAL;
+ return ErrorStatus(EINVAL);
}
}
-std::string PerformanceService::OnGetCpuPartition(Message& message,
- pid_t task_id) {
+Status<std::string> PerformanceService::OnGetCpuPartition(Message& message,
+ pid_t task_id) {
// Make sure the task id is valid and belongs to the sending process.
Task task(task_id);
if (!task || task.thread_group_id() != message.GetProcessId())
- REPLY_ERROR_RETURN(message, EINVAL, "");
+ return ErrorStatus(EINVAL);
return task.GetCpuSetPath();
}
-pdx::Status<void> PerformanceService::HandleMessage(Message& message) {
+Status<void> PerformanceService::HandleMessage(Message& message) {
+ ALOGD_IF(TRACE, "PerformanceService::HandleMessage: op=%d", message.GetOp());
switch (message.GetOp()) {
+ case PerformanceRPC::SetSchedulerPolicy::Opcode:
+ DispatchRemoteMethod<PerformanceRPC::SetSchedulerPolicy>(
+ *this, &PerformanceService::OnSetSchedulerPolicy, message);
+ return {};
+
case PerformanceRPC::SetCpuPartition::Opcode:
- DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
+ DispatchRemoteMethod<PerformanceRPC::SetCpuPartition>(
*this, &PerformanceService::OnSetCpuPartition, message);
return {};
diff --git a/services/vr/performanced/performance_service.h b/services/vr/performanced/performance_service.h
index 34abba7..b812535 100644
--- a/services/vr/performanced/performance_service.h
+++ b/services/vr/performanced/performance_service.h
@@ -1,12 +1,14 @@
#ifndef ANDROID_DVR_PERFORMANCED_PERFORMANCE_SERVICE_H_
#define ANDROID_DVR_PERFORMANCED_PERFORMANCE_SERVICE_H_
+#include <functional>
#include <string>
#include <unordered_map>
#include <pdx/service.h>
#include "cpu_set.h"
+#include "task.h"
namespace android {
namespace dvr {
@@ -27,11 +29,15 @@
PerformanceService();
- int OnSetCpuPartition(pdx::Message& message, pid_t task_id,
- const std::string& partition);
- int OnSetSchedulerClass(pdx::Message& message, pid_t task_id,
- const std::string& scheduler_class);
- std::string OnGetCpuPartition(pdx::Message& message, pid_t task_id);
+ pdx::Status<void> OnSetSchedulerPolicy(pdx::Message& message, pid_t task_id,
+ const std::string& scheduler_class);
+
+ pdx::Status<void> OnSetCpuPartition(pdx::Message& message, pid_t task_id,
+ const std::string& partition);
+ pdx::Status<void> OnSetSchedulerClass(pdx::Message& message, pid_t task_id,
+ const std::string& scheduler_class);
+ pdx::Status<std::string> OnGetCpuPartition(pdx::Message& message,
+ pid_t task_id);
CpuSetManager cpuset_;
@@ -43,10 +49,24 @@
unsigned long timer_slack;
int scheduler_policy;
int priority;
+ std::function<bool(const pdx::Message& message, const Task& task)>
+ permission_check;
+
+ // Check the permisison of the given task to use this scheduler class. If a
+ // permission check function is not set then all tasks are allowed.
+ bool IsAllowed(const pdx::Message& message, const Task& task) const {
+ if (permission_check)
+ return permission_check(message, task);
+ else
+ return true;
+ }
};
std::unordered_map<std::string, SchedulerClassConfig> scheduler_classes_;
+ std::function<bool(const pdx::Message& message, const Task& task)>
+ partition_permission_check_;
+
PerformanceService(const PerformanceService&) = delete;
void operator=(const PerformanceService&) = delete;
};
diff --git a/services/vr/performanced/performance_service_tests.cpp b/services/vr/performanced/performance_service_tests.cpp
index b526082..7de1f08 100644
--- a/services/vr/performanced/performance_service_tests.cpp
+++ b/services/vr/performanced/performance_service_tests.cpp
@@ -1,12 +1,22 @@
#include <errno.h>
#include <sched.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <condition_variable>
+#include <cstdlib>
#include <mutex>
#include <thread>
#include <dvr/performance_client_api.h>
#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+
+namespace {
+
+const char kTrustedUidEnvironmentVariable[] = "GTEST_TRUSTED_UID";
+
+} // anonymous namespace
TEST(DISABLED_PerformanceTest, SetCpuPartition) {
int error;
@@ -86,6 +96,27 @@
EXPECT_EQ(-EINVAL, error);
}
+// This API mirrors SetSchedulerClass for now. Replace with with a more specific
+// test once the policy API is fully implemented.
+TEST(PerformanceTest, SetSchedulerPolicy) {
+ int error;
+
+ error = dvrSetSchedulerPolicy(0, "background");
+ EXPECT_EQ(0, error);
+ EXPECT_EQ(SCHED_BATCH, sched_getscheduler(0));
+
+ error = dvrSetSchedulerPolicy(0, "audio:low");
+ EXPECT_EQ(0, error);
+ EXPECT_EQ(SCHED_FIFO | SCHED_RESET_ON_FORK, sched_getscheduler(0));
+
+ error = dvrSetSchedulerPolicy(0, "normal");
+ EXPECT_EQ(0, error);
+ EXPECT_EQ(SCHED_NORMAL, sched_getscheduler(0));
+
+ error = dvrSetSchedulerPolicy(0, "foobar");
+ EXPECT_EQ(-EINVAL, error);
+}
+
TEST(PerformanceTest, SchedulerClassResetOnFork) {
int error;
@@ -135,3 +166,273 @@
error = dvrGetCpuPartition(0, nullptr, sizeof(partition));
EXPECT_EQ(-EINVAL, error);
}
+
+TEST(PerformanceTest, Permissions) {
+ int error;
+
+ const int original_uid = getuid();
+ const int original_gid = getgid();
+ int trusted_uid = -1;
+
+ // See if the environment variable GTEST_TRUSTED_UID is set. If it is enable
+ // testing the ActivityManager trusted uid permission checks using that uid.
+ const char* trusted_uid_env = std::getenv(kTrustedUidEnvironmentVariable);
+ if (trusted_uid_env)
+ trusted_uid = std::atoi(trusted_uid_env);
+
+ ASSERT_EQ(AID_ROOT, original_uid)
+ << "This test must run as root to function correctly!";
+
+ // Switch the uid/gid to an id that should not have permission to access any
+ // privileged actions.
+ ASSERT_EQ(0, setresgid(AID_NOBODY, AID_NOBODY, -1))
+ << "Failed to set gid: " << strerror(errno);
+ ASSERT_EQ(0, setresuid(AID_NOBODY, AID_NOBODY, -1))
+ << "Failed to set uid: " << strerror(errno);
+
+ // Unprivileged policies.
+ error = dvrSetSchedulerPolicy(0, "batch");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "background");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "foreground");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "normal");
+ EXPECT_EQ(0, error);
+
+ // Privileged policies.
+ error = dvrSetSchedulerPolicy(0, "audio:low");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "audio:high");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "graphics");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "graphics:low");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "graphics:high");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "sensors");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "sensors:low");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "sensors:high");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "vr:system:arp");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "vr:app:render");
+ EXPECT_EQ(-EINVAL, error);
+
+ // uid=AID_SYSTEM / gid=AID_NOBODY
+ ASSERT_EQ(0, setresuid(original_uid, original_uid, -1))
+ << "Failed to restore uid: " << strerror(errno);
+ ASSERT_EQ(0, setresuid(AID_SYSTEM, AID_SYSTEM, -1))
+ << "Failed to set uid: " << strerror(errno);
+
+ // Unprivileged policies.
+ error = dvrSetSchedulerPolicy(0, "batch");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "background");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "foreground");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "normal");
+ EXPECT_EQ(0, error);
+
+ // Privileged policies.
+ error = dvrSetSchedulerPolicy(0, "audio:low");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "audio:high");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "graphics");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "graphics:low");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "graphics:high");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "sensors");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "sensors:low");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "sensors:high");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "vr:system:arp");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "vr:app:render");
+ EXPECT_EQ(0, error);
+
+ // uid=AID_NOBODY / gid=AID_SYSTEM
+ ASSERT_EQ(0, setresuid(original_uid, original_uid, -1))
+ << "Failed to restore uid: " << strerror(errno);
+ ASSERT_EQ(0, setresgid(original_gid, original_gid, -1))
+ << "Failed to restore gid: " << strerror(errno);
+ ASSERT_EQ(0, setresgid(AID_SYSTEM, AID_SYSTEM, -1))
+ << "Failed to set gid: " << strerror(errno);
+ ASSERT_EQ(0, setresuid(AID_SYSTEM, AID_NOBODY, -1))
+ << "Failed to set uid: " << strerror(errno);
+
+ // Unprivileged policies.
+ error = dvrSetSchedulerPolicy(0, "batch");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "background");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "foreground");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "normal");
+ EXPECT_EQ(0, error);
+
+ // Privileged policies.
+ error = dvrSetSchedulerPolicy(0, "audio:low");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "audio:high");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "graphics");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "graphics:low");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "graphics:high");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "sensors");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "sensors:low");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "sensors:high");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "vr:system:arp");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "vr:app:render");
+ EXPECT_EQ(0, error);
+
+ // uid=AID_GRAPHICS / gid=AID_NOBODY
+ ASSERT_EQ(0, setresuid(original_uid, original_uid, -1))
+ << "Failed to restore uid: " << strerror(errno);
+ ASSERT_EQ(0, setresgid(original_gid, original_gid, -1))
+ << "Failed to restore gid: " << strerror(errno);
+ ASSERT_EQ(0, setresgid(AID_NOBODY, AID_NOBODY, -1))
+ << "Failed to set gid: " << strerror(errno);
+ ASSERT_EQ(0, setresuid(AID_GRAPHICS, AID_GRAPHICS, -1))
+ << "Failed to set uid: " << strerror(errno);
+
+ // Unprivileged policies.
+ error = dvrSetSchedulerPolicy(0, "batch");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "background");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "foreground");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "normal");
+ EXPECT_EQ(0, error);
+
+ // Privileged policies.
+ error = dvrSetSchedulerPolicy(0, "audio:low");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "audio:high");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "graphics");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "graphics:low");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "graphics:high");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "sensors");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "sensors:low");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "sensors:high");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "vr:system:arp");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "vr:app:render");
+ EXPECT_EQ(-EINVAL, error);
+
+ // uid=AID_NOBODY / gid=AID_GRAPHICS
+ ASSERT_EQ(0, setresuid(original_uid, original_uid, -1))
+ << "Failed to restore uid: " << strerror(errno);
+ ASSERT_EQ(0, setresgid(original_gid, original_gid, -1))
+ << "Failed to restore gid: " << strerror(errno);
+ ASSERT_EQ(0, setresgid(AID_GRAPHICS, AID_GRAPHICS, -1))
+ << "Failed to set gid: " << strerror(errno);
+ ASSERT_EQ(0, setresuid(AID_NOBODY, AID_NOBODY, -1))
+ << "Failed to set uid: " << strerror(errno);
+
+ // Unprivileged policies.
+ error = dvrSetSchedulerPolicy(0, "batch");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "background");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "foreground");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "normal");
+ EXPECT_EQ(0, error);
+
+ // Privileged policies.
+ error = dvrSetSchedulerPolicy(0, "audio:low");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "audio:high");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "graphics");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "graphics:low");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "graphics:high");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "sensors");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "sensors:low");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "sensors:high");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "vr:system:arp");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "vr:app:render");
+ EXPECT_EQ(-EINVAL, error);
+
+ if (trusted_uid != -1) {
+ // uid=<trusted uid> / gid=AID_NOBODY
+ ASSERT_EQ(0, setresuid(original_uid, original_uid, -1))
+ << "Failed to restore uid: " << strerror(errno);
+ ASSERT_EQ(0, setresgid(original_gid, original_gid, -1))
+ << "Failed to restore gid: " << strerror(errno);
+ ASSERT_EQ(0, setresgid(AID_NOBODY, AID_NOBODY, -1))
+ << "Failed to set gid: " << strerror(errno);
+ ASSERT_EQ(0, setresuid(trusted_uid, trusted_uid, -1))
+ << "Failed to set uid: " << strerror(errno);
+
+ // Unprivileged policies.
+ error = dvrSetSchedulerPolicy(0, "batch");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "background");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "foreground");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "normal");
+ EXPECT_EQ(0, error);
+
+ // Privileged policies.
+ error = dvrSetSchedulerPolicy(0, "audio:low");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "audio:high");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "graphics");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "graphics:low");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "graphics:high");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "sensors");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "sensors:low");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "sensors:high");
+ EXPECT_EQ(-EINVAL, error);
+ error = dvrSetSchedulerPolicy(0, "vr:system:arp");
+ EXPECT_EQ(0, error);
+ error = dvrSetSchedulerPolicy(0, "vr:app:render");
+ EXPECT_EQ(0, error);
+ }
+
+ // Restore original effective uid/gid.
+ ASSERT_EQ(0, setresgid(original_gid, original_gid, -1))
+ << "Failed to restore gid: " << strerror(errno);
+ ASSERT_EQ(0, setresuid(original_uid, original_uid, -1))
+ << "Failed to restore uid: " << strerror(errno);
+}