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