Merge "SurfaceFlinger: Missed changes in HWC1. Fix build error."
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index d1e94ed..302a947 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -93,10 +93,6 @@
# ==========#
include $(CLEAR_VARS)
-ifdef BOARD_WLAN_DEVICE
-LOCAL_CFLAGS := -DFWDUMP_$(BOARD_WLAN_DEVICE)
-endif
-
LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
DumpstateService.cpp \
dumpstate.cpp
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 9434d56..4812de5 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1037,26 +1037,11 @@
RunCommand("WIFI NETWORKS", {"wpa_cli", "IFNAME=wlan0", "list_networks"},
CommandOptions::WithTimeout(20).Build());
-#ifdef FWDUMP_bcmdhd
- RunCommand("ND OFFLOAD TABLE", {WLUTIL, "nd_hostip"}, CommandOptions::AS_ROOT);
-
- RunCommand("DUMP WIFI INTERNAL COUNTERS (1)", {WLUTIL, "counters"}, AS_ROOT_20);
-
- RunCommand("ND OFFLOAD STATUS (1)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT);
-
-#endif
DumpFile("INTERRUPTS (1)", "/proc/interrupts");
RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
CommandOptions::WithTimeout(10).Build());
-#ifdef FWDUMP_bcmdhd
- RunCommand("DUMP WIFI STATUS", {"dhdutil", "-i", "wlan0", "dump"}, AS_ROOT_20);
-
- RunCommand("DUMP WIFI INTERNAL COUNTERS (2)", {WLUTIL, "counters"}, AS_ROOT_20);
-
- RunCommand("ND OFFLOAD STATUS (2)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT);
-#endif
DumpFile("INTERRUPTS (2)", "/proc/interrupts");
RunCommand("SYSTEM PROPERTIES", {"getprop"});
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index dc8e675..5431233 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -34,3 +34,15 @@
shared_libs: ["libcutils", "libselinux"],
init_rc: ["servicemanager.rc"],
}
+
+cc_binary {
+ name: "vndservicemanager",
+ defaults: ["servicemanager_flags"],
+ proprietary: true,
+ srcs: [
+ "service_manager.c",
+ "binder.c",
+ ],
+ shared_libs: ["libcutils", "libselinux"],
+ init_rc: ["vndservicemanager.rc"],
+}
diff --git a/cmds/servicemanager/bctest.c b/cmds/servicemanager/bctest.c
index 6466654..354df67 100644
--- a/cmds/servicemanager/bctest.c
+++ b/cmds/servicemanager/bctest.c
@@ -62,7 +62,7 @@
uint32_t svcmgr = BINDER_SERVICE_MANAGER;
uint32_t handle;
- bs = binder_open(128*1024);
+ bs = binder_open("/dev/binder", 128*1024);
if (!bs) {
fprintf(stderr, "failed to open binder driver\n");
return -1;
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
index 753aeb5..93a18fc 100644
--- a/cmds/servicemanager/binder.c
+++ b/cmds/servicemanager/binder.c
@@ -94,7 +94,7 @@
size_t mapsize;
};
-struct binder_state *binder_open(size_t mapsize)
+struct binder_state *binder_open(const char* driver, size_t mapsize)
{
struct binder_state *bs;
struct binder_version vers;
@@ -105,10 +105,10 @@
return NULL;
}
- bs->fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
+ bs->fd = open(driver, O_RDWR | O_CLOEXEC);
if (bs->fd < 0) {
- fprintf(stderr,"binder: cannot open device (%s)\n",
- strerror(errno));
+ fprintf(stderr,"binder: cannot open %s (%s)\n",
+ driver, strerror(errno));
goto fail_open;
}
diff --git a/cmds/servicemanager/binder.h b/cmds/servicemanager/binder.h
index 881ab07..c95b33f 100644
--- a/cmds/servicemanager/binder.h
+++ b/cmds/servicemanager/binder.h
@@ -46,7 +46,7 @@
struct binder_io *msg,
struct binder_io *reply);
-struct binder_state *binder_open(size_t mapsize);
+struct binder_state *binder_open(const char* driver, size_t mapsize);
void binder_close(struct binder_state *bs);
/* initiate a blocking binder call
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 43c4c8b..5d44e87 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -360,14 +360,21 @@
return 0;
}
-int main()
+int main(int argc, char** argv)
{
struct binder_state *bs;
union selinux_callback cb;
+ char *driver;
- bs = binder_open(128*1024);
+ if (argc > 1) {
+ driver = argv[1];
+ } else {
+ driver = "/dev/binder";
+ }
+
+ bs = binder_open(driver, 128*1024);
if (!bs) {
- ALOGE("failed to open binder driver\n");
+ ALOGE("failed to open binder driver %s\n", driver);
return -1;
}
diff --git a/cmds/servicemanager/vndservicemanager.rc b/cmds/servicemanager/vndservicemanager.rc
new file mode 100644
index 0000000..d5ddaaf
--- /dev/null
+++ b/cmds/servicemanager/vndservicemanager.rc
@@ -0,0 +1,6 @@
+service vndservicemanager /vendor/bin/vndservicemanager /dev/vndbinder
+ class core
+ user system
+ group system readproc
+ writepid /dev/cpuset/system-background/tasks
+
diff --git a/include/batteryservice/IBatteryPropertiesRegistrar.h b/include/batteryservice/IBatteryPropertiesRegistrar.h
index b5c3a4d..a7dbea6 100644
--- a/include/batteryservice/IBatteryPropertiesRegistrar.h
+++ b/include/batteryservice/IBatteryPropertiesRegistrar.h
@@ -27,6 +27,7 @@
REGISTER_LISTENER = IBinder::FIRST_CALL_TRANSACTION,
UNREGISTER_LISTENER,
GET_PROPERTY,
+ SCHEDULE_UPDATE,
};
class IBatteryPropertiesRegistrar : public IInterface {
@@ -36,6 +37,7 @@
virtual void registerListener(const sp<IBatteryPropertiesListener>& listener) = 0;
virtual void unregisterListener(const sp<IBatteryPropertiesListener>& listener) = 0;
virtual status_t getProperty(int id, struct BatteryProperty *val) = 0;
+ virtual void scheduleUpdate() = 0;
};
class BnBatteryPropertiesRegistrar : public BnInterface<IBatteryPropertiesRegistrar> {
diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h
index 64cf72e..05e9d09 100644
--- a/include/binder/ProcessState.h
+++ b/include/binder/ProcessState.h
@@ -35,6 +35,11 @@
{
public:
static sp<ProcessState> self();
+ /* initWithDriver() can be used to configure libbinder to use
+ * a different binder driver dev node. It must be called *before*
+ * any call to ProcessState::self(). /dev/binder remains the default.
+ */
+ static sp<ProcessState> initWithDriver(const char *driver);
void setContextObject(const sp<IBinder>& object);
sp<IBinder> getContextObject(const sp<IBinder>& caller);
@@ -67,7 +72,7 @@
private:
friend class IPCThreadState;
- ProcessState();
+ ProcessState(const char* driver);
~ProcessState();
ProcessState(const ProcessState& o);
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 98107c5..5c4cfe2 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -71,7 +71,17 @@
if (gProcess != NULL) {
return gProcess;
}
- gProcess = new ProcessState;
+ gProcess = new ProcessState("/dev/binder");
+ return gProcess;
+}
+
+sp<ProcessState> ProcessState::initWithDriver(const char* driver)
+{
+ Mutex::Autolock _l(gProcessMutex);
+ if (gProcess != NULL) {
+ LOG_ALWAYS_FATAL("ProcessState was already initialized.");
+ }
+ gProcess = new ProcessState(driver);
return gProcess;
}
@@ -307,9 +317,9 @@
androidSetThreadName( makeBinderThreadName().string() );
}
-static int open_driver()
+static int open_driver(const char *driver)
{
- int fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
+ int fd = open(driver, O_RDWR | O_CLOEXEC);
if (fd >= 0) {
int vers = 0;
status_t result = ioctl(fd, BINDER_VERSION, &vers);
@@ -330,13 +340,13 @@
ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
} else {
- ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
+ ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
}
return fd;
}
-ProcessState::ProcessState()
- : mDriverFD(open_driver())
+ProcessState::ProcessState(const char *driver)
+ : mDriverFD(open_driver(driver))
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 9e3fecb..d653db8 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -261,6 +261,13 @@
for (auto& b : mQueue) {
b.mIsStale = true;
+
+ // We set this to false to force the BufferQueue to resend the buffer
+ // handle upon acquire, since if we're here due to a producer
+ // disconnect, the consumer will have been told to purge its cache of
+ // slot-to-buffer-handle mappings and will not be able to otherwise
+ // obtain a valid buffer handle.
+ b.mAcquireCalled = false;
}
VALIDATE_CONSISTENCY();
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 91ce531..907e0493 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -1117,4 +1117,64 @@
ASSERT_EQ(true, output.bufferReplaced);
}
+TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ sp<IProducerListener> dummyListener(new DummyProducerListener);
+ ASSERT_EQ(OK, mProducer->connect(dummyListener, NATIVE_WINDOW_API_CPU,
+ true, &output));
+
+ int slot = BufferQueue::INVALID_BUFFER_SLOT;
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+ IGraphicBufferProducer::QueueBufferInput input(0ull, true,
+ HAL_DATASPACE_UNKNOWN, Rect::INVALID_RECT,
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+
+ // Dequeue, request, and queue one buffer
+ status_t result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0,
+ nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ // Acquire and release the buffer. Upon acquiring, the buffer handle should
+ // be non-null since this is the first time we've acquired this slot.
+ BufferItem item;
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(slot, item.mSlot);
+ ASSERT_NE(nullptr, item.mGraphicBuffer.get());
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // Dequeue and queue the buffer again
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ // Acquire and release the buffer again. Upon acquiring, the buffer handle
+ // should be null since this is not the first time we've acquired this slot.
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(slot, item.mSlot);
+ ASSERT_EQ(nullptr, item.mGraphicBuffer.get());
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+
+ // Dequeue and queue the buffer again
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0, nullptr));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
+
+ // Disconnect the producer end. This should clear all of the slots and mark
+ // the buffer in the queue as stale.
+ ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU));
+
+ // Acquire the buffer again. Upon acquiring, the buffer handle should not be
+ // null since the queued buffer should have been marked as stale, which
+ // should trigger the BufferQueue to resend the buffer handle.
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(slot, item.mSlot);
+ ASSERT_NE(nullptr, item.mGraphicBuffer.get());
+}
+
} // namespace android
diff --git a/services/batteryservice/IBatteryPropertiesRegistrar.cpp b/services/batteryservice/IBatteryPropertiesRegistrar.cpp
index 1fdda43..01a65ae 100644
--- a/services/batteryservice/IBatteryPropertiesRegistrar.cpp
+++ b/services/batteryservice/IBatteryPropertiesRegistrar.cpp
@@ -60,6 +60,12 @@
val->readFromParcel(&reply);
return ret;
}
+
+ void scheduleUpdate() {
+ Parcel data;
+ data.writeInterfaceToken(IBatteryPropertiesRegistrar::getInterfaceDescriptor());
+ remote()->transact(SCHEDULE_UPDATE, data, NULL);
+ }
};
IMPLEMENT_META_INTERFACE(BatteryPropertiesRegistrar, "android.os.IBatteryPropertiesRegistrar");
@@ -97,6 +103,12 @@
val.writeToParcel(reply);
return OK;
}
+
+ case SCHEDULE_UPDATE: {
+ CHECK_INTERFACE(IBatteryPropertiesRegistrar, data, reply);
+ scheduleUpdate();
+ return OK;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
};
diff --git a/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl b/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl
index b5dbb8b..67fd927 100644
--- a/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl
+++ b/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl
@@ -24,5 +24,6 @@
void enterVrMode() = 2;
void exitVrMode() = 3;
void setDebugMode(int mode) = 4;
+ void set2DMode(int mode) = 5;
}
diff --git a/services/vr/vr_window_manager/application.cpp b/services/vr/vr_window_manager/application.cpp
index 3dfd9f1..7c61076 100644
--- a/services/vr/vr_window_manager/application.cpp
+++ b/services/vr/vr_window_manager/application.cpp
@@ -274,15 +274,13 @@
}
controller_data_provider_->UnlockControllerData();
if (shmem_controller_active_) {
- // TODO(kpschoedel): change to ALOGV or remove.
- ALOGI("Controller shmem orientation: %f %f %f %f",
+ ALOGV("Controller shmem orientation: %f %f %f %f",
controller_orientation_.x(), controller_orientation_.y(),
controller_orientation_.z(), controller_orientation_.w());
if (shmem_controller_buttons_) {
- ALOGI("Controller shmem buttons: %017" PRIX64,
+ ALOGV("Controller shmem buttons: %017" PRIX64,
shmem_controller_buttons_);
}
- return;
}
}
}
diff --git a/services/vr/vr_window_manager/display_view.cpp b/services/vr/vr_window_manager/display_view.cpp
index 5f1e73e..8a1c84d 100644
--- a/services/vr/vr_window_manager/display_view.cpp
+++ b/services/vr/vr_window_manager/display_view.cpp
@@ -8,7 +8,6 @@
namespace {
constexpr float kLayerScaleFactor = 3.0f;
-constexpr unsigned int kVRAppLayerCount = 2;
constexpr unsigned int kMaximumPendingFrames = 8;
// clang-format off
@@ -99,7 +98,7 @@
// Determine if ths frame should be shown or hidden.
ViewMode CalculateVisibilityFromLayerConfig(const HwcCallback::Frame& frame,
- uint32_t vr_app) {
+ uint32_t *appid) {
auto& layers = frame.layers();
// TODO(achaulk): Figure out how to identify the current VR app for 2D app
@@ -120,6 +119,11 @@
return ViewMode::Hidden;
}
+ if(layers[index].appid != *appid) {
+ *appid = layers[index].appid;
+ return ViewMode::App;
+ }
+
// This is the VR app, ignore it.
index++;
@@ -136,6 +140,7 @@
if (!layers[i].should_skip_layer())
return ViewMode::VR;
}
+
return ViewMode::Hidden;
}
@@ -198,16 +203,25 @@
base::unique_fd DisplayView::OnFrame(std::unique_ptr<HwcCallback::Frame> frame,
bool debug_mode, bool* showing) {
- ViewMode visibility =
- CalculateVisibilityFromLayerConfig(*frame.get(), current_vr_app_);
+ uint32_t app = current_vr_app_;
+ ViewMode visibility = CalculateVisibilityFromLayerConfig(*frame.get(), &app);
if (visibility == ViewMode::Hidden && debug_mode)
visibility = ViewMode::VR;
- if (frame->layers().empty())
+ if (frame->layers().empty()) {
current_vr_app_ = 0;
- else
- current_vr_app_ = frame->layers().front().appid;
+ } else if (visibility == ViewMode::App) {
+ // This is either a VR app switch or a 2D app launching.
+ // If we can have VR apps, update if it's 0.
+ if (!always_2d_ && (current_vr_app_ == 0 || !use_2dmode_)) {
+ visibility = ViewMode::Hidden;
+ current_vr_app_ = app;
+ }
+ } else if (!current_vr_app_) {
+ // The VR app is running.
+ current_vr_app_ = app;
+ }
pending_frames_.emplace_back(std::move(frame), visibility);
diff --git a/services/vr/vr_window_manager/display_view.h b/services/vr/vr_window_manager/display_view.h
index 0a27781..9483e8b 100644
--- a/services/vr/vr_window_manager/display_view.h
+++ b/services/vr/vr_window_manager/display_view.h
@@ -44,6 +44,9 @@
uint32_t id() const { return id_; }
int touchpad_id() const { return touchpad_id_; }
+ void set_2dmode(bool mode) { use_2dmode_ = mode; }
+ void set_always_2d(bool mode) { always_2d_ = mode; }
+
private:
bool IsHit(const vec3& view_location, const vec3& view_direction,
vec3* hit_location, vec2* hit_location_in_window_coord,
@@ -79,6 +82,8 @@
vec2 ime_top_left_;
vec2 ime_size_;
bool has_ime_ = false;
+ bool use_2dmode_ = false;
+ bool always_2d_ = false;
struct PendingFrame {
PendingFrame() = default;
diff --git a/services/vr/vr_window_manager/shell_view.cpp b/services/vr/vr_window_manager/shell_view.cpp
index a21e883..e17b2ae 100644
--- a/services/vr/vr_window_manager/shell_view.cpp
+++ b/services/vr/vr_window_manager/shell_view.cpp
@@ -16,6 +16,8 @@
namespace {
+constexpr uint32_t kPrimaryDisplayId = 1;
+
const std::string kVertexShader = SHADER0([]() {
layout(location = 0) in vec4 aPosition;
layout(location = 1) in vec4 aTexCoord;
@@ -96,8 +98,8 @@
}
int GetTouchIdForDisplay(uint32_t display) {
- return display == 1 ? DVR_VIRTUAL_TOUCHPAD_PRIMARY
- : DVR_VIRTUAL_TOUCHPAD_VIRTUAL;
+ return display == kPrimaryDisplayId ? DVR_VIRTUAL_TOUCHPAD_PRIMARY
+ : DVR_VIRTUAL_TOUCHPAD_VIRTUAL;
}
} // namespace
@@ -190,6 +192,11 @@
result.append("\n");
}
+void ShellView::Set2DMode(bool mode) {
+ if (!displays_.empty())
+ displays_[0]->set_2dmode(mode);
+}
+
void ShellView::OnDrawFrame() {
bool visible = false;
@@ -253,6 +260,9 @@
}
auto display = new DisplayView(id, GetTouchIdForDisplay(id));
+ // Virtual displays only ever have 2D apps so force it.
+ if (id != kPrimaryDisplayId)
+ display->set_always_2d(true);
new_displays_.emplace_back(display);
return display;
}
diff --git a/services/vr/vr_window_manager/shell_view.h b/services/vr/vr_window_manager/shell_view.h
index 856c8b8..6887e7e 100644
--- a/services/vr/vr_window_manager/shell_view.h
+++ b/services/vr/vr_window_manager/shell_view.h
@@ -32,6 +32,8 @@
void EnableDebug(bool debug) override;
void VrMode(bool mode) override;
void dumpInternal(String8& result) override;
+ void Set2DMode(bool mode) override;
+
protected:
void DrawEye(EyeType eye, const mat4& perspective, const mat4& eye_matrix,
diff --git a/services/vr/vr_window_manager/shell_view_binder_interface.h b/services/vr/vr_window_manager/shell_view_binder_interface.h
index b58e4bd..9f77e5a 100644
--- a/services/vr/vr_window_manager/shell_view_binder_interface.h
+++ b/services/vr/vr_window_manager/shell_view_binder_interface.h
@@ -12,6 +12,7 @@
virtual void EnableDebug(bool debug) = 0;
virtual void VrMode(bool mode) = 0;
virtual void dumpInternal(String8& result) = 0;
+ virtual void Set2DMode(bool mode) = 0;
};
} // namespace dvr
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder.cpp b/services/vr/vr_window_manager/vr_window_manager_binder.cpp
index bd3f3ee..8868588 100644
--- a/services/vr/vr_window_manager/vr_window_manager_binder.cpp
+++ b/services/vr/vr_window_manager/vr_window_manager_binder.cpp
@@ -133,6 +133,11 @@
return binder::Status::ok();
}
+binder::Status VrWindowManagerBinder::set2DMode(int32_t mode) {
+ app_.Set2DMode(static_cast<bool>(mode));
+ return binder::Status::ok();
+}
+
status_t VrWindowManagerBinder::dump(
int fd, const Vector<String16>& args [[gnu::unused]]) {
String8 result;
diff --git a/services/vr/vr_window_manager/vr_window_manager_binder.h b/services/vr/vr_window_manager/vr_window_manager_binder.h
index 99ca27a..1915ffc 100644
--- a/services/vr/vr_window_manager/vr_window_manager_binder.h
+++ b/services/vr/vr_window_manager/vr_window_manager_binder.h
@@ -59,6 +59,7 @@
::android::binder::Status enterVrMode() override;
::android::binder::Status exitVrMode() override;
::android::binder::Status setDebugMode(int32_t mode) override;
+ ::android::binder::Status set2DMode(int32_t mode) override;
// Implements BBinder::dump().
status_t dump(int fd, const Vector<String16>& args) override;
diff --git a/services/vr/vr_window_manager/vr_wm_ctl.cpp b/services/vr/vr_window_manager/vr_wm_ctl.cpp
index c67b2eb..2e5c488 100644
--- a/services/vr/vr_window_manager/vr_wm_ctl.cpp
+++ b/services/vr/vr_window_manager/vr_wm_ctl.cpp
@@ -39,6 +39,8 @@
exit(report(vrwm->exitVrMode()));
} else if ((argc == 3) && (strcmp(argv[1], "debug") == 0)) {
exit(report(vrwm->setDebugMode(atoi(argv[2]))));
+ } else if ((argc == 3) && (strcmp(argv[1], "2d") == 0)) {
+ exit(report(vrwm->set2DMode(atoi(argv[2]))));
} else {
usage();
exit(2);