Skip to main content

GeneralUpdate.Maui.Android

Namespace: GeneralUpdate.Maui.Android | Main Entry: GeneralUpdateBootstrap.CreateDefault() / AddGeneralUpdateMauiAndroid() | NuGet Package: GeneralUpdate.Maui.Android

1. Component Overview

1.1 Overview

GeneralUpdate.Maui.Android is an Android auto-update component designed for .NET MAUI applications. It provides version discovery, resumable APK download, SHA256 integrity verification, and system Package Installer trigger in a unified update orchestration API.

Unlike desktop file-replacement updates, Android APK updates must go through the system Package Installer. This component encapsulates this platform difference and provides a two-phase API: ValidateAsync for version checking and ExecuteUpdateAsync for the complete download→verify→install workflow.

Core Capabilities:

CapabilityDescription
Version CheckBuilt-in System.Version parsing, auto-detects if target version is newer
Resumable APK DownloadHttpRangeDownloader with HTTP Range headers and progress reporting
SHA256 VerificationAuto file hash verification, corrupted files auto-deleted on failure
System Installer TriggerAndroidApkInstaller launches system Package Installer via FileProvider + Intent
DI IntegrationAddGeneralUpdateMauiAndroid() extension registers all services in IServiceCollection
HTTP Transport ConfigHttpDownloadOptions with SSL policies, proxy, timeout, auth, exponential backoff
Multi-Protocol AuthHMAC-SHA256, Bearer Token, API Key, HTTP Basic
Event NotificationsVersion discovery, download progress, completion stages, failure reasons
Concurrency SafetyInterlocked guard prevents concurrent ExecuteUpdateAsync calls
Temp File Management.downloading temp extension, atomic rename on completion

Solved Business Pain Points:

  • MAUI Android apps need built-in auto-update capability outside Google Play
  • Unstable networks during large APK downloads require resume support
  • Downloaded APKs need integrity verification for security
  • Deep DI container integration simplifies adoption in MAUI apps
MAUI Update ≠ Desktop File Replacement

On Android, APKs must be installed via the Android Package Installer. The system handles signature verification and app replacement. This component handles the platform difference, but APK signing and version management must still be handled in your build pipeline.

Use Cases:

  • MAUI Android app auto-update (enterprise distribution, beta testing, side-loading)
  • CDN / OSS distributed APK packages
  • DI-managed MAUI applications

1.2 Environment & Dependencies

ItemDescription
NuGet PackageGeneralUpdate.Maui.Android
Target Frameworknet10.0 + net10.0-android (multi-target; Android min API 21)
DependenciesMicrosoft.Extensions.DependencyInjection.Abstractions, Microsoft.Extensions.Http, Xamarin.AndroidX.Core
Platform SupportAndroid 5.0 (API 21) and above

2. Feature List

