1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.hdmi;
18 
19 import static android.hardware.hdmi.DeviceFeatures.FEATURE_NOT_SUPPORTED;
20 import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORTED;
21 import static android.hardware.hdmi.DeviceFeatures.FEATURE_SUPPORT_UNKNOWN;
22 
23 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
24 
25 import static com.google.common.truth.Truth.assertThat;
26 
27 import static org.mockito.ArgumentMatchers.anyString;
28 import static org.mockito.Mockito.doNothing;
29 import static org.mockito.Mockito.spy;
30 
31 import android.content.Context;
32 import android.content.ContextWrapper;
33 import android.hardware.hdmi.DeviceFeatures;
34 import android.hardware.hdmi.HdmiControlManager;
35 import android.hardware.hdmi.HdmiDeviceInfo;
36 import android.hardware.hdmi.IHdmiControlCallback;
37 import android.hardware.tv.cec.V1_0.SendMessageResult;
38 import android.os.Looper;
39 import android.os.RemoteException;
40 import android.os.test.TestLooper;
41 import android.platform.test.annotations.Presubmit;
42 
43 import androidx.test.InstrumentationRegistry;
44 import androidx.test.filters.SmallTest;
45 
46 import com.android.server.SystemService;
47 
48 import org.junit.Before;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 import org.junit.runners.JUnit4;
52 
53 import java.util.ArrayList;
54 import java.util.Collections;
55 
56 @SmallTest
57 @Presubmit
58 @RunWith(JUnit4.class)
59 public class SetAudioVolumeLevelDiscoveryActionTest {
60     private HdmiControlService mHdmiControlServiceSpy;
61     private HdmiCecController mHdmiCecController;
62     private HdmiCecLocalDevicePlayback mPlaybackDevice;
63     private FakeNativeWrapper mNativeWrapper;
64     private FakePowerManagerWrapper mPowerManager;
65     private Looper mLooper;
66     private Context mContextSpy;
67     private TestLooper mTestLooper = new TestLooper();
68     private int mPhysicalAddress = 0x1100;
69     private int mPlaybackLogicalAddress;
70 
71     private TestCallback mTestCallback;
72     private SetAudioVolumeLevelDiscoveryAction mAction;
73 
74     /**
75      * Setup: Local Playback device attempts to determine whether a connected TV supports
76      * <Set Audio Volume Level>.
77      */
78     @Before
setUp()79     public void setUp() throws RemoteException {
80         mContextSpy = spy(new ContextWrapper(
81                 InstrumentationRegistry.getInstrumentation().getTargetContext()));
82 
83         FakeAudioFramework audioFramework = new FakeAudioFramework();
84 
85         mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy,
86                 Collections.singletonList(HdmiDeviceInfo.DEVICE_PLAYBACK),
87                 audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
88         doNothing().when(mHdmiControlServiceSpy)
89                 .writeStringSystemProperty(anyString(), anyString());
90 
91         mLooper = mTestLooper.getLooper();
92         mHdmiControlServiceSpy.setIoLooper(mLooper);
93         mHdmiControlServiceSpy.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy));
94         mHdmiControlServiceSpy.setDeviceConfig(new FakeDeviceConfigWrapper());
95 
96         mNativeWrapper = new FakeNativeWrapper();
97         mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
98 
99         mHdmiCecController = HdmiCecController.createWithNativeWrapper(
100                 mHdmiControlServiceSpy, mNativeWrapper, mHdmiControlServiceSpy.getAtomWriter());
101         mHdmiControlServiceSpy.setCecController(mHdmiCecController);
102         mHdmiControlServiceSpy.setHdmiMhlController(
103                 HdmiMhlControllerStub.create(mHdmiControlServiceSpy));
104         mHdmiControlServiceSpy.initService();
105         mHdmiControlServiceSpy.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
106         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
107         mHdmiControlServiceSpy.setPowerManager(mPowerManager);
108 
109         mHdmiControlServiceSpy.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
110         mTestLooper.dispatchAll();
111 
112         mPlaybackDevice = mHdmiControlServiceSpy.playback();
113         mPlaybackLogicalAddress = mPlaybackDevice.getDeviceInfo().getLogicalAddress();
114 
115         // Setup specific to these tests
116         mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
117                 Constants.ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV));
118         mTestLooper.dispatchAll();
119         mTestCallback = new TestCallback();
120         mAction = new SetAudioVolumeLevelDiscoveryAction(mPlaybackDevice,
121                 Constants.ADDR_TV, mTestCallback);
122     }
123 
124     @Test
sendsSetAudioVolumeLevel()125     public void sendsSetAudioVolumeLevel() {
126         mPlaybackDevice.addAndStartAction(mAction);
127         mTestLooper.dispatchAll();
128 
129         HdmiCecMessage setAudioVolumeLevel = SetAudioVolumeLevelMessage.build(
130                 mPlaybackLogicalAddress, Constants.ADDR_TV,
131                 Constants.AUDIO_VOLUME_STATUS_UNKNOWN);
132         assertThat(mNativeWrapper.getResultMessages()).contains(setAudioVolumeLevel);
133     }
134 
135     @Test
noMatchingFeatureAbortReceived_actionSucceedsAndSetsFeatureSupported()136     public void noMatchingFeatureAbortReceived_actionSucceedsAndSetsFeatureSupported() {
137         mPlaybackDevice.addAndStartAction(mAction);
138         mTestLooper.dispatchAll();
139 
140         // Wrong opcode
141         mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
142                 Constants.ADDR_TV,
143                 mPlaybackLogicalAddress,
144                 Constants.MESSAGE_GIVE_DECK_STATUS,
145                 Constants.ABORT_UNRECOGNIZED_OPCODE));
146         // Wrong source
147         mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
148                 Constants.ADDR_AUDIO_SYSTEM,
149                 mPlaybackLogicalAddress,
150                 Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
151                 Constants.ABORT_UNRECOGNIZED_OPCODE));
152         mTestLooper.dispatchAll();
153 
154         mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
155         mTestLooper.dispatchAll();
156 
157         @DeviceFeatures.FeatureSupportStatus int savlSupport =
158                 mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
159                         .getDeviceFeatures().getSetAudioVolumeLevelSupport();
160 
161         assertThat(savlSupport).isEqualTo(FEATURE_SUPPORTED);
162         assertThat(mTestCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
163     }
164 
165     @Test
matchingFeatureAbortReceived_actionSucceedsAndSetsFeatureNotSupported()166     public void matchingFeatureAbortReceived_actionSucceedsAndSetsFeatureNotSupported() {
167         mPlaybackDevice.addAndStartAction(mAction);
168         mTestLooper.dispatchAll();
169 
170         mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildFeatureAbortCommand(
171                 Constants.ADDR_TV,
172                 mPlaybackLogicalAddress,
173                 Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
174                 Constants.ABORT_UNRECOGNIZED_OPCODE));
175         mTestLooper.dispatchAll();
176 
177         @DeviceFeatures.FeatureSupportStatus int savlSupport =
178                 mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
179                         .getDeviceFeatures().getSetAudioVolumeLevelSupport();
180 
181         assertThat(savlSupport).isEqualTo(FEATURE_NOT_SUPPORTED);
182         assertThat(mTestCallback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
183     }
184 
185     @Test
messageFailedToSend_actionFailsAndDoesNotUpdateFeatureSupport()186     public void messageFailedToSend_actionFailsAndDoesNotUpdateFeatureSupport() {
187         mNativeWrapper.setMessageSendResult(Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL,
188                 SendMessageResult.FAIL);
189         mTestLooper.dispatchAll();
190 
191         mPlaybackDevice.addAndStartAction(mAction);
192         mTestLooper.dispatchAll();
193 
194         @DeviceFeatures.FeatureSupportStatus int savlSupport =
195                 mHdmiControlServiceSpy.getHdmiCecNetwork().getCecDeviceInfo(Constants.ADDR_TV)
196                         .getDeviceFeatures().getSetAudioVolumeLevelSupport();
197 
198         assertThat(savlSupport).isEqualTo(FEATURE_SUPPORT_UNKNOWN);
199         assertThat(mTestCallback.getResult()).isEqualTo(
200                 HdmiControlManager.RESULT_COMMUNICATION_FAILED);
201     }
202 
203     private static class TestCallback extends IHdmiControlCallback.Stub {
204         private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>();
205 
206         @Override
onComplete(int result)207         public void onComplete(int result) {
208             mCallbackResult.add(result);
209         }
210 
getResult()211         private int getResult() {
212             assertThat(mCallbackResult.size()).isEqualTo(1);
213             return mCallbackResult.get(0);
214         }
215     }
216 }
217