checkForUpdate static method
Checks for compatible OTA updates on the active channel and branch.
Implementation
static Future<UpdateCheckResult> checkForUpdate({
String? channel,
String? branch,
}) async {
_isChecking.value = true;
_error.value = null;
final targetChannel = channel ?? _activeChannel;
final targetBranch = branch ?? _activeBranch;
try {
final manifest = await _adapter.checkServerForUpdate(
channel: targetChannel,
branch: targetBranch,
runtimeFingerprint: _localRuntimeFingerprint,
deviceId: _deviceId,
);
if (manifest == null) {
_isAvailable.value = false;
_isChecking.value = false;
return const UpdateCheckResult.upToDate();
}
// 1. Verify Cryptographic Runtime Fingerprint Compatibility
if (manifest.runtimeFingerprint.isNotEmpty &&
manifest.runtimeFingerprint.toLowerCase() != _localRuntimeFingerprint.toLowerCase()) {
final remoteShort = manifest.runtimeFingerprint.length >= 8 ? manifest.runtimeFingerprint.substring(0, 8) : manifest.runtimeFingerprint;
final localShort = _localRuntimeFingerprint.length >= 8 ? _localRuntimeFingerprint.substring(0, 8) : _localRuntimeFingerprint;
final reason = 'Incompatible native runtime fingerprint: remote requires "$remoteShort...", local binary is "$localShort..."';
logger.warn('BloomUpdates: OTA update "${manifest.id}" rejected! $reason');
_isAvailable.value = false;
_isChecking.value = false;
return UpdateCheckResult.rejected(reason: reason, manifest: manifest);
}
// 2. Evaluate Staged Percentage Rollout
if (manifest.rolloutPercentage < 100) {
final eligible = StagedRolloutEvaluator.isEligible(
deviceId: _deviceId,
updateId: manifest.id,
rolloutPercentage: manifest.rolloutPercentage,
);
if (!eligible) {
final bucket = StagedRolloutEvaluator.getDeviceBucket(_deviceId, manifest.id);
final reason = 'Device bucket ($bucket) excluded by staged rollout window (${manifest.rolloutPercentage}%)';
logger.debug('BloomUpdates: Device not in rollout partition: $reason');
_isAvailable.value = false;
_isChecking.value = false;
return UpdateCheckResult.rejected(reason: reason, manifest: manifest);
}
}
_pendingStagedManifest = manifest;
_isAvailable.value = true;
_isChecking.value = false;
return UpdateCheckResult.available(manifest);
} catch (e, st) {
logger.error('BloomUpdates: Check for update failed: $e', e, st);
_error.value = e;
_isAvailable.value = false;
_isChecking.value = false;
return UpdateCheckResult.rejected(reason: e.toString());
}
}