FeatureDescriptionTypeRequiredNotes
Version CheckCompare current vs target versionCoreYesValidateAsync, based on System.Version
Update ExecutionDownload→Verify→Install workflowCoreYesExecuteUpdateAsync, concurrency safe
APK Resume DownloadHTTP Range resume + progressCoreAutoHttpRangeDownloader
SHA256 VerificationAuto-verify, delete on mismatchCoreAutoVia IHashValidator
APK Install TriggerFileProvider + Intent to system installerCoreAutoAndroidApkInstaller, auto permission check
DI IntegrationOne-call service registrationExtensionRecommendedAddGeneralUpdateMauiAndroid()
HTTP Transport ConfigSSL, proxy, timeout, retry, authExtensionOptionalHttpDownloadOptions
Multi-Protocol AuthHMAC/Bearer/ApiKey/BasicExtensionOptionalGlobal or per-package
Progress NotificationBytes, speed, percentage, remainingCoreOptionalIProgress<DownloadStatistics>
Completion NotificationDownload, verification, install, workflowCoreOptionalAddListenerUpdateCompleted
Failure NotificationCategorized failure reasonCoreOptionalAddListenerUpdateFailed
Validation NotificationUpdate found eventCoreOptionalAddListenerValidate
State QueryReal-time state queryCoreOptionalCurrentState property
Concurrency GuardPrevent duplicate executionCoreAutoInterlocked atomic operation
Corrupt File Auto-CleanupAuto-delete on verification failureExtensionOptionalUpdateOptions.DeleteCorruptedPackageOnFailure
Progress Interval ControlCustom progress callback frequencyExtensionOptionalUpdateOptions.ProgressReportInterval
Custom DownloaderCustom download implementationExtensionOptionalImplement IUpdateDownloader
Custom Hash ValidatorCustom hash implementationExtensionOptionalImplement IHashValidator
Custom InstallerCustom install logicExtensionOptionalImplement IApkInstaller
Custom Storage ProviderCustom file path/storageExtensionOptionalImplement IUpdateStorageProvider
Custom LoggerCustom loggingExtensionOptionalImplement IUpdateLogger
Custom SSL PolicyCustom certificate validationExtensionOptionalImplement ISslValidationPolicy
Custom HTTP AuthCustom auth providerExtensionOptionalImplement IHttpAuthProvider

3. API Configuration

3.1 Properties

UpdateOptions:

FieldTypeDefaultRequiredDescription
CurrentVersionstring""YesCurrent app version
DownloadDirectorystring?nullOptionalDownload directory, auto-selected if null
TemporaryFileExtensionstring".downloading"OptionalTemp file extension
DeleteCorruptedPackageOnFailurebooltrueOptionalAuto-delete corrupted packages
ProgressReportIntervalTimeSpan500msOptionalProgress report interval
InstallOptionsAndroidInstallOptionsnew()OptionalInstall options

AndroidInstallOptions:

FieldTypeDefaultRequiredDescription
FileProviderAuthoritystring""YesFileProvider authority matching AndroidManifest
MimeTypestring"application/vnd.android.package-archive"OptionalAPK MIME type

HttpDownloadOptions:

FieldTypeDefaultRequiredDescription
SslValidationPolicyISslValidationPolicy?nullOptionalCustom SSL validation
DownloadTimeoutTimeSpan10minOptionalOverall download timeout
ProxyIWebProxy?nullOptionalHTTP proxy
UseProxyboolfalseOptionalWhether to use proxy
AuthProviderIHttpAuthProvider?nullOptionalGlobal auth provider

UpdatePackageInfo:

FieldTypeDefaultRequiredDescription
Versionstring""YesTarget version
DownloadUrlstring""YesAPK download URL
Sha256string""YesSHA256 hash
PackageSizelong?nullOptionalPackage size (bytes)
ApkFileNamestring?nullOptionalAPK file name
ForceUpdateboolfalseOptionalForce update
VersionNamestring?nullOptionalDisplay name
ReleaseNotesstring?nullOptionalRelease notes
PublishTimeDateTimeOffset?nullOptionalPublish time
AuthSchemeAuthScheme?nullOptionalAuth scheme
AuthTokenstring?nullOptionalAuth token
AuthSecretKeystring?nullOptionalHMAC secret key
BasicUsernamestring?nullOptionalBasic username
BasicPasswordstring?nullOptionalBasic password

UpdateState Enum:

ValueDescription
NoneIdle
CheckingChecking for updates
UpdateAvailableUpdate found
DownloadingDownloading
VerifyingVerifying integrity
ReadyToInstallReady to install
InstallingInstalling
CompletedCompleted
FailedFailed
CanceledCanceled

UpdateFailureReason Enum:

ValueDescription
UnknownUnknown error
InvalidInputInvalid input parameters
AlreadyInProgressUpdate already in progress
NetworkNetwork error
DownloadDownload error
FileAccessFile access error
IntegrityCheckFailedSHA256 verification failed
InstallPermissionDeniedInstall permission denied
InstallationInstallation error
CanceledUser canceled
NoUpdateAvailableNo update available

