1 /* 2 * Copyright (C) 2023 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.os.vibrator.persistence; 18 19 import static android.os.VibrationEffect.Composition.DELAY_TYPE_PAUSE; 20 import static android.os.VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET; 21 import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK; 22 import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK; 23 import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN; 24 import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK; 25 import static android.os.vibrator.persistence.VibrationXmlParser.isSupportedMimeType; 26 27 import static com.google.common.truth.Truth.assertThat; 28 29 import static org.junit.Assert.assertArrayEquals; 30 import static org.junit.Assert.assertThrows; 31 32 import android.os.PersistableBundle; 33 import android.os.VibrationEffect; 34 import android.os.vibrator.Flags; 35 import android.os.vibrator.PrebakedSegment; 36 import android.os.vibrator.PrimitiveSegment; 37 import android.platform.test.annotations.DisableFlags; 38 import android.platform.test.annotations.EnableFlags; 39 import android.platform.test.flag.junit.SetFlagsRule; 40 import android.util.Xml; 41 42 import com.android.modules.utils.TypedXmlPullParser; 43 44 import org.junit.Rule; 45 import org.junit.Test; 46 import org.junit.runner.RunWith; 47 import org.junit.runners.JUnit4; 48 import org.xmlpull.v1.XmlPullParser; 49 50 import java.io.ByteArrayOutputStream; 51 import java.io.IOException; 52 import java.io.StringReader; 53 import java.io.StringWriter; 54 import java.util.Arrays; 55 import java.util.Base64; 56 import java.util.HashMap; 57 import java.util.Map; 58 import java.util.Set; 59 60 /** 61 * Unit tests for {@link VibrationXmlParser} and {@link VibrationXmlSerializer}. 62 * 63 * <p>The {@link VibrationEffect} public APIs are covered by CTS to enforce the schema defined at 64 * services/core/xsd/vibrator/vibration/vibration.xsd. 65 */ 66 @RunWith(JUnit4.class) 67 public class VibrationEffectXmlSerializationTest { 68 69 @Rule 70 public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 71 72 @Test isSupportedMimeType_onlySupportsVibrationXmlMimeType()73 public void isSupportedMimeType_onlySupportsVibrationXmlMimeType() { 74 // Single MIME type supported 75 assertThat(isSupportedMimeType( 76 VibrationXmlParser.APPLICATION_VIBRATION_XML_MIME_TYPE)).isTrue(); 77 assertThat(isSupportedMimeType("application/vnd.android.haptics.vibration+xml")).isTrue(); 78 // without xml suffix not supported 79 assertThat(isSupportedMimeType("application/vnd.android.haptics.vibration")).isFalse(); 80 // different top-level not supported 81 assertThat(isSupportedMimeType("haptics/vnd.android.haptics.vibration+xml")).isFalse(); 82 // different type not supported 83 assertThat(isSupportedMimeType("application/vnd.android.vibration+xml")).isFalse(); 84 } 85 86 @Test testParseElement_fromVibrationTag_succeedAndParserPointsToEndVibrationTag()87 public void testParseElement_fromVibrationTag_succeedAndParserPointsToEndVibrationTag() 88 throws Exception { 89 VibrationEffect effect = VibrationEffect.startComposition() 90 .addPrimitive(PRIMITIVE_CLICK) 91 .addPrimitive(PRIMITIVE_TICK, 0.2497f) 92 .compose(); 93 String xml = """ 94 <vibration-effect> 95 <primitive-effect name="click"/> 96 <primitive-effect name="tick" scale="0.2497"/> 97 </vibration-effect> 98 """.trim(); 99 VibrationEffect effect2 = VibrationEffect.startComposition() 100 .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356) 101 .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7) 102 .compose(); 103 String xml2 = """ 104 <vibration-effect> 105 <primitive-effect name="low_tick" delayMs="356"/> 106 <primitive-effect name="spin" scale="0.6364" delayMs="7"/> 107 </vibration-effect> 108 """.trim(); 109 110 TypedXmlPullParser parser = createXmlPullParser(xml); 111 assertParseElementSucceeds(parser, effect); 112 parser.next(); 113 assertEndOfDocument(parser); 114 115 // Test no-issues when an end-tag follows the vibration XML. 116 // To test this, starting with the corresponding "start-tag" is necessary. 117 parser = createXmlPullParser("<next-tag>" + xml + "</next-tag>"); 118 // Move the parser once to point to the "<vibration-effect> tag. 119 parser.next(); 120 assertParseElementSucceeds(parser, effect); 121 parser.next(); 122 assertEndTag(parser, "next-tag"); 123 124 parser = createXmlPullParser(xml + "<next-tag>"); 125 assertParseElementSucceeds(parser, effect); 126 parser.next(); 127 assertStartTag(parser, "next-tag"); 128 129 parser = createXmlPullParser(xml + xml2); 130 assertParseElementSucceeds(parser, effect); 131 parser.next(); 132 assertParseElementSucceeds(parser, effect2); 133 parser.next(); 134 assertEndOfDocument(parser); 135 136 // Check when there is comment before the end tag. 137 xml = """ 138 <vibration-effect> 139 <primitive-effect name="tick"/> 140 <!-- hi --> 141 </vibration-effect> 142 """.trim(); 143 parser = createXmlPullParser(xml); 144 assertParseElementSucceeds( 145 parser, VibrationEffect.startComposition().addPrimitive(PRIMITIVE_TICK).compose()); 146 } 147 148 @Test 149 public void testParseElement_fromVibrationSelectTag_succeedAndParserPointsToEndVibrationSelectTag()150 testParseElement_fromVibrationSelectTag_succeedAndParserPointsToEndVibrationSelectTag() 151 throws Exception { 152 VibrationEffect effect1 = VibrationEffect.startComposition() 153 .addPrimitive(PRIMITIVE_CLICK) 154 .addPrimitive(PRIMITIVE_TICK, 0.2497f) 155 .compose(); 156 String vibrationXml1 = """ 157 <vibration-effect> 158 <primitive-effect name="click"/> 159 <primitive-effect name="tick" scale="0.2497"/> 160 </vibration-effect> 161 """.trim(); 162 VibrationEffect effect2 = VibrationEffect.startComposition() 163 .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356) 164 .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7) 165 .compose(); 166 String vibrationXml2 = """ 167 <vibration-effect> 168 <primitive-effect name="low_tick" delayMs="356"/> 169 <primitive-effect name="spin" scale="0.6364" delayMs="7"/> 170 </vibration-effect> 171 """.trim(); 172 173 String xml = "<vibration-select>" + vibrationXml1 + vibrationXml2 + "</vibration-select>"; 174 TypedXmlPullParser parser = createXmlPullParser(xml); 175 assertParseElementSucceeds(parser, effect1, effect2); 176 parser.next(); 177 assertEndOfDocument(parser); 178 179 // Test no-issues when an end-tag follows the vibration XML. 180 // To test this, starting with the corresponding "start-tag" is necessary. 181 parser = createXmlPullParser("<next-tag>" + xml + "</next-tag>"); 182 // Move the parser once to point to the "<vibration-effect> tag. 183 parser.next(); 184 assertParseElementSucceeds(parser, effect1, effect2); 185 parser.next(); 186 assertEndTag(parser, "next-tag"); 187 188 parser = createXmlPullParser(xml + "<next-tag>"); 189 assertParseElementSucceeds(parser, effect1, effect2); 190 parser.next(); 191 assertStartTag(parser, "next-tag"); 192 193 xml = "<vibration-select>" + vibrationXml1 + vibrationXml2 + "</vibration-select>" 194 + "<vibration-select>" + vibrationXml2 + vibrationXml1 + "</vibration-select>" 195 + vibrationXml1; 196 parser = createXmlPullParser(xml); 197 assertParseElementSucceeds(parser, effect1, effect2); 198 parser.next(); 199 assertParseElementSucceeds(parser, effect2, effect1); 200 parser.next(); 201 assertParseElementSucceeds(parser, effect1); 202 parser.next(); 203 assertEndOfDocument(parser); 204 205 // Check when there is comment before the end tag. 206 xml = "<vibration-select>" + vibrationXml1 + "<!-- comment --></vibration-select>"; 207 parser = createXmlPullParser(xml); 208 parser.next(); 209 assertParseElementSucceeds(parser, effect1); 210 } 211 212 @Test testParseElement_withHiddenApis_onlySucceedsWithFlag()213 public void testParseElement_withHiddenApis_onlySucceedsWithFlag() throws Exception { 214 // Check when the root tag is "vibration". 215 String xml = """ 216 <vibration-effect> 217 <predefined-effect name="texture_tick"/> 218 </vibration-effect> 219 """.trim(); 220 assertParseElementSucceeds(createXmlPullParser(xml), 221 VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS, 222 VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK)); 223 assertParseElementFails(xml); 224 225 // Check when the root tag is "vibration-select". 226 xml = "<vibration-select>" + xml + "</vibration-select>"; 227 assertParseElementSucceeds(createXmlPullParser(xml), 228 VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS, 229 VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK)); 230 assertParseElementFails(xml); 231 } 232 233 @Test testParseElement_badXml_throwsException()234 public void testParseElement_badXml_throwsException() { 235 // No "vibration-select" tag. 236 assertParseElementFails(""" 237 <vibration-effect> 238 rand text 239 <primitive-effect name="click"/> 240 </vibration-effect> 241 """); 242 assertParseElementFails(""" 243 <bad-tag> 244 <primitive-effect name="click"/> 245 </vibration-effect> 246 """); 247 assertParseElementFails(""" 248 <primitive-effect name="click"/> 249 </vibration-effect> 250 """); 251 assertParseElementFails(""" 252 <vibration-effect> 253 <primitive-effect name="click"/> 254 """); 255 256 // Incomplete XML. 257 assertParseElementFails(""" 258 <vibration-select> 259 <primitive-effect name="click"/> 260 """); 261 assertParseElementFails(""" 262 <vibration-select> 263 <vibration-effect> 264 <primitive-effect name="low_tick" delayMs="356"/> 265 </vibration-effect> 266 """); 267 268 // Bad vibration XML. 269 assertParseElementFails(""" 270 <vibration-select> 271 <primitive-effect name="low_tick" delayMs="356"/> 272 </vibration-effect> 273 </vibration-select> 274 """); 275 276 // "vibration-select" tag should have no attributes. 277 assertParseElementFails(""" 278 <vibration-select bad_attr="123"> 279 <vibration-effect> 280 <predefined-effect name="tick"/> 281 </vibration-effect> 282 </vibration-select> 283 """); 284 } 285 286 @Test testInvalidEffects_allFail()287 public void testInvalidEffects_allFail() { 288 // Invalid root tag. 289 String xml = """ 290 <vibration> 291 <predefined-effect name="click"/> 292 </vibration> 293 """; 294 295 assertPublicApisParserFails(xml); 296 assertHiddenApisParserFails(xml); 297 298 // Invalid effect name. 299 xml = """ 300 <vibration-effect> 301 <predefined-effect name="invalid"/> 302 </vibration-effect> 303 """; 304 305 assertPublicApisParserFails(xml); 306 assertHiddenApisParserFails(xml); 307 } 308 309 @Test testVibrationSelectTag_onlyParseDocumentSucceeds()310 public void testVibrationSelectTag_onlyParseDocumentSucceeds() throws Exception { 311 VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); 312 String xml = """ 313 <vibration-select> 314 <vibration-effect><predefined-effect name="click"/></vibration-effect> 315 </vibration-select> 316 """; 317 318 assertPublicApisParseDocumentSucceeds(xml, effect); 319 assertHiddenApisParseDocumentSucceeds(xml, effect); 320 321 assertPublicApisParseVibrationEffectFails(xml); 322 assertHiddenApisParseVibrationEffectFails(xml); 323 } 324 325 @Test testPrimitives_allSucceed()326 public void testPrimitives_allSucceed() throws Exception { 327 VibrationEffect effect = VibrationEffect.startComposition() 328 .addPrimitive(PRIMITIVE_CLICK) 329 .addPrimitive(PRIMITIVE_TICK, 0.2497f) 330 .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356) 331 .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7) 332 .compose(); 333 String xml = """ 334 <vibration-effect> 335 <primitive-effect name="click"/> 336 <primitive-effect name="tick" scale="0.2497"/> 337 <primitive-effect name="low_tick" delayMs="356"/> 338 <primitive-effect name="spin" scale="0.6364" delayMs="7"/> 339 </vibration-effect> 340 """; 341 342 assertPublicApisParserSucceeds(xml, effect); 343 assertPublicApisSerializerSucceeds(effect, "click", "tick", "low_tick", "spin"); 344 assertPublicApisRoundTrip(effect); 345 346 assertHiddenApisParserSucceeds(xml, effect); 347 assertHiddenApisSerializerSucceeds(effect, "click", "tick", "low_tick", "spin"); 348 assertHiddenApisRoundTrip(effect); 349 } 350 351 @Test testWaveforms_allSucceed()352 public void testWaveforms_allSucceed() throws Exception { 353 VibrationEffect effect = VibrationEffect.createWaveform(new long[]{123, 456, 789, 0}, 354 new int[]{254, 1, 255, 0}, /* repeat= */ 0); 355 String xml = """ 356 <vibration-effect> 357 <waveform-effect> 358 <repeating> 359 <waveform-entry durationMs="123" amplitude="254"/> 360 <waveform-entry durationMs="456" amplitude="1"/> 361 <waveform-entry durationMs="789" amplitude="255"/> 362 <waveform-entry durationMs="0" amplitude="0"/> 363 </repeating> 364 </waveform-effect> 365 </vibration-effect> 366 """; 367 368 assertPublicApisParserSucceeds(xml, effect); 369 assertPublicApisSerializerSucceeds(effect, "123", "456", "789", "254", "1", "255", "0"); 370 assertPublicApisRoundTrip(effect); 371 372 assertHiddenApisParserSucceeds(xml, effect); 373 assertHiddenApisSerializerSucceeds(effect, "123", "456", "789", "254", "1", "255", "0"); 374 assertHiddenApisRoundTrip(effect); 375 } 376 377 @Test testPredefinedEffects_publicEffectsWithDefaultFallback_allSucceed()378 public void testPredefinedEffects_publicEffectsWithDefaultFallback_allSucceed() 379 throws Exception { 380 for (Map.Entry<String, Integer> entry : createPublicPredefinedEffectsMap().entrySet()) { 381 VibrationEffect effect = VibrationEffect.get(entry.getValue()); 382 String xml = String.format(""" 383 <vibration-effect> 384 <predefined-effect name="%s"/> 385 </vibration-effect> 386 """, 387 entry.getKey()); 388 389 assertPublicApisParserSucceeds(xml, effect); 390 assertPublicApisSerializerSucceeds(effect, entry.getKey()); 391 assertPublicApisRoundTrip(effect); 392 393 assertHiddenApisParserSucceeds(xml, effect); 394 assertHiddenApisSerializerSucceeds(effect, entry.getKey()); 395 assertHiddenApisRoundTrip(effect); 396 } 397 } 398 399 @Test testPredefinedEffects_hiddenEffects_onlySucceedsWithFlag()400 public void testPredefinedEffects_hiddenEffects_onlySucceedsWithFlag() throws Exception { 401 for (Map.Entry<String, Integer> entry : createHiddenPredefinedEffectsMap().entrySet()) { 402 VibrationEffect effect = VibrationEffect.get(entry.getValue()); 403 String xml = String.format(""" 404 <vibration-effect> 405 <predefined-effect name="%s"/> 406 </vibration-effect> 407 """, 408 entry.getKey()); 409 410 assertPublicApisParserFails(xml); 411 assertPublicApisSerializerFails(effect); 412 413 assertHiddenApisParserSucceeds(xml, effect); 414 assertHiddenApisSerializerSucceeds(effect, entry.getKey()); 415 assertHiddenApisRoundTrip(effect); 416 } 417 } 418 419 @Test testPredefinedEffects_allEffectsWithNonDefaultFallback_onlySucceedsWithFlag()420 public void testPredefinedEffects_allEffectsWithNonDefaultFallback_onlySucceedsWithFlag() 421 throws Exception { 422 for (Map.Entry<String, Integer> entry : createAllPredefinedEffectsMap().entrySet()) { 423 boolean nonDefaultFallback = !PrebakedSegment.DEFAULT_SHOULD_FALLBACK; 424 VibrationEffect effect = VibrationEffect.get(entry.getValue(), nonDefaultFallback); 425 String xml = String.format(""" 426 <vibration-effect> 427 <predefined-effect name="%s" fallback="%s"/> 428 </vibration-effect> 429 """, 430 entry.getKey(), nonDefaultFallback); 431 432 assertPublicApisParserFails(xml); 433 assertPublicApisSerializerFails(effect); 434 435 assertHiddenApisParserSucceeds(xml, effect); 436 assertHiddenApisSerializerSucceeds(effect, entry.getKey()); 437 assertHiddenApisRoundTrip(effect); 438 } 439 } 440 441 @Test 442 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testWaveformEnvelopeEffect_allSucceed()443 public void testWaveformEnvelopeEffect_allSucceed() throws Exception { 444 VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder() 445 .addControlPoint(0.2f, 80f, 10) 446 .addControlPoint(0.5f, 150f, 10) 447 .build(); 448 449 String xml = """ 450 <vibration-effect> 451 <waveform-envelope-effect> 452 <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" /> 453 <control-point amplitude="0.5" frequencyHz="150.0" durationMs="10" /> 454 </waveform-envelope-effect> 455 </vibration-effect> 456 """; 457 458 assertPublicApisParserSucceeds(xml, effect); 459 assertPublicApisSerializerSucceeds(effect, xml); 460 assertPublicApisRoundTrip(effect); 461 assertHiddenApisParserSucceeds(xml, effect); 462 assertHiddenApisSerializerSucceeds(effect, xml); 463 assertHiddenApisRoundTrip(effect); 464 } 465 466 @Test 467 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testWaveformEnvelopeEffectWithInitialFrequency_allSucceed()468 public void testWaveformEnvelopeEffectWithInitialFrequency_allSucceed() throws Exception { 469 VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder() 470 .setInitialFrequencyHz(20) 471 .addControlPoint(0.2f, 80f, 10) 472 .addControlPoint(0.5f, 150f, 10) 473 .build(); 474 475 String xml = """ 476 <vibration-effect> 477 <waveform-envelope-effect initialFrequencyHz="20.0"> 478 <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" /> 479 <control-point amplitude="0.5" frequencyHz="150.0" durationMs="10" /> 480 </waveform-envelope-effect> 481 </vibration-effect> 482 """; 483 484 assertPublicApisParserSucceeds(xml, effect); 485 assertPublicApisSerializerSucceeds(effect, xml); 486 assertPublicApisRoundTrip(effect); 487 assertHiddenApisParserSucceeds(xml, effect); 488 assertHiddenApisSerializerSucceeds(effect, xml); 489 assertHiddenApisRoundTrip(effect); 490 } 491 492 @Test 493 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testWaveformEnvelopeEffect_badXml_throwsException()494 public void testWaveformEnvelopeEffect_badXml_throwsException() throws IOException { 495 // Incomplete XML 496 assertParseElementFails(""" 497 <vibration-effect> 498 <waveform-envelope-effect> 499 <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" /> 500 </vibration-effect> 501 """); 502 assertParseElementFails(""" 503 <vibration-effect> 504 <waveform-envelope-effect> 505 <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10"> 506 </waveform-envelope-effect> 507 </vibration-effect> 508 """); 509 assertParseElementFails(""" 510 <vibration-effect> 511 <waveform-envelope-effect> 512 <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" /> 513 </waveform-envelope-effect> 514 """); 515 516 // Bad vibration XML 517 assertParseElementFails(""" 518 <vibration-effect> 519 <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" /> 520 </waveform-envelope-effect> 521 </vibration-effect> 522 """); 523 524 // "waveform-envelope-effect" tag with invalid attributes 525 assertParseElementFails(""" 526 <vibration-effect> 527 <waveform-envelope-effect init_freq="20.0"> 528 <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" /> 529 </waveform-envelope-effect> 530 </vibration-effect> 531 """); 532 } 533 534 @Test 535 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testWaveformEnvelopeEffect_noControlPoints_allFail()536 public void testWaveformEnvelopeEffect_noControlPoints_allFail() throws IOException { 537 String xml = "<vibration-effect><waveform-envelope-effect/></vibration-effect>"; 538 assertPublicApisParserFails(xml); 539 assertHiddenApisParserFails(xml); 540 541 xml = "<vibration-effect><waveform-envelope-effect> \n " 542 + "</waveform-envelope-effect></vibration-effect>"; 543 assertPublicApisParserFails(xml); 544 assertHiddenApisParserFails(xml); 545 546 xml = "<vibration-effect><waveform-envelope-effect>invalid</waveform-envelope-effect" 547 + "></vibration-effect>"; 548 assertPublicApisParserFails(xml); 549 assertHiddenApisParserFails(xml); 550 551 xml = """ 552 <vibration-effect> 553 <waveform-envelope-effect> 554 <control-point /> 555 </waveform-envelope-effect> 556 </vibration-effect> 557 """; 558 assertPublicApisParserFails(xml); 559 assertHiddenApisParserFails(xml); 560 561 xml = """ 562 <vibration-effect> 563 <waveform-envelope-effect initialFrequencyHz="20.0" /> 564 </vibration-effect> 565 """; 566 assertPublicApisParserFails(xml); 567 assertHiddenApisParserFails(xml); 568 569 xml = """ 570 <vibration-effect> 571 <waveform-envelope-effect initialFrequencyHz="20.0"> \n </waveform-envelope-effect> 572 </vibration-effect> 573 """; 574 assertPublicApisParserFails(xml); 575 assertHiddenApisParserFails(xml); 576 577 xml = """ 578 <vibration-effect> 579 <waveform-envelope-effect initialFrequencyHz="20.0"> 580 invalid 581 </waveform-envelope-effect> 582 </vibration-effect> 583 """; 584 assertPublicApisParserFails(xml); 585 assertHiddenApisParserFails(xml); 586 587 xml = """ 588 <vibration-effect> 589 <waveform-envelope-effect initialFrequencyHz="20.0"> 590 <control-point /> 591 </waveform-envelope-effect> 592 </vibration-effect> 593 """; 594 assertPublicApisParserFails(xml); 595 assertHiddenApisParserFails(xml); 596 } 597 598 @Test 599 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testWaveformEnvelopeEffect_badControlPointData_allFail()600 public void testWaveformEnvelopeEffect_badControlPointData_allFail() throws IOException { 601 String xml = """ 602 <vibration-effect> 603 <waveform-envelope-effect> 604 <control-point amplitude="-1" frequencyHz="80.0" durationMs="100" /> 605 </waveform-envelope-effect> 606 </vibration-effect> 607 """; 608 assertPublicApisParserFails(xml); 609 assertHiddenApisParserFails(xml); 610 611 xml = """ 612 <vibration-effect> 613 <waveform-envelope-effect> 614 <control-point amplitude="0.2" frequencyHz="0" durationMs="100" /> 615 </waveform-envelope-effect> 616 </vibration-effect> 617 """; 618 assertPublicApisParserFails(xml); 619 assertHiddenApisParserFails(xml); 620 621 xml = """ 622 <vibration-effect> 623 <waveform-envelope-effect initialFrequencyHz="0"> 624 <control-point amplitude="0.2" frequencyHz="30" durationMs="100" /> 625 </waveform-envelope-effect> 626 </vibration-effect> 627 """; 628 assertPublicApisParserFails(xml); 629 assertHiddenApisParserFails(xml); 630 631 xml = """ 632 <vibration-effect> 633 <waveform-envelope-effect> 634 <control-point amplitude="0.2" frequencyHz="80.0" durationMs="0" /> 635 </waveform-envelope-effect> 636 </vibration-effect> 637 """; 638 assertPublicApisParserFails(xml); 639 assertHiddenApisParserFails(xml); 640 641 xml = """ 642 <vibration-effect> 643 <waveform-envelope-effect> 644 <control-point amplitude="0.2" /> 645 </waveform-envelope-effect> 646 </vibration-effect> 647 """; 648 assertPublicApisParserFails(xml); 649 assertHiddenApisParserFails(xml); 650 } 651 652 @Test 653 @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testWaveformEnvelopeEffect_featureFlagDisabled_allFail()654 public void testWaveformEnvelopeEffect_featureFlagDisabled_allFail() throws Exception { 655 VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder() 656 .setInitialFrequencyHz(20) 657 .addControlPoint(0.2f, 80f, 10) 658 .addControlPoint(0.5f, 150f, 10) 659 .build(); 660 661 String xml = """ 662 <vibration-effect> 663 <waveform-envelope-effect initialFrequencyHz="20.0"> 664 <control-point amplitude="0.2" frequencyHz="80.0" durationMs="10" /> 665 <control-point amplitude="0.5" frequencyHz="150.0" durationMs="10" /> 666 </waveform-envelope-effect> 667 </vibration-effect> 668 """; 669 670 assertPublicApisParserFails(xml); 671 assertPublicApisSerializerFails(effect); 672 assertHiddenApisParserFails(xml); 673 assertHiddenApisSerializerFails(effect); 674 } 675 676 @Test 677 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testBasicEnvelopeEffect_allSucceed()678 public void testBasicEnvelopeEffect_allSucceed() throws Exception { 679 VibrationEffect effect = new VibrationEffect.BasicEnvelopeBuilder() 680 .addControlPoint(0.2f, 0.5f, 10) 681 .addControlPoint(0.0f, 1f, 10) 682 .build(); 683 684 String xml = """ 685 <vibration-effect> 686 <basic-envelope-effect> 687 <control-point intensity="0.2" sharpness="0.5" durationMs="10" /> 688 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 689 </basic-envelope-effect> 690 </vibration-effect> 691 """; 692 693 assertPublicApisParserSucceeds(xml, effect); 694 assertPublicApisSerializerSucceeds(effect, xml); 695 assertPublicApisRoundTrip(effect); 696 assertHiddenApisParserSucceeds(xml, effect); 697 assertHiddenApisSerializerSucceeds(effect, xml); 698 assertHiddenApisRoundTrip(effect); 699 } 700 701 @Test 702 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testBasicEnvelopeEffectWithInitialSharpness_allSucceed()703 public void testBasicEnvelopeEffectWithInitialSharpness_allSucceed() throws Exception { 704 VibrationEffect effect = new VibrationEffect.BasicEnvelopeBuilder() 705 .setInitialSharpness(0.3f) 706 .addControlPoint(0.2f, 0.5f, 10) 707 .addControlPoint(0.0f, 1f, 10) 708 .build(); 709 710 String xml = """ 711 <vibration-effect> 712 <basic-envelope-effect initialSharpness="0.3"> 713 <control-point intensity="0.2" sharpness="0.5" durationMs="10" /> 714 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 715 </basic-envelope-effect> 716 </vibration-effect> 717 """; 718 719 assertPublicApisParserSucceeds(xml, effect); 720 assertPublicApisSerializerSucceeds(effect, xml); 721 assertPublicApisRoundTrip(effect); 722 assertHiddenApisParserSucceeds(xml, effect); 723 assertHiddenApisSerializerSucceeds(effect, xml); 724 assertHiddenApisRoundTrip(effect); 725 } 726 727 @Test 728 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testBasicEnvelopeEffect_badXml_throwsException()729 public void testBasicEnvelopeEffect_badXml_throwsException() throws IOException { 730 // Incomplete XML 731 assertParseElementFails(""" 732 <vibration-effect> 733 <basic-envelope-effect> 734 <control-point intensity="0.2" sharpness="0.8" durationMs="10" /> 735 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 736 </vibration-effect> 737 """); 738 assertParseElementFails(""" 739 <vibration-effect> 740 <basic-envelope-effect> 741 <control-point intensity="0.2" sharpness="0.8" durationMs="10"> 742 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 743 </basic-envelope-effect> 744 </vibration-effect> 745 """); 746 assertParseElementFails(""" 747 <vibration-effect> 748 <basic-envelope-effect> 749 <control-point intensity="0.2" sharpness="0.8" durationMs="10" /> 750 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 751 </basic-envelope-effect> 752 """); 753 754 // Bad vibration XML 755 assertParseElementFails(""" 756 <vibration-effect> 757 <control-point intensity="0.2" sharpness="0.8" durationMs="10" /> 758 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 759 </basic-envelope-effect> 760 </vibration-effect> 761 """); 762 763 // "basic-envelope-effect" tag with invalid attributes 764 assertParseElementFails(""" 765 <vibration-effect> 766 <basic-envelope-effect init_sharp="20.0"> 767 <control-point intensity="0.2" sharpness="0.8" durationMs="10" /> 768 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 769 </basic-envelope-effect> 770 </vibration-effect> 771 """); 772 } 773 774 @Test 775 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testBasicEnvelopeEffect_noControlPoints_allFail()776 public void testBasicEnvelopeEffect_noControlPoints_allFail() throws IOException { 777 String xml = "<vibration-effect><basic-envelope-effect/></vibration-effect>"; 778 assertPublicApisParserFails(xml); 779 assertHiddenApisParserFails(xml); 780 781 xml = "<vibration-effect><basic-envelope-effect> \n " 782 + "</basic-envelope-effect></vibration-effect>"; 783 assertPublicApisParserFails(xml); 784 assertHiddenApisParserFails(xml); 785 786 xml = "<vibration-effect><basic-envelope-effect>invalid</basic-envelope-effect" 787 + "></vibration-effect>"; 788 assertPublicApisParserFails(xml); 789 assertHiddenApisParserFails(xml); 790 791 xml = """ 792 <vibration-effect> 793 <basic-envelope-effect> 794 <control-point /> 795 </basic-envelope-effect> 796 </vibration-effect> 797 """; 798 assertPublicApisParserFails(xml); 799 assertHiddenApisParserFails(xml); 800 801 xml = """ 802 <vibration-effect> 803 <basic-envelope-effect initialSharpness="0.2" /> 804 </vibration-effect> 805 """; 806 assertPublicApisParserFails(xml); 807 assertHiddenApisParserFails(xml); 808 809 xml = """ 810 <vibration-effect> 811 <basic-envelope-effect initialSharpness="0.2"> \n </basic-envelope-effect> 812 </vibration-effect> 813 """; 814 assertPublicApisParserFails(xml); 815 assertHiddenApisParserFails(xml); 816 817 xml = """ 818 <vibration-effect> 819 <basic-envelope-effect initialSharpness="0.2"> 820 invalid 821 </basic-envelope-effect> 822 </vibration-effect> 823 """; 824 assertPublicApisParserFails(xml); 825 assertHiddenApisParserFails(xml); 826 827 xml = """ 828 <vibration-effect> 829 <basic-envelope-effect initialSharpness="0.2"> 830 <control-point /> 831 </basic-envelope-effect> 832 </vibration-effect> 833 """; 834 assertPublicApisParserFails(xml); 835 assertHiddenApisParserFails(xml); 836 } 837 838 @Test 839 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testBasicEnvelopeEffect_badControlPointData_allFail()840 public void testBasicEnvelopeEffect_badControlPointData_allFail() throws IOException { 841 String xml = """ 842 <vibration-effect> 843 <basic-envelope-effect> 844 <control-point intensity="-1" sharpness="0.8" durationMs="100" /> 845 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 846 </basic-envelope-effect> 847 </vibration-effect> 848 """; 849 assertPublicApisParserFails(xml); 850 assertHiddenApisParserFails(xml); 851 852 xml = """ 853 <vibration-effect> 854 <basic-envelope-effect> 855 <control-point intensity="0.2" sharpness="-1" durationMs="100" /> 856 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 857 </basic-envelope-effect> 858 </vibration-effect> 859 """; 860 assertPublicApisParserFails(xml); 861 assertHiddenApisParserFails(xml); 862 863 xml = """ 864 <vibration-effect> 865 <basic-envelope-effect initialSharpness="-1.0"> 866 <control-point intensity="0.2" sharpness="0.8" durationMs="0" /> 867 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 868 </basic-envelope-effect> 869 </vibration-effect> 870 """; 871 assertPublicApisParserFails(xml); 872 assertHiddenApisParserFails(xml); 873 874 xml = """ 875 <vibration-effect> 876 <basic-envelope-effect initialSharpness="2.0"> 877 <control-point intensity="0.2" sharpness="0.8" durationMs="0" /> 878 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 879 </basic-envelope-effect> 880 </vibration-effect> 881 """; 882 assertPublicApisParserFails(xml); 883 assertHiddenApisParserFails(xml); 884 885 xml = """ 886 <vibration-effect> 887 <basic-envelope-effect> 888 <control-point intensity="0.2" sharpness="0.8" durationMs="10" /> 889 <control-point intensity="0.5" sharpness="0.8" durationMs="10" /> 890 </basic-envelope-effect> 891 </vibration-effect> 892 """; 893 assertPublicApisParserFails(xml); 894 assertHiddenApisParserFails(xml); 895 896 xml = """ 897 <vibration-effect> 898 <basic-envelope-effect> 899 <control-point intensity="0.2" /> 900 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 901 </basic-envelope-effect> 902 </vibration-effect> 903 """; 904 assertPublicApisParserFails(xml); 905 assertHiddenApisParserFails(xml); 906 } 907 908 @Test 909 @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testBasicEnvelopeEffect_featureFlagDisabled_allFail()910 public void testBasicEnvelopeEffect_featureFlagDisabled_allFail() throws Exception { 911 VibrationEffect effect = new VibrationEffect.BasicEnvelopeBuilder() 912 .setInitialSharpness(0.3f) 913 .addControlPoint(0.2f, 0.5f, 10) 914 .addControlPoint(0.0f, 1f, 10) 915 .build(); 916 917 String xml = """ 918 <vibration-effect> 919 <basic-envelope-effect initialSharpness="0.3"> 920 <control-point intensity="0.2" sharpness="0.5" durationMs="10" /> 921 <control-point intensity="0.0" sharpness="1.0" durationMs="10" /> 922 </basic-envelope-effect> 923 </vibration-effect> 924 """; 925 926 assertPublicApisParserFails(xml); 927 assertPublicApisSerializerFails(effect); 928 929 assertHiddenApisParserFails(xml); 930 assertHiddenApisSerializerFails(effect); 931 } 932 933 @Test 934 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testRepeating_withWaveformEnvelopeEffect_allSucceed()935 public void testRepeating_withWaveformEnvelopeEffect_allSucceed() throws Exception { 936 VibrationEffect preamble = new VibrationEffect.WaveformEnvelopeBuilder() 937 .addControlPoint(0.1f, 50f, 10) 938 .addControlPoint(0.2f, 60f, 20) 939 .build(); 940 VibrationEffect repeating = new VibrationEffect.WaveformEnvelopeBuilder() 941 .setInitialFrequencyHz(70f) 942 .addControlPoint(0.3f, 80f, 25) 943 .addControlPoint(0.4f, 90f, 30) 944 .build(); 945 VibrationEffect effect = VibrationEffect.createRepeatingEffect(preamble, repeating); 946 947 String xml = """ 948 <vibration-effect> 949 <repeating-effect> 950 <preamble> 951 <waveform-envelope-effect> 952 <control-point amplitude="0.1" frequencyHz="50.0" durationMs="10"/> 953 <control-point amplitude="0.2" frequencyHz="60.0" durationMs="20"/> 954 </waveform-envelope-effect> 955 </preamble> 956 <repeating> 957 <waveform-envelope-effect initialFrequencyHz="70.0"> 958 <control-point amplitude="0.3" frequencyHz="80.0" durationMs="25"/> 959 <control-point amplitude="0.4" frequencyHz="90.0" durationMs="30"/> 960 </waveform-envelope-effect> 961 </repeating> 962 </repeating-effect> 963 </vibration-effect> 964 """; 965 966 assertPublicApisParserSucceeds(xml, effect); 967 assertPublicApisSerializerSucceeds(effect, "0.1", "0.2", "0.3", "0.4", "50.0", "60.0", 968 "70.0", "80.0", "90.0", "10", "20", "25", "30"); 969 assertPublicApisRoundTrip(effect); 970 971 assertHiddenApisParserSucceeds(xml, effect); 972 assertHiddenApisSerializerSucceeds(effect, "0.1", "0.2", "0.3", "0.4", "50.0", "60.0", 973 "70.0", "80.0", "90.0", "10", "20", "25", "30"); 974 assertHiddenApisRoundTrip(effect); 975 976 effect = VibrationEffect.createRepeatingEffect(repeating); 977 978 xml = """ 979 <vibration-effect> 980 <repeating-effect> 981 <repeating> 982 <waveform-envelope-effect initialFrequencyHz="70.0"> 983 <control-point amplitude="0.3" frequencyHz="80.0" durationMs="25"/> 984 <control-point amplitude="0.4" frequencyHz="90.0" durationMs="30"/> 985 </waveform-envelope-effect> 986 </repeating> 987 </repeating-effect> 988 </vibration-effect> 989 """; 990 991 assertPublicApisParserSucceeds(xml, effect); 992 assertPublicApisSerializerSucceeds(effect, "0.3", "0.4", "70.0", "80.0", "90.0", "25", 993 "30"); 994 assertPublicApisRoundTrip(effect); 995 996 assertHiddenApisParserSucceeds(xml, effect); 997 assertHiddenApisSerializerSucceeds(effect, "0.3", "0.4", "70.0", "80.0", "90.0", "25", 998 "30"); 999 assertHiddenApisRoundTrip(effect); 1000 } 1001 1002 @Test 1003 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testRepeating_withBasicEnvelopeEffect_allSucceed()1004 public void testRepeating_withBasicEnvelopeEffect_allSucceed() throws Exception { 1005 VibrationEffect preamble = new VibrationEffect.BasicEnvelopeBuilder() 1006 .addControlPoint(0.1f, 0.1f, 10) 1007 .addControlPoint(0.2f, 0.2f, 20) 1008 .addControlPoint(0.0f, 0.2f, 20) 1009 .build(); 1010 VibrationEffect repeating = new VibrationEffect.BasicEnvelopeBuilder() 1011 .setInitialSharpness(0.3f) 1012 .addControlPoint(0.3f, 0.4f, 25) 1013 .addControlPoint(0.4f, 0.6f, 30) 1014 .addControlPoint(0.0f, 0.7f, 35) 1015 .build(); 1016 VibrationEffect effect = VibrationEffect.createRepeatingEffect(preamble, repeating); 1017 1018 String xml = """ 1019 <vibration-effect> 1020 <repeating-effect> 1021 <preamble> 1022 <basic-envelope-effect> 1023 <control-point intensity="0.1" sharpness="0.1" durationMs="10" /> 1024 <control-point intensity="0.2" sharpness="0.2" durationMs="20" /> 1025 <control-point intensity="0.0" sharpness="0.2" durationMs="20" /> 1026 </basic-envelope-effect> 1027 </preamble> 1028 <repeating> 1029 <basic-envelope-effect initialSharpness="0.3"> 1030 <control-point intensity="0.3" sharpness="0.4" durationMs="25" /> 1031 <control-point intensity="0.4" sharpness="0.6" durationMs="30" /> 1032 <control-point intensity="0.0" sharpness="0.7" durationMs="35" /> 1033 </basic-envelope-effect> 1034 </repeating> 1035 </repeating-effect> 1036 </vibration-effect> 1037 """; 1038 1039 assertPublicApisParserSucceeds(xml, effect); 1040 assertPublicApisSerializerSucceeds(effect, "0.0", "0.1", "0.2", "0.3", "0.4", "0.1", "0.2", 1041 "0.3", "0.4", "0.6", "0.7", "10", "20", "25", "30", "35"); 1042 assertPublicApisRoundTrip(effect); 1043 1044 assertHiddenApisParserSucceeds(xml, effect); 1045 assertHiddenApisSerializerSucceeds(effect, "0.0", "0.1", "0.2", "0.3", "0.4", "0.1", "0.2", 1046 "0.3", "0.4", "0.6", "0.7", "10", "20", "25", "30", "35"); 1047 assertHiddenApisRoundTrip(effect); 1048 1049 effect = VibrationEffect.createRepeatingEffect(repeating); 1050 1051 xml = """ 1052 <vibration-effect> 1053 <repeating-effect> 1054 <repeating> 1055 <basic-envelope-effect initialSharpness="0.3"> 1056 <control-point intensity="0.3" sharpness="0.4" durationMs="25" /> 1057 <control-point intensity="0.4" sharpness="0.6" durationMs="30" /> 1058 <control-point intensity="0.0" sharpness="0.7" durationMs="35" /> 1059 </basic-envelope-effect> 1060 </repeating> 1061 </repeating-effect> 1062 </vibration-effect> 1063 """; 1064 1065 assertPublicApisParserSucceeds(xml, effect); 1066 assertPublicApisSerializerSucceeds(effect, "0.3", "0.4", "0.0", "0.4", "0.6", "0.7", "25", 1067 "30", "35"); 1068 assertPublicApisRoundTrip(effect); 1069 1070 assertHiddenApisParserSucceeds(xml, effect); 1071 assertHiddenApisSerializerSucceeds(effect, "0.3", "0.4", "0.0", "0.4", "0.6", "0.7", "25", 1072 "30", "35"); 1073 assertHiddenApisRoundTrip(effect); 1074 } 1075 1076 @Test 1077 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testRepeating_withPredefinedEffects_allSucceed()1078 public void testRepeating_withPredefinedEffects_allSucceed() throws Exception { 1079 for (Map.Entry<String, Integer> entry : createPublicPredefinedEffectsMap().entrySet()) { 1080 VibrationEffect preamble = VibrationEffect.get(entry.getValue()); 1081 VibrationEffect repeating = VibrationEffect.get(entry.getValue()); 1082 VibrationEffect effect = VibrationEffect.createRepeatingEffect(preamble, repeating); 1083 String xml = String.format(""" 1084 <vibration-effect> 1085 <repeating-effect> 1086 <preamble> 1087 <predefined-effect name="%s"/> 1088 </preamble> 1089 <repeating> 1090 <predefined-effect name="%s"/> 1091 </repeating> 1092 </repeating-effect> 1093 </vibration-effect> 1094 """, 1095 entry.getKey(), entry.getKey()); 1096 1097 assertPublicApisParserSucceeds(xml, effect); 1098 assertPublicApisSerializerSucceeds(effect, entry.getKey()); 1099 assertPublicApisRoundTrip(effect); 1100 1101 assertHiddenApisParserSucceeds(xml, effect); 1102 assertHiddenApisSerializerSucceeds(effect, entry.getKey()); 1103 assertHiddenApisRoundTrip(effect); 1104 1105 effect = VibrationEffect.createRepeatingEffect(repeating); 1106 xml = String.format(""" 1107 <vibration-effect> 1108 <repeating-effect> 1109 <repeating> 1110 <predefined-effect name="%s"/> 1111 </repeating> 1112 </repeating-effect> 1113 </vibration-effect> 1114 """, 1115 entry.getKey()); 1116 1117 assertPublicApisParserSucceeds(xml, effect); 1118 assertPublicApisSerializerSucceeds(effect, entry.getKey()); 1119 assertPublicApisRoundTrip(effect); 1120 1121 assertHiddenApisParserSucceeds(xml, effect); 1122 assertHiddenApisSerializerSucceeds(effect, entry.getKey()); 1123 assertHiddenApisRoundTrip(effect); 1124 } 1125 } 1126 1127 @Test 1128 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testRepeating_withWaveformEntry_allSucceed()1129 public void testRepeating_withWaveformEntry_allSucceed() throws Exception { 1130 VibrationEffect preamble = VibrationEffect.createWaveform(new long[]{123, 456, 789, 0}, 1131 new int[]{254, 1, 255, 0}, /* repeat= */ -1); 1132 VibrationEffect repeating = VibrationEffect.createWaveform(new long[]{123, 456, 789, 0}, 1133 new int[]{254, 1, 255, 0}, /* repeat= */ -1); 1134 VibrationEffect effect = VibrationEffect.createRepeatingEffect(preamble, repeating); 1135 1136 String xml = """ 1137 <vibration-effect> 1138 <repeating-effect> 1139 <preamble> 1140 <waveform-entry durationMs="123" amplitude="254"/> 1141 <waveform-entry durationMs="456" amplitude="1"/> 1142 <waveform-entry durationMs="789" amplitude="255"/> 1143 <waveform-entry durationMs="0" amplitude="0"/> 1144 </preamble> 1145 <repeating> 1146 <waveform-entry durationMs="123" amplitude="254"/> 1147 <waveform-entry durationMs="456" amplitude="1"/> 1148 <waveform-entry durationMs="789" amplitude="255"/> 1149 <waveform-entry durationMs="0" amplitude="0"/> 1150 </repeating> 1151 </repeating-effect> 1152 </vibration-effect> 1153 """; 1154 1155 assertPublicApisParserSucceeds(xml, effect); 1156 assertPublicApisSerializerSucceeds(effect, "123", "456", "789", "254", "1", "255", "0"); 1157 assertPublicApisRoundTrip(effect); 1158 1159 assertHiddenApisParserSucceeds(xml, effect); 1160 assertHiddenApisSerializerSucceeds(effect, "123", "456", "789", "254", "1", "255", "0"); 1161 assertHiddenApisRoundTrip(effect); 1162 1163 xml = """ 1164 <vibration-effect> 1165 <repeating-effect> 1166 <repeating> 1167 <waveform-entry durationMs="123" amplitude="254"/> 1168 <waveform-entry durationMs="456" amplitude="1"/> 1169 <waveform-entry durationMs="789" amplitude="255"/> 1170 <waveform-entry durationMs="0" amplitude="0"/> 1171 </repeating> 1172 </repeating-effect> 1173 </vibration-effect> 1174 """; 1175 1176 effect = VibrationEffect.createRepeatingEffect(repeating); 1177 1178 assertPublicApisParserSucceeds(xml, effect); 1179 assertPublicApisSerializerSucceeds(effect, "123", "456", "789", "254", "1", "255", "0"); 1180 assertPublicApisRoundTrip(effect); 1181 1182 assertHiddenApisParserSucceeds(xml, effect); 1183 assertHiddenApisSerializerSucceeds(effect, "123", "456", "789", "254", "1", "255", "0"); 1184 assertHiddenApisRoundTrip(effect); 1185 } 1186 1187 @Test 1188 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testRepeating_withPrimitives_allSucceed()1189 public void testRepeating_withPrimitives_allSucceed() throws Exception { 1190 VibrationEffect preamble = VibrationEffect.startComposition() 1191 .addPrimitive(PRIMITIVE_CLICK) 1192 .addPrimitive(PRIMITIVE_TICK, 0.2497f) 1193 .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356) 1194 .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7) 1195 .compose(); 1196 VibrationEffect repeating = VibrationEffect.startComposition() 1197 .addPrimitive(PRIMITIVE_CLICK) 1198 .addPrimitive(PRIMITIVE_TICK, 0.2497f) 1199 .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356) 1200 .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7) 1201 .compose(); 1202 VibrationEffect effect = VibrationEffect.createRepeatingEffect(preamble, repeating); 1203 1204 String xml = """ 1205 <vibration-effect> 1206 <repeating-effect> 1207 <preamble> 1208 <primitive-effect name="click" /> 1209 <primitive-effect name="tick" scale="0.2497" /> 1210 <primitive-effect name="low_tick" delayMs="356" /> 1211 <primitive-effect name="spin" scale="0.6364" delayMs="7" /> 1212 </preamble> 1213 <repeating> 1214 <primitive-effect name="click" /> 1215 <primitive-effect name="tick" scale="0.2497" /> 1216 <primitive-effect name="low_tick" delayMs="356" /> 1217 <primitive-effect name="spin" scale="0.6364" delayMs="7" /> 1218 </repeating> 1219 </repeating-effect> 1220 </vibration-effect> 1221 """; 1222 1223 assertPublicApisParserSucceeds(xml, effect); 1224 assertPublicApisSerializerSucceeds(effect, "click", "tick", "low_tick", "spin"); 1225 assertPublicApisRoundTrip(effect); 1226 1227 assertHiddenApisParserSucceeds(xml, effect); 1228 assertHiddenApisSerializerSucceeds(effect, "click", "tick", "low_tick", "spin"); 1229 assertHiddenApisRoundTrip(effect); 1230 1231 repeating = VibrationEffect.startComposition() 1232 .addPrimitive(PRIMITIVE_CLICK) 1233 .addPrimitive(PRIMITIVE_TICK, 0.2497f) 1234 .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356) 1235 .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7) 1236 .compose(); 1237 effect = VibrationEffect.createRepeatingEffect(repeating); 1238 1239 xml = """ 1240 <vibration-effect> 1241 <repeating-effect> 1242 <repeating> 1243 <primitive-effect name="click" /> 1244 <primitive-effect name="tick" scale="0.2497" /> 1245 <primitive-effect name="low_tick" delayMs="356" /> 1246 <primitive-effect name="spin" scale="0.6364" delayMs="7" /> 1247 </repeating> 1248 </repeating-effect> 1249 </vibration-effect> 1250 """; 1251 1252 assertPublicApisParserSucceeds(xml, effect); 1253 assertPublicApisSerializerSucceeds(effect, "click", "tick", "low_tick", "spin"); 1254 assertPublicApisRoundTrip(effect); 1255 1256 assertHiddenApisParserSucceeds(xml, effect); 1257 assertHiddenApisSerializerSucceeds(effect, "click", "tick", "low_tick", "spin"); 1258 assertHiddenApisRoundTrip(effect); 1259 } 1260 1261 @Test 1262 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testRepeating_withMixedVibrations_allSucceed()1263 public void testRepeating_withMixedVibrations_allSucceed() throws Exception { 1264 VibrationEffect preamble = new VibrationEffect.WaveformEnvelopeBuilder() 1265 .addControlPoint(0.1f, 50f, 10) 1266 .build(); 1267 VibrationEffect repeating = VibrationEffect.get(VibrationEffect.EFFECT_TICK); 1268 VibrationEffect effect = VibrationEffect.createRepeatingEffect(preamble, repeating); 1269 String xml = """ 1270 <vibration-effect> 1271 <repeating-effect> 1272 <preamble> 1273 <waveform-envelope-effect> 1274 <control-point amplitude="0.1" frequencyHz="50.0" durationMs="10"/> 1275 </waveform-envelope-effect> 1276 </preamble> 1277 <repeating> 1278 <predefined-effect name="tick"/> 1279 </repeating> 1280 </repeating-effect> 1281 </vibration-effect> 1282 """; 1283 assertPublicApisParserSucceeds(xml, effect); 1284 assertPublicApisSerializerSucceeds(effect, "0.1", "50.0", "10", "tick"); 1285 assertPublicApisRoundTrip(effect); 1286 1287 assertHiddenApisParserSucceeds(xml, effect); 1288 assertHiddenApisSerializerSucceeds(effect, "0.1", "50.0", "10", "tick"); 1289 assertHiddenApisRoundTrip(effect); 1290 1291 preamble = VibrationEffect.createWaveform(new long[]{123, 456}, 1292 new int[]{254, 1}, /* repeat= */ -1); 1293 repeating = new VibrationEffect.BasicEnvelopeBuilder() 1294 .addControlPoint(0.3f, 0.4f, 25) 1295 .addControlPoint(0.0f, 0.5f, 30) 1296 .build(); 1297 effect = VibrationEffect.createRepeatingEffect(preamble, repeating); 1298 1299 xml = """ 1300 <vibration-effect> 1301 <repeating-effect> 1302 <preamble> 1303 <waveform-entry durationMs="123" amplitude="254"/> 1304 <waveform-entry durationMs="456" amplitude="1"/> 1305 </preamble> 1306 <repeating> 1307 <basic-envelope-effect> 1308 <control-point intensity="0.3" sharpness="0.4" durationMs="25" /> 1309 <control-point intensity="0.0" sharpness="0.5" durationMs="30" /> 1310 </basic-envelope-effect> 1311 </repeating> 1312 </repeating-effect> 1313 </vibration-effect> 1314 """; 1315 1316 assertPublicApisParserSucceeds(xml, effect); 1317 assertPublicApisSerializerSucceeds(effect, "123", "456", "254", "1", "0.3", "0.0", "0.4", 1318 "0.5", "25", "30"); 1319 assertPublicApisRoundTrip(effect); 1320 1321 assertHiddenApisParserSucceeds(xml, effect); 1322 assertHiddenApisSerializerSucceeds(effect, "123", "456", "254", "1", "0.3", "0.0", "0.4", 1323 "0.5", "25", "30"); 1324 assertHiddenApisRoundTrip(effect); 1325 1326 preamble = VibrationEffect.startComposition() 1327 .addPrimitive(PRIMITIVE_CLICK) 1328 .compose(); 1329 effect = VibrationEffect.createRepeatingEffect(preamble, repeating); 1330 1331 xml = """ 1332 <vibration-effect> 1333 <repeating-effect> 1334 <preamble> 1335 <primitive-effect name="click" /> 1336 </preamble> 1337 <repeating> 1338 <basic-envelope-effect> 1339 <control-point intensity="0.3" sharpness="0.4" durationMs="25" /> 1340 <control-point intensity="0.0" sharpness="0.5" durationMs="30" /> 1341 </basic-envelope-effect> 1342 </repeating> 1343 </repeating-effect> 1344 </vibration-effect> 1345 """; 1346 1347 assertPublicApisParserSucceeds(xml, effect); 1348 assertPublicApisSerializerSucceeds(effect, "click", "0.3", "0.4", "0.0", "0.5", "25", "30"); 1349 assertPublicApisRoundTrip(effect); 1350 1351 assertHiddenApisParserSucceeds(xml, effect); 1352 assertHiddenApisSerializerSucceeds(effect, "click", "0.3", "0.4", "0.0", "0.5", "25", "30"); 1353 assertHiddenApisRoundTrip(effect); 1354 } 1355 1356 @Test 1357 @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testRepeating_badXml_throwsException()1358 public void testRepeating_badXml_throwsException() throws IOException { 1359 // Incomplete XML 1360 assertParseElementFails(""" 1361 <vibration-effect> 1362 <repeating-effect> 1363 <preamble> 1364 <primitive-effect name="click" /> 1365 </preamble> 1366 <repeating> 1367 <primitive-effect name="click" /> 1368 """); 1369 1370 assertParseElementFails(""" 1371 <vibration-effect> 1372 <repeating-effect> 1373 <primitive-effect name="click" /> 1374 <repeating> 1375 <primitive-effect name="click" /> 1376 </repeating> 1377 </repeating-effect> 1378 </vibration-effect> 1379 """); 1380 1381 assertParseElementFails(""" 1382 <vibration-effect> 1383 <repeating-effect> 1384 <preamble> 1385 <primitive-effect name="click" /> 1386 </preamble> 1387 <primitive-effect name="click" /> 1388 </repeating-effect> 1389 </vibration-effect> 1390 """); 1391 1392 // Bad vibration XML 1393 assertParseElementFails(""" 1394 <vibration-effect> 1395 <repeating-effect> 1396 <repeating> 1397 <primitive-effect name="click" /> 1398 </repeating> 1399 <preamble> 1400 <primitive-effect name="click" /> 1401 </preamble> 1402 </repeating-effect> 1403 </vibration-effect> 1404 """); 1405 1406 assertParseElementFails(""" 1407 <vibration-effect> 1408 <repeating-effect> 1409 <repeating> 1410 <preamble> 1411 <primitive-effect name="click" /> 1412 </preamble> 1413 <primitive-effect name="click" /> 1414 </repeating> 1415 </repeating-effect> 1416 </vibration-effect> 1417 """); 1418 1419 assertParseElementFails(""" 1420 <vibration-effect> 1421 <repeating-effect> 1422 <preamble> 1423 <primitive-effect name="click" /> 1424 <repeating> 1425 <primitive-effect name="click" /> 1426 </repeating> 1427 </preamble> 1428 </repeating-effect> 1429 </vibration-effect> 1430 """); 1431 1432 assertParseElementFails(""" 1433 <vibration-effect> 1434 <repeating-effect> 1435 <primitive-effect name="click" /> 1436 <primitive-effect name="click" /> 1437 </repeating-effect> 1438 </vibration-effect> 1439 """); 1440 } 1441 1442 @Test 1443 @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) testRepeatingEffect_featureFlagDisabled_allFail()1444 public void testRepeatingEffect_featureFlagDisabled_allFail() throws Exception { 1445 VibrationEffect repeating = VibrationEffect.startComposition() 1446 .addPrimitive(PRIMITIVE_CLICK) 1447 .addPrimitive(PRIMITIVE_TICK, 0.2497f) 1448 .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356) 1449 .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7) 1450 .compose(); 1451 VibrationEffect effect = VibrationEffect.createRepeatingEffect(repeating); 1452 1453 String xml = """ 1454 <vibration-effect> 1455 <repeating-effect> 1456 <repeating> 1457 <primitive-effect name="click" /> 1458 <primitive-effect name="tick" scale="0.2497" /> 1459 <primitive-effect name="low_tick" delayMs="356" /> 1460 <primitive-effect name="spin" scale="0.6364" delayMs="7" /> 1461 </repeating> 1462 </repeating-effect> 1463 </vibration-effect> 1464 """; 1465 1466 assertPublicApisParserFails(xml); 1467 assertPublicApisSerializerFails(effect); 1468 assertHiddenApisParserFails(xml); 1469 assertHiddenApisSerializerFails(effect); 1470 } 1471 1472 @Test 1473 @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS) testVendorEffect_allSucceed()1474 public void testVendorEffect_allSucceed() throws Exception { 1475 PersistableBundle vendorData = new PersistableBundle(); 1476 vendorData.putInt("id", 1); 1477 vendorData.putDouble("scale", 0.5); 1478 vendorData.putBoolean("loop", false); 1479 vendorData.putLongArray("amplitudes", new long[] { 0, 255, 128 }); 1480 vendorData.putString("label", "vibration"); 1481 1482 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 1483 vendorData.writeToStream(outputStream); 1484 String vendorDataStr = Base64.getEncoder().encodeToString(outputStream.toByteArray()); 1485 1486 VibrationEffect effect = VibrationEffect.createVendorEffect(vendorData); 1487 String xml = "<vibration-effect><vendor-effect> " // test trailing whitespace is ignored 1488 + vendorDataStr 1489 + " \n </vendor-effect></vibration-effect>"; 1490 1491 assertPublicApisParserSucceeds(xml, effect); 1492 assertPublicApisSerializerSucceeds(effect, vendorDataStr); 1493 assertPublicApisRoundTrip(effect); 1494 1495 assertHiddenApisParserSucceeds(xml, effect); 1496 assertHiddenApisSerializerSucceeds(effect, vendorDataStr); 1497 assertHiddenApisRoundTrip(effect); 1498 1499 // Check PersistableBundle from round-trip 1500 PersistableBundle parsedVendorData = 1501 ((VibrationEffect.VendorEffect) parseVibrationEffect(serialize(effect), 1502 /* flags= */ 0)).getVendorData(); 1503 assertThat(parsedVendorData.size()).isEqualTo(vendorData.size()); 1504 assertThat(parsedVendorData.getInt("id")).isEqualTo(1); 1505 assertThat(parsedVendorData.getDouble("scale")).isEqualTo(0.5); 1506 assertThat(parsedVendorData.getBoolean("loop")).isFalse(); 1507 assertArrayEquals(parsedVendorData.getLongArray("amplitudes"), new long[] { 0, 255, 128 }); 1508 assertThat(parsedVendorData.getString("label")).isEqualTo("vibration"); 1509 } 1510 1511 @Test 1512 @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS) testInvalidVendorEffect_allFail()1513 public void testInvalidVendorEffect_allFail() throws IOException { 1514 String emptyTag = "<vibration-effect><vendor-effect/></vibration-effect>"; 1515 assertPublicApisParserFails(emptyTag); 1516 assertHiddenApisParserFails(emptyTag); 1517 1518 String emptyStringTag = 1519 "<vibration-effect><vendor-effect> \n </vendor-effect></vibration-effect>"; 1520 assertPublicApisParserFails(emptyStringTag); 1521 assertHiddenApisParserFails(emptyStringTag); 1522 1523 String invalidString = 1524 "<vibration-effect><vendor-effect>invalid</vendor-effect></vibration-effect>"; 1525 assertPublicApisParserFails(invalidString); 1526 assertHiddenApisParserFails(invalidString); 1527 1528 String validBase64String = 1529 "<vibration-effect><vendor-effect>c29tZXNh</vendor-effect></vibration-effect>"; 1530 assertPublicApisParserFails(validBase64String); 1531 assertHiddenApisParserFails(validBase64String); 1532 1533 PersistableBundle emptyData = new PersistableBundle(); 1534 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 1535 emptyData.writeToStream(outputStream); 1536 String emptyBundleString = "<vibration-effect><vendor-effect>" 1537 + Base64.getEncoder().encodeToString(outputStream.toByteArray()) 1538 + "</vendor-effect></vibration-effect>"; 1539 assertPublicApisParserFails(emptyBundleString); 1540 assertHiddenApisParserFails(emptyBundleString); 1541 } 1542 1543 @Test 1544 @DisableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS) testVendorEffect_featureFlagDisabled_allFail()1545 public void testVendorEffect_featureFlagDisabled_allFail() throws Exception { 1546 PersistableBundle vendorData = new PersistableBundle(); 1547 vendorData.putInt("id", 1); 1548 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 1549 vendorData.writeToStream(outputStream); 1550 String vendorDataStr = Base64.getEncoder().encodeToString(outputStream.toByteArray()); 1551 String xml = "<vibration-effect><vendor-effect>" 1552 + vendorDataStr 1553 + "</vendor-effect></vibration-effect>"; 1554 VibrationEffect vendorEffect = VibrationEffect.createVendorEffect(vendorData); 1555 1556 assertPublicApisParserFails(xml); 1557 assertPublicApisSerializerFails(vendorEffect); 1558 1559 assertHiddenApisParserFails(xml); 1560 assertHiddenApisSerializerFails(vendorEffect); 1561 } 1562 1563 @Test 1564 @EnableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY) testPrimitiveDelayType_allSucceed()1565 public void testPrimitiveDelayType_allSucceed() throws Exception { 1566 VibrationEffect effect = VibrationEffect.startComposition() 1567 .addPrimitive(PRIMITIVE_TICK, 1.0f, 0, DELAY_TYPE_RELATIVE_START_OFFSET) 1568 .addPrimitive(PRIMITIVE_CLICK, 0.123f, 10, DELAY_TYPE_PAUSE) 1569 .compose(); 1570 String xml = """ 1571 <vibration-effect> 1572 <primitive-effect name="tick" delayType="relative_start_offset"/> 1573 <primitive-effect name="click" scale="0.123" delayMs="10"/> 1574 </vibration-effect> 1575 """; 1576 1577 assertPublicApisParserSucceeds(xml, effect); 1578 assertPublicApisSerializerSucceeds(effect, "tick", "click"); 1579 // Delay type pause is not serialized, as it's the default one 1580 assertPublicApisSerializerSucceeds(effect, "relative_start_offset", "click"); 1581 assertPublicApisRoundTrip(effect); 1582 1583 assertHiddenApisParserSucceeds(xml, effect); 1584 assertHiddenApisSerializerSucceeds(effect, "tick", "click"); 1585 assertHiddenApisRoundTrip(effect); 1586 1587 // Check PersistableBundle from round-trip 1588 VibrationEffect.Composed parsedEffect = ((VibrationEffect.Composed) parseVibrationEffect( 1589 serialize(effect), /* flags= */ 0)); 1590 assertThat(parsedEffect.getRepeatIndex()).isEqualTo(-1); 1591 assertThat(parsedEffect.getSegments()).containsExactly( 1592 new PrimitiveSegment(PRIMITIVE_TICK, 1.0f, 0, DELAY_TYPE_RELATIVE_START_OFFSET), 1593 new PrimitiveSegment(PRIMITIVE_CLICK, 0.123f, 10, DELAY_TYPE_PAUSE)) 1594 .inOrder(); 1595 } 1596 1597 @Test 1598 @EnableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY) testPrimitiveInvalidDelayType_allFail()1599 public void testPrimitiveInvalidDelayType_allFail() { 1600 String emptyAttribute = """ 1601 <vibration-effect> 1602 <primitive-effect name="tick" delayType=""/> 1603 </vibration-effect> 1604 """; 1605 assertPublicApisParserFails(emptyAttribute); 1606 assertHiddenApisParserFails(emptyAttribute); 1607 1608 String invalidString = """ 1609 <vibration-effect> 1610 <primitive-effect name="tick" delayType="invalid"/> 1611 </vibration-effect> 1612 """; 1613 assertPublicApisParserFails(invalidString); 1614 assertHiddenApisParserFails(invalidString); 1615 } 1616 1617 @Test 1618 @DisableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY) testPrimitiveDelayType_featureFlagDisabled_allFail()1619 public void testPrimitiveDelayType_featureFlagDisabled_allFail() { 1620 VibrationEffect effect = VibrationEffect.startComposition() 1621 .addPrimitive(PRIMITIVE_TICK, 1.0f, 0, DELAY_TYPE_RELATIVE_START_OFFSET) 1622 .addPrimitive(PRIMITIVE_CLICK, 0.123f, 10, DELAY_TYPE_PAUSE) 1623 .compose(); 1624 String xml = """ 1625 <vibration-effect> 1626 <primitive-effect name="tick" delayType="relative_start_offset"/> 1627 <primitive-effect name="click" scale="0.123" delayMs="10" delayType="pause"/> 1628 </vibration-effect> 1629 """; 1630 1631 assertPublicApisParserFails(xml); 1632 assertPublicApisSerializerFails(effect); 1633 1634 assertHiddenApisParserFails(xml); 1635 assertHiddenApisSerializerFails(effect); 1636 } 1637 assertPublicApisParserFails(String xml)1638 private void assertPublicApisParserFails(String xml) { 1639 assertThrows("Expected parseVibrationEffect to fail for " + xml, 1640 VibrationXmlParser.ParseFailedException.class, 1641 () -> parseVibrationEffect(xml, /* flags= */ 0)); 1642 assertThrows("Expected parseDocument to fail for " + xml, 1643 VibrationXmlParser.ParseFailedException.class, 1644 () -> parseDocument(xml, /* flags= */ 0)); 1645 } 1646 assertPublicApisParseVibrationEffectFails(String xml)1647 private void assertPublicApisParseVibrationEffectFails(String xml) { 1648 assertThrows("Expected parseVibrationEffect to fail for " + xml, 1649 VibrationXmlParser.ParseFailedException.class, 1650 () -> parseVibrationEffect(xml, /* flags= */ 0)); 1651 } 1652 assertPublicApisParserSucceeds(String xml, VibrationEffect effect)1653 private void assertPublicApisParserSucceeds(String xml, VibrationEffect effect) 1654 throws Exception { 1655 assertPublicApisParseDocumentSucceeds(xml, effect); 1656 assertPublicApisParseVibrationEffectSucceeds(xml, effect); 1657 } 1658 assertPublicApisParseDocumentSucceeds(String xml, VibrationEffect... effects)1659 private void assertPublicApisParseDocumentSucceeds(String xml, VibrationEffect... effects) 1660 throws Exception { 1661 assertThat(parseDocument(xml, /* flags= */ 0)) 1662 .isEqualTo(new ParsedVibration(Arrays.asList(effects))); 1663 } 1664 assertPublicApisParseVibrationEffectSucceeds(String xml, VibrationEffect effect)1665 private void assertPublicApisParseVibrationEffectSucceeds(String xml, VibrationEffect effect) 1666 throws Exception { 1667 assertThat(parseVibrationEffect(xml, /* flags= */ 0)).isEqualTo(effect); 1668 } 1669 assertHiddenApisParserFails(String xml)1670 private void assertHiddenApisParserFails(String xml) { 1671 assertThrows("Expected parseVibrationEffect to fail for " + xml, 1672 VibrationXmlParser.ParseFailedException.class, 1673 () -> parseVibrationEffect(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS)); 1674 assertThrows("Expected parseDocument to fail for " + xml, 1675 VibrationXmlParser.ParseFailedException.class, 1676 () -> parseDocument(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS)); 1677 } 1678 assertHiddenApisParseVibrationEffectFails(String xml)1679 private void assertHiddenApisParseVibrationEffectFails(String xml) { 1680 assertThrows("Expected parseVibrationEffect to fail for " + xml, 1681 VibrationXmlParser.ParseFailedException.class, 1682 () -> parseVibrationEffect(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS)); 1683 } 1684 assertHiddenApisParserSucceeds(String xml, VibrationEffect effect)1685 private void assertHiddenApisParserSucceeds(String xml, VibrationEffect effect) 1686 throws Exception { 1687 assertHiddenApisParseDocumentSucceeds(xml, effect); 1688 assertHiddenApisParseVibrationEffectSucceeds(xml, effect); 1689 } 1690 assertHiddenApisParseDocumentSucceeds(String xml, VibrationEffect... effect)1691 private void assertHiddenApisParseDocumentSucceeds(String xml, VibrationEffect... effect) 1692 throws Exception { 1693 assertThat(parseDocument(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS)) 1694 .isEqualTo(new ParsedVibration(Arrays.asList(effect))); 1695 } 1696 assertHiddenApisParseVibrationEffectSucceeds(String xml, VibrationEffect effect)1697 private void assertHiddenApisParseVibrationEffectSucceeds(String xml, VibrationEffect effect) 1698 throws Exception { 1699 assertThat(parseVibrationEffect(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS)) 1700 .isEqualTo(effect); 1701 } 1702 assertPublicApisSerializerFails(VibrationEffect effect)1703 private void assertPublicApisSerializerFails(VibrationEffect effect) { 1704 assertThrows("Expected serialization to fail for " + effect, 1705 VibrationXmlSerializer.SerializationFailedException.class, 1706 () -> serialize(effect)); 1707 } 1708 assertHiddenApisSerializerFails(VibrationEffect effect)1709 private void assertHiddenApisSerializerFails(VibrationEffect effect) { 1710 assertThrows("Expected serialization to fail for " + effect, 1711 VibrationXmlSerializer.SerializationFailedException.class, 1712 () -> serialize(effect, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS)); 1713 } 1714 assertPublicApisSerializerSucceeds(VibrationEffect effect, String... expectedSegments)1715 private void assertPublicApisSerializerSucceeds(VibrationEffect effect, 1716 String... expectedSegments) throws Exception { 1717 assertSerializationContainsSegments(serialize(effect), expectedSegments); 1718 } 1719 assertHiddenApisSerializerSucceeds(VibrationEffect effect, String... expectedSegments)1720 private void assertHiddenApisSerializerSucceeds(VibrationEffect effect, 1721 String... expectedSegments) throws Exception { 1722 assertSerializationContainsSegments( 1723 serialize(effect, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS), expectedSegments); 1724 } 1725 assertPublicApisRoundTrip(VibrationEffect effect)1726 private void assertPublicApisRoundTrip(VibrationEffect effect) throws Exception { 1727 assertThat(parseVibrationEffect(serialize(effect, /* flags= */ 0), /* flags= */ 0)) 1728 .isEqualTo(effect); 1729 } 1730 assertHiddenApisRoundTrip(VibrationEffect effect)1731 private void assertHiddenApisRoundTrip(VibrationEffect effect) throws Exception { 1732 String xml = serialize(effect, VibrationXmlSerializer.FLAG_ALLOW_HIDDEN_APIS); 1733 assertThat(parseVibrationEffect(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS)) 1734 .isEqualTo(effect); 1735 } 1736 createXmlPullParser(String xml)1737 private TypedXmlPullParser createXmlPullParser(String xml) throws Exception { 1738 TypedXmlPullParser parser = Xml.newFastPullParser(); 1739 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 1740 parser.setInput(new StringReader(xml)); 1741 parser.next(); // read START_DOCUMENT 1742 return parser; 1743 } 1744 1745 /** 1746 * Asserts parsing vibration from an open TypedXmlPullParser succeeds, and that the parser 1747 * points to the end "vibration" or "vibration-select" tag. 1748 */ assertParseElementSucceeds( TypedXmlPullParser parser, VibrationEffect... effects)1749 private void assertParseElementSucceeds( 1750 TypedXmlPullParser parser, VibrationEffect... effects) throws Exception { 1751 assertParseElementSucceeds(parser, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS, effects); 1752 } 1753 assertParseElementSucceeds( TypedXmlPullParser parser, int flags, VibrationEffect... effects)1754 private void assertParseElementSucceeds( 1755 TypedXmlPullParser parser, int flags, VibrationEffect... effects) throws Exception { 1756 String tagName = parser.getName(); 1757 assertThat(Set.of("vibration-effect", "vibration-select")).contains(tagName); 1758 1759 assertThat(parseElement(parser, flags)) 1760 .isEqualTo(new ParsedVibration(Arrays.asList(effects))); 1761 assertThat(parser.getEventType()).isEqualTo(XmlPullParser.END_TAG); 1762 assertThat(parser.getName()).isEqualTo(tagName); 1763 } 1764 assertEndTag(TypedXmlPullParser parser, String expectedTagName)1765 private void assertEndTag(TypedXmlPullParser parser, String expectedTagName) throws Exception { 1766 assertThat(parser.getName()).isEqualTo(expectedTagName); 1767 assertThat(parser.getEventType()).isEqualTo(parser.END_TAG); 1768 } 1769 assertStartTag(TypedXmlPullParser parser, String expectedTagName)1770 private void assertStartTag(TypedXmlPullParser parser, String expectedTagName) 1771 throws Exception { 1772 assertThat(parser.getName()).isEqualTo(expectedTagName); 1773 assertThat(parser.getEventType()).isEqualTo(parser.START_TAG); 1774 } 1775 assertEndOfDocument(TypedXmlPullParser parser)1776 private void assertEndOfDocument(TypedXmlPullParser parser) throws Exception { 1777 assertThat(parser.getEventType()).isEqualTo(parser.END_DOCUMENT); 1778 } 1779 assertParseElementFails(String xml)1780 private void assertParseElementFails(String xml) { 1781 assertThrows("Expected parsing to fail for " + xml, 1782 VibrationXmlParser.ParseFailedException.class, 1783 () -> parseElement(createXmlPullParser(xml), /* flags= */ 0)); 1784 } 1785 assertSerializationContainsSegments(String xml, String[] expectedSegments)1786 private void assertSerializationContainsSegments(String xml, String[] expectedSegments) { 1787 for (String expectedSegment : expectedSegments) { 1788 assertThat(xml).contains(expectedSegment); 1789 } 1790 } 1791 parseVibrationEffect( String xml, @VibrationXmlParser.Flags int flags)1792 private static VibrationEffect parseVibrationEffect( 1793 String xml, @VibrationXmlParser.Flags int flags) throws Exception { 1794 return VibrationXmlParser.parseVibrationEffect(new StringReader(xml), flags); 1795 } 1796 parseDocument(String xml, int flags)1797 private static ParsedVibration parseDocument(String xml, int flags) throws Exception { 1798 return VibrationXmlParser.parseDocument(new StringReader(xml), flags); 1799 } 1800 parseElement(TypedXmlPullParser parser, int flags)1801 private static ParsedVibration parseElement(TypedXmlPullParser parser, int flags) 1802 throws Exception { 1803 return VibrationXmlParser.parseElement(parser, flags); 1804 } 1805 serialize(VibrationEffect effect)1806 private static String serialize(VibrationEffect effect) throws Exception { 1807 StringWriter writer = new StringWriter(); 1808 VibrationXmlSerializer.serialize(effect, writer); 1809 return writer.toString(); 1810 } 1811 serialize(VibrationEffect effect, @VibrationXmlSerializer.Flags int flags)1812 private static String serialize(VibrationEffect effect, @VibrationXmlSerializer.Flags int flags) 1813 throws Exception { 1814 StringWriter writer = new StringWriter(); 1815 VibrationXmlSerializer.serialize(effect, writer, flags); 1816 return writer.toString(); 1817 } 1818 createAllPredefinedEffectsMap()1819 private static Map<String, Integer> createAllPredefinedEffectsMap() { 1820 Map<String, Integer> map = createHiddenPredefinedEffectsMap(); 1821 map.putAll(createPublicPredefinedEffectsMap()); 1822 return map; 1823 } 1824 createPublicPredefinedEffectsMap()1825 private static Map<String, Integer> createPublicPredefinedEffectsMap() { 1826 Map<String, Integer> map = new HashMap<>(); 1827 map.put("tick", VibrationEffect.EFFECT_TICK); 1828 map.put("click", VibrationEffect.EFFECT_CLICK); 1829 map.put("heavy_click", VibrationEffect.EFFECT_HEAVY_CLICK); 1830 map.put("double_click", VibrationEffect.EFFECT_DOUBLE_CLICK); 1831 return map; 1832 } 1833 createHiddenPredefinedEffectsMap()1834 private static Map<String, Integer> createHiddenPredefinedEffectsMap() { 1835 Map<String, Integer> map = new HashMap<>(); 1836 map.put("texture_tick", VibrationEffect.EFFECT_TEXTURE_TICK); 1837 map.put("pop", VibrationEffect.EFFECT_POP); 1838 map.put("thud", VibrationEffect.EFFECT_THUD); 1839 for (int i = 0; i < VibrationEffect.RINGTONES.length; i++) { 1840 map.put(String.format("ringtone_%d", i + 1), VibrationEffect.RINGTONES[i]); 1841 } 1842 return map; 1843 } 1844 } 1845