public abstract class UpdateInfoService extends Service


Base class for implementing the AndroidX Security State Update Provider service.

This abstract class provides the foundational implementation for the IUpdateInfoService AIDL interface. It manages the complexity of serving security update information to client applications, enforcing a consistent contract for data freshness, resource usage, and observability.

Responsibilities

  • Session Management: Implements a Factory pattern to create secure, stateful sessions (IUpdateInfoSession) for each connecting client.

  • Identity Verification: Validates client-provided package names against their Kernel-level UIDs to prevent Intent spoofing and guarantee secure attribution. To successfully perform this validation, the host application must have broad package visibility (e.g., via the QUERY_ALL_PACKAGES permission or by running as the system user).

  • Concurrency Control: Implements Double-Checked Locking to prevent "thundering herd" scenarios where multiple clients trigger parallel network requests.

  • Rate Limiting: Enforces a default throttling policy (e.g., maximum one check per hour) to protect backend infrastructure.

  • Caching: Automatically persists fetched results and freshness metadata to local storage.

Monitoring & Telemetry

To support high-scale hosts that require strict monitoring, this class provides a comprehensive set of observability hooks. Hosts can override these methods to integrate with their own logging infrastructure:

Host Implementation

Host applications (such as the System Updater, Google Play Store, or OEM-specific updaters) must extend this class and implement the fetchUpdates method to provide the actual network logic.

Manifest Declaration

To expose this service, you must declare it in your AndroidManifest.xml with the exported="true" attribute and an intent-filter for the UPDATE_INFO_SERVICE action:

<service
android:name=".MyUpdateInfoService"
android:exported="true">
<intent-filter>
<action android:name="androidx.security.state.provider.UPDATE_INFO_SERVICE" />
</intent-filter>
</service>

Summary

Public constructors

Public methods

void
dump(FileDescriptor fd, PrintWriter writer, String[] args)

Dumps the state of the service for debugging (e.g. adb shell dumpsys activity service).

final IBinder
onBind(Intent intent)

Called by the system when a client binds to the service.

final boolean
onUnbind(Intent intent)

Called by the system when all clients have disconnected from the service's factory interface.

Protected methods

abstract @NonNull List<@NonNull UpdateInfo>

Performs the actual network request to fetch fresh updates from the backend.

int

Retrieves the UID of the calling process.

void
onClientConnected(@NonNull String packageName, int callerUid)

Called when a client successfully establishes a session with the update provider.

void
onClientDisconnected(@NonNull String packageName, int callerUid)

Called when a client explicitly closes its session or its process terminates unexpectedly.

void

Callback for handling exceptions that occur during the update check process.

void

Called when a request completes.

boolean

Determines if the local cache is stale and a refresh should be attempted.

boolean

Checks if the update check operation should be throttled.

Inherited methods

From android.content.ComponentCallbacks
From android.content.ComponentCallbacks2
void
onTrimMemory(int level)
From android.content.Context
boolean
bindIsolatedService(
    @NonNull Intent service,
    @NonNull Context.BindServiceFlags flags,
    @NonNull String instanceName,
    @NonNull Executor executor,
    @NonNull ServiceConnection conn
)
boolean
bindIsolatedService(
    @NonNull Intent service,
    int flags,
    @NonNull String instanceName,
    @NonNull Executor executor,
    @NonNull ServiceConnection conn
)
boolean
bindService(
    @NonNull Intent service,
    @NonNull ServiceConnection conn,
    @NonNull Context.BindServiceFlags flags
)
boolean
bindService(
    @NonNull Intent service,
    @NonNull ServiceConnection conn,
    int flags
)
boolean
bindService(
    @NonNull Intent service,
    @NonNull Context.BindServiceFlags flags,
    @NonNull Executor executor,
    @NonNull ServiceConnection conn
)
boolean
bindService(
    @NonNull Intent service,
    int flags,
    @NonNull Executor executor,
    @NonNull ServiceConnection conn
)
boolean
bindServiceAsUser(
    @NonNull Intent service,
    @NonNull ServiceConnection conn,
    @NonNull Context.BindServiceFlags flags,
    @NonNull UserHandle user
)
boolean
bindServiceAsUser(
    @NonNull Intent service,
    @NonNull ServiceConnection conn,
    int flags,
    @NonNull UserHandle user
)
int
int
@NonNull int[]
checkCallingOrSelfUriPermissions(
    @NonNull List<@NonNull Uri> uris,
    int modeFlags
)
int
int
checkCallingUriPermission(@NonNull Uri uri, int modeFlags)
@NonNull int[]
int
checkContentUriPermissionFull(
    @NonNull Uri uri,
    int pid,
    int uid,
    int modeFlags
)
int
checkPermission(@NonNull String permission, int pid, int uid)
int
int
checkUriPermission(@NonNull Uri uri, int pid, int uid, int modeFlags)
int
checkUriPermission(
    @Nullable Uri uri,
    @Nullable String readPermission,
    @Nullable String writePermission,
    int pid,
    int uid,
    int modeFlags
)
@NonNull int[]
checkUriPermissions(
    @NonNull List<@NonNull Uri> uris,
    int pid,
    int uid,
    int modeFlags
)
void