UpdateCompletionStage Enum:

ValueDescription
DownloadCompletedDownload complete
VerificationCompletedVerification complete
InstallationTriggeredInstaller launched
WorkflowCompletedWorkflow complete

3.2 Instance Methods

GeneralUpdateBootstrap (Static Factory):

MethodParametersUse CaseNotes
CreateDefault(HttpClient?, IUpdateLogger?, HttpDownloadOptions?)httpClient — optional external HttpClient; logger — optional logger; httpOptions — optional HTTP configCreate default bootstrapWhen httpOptions is set, httpClient is ignored
AddGeneralUpdateMauiAndroid(IServiceCollection, HttpClient?)services — DI container; httpClient — optional HttpClientRegister all update services in DIReturns IServiceCollection for chaining

IAndroidBootstrap:

MethodParametersUse CaseNotes
ValidateAsync(UpdatePackageInfo, UpdateOptions, CancellationToken)packageInfo, options, ctCheck for updatesReturns UpdateCheckResult with IsUpdateAvailable
ExecuteUpdateAsync(UpdatePackageInfo, UpdateOptions, CancellationToken)packageInfo, options, ctExecute full update workflowConcurrency guard prevents duplicate calls
Dispose()NoneRelease resources
CurrentStateProperty (UpdateState)Query current stateThread-safe (Volatile.Read)

3.3 Events

EventArgsTriggeredUsage
AddListenerValidateValidateEventArgsPackageInfoWhen update is foundUI display of new version info
AddListenerDownloadProgressChangedDownloadProgressChangedEventArgsPackageInfo, Statistics, StatusDescriptionOn download progressProgress bar update
AddListenerUpdateCompletedUpdateCompletedEventArgsPackageInfo, Stage, PackagePathOn stage completionFour stages: download, verification, install, workflow
AddListenerUpdateFailedUpdateFailedEventArgsReason, Message, Exception, PackageInfoOn any failureStructured failure reason

4. Advanced Examples

4.1 Extensibility Overview

All services can be replaced via DI after AddGeneralUpdateMauiAndroid():

InterfaceDefault ImplementationDescription
IUpdateDownloaderHttpRangeDownloaderResumable APK download
IHashValidatorSha256ValidatorSHA256 hash verification
IApkInstallerAndroidApkInstallerAPK install trigger
IUpdateStorageProviderUpdateFileStoreFile path and storage operations
IUpdateLoggerNullUpdateLoggerLogger
IUpdateBootstrapAndroidBootstrapUpdate orchestration
ISslValidationPolicyVia HttpDownloadOptionsSSL validation
IHttpAuthProviderVia HttpDownloadOptions or per-packageHTTP auth provider

4.2 Scenario Examples

Scenario 1: DI Container Integration

using GeneralUpdate.Maui.Android.Abstractions;
using GeneralUpdate.Maui.Android.Models;
using GeneralUpdate.Maui.Android.Services;

public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts => { });

builder.Services.AddGeneralUpdateMauiAndroid();
builder.Services.AddSingleton<MainViewModel>();
builder.Services.AddSingleton<MainPage>();

return builder.Build();
}
}

public class MainViewModel
{
private readonly IAndroidBootstrap _bootstrap;

public MainViewModel(IAndroidBootstrap bootstrap)
{
_bootstrap = bootstrap;
}

public async Task CheckAndUpdateAsync()
{
var package = new UpdatePackageInfo
{
Version = "2.0.0",
DownloadUrl = "https://example.com/app-v2.apk",
Sha256 = "expected-sha256"
};

var options = new UpdateOptions
{
CurrentVersion = "1.0.0",
InstallOptions = new AndroidInstallOptions
{
FileProviderAuthority = "com.myapp.fileprovider"
}
};

var result = await _bootstrap.ExecuteUpdateAsync(package, options, CancellationToken.None);
Console.WriteLine(result.IsSuccess ? "Update succeeded!" : $"Failed: {result.Message}");
}
}

