开发环境:macOS Big Sur (11.6.2)/Flutter 2.8.1/Android Studio Atrctic Fox (2020.3.1 Patch 4)
参照 将Flutter module集成到Android项目(Android Studio Arctic Fox 2020.3.1/Flutter 2.8.1) 建立项目,在执行 Flutter 单元测试代码的时候报错
开发环境:macOS Big Sur (11.6.2)/Flutter 2.8.1/Android Studio Atrctic Fox (2020.3.1 Patch 4)
参照 将Flutter module集成到Android项目(Android Studio Arctic Fox 2020.3.1/Flutter 2.8.1) 建立项目,在执行 Flutter 单元测试代码的时候报错
最近,在使用升级老项目的 compileSdkVersion 到 32 进行 Android 12L适配的时候,报错
1 |
Build-tool 32.0.0 is missing DX at C:\Users\xxxx\AppData\Local\Android\Sdk\build-tools\32.0.0\dx.bat |
详细的报错信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 |
Starting Gradle Daemon... Gradle Daemon started in 2 s 200 ms > Configure project :app Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Observed package id 'system-images;android-31;google_apis;x86_64' in inconsistent location 'C:\Users\xxxx\AppData\Local\Android\Sdk\system-images\android-S\google_apis\x86_64' (Expected 'C:\Users\xxxx\AppData\Local\Android\Sdk\system-images\android-31\google_apis\x86_64') Build-tool 32.0.0 is missing DX at C:\Users\xxxx\AppData\Local\Android\Sdk\build-tools\32.0.0\dx.bat Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Observed package id 'system-images;android-31;google_apis;x86_64' in inconsistent location 'C:\Users\xxxx\AppData\Local\Android\Sdk\system-images\android-S\google_apis\x86_64' (Expected 'C:\Users\xxxx\AppData\Local\Android\Sdk\system-images\android-31\google_apis\x86_64') File C:\Users\xxxx\.android\repositories.cfg could not be loaded. Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01 Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01 Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01 Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01 Observed package id 'system-images;android-31;google_apis;x86_64' in inconsistent location 'C:\Users\xxxx\AppData\Local\Android\Sdk\system-images\android-S\google_apis\x86_64' (Expected 'C:\Users\xxxx\AppData\Local\Android\Sdk\system-images\android-31\google_apis\x86_64') Build-tool 32.0.0 is missing DX at C:\Users\xxxx\AppData\Local\Android\Sdk\build-tools\32.0.0\dx.bat FAILURE: Build failed with an exception. * What went wrong: Could not resolve all dependencies for configuration ':app:uatOutDebugRuntimeClasspath'. Could not create task ':app:minifyPreProOutReleaseWithR8'. Cannot query the value of this provider because it has no value available. * Try: Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Exception is: com.intellij.openapi.externalSystem.model.ExternalSystemException: Could not resolve all dependencies for configuration ':app:uatOutDebugRuntimeClasspath'. Could not create task ':app:minifyPreProOutReleaseWithR8'. Cannot query the value of this provider because it has no value available. at org.jetbrains.plugins.gradle.model.ProjectImportAction.addBuildModels(ProjectImportAction.java:258) at org.jetbrains.plugins.gradle.model.ProjectImportAction.execute(ProjectImportAction.java:116) at org.jetbrains.plugins.gradle.model.ProjectImportAction.execute(ProjectImportAction.java:41) at org.gradle.tooling.internal.consumer.connection.InternalBuildActionAdapter.execute(InternalBuildActionAdapter.java:64) at org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner$ActionRunningListener.runAction(AbstractClientProvidedBuildActionRunner.java:134) at org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner$ActionRunningListener.buildFinished(AbstractClientProvidedBuildActionRunner.java:119) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.event.DefaultListenerManager$ListenerDetails.dispatch(DefaultListenerManager.java:398) at org.gradle.internal.event.DefaultListenerManager$ListenerDetails.dispatch(DefaultListenerManager.java:380) at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:61) at org.gradle.internal.event.DefaultListenerManager$EventBroadcast$ListenerDispatch.dispatch(DefaultListenerManager.java:368) at org.gradle.internal.event.DefaultListenerManager$EventBroadcast$ListenerDispatch.dispatch(DefaultListenerManager.java:355) at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:43) at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:245) at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:157) at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:61) at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:346) at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:249) at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:141) at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37) at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94) at com.sun.proxy.$Proxy19.buildFinished(Unknown Source) at org.gradle.initialization.DefaultGradleLauncher.finishBuild(DefaultGradleLauncher.java:196) at org.gradle.initialization.DefaultGradleLauncher.finishBuild(DefaultGradleLauncher.java:132) at org.gradle.internal.invocation.GradleBuildController$1.create(GradleBuildController.java:72) at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:213) at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:67) at org.gradle.internal.invocation.GradleBuildController.run(GradleBuildController.java:56) at org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner.runClientAction(AbstractClientProvidedBuildActionRunner.java:53) at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:47) at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35) at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35) at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:66) at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32) at org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner.run(FileSystemWatchingBuildActionRunner.java:90) at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:41) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:49) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:44) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62) at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76) at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:44) at org.gradle.launcher.exec.InProcessBuildActionExecuter.lambda$execute$0(InProcessBuildActionExecuter.java:59) at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:86) at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:58) at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:30) at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.lambda$execute$0(BuildTreeScopeLifecycleBuildActionExecuter.java:34) at org.gradle.internal.buildtree.BuildTreeState.run(BuildTreeState.java:53) at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.execute(BuildTreeScopeLifecycleBuildActionExecuter.java:33) at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.execute(BuildTreeScopeLifecycleBuildActionExecuter.java:28) at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:104) at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:55) at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:64) at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:37) at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.lambda$execute$0(SessionScopeLifecycleBuildActionExecuter.java:54) at org.gradle.internal.session.BuildSessionState.run(BuildSessionState.java:67) at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.execute(SessionScopeLifecycleBuildActionExecuter.java:50) at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.execute(SessionScopeLifecycleBuildActionExecuter.java:36) at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:36) at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:25) at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:59) at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:31) at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:58) at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:42) at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:47) at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:31) at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:65) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78) at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75) at org.gradle.util.Swapper.swap(Swapper.java:38) at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:84) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52) at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) org.gradle.api.artifacts.ResolveException: Could not resolve all dependencies for configuration ':app:uatOutDebugRuntimeClasspath'. at org.gradle.api.internal.artifacts.ivyservice.ErrorHandlingConfigurationResolver.wrapException(ErrorHandlingConfigurationResolver.java:105) at org.gradle.api.internal.artifacts.ivyservice.ErrorHandlingConfigurationResolver.resolveGraph(ErrorHandlingConfigurationResolver.java:76) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$1.call(DefaultConfiguration.java:645) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$1.call(DefaultConfiguration.java:636) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62) at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76) at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.resolveGraphIfRequired(DefaultConfiguration.java:636) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.lambda$resolveExclusively$4(DefaultConfiguration.java:616) at org.gradle.api.internal.project.DefaultProjectStateRegistry$CalculatedModelValueImpl.update(DefaultProjectStateRegistry.java:362) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.resolveExclusively(DefaultConfiguration.java:613) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.resolveToStateOrLater(DefaultConfiguration.java:600) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.access$1900(DefaultConfiguration.java:150) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$SelectedArtifactsProvider.getValue(DefaultConfiguration.java:1341) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$SelectedArtifactsProvider.getValue(DefaultConfiguration.java:1331) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationFileCollection.getSelectedArtifacts(DefaultConfiguration.java:1403) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationFileCollection.access$3500(DefaultConfiguration.java:1346) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationArtifactCollection.lambda$new$0(DefaultConfiguration.java:1970) at org.gradle.internal.model.CalculatedValueContainerFactory$SupplierBackedCalculator.calculateValue(CalculatedValueContainerFactory.java:78) at org.gradle.internal.model.CalculatedValueContainer$CalculationState.lambda$attachValue$0(CalculatedValueContainer.java:216) at org.gradle.internal.Try.ofFailable(Try.java:39) at org.gradle.internal.model.CalculatedValueContainer$CalculationState.attachValue(CalculatedValueContainer.java:211) at org.gradle.internal.model.CalculatedValueContainer.finalizeIfNotAlready(CalculatedValueContainer.java:184) at org.gradle.internal.model.CalculatedValueContainer.finalizeIfNotAlready(CalculatedValueContainer.java:175) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationArtifactCollection.ensureResolved(DefaultConfiguration.java:2012) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationArtifactCollection.getArtifacts(DefaultConfiguration.java:1989) at com.android.build.gradle.internal.ide.dependencies.ArtifactDependencyGraph.getRuntimeComponentIdentifiers(ArtifactDependencyGraph.java:362) at com.android.build.gradle.internal.ide.dependencies.ArtifactDependencyGraph.createDependencies(ArtifactDependencyGraph.java:192) at com.android.build.gradle.internal.ide.ModelBuilder.getDependencies(ModelBuilder.java:841) at com.android.build.gradle.internal.ide.ModelBuilder.createAndroidArtifact(ModelBuilder.java:869) at com.android.build.gradle.internal.ide.ModelBuilder.createVariant(ModelBuilder.java:609) at com.android.build.gradle.internal.ide.ModelBuilder.buildVariant(ModelBuilder.java:564) at com.android.build.gradle.internal.ide.ModelBuilder.buildAll(ModelBuilder.java:221) at com.android.build.gradle.internal.ide.ModelBuilder.buildAll(ModelBuilder.java:144) at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuilderWithParameter.build(DefaultToolingModelBuilderRegistry.java:194) at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$LockSingleProjectBuilder.lambda$build$0(DefaultToolingModelBuilderRegistry.java:211) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$withProjectLock$3(DefaultProjectStateRegistry.java:310) at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:213) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withProjectLock(DefaultProjectStateRegistry.java:310) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.fromMutableState(DefaultProjectStateRegistry.java:291) at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$LockSingleProjectBuilder.build(DefaultToolingModelBuilderRegistry.java:211) at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuildOperationWrappingBuilder$1.call(DefaultToolingModelBuilderRegistry.java:246) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62) at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76) at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76) at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuildOperationWrappingBuilder.build(DefaultToolingModelBuilderRegistry.java:243) at org.gradle.tooling.internal.provider.runner.DefaultBuildController.getParameterizedModel(DefaultBuildController.java:156) at org.gradle.tooling.internal.provider.runner.DefaultBuildController.getModel(DefaultBuildController.java:104) at org.gradle.tooling.internal.consumer.connection.ParameterAwareBuildControllerAdapter.getModel(ParameterAwareBuildControllerAdapter.java:39) at org.gradle.tooling.internal.consumer.connection.UnparameterizedBuildController.getModel(UnparameterizedBuildController.java:113) at org.gradle.tooling.internal.consumer.connection.NestedActionAwareBuildControllerAdapter.getModel(NestedActionAwareBuildControllerAdapter.java:31) at org.gradle.tooling.internal.consumer.connection.UnparameterizedBuildController.findModel(UnparameterizedBuildController.java:97) at org.gradle.tooling.internal.consumer.connection.NestedActionAwareBuildControllerAdapter.findModel(NestedActionAwareBuildControllerAdapter.java:31) at org.jetbrains.plugins.gradle.model.ProjectImportAction$MyBuildController.findModel(ProjectImportAction.java:574) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker.findVariantModel(AndroidExtraModelProviderWorker.kt:521) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker.access$findVariantModel(AndroidExtraModelProviderWorker.kt:48) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker$getVariantAndModuleDependenciesAction$1.invoke(AndroidExtraModelProviderWorker.kt:450) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker$getVariantAndModuleDependenciesAction$1.invoke(AndroidExtraModelProviderWorker.kt:449) at com.android.tools.idea.gradle.project.sync.SequentialSyncActionRunner.runAction(SyncActionRunner.kt:61) at com.android.tools.idea.gradle.project.sync.SequentialSyncActionRunner.runActions(SyncActionRunner.kt:57) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker.chooseSelectedVariants(AndroidExtraModelProviderWorker.kt:364) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker.populateAndroidModels(AndroidExtraModelProviderWorker.kt:154) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker.populateBuildModels(AndroidExtraModelProviderWorker.kt:65) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProvider.populateBuildModels(AndroidExtraModelProvider.kt:52) at org.jetbrains.plugins.gradle.model.ProjectImportAction.addBuildModels(ProjectImportAction.java:246) at org.jetbrains.plugins.gradle.model.ProjectImportAction.execute(ProjectImportAction.java:116) at org.jetbrains.plugins.gradle.model.ProjectImportAction.execute(ProjectImportAction.java:41) at org.gradle.tooling.internal.consumer.connection.InternalBuildActionAdapter.execute(InternalBuildActionAdapter.java:64) at org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner$ActionRunningListener.runAction(AbstractClientProvidedBuildActionRunner.java:134) at org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner$ActionRunningListener.buildFinished(AbstractClientProvidedBuildActionRunner.java:119) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.event.DefaultListenerManager$ListenerDetails.dispatch(DefaultListenerManager.java:398) at org.gradle.internal.event.DefaultListenerManager$ListenerDetails.dispatch(DefaultListenerManager.java:380) at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:61) at org.gradle.internal.event.DefaultListenerManager$EventBroadcast$ListenerDispatch.dispatch(DefaultListenerManager.java:368) at org.gradle.internal.event.DefaultListenerManager$EventBroadcast$ListenerDispatch.dispatch(DefaultListenerManager.java:355) at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:43) at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:245) at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:157) at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:61) at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:346) at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:249) at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:141) at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37) at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94) at com.sun.proxy.$Proxy19.buildFinished(Unknown Source) at org.gradle.initialization.DefaultGradleLauncher.finishBuild(DefaultGradleLauncher.java:196) at org.gradle.initialization.DefaultGradleLauncher.finishBuild(DefaultGradleLauncher.java:132) at org.gradle.internal.invocation.GradleBuildController$1.create(GradleBuildController.java:72) at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:213) at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:67) at org.gradle.internal.invocation.GradleBuildController.run(GradleBuildController.java:56) at org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner.runClientAction(AbstractClientProvidedBuildActionRunner.java:53) at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:47) at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35) at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35) at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:66) at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32) at org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner.run(FileSystemWatchingBuildActionRunner.java:90) at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:41) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:49) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:44) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62) at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76) at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:44) at org.gradle.launcher.exec.InProcessBuildActionExecuter.lambda$execute$0(InProcessBuildActionExecuter.java:59) at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:86) at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:58) at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:30) at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.lambda$execute$0(BuildTreeScopeLifecycleBuildActionExecuter.java:34) at org.gradle.internal.buildtree.BuildTreeState.run(BuildTreeState.java:53) at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.execute(BuildTreeScopeLifecycleBuildActionExecuter.java:33) at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.execute(BuildTreeScopeLifecycleBuildActionExecuter.java:28) at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:104) at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:55) at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:64) at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:37) at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.lambda$execute$0(SessionScopeLifecycleBuildActionExecuter.java:54) at org.gradle.internal.session.BuildSessionState.run(BuildSessionState.java:67) at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.execute(SessionScopeLifecycleBuildActionExecuter.java:50) at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.execute(SessionScopeLifecycleBuildActionExecuter.java:36) at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:36) at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:25) at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:59) at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:31) at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:58) at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:42) at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:47) at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:31) at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:65) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78) at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75) at org.gradle.util.Swapper.swap(Swapper.java:38) at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:84) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52) at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) at java.base/java.lang.Thread.run(Thread.java:834) Caused by: org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreationException: Could not create task ':app:minifyPreProOutReleaseWithR8'. at org.gradle.api.internal.tasks.DefaultTaskContainer.taskCreationException(DefaultTaskContainer.java:719) at org.gradle.api.internal.tasks.DefaultTaskContainer.access$600(DefaultTaskContainer.java:77) at org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider.domainObjectCreationException(DefaultTaskContainer.java:711) at org.gradle.api.internal.DefaultNamedDomainObjectCollection$AbstractDomainObjectCreatingProvider.tryCreate(DefaultNamedDomainObjectCollection.java:948) at org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider.access$1401(DefaultTaskContainer.java:658) at org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider$1.run(DefaultTaskContainer.java:684) at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29) at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56) at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71) at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71) at org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider.tryCreate(DefaultTaskContainer.java:680) at org.gradle.api.internal.DefaultNamedDomainObjectCollection$AbstractDomainObjectCreatingProvider.calculateOwnValue(DefaultNamedDomainObjectCollection.java:929) at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:103) at org.gradle.api.internal.provider.FlatMapProvider.calculateOwnValue(FlatMapProvider.java:46) at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:103) at org.gradle.api.internal.provider.MappingProvider.calculateOwnValue(MappingProvider.java:55) at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:103) at org.gradle.api.internal.provider.DefaultProperty.calculateValueFrom(DefaultProperty.java:128) at org.gradle.api.internal.provider.DefaultProperty.calculateValueFrom(DefaultProperty.java:25) at org.gradle.api.internal.provider.AbstractProperty.doCalculateValue(AbstractProperty.java:133) at org.gradle.api.internal.provider.AbstractProperty.calculateOwnValue(AbstractProperty.java:127) at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:103) at org.gradle.api.internal.provider.DefaultProperty.calculateValueFrom(DefaultProperty.java:128) at org.gradle.api.internal.provider.DefaultProperty.calculateValueFrom(DefaultProperty.java:25) at org.gradle.api.internal.provider.AbstractProperty.doCalculateValue(AbstractProperty.java:133) at org.gradle.api.internal.provider.AbstractProperty.calculateOwnValue(AbstractProperty.java:127) at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:103) at org.gradle.api.internal.provider.DefaultProperty.calculateValueFrom(DefaultProperty.java:128) at org.gradle.api.internal.provider.DefaultProperty.calculateValueFrom(DefaultProperty.java:25) at org.gradle.api.internal.provider.AbstractProperty.doCalculateValue(AbstractProperty.java:133) at org.gradle.api.internal.provider.AbstractProperty.calculateOwnValue(AbstractProperty.java:127) at org.gradle.api.internal.provider.AbstractMinimalProvider.get(AbstractMinimalProvider.java:84) at org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact.getDelegate(LazyPublishArtifact.java:94) at org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact.getName(LazyPublishArtifact.java:64) at org.gradle.util.GUtil.getOrDefault(GUtil.java:167) at org.gradle.api.internal.artifacts.publish.DecoratingPublishArtifact.getName(DecoratingPublishArtifact.java:51) at org.gradle.api.internal.artifacts.publish.DecoratingPublishArtifact_Decorated.getName(Unknown Source) at org.gradle.internal.component.model.DefaultIvyArtifactName.forPublishArtifact(DefaultIvyArtifactName.java:36) at org.gradle.internal.component.local.model.PublishArtifactLocalArtifactMetadata.<init>(PublishArtifactLocalArtifactMetadata.java:40) at org.gradle.internal.component.local.model.DefaultLocalComponentMetadata.addVariant(DefaultLocalComponentMetadata.java:160) at org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultLocalComponentMetadataBuilder$1.visitChildVariant(DefaultLocalComponentMetadataBuilder.java:66) at org.gradle.api.internal.artifacts.configurations.DefaultVariant.visit(DefaultVariant.java:72) at org.gradle.api.internal.artifacts.configurations.DefaultConfigurationPublications.collectVariants(DefaultConfigurationPublications.java:95) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.collectVariants(DefaultConfiguration.java:1052) at org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultLocalComponentMetadataBuilder.addConfiguration(DefaultLocalComponentMetadataBuilder.java:53) at org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultRootComponentMetadataBuilder.addConfiguration(DefaultRootComponentMetadataBuilder.java:102) at org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultRootComponentMetadataBuilder.getRootComponentMetadata(DefaultRootComponentMetadataBuilder.java:96) at org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultRootComponentMetadataBuilder.lambda$buildRootComponentMetadata$0(DefaultRootComponentMetadataBuilder.java:86) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.fromMutableState(DefaultProjectStateRegistry.java:277) at org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultRootComponentMetadataBuilder.buildRootComponentMetadata(DefaultRootComponentMetadataBuilder.java:84) at org.gradle.api.internal.artifacts.ivyservice.moduleconverter.DefaultRootComponentMetadataBuilder.toRootComponentMetaData(DefaultRootComponentMetadataBuilder.java:70) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.toRootComponentMetaData(DefaultConfiguration.java:1194) at org.gradle.api.internal.artifacts.ivyservice.resolveengine.DefaultArtifactDependencyResolver$DefaultResolveContextToComponentResolver.resolve(DefaultArtifactDependencyResolver.java:227) at org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.builder.DependencyGraphBuilder.resolve(DependencyGraphBuilder.java:138) at org.gradle.api.internal.artifacts.ivyservice.resolveengine.DefaultArtifactDependencyResolver.resolve(DefaultArtifactDependencyResolver.java:145) at org.gradle.api.internal.artifacts.ivyservice.DefaultConfigurationResolver.resolveGraph(DefaultConfigurationResolver.java:180) at org.gradle.api.internal.artifacts.ivyservice.ShortCircuitEmptyConfigurationResolver.resolveGraph(ShortCircuitEmptyConfigurationResolver.java:85) at org.gradle.api.internal.artifacts.ivyservice.ErrorHandlingConfigurationResolver.resolveGraph(ErrorHandlingConfigurationResolver.java:74) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$1.call(DefaultConfiguration.java:645) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$1.call(DefaultConfiguration.java:636) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62) at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76) at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.resolveGraphIfRequired(DefaultConfiguration.java:636) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.lambda$resolveExclusively$4(DefaultConfiguration.java:616) at org.gradle.api.internal.project.DefaultProjectStateRegistry$CalculatedModelValueImpl.update(DefaultProjectStateRegistry.java:362) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.resolveExclusively(DefaultConfiguration.java:613) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.resolveToStateOrLater(DefaultConfiguration.java:600) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.access$1900(DefaultConfiguration.java:150) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$SelectedArtifactsProvider.getValue(DefaultConfiguration.java:1341) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$SelectedArtifactsProvider.getValue(DefaultConfiguration.java:1331) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationFileCollection.getSelectedArtifacts(DefaultConfiguration.java:1403) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationFileCollection.access$3500(DefaultConfiguration.java:1346) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationArtifactCollection.lambda$new$0(DefaultConfiguration.java:1970) at org.gradle.internal.model.CalculatedValueContainerFactory$SupplierBackedCalculator.calculateValue(CalculatedValueContainerFactory.java:78) at org.gradle.internal.model.CalculatedValueContainer$CalculationState.lambda$attachValue$0(CalculatedValueContainer.java:216) at org.gradle.internal.Try.ofFailable(Try.java:39) at org.gradle.internal.model.CalculatedValueContainer$CalculationState.attachValue(CalculatedValueContainer.java:211) at org.gradle.internal.model.CalculatedValueContainer.finalizeIfNotAlready(CalculatedValueContainer.java:184) at org.gradle.internal.model.CalculatedValueContainer.finalizeIfNotAlready(CalculatedValueContainer.java:175) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationArtifactCollection.ensureResolved(DefaultConfiguration.java:2012) at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ConfigurationArtifactCollection.getArtifacts(DefaultConfiguration.java:1989) at com.android.build.gradle.internal.ide.dependencies.ArtifactDependencyGraph.getRuntimeComponentIdentifiers(ArtifactDependencyGraph.java:362) at com.android.build.gradle.internal.ide.dependencies.ArtifactDependencyGraph.createDependencies(ArtifactDependencyGraph.java:192) at com.android.build.gradle.internal.ide.ModelBuilder.getDependencies(ModelBuilder.java:841) at com.android.build.gradle.internal.ide.ModelBuilder.createAndroidArtifact(ModelBuilder.java:869) at com.android.build.gradle.internal.ide.ModelBuilder.createVariant(ModelBuilder.java:609) at com.android.build.gradle.internal.ide.ModelBuilder.buildVariant(ModelBuilder.java:564) at com.android.build.gradle.internal.ide.ModelBuilder.buildAll(ModelBuilder.java:221) at com.android.build.gradle.internal.ide.ModelBuilder.buildAll(ModelBuilder.java:144) at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuilderWithParameter.build(DefaultToolingModelBuilderRegistry.java:194) at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$LockSingleProjectBuilder.lambda$build$0(DefaultToolingModelBuilderRegistry.java:211) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$withProjectLock$3(DefaultProjectStateRegistry.java:310) at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:213) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withProjectLock(DefaultProjectStateRegistry.java:310) at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.fromMutableState(DefaultProjectStateRegistry.java:291) at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$LockSingleProjectBuilder.build(DefaultToolingModelBuilderRegistry.java:211) at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuildOperationWrappingBuilder$1.call(DefaultToolingModelBuilderRegistry.java:246) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62) at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76) at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76) at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuildOperationWrappingBuilder.build(DefaultToolingModelBuilderRegistry.java:243) at org.gradle.tooling.internal.provider.runner.DefaultBuildController.getParameterizedModel(DefaultBuildController.java:156) at org.gradle.tooling.internal.provider.runner.DefaultBuildController.getModel(DefaultBuildController.java:104) at org.gradle.tooling.internal.consumer.connection.ParameterAwareBuildControllerAdapter.getModel(ParameterAwareBuildControllerAdapter.java:39) at org.gradle.tooling.internal.consumer.connection.UnparameterizedBuildController.getModel(UnparameterizedBuildController.java:113) at org.gradle.tooling.internal.consumer.connection.NestedActionAwareBuildControllerAdapter.getModel(NestedActionAwareBuildControllerAdapter.java:31) at org.gradle.tooling.internal.consumer.connection.UnparameterizedBuildController.findModel(UnparameterizedBuildController.java:97) at org.gradle.tooling.internal.consumer.connection.NestedActionAwareBuildControllerAdapter.findModel(NestedActionAwareBuildControllerAdapter.java:31) at org.jetbrains.plugins.gradle.model.ProjectImportAction$MyBuildController.findModel(ProjectImportAction.java:574) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker.findVariantModel(AndroidExtraModelProviderWorker.kt:521) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker.access$findVariantModel(AndroidExtraModelProviderWorker.kt:48) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker$getVariantAndModuleDependenciesAction$1.invoke(AndroidExtraModelProviderWorker.kt:450) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker$getVariantAndModuleDependenciesAction$1.invoke(AndroidExtraModelProviderWorker.kt:449) at com.android.tools.idea.gradle.project.sync.SequentialSyncActionRunner.runAction(SyncActionRunner.kt:61) at com.android.tools.idea.gradle.project.sync.SequentialSyncActionRunner.runActions(SyncActionRunner.kt:57) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker.chooseSelectedVariants(AndroidExtraModelProviderWorker.kt:364) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker.populateAndroidModels(AndroidExtraModelProviderWorker.kt:154) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProviderWorker.populateBuildModels(AndroidExtraModelProviderWorker.kt:65) at com.android.tools.idea.gradle.project.sync.AndroidExtraModelProvider.populateBuildModels(AndroidExtraModelProvider.kt:52) at org.jetbrains.plugins.gradle.model.ProjectImportAction.addBuildModels(ProjectImportAction.java:246) at org.jetbrains.plugins.gradle.model.ProjectImportAction.execute(ProjectImportAction.java:116) at org.jetbrains.plugins.gradle.model.ProjectImportAction.execute(ProjectImportAction.java:41) at org.gradle.tooling.internal.consumer.connection.InternalBuildActionAdapter.execute(InternalBuildActionAdapter.java:64) at org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner$ActionRunningListener.runAction(AbstractClientProvidedBuildActionRunner.java:134) at org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner$ActionRunningListener.buildFinished(AbstractClientProvidedBuildActionRunner.java:119) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.event.DefaultListenerManager$ListenerDetails.dispatch(DefaultListenerManager.java:398) at org.gradle.internal.event.DefaultListenerManager$ListenerDetails.dispatch(DefaultListenerManager.java:380) at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:61) at org.gradle.internal.event.DefaultListenerManager$EventBroadcast$ListenerDispatch.dispatch(DefaultListenerManager.java:368) at org.gradle.internal.event.DefaultListenerManager$EventBroadcast$ListenerDispatch.dispatch(DefaultListenerManager.java:355) at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:43) at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:245) at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:157) at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:61) at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:346) at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:249) at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:141) at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37) at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94) at com.sun.proxy.$Proxy19.buildFinished(Unknown Source) at org.gradle.initialization.DefaultGradleLauncher.finishBuild(DefaultGradleLauncher.java:196) at org.gradle.initialization.DefaultGradleLauncher.finishBuild(DefaultGradleLauncher.java:132) at org.gradle.internal.invocation.GradleBuildController$1.create(GradleBuildController.java:72) at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:213) at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:67) at org.gradle.internal.invocation.GradleBuildController.run(GradleBuildController.java:56) at org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner.runClientAction(AbstractClientProvidedBuildActionRunner.java:53) at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:47) at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35) at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35) at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:66) at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32) at org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner.run(FileSystemWatchingBuildActionRunner.java:90) at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:41) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:49) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:44) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62) at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76) at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76) at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:44) at org.gradle.launcher.exec.InProcessBuildActionExecuter.lambda$execute$0(InProcessBuildActionExecuter.java:59) at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:86) at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:58) at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:30) at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.lambda$execute$0(BuildTreeScopeLifecycleBuildActionExecuter.java:34) at org.gradle.internal.buildtree.BuildTreeState.run(BuildTreeState.java:53) at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.execute(BuildTreeScopeLifecycleBuildActionExecuter.java:33) at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.execute(BuildTreeScopeLifecycleBuildActionExecuter.java:28) at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:104) at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:55) at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:64) at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:37) at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.lambda$execute$0(SessionScopeLifecycleBuildActionExecuter.java:54) at org.gradle.internal.session.BuildSessionState.run(BuildSessionState.java:67) at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.execute(SessionScopeLifecycleBuildActionExecuter.java:50) at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.execute(SessionScopeLifecycleBuildActionExecuter.java:36) at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:36) at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:25) at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:59) at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:31) at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:58) at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:42) at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:47) at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:31) at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:65) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78) at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75) at org.gradle.util.Swapper.swap(Swapper.java:38) at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:84) at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37) at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104) at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52) at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) at java.base/java.lang.Thread.run(Thread.java:834) Caused by: org.gradle.api.internal.provider.MissingValueException: Cannot query the value of this provider because it has no value available. at org.gradle.api.internal.provider.AbstractMinimalProvider.get(AbstractMinimalProvider.java:86) at com.android.build.gradle.internal.scope.GlobalScope.getFullBootClasspath(GlobalScope.java:435) at com.android.build.gradle.internal.tasks.R8Task$CreationAction.configure(R8Task.kt:250) at com.android.build.gradle.internal.tasks.R8Task$CreationAction.configure(R8Task.kt:172) at com.android.build.gradle.internal.tasks.factory.TaskConfigurationActions.execute(TaskFactoryUtils.kt:94) at com.android.build.gradle.internal.tasks.factory.TaskConfigurationActions.execute(TaskFactoryUtils.kt:80) at org.gradle.api.internal.DefaultMutationGuard$2.execute(DefaultMutationGuard.java:44) at org.gradle.api.internal.DefaultMutationGuard$2.execute(DefaultMutationGuard.java:44) at org.gradle.configuration.internal.DefaultUserCodeApplicationContext$CurrentApplication$1.execute(DefaultUserCodeApplicationContext.java:100) at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction$1.run(DefaultCollectionCallbackActionDecorator.java:95) at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29) at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75) at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68) at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56) at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71) at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45) at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71) at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction.execute(DefaultCollectionCallbackActionDecorator.java:92) at org.gradle.internal.ImmutableActionSet$SetWithManyActions.execute(ImmutableActionSet.java:329) at org.gradle.api.internal.DefaultDomainObjectCollection.doAdd(DefaultDomainObjectCollection.java:264) at org.gradle.api.internal.DefaultNamedDomainObjectCollection.doAdd(DefaultNamedDomainObjectCollection.java:113) at org.gradle.api.internal.DefaultDomainObjectCollection.add(DefaultDomainObjectCollection.java:258) at org.gradle.api.internal.DefaultNamedDomainObjectCollection$AbstractDomainObjectCreatingProvider.tryCreate(DefaultNamedDomainObjectCollection.java:944) ... 244 more * Get more help at https://help.gradle.org BUILD FAILED in 57s |
报错产生的原因是从 Android SDK 31.0.0 开始,SDK里面的构建工具 dx.jar 被改名成 d8.jar,早期(7.x之前)的构建工具在尝试调用的时候找不到原来的的 dx.jar 导致报错。
解决方法有两种:
Android Studio error "Installed Build Tools revision 31.0.0 is corrupted"
Flutter 2.5 正式版已于上周正式发布!这是一次重要的版本更新,也是 Flutter 发布历史上各项统计数据排名第二的版本。我们关闭了 4600 个 Issue,合并了 3932 个 PR,它们分别来自 252 个贡献者和 216 个审核者。回顾去年 -- 我们收到来自 1337 个贡献者提交的 21072 个 PR,其中有 15172 个被合并。在详述本次更新的内容之前,我们想强调,Flutter 的首要工作始终是高质量交付开发者们所需要功能。
Flutter 2.5 带来了一些重要的性能和工具改进,以帮助开发者们追踪应用中的性能问题。同时,加入了一些新的功能,包括对 Android 的全屏支持、 对 Material You (也称 v3) 的更多支持、对文本编辑的更新以支持切换键盘快捷键、在 Widget Inspector 中查看 widget 详情、在 Visual Studio Code 项目中添加依赖关系的新支持、从 IntelliJ / Android Studio 的测试运行中获得测试覆盖率信息的新支持,以及一个更贴近 Flutter 应用在真实的使用场景下的应用模板等。这个版本充满了令人兴奋的新更新,让我们开始介绍吧!
该版本进行了一些性能上的改进:首先是一项用于从离线训练运行中连接 Metal 着色器预编译的 PR (#25644),这将最坏情况下的光栅化时间减少了 2/3 (如我们的基准测试所示),将第 99 百分位的帧时间减少了一半。我们在减少 iOS 卡顿方面取得了持续性的进展,这也是在这条道路上迈出的另一步。然而,着色器预热只是卡顿的一个来源。在该版本以前,处理来自网络、文件系统、插件或其他 isolate 的异步事件可能导致动画中断,这是另一个卡顿的来源。在该版本中我们对 UI Isolate 的事件循环的调度策略 (#25789) 进行了改进,现在帧处理优先于其他异步事件的处理,在我们的测试中,其导致的卡顿已经被消除。
最近看到有人面试的时候被问到了 “Dart中函数调用是值传递还是引用传递?”
初看感觉跟Java应该是相同的,结果搜索到的链接里面言之凿凿的说是传值操作,顿时感觉有些迷茫。
针对大对象,如果进行拷贝传值,会诱发大量的内存复制操作,这种情况是不可想象的。而基本类型,比如整数,字符,传递地址又是得不偿失的性能损失,明明通过寄存器就可以直接传递参数,却非要从内存中读取一遍,是完全没有必要的。
Dart中,基本数据类型传值,类传引用
类对象传值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
main(){ Test a = new Test(5); print("a的初始值为:${a.value}"); setValue(a); print("修改后a的值为: ${a.value}"); } class Test{ int value = 1; Test(int newValue){ this.value = newValue; } } setValue(Test s){ print("修改value为100"); s.value = 100; } |
输出结果为:
1 2 3 |
a的初始值为:5 修改value为100 修改后a的值为:100 |
基本类型传值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
main(){ int s = 6; setValue(s); print(s); //输出6,而不是7 } class Test{ int value = 1; Test(int newValue){ this.value = newValue; } } setValue(int s){ s += 1; } |
上面的代码,其实转换成 Java 也是一样的结果。
但是深入思考,在 Flutter 中,上面的结论并不全面,对于异构模型而言,取决于内存架构。
如果是非一致内存架构,则CPU跟GPU之间只能进行拷贝操作。
即使是一致内存架构,也分为物理一致还是软件一致,如果仅仅是软件一致,比如PCIE独立显卡,依旧是只能进行拷贝操作,当然拷贝是显卡驱动完成的。只有物理,驱动都完全支持的情况下,才会出现物理意义上的引用传值的情况。有时候,即是两者都支持,GPU对虚拟内存的支持也是一个决定因素。
还是蛮复杂的。
Dart VM
有多种方式去运行Dart
代码,比如:
JIT
模式运行源码或者Kernal binary
snapshot
方式:AOT snapshot
和 AppJIT shanpshot
两者的主要区别在于VM
将Dart
源码转换成可执行代码的时机和方式。
VM
中的任何Dart
代码都是运行在隔离的isolate
当中,isolate
具有自己的内存(堆)和线程控制的隔离运行环境。VM
可以同时具有多个isolate
执行Dart
的代码,但不同的isolate
之间不能直接共享任何的状态,只能通过消息端口来进行通信。 我们所说的线程和isolate
之间的关系其实有点模糊,而且isolate
也比较依赖VM
是怎样嵌入到应用程序当中的。线程和isolate
有下面这样的一些限制:
isolate
,如果线程要进入另一个isolate
就要先退出当前的mutator
线程和isolate
相关联,mutator
是用来执行Dart
代码和调用VM API
的线程所以一个线程只能进入一个isolate
执行Dart
代码,退出之后才能进入另一个isolate
。 不同的线程也能进入同一个isolate
,但不能同时。
当然除了拥有一个mutator
线程之外,isolate
还可以有多个helper
线程,比如:
JIT
编译线程GC
线程GC
标记线程VM
内部使用了线程池(dart::ThreadPool)来管理系统的线程,而且内部是基于 dart::ThreadPool::Task 的概念去构建的,而不是直接使用的系统线程。例如,GC
的过程就是生成一个 dart::ConcurrentSweeperTask 丢给VM
的线程池去处理,而不是使用一个专门的线程来做垃圾回收,线程池可以选择一个空闲的线程或者在没有空闲线程的时候新建一个线程来处理这个任务。类似的,消息循环的处理也并没有使用一个专门的event loop
线程,而是在有新消息的时候产生一个 dart::MessageHandlerTask 给线程池。
你可以在命令行下直接给Dart
的源码去执行,例如:
1 2 |
// hello.dart main() => print('Hello, World!'); |
1 2 |
$ dart hello.dart Hello, World! |
事实上Dart 2 VM
之后就不再支持直接运行Dart
源码了,VM
使用了一种Kernel binaries(也就是 dill 文件)
包含了序列化的 Kernel ASTs。所以源代码要先经过通用前端 common front-end (CFE) 处理成Kernel AST
,而CFE
是用Dart
写的,可以给VM/dart2js/Dart Dev Compiler
这些不同的Dart
工具使用。
那么为了保持直接执行Dart
源码的便捷性,所以有一个叫做kernel service
的isolate
,负责将Dart
源码处理成Kernel
,VM
再将Kernel binary
拿去运行。
但是CFE
和用户的Dart
代码是可以在不同的设备上执行,例如在Flutter
当中,就是将Dart
代码编译成Kernel
,和执行Kernel
的过程个隔离开来,编译Dart
源码的步骤放在了用户的开发机上,执行Kernel
放在了移动设备上,Flutter tools
负责从开发机上将Kernel binary
发送到移动设备上。
flutter tool
并不能自己解析Dart
源码,它使用了一个叫frontend_server
的处理,frontend_server
实际上就是CFE
的封装和Flutter
上特定的Kernel-to-Kernel
的转换。frontend_server
编译Dart
源码到Kernel
文件,flutter tools
将它同步到执行设备上。Flutter
的hot reload
也正是依赖frontend_server
的,frontend_server
在hot reload
的过程中能够重用之前编译中的CFE
状态,只重编已经更改了的部分。
只有Kernel binary
能够被VM
加载,并解析创建各种对象。不过这个过程是懒加载的,只有被使用到的库和类的信息才会被装载。每一个程序的实体都会保留指向对应Kernel binary
的指针,在需要的时候可以去加载更多的信息。
类的信息只有在被使用的过程中(例如:查找类的成员,或新建对象)才会被完全反序列化出来,从Kernel binary
读取类的成员信息,但是函数只会反序列化出函数签名信息,函数体只有在被调用运行的时候才会进一步反序列化出来。
从Kernel binary
加载了足够多的信息供运行时成功解析和调用方法之后,就会去解析和调用到main
函数了。
程序运行的最初所有的函数主体都不是实际可执行的代码,而是一个占位符,指向LazyCompileStub
,它只是简单的要求运行时系统为当前的函数生成可执行的代码,然后尾部调用新生成的代码。
首次编译函数时,是通过未优化编译器来完成的。
未优化编译器通过两个步骤来生成机器码:
AST
进行遍历,以生成函数主体的控制流程图CFG
。CFG
由填充了中间语言IL
指令的基本块组成。这里使用的IL
指令类似于基于堆栈的虚拟机的指令:从堆栈中获取操作数,执行操作,然后将结果压入同一堆栈。CFG
使用一对多的低级IL
指令直接生成机器码:每条IL
指令扩展为多条机器指令这个过程中还没有执行优化,未优化编译器的目标是快速的生成可执行指令。这也意味着不会尝试静态解析任何未从Kernel binary
文件中加载的调用,所以调用的编译是动态完成的。VM
在这个时候还不能使用任何基于vitual table
或者interface table
的调度,而是使用inline caching
实现动态调用的。
inline caching
的核心是在调用的时候缓存对应方法解析的结果,VM
使用的inline caching
机制包括:
stub
,实现方法调用的快速路径,stub
在给定的缓存中查找是否有和接收者匹配的类型,如果找到了增加相应的频次计数器,并且尾部调用缓存的方法;否则,stub
调用系统的查找解析逻辑,如果解析成功就更新缓存,并且后续的调用使用对应缓存的方法。下图说明了inline cache
在animal.toFace()
调用时的关系和状态,使用Dog
实例调用两次,Cat
实例调用一次:
未优化的编译器足以执行所有的Dart
代码,只是它的执行速度会慢一些,所以呢VM
还需要实现自适应的优化编译路径,自适应的优化是采用程序运行时的信息去驱动优化策略。未优化的代码在运行时会收集以下信息:
Inline caches
过程中每一个方法调用接受的类型信息当某个函数的执行计数器达到某个阈值,这个函数就会提交给后台优化编译器进行优化。
优化编译的方式和未优化编译有点类似,通过遍历序列化的Kernel AST
为正在优化的函数构建未优化的IL
,不同的是与其直接将IL
转换为机器码,优化编译器会将未优化的IL
转换成基于static single assignment (SSA)
的优化IL
。基于SSA
的IL
根据收集到的类型信息,经典的优化手段和Dart
的特殊优化:比如,inlining, range analysis, type propagation, representation selection, store-to-load and load-to-load forwarding, global value numbering, allocation sinking, etc
。最后,使用线性扫描寄存器分配器和简单的一对多的IL
指令,将优化的IL
降低为机器码。
编译完成之后后端编译器请求mutator
线程进入一个安全点(safepoint
)并且将优化的代码attaches
到对应的调用函数上,下次调用该函数的时候就能直接使用优化的代码。
需要注意的是,由优化编译器生成的代码是基于运行时收集到的特定信息完成的,例如一个接受动态类型的函数调用,只接收到某个特定的类型,就会被转换成直接的调用,然后检查接收到的类型是否一致。但是在程序的执行过程中,有可能接收到的类型是其他的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
void printAnimal(obj) { print('Animal {'); print(' ${obj.toString()}'); print('}'); } // Call printAnimal(...) a lot of times with an intance of Cat. // As a result printAnimal(...) will be optimized under the // assumption that obj is always a Cat. for (var i = 0; i < 50000; i++) printAnimal(Cat()); // Now call printAnimal(...) with a Dog - optimized version // can not handle such an object, because it was // compiled under assumption that obj is always a Cat. // This leads to deoptimization. printAnimal(Dog()); |
优化代码是基于运行时信息对输入做了一些假设而产生的,如果在后续的运行过程中输入和假设不匹配,它就要防止违反这些假设,并且能够在违反的情况能够恢复正常运行。这个过程就叫着反优化:只要优化版本遇到无法处理的情况,它就会将执行转移到未优化函数的匹配点并继续运行。未优化的版本不做任何假设,可以处理所有可能的输入。
VM
通常会在反优化后放弃优化的版本,然后在以后使用更新的类型反馈再次对其进行优化。VM
防止违反优化假设一般有两种方式:
Inline checks (e.g. CheckSmi, CheckClass IL instructions)
验证输入是否符合优化。例如,将动态调用转换为直接调用时,编译器会在直接调用之前添加这些检查。在此类检查中发生的反优化称为eager deoptimization
,因为它很容易在 check 的时候被检测出来。frames
将被标记,并且在执行返回时将对其进行反优化。这种反优化也称为延迟反优化:因为它会延迟到控制权返回到优化代码为止。VM
有能力序列化isolate
堆上的对象为二进制的snapshot
文件,并且可以使用snapshot
重新创建相同状态的isolate
.
snapshot
针对启动速度做了相应的优化,本质上是要创建的对象的列表和他们之间关系。相对于解析Dart
源码并逐步创建VM
内部的数据结构,VM
可以将isolate
所必须的数据结构全部打包在snapshot
中。
但最初snapshot
是不包括机器码的,在后来开发AOT
编译的时候就加上去了,开发AOT
编译和带机器码的snapshot
是为了允许VM
在一些无法JIT
的平台上运行。带代码的snapshot
几乎和普通的snapshot
的工作方式是一样的,只是它带有一个代码块,这部分是不需要反序列化的,代码块可以直接map
进堆内存。
AppJIT snapshot
可以减少大型Dart
应用(比如:dartanalyzer 或者 dart2js)的JIT
预热时间,在小型应用和VM
使用JIT
编译的时间差不多。
AppJIT snapshots
其实是VM
使用一些模拟的数据来训练程序,然后将生成的代码和VM
内部的数据结构序列化而生成的,然后分发这个snapshot
而不是源码或者Kernel binary
。VM
使用这个snapshot
仍然可以在实际运行的过程中发现数据不匹配训练时而启用JIT
。
AOT snapshot
最初是为了无法进行JIT
编译的平台而引入的,但也可以用来优化启动速度。无法进行JIT
就意味着:
AOT snapshot
必须包含在应用程序执行期间可以调用的每个功能的可执行代码为了满足这些要求,AOT
编译过程中会进行全局静态分析(type flow analysis or TFA),以从已知的入口点确定应用程序的哪些部分是被使用的,分配了哪些类以及类型是如何在程序中传递的。所有这些分析都是保守的,因为必须要保证正确性,有可能会牺牲一点性能,这跟JIT
不太一样,JIT
生成的代码还可以通过反优化来回到未优化的代码上运行。然后所有可达的代码块都将被编译成机器码,不会再进行任何的类型推测的优化。编译完所有的代码块之后,就可以获得堆的快照了。
然后,可以使用预编译的运行时来运行生成的snapshot
,该运行时是Dart VM
的特殊变体,其中不包括诸如JIT
和动态代码加载工具之类的组件。
即使进行了全局和局部分析,AOT
编译的代码仍可能包含无法静态虚拟化的调用操作。为了弥补这种情况,运行时使用了类似JIT
过程中的inline cache
,在这里叫着switchable calls
. JIT
部分上面讲过了,inline cache
主要包括两部分,一个缓存对象(通常是 dart::UntaggedICData )和一个VM
的调用(例如:InlineCacheStub),在JIT
模式下运行时只会更新 cache 的缓存,但是在AOT
中,运行时可以根据inline cache
的状态选择替换缓存和要调用的VM
函数路径。
所有的动态调用最初都是unlinked
状态,首次调用时会触发UnlinkedCallStub
的调用,它又会调用DRT_UnlinkedCall
去 link 当前的调用点。 如果DRT_UnlinkedCall
尝试将调用点的状态切换为monomorphic
,在这个状态下调用就会被替换成直接调用,它通过一个特殊的入口进入方法,并且在入口处验证类型。
在上图的例子中,当 obj.method() 首次执行时,obj 是 C 的实例,那么 obj.method 就会被解析成 C.method,下一次出现同样的调用就会直接调用到 C.method,跳过方法查找的过程。但是进入 C.method 仍然是通过一个特殊的入口进入的,验证 obj 是 C 的实例;如果不是的话,DRT_MonomorphicMiss 就会被调用尝试去进入下一个状态。C.method 有可能仍然是调用的目标函数,例如,obj 是类D的实例,D继承C并且没有override
C.method。在这种情况下,我们检查是否可以进入single target
状态,由 SingleTargetCallStub 实现(也可查看 dart::UntaggedSingleTargetCache)。
在AOT
编译过程中,大部分类会在继承结构的深度优先遍历过程分配一个 ID,如果类C具有D0..Dn这些子类,而且都没有override C.method
,那么C.:cid <= classId(obj) <= max(D0.:cid, ..., Dn.:cid)
表示 obj.method 会被解析成 C.method。在这种情况下,与其进行单态类(monomorphic
状态)的比较,我们可以使用类的 ID 范围去检查C的所有子类。
换言之,调用的时候会使用线行扫描inline cache
, 类似JIT
模式(查看ICCallThroughCodeStub, dart::UntaggedICData 以及 dart::PatchableCallHandler::DoMegamorphicMiss)
当然,如果线性数组中的检查数量超过阈值,将切换为使用类似字典的数据结构。(查看 MegamorphicCallStub, dart::UntaggedMegamorphicCache 以及 dart::PatchableCallHandler::DoMegamorphicMiss)
数月前我们在攻防两个方向经历了一场“真枪实弹”的考验,期间团队的目光曾一度聚焦到Chromium组件上。其实,早在 Microsoft 2018年宣布 Windows的新浏览器 Microsoft Edge 将基于Chromium内核进行构建之前,伴随互联网发展至今的浏览器之争其实早就已经有了定论,Chromium已然成为现代浏览器的事实标准,市场占有率也一骑绝尘。在服务端、桌面还是移动端,甚至据传SpaceX火箭亦搭载了基于Chromium开发的控制面板。
Chromium内核的安全问题,早已悄无声息地牵动着互联网生活方方面面。基于对实战经历的复盘,本文将从Chromium架构及安全机制概况入手,剖析Chromium组件在多场景下给企业带来的安全风险并一探收敛方案。
Chromium主要包含两大核心组成部分:渲染引擎和浏览器内核。
Chromium目前使用Blink作为渲染引擎,它是基于webkit定制而来的,核心逻辑位于项目仓库的third_party/blink/目录下。渲染引擎做的事情主要有:
Blink组件间的调用先后关系,可用下图概括:
可以说,几乎所有发生在浏览器页签中的工作,都有Blink参与处理。由于涉及许多组件库,不难想象过程中可能会出现的安全风险一定不少。据《The Security Architecture of the Chromium Browser》一文的统计数据,约67.4%的浏览器漏洞都出在渲染引擎中,这也是为什么要引入Sandbox这么重要。
浏览器内核扮演连接渲染引擎及系统的“中间人”角色,具有一定“特权”,负责处理的事务包括但不限于:
1) 管理收藏夹、cookies以及保存的密码等重要用户信息
2) 负责处理网络通讯相关的事务
3) 在渲染引擎和系统间起中间人的角色。渲染引擎通过Mojo与浏览器内核交互,包含组件:download、payments等等。
前述部分提到,Chromium渲染引擎涉及大量C++编写的组件,出现漏洞的概率不小。因此,基于纵深防御理念浏览器引入了涉及三层结构。渲染引擎等组件不直接与系统交互,而是通过一个被称为MOJO的IPC组件与浏览器引擎通讯(也被称为:broker),再与系统交互。进而可以实现:即便沙箱中的进程被攻破,但无法随意调用系统API产生更大的危害。有点类似:即便攻破了一个容器实例,在没有逃逸或提权漏洞的情况下,宿主机安全一定程度上不受影响(实际上,浏览器的Sandbox和容器隔离的部分技术原理是相似的)。
浏览器渲染引擎、GPU、PPAPI插件以及语音识别服务等进程是运行在沙箱中的。此外不同系统平台下的部分服务也会受沙箱保护,例如Windows下打印时调用的PDF转换服务、icon浏览服务;MacOS下NaCl loader、需要访问IOSurface的镜像服务等。
更多细节可查阅Chromium项目文件sandbox_type.h和sandbox_type.cc中的源码定义:
1 2 3 4 5 |
// sandbox/policy/sandbox_type.h Line 24 enum class SandboxType { // sandbox/policy/sandbox_type.cc Line 21 bool IsUnsandboxedSandboxType(SandboxType sandbox_type) { |
在Windows平台上,Chrome组合使用了系统提供的Restricted Token、Integrity Level、The Windows job object、The Windows desktop object机制来实现沙盒。其中最重要的一点是,把写操作权限限制起来,这样攻击这就无法通过写入文件或注册表键来攻击系统。
Chrome在Linux系统上使用的沙箱技术主要涉及两层:
层级 | 功能 |
---|---|
Layer - 1 | 用于限制运行在其中的进程对资源的访问 |
Layer - 2 | 用于有关进程对系统内核某些攻击面的访问 |
第一层沙箱采用setuid sandbox方案。
其主要功能封装在二进制文件chrome_sandbox内,在编译项目时需要单独添加参数“ninja -C xxx chrome chrome_sandbox”编译,可以通过设置环境变量CHROME_DEVEL_SANDBOX指定Chrome调用的setuid sandbox二进制文件。
setuid sandbox主要依赖两项机制来构建沙盒环境:CLONE_NEWPID和CLONE_NEWNET方法。CLONE_NEWPID一方面会借助chroots,来限制相关进程对文件系统命名空间的访问;另一方面会在调用clone()时指定CLONE_NEWPID选项,借助PID namespace,让运行在沙盒中的进程无法调用ptrace()或kill()操作沙盒外的进程。而CLONE_NEWNET则用于限制在沙盒内进程的网络请求访问,值得一提的是,使用该方法需要CAP_SYS_ADMIN权限。这也使得当Chrome组件在容器内运行时,沙箱能力所需的权限会和容器所管理的权限有冲突;我们无法用最小的权限在容器里启动Chrome沙箱,本文4.2.2部分会详细阐述此处的解决之道。
更多详参见Linux Namespace及cgroups介绍说明:《Resource management: Linux kernel Namespaces and cgroups》- https://sites.cs.ucsb.edu/~rich/class/cs293b-cloud/papers/lxc-namespace.pdf
由于setuid sandbox方案存在一定短板。自Chrome 44版本起已推荐namespaces sandbox来替代setuid sandbox方案,其主要依赖于Linux内核提供的user namespaces机制,相关逻辑可在项目的如下行代码看到:
1 |
https://source.chromium.org/chromium/chromium/src/+/main:sandbox/policy/linux/sandbox_linux.cc;drc=2311701cab51ef03be34fba491e0a855371d4f84;l=544 |
第二层沙箱采用Seccomp-BPF方案,用来限制进程访问内核特定攻击面。
其原理是:通过将Seccomp和BPF规则结合,实现基于用户配置的策略白名单,对系统调用及其参数进行过滤限制。
1 2 |
// sandbox/policy/linux/sandbox_linux.cc Line 413 StartSeccompBPF(sandbox_type, std::move(hook), options); |
Chromium涉及的组件众多,使用的C++语言天然决定了会潜在不少安全问题。例如:一个V8中的内存安全问题(如:CVE-2021-21220、CVE-2019–5782),组合Web Assembly将Shellcode写入RWX Pages,在未受沙箱保护的情况下,就能实现远程代码执行。
沙箱机制组合使用了OS相关的隔离能力(如:Linux平台上的namespace、Seccomp-BPF机制),限制了被沙箱保护进程的资源访问以及syscall能力,能很好的防止出现在渲染引擎中的漏洞,被用于直接实现RCE :但沙箱机制也存在一些不足,历史上也出现过沙箱逃逸的漏洞,例如:Google Project Zero团队曾发布的《Virtually Unlimited Memory: Escaping the Chrome Sandbox》一文。
综上,在无法100%预防Chromium渲染进程出现内存安全问题的情况下,开启沙箱保护是一项必须落地的最佳安全实践。
作为一款客户端组件,在评估Chromium漏洞时,常常会聚焦于客户端的攻防场景。但根据我们的经验,受chromium漏洞影响的不仅有客户端应用,也包含了服务器上运行的程序,例如:部署在服务器端、基于Chrome Headless应用的爬虫程序等。
随着Phantomjs项目停止维护,Chromium headless已经成为Headless Browser的首选。在日常开发、测试、安全扫描、运维中,有许多地方会用到Headless Browser,包括不限于以下场景:
1 2 3 4 5 |
● 前端测试 ● 监控 ● 网站截图 ● 安全扫描器 ● 爬虫 |
在这些场景中,如果程序本身使用的Chromium存在漏洞,且访问的URL可被外部控制,那么就可能受到攻击最终导致服务器被外部攻击者控制。
以常见的使用Chrome headless的爬虫为例,如果在一些网站测试投放包含exploit的链接,有概率会被爬虫获取,相关爬取逻辑的通常做法是新建tab导航至爬取到的链接。此时,如果爬虫依赖的chromium应用程序更新不及时,且启动时设置了—no-sandbox参数,链接指向页面内的exploit会成功执行,进而允许攻击者控制爬虫对应的服务器。
为何 —no-sandbox 会如此泛滥呢?我们不妨来看一下,当我们在ROOT下启动Chrome,会有什么样的提示呢?
我们会得到 Running as root without —no-sandbox is not supported 的错误提示,且无法启动 Chrome;这对于以研发效率和产品功能优先的研发同学来说无异于提示“请使用 —no-sandbox 来启动 Chrome”, 应用容器化的进程也加剧了使用ROOT用户启动应用程序的情况。你不得不创建一个新的普通用户来启动Chrome服务,例如在 Dockerfile 里加入 RUN useradd chrome 和 USER chrome 语句;有些基于Chrome的著名第三方库甚至会在代码中隐形植入关闭 sandbox的代码,当研发同学在ROOT下启动应用程序时,第三方库会默认关闭sandbox,添加 —no-sandbox 参数,例如 Golang 第三方 package Chromedp 的代码:
此时,对于开发同学来说使用 —no-sandbox 参数甚至是无感的,直至自己的容器或服务器被攻击者入侵控制。
即使研发同学sandbox来避免安全风险的意识,在容器化的应用内启动chrome也是不易的;为镜像创建一个新的非ROOT用户并非唯一的条件,Chrome sandbox 需要调用一些特定的 syscall 或 linux capabilities 权限用于启动 sandbox 逻辑,同时容器镜像需要打入 chrome-sandbox二进制文件并写入环境变量以供Chrome进程找到sandbox程序。若未对Chrome容器进行特定的权限配置,chrome将输出 Operation not permitted 报错信息并退出。
所以,网络上有大量的文档和博客推荐启用 —no-sandbox 来解决 Chrome headless 的使用问题,这也间接助长了 —no-sandbox 参数这种错误用法的泛滥:
我们将在后面的章节里详细为您讲解 Chrome Sandbox 在容器以及容器集群中方便快捷且安全合理的部署解决方案。
未知攻焉知防?虽然在已有Exploit的情况下进行漏洞利用并不困难,但知悉漏洞利用的流程和攻击行为有助于我们更好的构建安全能力。以下以最近的CVE-2021-21224漏洞为例,当服务端上程序使用的chromium版本存在漏洞时,且未开启Sandbox,可以利用这个漏洞来获取服务器的权限。
首先攻击者使用metasploit生成shellcode,这里假设chromium是在linux上运行且架构为x64。同时,考虑到爬虫运行结束后往往会结束浏览器进程,通过设置PrependFork为true可以保证session的持久运行。
生成shellcode后监听端口:
实战中,可以通过投递带exploit的链接到各个网站上,这里假设攻击者控制的服务器正在被爬取或者正在被渗透测试人员的扫描器扫描:
成功获取到爬虫/扫描器的服务器session:
meterpreter的进程是fork后的chrome子进程:
可以猜想,不仅是各种内嵌浏览器的客户端程序易受chromium相关漏洞影响,可能有相当多的服务端程序也暴露在chromium 0Day/Nday的攻击下。 chromium漏洞将会成为企业防御边界的新的突破口,而这个突破口是自内而外的,相比开放端口在外的服务漏洞,这种攻击可能会更隐蔽。
作为防御方,我们也可以利用chromium漏洞来反制一些攻击者,如果攻击者安全意识较差或者使用的工具安全性不强,防御方在服务器上托管带有exploit的网页,攻击者的爬虫/扫描器扫到了这些网页就可能被反制攻击。
在面对Chromium组件风险时,客户端场景往往首当其冲。通常,其风险成立条件有两点:1、使用了存在漏洞的Chromium组件;2、可以指定Webview组件访问特定的网站地址。
目前,移动客户端主要分两大“阵营”:安卓和iOS,最大相关风险是Webview类组件。前者 Android System Webview是基于Chromium源代码开发的,所以当1 Day披露时,需要及时跟进影响;iOS App一般会使用WKWebView和JavaScriptCore,Chromium 1 Day影响iOS应用的可能性较低。
除了使用系统自带的Webview组件,另外一种比较常见且更容易引起注意的方式是使用应用内置或独立于系统之外的浏览器组件;此时,应用会选用Chromium体系的概率较高。应用选择自己内置并维护浏览器组件的原因有很多,例如以下几类需求:
1、在浏览器内核层回收更多用于Debug的客户端信息;
2、支持如夜间模式、中文优化等用户需求;
3、支持更多的视频格式和文件格式;
也有应用为了应对此前App Store在WWDC大会提出的限制(即App Store中的所有应用都必须启用App Transport Security 安全功能并全量走HTTPS),使用改过的Webview组件曲线救国,以便达到App Store的合规需求。
也因为应用自己维护所使用的浏览器组件,当系统的WebView跟随系统升级而修复漏洞时,应用所使用的的浏览器组件并不跟着更新;作为应用开发者自己维护的硬分支,Chromium不断的功能变更和漏洞修复补丁都需要应用开发者自行合并和兼容;这不仅需要硬核的浏览器研发能力也需要日以继夜不断的坚持。再加上,无论在移动端还是桌面客户端,在使用应用内WebView时为了更加轻便和简洁,浏览器组件多是以单进程的方式启动;而在我们之前对Sandbox技术的介绍中,浏览器Sandbox和单进程WebView组件显然是冲突的;这也使得历史上关闭Sandbox能力的客户端程序,在漏洞修复过程中,对于开启Sandbox的修复操作存在历史包袱。
无论如何,我们始终不建议移动端应用的WebView组件可以由用户控制并打开开放性的页面;这会使得应用内加载的内容可能存在不可控或不可信的内容。WebView组件可以打开的URL,应该用白名单进行限制;特别是可以用 Deeplink 打开并且存在 URL 参数的 WebView。
许多桌面客户端应用也是基于Chromium构建的。一类是基于Chromium定制的浏览器产品、或内置基于Chromium开发Webview组件的桌面客户端应用;另一类是基于Electron构建的桌面客户端应用。
前者与传统Chrome浏览器或是嵌入在移动客户端的Webview组件类似,如果未开启沙箱保护,面临很大的风险。而后者Electron则是在评估Chromium漏洞攻防利用场景时,比较容易被忽视的一块。Electron基于Chromium和Node构建,其主要特性之一就是能在渲染进程中运行Node.js。目前有许多客户端工具基于它开发,涉及:VS Code、Typora、Slack等。默认情况下,渲染器进程为受沙箱保护,这是因为:大多数Node.js 的API都需要系统权限,没有文件系统权限的情况下require()是不可用的,而该文件系统权限在沙箱环境下是不可用的,但功能性进程受沙箱保护。 Electron除面临渲染引擎本身的安全风险外,主要风险源自于其本身的功能特性 —— nodeIntegration。当该选项被设置为true,表示renderer有权限访问node.js API,进而执行“特权”操作。这时如果攻击者能自由控制渲染的页面内容,则可直接实现RCE。
回到我们今天的主题:修复和防御。如上我们知道,Chromium的安全问题是方方面面的,各类安全风险也会在不同的场景上产生,那么如何收敛就是企业安全建设永恒的话题;最后我们想分享我们的安全实践经验,力求解答在安全实践中我们遇到的以下几个问题,如:Chrome组件的漏洞都有哪些?Google又是如何跟进它们的?我们又该如何评估和检测Chrome持续更新过程中所公开的1Day风险?最终如何修复?Linux容器中开启Chrome沙盒的最佳实践又是什么?
有两个渠道可以及时了解到Chromium漏洞披露情况:
● Chromium工单系统。该平台上收录了所有已公开的Chrome安全Issue,可借助特定关键词检索。如检索已公开的高风险安全问题,可访问:https://bugs.chromium.org/p/chromium/issues/list?can=1&q=Security_Severity%3DHigh%20&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified&sort=-modified&num=100&start=
● Chrome发布日志。Chrome稳定版本发布消息会在https://chromereleases.googleblog.com/上发出,和稳定版本发布消息一起的还有该版本做了哪些安全更新以及对应漏洞的奖金。
事实上,甲方安全人员还可以借助一些技巧,提前了解安全问题的修复细节。Gerrit是基于git的一款Code Review平台,chrome team使用该平台进行code review:https://chromium-review.googlesource.com/。该平台上的主题会关联对应的issue id,通过对应修复commit的主题可以了解到issue的修复方案和代码。
chromium使用https://bugs.chromium.org对chromium的bug进行跟踪。可以用短链来访问对应的issue,例如issue 1195777可以用该链接访问:https://crbug.com/1195777。
chromium安全问题对应关联的issue在修复期间并且在补丁发布后也不一定是可见的,官方给出的披露原则是在补丁广泛应用后才会开放issue的限制。但是Gerrit上对issue修复代码的code review和关联信息是一直可见的,我们如果想了解某个issue具体的修复代码和方案可以在Gerrit上找到。
以issue 1195777为例,在Gerrit使用bug搜索关键字可以搜到对应commit的code review主题:
而如果只有CVE编号,CVE的References一般会给出issue的短链,虽然通常该issue限制访问,但是仍可以通过Gerrit了解相关issue的具体修复代码,安全研究者可以根据这些修复代码对该问题进行分析,进而推测出漏洞复现代码。
难怪Twitter上某位研究员会说:“如果0-Day有Chromium Bug Tracker的编号,那它就不算0-Day了”。
另外,还可以从 国家信息安全漏洞库 检索相关的漏洞问题。
通常,在Chromium官方披露漏洞或外部已出现在野利用的案例后,应进行风险评估,主要聚两个问题:
● 公司内哪些产品受漏洞影响?
● 外部披露的exp是否能真实利用形成危害?
在获悉一个漏洞的存在后,安全人员需要评估漏洞对公司的影响如何。通常一个可利用的漏洞在披露后会马上有安全人员写出exploit,而公开的exploit将导致利用门槛的大幅降低。因此,常常需要监控公开信息渠道的exploit信息,例如:监控Github、Twitter等平台的消息。但是早在exploit披露前,就可以通过
Chromium Monorail系统中的issues、代码CL或者更新日志提前了解风险。
一个漏洞的影响评估流程可以按下面几步走:
1、 确定存在漏洞组件为哪个部分。
2、 采集使用了该组件的产品(包括:使用了嵌入式浏览器的客户端、单纯使用v8引擎等组件的软件、使用了chrome headless的服务端程序);有些产品仅使用chrome的一部分组件可能不受影响。例如:v8就会影响所有用Chromium内核的产品,但iOS客户端如果用JavaScriptCore,则不受影响。
3、 确认使用存在漏洞组件的产品使用的版本是否受影响,如果产品本身对chromium进行定制化开发的情况下,难以根据版本确定,可以通过PoC(部分场景下,可借助Chromium项目中的单元测试用例)进行黑盒测试或者白盒审计受影响代码是否存在,是否存在漏洞的触发路径。
4、 原则上内存破坏类的漏洞在没有exploit公开的情况下也需要尽快修复,存在公开exploit的情况下,需要立即修复;有时候exploit使用到的exploit技术可能仅适用于某些版本的chromium,但是并不代表这些版本之外的chromium完全没有利用的可能。例如使用WebAssembly创建RWX pages来写入shellcode的技术在客户端使用的chromium版本不支持,但依旧存在通过ROP等技术来到达RCE的可能性。
V8等组件会编写单元测试js文件,可以基于此修改形成页面,来通过黑盒的方式判断组件是否受对应漏洞影响。对于漏洞测试来说,这个资源也是极好的TestCase。
以CVE-2021-21224为例,编写黑盒测试用例过程如下:
1、 通过Issue编号定位到对应的Chromium Gerrit工单
https://chromium-review.googlesource.com/c/v8/v8/+/2838235
2、 定位到官方提供的、针对该漏洞的单元测试文件
https://chromium-review.googlesource.com/c/v8/v8/+/2838235/4/test/mjsunit/compiler/regress-1195777.js
1 2 3 4 5 6 7 8 9 10 11 12 13 |
(function() { function foo(b) { let y = (new Date(42)).getMilliseconds(); let x = -1; if (b) x = 0xFFFF_FFFF; return y < Math.max(1 << y, x, 1 + y); } assertTrue(foo(true)); %PrepareFunctionForOptimization(foo); assertTrue(foo(false)); %OptimizeFunctionOnNextCall(foo); assertTrue(foo(true)); })(); |
3、 基于单元测试文件修改生成黑盒测试用例
如果仔细观察,会发现上述单元测试代码中包含%开头的函数。它们是v8引擎内置的runtime函数,用于触发v8引擎的某些功能特性,需要在v8的debug版本d8命令行工具启动时,追加—allow-natives-syntax参数才会生效。因此,直接将上述单元测试js来测试是无法准确测出是否存在漏洞的。但可以通过编写js代码,实现相同的效果,例如:
值得一提的是,前述漏洞的单元测试用例并不会造成浏览器tab崩溃,而只是输出的数值与预期不符。因此,可以看到上述单元测试用例中引入了assertTrue、assertEquals等断言方法,用于判断单元测试数值是否与预期相等。如果不等,则认为存在漏洞。在进行改造时,也要一并用自己的JavaScript代码替换。最终,前述官方提供的测试用例可改造如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
function foo(b) { let y = (new Date(42)).getMilliseconds(); let x = -1; if (b) x = 0xFFFF_FFFF; return y < Math.max(1 << y, x, 1 + y); } function check(val) { console.log(val); if(!val){ alert("CVE-2021-21224 found!") } } // 断言函数,判断函数返回值是否为true // assertTrue(foo(true)); let val1 = foo(true); check(val1); // v8内置runtime函数 // %PrepareFunctionForOptimization(foo); for (i=0; i<100000; i++) { foo(false); } // 断言函数,判断函数返回值是否为true // assertTrue(foo(false)); let val2 = foo(false); check(val2); // v8内置runtime函数 // %OptimizeFunctionOnNextCall(foo); for (i=0; i<100000; i++) { foo(true); } // 断言函数,判断函数返回值是否为true // assertTrue(foo(true)); let val3 = foo(true); check(val3); |
4、 最终效果如下
如上面所述,由于Chrome漏洞即便在没有正式发布安全公告前,就已经有Issue ID,且能通过Gerrit平台找到涉及的代码变动。因此,开发人员可以抢先在公司内部代码仓库进行全局静态代码扫描并修复问题。
目的 | 策略 | 目的 |
---|---|---|
收集包含chromium组件的仓库 | 扫描特定文件名特征(如有需要可添加一些代码特征) | 掌握企业内应用的组件指纹 |
精确判断某个Issue对应的代码是否已修复 | 扫描文件名特征 + 每个Issue对应的代码特征 | 追踪特定漏洞的修复情况 |
● 收集包含chromium组件的仓库
不同的项目可能会引入Chromium整体或部分相关的组件,通常可结合文件名、或特定的代码片段,在公司的代码仓库中收集包含相关指纹的仓库。
● 精确判断某个Issue对应的代码是否已修复
以要精准扫描全局代码仓库中是否存在涉及v8组件的CVE-2021-21224的漏洞代码为例。可基于semgrep类方案,对公司代码仓库进行全局检查,编写静态代码扫描步骤如下:
1、 根据Issue号找到对应的漏洞修复代码变动
● https://chromium-review.googlesource.com/c/v8/v8/+/2838235
● https://chromium-review.googlesource.com/c/v8/v8/+/2838235/4/src/compiler/representation-change.cc
2、确定涉及文件representation-change.cc,存在漏洞的代码特征为
1 2 3 4 5 6 7 |
if (output_type.Is(Type::Signed32()) || output_type.Is(Type::Unsigned32())) { op = machine()->TruncateInt64ToInt32(); } else if (output_type.Is(cache_->kSafeInteger) && use_info.truncation().IsUsedAsWord32()) { op = machine()->TruncateInt64ToInt32(); } |
3、可编写semgrep规则如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
rules: - id: chromium-v8-1195777 message: | A Type Confusion vulnerability in Chromium v8 Engine, labeled as CVE-2021-21224, is found in the project. Please fix this bug by following the [instructions](https://iwiki.woa.com/pages/viewpage.action?pageId=801201056) on iWiki. languages: - generic metadata: references: - https://chromereleases.googleblog.com/2021/04/stable-channel-update-for-desktop_20.html - https://chromium-review.googlesource.com/c/v8/v8/+/2817791/4/src/compiler/representation-change.cc category: security severity: WARNING pattern: | if (output_type.Is(Type::Signed32()) || output_type.Is(Type::Unsigned32())) {...} else if (output_type.Is(cache_->kSafeInteger) && use_info.truncation().IsUsedAsWord32()) {...} paths: include: - "representation-change.cc" |
4、调用命令扫描
1 |
semgrep --include=representation-change.cc --config=1195777.yml thirdparty_v8 |
5、最终效果,如下
针对部署在服务器端、且使用了Chromium的程序,除了上述方法之外,可以考虑借助HIDS、EDR或RASP等系统采集进程特征,排查存在风险的实例。
同时满足下面两个条件的 cmdline,其进程我们就可以认为是存在风险的:
● 程序名包含 Chrome 或 Chromium
● 且 Cmdline 中包含 —no-sandbox 参数或 —disable-setuid-sandbox
这里大家可能会产生疑问,这里为什么单独检测 Sandbox 的开启与关闭就判断风险呢?若Chromium组件已经使用最新发布的commit编译而成,包含了所有的漏洞补丁,也一样不会受到1Day和NDay漏洞的影响。其实,这里主要考虑到Chrome在对漏洞修复是十分频繁的,持续的升级存在一定的维护成本,且不排除攻击者拥有Chromium 0Day的可能。相较之下,逃逸Sandbox以控制浏览器所在的主机,是比较困难的;所以要求线上业务,尽可能开启 Chromium Sandbox特性。
另外,以上方案若Chrome可执行文件被修改了文件名,则容易产生漏报。另一种可选的方案是:提取出多个Chrome的特有选项进行过滤。例如,headless浏览器启动时一般不会导航至特定url,此时命令行会存在about:blank,再用Chrome特定的区别于其他浏览器的选项进行排除。
更复杂的方案可以提取出Chrome执行文件的文件特征,或者建立Chrome执行文件的hashsum数据库来判断进程的执行文件是否是Chrome浏览器,进而再筛选启动时用了不安全配置的进程。其实,我们在大规模观察相关的进程数据和运营之后,发现利用 —no-sandbox 单个因素进行进程数据分析并获取未开启Sandbox的Chromium进程,这样简单粗暴的做法并不会产生太多误报;有些进程看似非 Chromium 浏览器,但其实也集成了 Chromium 并使用 no-sandbox 参数。
无论是客户端还是服务端,为了解决Chrome漏洞的远程命令执行风险,启用Chrome Sandbox,去除启动Chrome组件时的 —no-sandbox参数都是必须推进的安全实践。
如果客户端程序直接使用了Chrome的最新版本,且未进行过于复杂的二次开发和迁移,没有历史包袱的话,在客户端里开启Chrome Sandbox,其实就是使用Chrome组件的默认安全设计,障碍是比较小的。
此处根据不同场景和需求,存在三种不同的修复方案:
1、启动 Chrome 时切勿使用 —no-sandbox 参数,错误的例子如:./bin/chrome --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 --disable-setuid-sandbox --no-sandbox
2、使用普通用户而非 root 用户启动 chrome headless 进程
下载 https://download-chromium.appspot.com/ 中的最新版本进行更新,并在后续迭代中持续升级到最新版(Chromium的最新版本会编译最新的MR和Commit,因此也会修复Chrome未修复的0.5Day漏洞,下载链接包含了所有的操作系统的 Chromium ,例如Linux 可访问 https://download-chromium.appspot.com/?platform=Linux_x64&type=snapshots 下载)。
请注意,如果不希望相似的安全风险如之前的Fastjson那样需要反复跟进并且高频推动业务修复,强烈建议安全团队推动业务参考方案一开启Sandbox,方案二可以当成短期方案规避当前风险。经统计,2010年至今Google共对外公开Chromium高危漏洞1800多个;Chromium的漏洞修复十分频繁,若不开启Sandbox,需持续更新最新版本。
若要启用Sandbox,需要解决一定的依赖:首先,Chrome的Sandbox技术依赖于Linux内核版本,低版本的内核无法使用。各Sandbox技术Linux内核依赖可参考下图
Chrome 运行时会寻找 chrome-sandbox 文件,一般下载 Chrome 的 Release 时,Chrome程序目录下都包含了 Sandbox 程序,若无法寻找到 chrome-sandbox 文件可能会产生下述 Error 信息:
[0418/214027.785590:FATAL:zygote_host_impl_linux.cc(116)] No usable sandbox! Update your kernel or see https://chromium.googlesource.com/chromium/src/+/master/docs/linux/suid_sandbox_development.md for more information on developing with the SUID sandbox. If you want to live dangerously and need an immediate workaround, you can try using —no-sandbox.
可参考
https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#alternative-setup-setuid-sandbox 进行配置。若服务器的 Chrome 目录下包含了 chrome-sandbox 文件,则可以直接修改配置运行,若不包含,可前往 https://download-chromium.appspot.com/ 下载对应版本的 chrome-sandbox 文件使用。(注:Chrome 可执行文件的同一目录内包含 chrome-sandbox 程序,则无需手动设置 CHROME_DEVEL_SANDBOX 环境变量)
另外一个更加合适合理的设计是尽量避免使用应用内置的浏览器打开开放性URL页面。我们应该尽量使用系统的浏览器去打开非公司域名的URL链接(同时应该注意公司域名下的URL跳转风险);把打开URL的能力和场景交还给系统浏览器或专门的浏览器应用;保障应用内加载的资源都是可控的。
此方案同样适用于:客户端内置的Chromium Webview组件短时间内无法随系统快速更新,且由于历史包袱无法Webview组件无法开启沙箱。此时,在客户端引入一个“降级”逻辑,将不可信的页面跳转交给系统默认的浏览器打开。由于系统默认的浏览器通常默认是打开沙箱的,因此不失为一种“缓兵之计”。
业界云原生实践的发展非常迅速,企业应用容器化、组件容器化的脚步也势不可挡。从当前的Kubernetes应用设计的角度出发,Chrome Headless组件在逻辑上是非常适用于无状态应用的设计的,所以Chrome组件在容器化的进程也比较快。也因此,在HIDS进程大盘中, 启用 —no-sandbox 的 Chrome headless 进程也一直在持续增多。
如果 Chrome 浏览器组件已经实现了容器化,那么您想使用 Chrome sandbox 肯定会遇到各种麻烦;网络上有很多不完全安全的建议和文档,请尽量不要给容器添加 privileged 权限和 SYS_ADMIN 权限,这将可能引入新的风险,详情可参考我们之前的文章《红蓝对抗中的云原生漏洞挖掘及利用实录》。
我们应该尽量使用例如 —security-opt 的方案对容器权限进行可控范围内的限制,构建一个 Seccomp 白名单用于更安全的支持容器场景,这是一个足够优雅且较为通用的方式。如果企业已经践行了K8s容器集群安全管理的规范和能力,在集群内新建带有privileged 权限或 SYS_ADMIN 权限的应用容器是会被集群管理员明确拒绝的,Seccomp是一个安全且可管理的方案。
你可以参考下述方式启动一个带有 seccomp 配置的容器:
docker run -it —security-opt seccomp:./chrome.json chrome-sandbox-hub-image-near —headless —dump-dom https://github.com/neargle
实际上seccomp配置文件规定了一个可管理的syscall白名单,我们的配置文件就是需要把Sandbox所需的系统权限用白名单方式赋予给容器,使得容器可以调用多个原本默认禁止的 syscall。可以使用下列命令来检测当前的操作系统是否支持 seccomp:
➜ grep CONFIG_SECCOMP= /boot/config-$(uname -r)
CONFIG_SECCOMP=y
如果你的容器使用K8s进行部署,那你可以在 spec.securityContext.seccompProfile 中配置上述 chrome.json 文件。
通过白名单设置 Chrome 所需的 syscall 以最小化容器权限,避免容器逃逸的风险,同时也符合多租户容器集群的安全设计,是一个推荐的方案;设置 Seccomp 后,容器内可正常启用 chrome-sandbox,如下图。
根据在HIDS收集到的资产和内部操作系统的特性,可以利用 strace 工具很容易收集到启动 Sandbox 所需的 SysCall,并根据 SysCall 编写所需的 seccomp 配置文件。当然直接使用开源社区里现成的配置文件也能适用于绝大部分环境,著名前端测试工具 lighthouse 所用的配置文件是一个非常不错的参考:https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/recipes/docker-client/seccomp-chrome.json。
随着Chromium在企业各场景下的广泛应用,需要针对性地设置风险例行检测及应急响应方案,涉及的风险与应用场景、检查及修复方式,可概括如下:
除Chromium外,企业开发时也不乏会涉及到Safari、Firefox等其他浏览器类组件的场景,在进行风险排查和响应时可借鉴类似的思路。
[1] Linux Sandboxing
https://chromium.googlesource.com/chromium/src/+/HEAD/docs/linux/sandboxing.md
[2] The Security Architecture of the Chromium Browser
https://seclab.stanford.edu/websec/chromium/chromium-security-architecture.pdf
[3] My Take on Chrome Sandbox Escape Exploit Chain
https://medium.com/swlh/my-take-on-chrome-sandbox-escape-exploit-chain-dbf5a616eec5
[4] Linux SUID Sandbox
https://chromium.googlesource.com/chromium/src/+/HEAD/docs/linux/suid_sandbox.md
[5] How Blink Works
https://docs.google.com/document/d/1aitSOucL0VHZa9Z2vbRJSyAIsAz24kX8LFByQ5xQnUg/edit
[6] Chrome浏览器引擎 Blink & V8
https://zhuanlan.zhihu.com/p/279920830
[7] Blink-in-JavaScript
https://docs.google.com/presentation/d/1XvZdAF29Fgn19GCjDhHhlsECJAfOR49tpUFWrbtQAwU/htmlpresent
[8] core/script: How a Script Element Works in Blink
https://docs.google.com/presentation/d/1H-1U9LmCghOmviw0nYE_SP_r49-bU42SkViBn539-vg/edit#slide=id.gc6f73
[9] [TPSA21-12] 关于Chrome存在安全问题可能影响Windows版本微信的通告
https://mp.weixin.qq.com/s/qAnxwM1Udulj1K3Wn2awVQ
[10] Hacking Team Android Browser Exploit代码分析
https://security.tencent.com/index.php/blog/msg/87
[11] 物联网安全系列之远程破解Google Home
https://security.tencent.com/index.php/blog/msg/141
[12] Android Webview UXSS 漏洞攻防
https://security.tencent.com/index.php/blog/msg/70
准备同步代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ cd ~ $ mkdir flutter_engine $ cd flutter_engine # 检出构建依赖 # git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git # 国内用户可从gitlab中检出 $ git clone https://gitlab.com/chromiumsrc/depot_tools.git # 添加到搜索路径 $ export PATH=$PATH:`pwd`/depot_tools |
生成配置代码同步配置文件:
1 |
$ vim .gclient |
内容如下:
1 2 3 4 5 6 7 8 9 10 |
solutions = [ { "managed": False, "name": "src/flutter", "url": "https://github.com/flutter/engine.git", "custom_deps": {}, "deps_file": "DEPS", "safesync_url": "", }, ] |
注意,上面的 name 字段不能变更,否则会在同步代码的时候报错
1 2 3 4 |
________ running 'python3 src/flutter/tools/pub_get_offline.py' in '~/flutter_engine' /usr/local/bin/python3: can't open file '~/flutter_engine/src/flutter/tools/pub_get_offline.py': [Errno 2] No such file or directory Error: Command 'python3 src/flutter/tools/pub_get_offline.py' returned non-zero exit status 2 in ~/flutter_src /usr/local/bin/python3: can't open file '~/flutter_engine/src/flutter/tools/pub_get_offline.py': [Errno 2] No such file or directory |
执行代码同步命令(国内需要配置代理):
1 2 3 4 5 6 7 8 9 |
$ gclient sync -D --force --reset # 同步时间超长,要花费几个小时,同步代码约16-17GB,压缩之后约8.75GB # 如果已经执行过同步,代码会被锁定到以前拉取到的某个特定版本。 # 如果想更新到主分钟的最新代码,目前没有很好的办法,只能先整个删除src目录,再次执行同步命令 # 当然,如果需要更新flutter的代码,相关依赖不更新的情况下, # 也可以直接在src/flutter目录下执行git pull 命令来手动更新 |
从源代码开始构建:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ cd src $ ./flutter/tools/gn --ios --unoptimized # 编译配置文件生成在out目录下 # 执行编译 $ ninja -C out/ios_debug_unopt $ ./flutter/tools/gn --android --unoptimized $ ninja -C out/android_debug_unopt # release $ ./flutter/tools/gn --android --runtime-mode=release $ ninja -C out/android_release # 生成的 libflutter.so 很大,被压缩后的在 lib.stripped 目录下,但是这个版本的符号还在,进一步压缩可以移除符号 |
一般情况下我们使用的是官方版本的引擎,如果需要调试 libflutter.so 里面的符号,可以在flutter_infra页面直接下载带有符号表的SO文件,根据打包时使用的Flutter工具版本下载对应的文件即可。
比如2.8.1版本:
1 2 3 4 5 6 7 8 |
$ flutter --version # version命令可以看到Engine对应的版本 06afdfe54e Flutter 2.8.1 • channel stable • https://github.com/flutter/flutter.git Framework • revision 77d935af4d (3 周前) • 2021-12-16 08:37:33 -0800 Engine • revision 890a5fca2e Tools • Dart 2.15.1 $ cat `dirname $(which flutter)`/internal/engine.version # flutter安装目录下的engine.version文件也可以看到完整的版本信息 890a5fca2e34db413be624fc83aeea8e61d42ce6 890a5fca2e34db413be624fc83aeea8e61d42ce6 |
拿到引擎版本号后在https://console.cloud.google.com/storage/browser/flutter_infra_release/flutter/890a5fca2e34db413be624fc83aeea8e61d42ce6/ 看到该版本对应的所有构建产物,下载android-arm-release、android-arm64-release和android-x86目录下的symbols.zip。
使用过 Flutter 构建 APP 的人可能有个疑惑,Flutter 的编译产物有时候是一个共享库,有时候是几个文件,比较难于理解,本文主要介绍 Flutter 中的编译模式。
源代码需要编译才能运行,一般来讲编译模式分为 JIT 和 AOT 两大类
全称为 Just In Time(即时编译),比较典型的就是 V8 JS引擎,它能够即时的编译和运行 JavaScript 代码。你只要输入 JavaScript 的字符串源码,V8 就能编译和运行这段代码。通常来说,支持 JIT 的语言一般能够支持自省函数(eval),在运行时动态的执行源码。
所以 JIT 模式有个显然的优点,你可以直接将代码分发给用户,而不用考虑用的机器架构。有这个,就可以为用户提供丰富和动态的内容。
但缺点也是比较明显的,大量的字符串源码,将花费 JIT 编译器大量的时间和内存来编译和执行,会让用户感觉应用启动缓慢。
全称为 Ahead Of Time(事前编译), 典型的例子就是像 C/C++ 需要被编译成特殊的二进制,才可以通过进程加载和运行。
AOT 的优势就是速度快,通过事先编译好的二进制代码,加载和执行的速度都会非常快,在密集计算或者图形渲染的场景下能够获得比较好的用户体验。
但 AOT 也有一些缺点,编译源代码的时候,需要注意用户的设备架构。对于不同的构架需要生成不同的二进制代码,也就会增加应用需要下载的程序包大小。而且二进制代码需要获得执行权限,所以无法在权限比较严格的系统(比如iOS)中动态更新。
Flutter 使用 Dart 作为开发语言,自然和 Dart 的编译模式脱离不了关系,所以我们先来看一下 Dart 的编译模式。
Script: 最常见的 JIT 模式,就像 Node.js 那样,可以在命令行直接执行 Dart 源代码
Script Snapshot: 不同于 Script 模式,Script Snapshot 模式载入的是已经 token 化的 dart 源码,提前做了编译期间的 Lexer 步骤, 也是属于 JIT 模式
Application Snapshot: JIT 模式,这种模式来源于 dart vm直接载入源码后 dump 出数据。dart vm 通过这种数据启动会更快。不过这种模式是区分架构的,在 X64 下生成的无法给 IA_32 用
AOT: AOT 模式,dart 源代码会被编译成汇编文件,汇编再经过汇编器生成不同架构下的二进制代码
用表格总结一下:
Flutter 程序完全用 Dart, 理论上 Flutter 的编译模式应该和 Dart 的一致,事实上因为 Android 和 iOS 生态的差异,Flutter 衍生了不同的编译模式。
Script: 和 Dart 的 Script 模式一样,但是没有开启使用
Script Snapshot: 也是和 Dart 的 Script Snapshot 模式一样,也没有开启使用
Kernel Snapshot: Dart 的 bytecode 模式,在某种程度上类似 JVM。在 Flutter 项目中也被叫做 Core Snapshot,它是和设备架构无关的
Core JIT: 一种编译后的二进制格式,程序的数据和指令被打包成特殊的二进制,在运行时加载。事实上Core JIT 也被叫做 AOT Blobs, 是 AOT 的一种
AOT Assembly: 和 Dart 的 AOT 模式一样
所以在 Flutter 里会更复杂一点,我们结合 Flutter 的各个开发阶段来解读一下。
开发 Flutter APP 的时候,我们需要 HOT Reload 方便 UI 快速成型,同时也需要比较高的性能来进行视图渲染,所以 Flutter 在 Debug 下使用了 Kernel Snapshot 编译模式,会生成如下产物:
isolate_snapshot_data: 加速 isolate 启动的数据,和业务无关
vm_snapshot_data: 加速 Dart VM 启动的数据,和业务无关
kernel_blob.bin: 业务代码产物
在生产阶段,应用需要非常快的速度。所以 Flutter 使用了 AOT 编译模式,但是在不同的平台下,还是有些不一样的
在 iOS 平台由于 App Store 审核条例不允许动态下发可执行二进制代码,所以在 iOS 上,除了 JavaScript,其他语言都使用 AOT 编译。
但是在 Android 平台上,Core JIT 和 AOT Assembly 都支持了,在 Core JIT 模式下会生成四个产物:isolate_snapshot_data/vm_snapshot_data/isolate_snapshot_instr/vm_snapshot_instr
vm_snapshot_instr 和 isolate_snapshot_instr 是 Dart VM 和 isolate 的指令,在载入后,直接将该块内存执行即可。
isolate_snapshot_data 和 vm_snapshot_data 是 Dart VM 和 isolate 的启动数据,主要是为了加速启动速度。
Flutter Engine 包含了 Dart 的运行时,Flutter 应用的编译产物必须和 Engine 匹配才行,Engine 在不同的阶段提供了不同的支持
通过 gen_snapshot -h 也可以查看到,Engine 对编译模式的支持,事实上 Dart 源码就是经过 gen_snapshot 去处理的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
To create a core snapshot: --snapshot_kind=core --vm_snapshot_data=<output-file> --isolate_snapshot_data=<output-file> <dart-kernel-file> To create an AOT application snapshot as assembly suitable for compilation as a static or dynamic library: --snapshot_kind=app-aot-assembly --assembly=<output-file> [--obfuscate] [--save-obfuscation-map=<map-filename>] <dart-kernel-file> To create an AOT application snapshot as an ELF shared library: --snapshot_kind=app-aot-elf --elf=<output-file> [--strip] [--obfuscate] [--save-obfuscation-map=<map-filename>] <dart-kernel-file> AOT snapshots can be obfuscated: that is all identifiers will be renamed during compilation. This mode is enabled with --obfuscate flag. Mapping between original and obfuscated names can be serialized as a JSON array using --save-obfuscation-map=<filename> option. See dartbug.com/30524 for implementation details and limitations of the obfuscation pass. |
AOT snapshots 也可以使用 obfuscated 选项开启混淆, --save-obfuscation-map=<filename> 保存符号 mapping 文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ flutter doctor Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel stable, 1.22.5, on macOS 11.6.2 20G314 darwin-x64, locale zh-Hans-CN) [!] Android toolchain - develop for Android devices (Android SDK version 30.0.2) ✗ Android license status unknown. Run `flutter doctor --android-licenses` to accept the SDK licenses. See https://flutter.dev/docs/get-started/install/macos#android-setup for more details. [✓] Xcode - develop for iOS and macOS (Xcode 13.2.1) [!] Android Studio (version 2020.3) ✗ Flutter plugin not installed; this adds Flutter specific functionality. ✗ Dart plugin not installed; this adds Dart specific functionality. ✗ Unable to find bundled Java version. [!] IntelliJ IDEA Ultimate Edition (version 2018.3.6) ✗ Flutter plugin not installed; this adds Flutter specific functionality. ✗ Dart plugin not installed; this adds Dart specific functionality. [✓] VS Code (version 1.63.2) [✓] Connected device (1 available) ! Doctor found issues in 3 categories. |
在执行 flutter doctor --android-licenses 的时候报错:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ flutter doctor --android-licenses Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema at com.android.repository.api.SchemaModule$SchemaModuleVersion.<init>(SchemaModule.java:156) at com.android.repository.api.SchemaModule.<init>(SchemaModule.java:75) at com.android.sdklib.repository.AndroidSdkHandler.<clinit>(AndroidSdkHandler.java:81) at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:73) at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:48) Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) ... 5 more |
原因为 Java 版本太高,Flutter 1.22.5 只能使用 Java 1.8版本:
1 2 3 |
$ export JAVA_HOME=/Users/xxxx/Library/Java/JavaVirtualMachines/corretto-1.8.0_312/Contents/Home $ flutter doctor --android-licenses |
对于错误
1 2 3 4 |
[!] Android Studio (version 2020.3) ✗ Flutter plugin not installed; this adds Flutter specific functionality. ✗ Dart plugin not installed; this adds Dart specific functionality. ✗ Unable to find bundled Java version. |
原因:
1 2 3 |
Plugin folder has changed in 4.1 version; From : ~/Library/Application\ Support/AndroidStudio4.1 To: ~/Library/Application\ Support/Google/AndroidStudio4.1/plugins |
Android Studio(4.1)后,安装插件的位置发生了变化,但是flutter doctor
还是去原先的位置找,导致的安装过插件还报错。
解决方法:
1 |
$ ln -s ~/Library/Application\ Support/Google/AndroidStudio2020.3/plugins ~/Library/Application\ Support/AndroidStudio2020.3 |
更简单的方式是执行
1 |
$ flutter upgrade |
升级到 flutter 2.8.1 以及之后的版本即可。