This method is deprecated. Deprecated in Java

@NonNull Context
@NonNull Context
@NonNull Context
@NonNull Context
@NonNull Context
createDeviceContext(int deviceId)
@NonNull Context
@NonNull Context
@NonNull Context
createPackageContext(@NonNull String packageName, int flags)
@NonNull Context
createWindowContext(int type, @Nullable Bundle options)
@NonNull Context
createWindowContext(
    @NonNull Display display,
    int type,
    @Nullable Bundle options
)
@NonNull String[]
boolean
boolean
boolean
void
enforceCallingOrSelfPermission(
    @NonNull String permission,
    @Nullable String message
)
void
enforceCallingOrSelfUriPermission(
    @NonNull Uri uri,
    int modeFlags,
    @NonNull String message
)
void
enforceCallingPermission(
    @NonNull String permission,
    @Nullable String message
)
void
enforceCallingUriPermission(
    @NonNull Uri uri,
    int modeFlags,
    @NonNull String message
)
void
enforcePermission(
    @NonNull String permission,
    int pid,
    int uid,
    @Nullable String message
)
void
enforceUriPermission(
    @NonNull Uri uri,
    int pid,
    int uid,
    int modeFlags,
    @NonNull String message
)
void
enforceUriPermission(
    @Nullable Uri uri,
    @Nullable String readPermission,
    @Nullable String writePermission,
    int pid,
    int uid,
    int modeFlags,
    @Nullable String message
)
@NonNull String[]
final int
getColor(int id)
final @NonNull ColorStateList
@NonNull File
@NonNull File
getDir(@NonNull String name, int mode)
final @Nullable Drawable
getDrawable(int id)
@Nullable File
@NonNull File[]
@NonNull File
@NonNull SharedPreferences
getSharedPreferences(@NonNull String name, int mode)
final @NonNull String
getString(int resId)
final @NonNull String
getString(int resId, @NonNull Object... formatArgs)
@NonNull Object
final @NonNull T
<T extends Object> getSystemService(@NonNull Class<@NonNull T> serviceClass)
String
final @NonNull CharSequence
getText(int resId)
void
grantUriPermission(
    @NonNull String toPackage,
    @NonNull Uri uri,
    int modeFlags
)
boolean
moveDatabaseFrom(@NonNull Context sourceContext, @NonNull String name)
boolean
moveSharedPreferencesFrom(
    @NonNull Context sourceContext,
    @NonNull String name
)
final @NonNull TypedArray
final @NonNull TypedArray
obtainStyledAttributes(int resid, @NonNull int[] attrs)
final @NonNull TypedArray
final @NonNull TypedArray
obtainStyledAttributes(
    @Nullable AttributeSet set,
    @NonNull int[] attrs,
    int defStyleAttr,
    int defStyleRes
)
@NonNull FileInputStream
@NonNull FileOutputStream
openFileOutput(@NonNull String name, int mode)
@NonNull SQLiteDatabase
openOrCreateDatabase(
    @NonNull String name,
    int mode,
    @NonNull SQLiteDatabase.CursorFactory factory
)
@NonNull SQLiteDatabase
openOrCreateDatabase(
    @NonNull String name,
    int mode,
    @NonNull SQLiteDatabase.CursorFactory factory,
    @Nullable DatabaseErrorHandler errorHandler
)
@NonNull Drawable

This method is deprecated. Deprecated in Java

void
void
registerDeviceIdChangeListener(
    @NonNull Executor executor,
    @NonNull IntConsumer listener
)
Intent
registerReceiver(
    @Nullable BroadcastReceiver receiver,
    @NonNull IntentFilter filter
)
Intent
registerReceiver(
    @Nullable BroadcastReceiver receiver,
    @NonNull IntentFilter filter,
    int flags
)
Intent
registerReceiver(
    @Nullable BroadcastReceiver receiver,
    @NonNull IntentFilter filter,
    @Nullable String broadcastPermission,
    @Nullable Handler scheduler
)
Intent
registerReceiver(
    @Nullable BroadcastReceiver receiver,
    @NonNull IntentFilter filter,
    @Nullable String broadcastPermission,
    @Nullable Handler scheduler,
    int flags
)
void

