@_nazmiforreal/flutter-ota 0.1.25 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/bin/flutter-ota-linux-x64 +0 -0
  2. package/bin/flutter-ota.js +12 -11
  3. package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +46 -13
  4. package/dart-src/packages/cli-tools/lib/src/backend.dart +6 -6
  5. package/dart-src/packages/cli-tools/lib/src/commands/build.dart +2 -0
  6. package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +7 -7
  7. package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +4 -1
  8. package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +1 -1
  9. package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +1 -1
  10. package/dart-src/packages/cli-tools/lib/src/config.dart +5 -5
  11. package/dart-src/packages/cli-tools/lib/src/operations.dart +2 -2
  12. package/dart-src/packages/cli-tools/pubspec.yaml +8 -21
  13. package/dart-src/packages/cli-tools/test/backends_test.dart +3 -3
  14. package/dart-src/packages/cli-tools/test/cli_test.dart +1 -1
  15. package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +2 -2
  16. package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +2 -2
  17. package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +4 -1
  18. package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +2 -2
  19. package/dart-src/packages/cli-tools/test/pocketbase_smoke_test.dart +1 -1
  20. package/dart-src/root/README.md +394 -0
  21. package/dart-src/root/analysis_options.yaml +21 -0
  22. package/dart-src/root/android/build.gradle +116 -0
  23. package/dart-src/root/android/consumer-rules.pro +23 -0
  24. package/dart-src/root/android/settings.gradle +1 -0
  25. package/dart-src/root/android/src/main/AndroidManifest.xml +21 -0
  26. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/ApplyResult.kt +52 -0
  27. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BlacklistStore.kt +154 -0
  28. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/BootDiagnosticStore.kt +150 -0
  29. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/CrashGuard.kt +271 -0
  30. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherApplication.kt +201 -0
  31. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherAutoInitProvider.kt +93 -0
  32. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/FlutterPatcherPlugin.kt +245 -0
  33. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/LoaderHook.kt +298 -0
  34. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatchManager.kt +1579 -0
  35. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfig.kt +173 -0
  36. package/dart-src/root/android/src/main/kotlin/com/flutter_patcher/flutter_patcher/SignatureVerifier.kt +177 -0
  37. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/CrashGuardTest.kt +61 -0
  38. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/LoaderHookTest.kt +25 -0
  39. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatchManagerTest.kt +401 -0
  40. package/dart-src/root/android/src/test/kotlin/com/flutter_patcher/flutter_patcher/PatcherConfigTest.kt +30 -0
  41. package/dart-src/root/bin/pack.dart +182 -0
  42. package/dart-src/root/lib/flutter_ota_kit.dart +1170 -0
  43. package/dart-src/root/lib/src/app_version_resolver.dart +36 -0
  44. package/dart-src/root/lib/src/aws_update_source.dart +97 -0
  45. package/dart-src/root/lib/src/blacklist.dart +54 -0
  46. package/dart-src/root/lib/src/boot_diagnostic.dart +138 -0
  47. package/dart-src/root/lib/src/cloudflare_update_source.dart +97 -0
  48. package/dart-src/root/lib/src/flutter_ota_app.dart +69 -0
  49. package/dart-src/root/lib/src/io.dart +48 -0
  50. package/dart-src/root/lib/src/io_stub.dart +19 -0
  51. package/dart-src/root/lib/src/ota_progress_overlay.dart +987 -0
  52. package/dart-src/root/lib/src/pack/pack.dart +434 -0
  53. package/dart-src/root/lib/src/patch_info.dart +12 -0
  54. package/dart-src/root/lib/src/patcher_channel.dart +160 -0
  55. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_database.dart +1 -1
  56. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_s3_client.dart +1 -1
  57. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage.dart +1 -1
  58. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_storage_profile.dart +1 -1
  59. package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/with_cloudfront_signed_url.dart +1 -1
  60. package/dart-src/root/lib/src/pkg/client/flutter_ota_kit_client.dart +1 -0
  61. package/dart-src/root/lib/src/pkg/client/src/server_update_source.dart +46 -0
  62. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/cloudflare_worker_database.dart +1 -1
  63. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_build_where.dart +1 -1
  64. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_bundle_mapper.dart +3 -3
  65. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database.dart +1 -1
  66. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_database_plugin.dart +2 -2
  67. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_s3_client.dart +1 -1
  68. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage.dart +1 -1
  69. package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_storage_profile.dart +1 -1
  70. package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/status.dart +1 -1
  71. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/flutter_ota_kit_plugin_core.dart +1 -1
  72. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_blob_database_plugin.dart +1 -1
  73. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/filter_compatible_app_versions.dart +1 -1
  74. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/get_update_info.dart +1 -1
  75. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/request_update_bundle_state.dart +1 -1
  76. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/resolve_update_info_from_bundles.dart +1 -1
  77. package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/types.dart +2 -2
  78. package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_bundle_mapper.dart +2 -2
  79. package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_database.dart +26 -15
  80. package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_storage.dart +1 -1
  81. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_bundle_mapper.dart +2 -2
  82. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_database.dart +2 -2
  83. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_get_update_info.dart +2 -2
  84. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_storage.dart +1 -1
  85. package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_types.dart +1 -1
  86. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_bundle_mapper.dart +2 -2
  87. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_database.dart +2 -2
  88. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_database.dart +1 -1
  89. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_edge_function_storage.dart +1 -1
  90. package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_storage.dart +1 -1
  91. package/dart-src/root/lib/src/pocketbase_update_source.dart +107 -0
  92. package/dart-src/root/lib/src/postgres_update_source.dart +95 -0
  93. package/dart-src/root/lib/src/shared_update_check.dart +131 -0
  94. package/dart-src/root/lib/src/supabase_update_source.dart +93 -0
  95. package/dart-src/root/pubspec.yaml +54 -0
  96. package/package.json +2 -1
  97. package/scripts/postinstall.js +17 -13
  98. package/scripts/vendor.sh +22 -7
  99. package/dart-src/packages/core/CHANGELOG.md +0 -21
  100. package/dart-src/packages/core/README.md +0 -23
  101. package/dart-src/packages/core/example/lib/main.dart +0 -69
  102. package/dart-src/packages/core/pubspec.yaml +0 -13
  103. package/dart-src/packages/core/test/rollout_test.dart +0 -135
  104. package/dart-src/packages/core/test/semver_test.dart +0 -55
  105. package/dart-src/packages/core/test/semver_vectors.json +0 -1
  106. package/dart-src/packages/core/test/types_test.dart +0 -131
  107. package/dart-src/packages/core/test/uuid_test.dart +0 -40
  108. package/dart-src/packages/core/tool/dbg.dart +0 -7
  109. package/dart-src/plugins/aws/.gitkeep +0 -0
  110. package/dart-src/plugins/aws/CHANGELOG.md +0 -23
  111. package/dart-src/plugins/aws/LICENSE +0 -21
  112. package/dart-src/plugins/aws/README.md +0 -28
  113. package/dart-src/plugins/aws/example/lib/main.dart +0 -60
  114. package/dart-src/plugins/aws/pubspec.yaml +0 -30
  115. package/dart-src/plugins/aws/test/aws_database_test.dart +0 -161
  116. package/dart-src/plugins/aws/test/aws_storage_test.dart +0 -73
  117. package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +0 -78
  118. package/dart-src/plugins/cloudflare/.gitkeep +0 -0
  119. package/dart-src/plugins/cloudflare/CHANGELOG.md +0 -23
  120. package/dart-src/plugins/cloudflare/LICENSE +0 -21
  121. package/dart-src/plugins/cloudflare/README.md +0 -25
  122. package/dart-src/plugins/cloudflare/example/lib/main.dart +0 -62
  123. package/dart-src/plugins/cloudflare/migrations/0001_hot-updater_init.sql +0 -16
  124. package/dart-src/plugins/cloudflare/migrations/0002_hot-updater_0.13.0.sql +0 -8
  125. package/dart-src/plugins/cloudflare/migrations/0003_hot-updater_0.18.0.sql +0 -38
  126. package/dart-src/plugins/cloudflare/migrations/0004_hot-updater_0.29.0.sql +0 -11
  127. package/dart-src/plugins/cloudflare/migrations/0005_hot-updater_0.31.0.sql +0 -24
  128. package/dart-src/plugins/cloudflare/pubspec.yaml +0 -28
  129. package/dart-src/plugins/cloudflare/sql/bundles.sql +0 -42
  130. package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +0 -126
  131. package/dart-src/plugins/cloudflare/test/d1_bundle_mapper_test.dart +0 -106
  132. package/dart-src/plugins/cloudflare/test/d1_database_test.dart +0 -200
  133. package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +0 -261
  134. package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +0 -78
  135. package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +0 -74
  136. package/dart-src/plugins/plugin-core/CHANGELOG.md +0 -20
  137. package/dart-src/plugins/plugin-core/LICENSE +0 -21
  138. package/dart-src/plugins/plugin-core/README.md +0 -21
  139. package/dart-src/plugins/plugin-core/example/lib/main.dart +0 -61
  140. package/dart-src/plugins/plugin-core/pubspec.yaml +0 -23
  141. package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +0 -170
  142. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_store_test.dart +0 -74
  143. package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +0 -345
  144. package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +0 -843
  145. package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +0 -298
  146. package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +0 -195
  147. package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +0 -56
  148. package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +0 -1589
  149. package/dart-src/plugins/plugin-core/test/helpers_test.dart +0 -283
  150. package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +0 -202
  151. package/dart-src/plugins/plugin-core/test/request_update_bundle_state_test.dart +0 -86
  152. package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +0 -88
  153. package/dart-src/plugins/pocketbase/CHANGELOG.md +0 -23
  154. package/dart-src/plugins/pocketbase/LICENSE +0 -21
  155. package/dart-src/plugins/pocketbase/README.md +0 -54
  156. package/dart-src/plugins/pocketbase/example/lib/main.dart +0 -62
  157. package/dart-src/plugins/pocketbase/pubspec.yaml +0 -26
  158. package/dart-src/plugins/pocketbase/test/pb_uri_test.dart +0 -12
  159. package/dart-src/plugins/postgres/.gitkeep +0 -0
  160. package/dart-src/plugins/postgres/CHANGELOG.md +0 -23
  161. package/dart-src/plugins/postgres/LICENSE +0 -21
  162. package/dart-src/plugins/postgres/README.md +0 -29
  163. package/dart-src/plugins/postgres/example/lib/main.dart +0 -67
  164. package/dart-src/plugins/postgres/pubspec.yaml +0 -26
  165. package/dart-src/plugins/postgres/sql/bundles.sql +0 -45
  166. package/dart-src/plugins/postgres/sql/get_target_app_version_list.sql +0 -21
  167. package/dart-src/plugins/postgres/sql/get_update_info.spec.ts +0 -171
  168. package/dart-src/plugins/postgres/sql/get_update_info_by_app_version.sql +0 -126
  169. package/dart-src/plugins/postgres/sql/get_update_info_by_fingerprint_hash.sql +0 -125
  170. package/dart-src/plugins/postgres/sql/hash_user_id.sql +0 -30
  171. package/dart-src/plugins/postgres/sql/is_device_eligible.sql +0 -237
  172. package/dart-src/plugins/postgres/sql/prepareSql.ts +0 -11
  173. package/dart-src/plugins/postgres/test/mock_postgres_client.dart +0 -320
  174. package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +0 -139
  175. package/dart-src/plugins/postgres/test/postgres_database_test.dart +0 -210
  176. package/dart-src/plugins/postgres/test/postgres_storage_runtime_test.dart +0 -60
  177. package/dart-src/plugins/supabase/CHANGELOG.md +0 -24
  178. package/dart-src/plugins/supabase/LICENSE +0 -21
  179. package/dart-src/plugins/supabase/README.md +0 -53
  180. package/dart-src/plugins/supabase/example/lib/main.dart +0 -63
  181. package/dart-src/plugins/supabase/pubspec.yaml +0 -27
  182. package/dart-src/plugins/supabase/supabase/edge-functions/index.ts +0 -44
  183. package/dart-src/plugins/supabase/supabase/edge-functions/runtime.docker.integration.spec.ts +0 -968
  184. package/dart-src/plugins/supabase/supabase/migrations/20250103114225_init.sql +0 -232
  185. package/dart-src/plugins/supabase/supabase/migrations/20250314000000_hot-updater_0.13.0.sql +0 -137
  186. package/dart-src/plugins/supabase/supabase/migrations/20250516000000_hot-updater_0.18.0.sql +0 -206
  187. package/dart-src/plugins/supabase/supabase/migrations/20251014000000_hot-updater_0.21.0.sql +0 -186
  188. package/dart-src/plugins/supabase/supabase/migrations/20260401000000_hot-updater_0.29.0.sql +0 -505
  189. package/dart-src/plugins/supabase/supabase/migrations/20260414000000_hot-updater_0.30.0.sql +0 -50
  190. package/dart-src/plugins/supabase/supabase/migrations/20260422000000_hot-updater_0.31.0.sql +0 -20
  191. package/dart-src/plugins/supabase/supabase/migrations/20260520014100_hot-updater_rls.sql +0 -75
  192. package/dart-src/plugins/supabase/test/mock_supabase_client.dart +0 -654
  193. package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +0 -115
  194. package/dart-src/plugins/supabase/test/supabase_database_test.dart +0 -198
  195. package/dart-src/plugins/supabase/test/supabase_storage_test.dart +0 -168
  196. /package/dart-src/{packages/core → root}/LICENSE +0 -0
  197. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/flutter_ota_kit_aws.dart +0 -0
  198. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_client.dart +0 -0
  199. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_cloudfront_signer.dart +0 -0
  200. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_config.dart +0 -0
  201. /package/dart-src/{plugins/aws/lib → root/lib/src/pkg/aws}/src/aws_ssm_client.dart +0 -0
  202. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/flutter_ota_kit_cloudflare.dart +0 -0
  203. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_client.dart +0 -0
  204. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/d1_config.dart +0 -0
  205. /package/dart-src/{plugins/cloudflare/lib → root/lib/src/pkg/cloudflare}/src/r2_config.dart +0 -0
  206. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/flutter_ota_kit_core.dart +0 -0
  207. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/app_update_info.dart +0 -0
  208. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle.dart +0 -0
  209. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_artifacts.dart +0 -0
  210. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/bundle_patch_artifact.dart +0 -0
  211. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/changed_asset.dart +0 -0
  212. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/get_bundles_args.dart +0 -0
  213. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/metadata.dart +0 -0
  214. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/patch_info.dart +0 -0
  215. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/platform.dart +0 -0
  216. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/rollout.dart +0 -0
  217. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver.dart +0 -0
  218. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_range.dart +0 -0
  219. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/semver_version.dart +0 -0
  220. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/strategy.dart +0 -0
  221. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/update_bundle_params.dart +0 -0
  222. /package/dart-src/{packages/core/lib → root/lib/src/pkg/core}/src/uuid.dart +0 -0
  223. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/asset_storage_layout.dart +0 -0
  224. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_helpers.dart +0 -0
  225. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_storage_layout.dart +0 -0
  226. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work.dart +0 -0
  227. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/bundle_unit_of_work_store.dart +0 -0
  228. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/calculate_pagination.dart +0 -0
  229. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/compression_format.dart +0 -0
  230. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/content_addressed_assets.dart +0 -0
  231. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_database_plugin.dart +0 -0
  232. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_key_builder.dart +0 -0
  233. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/create_storage_plugin.dart +0 -0
  234. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/generate_min_bundle_id.dart +0 -0
  235. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/is_object.dart +0 -0
  236. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/legacy_asset_storage_layout.dart +0 -0
  237. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/paginate_bundles.dart +0 -0
  238. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/parse_storage_uri.dart +0 -0
  239. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/query_bundles.dart +0 -0
  240. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_helpers.dart +0 -0
  241. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/storage_profile.dart +0 -0
  242. /package/dart-src/{plugins/plugin-core/lib → root/lib/src/pkg/plugin_core}/src/uuidv7.dart +0 -0
  243. /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/flutter_ota_kit_pocketbase.dart +0 -0
  244. /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_client.dart +0 -0
  245. /package/dart-src/{plugins/pocketbase/lib → root/lib/src/pkg/pocketbase}/src/pocketbase_config.dart +0 -0
  246. /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/flutter_ota_kit_postgres.dart +0 -0
  247. /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_client.dart +0 -0
  248. /package/dart-src/{plugins/postgres/lib → root/lib/src/pkg/postgres}/src/postgres_config.dart +0 -0
  249. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/edge.dart +0 -0
  250. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/flutter_ota_kit_supabase.dart +0 -0
  251. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/error_message.dart +0 -0
  252. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_adapter.dart +0 -0
  253. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_client_http.dart +0 -0
  254. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_config.dart +0 -0
  255. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/supabase_signed_url_batcher.dart +0 -0
  256. /package/dart-src/{plugins/supabase/lib → root/lib/src/pkg/supabase}/src/types.dart +0 -0
