Fix double release of ScopedWakelock

Today, when a ScopedWakelock is moved, the default move constructor
doesn't unlock the old wakelock instance. This results in the moved
ScopedWakelock instance decrementing the wakelock ref count which leaves
the multi-HAL out of sync from the sensor service.

Fix this by adding a custom move constructor / operator to ensure old
state is cleared on the moved instance.

Bug: 163468874
Test: Load multi-HAL and verify that it properly waits to release the
wakelock until the sensor service notifies that it has a lock held.
Test: Run unit tests

Change-Id: Ifd5a3c7596f78d7a756c4472f30efb625d670791
diff --git a/sensors/common/default/2.X/multihal/HalProxy.cpp b/sensors/common/default/2.X/multihal/HalProxy.cpp
index 75ffc17..fe3fc84 100644
--- a/sensors/common/default/2.X/multihal/HalProxy.cpp
+++ b/sensors/common/default/2.X/multihal/HalProxy.cpp
@@ -693,6 +693,10 @@
                                                         int64_t timeoutStart /* = -1 */) {
     if (!mThreadsRun.load()) return;
     std::lock_guard<std::recursive_mutex> lockGuard(mWakelockMutex);
+    if (delta > mWakelockRefCount) {
+        ALOGE("Decrementing wakelock ref count by %zu when count is %zu",
+              delta, mWakelockRefCount);
+    }
     if (timeoutStart == -1) timeoutStart = mWakelockTimeoutResetTime;
     if (mWakelockRefCount == 0 || timeoutStart < mWakelockTimeoutResetTime) return;
     mWakelockRefCount -= std::min(mWakelockRefCount, delta);
diff --git a/sensors/common/default/2.X/multihal/ScopedWakelock.cpp b/sensors/common/default/2.X/multihal/ScopedWakelock.cpp
index bf2ad35..2a2baa2 100644
--- a/sensors/common/default/2.X/multihal/ScopedWakelock.cpp
+++ b/sensors/common/default/2.X/multihal/ScopedWakelock.cpp
@@ -28,6 +28,21 @@
             .count();
 }
 