This method is deprecated. Deprecated in Java

void

This method is deprecated. Deprecated in Java

void
void
void
revokeUriPermission(@NonNull Uri uri, int modeFlags)
void
revokeUriPermission(
    @NonNull String targetPackage,
    @NonNull Uri uri,
    int modeFlags
)
void
void
sendBroadcast(@NonNull Intent intent, @Nullable String receiverPermission)
void
sendBroadcast(
    @NonNull Intent intent,
    @Nullable String receiverPermission,
    @Nullable Bundle options
)
void
void
sendBroadcastAsUser(
    @NonNull Intent intent,
    @NonNull UserHandle user,
    String receiverPermission
)
void
sendBroadcastWithMultiplePermissions(
    @NonNull Intent intent,
    @NonNull String[] receiverPermissions
)
void
sendOrderedBroadcast(
    @NonNull Intent intent,
    @Nullable String receiverPermission
)
void
sendOrderedBroadcast(
    @NonNull Intent intent,
    @Nullable String receiverPermission,
    @Nullable Bundle options
)
void
sendOrderedBroadcast(
    @NonNull Intent intent,
    @Nullable String receiverPermission,
    @Nullable BroadcastReceiver resultReceiver,
    @Nullable Handler scheduler,
    int initialCode,
    @Nullable String initialData,
    @Nullable Bundle initialExtras
)
void
sendOrderedBroadcast(
    @NonNull Intent intent,
    @Nullable String receiverPermission,
    @Nullable Bundle options,
    @Nullable BroadcastReceiver resultReceiver,
    @Nullable Handler scheduler,
    int initialCode,
    @Nullable String initialData,
    @Nullable Bundle initialExtras
)
void
sendOrderedBroadcast(
    @NonNull Intent intent,
    @Nullable String receiverPermission,
    @Nullable String receiverAppOp,
    @Nullable BroadcastReceiver resultReceiver,
    @Nullable Handler scheduler,
    int initialCode,
    @Nullable String initialData,
    @Nullable Bundle initialExtras
)
void
sendOrderedBroadcastAsUser(
    @NonNull Intent intent,
    @NonNull UserHandle user,
    @Nullable String receiverPermission,
    @Nullable BroadcastReceiver resultReceiver,
    @Nullable Handler scheduler,
    int initialCode,
    @Nullable String initialData,
    @Nullable Bundle initialExtras
)
void

This method is deprecated. Deprecated in Java

void

This method is deprecated. Deprecated in Java

void

This method is deprecated. Deprecated in Java

void
sendStickyOrderedBroadcast(
    @NonNull Intent intent,
    @Nullable BroadcastReceiver resultReceiver,
    @Nullable Handler scheduler,
    int initialCode,
    @Nullable String initialData,
    @Nullable Bundle initialExtras
)

This method is deprecated. Deprecated in Java

void
sendStickyOrderedBroadcastAsUser(
    @NonNull Intent intent,
    @NonNull UserHandle user,
    @Nullable BroadcastReceiver resultReceiver,
    @Nullable Handler scheduler,
    int initialCode,
    @Nullable String initialData,
    @Nullable Bundle initialExtras
)

This method is deprecated. Deprecated in Java

void
setTheme(int resid)
void

This method is deprecated. Deprecated in Java

void

This method is deprecated. Deprecated in Java

void
startActivities(@NonNull Intent[] intents)
void
startActivities(@NonNull Intent[] intents, @Nullable Bundle options)
void
void
@Nullable ComponentName
boolean
startInstrumentation(
    @NonNull ComponentName className,
    @Nullable String profileFile,
    @Nullable Bundle arguments
)
void
startIntentSender(
    @NonNull IntentSender intent,
    @Nullable Intent fillInIntent,
    int flagsMask,
    int flagsValues,
    int extraFlags
)
void
startIntentSender(
    @NonNull IntentSender intent,
    @Nullable Intent fillInIntent,
    int flagsMask,
    int flagsValues,
    int extraFlags,
    @Nullable Bundle options
)
@Nullable ComponentName
boolean
void
void
void
void
void
updateServiceGroup(
    @NonNull ServiceConnection conn,
    int group,
    int importance
)
From android.content.ContextWrapper
void
@NonNull Context
@NonNull ApplicationInfo
@NonNull AssetManager
@NonNull AttributionSource
@Nullable String
@NonNull Context
@NonNull File
@NonNull ClassLoader
@NonNull File
@NonNull ContentResolver
@NonNull File
int
@Nullable Display
@Nullable File
@NonNull File[]
@NonNull File[]
@NonNull File
@NonNull Executor
@NonNull Looper
@NonNull File
@NonNull File
@NonNull File[]
@NonNull String
@NonNull String
@NonNull PackageManager
@NonNull String
@NonNull String
@Nullable ContextParams
@NonNull Resources
@NonNull Resources.Theme
@NonNull Drawable

