1 /*
2  * Copyright (C) 2018 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 package com.android.server.hdmi;
17 
18 import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
19 import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
20 import static com.android.server.hdmi.Constants.ADDR_TV;
21 import static com.android.server.hdmi.Constants.PATH_RELATIONSHIP_ANCESTOR;
22 
23 import static org.mockito.ArgumentMatchers.any;
24 import static org.mockito.ArgumentMatchers.anyInt;
25 import static org.mockito.ArgumentMatchers.anyString;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.atLeastOnce;
28 import static org.mockito.Mockito.doNothing;
29 import static org.mockito.Mockito.doReturn;
30 import static org.mockito.Mockito.spy;
31 import static org.mockito.Mockito.times;
32 import static org.mockito.Mockito.verify;
33 
34 import android.content.Context;
35 import android.content.ContextWrapper;
36 import android.hardware.hdmi.HdmiControlManager;
37 import android.hardware.hdmi.HdmiDeviceInfo;
38 import android.hardware.hdmi.HdmiPortInfo;
39 import android.hardware.tv.cec.V1_0.SendMessageResult;
40 import android.os.Binder;
41 import android.os.Looper;
42 import android.os.RemoteException;
43 import android.os.test.TestLooper;
44 import android.platform.test.annotations.Presubmit;
45 import android.stats.hdmi.HdmiStatsEnums;
46 
47 import androidx.test.InstrumentationRegistry;
48 import androidx.test.filters.SmallTest;
49 
50 import com.android.server.SystemService;
51 
52 import org.junit.Before;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 import org.junit.runners.JUnit4;
56 import org.mockito.Mockito;
57 
58 import java.util.Collections;
59 
60 /**
61  * Tests for the {@link HdmiCecAtomWriter} class and its usage by the HDMI-CEC framework.
62  */
63 @SmallTest
64 @Presubmit
65 @RunWith(JUnit4.class)
66 public class HdmiCecAtomLoggingTest {
67     private HdmiCecAtomWriter mHdmiCecAtomWriterSpy;
68     private HdmiControlService mHdmiControlServiceSpy;
69     private HdmiCecController mHdmiCecController;
70     private HdmiMhlControllerStub mHdmiMhlControllerStub;
71     private FakeNativeWrapper mNativeWrapper;
72     private FakePowerManagerWrapper mPowerManager;
73     private HdmiCecNetwork mHdmiCecNetwork;
74     private Looper mLooper;
75     private Context mContextSpy;
76     private TestLooper mTestLooper = new TestLooper();
77     private int mPhysicalAddress = 0x1110;
78     private HdmiPortInfo[] mHdmiPortInfo;
79 
80     @Before
setUp()81     public void setUp() throws RemoteException {
82         mHdmiCecAtomWriterSpy = spy(new HdmiCecAtomWriter());
83 
84         mLooper = mTestLooper.getLooper();
85 
86         mContextSpy = spy(new ContextWrapper(
87                 InstrumentationRegistry.getInstrumentation().getTargetContext()));
88 
89         FakeAudioFramework audioFramework = new FakeAudioFramework();
90 
91         mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy,
92                 Collections.singletonList(HdmiDeviceInfo.DEVICE_PLAYBACK),
93                 audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
94         doNothing().when(mHdmiControlServiceSpy)
95                 .writeStringSystemProperty(anyString(), anyString());
96         doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
97 
98         HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
99         doReturn(hdmiCecConfig).when(mHdmiControlServiceSpy).getHdmiCecConfig();
100 
101         mHdmiControlServiceSpy.setIoLooper(mLooper);
102         mHdmiControlServiceSpy.setCecMessageBuffer(
103                 new CecMessageBuffer(mHdmiControlServiceSpy));
104 
105         mNativeWrapper = new FakeNativeWrapper();
106         mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
107 
108         mHdmiCecController = HdmiCecController.createWithNativeWrapper(
109                 mHdmiControlServiceSpy, mNativeWrapper, mHdmiCecAtomWriterSpy);
110         mHdmiControlServiceSpy.setCecController(mHdmiCecController);
111 
112         mHdmiMhlControllerStub = HdmiMhlControllerStub.create(mHdmiControlServiceSpy);
113         mHdmiControlServiceSpy.setHdmiMhlController(
114                 mHdmiMhlControllerStub);
115 
116         mHdmiCecNetwork = new HdmiCecNetwork(mHdmiControlServiceSpy,
117                 mHdmiCecController, mHdmiMhlControllerStub);
118         mHdmiControlServiceSpy.setHdmiCecNetwork(mHdmiCecNetwork);
119         mHdmiControlServiceSpy.setDeviceConfig(new FakeDeviceConfigWrapper());
120 
121         HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
122         hdmiPortInfos[0] =
123                 new HdmiPortInfo.Builder(1, HdmiPortInfo.PORT_OUTPUT, 0x0000)
124                         .setCecSupported(true)
125                         .setMhlSupported(false)
126                         .setArcSupported(false)
127                         .build();
128         mNativeWrapper.setPortInfo(hdmiPortInfos);
129         mNativeWrapper.setPortConnectionStatus(1, true);
130 
131         mHdmiControlServiceSpy.initService();
132         mPowerManager = new FakePowerManagerWrapper(mContextSpy);
133         mHdmiControlServiceSpy.setPowerManager(mPowerManager);
134         mHdmiControlServiceSpy.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
135 
136         mTestLooper.dispatchAll();
137 
138         Mockito.reset(mHdmiCecAtomWriterSpy);
139     }
140 
141     @Test
testActiveSourceChanged_calledOnSetActiveSource()142     public void testActiveSourceChanged_calledOnSetActiveSource() {
143         mHdmiControlServiceSpy.setActiveSource(1, 0x1111, "caller");
144         verify(mHdmiCecAtomWriterSpy, times(1))
145                 .activeSourceChanged(1, 0x1111, PATH_RELATIONSHIP_ANCESTOR);
146     }
147 
148     @Test
testMessageReported_calledOnOutgoingMessage()149     public void testMessageReported_calledOnOutgoingMessage() {
150         HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
151                 mPhysicalAddress);
152 
153         mHdmiCecController.sendCommand(message);
154         mTestLooper.dispatchAll();
155 
156         verify(mHdmiCecAtomWriterSpy, times(1)).messageReported(
157                 eq(message),
158                 eq(HdmiStatsEnums.OUTGOING),
159                 anyInt(),
160                 eq(SendMessageResult.SUCCESS));
161     }
162 
163     @Test
testMessageReported_calledOnIncomingMessage()164     public void testMessageReported_calledOnIncomingMessage() {
165         HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
166 
167         mNativeWrapper.onCecMessage(message);
168         mTestLooper.dispatchAll();
169 
170         verify(mHdmiCecAtomWriterSpy, times(1)).messageReported(
171                 eq(message),
172                 eq(HdmiStatsEnums.INCOMING),
173                 anyInt());
174     }
175 
176     @Test
testMessageReported_calledWithUid()177     public void testMessageReported_calledWithUid() {
178         int callerUid = 1234;
179         int runnerUid = 5678;
180 
181         mHdmiControlServiceSpy.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
182         mHdmiControlServiceSpy.onBootPhase(PHASE_BOOT_COMPLETED);
183 
184         Binder.setCallingWorkSourceUid(callerUid);
185 
186         mHdmiControlServiceSpy.runOnServiceThread(
187                 () -> mHdmiControlServiceSpy.setStandbyMode(true));
188 
189         Binder.setCallingWorkSourceUid(runnerUid);
190 
191         mTestLooper.dispatchAll();
192 
193         verify(mHdmiCecAtomWriterSpy, atLeastOnce()).messageReported(
194                 any(),
195                 anyInt(),
196                 eq(callerUid),
197                 anyInt());
198     }
199 
200     @Test
testMessageReported_writesAtom_userControlPressed()201     public void testMessageReported_writesAtom_userControlPressed() {
202         HdmiCecMessage message = HdmiCecMessageBuilder.buildUserControlPressed(
203                 Constants.ADDR_TV,
204                 Constants.ADDR_PLAYBACK_1,
205                 HdmiCecKeycode.CEC_KEYCODE_MUTE
206         );
207 
208         mHdmiCecAtomWriterSpy.messageReported(
209                 message,
210                 HdmiStatsEnums.INCOMING,
211                 1234);
212 
213         verify(mHdmiCecAtomWriterSpy, times(1))
214                 .writeHdmiCecMessageReportedAtom(
215                         1234,
216                         HdmiStatsEnums.INCOMING,
217                         Constants.ADDR_TV,
218                         Constants.ADDR_PLAYBACK_1,
219                         Constants.MESSAGE_USER_CONTROL_PRESSED,
220                         HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
221                         HdmiStatsEnums.VOLUME_MUTE,
222                         HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN,
223                         HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN);
224     }
225 
226     @Test
testMessageReported_writesAtom_userControlPressed_noParams()227     public void testMessageReported_writesAtom_userControlPressed_noParams() {
228         HdmiCecMessage message = HdmiCecMessage.build(
229                 Constants.ADDR_TV,
230                 Constants.ADDR_PLAYBACK_1,
231                 Constants.MESSAGE_USER_CONTROL_PRESSED,
232                 new byte[0]);
233 
234         mHdmiCecAtomWriterSpy.messageReported(
235                 message,
236                 HdmiStatsEnums.INCOMING,
237                 1234);
238 
239         verify(mHdmiCecAtomWriterSpy, times(1))
240                 .writeHdmiCecMessageReportedAtom(
241                         1234,
242                         HdmiStatsEnums.INCOMING,
243                         Constants.ADDR_TV,
244                         Constants.ADDR_PLAYBACK_1,
245                         Constants.MESSAGE_USER_CONTROL_PRESSED,
246                         HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
247                         HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN,
248                         HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN,
249                         HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN);
250     }
251 
252     @Test
testMessageReported_writesAtom_featureAbort()253     public void testMessageReported_writesAtom_featureAbort() {
254         HdmiCecMessage message = HdmiCecMessageBuilder.buildFeatureAbortCommand(
255                 Constants.ADDR_TV,
256                 Constants.ADDR_PLAYBACK_1,
257                 Constants.MESSAGE_RECORD_ON,
258                 Constants.ABORT_UNRECOGNIZED_OPCODE
259         );
260 
261         mHdmiCecAtomWriterSpy.messageReported(
262                 message,
263                 HdmiStatsEnums.INCOMING,
264                 1234);
265 
266         verify(mHdmiCecAtomWriterSpy, times(1))
267                 .writeHdmiCecMessageReportedAtom(
268                         1234,
269                         HdmiStatsEnums.INCOMING,
270                         Constants.ADDR_TV,
271                         Constants.ADDR_PLAYBACK_1,
272                         Constants.MESSAGE_FEATURE_ABORT,
273                         HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
274                         HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN,
275                         Constants.MESSAGE_RECORD_ON,
276                         HdmiStatsEnums.UNRECOGNIZED_OPCODE);
277     }
278 
279     @Test
testMessageReported_writesAtom_featureAbort_noParams()280     public void testMessageReported_writesAtom_featureAbort_noParams() {
281         HdmiCecMessage message = HdmiCecMessage.build(
282                 Constants.ADDR_TV,
283                 Constants.ADDR_PLAYBACK_1,
284                 Constants.MESSAGE_FEATURE_ABORT,
285                 new byte[0]);
286 
287         mHdmiCecAtomWriterSpy.messageReported(
288                 message,
289                 HdmiStatsEnums.INCOMING,
290                 1234);
291 
292         verify(mHdmiCecAtomWriterSpy, times(1))
293                 .writeHdmiCecMessageReportedAtom(
294                         1234,
295                         HdmiStatsEnums.INCOMING,
296                         Constants.ADDR_TV,
297                         Constants.ADDR_PLAYBACK_1,
298                         Constants.MESSAGE_FEATURE_ABORT,
299                         HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN,
300                         HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN,
301                         HdmiCecAtomWriter.FEATURE_ABORT_OPCODE_UNKNOWN,
302                         HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN);
303     }
304 }
305