+ScopedWakelock::ScopedWakelock(ScopedWakelock&& other) {
+    *this = std::move(other);
+}
+
+ScopedWakelock& ScopedWakelock::operator=(ScopedWakelock&& other) {
+    mRefCounter = other.mRefCounter;
+    mCreatedAtTimeNs = other.mCreatedAtTimeNs;
+    mLocked = other.mLocked;
+
+    other.mRefCounter = nullptr;
+    other.mCreatedAtTimeNs = 0;
+    other.mLocked = false;
+    return *this;
+}
+
 ScopedWakelock::ScopedWakelock(IScopedWakelockRefCounter* refCounter, bool locked)
     : mRefCounter(refCounter), mLocked(locked) {
     if (mLocked) {
diff --git a/sensors/common/default/2.X/multihal/include/V2_0/ScopedWakelock.h b/sensors/common/default/2.X/multihal/include/V2_0/ScopedWakelock.h
index 1cc5cd5..b3f398c 100644
--- a/sensors/common/default/2.X/multihal/include/V2_0/ScopedWakelock.h
+++ b/sensors/common/default/2.X/multihal/include/V2_0/ScopedWakelock.h
@@ -81,14 +81,15 @@
  */
 class ScopedWakelock {
   public:
-    ScopedWakelock(ScopedWakelock&&) = default;
-    ScopedWakelock& operator=(ScopedWakelock&&) = default;
+    ScopedWakelock(ScopedWakelock&& other);
+    ScopedWakelock& operator=(ScopedWakelock&& other);
     virtual ~ScopedWakelock();
 
     bool isLocked() const { return mLocked; }
 
   private:
     friend class HalProxyCallbackBase;
+    friend class ScopedWakelockTest;
     IScopedWakelockRefCounter* mRefCounter;
     int64_t mCreatedAtTimeNs;
     bool mLocked;
diff --git a/sensors/common/default/2.X/multihal/tests/Android.bp b/sensors/common/default/2.X/multihal/tests/Android.bp
index a15faed..1b60f4b 100644
--- a/sensors/common/default/2.X/multihal/tests/Android.bp
+++ b/sensors/common/default/2.X/multihal/tests/Android.bp
@@ -90,7 +90,10 @@
 
 cc_test {
     name: "android.hardware.sensors@2.X-halproxy-unit-tests",
-    srcs: ["HalProxy_test.cpp"],
+    srcs: [
+        "HalProxy_test.cpp",
+        "ScopedWakelock_test.cpp",
+    ],
     vendor: true,
     header_libs: [
         "android.hardware.sensors@2.X-shared-utils",
diff --git a/sensors/common/default/2.X/multihal/tests/ScopedWakelock_test.cpp b/sensors/common/default/2.X/multihal/tests/ScopedWakelock_test.cpp
new file mode 100644
index 0000000..133d9e8
--- /dev/null
+++ b/sensors/common/default/2.X/multihal/tests/ScopedWakelock_test.cpp
@@ -0,0 +1,110 @@
+//
+// Copyright (C) 2020 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 <gtest/gtest.h>
+
+#include "V2_0/ScopedWakelock.h"
+
+namespace android {
+namespace hardware {
+namespace sensors {
+namespace V2_0 {
+namespace implementation {
+
+class RefCounter : public IScopedWakelockRefCounter {
+  public:
+    size_t incCount = 0;
+    size_t decCount = 0;
+
+    bool incrementRefCountAndMaybeAcquireWakelock(size_t /* delta */,
+                                                  int64_t* /* timeoutStart */) override {
+        incCount++;
+        return true;
+    }
+
+    void decrementRefCountAndMaybeReleaseWakelock(size_t /* delta */,
+                                                  int64_t /* timeoutStart */) override {
+        decCount++;
+    }
+};
+
+class ScopedWakelockTest : public testing::Test {
+  public:
+    ScopedWakelock createScopedWakelock(bool locked) {
+        return ScopedWakelock(&mRefCounter, locked);
+    }
+
+    RefCounter mRefCounter;
+};
+
+TEST_F(ScopedWakelockTest, UnlockedAfterMoved) {
+    ScopedWakelock wakelock = createScopedWakelock(false /* locked */);
+
+    ScopedWakelock movedWakelock(std::move(wakelock));
+
+    EXPECT_FALSE(wakelock.isLocked());
+    EXPECT_FALSE(movedWakelock.isLocked());
+}
+
+TEST_F(ScopedWakelockTest, LockedAfterMoved) {
+    ScopedWakelock wakelock = createScopedWakelock(true /* locked */);
+
+    ScopedWakelock movedWakelock(std::move(wakelock));
+
+    EXPECT_FALSE(wakelock.isLocked());
+    EXPECT_TRUE(movedWakelock.isLocked());
+}
+
+TEST_F(ScopedWakelockTest, Locked) {
+    ScopedWakelock wakelock = createScopedWakelock(true /* locked */);
+
+    EXPECT_TRUE(wakelock.isLocked());
+}
+
+TEST_F(ScopedWakelockTest, Unlocked) {
+    ScopedWakelock wakelock = createScopedWakelock(false /* locked */);
+
+    EXPECT_FALSE(wakelock.isLocked());
+}
+
+TEST_F(ScopedWakelockTest, ScopedLocked) {
+    { createScopedWakelock(true /* locked */); }
+
+    EXPECT_EQ(mRefCounter.incCount, 1);
+    EXPECT_EQ(mRefCounter.decCount, 1);
+}
+
+TEST_F(ScopedWakelockTest, ScopedUnlockIsNoop) {
+    { createScopedWakelock(false /* locked */); }
+
+    EXPECT_EQ(mRefCounter.incCount, 0);
+    EXPECT_EQ(mRefCounter.decCount, 0);
+}
+
+TEST_F(ScopedWakelockTest, ScopedLockedMove) {
+    {
+        ScopedWakelock wakelock = createScopedWakelock(true /* locked */);
+        ScopedWakelock movedWakelock(std::move(wakelock));
+    }
+
+    EXPECT_EQ(mRefCounter.incCount, 1);
+    EXPECT_EQ(mRefCounter.decCount, 1);
+}
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace sensors
+}  // namespace hardware
+}  // namespace android
\ No newline at end of file