This method is deprecated. Deprecated in Java

int

This method is deprecated. Deprecated in Java

int

This method is deprecated. Deprecated in Java

boolean
boolean
boolean
void
sendOrderedBroadcast(
    @NonNull Intent intent,
    int initialCode,
    @Nullable String receiverPermission,
    @Nullable String receiverAppOp,
    @Nullable BroadcastReceiver resultReceiver,
    @Nullable Handler scheduler,
    @Nullable String initialData,
    @Nullable Bundle initialExtras,
    @Nullable Bundle options
)
From android.app.Service
final @NonNull Application
final int
void
void
void
void
onStart(@NonNull Intent intent, int startId)

This method is deprecated. Deprecated in Java

int
onStartCommand(@NonNull Intent intent, int flags, int startId)
void
void
onTimeout(int startId)
void
onTimeout(int startId, int fgsType)
final void
startForeground(int id, @NonNull Notification notification)
final void
startForeground(
    int id,
    @NonNull Notification notification,
    int foregroundServiceType
)
final void
stopForeground(int notificationBehavior)
final void
stopForeground(boolean removeNotification)

This method is deprecated. Deprecated in Java

final void
final void
stopSelf(int startId)
final boolean
stopSelfResult(int startId)

Public constructors

UpdateInfoService

Added in 1.0.0-alpha03
public UpdateInfoService()

Public methods

dump

Added in 1.0.0-alpha03
public void dump(FileDescriptor fd, PrintWriter writer, String[] args)

Dumps the state of the service for debugging (e.g. adb shell dumpsys activity service).

This implementation provides a snapshot of the internal state, including:

  • Active Request Count: To detect stuck threads or high concurrency.

  • Global Last Check Time: The timestamp of the last successful service-wide sync (Service Health).

  • Throttling Status: Whether the rate limiter is currently blocking network requests.

  • Cached Updates: A complete list of stored updates with detailed metadata (Component, SPL, Provider, Published Date, Last Checked Time).

onBind

Added in 1.0.0-alpha03
public final IBinder onBind(Intent intent)

Called by the system when a client binds to the service.

This method verifies that the Intent action matches the expected contract (ACTION_BIND). If the action is missing or incorrect, the binding is rejected to ensure the service is not exposed unintentionally.

Upon successful verification, this method returns a factory Binder (IUpdateInfoService) that allows the client to establish a secure, authenticated session.

Parameters
Intent intent

The Intent that was used to bind to this service.

Returns
IBinder

The IUpdateInfoService factory interface, or null if the Intent action is invalid.

onUnbind

Added in 1.0.0-alpha03
public final boolean onUnbind(Intent intent)

Called by the system when all clients have disconnected from the service's factory interface.

This method is marked final to enforce the library's lifecycle contract. It returns false, which forces the Android system to call onBind for any future connections. This simplifies the service's lifecycle management by ensuring we never need to handle the android.app.Service.onRebind edge case.

Lifecycle Note: Individual client session disconnections and telemetry are tracked when the client calls IUpdateInfoSession.close (which triggers onClientDisconnected), not within this system-level callback.

Parameters
Intent intent

The Intent that was used to bind to this service.

Returns
boolean

false to ensure onRebind is not called.

Protected methods

fetchUpdates

protected abstract @NonNull List<@NonNull UpdateInfofetchUpdates()

Performs the actual network request to fetch fresh updates from the backend.

Template Method: This method is implemented by the host application (e.g., the System Updater) and invoked by the UpdateInfoService base class on a background thread. The base class handles concurrency, caching, and rate-limiting.

Preconditions: This method is only called if shouldFetchUpdates returns true and shouldThrottle returns false (the rate limiter allows the request).

Error Handling: If this method throws an exception (e.g., due to a network timeout or server error), the base class will catch it, invoke onFetchFailed for telemetry logging, and gracefully return the currently cached data to the client.

Returns
@NonNull List<@NonNull UpdateInfo>

A list of UpdateInfo objects currently available for the device.

getCallerUid

Added in 1.0.0-alpha03
protected int getCallerUid()

