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