Add tests for HdmiAudioSystemClient

Bug: 110430593
Test: atest android.hardware.hdmi
Change-Id: Id988ed8180b4728e96423d5d0ca38e017b01a97f
diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
index c1606f9d..9bebbd2 100644
--- a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
+++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
@@ -15,9 +15,13 @@
  */
 package android.hardware.hdmi;
 
+import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.RemoteException;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
 /**
  * HdmiAudioSystemClient represents HDMI-CEC logical device of type Audio System in the Android
  * system which acts as an audio system device such as sound bar.
@@ -28,12 +32,11 @@
  * @hide
  */
 public final class HdmiAudioSystemClient extends HdmiClient {
-    // TODO(b/110430593): add tests for this class
     private static final String TAG = "HdmiAudioSystemClient";
 
     private static final int REPORT_AUDIO_STATUS_INTERVAL_MS = 500;
 
-    private final Handler mHandler = new Handler();
+    private final Handler mHandler;
     private boolean mCanSendAudioStatus = true;
     private boolean mPendingReportAudioStatus;
 
@@ -41,9 +44,15 @@
     private int mLastMaxVolume;
     private boolean mLastIsMute;
 
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public HdmiAudioSystemClient(IHdmiControlService service) {
+        this(service, null);
+    }
 
-    /* package */ HdmiAudioSystemClient(IHdmiControlService service) {
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public HdmiAudioSystemClient(IHdmiControlService service, @Nullable Handler handler) {
         super(service);
+        mHandler = handler == null ? new Handler() : handler;
     }
 
     /** @hide */
diff --git a/core/tests/hdmitests/Android.mk b/core/tests/hdmitests/Android.mk
new file mode 100644
index 0000000..e0d2c09
--- /dev/null
+++ b/core/tests/hdmitests/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2018 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test frameworks-base-testutils
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_PACKAGE_NAME := HdmiCecTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/hdmitests/AndroidManifest.xml b/core/tests/hdmitests/AndroidManifest.xml
new file mode 100644
index 0000000..1460b41
--- /dev/null
+++ b/core/tests/hdmitests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.hardware.hdmi"
+    android:sharedUserId="android.uid.system" >
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.hardware.hdmi"
+        android:label="HDMI CEC Tests"/>
+
+</manifest>
\ No newline at end of file
diff --git a/core/tests/hdmitests/AndroidTest.xml b/core/tests/hdmitests/AndroidTest.xml
new file mode 100644
index 0000000..7ef672d
--- /dev/null
+++ b/core/tests/hdmitests/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<configuration description="Runs HDMI CEC Tests.">
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-suite-tag" value="apct-instrumentation"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="HdmiCecTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="HdmiTests"/>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.hardware.hdmi" />
+        <option name="hidden-api-checks" value="false"/>
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
new file mode 100644
index 0000000..7b76a08
--- /dev/null
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package android.hardware.hdmi;
+
+import android.os.Handler;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import android.util.Log;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link HdmiAudioSystemClient}
+ */
+@RunWith(JUnit4.class)
+@SmallTest
+public class HdmiAudioSystemClientTest {
+    private static final String TAG = "HdmiAudioSystemClientTe";
+
+    private HdmiAudioSystemClient mHdmiAudioSystemClient;
+    private TestLooper mTestLooper;
+    private int mVolume;
+    private int mMaxVolume;
+    private boolean mIsMute;
+
+    @Before
+    public void before() {
+        Log.d(TAG, "before()");
+        IHdmiControlService mService = new TestHdmiControlService();
+        mTestLooper = new TestLooper();
+        mHdmiAudioSystemClient =
+                new HdmiAudioSystemClient(mService, new Handler(mTestLooper.getLooper()));
+        resetVariables();
+    }
+
+    @Test
+    public void testSingleCommand() {
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 50, 100, false);
+        assertAudioStatus(50, 100, false);
+    }
+
+    @Test
+    public void testMultipleCommands_longTimeBetweenCalls() {
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 50, 100, false);
+        assertAudioStatus(50, 100, false);
+        mTestLooper.moveTimeForward(500);
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 60, 100, false);
+        assertAudioStatus(60, 100, false);
+    }
+
+    @Test
+    public void testMultipleCommands_shortTimeBetweenCalls() {
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 1, 100, false);
+        assertAudioStatus(1, 100, false);
+
+        mTestLooper.moveTimeForward(100); // current time: 100ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 10, 100, false);
+        assertAudioStatus(1, 100, false); // command not sent, no change
+
+        mTestLooper.moveTimeForward(100); // current time: 200ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 20, 100, false);
+        assertAudioStatus(1, 100, false); // command not sent, no change
+
+        mTestLooper.moveTimeForward(300); // current time: 500ms
+        mTestLooper.dispatchAll();
+        assertAudioStatus(20, 100, false); // pending command sent, changed to 20
+
+        mTestLooper.moveTimeForward(100); // current time: 600ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 60, 100, false);
+        assertAudioStatus(20, 100, false); // command not sent, no change
+
+        mTestLooper.moveTimeForward(200); // current time: 800ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 80, 100, false);
+        assertAudioStatus(20, 100, false); // command not sent, no change
+
+        mTestLooper.moveTimeForward(200); // current time: 1000ms
+        mTestLooper.dispatchAll();
+        assertAudioStatus(80, 100, false); // command sent, changed to 80
+    }
+
+    @Test
+    public void testMultipleCommands_shortTimeAndReturn() {
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 1, 100, false);
+        assertAudioStatus(1, 100, false);
+
+        mTestLooper.moveTimeForward(100); // current time: 100ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 20, 100, false);
+        assertAudioStatus(1, 100, false); // command not sent, no change
+
+        mTestLooper.moveTimeForward(100); // current time: 200ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 1, 100, false);
+        assertAudioStatus(1, 100, false); // command not sent, no change
+
+        mTestLooper.moveTimeForward(300); // current time: 500ms
+        mTestLooper.dispatchAll();
+        assertAudioStatus(1, 100, false); // pending command sent
+    }
+
+    @Test
+    public void testMultipleCommands_muteAdjust() {
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 1, 100, false);
+        assertAudioStatus(1, 100, false);
+
+        mTestLooper.moveTimeForward(100); // current time: 100ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(true, 10, 100, true);
+        assertAudioStatus(10, 100, true); // mute adjust, command sent, changed to 10
+
+        mTestLooper.moveTimeForward(100); // current time: 200ms
+        mTestLooper.dispatchAll();
+        mHdmiAudioSystemClient.sendReportAudioStatusCecCommand(false, 20, 100, false);
+        assertAudioStatus(10, 100, true); // command not sent, no change
+
+        mTestLooper.moveTimeForward(300); // current time: 500ms
+        mTestLooper.dispatchAll();
+        assertAudioStatus(20, 100, false); // pending command sent, changed to 20, unmuted
+    }
+
+    private void assertAudioStatus(int volume, int maxVolume, boolean isMute) {
+        Assert.assertEquals(volume, mVolume);
+        Assert.assertEquals(maxVolume, mMaxVolume);
+        Assert.assertEquals(isMute, mIsMute);
+    }
+
+    private void resetVariables() {
+        mVolume = -1;
+        mMaxVolume = -1;
+        mIsMute = true;
+    }
+
+    private final class TestHdmiControlService extends IHdmiControlService.Stub {
+
+        @Override
+        public int[] getSupportedTypes() {
+            return null;
+        }
+
+        @Override
+        public HdmiDeviceInfo getActiveSource() {
+            return null;
+        }
+
+        @Override
+        public void deviceSelect(final int deviceId, final IHdmiControlCallback callback) {
+        }
+
+        @Override
+        public void portSelect(final int portId, final IHdmiControlCallback callback) {
+        }
+
+        @Override
+        public void sendKeyEvent(final int deviceType, final int keyCode, final boolean isPressed) {
+        }
+
+        @Override
+        public void oneTouchPlay(final IHdmiControlCallback callback) {
+        }
+
+        @Override
+        public void queryDisplayStatus(final IHdmiControlCallback callback) {
+        }
+
+        @Override
+        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
+        }
+
+        @Override
+        public void removeHotplugEventListener(final IHdmiHotplugEventListener listener) {
+        }
+
+        @Override
+        public void addDeviceEventListener(final IHdmiDeviceEventListener listener) {
+        }
+
+        @Override
+        public List<HdmiPortInfo> getPortInfo() {
+            return null;
+        }
+
+        @Override
+        public boolean canChangeSystemAudioMode() {
+            return false;
+        }
+
+        @Override
+        public boolean getSystemAudioMode() {
+            return false;
+        }
+
+        @Override
+        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
+        }
+
+        @Override
+        public void addSystemAudioModeChangeListener(
+                final IHdmiSystemAudioModeChangeListener listener) {
+        }
+
+        @Override
+        public void removeSystemAudioModeChangeListener(
+                final IHdmiSystemAudioModeChangeListener listener) {
+        }
+
+        @Override
+        public void setInputChangeListener(final IHdmiInputChangeListener listener) {
+        }
+
+        @Override
+        public List<HdmiDeviceInfo> getInputDevices() {
+            return null;
+        }
+
+        // Returns all the CEC devices on the bus including system audio, switch,
+        // even those of reserved type.
+        @Override
+        public List<HdmiDeviceInfo> getDeviceList() {
+            return null;
+        }
+
+        @Override
+        public void setSystemAudioVolume(final int oldIndex, final int newIndex,
+                final int maxIndex) {
+        }
+
+        @Override
+        public void setSystemAudioMute(final boolean mute) {
+        }
+
+        @Override
+        public void setArcMode(final boolean enabled) {
+        }
+
+        @Override
+        public void setProhibitMode(final boolean enabled) {
+        }
+
+        @Override
+        public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
+                final int deviceType) {
+        }
+
+        @Override
+        public void sendVendorCommand(final int deviceType, final int targetAddress,
+                final byte[] params, final boolean hasVendorId) {
+        }
+
+        @Override
+        public void sendStandby(final int deviceType, final int deviceId) {
+        }
+
+        @Override
+        public void setHdmiRecordListener(IHdmiRecordListener listener) {
+        }
+
+        @Override
+        public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) {
+        }
+
+        @Override
+        public void stopOneTouchRecord(final int recorderAddress) {
+        }
+
+        @Override
+        public void startTimerRecording(final int recorderAddress, final int sourceType,
+                final byte[] recordSource) {
+        }
+
+        @Override
+        public void clearTimerRecording(final int recorderAddress, final int sourceType,
+                final byte[] recordSource) {
+        }
+
+        @Override
+        public void sendMhlVendorCommand(final int portId, final int offset, final int length,
+                final byte[] data) {
+        }
+
+        @Override
+        public void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener) {
+        }
+
+        @Override
+        public void setStandbyMode(final boolean isStandbyModeOn) {
+        }
+
+        @Override
+        public void reportAudioStatus(final int deviceType, final int volume, final int maxVolume,
+                final boolean isMute) {
+            mVolume = volume;
+            mMaxVolume = maxVolume;
+            mIsMute = isMute;
+        }
+    }
+
+}