Retrieves the UID of the calling process.

The default implementation delegates to android.os.Binder.getCallingUid. Host implementations using a proxy or broker architecture should override this to return the logical client UID instead of the broker's UID, ensuring correct attribution.

onClientConnected

Added in 1.0.0-alpha03
protected void onClientConnected(@NonNull String packageName, int callerUid)

Called when a client successfully establishes a session with the update provider.

This method is invoked when a client calls IUpdateInfoService.openSession and passes the identity validation checks.

Usage: Override this method to log connection metrics (e.g., "Session Started") or track adoption trends based on the client package name.

Client Identity & Security: The packageName provided to this hook is strictly validated against the Kernel-verified callerUid by the Android system. This guarantees that the identity is authentic and cannot be spoofed.

Package Visibility: The host application must have package visibility (e.g., via the QUERY_ALL_PACKAGES permission) to verify the client's package name. If the host cannot verify the package name, the connection will be rejected with a SecurityException before this hook is ever called.

Parameters
@NonNull String packageName

The explicitly verified package name of the client application.

int callerUid

The Kernel-verified Linux UID of the client process.

onClientDisconnected

Added in 1.0.0-alpha03
protected void onClientDisconnected(@NonNull String packageName, int callerUid)

Called when a client explicitly closes its session or its process terminates unexpectedly.

This method is invoked when the client calls IUpdateInfoSession.close or when the Android system detects that the process hosting the client has died (via android.os.IBinder.DeathRecipient).

Usage: Override this method to log session ends (e.g., "Session Ended") to calculate session duration when paired with onClientConnected. You can also use this to perform per-client resource cleanup.

Parameters
@NonNull String packageName

The explicitly verified package name of the client application.

int callerUid

The Kernel-verified Linux UID of the client process.

onFetchFailed

Added in 1.0.0-alpha03
protected void onFetchFailed(@NonNull Exception e)

Callback for handling exceptions that occur during the update check process.

Template Method: The UpdateInfoService base class executes the update check logic and catches all unhandled exceptions to prevent the service from crashing. When an exception is caught, the base class gracefully falls back to returning the currently cached data to the client, and invokes this method.

This method will be triggered if an exception occurs during any step of the lifecycle, including:

  • Determining cache freshness (e.g., inside shouldFetchUpdates).

  • Evaluating rate limits (e.g., inside shouldThrottle).

  • Fetching data from the backend (e.g., inside fetchUpdates).

  • Persisting the fetched results or metadata to local storage.

The default implementation logs the error to Logcat. Hosts can override this method to report failures to their own telemetry or crash reporting systems.

Parameters
@NonNull Exception e

The exception caught by the base class during the operation.

onRequestCompleted

Added in 1.0.0-alpha03
protected void onRequestCompleted(@NonNull UpdateCheckTelemetry telemetry)

Called when a request completes. Override this to log telemetry.

This hook is called for every request to listAvailableUpdates, regardless of whether it succeeded, failed, was throttled, or was served from the cache.

Usage: Use this hook to log granular performance metrics (latency histograms) and resource usage.

Attribution: The telemetry object contains the UpdateCheckTelemetry.callerUid, which is the Kernel-verified UID of the calling process. This is the authoritative source for attributing load (CPU/Network/Lock Contention) to specific client applications.

Parameters
@NonNull UpdateCheckTelemetry telemetry

The UpdateCheckTelemetry containing metrics and outcomes.

shouldFetchUpdates

Added in 1.0.0-alpha03
protected boolean shouldFetchUpdates()

Determines if the local cache is stale and a refresh should be attempted.

The default implementation returns true if the data is older than 1 hour. Hosts can override this to implement custom caching policies (e.g., 4 hours, 24 hours).

Returns
boolean

true if a network fetch should be attempted.

shouldThrottle

Added in 1.0.0-alpha03
protected boolean shouldThrottle()

Checks if the update check operation should be throttled.

Default Behavior: The default implementation delegates to an internal rate limiter backed by SharedPreferences. This enforces a standard minimum interval (e.g., 1 hour) between network requests to protect backend infrastructure. The rate limiting state persists across application restarts.

Host Customization: Hosts can override this method to inject custom rate-limiting logic, such as blocking background checks when the device is on a metered network or has low battery.

Graceful Degradation: If this method returns true, the base class skips calling fetchUpdates and gracefully returns the currently cached data to the client. Because the client receives the cached state along with its original lastCheckTimeMillis, the client is not expected to manage complex backoff or retry loops.

Returns
boolean

true if the network request should be blocked (throttled).