• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 android.security.cts;
18 
19 import android.media.audiofx.AudioEffect;
20 import android.media.audiofx.EnvironmentalReverb;
21 import android.media.audiofx.Equalizer;
22 import android.media.audiofx.PresetReverb;
23 import android.media.MediaPlayer;
24 import android.platform.test.annotations.SecurityTest;
25 import android.test.InstrumentationTestCase;
26 import android.util.Log;
27 
28 import java.nio.ByteBuffer;
29 import java.nio.ByteOrder;
30 import java.nio.charset.StandardCharsets;
31 import java.util.Arrays;
32 import java.util.UUID;
33 
34 @SecurityTest
35 public class EffectBundleTest extends InstrumentationTestCase {
36     private static final String TAG = "EffectBundleTest";
37     private static final int[] INVALID_BAND_ARRAY = {Integer.MIN_VALUE, -10000, -100, -2, -1};
38     private static final int mValue0 = 9999; //unlikely values. Should not change
39     private static final int mValue1 = 13877;
40     private static final int PRESET_CUSTOM = -1; //keep in sync AudioEqualizer.h
41 
42     private static final int MEDIA_SHORT = 0;
43     private static final int MEDIA_LONG = 1;
44 
45     // should match audio_effect.h (native)
46     private static final int EFFECT_CMD_SET_PARAM = 5;
47 
48     private static final int intSize = 4;
49 
50     //Testing security bug: 32436341
51     @SecurityTest(minPatchLevel = "2017-01")
testEqualizer_getParamCenterFreq()52     public void testEqualizer_getParamCenterFreq() throws Exception {
53         if (!hasEqualizer()) {
54             return;
55         }
56         testGetParam(MEDIA_SHORT, Equalizer.PARAM_CENTER_FREQ, INVALID_BAND_ARRAY, mValue0,
57                 mValue1);
58     }
59 
60     //Testing security bug: 32588352
61     @SecurityTest(minPatchLevel = "2017-01")
testEqualizer_getParamCenterFreq_long()62     public void testEqualizer_getParamCenterFreq_long() throws Exception {
63         if (!hasEqualizer()) {
64             return;
65         }
66         testGetParam(MEDIA_LONG, Equalizer.PARAM_CENTER_FREQ, INVALID_BAND_ARRAY, mValue0, mValue1);
67     }
68 
69     //Testing security bug: 32438598
70     @SecurityTest(minPatchLevel = "2017-01")
testEqualizer_getParamBandLevel()71     public void testEqualizer_getParamBandLevel() throws Exception {
72         if (!hasEqualizer()) {
73             return;
74         }
75         testGetParam(MEDIA_SHORT, Equalizer.PARAM_BAND_LEVEL, INVALID_BAND_ARRAY, mValue0, mValue1);
76     }
77 
78     //Testing security bug: 32584034
79     @SecurityTest(minPatchLevel = "2017-01")
testEqualizer_getParamBandLevel_long()80     public void testEqualizer_getParamBandLevel_long() throws Exception {
81         if (!hasEqualizer()) {
82             return;
83         }
84         testGetParam(MEDIA_LONG, Equalizer.PARAM_BAND_LEVEL, INVALID_BAND_ARRAY, mValue0, mValue1);
85     }
86 
87     //Testing security bug: 32247948
88     @SecurityTest(minPatchLevel = "2017-01")
testEqualizer_getParamFreqRange()89     public void testEqualizer_getParamFreqRange() throws Exception {
90         if (!hasEqualizer()) {
91             return;
92         }
93         testGetParam(MEDIA_SHORT, Equalizer.PARAM_BAND_FREQ_RANGE, INVALID_BAND_ARRAY, mValue0,
94                 mValue1);
95     }
96 
97     //Testing security bug: 32588756
98     @SecurityTest(minPatchLevel = "2017-01")
testEqualizer_getParamFreqRange_long()99     public void testEqualizer_getParamFreqRange_long() throws Exception {
100         if (!hasEqualizer()) {
101             return;
102         }
103         testGetParam(MEDIA_LONG, Equalizer.PARAM_BAND_FREQ_RANGE, INVALID_BAND_ARRAY, mValue0,
104                 mValue1);
105     }
106 
107     //Testing security bug: 32448258
108     @SecurityTest(minPatchLevel = "2017-01")
testEqualizer_getParamPresetName()109     public void testEqualizer_getParamPresetName() throws Exception {
110         if (!hasEqualizer()) {
111             return;
112         }
113         testParamPresetName(MEDIA_SHORT);
114     }
115 
116     //Testing security bug: 32588016
117     @SecurityTest(minPatchLevel = "2017-01")
testEqualizer_getParamPresetName_long()118     public void testEqualizer_getParamPresetName_long() throws Exception {
119         if (!hasEqualizer()) {
120             return;
121         }
122         testParamPresetName(MEDIA_LONG);
123     }
124 
testParamPresetName(int media)125     private void testParamPresetName(int media) {
126         final int command = Equalizer.PARAM_GET_PRESET_NAME;
127         for (int invalidBand : INVALID_BAND_ARRAY)
128         {
129             final byte testValue = 7;
130             byte reply[] = new byte[Equalizer.PARAM_STRING_SIZE_MAX];
131             Arrays.fill(reply, testValue);
132             if (!eqGetParam(media, command, invalidBand, reply)) {
133                 fail("getParam PARAM_GET_PRESET_NAME did not complete successfully");
134             }
135             //Compare
136             if (invalidBand == PRESET_CUSTOM) {
137                 final String expectedName = "Custom";
138                 int length = 0;
139                 while (reply[length] != 0) length++;
140                 try {
141                     final String presetName =  new String(reply, 0, length,
142                             StandardCharsets.ISO_8859_1.name());
143                     assertEquals("getPresetName custom preset name failed", expectedName,
144                             presetName);
145                 } catch (Exception e) {
146                     Log.w(TAG,"Problem creating reply string.");
147                 }
148             } else {
149                 for (int i = 0; i < reply.length; i++) {
150                     assertEquals(String.format("getParam should not change reply at byte %d", i),
151                             testValue, reply[i]);
152                 }
153             }
154         }
155     }
156 
157     //testing security bug: 32095626
158     @SecurityTest(minPatchLevel = "2017-01")
testEqualizer_setParamBandLevel()159     public void testEqualizer_setParamBandLevel() throws Exception {
160         if (!hasEqualizer()) {
161             return;
162         }
163         final int command = Equalizer.PARAM_BAND_LEVEL;
164         short[] value = { 1000 };
165         for (int invalidBand : INVALID_BAND_ARRAY)
166         {
167             if (!eqSetParam(MEDIA_SHORT, command, invalidBand, value)) {
168                 fail("setParam PARAM_BAND_LEVEL did not complete successfully");
169             }
170         }
171     }
172 
173     //testing security bug: 32585400
174     @SecurityTest(minPatchLevel = "2017-01")
testEqualizer_setParamBandLevel_long()175     public void testEqualizer_setParamBandLevel_long() throws Exception {
176         if (!hasEqualizer()) {
177             return;
178         }
179         final int command = Equalizer.PARAM_BAND_LEVEL;
180         short[] value = { 1000 };
181         for (int invalidBand : INVALID_BAND_ARRAY)
182         {
183             if (!eqSetParam(MEDIA_LONG, command, invalidBand, value)) {
184                 fail("setParam PARAM_BAND_LEVEL did not complete successfully");
185             }
186         }
187     }
188 
189     //testing security bug: 32705438
190     @SecurityTest(minPatchLevel = "2017-02")
testEqualizer_getParamFreqRangeCommand_short()191     public void testEqualizer_getParamFreqRangeCommand_short() throws Exception {
192         if (!hasEqualizer()) {
193             return;
194         }
195         assertTrue("testEqualizer_getParamFreqRangeCommand_short did not complete successfully",
196                 eqGetParamFreqRangeCommand(MEDIA_SHORT));
197     }
198 
199     //testing security bug: 32703959
200     @SecurityTest(minPatchLevel = "2017-02")
testEqualizer_getParamFreqRangeCommand_long()201     public void testEqualizer_getParamFreqRangeCommand_long() throws Exception {
202         if (!hasEqualizer()) {
203             return;
204         }
205         assertTrue("testEqualizer_getParamFreqRangeCommand_long did not complete successfully",
206                 eqGetParamFreqRangeCommand(MEDIA_LONG));
207     }
208 
209     //testing security bug: 37563371 (short media)
210     @SecurityTest(minPatchLevel = "2017-09")
testEqualizer_setParamProperties_short()211     public void testEqualizer_setParamProperties_short() throws Exception {
212         if (!hasEqualizer()) {
213             return;
214         }
215         assertTrue("testEqualizer_setParamProperties_long did not complete successfully",
216                 eqSetParamProperties(MEDIA_SHORT));
217     }
218 
219     //testing security bug: 37563371 (long media)
220     @SecurityTest(minPatchLevel = "2017-09")
testEqualizer_setParamProperties_long()221     public void testEqualizer_setParamProperties_long() throws Exception {
222         if (!hasEqualizer()) {
223             return;
224         }
225         assertTrue("testEqualizer_setParamProperties_long did not complete successfully",
226                 eqSetParamProperties(MEDIA_LONG));
227     }
228 
229     //Testing security bug: 63662938
230     @SecurityTest(minPatchLevel = "2017-10")
testDownmix_setParameter()231     public void testDownmix_setParameter() throws Exception {
232         verifyZeroPVSizeRejectedForSetParameter(
233                 EFFECT_TYPE_DOWNMIX, new int[] { DOWNMIX_PARAM_TYPE });
234     }
235 
236     /**
237      * Definitions for the downmix effect. Taken from
238      * system/media/audio/include/system/audio_effects/effect_downmix.h
239      * This effect is normally not exposed to applications.
240      */
241     private static final UUID EFFECT_TYPE_DOWNMIX = UUID
242             .fromString("381e49cc-a858-4aa2-87f6-e8388e7601b2");
243     private static final int DOWNMIX_PARAM_TYPE = 0;
244 
245     //Testing security bug: 63526567
246     @SecurityTest(minPatchLevel = "2017-10")
testEnvironmentalReverb_setParameter()247     public void testEnvironmentalReverb_setParameter() throws Exception {
248         verifyZeroPVSizeRejectedForSetParameter(
249                 AudioEffect.EFFECT_TYPE_ENV_REVERB, new int[] {
250                   EnvironmentalReverb.PARAM_ROOM_LEVEL,
251                   EnvironmentalReverb.PARAM_ROOM_HF_LEVEL,
252                   EnvironmentalReverb.PARAM_DECAY_TIME,
253                   EnvironmentalReverb.PARAM_DECAY_HF_RATIO,
254                   EnvironmentalReverb.PARAM_REFLECTIONS_LEVEL,
255                   EnvironmentalReverb.PARAM_REFLECTIONS_DELAY,
256                   EnvironmentalReverb.PARAM_REVERB_LEVEL,
257                   EnvironmentalReverb.PARAM_REVERB_DELAY,
258                   EnvironmentalReverb.PARAM_DIFFUSION,
259                   EnvironmentalReverb.PARAM_DENSITY,
260                   10 // EnvironmentalReverb.PARAM_PROPERTIES
261                 }
262         );
263     }
264 
265     //Testing security bug: 67647856
266     @SecurityTest(minPatchLevel = "2018-01")
testPresetReverb_setParameter()267     public void testPresetReverb_setParameter() throws Exception {
268         verifyZeroPVSizeRejectedForSetParameter(
269                 AudioEffect.EFFECT_TYPE_PRESET_REVERB, new int[] {
270                   PresetReverb.PARAM_PRESET
271                 }
272         );
273     }
274 
eqSetParamProperties(int media)275     private boolean eqSetParamProperties(int media) {
276         MediaPlayer mp = null;
277         Equalizer eq = null;
278         boolean status = false;
279         try {
280             mp = MediaPlayer.create(getInstrumentation().getContext(),  getMediaId(media));
281             eq = new Equalizer(0 /*priority*/, mp.getAudioSessionId());
282 
283             int shortSize = 2; //bytes
284 
285             int cmdCode = EFFECT_CMD_SET_PARAM;
286             byte command[] = concatArrays(/*status*/ intToByteArray(0),
287                     /*psize*/ intToByteArray(1 * intSize),
288                     /*vsize*/ intToByteArray(2 * shortSize),
289                     /*data[0]*/ intToByteArray((int) 9 /*EQ_PARAM_PROPERTIES*/),
290                     /*data[4]*/ shortToByteArray((short)-1 /*preset*/),
291                     /*data[6]*/ shortToByteArray((short)5 /*FIVEBAND_NUMBANDS*/));
292             byte reply[] = new byte[ 4 /*command.length*/];
293 
294             AudioEffect af = eq;
295             Object o = AudioEffect.class.getDeclaredMethod("command", int.class, byte[].class,
296                     byte[].class).invoke(af, cmdCode, command, reply);
297 
298             int replyValue = byteArrayToInt(reply, 0 /*offset*/);
299             if (replyValue >= 0) {
300                 Log.w(TAG, "Reply Value: " + replyValue);
301             }
302             assertTrue("Negative replyValue was expected ", replyValue < 0);
303             status = true;
304         } catch (Exception e) {
305             Log.w(TAG,"Problem setting parameter in equalizer");
306         } finally {
307             if (eq != null) {
308                 eq.release();
309             }
310             if (mp != null) {
311                 mp.release();
312             }
313         }
314         return status;
315     }
316 
eqGetParamFreqRangeCommand(int media)317     private boolean eqGetParamFreqRangeCommand(int media) {
318         MediaPlayer mp = null;
319         Equalizer eq = null;
320         boolean status = false;
321         try {
322             mp = MediaPlayer.create(getInstrumentation().getContext(), getMediaId(media));
323             eq = new Equalizer(0 /*priority*/, mp.getAudioSessionId());
324 
325             short band = 2;
326 
327             //baseline
328             int cmdCode = 8; // EFFECT_CMD_GET_PARAM
329             byte command[] = concatArrays(/*status*/ intToByteArray(0),
330                     /*psize*/ intToByteArray(2 * intSize),
331                     /*vsize*/ intToByteArray(2 * intSize),
332                     /*data[0]*/ intToByteArray(Equalizer.PARAM_BAND_FREQ_RANGE),
333                     /*data[1]*/ intToByteArray((int) band));
334 
335             byte reply[] = new byte[command.length];
336 
337             AudioEffect af = eq;
338             Object o = AudioEffect.class.getDeclaredMethod("command", int.class, byte[].class,
339                     byte[].class).invoke(af, cmdCode, command, reply);
340 
341             int methodStatus = AudioEffect.ERROR;
342             if (o != null) {
343                 methodStatus = Integer.valueOf(o.toString()).intValue();
344             }
345 
346             assertTrue("Command expected to fail", methodStatus <= 0);
347             int sum = 0;
348             for (int i = 0; i < reply.length; i++) {
349                 sum += Math.abs(reply[i]);
350             }
351 
352             assertEquals("reply expected to be all zeros", sum, 0);
353             status = true;
354         } catch (Exception e) {
355             Log.w(TAG,"Problem testing eqGetParamFreqRangeCommand");
356             status = false;
357         } finally {
358             if (eq != null) {
359                 eq.release();
360             }
361             if (mp != null) {
362                 mp.release();
363             }
364         }
365         return status;
366     }
367 
eqGetParam(int media, int command, int band, byte[] reply)368     private boolean eqGetParam(int media, int command, int band, byte[] reply) {
369         MediaPlayer mp = null;
370         Equalizer eq = null;
371         boolean status = false;
372         try {
373             mp = MediaPlayer.create(getInstrumentation().getContext(), getMediaId(media));
374             eq = new Equalizer(0 /*priority*/, mp.getAudioSessionId());
375 
376             AudioEffect af = eq;
377             int cmd[] = {command, band};
378 
379             AudioEffect.class.getDeclaredMethod("getParameter", int[].class,
380                     byte[].class).invoke(af, cmd, reply);
381             status = true;
382         } catch (Exception e) {
383             Log.w(TAG,"Problem testing equalizer");
384             status = false;
385         } finally {
386             if (eq != null) {
387                 eq.release();
388             }
389             if (mp != null) {
390                 mp.release();
391             }
392         }
393         return status;
394     }
395 
eqGetParam(int media, int command, int band, int[] reply)396     private boolean eqGetParam(int media, int command, int band, int[] reply) {
397         MediaPlayer mp = null;
398         Equalizer eq = null;
399         boolean status = false;
400         try {
401             mp = MediaPlayer.create(getInstrumentation().getContext(), getMediaId(media));
402             eq = new Equalizer(0 /*priority*/, mp.getAudioSessionId());
403 
404             AudioEffect af = eq;
405             int cmd[] = {command, band};
406 
407             AudioEffect.class.getDeclaredMethod("getParameter", int[].class,
408                     int[].class).invoke(af, cmd, reply);
409             status = true;
410         } catch (Exception e) {
411             Log.w(TAG,"Problem getting parameter from equalizer");
412             status = false;
413         } finally {
414             if (eq != null) {
415                 eq.release();
416             }
417             if (mp != null) {
418                 mp.release();
419             }
420         }
421         return status;
422     }
423 
testGetParam(int media, int command, int[] bandArray, int value0, int value1)424     private void testGetParam(int media, int command, int[] bandArray, int value0, int value1) {
425         int reply[] = {value0, value1};
426         for (int invalidBand : INVALID_BAND_ARRAY)
427         {
428             if (!eqGetParam(media, command, invalidBand, reply)) {
429                 fail(String.format("getParam for command %d did not complete successfully",
430                         command));
431             }
432             assertEquals("getParam should not change value0", value0, reply[0]);
433             assertEquals("getParam should not change value1", value1, reply[1]);
434         }
435     }
436 
eqSetParam(int media, int command, int band, short[] value)437     private boolean eqSetParam(int media, int command, int band, short[] value) {
438         MediaPlayer mp = null;
439         Equalizer eq = null;
440         boolean status = false;
441         try {
442             mp = MediaPlayer.create(getInstrumentation().getContext(),  getMediaId(media));
443             eq = new Equalizer(0 /*priority*/, mp.getAudioSessionId());
444 
445             AudioEffect af = eq;
446             int cmd[] = {command, band};
447 
448             AudioEffect.class.getDeclaredMethod("setParameter", int[].class,
449                     short[].class).invoke(af, cmd, value);
450             status = true;
451         } catch (Exception e) {
452             Log.w(TAG,"Problem setting parameter in equalizer");
453             status = false;
454         } finally {
455             if (eq != null) {
456                 eq.release();
457             }
458             if (mp != null) {
459                 mp.release();
460             }
461         }
462         return status;
463     }
464 
getMediaId(int media)465     private int getMediaId(int media) {
466         switch (media) {
467             default:
468             case MEDIA_SHORT:
469                 return R.raw.good;
470             case MEDIA_LONG:
471                 return R.raw.onekhzsine_90sec;
472         }
473     }
474 
475     // Verifies that for all the effects of the specified type
476     // an attempt to pass psize = 0 or vsize = 0 to 'set parameter' command
477     // is rejected by the effect.
verifyZeroPVSizeRejectedForSetParameter( UUID effectType, final int paramCodes[])478     private void verifyZeroPVSizeRejectedForSetParameter(
479             UUID effectType, final int paramCodes[]) throws Exception {
480 
481         boolean effectFound = false;
482         for (AudioEffect.Descriptor descriptor : AudioEffect.queryEffects()) {
483             if (descriptor.type.compareTo(effectType) != 0) continue;
484 
485             effectFound = true;
486             AudioEffect ae = null;
487             MediaPlayer mp = null;
488             try {
489                 mp = MediaPlayer.create(getInstrumentation().getContext(), R.raw.good);
490                 java.lang.reflect.Constructor ct = AudioEffect.class.getConstructor(
491                         UUID.class, UUID.class, int.class, int.class);
492                 try {
493                     ae = (AudioEffect) ct.newInstance(descriptor.type, descriptor.uuid,
494                             /*priority*/ 0, mp.getAudioSessionId());
495                 } catch (Exception e) {
496                     // Not every effect can be instantiated by apps.
497                     Log.w(TAG, "Failed to create effect " + descriptor.uuid);
498                     continue;
499                 }
500                 java.lang.reflect.Method command = AudioEffect.class.getDeclaredMethod(
501                         "command", int.class, byte[].class, byte[].class);
502                 for (int paramCode : paramCodes) {
503                     executeSetParameter(ae, command, intSize, 0, paramCode);
504                     executeSetParameter(ae, command, 0, intSize, paramCode);
505                 }
506             } finally {
507                 if (ae != null) {
508                     ae.release();
509                 }
510                 if (mp != null) {
511                     mp.release();
512                 }
513             }
514         }
515 
516         if (!effectFound) {
517             Log.w(TAG, "No effect with type " + effectType + " was found");
518         }
519     }
520 
executeSetParameter(AudioEffect ae, java.lang.reflect.Method command, int paramSize, int valueSize, int paramCode)521     private void executeSetParameter(AudioEffect ae, java.lang.reflect.Method command,
522             int paramSize, int valueSize, int paramCode) throws Exception {
523         byte cmdBuf[] = concatArrays(/*status*/ intToByteArray(0),
524                 /*psize*/ intToByteArray(paramSize),
525                 /*vsize*/ intToByteArray(valueSize),
526                 /*data[0]*/ intToByteArray(paramCode));
527         byte reply[] = new byte[intSize];
528         Integer ret = (Integer)command.invoke(ae, EFFECT_CMD_SET_PARAM, cmdBuf, reply);
529         if (ret >= 0) {
530             int val = byteArrayToInt(reply, 0 /*offset*/);
531             assertTrue("Negative reply value expected, effect " + ae.getDescriptor().uuid +
532                     ", parameter " + paramCode + ", reply value " + val,
533                     val < 0);
534         } else {
535             // Some effect implementations detect this condition at the command dispatch level,
536             // and reject command execution. That's also OK, but log a message so the test
537             // author can double check if 'paramCode' is correct.
538             Log.w(TAG, "\"Set parameter\" command rejected for effect " + ae.getDescriptor().uuid +
539                     ", parameter " + paramCode + ", return code " + ret);
540         }
541     }
542 
hasEqualizer()543     private boolean hasEqualizer() {
544         boolean result = false;
545         try {
546             MediaPlayer mp = MediaPlayer.create(getInstrumentation().getContext(), R.raw.good);
547             new Equalizer(0 /*priority*/, mp.getAudioSessionId());
548             result = true;
549         } catch (Exception e) {
550             Log.d(TAG, "Cannot create equalizer");
551         }
552         return result;
553     }
554 
intToByteArray(int value)555     private static byte[] intToByteArray(int value) {
556         ByteBuffer converter = ByteBuffer.allocate(4);
557         converter.order(ByteOrder.nativeOrder());
558         converter.putInt(value);
559         return converter.array();
560     }
561 
byteArrayToInt(byte[] valueBuf, int offset)562     public static int byteArrayToInt(byte[] valueBuf, int offset) {
563         ByteBuffer converter = ByteBuffer.wrap(valueBuf);
564         converter.order(ByteOrder.nativeOrder());
565         return converter.getInt(offset);
566     }
567 
shortToByteArray(short value)568     private static byte[] shortToByteArray(short value) {
569         ByteBuffer converter = ByteBuffer.allocate(2);
570         converter.order(ByteOrder.nativeOrder());
571         short sValue = (short) value;
572         converter.putShort(sValue);
573         return converter.array();
574     }
575 
concatArrays(byte[]... arrays)576     private static  byte[] concatArrays(byte[]... arrays) {
577         int len = 0;
578         for (byte[] a : arrays) {
579             len += a.length;
580         }
581         byte[] b = new byte[len];
582 
583         int offs = 0;
584         for (byte[] a : arrays) {
585             System.arraycopy(a, 0, b, offs, a.length);
586             offs += a.length;
587         }
588         return b;
589     }
590 }
591