Scenario 2: Custom HTTP Authentication

using GeneralUpdate.Maui.Android.Models;
using GeneralUpdate.Maui.Android.Services;

// Global auth provider
var httpOptions = new HttpDownloadOptions
{
AuthProvider = new BearerTokenAuthProvider("your-token-here")
};

var bootstrap = GeneralUpdateBootstrap.CreateDefault(
httpClient: null, logger: null, httpOptions: httpOptions);

// Per-package auth
var package = new UpdatePackageInfo
{
Version = "2.0.0",
DownloadUrl = "https://example.com/app-v2.apk",
Sha256 = "expected-sha256",
AuthScheme = Enums.AuthScheme.Bearer,
AuthToken = "your-token-here"
};

Scenario 3: Non-DI Direct Usage

using GeneralUpdate.Maui.Android;
using GeneralUpdate.Maui.Android.Models;

var bootstrap = GeneralUpdateBootstrap.CreateDefault();

var options = new UpdateOptions
{
CurrentVersion = "1.0.0",
InstallOptions = new AndroidInstallOptions
{
FileProviderAuthority = "com.myapp.fileprovider"
}
};

var package = new UpdatePackageInfo
{
Version = "2.0.0",
DownloadUrl = "https://example.com/app-v2.apk",
Sha256 = "expected-sha256"
};

var result = await bootstrap.ExecuteUpdateAsync(package, options, CancellationToken.None);

5. Basic Usage Examples

5.1 Quick Start (Minimal Demo)

using GeneralUpdate.Maui.Android;
using GeneralUpdate.Maui.Android.Models;

var bootstrap = GeneralUpdateBootstrap.CreateDefault();

var package = new UpdatePackageInfo
{
Version = "2.0.0",
DownloadUrl = "https://update.example.com/app-v2.0.0.apk",
Sha256 = "a1b2c3d4e5f6..."
};

var options = new UpdateOptions
{
CurrentVersion = "1.0.0",
InstallOptions = new AndroidInstallOptions
{
FileProviderAuthority = "com.myapp.fileprovider"
}
};

var checkResult = await bootstrap.ValidateAsync(package, options, CancellationToken.None);
if (!checkResult.IsUpdateAvailable)
{
Console.WriteLine("Already up to date.");
return;
}

var result = await bootstrap.ExecuteUpdateAsync(package, options, CancellationToken.None);
Console.WriteLine(result.IsSuccess ? "Update completed." : $"Update failed: {result.Message}");

5.2 Parameter Combination Example

using GeneralUpdate.Maui.Android;
using GeneralUpdate.Maui.Android.Enums;
using GeneralUpdate.Maui.Android.Models;

var httpOptions = new HttpDownloadOptions
{
DownloadTimeout = TimeSpan.FromMinutes(30),
AuthProvider = new GeneralUpdate.Maui.Android.Services.BearerTokenAuthProvider("token")
};

var bootstrap = GeneralUpdateBootstrap.CreateDefault(
httpClient: null, logger: null, httpOptions: httpOptions);

bootstrap.AddListenerDownloadProgressChanged += (_, e) =>
{
var s = e.Statistics;
Console.WriteLine($"Progress: {s.ProgressPercentage:F1}%");
};

var options = new UpdateOptions
{
CurrentVersion = "1.0.0",
TemporaryFileExtension = ".downloading",
DeleteCorruptedPackageOnFailure = true,
ProgressReportInterval = TimeSpan.FromMilliseconds(250),
InstallOptions = new AndroidInstallOptions
{
FileProviderAuthority = "com.myapp.fileprovider"
}
};

