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 com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
20 import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 
24 import android.content.Context;
25 import android.hardware.hdmi.HdmiControlManager;
26 import android.hardware.hdmi.HdmiDeviceInfo;
27 import android.os.Looper;
28 import android.os.test.TestLooper;
29 import android.platform.test.annotations.Presubmit;
30 
31 import androidx.test.InstrumentationRegistry;
32 import androidx.test.filters.SmallTest;
33 
34 import com.android.server.hdmi.RequestSadAction.RequestSadCallback;
35 
36 import org.junit.Before;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 import org.junit.runners.JUnit4;
40 
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Collections;
44 import java.util.List;
45 
46 @SmallTest
47 @Presubmit
48 @RunWith(JUnit4.class)
49 public class RequestSadActionTest {
50 
51     private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1;
52     private static final ArrayList<Integer> CODECS_TO_QUERY_1 = new ArrayList<Integer>(
53             Arrays.asList(Constants.AUDIO_CODEC_LPCM, Constants.AUDIO_CODEC_DD,
54                     Constants.AUDIO_CODEC_MPEG1, Constants.AUDIO_CODEC_MP3));
55     private static final ArrayList<Integer> CODECS_TO_QUERY_2 = new ArrayList<Integer>(
56             Arrays.asList(Constants.AUDIO_CODEC_MPEG2, Constants.AUDIO_CODEC_AAC,
57                     Constants.AUDIO_CODEC_DTS, Constants.AUDIO_CODEC_ATRAC));
58     private static final ArrayList<Integer> CODECS_TO_QUERY_3 = new ArrayList<Integer>(
59             Arrays.asList(Constants.AUDIO_CODEC_ONEBITAUDIO, Constants.AUDIO_CODEC_DDP,
60                     Constants.AUDIO_CODEC_DTSHD, Constants.AUDIO_CODEC_TRUEHD));
61     private static final ArrayList<Integer> CODECS_TO_QUERY_4 = new ArrayList<Integer>(
62             Arrays.asList(Constants.AUDIO_CODEC_DST, Constants.AUDIO_CODEC_WMAPRO,
63                     Constants.AUDIO_CODEC_MAX));
64 
65     private HdmiControlService mHdmiControlService;
66     private HdmiCecController mHdmiCecController;
67     private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
68     private FakeNativeWrapper mNativeWrapper;
69     private FakePowerManagerWrapper mPowerManager;
70     private Looper mMyLooper;
71     private TestLooper mTestLooper = new TestLooper();
72     private int mTvLogicalAddress;
73     private List<byte[]> mSupportedSads;
74     private RequestSadCallback mCallback =
75             new RequestSadCallback() {
76                 @Override
77                 public void onRequestSadDone(
78                         List<byte[]> supportedSads) {
79                     mSupportedSads = supportedSads;
80                 }
81             };
82 
concatenateSads(List<byte[]> sads)83     private static byte[] concatenateSads(List<byte[]> sads) {
84         byte[] concatenatedSads = new byte[sads.size() * 3];
85         for (int i = 0; i < sads.size(); i++) {
86             for (int j = 0; j < 3; j++) {
87                 concatenatedSads[3 * i + j] = sads.get(i)[j];
88             }
89         }
90         return concatenatedSads;
91     }
92 
93     @Before
setUp()94     public void setUp() {
95         Context context = InstrumentationRegistry.getTargetContext();
96         mMyLooper = mTestLooper.getLooper();
97 
98         FakeAudioFramework audioFramework = new FakeAudioFramework();
99 
100         mHdmiControlService =
101                 new HdmiControlService(context, Collections.singletonList(HdmiDeviceInfo.DEVICE_TV),
102                         audioFramework.getAudioManager(),
103                         audioFramework.getAudioDeviceVolumeManager()) {
104                     @Override
105                     boolean isCecControlEnabled() {
106                         return true;
107                     }
108 
109                     @Override
110                     protected void writeStringSystemProperty(String key, String value) {
111                         // do nothing
112                     }
113 
114                     @Override
115                     boolean isPowerStandbyOrTransient() {
116                         return false;
117                     }
118                 };
119 
120         mHdmiControlService.setIoLooper(mMyLooper);
121         mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
122         mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
123         mNativeWrapper = new FakeNativeWrapper();
124         mHdmiCecController = HdmiCecController.createWithNativeWrapper(
125                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
126         mHdmiControlService.setCecController(mHdmiCecController);
127         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
128         mHdmiControlService.initService();
129         mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
130         mPowerManager = new FakePowerManagerWrapper(context);
131         mHdmiControlService.setPowerManager(mPowerManager);
132         mNativeWrapper.setPhysicalAddress(0x0000);
133         mTestLooper.dispatchAll();
134         mHdmiCecLocalDeviceTv = mHdmiControlService.tv();
135         mTvLogicalAddress = mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress();
136         mNativeWrapper.clearResultMessages();
137     }
138 
139     @Test
noResponse_queryAgainOnce_emptyResult()140     public void noResponse_queryAgainOnce_emptyResult() {
141         RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
142                 mCallback);
143         action.start();
144         mTestLooper.dispatchAll();
145         assertThat(mSupportedSads).isNull();
146 
147         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
148                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
149                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
150         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
151         mNativeWrapper.clearResultMessages();
152         mTestLooper.moveTimeForward(TIMEOUT_MS);
153         mTestLooper.dispatchAll();
154         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
155         mTestLooper.moveTimeForward(TIMEOUT_MS);
156         mTestLooper.dispatchAll();
157 
158         assertThat(mSupportedSads).isNotNull();
159         assertThat(mSupportedSads.size()).isEqualTo(0);
160 
161         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
162                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
163                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
164         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
165                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
166                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
167         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
168                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
169                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
170 
171         mTestLooper.moveTimeForward(TIMEOUT_MS);
172         mTestLooper.dispatchAll();
173         mTestLooper.moveTimeForward(TIMEOUT_MS);
174         mTestLooper.dispatchAll();
175         mTestLooper.moveTimeForward(TIMEOUT_MS);
176         mTestLooper.dispatchAll();
177         mTestLooper.moveTimeForward(TIMEOUT_MS);
178         mTestLooper.dispatchAll();
179         mTestLooper.moveTimeForward(TIMEOUT_MS);
180         mTestLooper.dispatchAll();
181         mTestLooper.moveTimeForward(TIMEOUT_MS);
182         mTestLooper.dispatchAll();
183 
184         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(expected2);
185         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(expected3);
186         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(expected4);
187         assertThat(mSupportedSads.size()).isEqualTo(0);
188     }
189 
190     @Test
unrecognizedOpcode_dontQueryAgain_emptyResult()191     public void unrecognizedOpcode_dontQueryAgain_emptyResult() {
192         RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
193                 mCallback);
194         action.start();
195         mTestLooper.dispatchAll();
196         assertThat(mSupportedSads).isNull();
197 
198         HdmiCecMessage unrecognizedOpcode = HdmiCecMessageBuilder.buildFeatureAbortCommand(
199                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress,
200                 Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
201                 Constants.ABORT_UNRECOGNIZED_OPCODE);
202 
203         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
204                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
205                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
206         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
207         action.processCommand(unrecognizedOpcode);
208         mTestLooper.dispatchAll();
209 
210         assertThat(mSupportedSads).isNotNull();
211         assertThat(mSupportedSads.size()).isEqualTo(0);
212 
213         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
214                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
215                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
216         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
217                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
218                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
219         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
220                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
221                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
222 
223         mTestLooper.moveTimeForward(TIMEOUT_MS);
224         mTestLooper.dispatchAll();
225         mTestLooper.moveTimeForward(TIMEOUT_MS);
226         mTestLooper.dispatchAll();
227         mTestLooper.moveTimeForward(TIMEOUT_MS);
228         mTestLooper.dispatchAll();
229         mTestLooper.moveTimeForward(TIMEOUT_MS);
230         mTestLooper.dispatchAll();
231         mTestLooper.moveTimeForward(TIMEOUT_MS);
232         mTestLooper.dispatchAll();
233         mTestLooper.moveTimeForward(TIMEOUT_MS);
234         mTestLooper.dispatchAll();
235 
236         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(expected2);
237         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(expected3);
238         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(expected4);
239         assertThat(mSupportedSads.size()).isEqualTo(0);
240     }
241 
242     @Test
featureAbort_dontQueryAgain_emptyResult()243     public void featureAbort_dontQueryAgain_emptyResult() {
244         RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
245                 mCallback);
246         action.start();
247         mTestLooper.dispatchAll();
248         HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand(
249                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress,
250                 Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
251                 Constants.ABORT_INVALID_OPERAND);
252 
253         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
254                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
255                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
256         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
257         mNativeWrapper.clearResultMessages();
258         action.processCommand(featureAbort);
259         mTestLooper.dispatchAll();
260 
261         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
262                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
263                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
264         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
265         mNativeWrapper.clearResultMessages();
266         action.processCommand(featureAbort);
267         mTestLooper.dispatchAll();
268 
269         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
270                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
271                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
272         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
273         mNativeWrapper.clearResultMessages();
274         action.processCommand(featureAbort);
275         mTestLooper.dispatchAll();
276 
277         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
278                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
279                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
280         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
281         action.processCommand(featureAbort);
282         mTestLooper.dispatchAll();
283 
284         assertThat(mSupportedSads.size()).isEqualTo(0);
285     }
286 
287     @Test
allSupported_completeResult()288     public void allSupported_completeResult() {
289         RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
290                 mCallback);
291         action.start();
292         mTestLooper.dispatchAll();
293 
294         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
295                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
296                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
297         byte[] sadsToRespond_1 = new byte[]{
298                 0x0F, 0x18, 0x4A,
299                 0x17, 0x64, 0x5A,
300                 0x1F, 0x4B, 0x00,
301                 0x27, 0x20, 0x0A};
302         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
303                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
304         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
305         mNativeWrapper.clearResultMessages();
306         action.processCommand(response1);
307         mTestLooper.dispatchAll();
308 
309         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
310                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
311                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
312         byte[] sadsToRespond_2 = new byte[]{
313                 0x2F, 0x18, 0x4A,
314                 0x37, 0x64, 0x5A,
315                 0x3F, 0x4B, 0x00,
316                 0x47, 0x20, 0x0A};
317         HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
318                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
319         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
320         mNativeWrapper.clearResultMessages();
321         action.processCommand(response2);
322         mTestLooper.dispatchAll();
323 
324         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
325                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
326                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
327         byte[] sadsToRespond_3 = new byte[]{
328                 0x4F, 0x18, 0x4A,
329                 0x57, 0x64, 0x5A,
330                 0x5F, 0x4B, 0x00,
331                 0x67, 0x20, 0x0A};
332         HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
333                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
334         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
335         mNativeWrapper.clearResultMessages();
336         action.processCommand(response3);
337         mTestLooper.dispatchAll();
338 
339         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
340                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
341                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
342         byte[] sadsToRespond_4 = new byte[]{
343                 0x6F, 0x18, 0x4A,
344                 0x77, 0x64, 0x5A,
345                 0x7F, 0x4B, 0x00};
346         HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
347                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_4);
348         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
349         action.processCommand(response4);
350         mTestLooper.dispatchAll();
351 
352         assertThat(mSupportedSads.size()).isEqualTo(15);
353         assertThat(Arrays.equals(sadsToRespond_1,
354                 concatenateSads(mSupportedSads.subList(0, 4)))).isTrue();
355         assertThat(Arrays.equals(sadsToRespond_2,
356                 concatenateSads(mSupportedSads.subList(4, 8)))).isTrue();
357         assertThat(Arrays.equals(sadsToRespond_3,
358                 concatenateSads(mSupportedSads.subList(8, 12)))).isTrue();
359         assertThat(Arrays.equals(sadsToRespond_4,
360                 concatenateSads(mSupportedSads.subList(12, 15)))).isTrue();
361     }
362 
363     @Test
subsetSupported_subsetResult()364     public void subsetSupported_subsetResult() {
365         RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
366                 mCallback);
367         action.start();
368         mTestLooper.dispatchAll();
369 
370         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
371                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
372                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
373         byte[] sadsToRespond_1 = new byte[]{
374                 0x0F, 0x18, 0x4A,
375                 0x1F, 0x4B, 0x00,
376                 0x27, 0x20, 0x0A};
377         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
378                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
379         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
380         mNativeWrapper.clearResultMessages();
381         action.processCommand(response1);
382         mTestLooper.dispatchAll();
383 
384         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
385                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
386                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
387         byte[] sadsToRespond_2 = new byte[]{
388                 0x2F, 0x20, 0x0A};
389         HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
390                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
391         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
392         mNativeWrapper.clearResultMessages();
393         action.processCommand(response2);
394         mTestLooper.dispatchAll();
395 
396         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
397                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
398                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
399         byte[] sadsToRespond_3 = new byte[]{
400                 0x4F, 0x18, 0x4A,
401                 0x57, 0x64, 0x5A,
402                 0x5F, 0x4B, 0x00,
403                 0x67, 0x20, 0x0A};
404         HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
405                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
406         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
407         mNativeWrapper.clearResultMessages();
408         action.processCommand(response3);
409         mTestLooper.dispatchAll();
410 
411         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
412                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
413                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
414         byte[] sadsToRespond_4 = new byte[]{
415                 0x7F, 0x4B, 0x00};
416         HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
417                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_4);
418         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
419         action.processCommand(response4);
420         mTestLooper.dispatchAll();
421 
422         assertThat(mSupportedSads.size()).isEqualTo(9);
423         assertThat(Arrays.equals(sadsToRespond_1,
424                 concatenateSads(mSupportedSads.subList(0, 3)))).isTrue();
425         assertThat(Arrays.equals(sadsToRespond_2,
426                 concatenateSads(mSupportedSads.subList(3, 4)))).isTrue();
427         assertThat(Arrays.equals(sadsToRespond_3,
428                 concatenateSads(mSupportedSads.subList(4, 8)))).isTrue();
429         assertThat(Arrays.equals(sadsToRespond_4,
430                 concatenateSads(mSupportedSads.subList(8, 9)))).isTrue();
431     }
432 
433     @Test
invalidCodecs_emptyResults()434     public void invalidCodecs_emptyResults() {
435         RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
436                 mCallback);
437         action.start();
438         mTestLooper.dispatchAll();
439 
440         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
441                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
442                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
443         // Negative values require explicit casting
444         byte[] sadsToRespond_1 = new byte[]{
445                 (byte) 0x80, 0x18, 0x4A,
446                 (byte) 0x82, 0x64, 0x5A,
447                 (byte) 0x87, 0x4B, 0x00,
448                 (byte) 0x8F, 0x20, 0x0A};
449         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
450                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
451         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
452         mNativeWrapper.clearResultMessages();
453         action.processCommand(response1);
454         mTestLooper.dispatchAll();
455 
456         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
457                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
458                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
459         byte[] sadsToRespond_2 = new byte[]{
460                 (byte) 0x92, 0x18, 0x4A,
461                 (byte) 0x98, 0x64, 0x5A,
462                 (byte) 0xA1, 0x4B, 0x00,
463                 (byte) 0xA4, 0x20, 0x0A};
464         HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
465                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
466         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
467         mNativeWrapper.clearResultMessages();
468         action.processCommand(response2);
469         mTestLooper.dispatchAll();
470 
471         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
472                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
473                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
474         byte[] sadsToRespond_3 = new byte[]{
475                 (byte) 0xB3, 0x18, 0x4A,
476                 (byte) 0xBA, 0x64, 0x5A,
477                 (byte) 0xCB, 0x4B, 0x00,
478                 (byte) 0xCF, 0x20, 0x0A};
479         HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
480                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
481         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
482         mNativeWrapper.clearResultMessages();
483         action.processCommand(response3);
484         mTestLooper.dispatchAll();
485 
486         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
487                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
488                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
489         byte[] sadsToRespond_4 = new byte[]{
490                 (byte) 0xF0, 0x18, 0x4A,
491                 (byte) 0xF1, 0x64, 0x5A,
492                 (byte) 0xF3, 0x4B, 0x00};
493         HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
494                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_4);
495         assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
496         action.processCommand(response4);
497         mTestLooper.dispatchAll();
498 
499         assertThat(mSupportedSads.size()).isEqualTo(0);
500     }
501 
502     @Test
invalidMessageLength_queryAgainOnce()503     public void invalidMessageLength_queryAgainOnce() {
504         RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
505                 mCallback);
506         action.start();
507         mTestLooper.dispatchAll();
508         assertThat(mSupportedSads).isNull();
509 
510         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
511                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
512                 CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
513         byte[] sadsToRespond_1 = new byte[]{
514                 0x0F, 0x18,
515                 0x17, 0x64, 0x5A,
516                 0x1F, 0x4B, 0x00,
517                 0x27, 0x20, 0x0A};
518         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
519                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
520         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
521         mNativeWrapper.clearResultMessages();
522         action.processCommand(response1);
523         mTestLooper.dispatchAll();
524         mTestLooper.moveTimeForward(TIMEOUT_MS);
525         mTestLooper.dispatchAll();
526         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
527         mNativeWrapper.clearResultMessages();
528         mTestLooper.moveTimeForward(TIMEOUT_MS);
529         mTestLooper.dispatchAll();
530 
531         assertThat(mSupportedSads).isNotNull();
532         assertThat(mSupportedSads.size()).isEqualTo(0);
533 
534         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
535                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
536                 CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
537         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
538                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
539                 CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
540         HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
541                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
542                 CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
543 
544         mTestLooper.moveTimeForward(TIMEOUT_MS);
545         mTestLooper.dispatchAll();
546         mTestLooper.moveTimeForward(TIMEOUT_MS);
547         mTestLooper.dispatchAll();
548         mTestLooper.moveTimeForward(TIMEOUT_MS);
549         mTestLooper.dispatchAll();
550         mTestLooper.moveTimeForward(TIMEOUT_MS);
551         mTestLooper.dispatchAll();
552         mTestLooper.moveTimeForward(TIMEOUT_MS);
553         mTestLooper.dispatchAll();
554         mTestLooper.moveTimeForward(TIMEOUT_MS);
555         mTestLooper.dispatchAll();
556 
557         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(expected2);
558         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(expected3);
559         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(expected4);
560         assertThat(mSupportedSads.size()).isEqualTo(0);
561     }
562 
563     @Test
selectedSads_allSupported_completeResult()564     public void selectedSads_allSupported_completeResult() {
565         mHdmiControlService.getHdmiCecConfig().setIntValue(
566                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1,
567                 HdmiControlManager.QUERY_SAD_DISABLED);
568         mHdmiControlService.getHdmiCecConfig().setIntValue(
569                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO,
570                 HdmiControlManager.QUERY_SAD_DISABLED);
571         mHdmiControlService.getHdmiCecConfig().setIntValue(
572                 HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
573                 HdmiControlManager.QUERY_SAD_DISABLED);
574         RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
575                 mCallback);
576         action.start();
577         mTestLooper.dispatchAll();
578 
579         HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
580                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
581                 new int[]{Constants.AUDIO_CODEC_LPCM, Constants.AUDIO_CODEC_DD,
582                         Constants.AUDIO_CODEC_MP3, Constants.AUDIO_CODEC_MPEG2});
583         byte[] sadsToRespond_1 = new byte[]{
584                 0x0F, 0x18, 0x4A,
585                 0x17, 0x64, 0x5A,
586                 0x27, 0x20, 0x0A,
587                 0x2F, 0x18, 0x4A};
588         HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
589                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_1);
590         assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
591         mNativeWrapper.clearResultMessages();
592         action.processCommand(response1);
593         mTestLooper.dispatchAll();
594 
595         HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
596                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
597                 new int[]{Constants.AUDIO_CODEC_AAC, Constants.AUDIO_CODEC_DTS,
598                         Constants.AUDIO_CODEC_ATRAC, Constants.AUDIO_CODEC_DDP});
599         byte[] sadsToRespond_2 = new byte[]{
600                 0x37, 0x64, 0x5A,
601                 0x3F, 0x4B, 0x00,
602                 0x47, 0x20, 0x0A,
603                 0x4F, 0x18, 0x4A};
604         HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
605                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_2);
606         assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
607         mNativeWrapper.clearResultMessages();
608         action.processCommand(response2);
609         mTestLooper.dispatchAll();
610 
611         HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
612                 mTvLogicalAddress, Constants.ADDR_AUDIO_SYSTEM,
613                 new int[]{Constants.AUDIO_CODEC_DTSHD, Constants.AUDIO_CODEC_TRUEHD,
614                         Constants.AUDIO_CODEC_DST, Constants.AUDIO_CODEC_MAX});
615         byte[] sadsToRespond_3 = new byte[]{
616                 0x5F, 0x4B, 0x00,
617                 0x67, 0x20, 0x0A,
618                 0x6F, 0x18, 0x4A,
619                 0x7F, 0x4B, 0x00};
620         HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
621                 Constants.ADDR_AUDIO_SYSTEM, mTvLogicalAddress, sadsToRespond_3);
622         assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
623         mNativeWrapper.clearResultMessages();
624         action.processCommand(response3);
625         mTestLooper.dispatchAll();
626 
627         assertThat(mSupportedSads.size()).isEqualTo(12);
628         assertThat(Arrays.equals(sadsToRespond_1,
629                 concatenateSads(mSupportedSads.subList(0, 4)))).isTrue();
630         assertThat(Arrays.equals(sadsToRespond_2,
631                 concatenateSads(mSupportedSads.subList(4, 8)))).isTrue();
632         assertThat(Arrays.equals(sadsToRespond_3,
633                 concatenateSads(mSupportedSads.subList(8, 12)))).isTrue();
634     }
635 }
636