Revise virtual touchpad interface.
- Explicit start and stop, outside of which the evdev devices
don't exist.
- Permission test (not compiled by default pending build & SELinux
support for temporarily retaining a second copy of the service
for vr_wm).
- Enforce a single user of the touchpad.
- Support 'dumpsys'.
Bug: 36051900
Test: log inspection
Change-Id: I038ed2632d5adf50a3565a981031691d5dc5f7cd
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
index a1f281c..2e2f622 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
@@ -1,22 +1,136 @@
#include "VirtualTouchpadService.h"
+#include <inttypes.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/PermissionCache.h>
#include <binder/Status.h>
+#include <cutils/log.h>
#include <linux/input.h>
-#include <log/log.h>
+#include <private/android_filesystem_config.h>
#include <utils/Errors.h>
namespace android {
namespace dvr {
-binder::Status VirtualTouchpadService::touch(int touchpad,
- float x, float y, float pressure) {
- const status_t error = touchpad_->Touch(touchpad, x, y, pressure);
- return error ? binder::Status::fromStatusT(error) : binder::Status::ok();
+namespace {
+const String16 kDumpPermission("android.permission.DUMP");
+const String16 kTouchPermission("android.permission.RESTRICTED_VR_ACCESS");
+} // anonymous namespace
+
+VirtualTouchpadService::~VirtualTouchpadService() {
+ if (client_pid_) {
+ client_pid_ = 0;
+ touchpad_->Detach();
+ }
+}
+
+binder::Status VirtualTouchpadService::attach() {
+ pid_t pid;
+ if (!CheckTouchPermission(&pid)) {
+ return binder::Status::fromStatusT(PERMISSION_DENIED);
+ }
+ if (client_pid_ == pid) {
+ // The same client has called attach() twice with no intervening detach().
+ // This indicates a problem with the client, so return an error.
+ // However, since the client is already attached, any touchpad actions
+ // it takes will still work.
+ ALOGE("pid=%ld attached twice", static_cast<long>(pid));
+ return binder::Status::fromStatusT(ALREADY_EXISTS);
+ }
+ if (client_pid_ != 0) {
+ // Attach while another client is attached. This can happen if the client
+ // dies without cleaning up after itself, so move ownership to the current
+ // caller. If two actual clients have connected, the problem will be
+ // reported when the previous client performs any touchpad action.
+ ALOGE("pid=%ld replaces %ld", static_cast<long>(pid),
+ static_cast<long>(client_pid_));
+ }
+ client_pid_ = pid;
+ if (const status_t error = touchpad_->Attach()) {
+ return binder::Status::fromStatusT(error);
+ }
+ return binder::Status::ok();
+}
+
+binder::Status VirtualTouchpadService::detach() {
+ if (!CheckPermissions()) {
+ return binder::Status::fromStatusT(PERMISSION_DENIED);
+ }
+ client_pid_ = 0;
+ if (const status_t error = touchpad_->Detach()) {
+ return binder::Status::fromStatusT(error);
+ }
+ return binder::Status::ok();
+}
+
+binder::Status VirtualTouchpadService::touch(int touchpad, float x, float y,
+ float pressure) {
+ if (!CheckPermissions()) {
+ return binder::Status::fromStatusT(PERMISSION_DENIED);
+ }
+ if (const status_t error = touchpad_->Touch(touchpad, x, y, pressure)) {
+ return binder::Status::fromStatusT(error);
+ }
+ return binder::Status::ok();
}
binder::Status VirtualTouchpadService::buttonState(int touchpad, int buttons) {
- const status_t error = touchpad_->ButtonState(touchpad, buttons);
- return error ? binder::Status::fromStatusT(error) : binder::Status::ok();
+ if (!CheckPermissions()) {
+ return binder::Status::fromStatusT(PERMISSION_DENIED);
+ }
+ if (const status_t error = touchpad_->ButtonState(touchpad, buttons)) {
+ return binder::Status::fromStatusT(error);
+ }
+ return binder::Status::ok();
+}
+
+status_t VirtualTouchpadService::dump(
+ int fd, const Vector<String16>& args[[gnu::unused]]) {
+ String8 result;
+ const android::IPCThreadState* ipc = android::IPCThreadState::self();
+ const pid_t pid = ipc->getCallingPid();
+ const uid_t uid = ipc->getCallingUid();
+ if ((uid != AID_SHELL) &&
+ !PermissionCache::checkPermission(kDumpPermission, pid, uid)) {
+ result.appendFormat("Permission denial: can't dump " LOG_TAG
+ " from pid=%ld, uid=%ld\n",
+ static_cast<long>(pid), static_cast<long>(uid));
+ } else {
+ result.appendFormat("[service]\nclient_pid = %ld\n\n",
+ static_cast<long>(client_pid_));
+ touchpad_->dumpInternal(result);
+ }
+ write(fd, result.string(), result.size());
+ return OK;
+}
+
+bool VirtualTouchpadService::CheckPermissions() {
+ pid_t pid;
+ if (!CheckTouchPermission(&pid)) {
+ return false;
+ }
+ if (client_pid_ != pid) {
+ ALOGE("pid=%ld is not owner", static_cast<long>(pid));
+ return false;
+ }
+ return true;
+}
+
+bool VirtualTouchpadService::CheckTouchPermission(pid_t* out_pid) {
+ const android::IPCThreadState* ipc = android::IPCThreadState::self();
+ *out_pid = ipc->getCallingPid();
+#ifdef SELINUX_ACCESS_CONTROL
+ return true;
+#else
+ const uid_t uid = ipc->getCallingUid();
+ const bool permission = PermissionCache::checkPermission(kTouchPermission, *out_pid, uid);
+ if (!permission) {
+ ALOGE("permission denied to pid=%ld uid=%ld", static_cast<long>(*out_pid),
+ static_cast<long>(uid));
+ }
+ return permission;
+#endif
}
} // namespace dvr