From 1d0b1377d85c43f45772064880f27d54c417e183 Mon Sep 17 00:00:00 2001 From: Amy Date: Thu, 24 May 2018 14:36:25 -0700 Subject: [PATCH] HdmiCecLocalDeviceTest Test: atest com.android.server.hdmi Change-Id: Ic051138202531871b6227d57ef35038d0971e9e5 --- .../android/server/hdmi/HdmiCecLocalDevice.java | 14 +- .../server/hdmi/HdmiCecMessageValidator.java | 2 +- .../android/server/hdmi/HdmiControlService.java | 35 +++- .../server/hdmi/HdmiCecLocalDeviceTest.java | 221 +++++++++++++++++++++ 4 files changed, 261 insertions(+), 11 deletions(-) create mode 100644 services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 0cba76ba7346..56813679a0fb 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -16,6 +16,7 @@ package com.android.server.hdmi; +import android.annotation.Nullable; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.input.InputManager; import android.os.Handler; @@ -31,6 +32,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; +import com.android.server.hdmi.HdmiControlService.SendMessageCallback; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -249,11 +251,11 @@ abstract class HdmiCecLocalDevice { case Constants.MESSAGE_SET_MENU_LANGUAGE: return handleSetMenuLanguage(message); case Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS: - return handleGivePhysicalAddress(); + return handleGivePhysicalAddress(null); case Constants.MESSAGE_GIVE_OSD_NAME: return handleGiveOsdName(message); case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID: - return handleGiveDeviceVendorId(); + return handleGiveDeviceVendorId(null); case Constants.MESSAGE_GET_CEC_VERSION: return handleGetCecVersion(message); case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS: @@ -325,23 +327,23 @@ abstract class HdmiCecLocalDevice { } @ServiceThreadOnly - protected boolean handleGivePhysicalAddress() { + protected boolean handleGivePhysicalAddress(@Nullable SendMessageCallback callback) { assertRunOnServiceThread(); int physicalAddress = mService.getPhysicalAddress(); HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( mAddress, physicalAddress, mDeviceType); - mService.sendCecCommand(cecMessage); + mService.sendCecCommand(cecMessage, callback); return true; } @ServiceThreadOnly - protected boolean handleGiveDeviceVendorId() { + protected boolean handleGiveDeviceVendorId(@Nullable SendMessageCallback callback) { assertRunOnServiceThread(); int vendorId = mService.getVendorId(); HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand( mAddress, vendorId); - mService.sendCecCommand(cecMessage); + mService.sendCecCommand(cecMessage, callback); return true; } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java index 8c00be5f25b4..4ad51de2e25b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java @@ -22,7 +22,7 @@ import android.util.SparseArray; /** * A helper class to validates {@link HdmiCecMessage}. */ -public final class HdmiCecMessageValidator { +public class HdmiCecMessageValidator { private static final String TAG = "HdmiCecMessageValidator"; static final int OK = 0; diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index ba6da05cfe08..a1753e58c4ea 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -68,6 +68,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.SystemService; @@ -290,6 +291,9 @@ public class HdmiControlService extends SystemService { @Nullable private PowerManager mPowerManager; + @Nullable + private Looper mIoLooper; + // Last input port before switching to the MHL port. Should switch back to this port // when the mobile device sends the request one touch play with off. // Gets invalidated if we go to other port/input. @@ -383,13 +387,18 @@ public class HdmiControlService extends SystemService { @Override public void onStart() { - mIoThread.start(); + if (mIoLooper == null) { + mIoThread.start(); + mIoLooper = mIoThread.getLooper(); + } mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; mProhibitMode = false; mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true); mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true); - mCecController = HdmiCecController.create(this); + if (mCecController == null) { + mCecController = HdmiCecController.create(this); + } if (mCecController != null) { if (mHdmiControlEnabled) { initializeCec(INITIATED_BY_BOOT_UP); @@ -406,7 +415,9 @@ public class HdmiControlService extends SystemService { mMhlDevices = Collections.emptyList(); initPortInfo(); - mMessageValidator = new HdmiCecMessageValidator(this); + if (mMessageValidator == null) { + mMessageValidator = new HdmiCecMessageValidator(this); + } publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService()); if (mCecController != null) { @@ -424,6 +435,11 @@ public class HdmiControlService extends SystemService { mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED); } + @VisibleForTesting + void setCecController(HdmiCecController cecController) { + mCecController = cecController; + } + @Override public void onBootPhase(int phase) { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { @@ -747,8 +763,19 @@ public class HdmiControlService extends SystemService { * *

Declared as package-private. */ + @Nullable Looper getIoLooper() { - return mIoThread.getLooper(); + return mIoLooper; + } + + @VisibleForTesting + void setIoLooper(Looper ioLooper) { + mIoLooper = ioLooper; + } + + @VisibleForTesting + void setMessageValidator(HdmiCecMessageValidator messageValidator) { + mMessageValidator = messageValidator; } /** diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java new file mode 100644 index 000000000000..78cb56baf86e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java @@ -0,0 +1,221 @@ +/* + * 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 com.android.server.hdmi; + +import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV; +import static com.android.server.hdmi.Constants.ADDR_BROADCAST; +import static com.android.server.hdmi.Constants.ADDR_TV; +import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED; +import static com.android.server.hdmi.Constants.MESSAGE_DEVICE_VENDOR_ID; +import static com.android.server.hdmi.Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import android.hardware.hdmi.HdmiPortInfo; +import android.os.test.TestLooper; +import android.support.test.filters.SmallTest; +import android.os.MessageQueue; +import com.android.server.hdmi.HdmiCecController.NativeWrapper; +import junit.framework.Assert; +import java.util.Arrays; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@SmallTest +@RunWith(JUnit4.class) +/** + * Tests for {@link HdmiCecLocalDevice} class. + */ +public class HdmiCecLocalDeviceTest { + + private static final class NativeWrapperImpl implements NativeWrapper { + + @Override + public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) { + return 1L; + } + + @Override + public int nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress, + byte[] body) { + return SendCecCommandFactory(srcAddress, dstAddress, body); + } + + @Override + public int nativeAddLogicalAddress(long controllerPtr, int logicalAddress) { + return 0; + } + + @Override + public void nativeClearLogicalAddress(long controllerPtr) { + + } + + @Override + public int nativeGetPhysicalAddress(long controllerPtr) { + return mPhysicalAddr; + } + + @Override + public int nativeGetVersion(long controllerPtr) { + return 0; + } + + @Override + public int nativeGetVendorId(long controllerPtr) { + return 0; + } + + @Override + public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) { + return new HdmiPortInfo[0]; + } + + @Override + public void nativeSetOption(long controllerPtr, int flag, boolean enabled) { + + } + + @Override + public void nativeSetLanguage(long controllerPtr, String language) { + + } + + @Override + public void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag) { + + } + + @Override + public boolean nativeIsConnected(long controllerPtr, int port) { + return false; + } + } + + private static int SendCecCommandFactory(int srcAddress, int dstAddress, byte[] body) { + switch(body[0] & 0xFF) { + /** {@link Constants#MESSAGE_GIVE_PHYSICAL_ADDRESS} */ + case MESSAGE_REPORT_PHYSICAL_ADDRESS: + case MESSAGE_DEVICE_VENDOR_ID: + return srcAddress == mSrcAddr && + dstAddress == mDesAddr && + Arrays.equals(Arrays.copyOfRange(body, 1, body.length), param)? 0 : 1; + default: + return 1; + } + } + + private class MyHdmiCecLocalDevice extends HdmiCecLocalDevice { + + + protected MyHdmiCecLocalDevice(HdmiControlService service, int deviceType) { + super(service, deviceType); + } + + @Override + protected void onAddressAllocated(int logicalAddress, int reason) { + + } + + @Override + protected int getPreferredAddress() { + return 0; + } + + @Override + protected void setPreferredAddress(int addr) { + + } + } + + private MyHdmiCecLocalDevice mHdmiLocalDevice; + private HdmiControlService mHdmiControlService; + private HdmiCecController mHdmiCecController; + private TestLooper mTestLooper = new TestLooper(); + private static int mDesAddr = -1; + private static int mSrcAddr = -1; + private static int mPhysicalAddr = 2; + private int callbackResult; + private HdmiCecMessageValidator mMessageValidator; + private static byte[] param; + + @Before + public void SetUp() { + mHdmiControlService = new HdmiControlService(null); + mHdmiControlService.setIoLooper(mTestLooper.getLooper()); + mHdmiCecController = HdmiCecController.createWithNativeWrapper( + mHdmiControlService, new NativeWrapperImpl()); + mHdmiControlService.setCecController(mHdmiCecController); + mHdmiLocalDevice = new MyHdmiCecLocalDevice( + mHdmiControlService, DEVICE_TV); + mMessageValidator = new HdmiCecMessageValidator(mHdmiControlService){ + @Override + int isValid(HdmiCecMessage message) { + return HdmiCecMessageValidator.OK; + } + }; + mHdmiControlService.setMessageValidator(mMessageValidator); + } + + @Test + public void dispatchMessage_desNotValid() { + HdmiCecMessage msg = new HdmiCecMessage( + ADDR_TV, ADDR_TV, Constants.MESSAGE_CEC_VERSION, HdmiCecMessage.EMPTY_PARAM); + boolean handleResult = mHdmiLocalDevice.dispatchMessage(msg); + assertFalse(handleResult); + } + + @Test + public void handleGivePhysicalAddress_success() { + mSrcAddr = ADDR_UNREGISTERED; + mDesAddr = ADDR_BROADCAST; + param = new byte[] { + (byte) ((mPhysicalAddr >> 8) & 0xFF), + (byte) (mPhysicalAddr & 0xFF), + (byte) (DEVICE_TV & 0xFF) + }; + callbackResult = -1; + boolean handleResult = mHdmiLocalDevice.handleGivePhysicalAddress( + (int finalResult) -> callbackResult = finalResult); + mTestLooper.dispatchAll(); + /** + * Test if CecMessage is sent successfully + * SendMessageResult#SUCCESS is defined in HAL as 0 + */ + assertEquals(0, callbackResult); + assertTrue(handleResult); + } + + @Test + public void handleGiveDeviceVendorId_success() { + mSrcAddr = ADDR_UNREGISTERED; + mDesAddr = ADDR_BROADCAST; + /** nativeGetVendorId returns 0 */ + param = new byte[] { + (byte) ((0 >> 8) & 0xFF), + (byte) (0 & 0xFF), + (byte) (0 & 0xFF) + }; + callbackResult = -1; + mHdmiLocalDevice.handleGiveDeviceVendorId( + (int finalResult) -> callbackResult = finalResult); + mTestLooper.dispatchAll(); + assertEquals(0, callbackResult); + } +} -- 2.11.0