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