var package = new UpdatePackageInfo
{
Version = "2.0.0",
DownloadUrl = "https://cdn.example.com/app-v2.0.0.apk",
Sha256 = "expected-sha256",
PackageSize = 50_000_000,
ApkFileName = "app-v2.0.0.apk"
};

var result = await bootstrap.ExecuteUpdateAsync(package, options, CancellationToken.None);

5.3 Production Example

using GeneralUpdate.Maui.Android;
using GeneralUpdate.Maui.Android.Abstractions;
using GeneralUpdate.Maui.Android.Enums;
using GeneralUpdate.Maui.Android.Models;
using System.Net.Http.Json;

public sealed class MauiUpdateService : IDisposable
{
private readonly HttpClient _httpClient;
private IAndroidBootstrap? _bootstrap;
private readonly string _fileProviderAuthority;
private bool _disposed;

public MauiUpdateService(HttpClient httpClient, string fileProviderAuthority)
{
_httpClient = httpClient;
_fileProviderAuthority = fileProviderAuthority;
}

public void Initialize(HttpDownloadOptions? httpOptions = null)
{
_bootstrap = GeneralUpdateBootstrap.CreateDefault(
httpClient: null, logger: null, httpOptions: httpOptions);
WireEvents();
}

public async Task<bool> CheckAndUpdateAsync(
string serverUrl, string currentVersion, CancellationToken ct = default)
{
if (_bootstrap == null)
throw new InvalidOperationException("Call Initialize() first.");

try
{
var package = await FetchPackageFromServerAsync(serverUrl, currentVersion, ct);
if (package == null) return true; // No update

var options = new UpdateOptions
{
CurrentVersion = currentVersion,
InstallOptions = new AndroidInstallOptions
{
FileProviderAuthority = _fileProviderAuthority
}
};

var checkResult = await _bootstrap.ValidateAsync(package, options, ct);
if (!checkResult.IsUpdateAvailable) return true;

var execResult = await _bootstrap.ExecuteUpdateAsync(package, options, ct);
return execResult.IsSuccess;
}
catch (OperationCanceledException) { return false; }
catch (HttpRequestException ex) { OnStatusChanged?.Invoke(this, $"Network: {ex.Message}"); return false; }
}

private Task<UpdatePackageInfo?> FetchPackageFromServerAsync(
string serverUrl, string currentVersion, CancellationToken ct)
{
// Implementation: query server API for package metadata
throw new NotImplementedException();
}

private void WireEvents() { /* wire bootstrap events */ }

public event EventHandler<string>? OnStatusChanged;

public void Dispose()
{
if (_disposed) return;
_bootstrap?.Dispose();
_disposed = true;
}
}

6. Global Configuration

DI Registration

// Default HttpClient
services.AddGeneralUpdateMauiAndroid();

// External HttpClient (shared pool)
services.AddGeneralUpdateMauiAndroid(myHttpClient);

// IHttpClientFactory
services.AddHttpClient<IUpdateDownloader, HttpRangeDownloader>(client =>
{
client.Timeout = TimeSpan.FromMinutes(10);
});
services.AddSingleton<IAndroidBootstrap, AndroidBootstrap>();

Authentication

SchemeAuthSchemeRequired Fields
HMAC-SHA256HmacAuthSecretKey
Bearer TokenBearerAuthToken
API KeyApiKeyAuthToken
HTTP BasicBasicBasicUsername + BasicPassword

SSL Policy

PolicyClassUse Case
System DefaultnullProduction
Allow All (dev only)AllowAllSslValidationPolicyDevelopment
CustomImplement ISslValidationPolicyPrivate CA

AndroidManifest

<application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>

Platforms/Android/Resources/xml/file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="update/" />
</paths>

Platform Notes

ItemDetails
Min API21 (Android 5.0)
Install PermissionAndroid 8.0+ requires CanRequestPackageInstalls() check
FileProviderRequired to pass APK URI to system installer