@@ -0,0 +1,201 @@
1
+ package com.flutter_patcher.flutter_patcher
2
+
3
+ import android.content.Context
4
+ import android.util.Log
5
+ import io.flutter.app.FlutterApplication
6
+
7
+ /**
8
+ * 宿主 App 需在 AndroidManifest.xml 中使用这个 Application(或在自己的 Application
9
+ * 里调 [attachPatcher])。
10
+ *
11
+ * 启动流程:
12
+ * 1. attachBaseContext
13
+ * → CrashGuard.shouldLoadPatch() 熔断检查(API 30+ 走 ApplicationExitInfo
14
+ * 精确识别上次进程是真崩溃还是被用户/系统结束;API < 30 走朴素策略:
15
+ * patch_loading=true 即视为一次崩溃)
16
+ * → PatchManager.getValidPatchPath() 磁盘补丁验签
17
+ * → LoaderHook.install() 反射替换 FlutterLoader
18
+ * → CrashGuard.markBooting() 标记「启动中」并记录 pid(API 30+ 用于 ExitInfo 查询)
19
+ * 2. Dart 首帧 → MethodChannel("flutter_ota_kit#reportBootSuccess")
20
+ * → CrashGuard.markBootSuccess() 重置计数
21
+ *
22
+ * API 30+ 上,用户主动从最近任务划掉 / OOM kill / 强停等「非崩溃退出」不计入
23
+ * crash_count,只有真崩溃(CRASH / CRASH_NATIVE / ANR / INITIALIZATION_FAILURE)
24
+ * 才会触发熔断。API < 30 没有这层分类,对长尾设备做 fail-fast 取舍。
25
+ *
26
+ * 用户如果已经有自己的 Application 基类,直接把 [attachPatcher] 搬过去即可,
27
+ * 不需要继承本类。
28
+ */
29
+ open class FlutterPatcherApplication : FlutterApplication() {
30
+
31
+ companion object {
32
+ private const val TAG = "FlutterPatcher/App"
33
+
34
+ @Volatile
35
+ private var attachAttempted = false
36
+
37
+ @Volatile
38
+ private var attachResult = false
39
+
40
+ /**
41
+ * 在 Application.attachBaseContext(base) 中、super 调用之后调用。
42
+ * 返回补丁是否成功注入。
43
+ */
44
+ @JvmStatic
45
+ @Synchronized
46
+ fun attachPatcher(context: Context): Boolean {
47
+ if (attachAttempted) {
48
+ Log.d(TAG, "attachPatcher already ran in this process, result=$attachResult")
49
+ return attachResult
50
+ }
51
+ attachAttempted = true
52
+ attachResult = doAttachPatcher(context)
53
+ return attachResult
54
+ }
55
+
56
+ private fun doAttachPatcher(context: Context): Boolean {
57
+ // 本次启动是否已往 BootDiagnosticStore 写过精准 status。
58
+ // 用于在收尾阶段判断要不要兜底成 NO_PATCH(避免覆盖更精准的 reason)。
59
+ var diagRecorded = false
60
+ val appVc = PatcherConfig.currentVersionCode(context)
61
+
62
+ return try {
63
+ // 在熔断/校验丢弃前预读 (version, downloadMd5),等会儿入黑名单要用。
64
+ // 即便文件后续被删,这里捕获的字符串仍有效。
65
+ val patchManager = PatchManager(context)
66
+ val crashedMeta = patchManager.currentMeta()
67
+
68
+ val guard = CrashGuard(context)
69
+ val canLoad = guard.shouldLoadPatch { crashCount ->
70
+ // 熔断触发 ⇒ 这个补丁连续没活到 verified,加入黑名单防止重试。
71
+ if (crashedMeta != null) {
72
+ BlacklistStore.add(
73
+ context,
74
+ crashedMeta.first,
75
+ crashedMeta.second,
76
+ BlacklistStore.REASON_BOOT_CRASH,
77
+ )
78
+ }
79
+ BootDiagnosticStore.record(
80
+ context = context,
81
+ status = BootDiagnosticStore.DROPPED_CIRCUIT_BREAKER,
82
+ patchVersion = crashedMeta?.first,
83
+ appVersionCode = appVc,
84
+ crashCount = crashCount,
85
+ message = if (crashedMeta != null)
86
+ "$crashCount boot failure(s); blacklisted (version=${crashedMeta.first})"
87
+ else
88
+ "$crashCount boot failure(s)",
89
+ )
90
+ diagRecorded = true
91
+ }
92
+ if (!canLoad) {
93
+ Log.w(TAG, "circuit breaker tripped, skip patch")
94
+ return false
95
+ }
96
+
97
+ val patch = patchManager.getValidPatch { status, version, extras ->
98
+ // md5 / signature 失败 = 强烈"补丁有问题"信号,连带入黑名单。
99
+ // meta_corrupted / version_code_mismatch 不入黑名单(前者 key 不全,后者
100
+ // 属于正常 APK 升级而非补丁本身有问题)。
101
+ val blacklistMd5 = extras["blacklistMd5"] as? String
102
+ val blacklistReason = when (status) {
103
+ BootDiagnosticStore.DROPPED_MD5_MISMATCH ->
104
+ BlacklistStore.REASON_MD5_MISMATCH
105
+ BootDiagnosticStore.DROPPED_SIGNATURE_INVALID ->
106
+ BlacklistStore.REASON_SIGNATURE_INVALID
107
+ else -> null
108
+ }
109
+ if (blacklistReason != null && version != null && !blacklistMd5.isNullOrEmpty()) {
110
+ BlacklistStore.add(context, version, blacklistMd5, blacklistReason)
111
+ }
112
+
113
+ BootDiagnosticStore.record(
114
+ context = context,
115
+ status = status,
116
+ patchVersion = version,
117
+ patchTargetVersionCode = extras["patchTargetVersionCode"] as? Long,
118
+ appVersionCode = (extras["appVersionCode"] as? Long) ?: appVc,
119
+ message = extras["message"] as? String,
120
+ )
121
+ diagRecorded = true
122
+ }
123
+ if (patch == null) {
124
+ Log.d(TAG, "no usable patch, boot with built-in libapp.so")
125
+ if (!diagRecorded) {
126
+ // DROPPED_* 是用户最关心的"上次为啥被丢弃"信息,不应被这次
127
+ // 无信息量的 NO_PATCH 覆盖。典型场景:上一次冷启动里 Dart 错误
128
+ // 钩子把补丁判崩 + record(DROPPED_CIRCUIT_BREAKER) + 删补丁,
129
+ // 但进程没死、UI 没建出来;用户划掉重开后这一次进入本分支,
130
+ // 写 NO_PATCH 会让 DiagCard 看不到 trip 信息。
131
+ // 后续装上新补丁时,line 127 会写 PATCHED 自然覆盖,sticky 自动解除。
132
+ val existing = BootDiagnosticStore.read(context)
133
+ val existingStatus = existing?.get("status") as? String
134
+ val shouldPreserve = existingStatus != null &&
135
+ existingStatus.startsWith("DROPPED_")
136
+ if (!shouldPreserve) {
137
+ BootDiagnosticStore.record(
138
+ context = context,
139
+ status = BootDiagnosticStore.NO_PATCH,
140
+ appVersionCode = appVc,
141
+ )
142
+ } else {
143
+ Log.d(TAG, "preserved existing $existingStatus over NO_PATCH")
144
+ }
145
+ }
146
+ return false
147
+ }
148
+
149
+ // 走到这里说明补丁已通过校验。预先取版本号,方便后面 record。
150
+ val patchVersion = patchManager.currentVersion().ifEmpty { null }
151
+
152
+ // 标记「启动中」——必须 commit 同步写入,确保进程崩溃前状态已持久化
153
+ guard.markBooting()
154
+
155
+ val attemptedFields = mutableListOf<String>()
156
+ val ok = LoaderHook.install(
157
+ context,
158
+ patch.soPath,
159
+ patch.assetsBundlePath,
160
+ patch.assetsArchivePath,
161
+ attemptedFields
162
+ )
163
+ if (ok) {
164
+ BootDiagnosticStore.record(
165
+ context = context,
166
+ status = BootDiagnosticStore.PATCHED,
167
+ patchVersion = patchVersion,
168
+ appVersionCode = appVc,
169
+ )
170
+ } else {
171
+ // 注入失败:本次启动根本没换 libapp.so,不视为一次崩溃
172
+ guard.reset()
173
+ BootDiagnosticStore.record(
174
+ context = context,
175
+ status = BootDiagnosticStore.HOOK_INSTALL_FAILED,
176
+ patchVersion = patchVersion,
177
+ appVersionCode = appVc,
178
+ attemptedLoaderFields = attemptedFields.toList(),
179
+ message = "FlutterLoader reflection failed; patch retained",
180
+ )
181
+ }
182
+ ok
183
+ } catch (e: Exception) {
184
+ Log.e(TAG, "attachPatcher failed, fallback to built-in", e)
185
+ CrashGuard(context).reset()
186
+ BootDiagnosticStore.record(
187
+ context = context,
188
+ status = BootDiagnosticStore.UNKNOWN,
189
+ appVersionCode = appVc,
190
+ message = e.message ?: e.javaClass.simpleName,
191
+ )
192
+ false
193
+ }
194
+ }
195
+ }
196
+
197
+ override fun attachBaseContext(base: Context) {
198
+ super.attachBaseContext(base)
199
+ attachPatcher(this)
200
+ }
201
+ }
@@ -0,0 +1,93 @@
1
+ package com.flutter_patcher.flutter_patcher
2
+
3
+ import android.content.ContentProvider
4
+ import android.content.ContentValues
5
+ import android.database.Cursor
6
+ import android.net.Uri
7
+ import android.util.Log
8
+
9
+ /**
10
+ * 自动初始化 ContentProvider —— 宿主无需改 Application 即可挂上补丁加载。
11
+ *
12
+ * # 生命周期
13
+ * Android `ActivityThread.handleBindApplication` 的固定顺序:
14
+ *
15
+ * ```
16
+ * Application.attachBaseContext()
17
+ * ↓
18
+ * installContentProviders() ← 本 provider.onCreate 在这里触发
19
+ * ↓
20
+ * Application.onCreate()
21
+ * ↓
22
+ * Activity lifecycle(FlutterActivity.onCreate → 首次 new FlutterEngine)
23
+ * ```
24
+ *
25
+ * 插件反射替换 `FlutterInjector.flutterLoader` 只要求"早于第一次
26
+ * `FlutterInjector.instance()` 真正被使用",ContentProvider 这个时机足够。
27
+ * 这是 Firebase / WorkManager / androidx.startup 都在用的自动初始化模式。
28
+ *
29
+ * # 边界:什么时候走不通
30
+ * 宿主在自己 `Application.attachBaseContext` 里 **预热 FlutterEngine**(罕见,
31
+ * 主要出现在大厂混合工程的冷启动优化)。此时 provider 还没轮到、Engine 已起来,
32
+ * 反射来不及。这类工程应在宿主 Manifest 里关掉自动初始化,改为显式调用:
33
+ *
34
+ * ```xml
35
+ * <provider
36
+ * android:name="com.flutter_patcher.flutter_patcher.FlutterPatcherAutoInitProvider"
37
+ * android:authorities="${applicationId}.flutter_patcher.autoinit"
38
+ * tools:node="remove" />
39
+ * ```
40
+ *
41
+ * 然后在自己的 Application 里:
42
+ *
43
+ * ```kotlin
44
+ * override fun attachBaseContext(base: Context) {
45
+ * super.attachBaseContext(base)
46
+ * FlutterPatcherApplication.attachPatcher(base)
47
+ * }
48
+ * ```
49
+ */
50
+ class FlutterPatcherAutoInitProvider : ContentProvider() {
51
+
52
+ companion object {
53
+ private const val TAG = "FlutterPatcher/AutoInit"
54
+ }
55
+
56
+ override fun onCreate(): Boolean {
57
+ val ctx = context ?: run {
58
+ Log.w(TAG, "context is null, skip auto init")
59
+ return false
60
+ }
61
+ Log.d(TAG, "auto init via ContentProvider.onCreate()")
62
+ FlutterPatcherApplication.attachPatcher(ctx)
63
+ return true
64
+ }
65
+
66
+ // ==================== no-op data surface ====================
67
+ // 本 provider 不对外暴露任何数据;所有 CRUD 都返回空 / 0 即可。
68
+
69
+ override fun query(
70
+ uri: Uri,
71
+ projection: Array<out String>?,
72
+ selection: String?,
73
+ selectionArgs: Array<out String>?,
74
+ sortOrder: String?
75
+ ): Cursor? = null
76
+
77
+ override fun getType(uri: Uri): String? = null
78
+
79
+ override fun insert(uri: Uri, values: ContentValues?): Uri? = null
80
+
81
+ override fun delete(
82
+ uri: Uri,
83
+ selection: String?,
84
+ selectionArgs: Array<out String>?
85
+ ): Int = 0
86
+
87
+ override fun update(
88
+ uri: Uri,
89
+ values: ContentValues?,
90
+ selection: String?,
91
+ selectionArgs: Array<out String>?
92
+ ): Int = 0
93
+ }
@@ -0,0 +1,245 @@
1
+ package com.flutter_patcher.flutter_patcher
2
+
3
+ import android.content.Context
4
+ import android.os.Build
5
+ import android.os.Handler
6
+ import android.os.Looper
7
+ import android.util.Log
8
+ import io.flutter.embedding.engine.plugins.FlutterPlugin
9
+ import io.flutter.plugin.common.EventChannel
10
+ import io.flutter.plugin.common.MethodCall
11
+ import io.flutter.plugin.common.MethodChannel
12
+ import io.flutter.plugin.common.MethodChannel.MethodCallHandler
13
+ import io.flutter.plugin.common.MethodChannel.Result
14
+
15
+ /**
16
+ * Dart ↔ Android 的 MethodChannel 处理器。
17
+ *
18
+ * Channel 名:
19
+ * - MethodChannel `flutter_ota_kit` —— RPC(saveConfig / applyPatch / ...)
20
+ * - EventChannel `flutter_ota_kit/events` —— applyPatch 过程的阶段 / 进度事件
21
+ *
22
+ * MethodChannel 方法:
23
+ * - saveConfig(publicKeyBase64, maxCrashCount, strictSignature,
24
+ * loaderFieldCandidates, loaderFallbackHeuristic)
25
+ * - markBooting() — Dart init 最开头调用,补写一次「启动中」
26
+ * - reportBootSuccess() — Dart 首帧渲染时调用,清熔断
27
+ * - reportDartBootError(Map) — 引导阶段 Dart 未捕获异常上报,等同一次真崩溃
28
+ * - applyPatch(Map) -> Map{ok, error, message}
29
+ * - rollback()
30
+ * - currentVersion() -> String?
31
+ * - lastBootDiagnostic() -> Map? — 上次冷启动补丁加载诊断(见 BootDiagnosticStore)
32
+ * - cacheDir() -> String — 插件可写的临时目录,供 Dart applyPatchBytes 内部 staging 使用
33
+ * - appVersionCode() -> Long — 当前宿主 APK versionCode,A3 便捷查询
34
+ * - deviceAbi() -> String — 当前设备首选 ABI,用于服务端按 ABI 分发补丁
35
+ * - blacklist() -> List<Map> — 已知"装上就出事"的补丁本地黑名单
36
+ * - clearBlacklist() — 清空黑名单(仅调试 / 显式恢复用)
37
+ */
38
+ class FlutterPatcherPlugin :
39
+ FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
40
+
41
+ companion object {
42
+ private const val TAG = "FlutterPatcher/Plugin"
43
+ private const val CHANNEL = "flutter_ota_kit"
44
+ private const val EVENT_CHANNEL = "flutter_ota_kit/events"
45
+ }
46
+
47
+ private lateinit var channel: MethodChannel
48
+ private lateinit var eventChannel: EventChannel
49
+ private lateinit var appContext: Context
50
+ private val main = Handler(Looper.getMainLooper())
51
+
52
+ @Volatile
53
+ private var eventSink: EventChannel.EventSink? = null
54
+
55
+ override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
56
+ appContext = binding.applicationContext
57
+ channel = MethodChannel(binding.binaryMessenger, CHANNEL)
58
+ channel.setMethodCallHandler(this)
59
+ eventChannel = EventChannel(binding.binaryMessenger, EVENT_CHANNEL)
60
+ eventChannel.setStreamHandler(this)
61
+ }
62
+
63
+ // ==================== EventChannel ====================
64
+
65
+ override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
66
+ eventSink = events
67
+ }
68
+
69
+ override fun onCancel(arguments: Any?) {
70
+ eventSink = null
71
+ }
72
+
73
+ private fun emitProgress(phase: String, received: Long, total: Long) {
74
+ val sink = eventSink ?: return
75
+ val event = mapOf(
76
+ "phase" to phase,
77
+ "received" to received,
78
+ "total" to total
79
+ )
80
+ main.post { sink.success(event) }
81
+ }
82
+
83
+ override fun onMethodCall(call: MethodCall, result: Result) {
84
+ when (call.method) {
85
+ "saveConfig" -> handleSaveConfig(call, result)
86
+ "markBooting" -> {
87
+ CrashGuard(appContext).markBooting()
88
+ result.success(null)
89
+ }
90
+ "reportBootSuccess" -> {
91
+ CrashGuard(appContext).markBootSuccess()
92
+ result.success(null)
93
+ }
94
+ "reportDartBootError" -> handleReportDartBootError(call, result)
95
+ "applyPatch" -> handleApplyPatch(call, result)
96
+ "rollback" -> handleRollback(result)
97
+ "rollbackToPrevious" -> handleRollbackToPrevious(result)
98
+ "currentVersion" -> handleCurrentVersion(result)
99
+ "lastBootDiagnostic" -> handleLastBootDiagnostic(result)
100
+ "cacheDir" -> result.success(appContext.cacheDir.absolutePath)
101
+ "appVersionCode" -> result.success(PatcherConfig.currentVersionCode(appContext))
102
+ "deviceAbi" -> result.success(Build.SUPPORTED_ABIS.firstOrNull().orEmpty())
103
+ "blacklist" -> result.success(BlacklistStore.entries(appContext))
104
+ "clearBlacklist" -> {
105
+ BlacklistStore.clear(appContext)
106
+ result.success(null)
107
+ }
108
+ "reportApplyFailure" -> handleReportApplyFailure(call, result)
109
+ "isVersionBlacklisted" -> handleIsVersionBlacklisted(call, result)
110
+ else -> result.notImplemented()
111
+ }
112
+ }
113
+
114
+ override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
115
+ channel.setMethodCallHandler(null)
116
+ eventChannel.setStreamHandler(null)
117
+ eventSink = null
118
+ }
119
+
120
+ // ==================== handlers ====================
121
+
122
+ private fun handleSaveConfig(call: MethodCall, result: Result) {
123
+ val pk = call.argument<String>("publicKeyBase64") ?: ""
124
+ val max = call.argument<Int>("maxCrashCount") ?: PatcherConfig.DEFAULT_MAX_CRASH
125
+ val strict = call.argument<Boolean>("strictSignature") ?: PatcherConfig.DEFAULT_STRICT_SIG
126
+ val fields = call.argument<List<String>>("loaderFieldCandidates") ?: emptyList()
127
+ val heuristic = call.argument<Boolean>("loaderFallbackHeuristic")
128
+ ?: PatcherConfig.DEFAULT_LOADER_HEURISTIC
129
+ val maxPatchHistory = call.argument<Int>("maxPatchHistory") ?: PatcherConfig.MAX_PATCH_HISTORY
130
+ val maxAssetHistory = call.argument<Int>("maxAssetHistory") ?: PatcherConfig.MAX_ASSET_HISTORY
131
+ PatcherConfig.saveConfig(appContext, pk, max, strict, fields, heuristic, maxPatchHistory, maxAssetHistory)
132
+ result.success(null)
133
+ }
134
+
135
+ private fun handleApplyPatch(call: MethodCall, result: Result) {
136
+ @Suppress("UNCHECKED_CAST")
137
+ val args = call.arguments as? Map<String, Any?>
138
+ if (args == null) {
139
+ result.error("INVALID_ARGS", "applyPatch expects a Map", null)
140
+ return
141
+ }
142
+ Thread {
143
+ val progress: ProgressCallback = { phase, received, total ->
144
+ emitProgress(phase, received, total)
145
+ }
146
+ val applyResult = try {
147
+ PatchManager(appContext, progress).applyPatch(args)
148
+ } catch (e: Exception) {
149
+ Log.e(TAG, "applyPatch error", e)
150
+ ApplyResult.failure(ApplyErrorCode.UNKNOWN, e.message ?: e.javaClass.simpleName)
151
+ }
152
+ main.post { result.success(applyResult.toMap()) }
153
+ }.start()
154
+ }
155
+
156
+ private fun handleRollback(result: Result) {
157
+ Thread {
158
+ try {
159
+ PatchManager(appContext).rollback()
160
+ } catch (e: Exception) {
161
+ Log.e(TAG, "rollback error", e)
162
+ }
163
+ main.post { result.success(null) }
164
+ }.start()
165
+ }
166
+
167
+ private fun handleRollbackToPrevious(result: Result) {
168
+ Thread {
169
+ try {
170
+ val outcome = PatchManager(appContext).rollbackToPrevious()
171
+ main.post { result.success(outcome.name) }
172
+ } catch (e: Exception) {
173
+ Log.e(TAG, "rollbackToPrevious error", e)
174
+ main.post { result.success(RollbackResult.FALLBACK_TO_BASE.name) }
175
+ }
176
+ }.start()
177
+ }
178
+
179
+ private fun handleCurrentVersion(result: Result) {
180
+ val v = PatchManager(appContext).currentVersion()
181
+ result.success(if (v.isEmpty()) null else v)
182
+ }
183
+
184
+ private fun handleLastBootDiagnostic(result: Result) {
185
+ // null 表示从未 record 过(首次安装 / pm clear 后第一次启动且尚未到首帧)
186
+ result.success(BootDiagnosticStore.read(appContext))
187
+ }
188
+
189
+ /**
190
+ * Dart 侧 PlatformDispatcher.onError / FlutterError.onError 在「verifyAfter 窗口内」
191
+ * 抓到的未捕获异常 ⇒ 等同 ApplicationExitInfo.REASON_CRASH 处理:crash_count += 1,
192
+ * 达阈值则 onTrip 回调里走完整的"加黑名单 + 记诊断 + 删补丁"链路。
193
+ *
194
+ * 与 attachPatcher 里 shouldLoadPatch 的 onTrip 逻辑保持一致,让用户在 DiagCard
195
+ * 上看到 status=DROPPED_CIRCUIT_BREAKER 而不是上一次的 status=PATCHED 残影。
196
+ */
197
+ private fun handleReportDartBootError(call: MethodCall, result: Result) {
198
+ val message = call.argument<String>("message")
199
+ val patchManager = PatchManager(appContext)
200
+ val crashedMeta = patchManager.currentMeta()
201
+ val appVc = PatcherConfig.currentVersionCode(appContext)
202
+
203
+ CrashGuard(appContext).reportDartBootError(message) { crashCount ->
204
+ if (crashedMeta != null) {
205
+ BlacklistStore.add(
206
+ appContext,
207
+ crashedMeta.first,
208
+ crashedMeta.second,
209
+ BlacklistStore.REASON_BOOT_CRASH,
210
+ )
211
+ }
212
+ BootDiagnosticStore.record(
213
+ context = appContext,
214
+ status = BootDiagnosticStore.DROPPED_CIRCUIT_BREAKER,
215
+ patchVersion = crashedMeta?.first,
216
+ appVersionCode = appVc,
217
+ crashCount = crashCount,
218
+ message = if (crashedMeta != null)
219
+ "$crashCount Dart boot failure(s); blacklisted (version=${crashedMeta.first}); ${message ?: ""}"
220
+ else
221
+ "$crashCount Dart boot failure(s); ${message ?: ""}",
222
+ )
223
+ }
224
+ result.success(null)
225
+ }
226
+
227
+ private fun handleReportApplyFailure(call: MethodCall, result: Result) {
228
+ val version = call.argument<String>("version") ?: ""
229
+ val md5 = call.argument<String>("md5") ?: ""
230
+ val reason = call.argument<String>("reason") ?: "APPLY_FAILED"
231
+ BlacklistStore.add(appContext, version, md5, reason)
232
+ result.success(null)
233
+ }
234
+
235
+ private fun handleIsVersionBlacklisted(call: MethodCall, result: Result) {
236
+ val version = call.argument<String>("version") ?: ""
237
+ val md5 = call.argument<String>("md5") ?: ""
238
+ val found = if (md5.isNotEmpty()) {
239
+ BlacklistStore.contains(appContext, version, md5)
240
+ } else {
241
+ BlacklistStore.containsByVersion(appContext, version)
242
+ }
243
+ result.success(found)
244
+ }
245
+ }