abstract class UpdateInfoService : 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 functions

open Unit
dump(fd: FileDescriptor?, writer: PrintWriter?, args: Array<String>?)

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 functions

abstract suspend List<UpdateInfo>

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

open Int

Retrieves the UID of the calling process.

open Unit
onClientConnected(packageName: String, callerUid: Int)

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

open Unit
onClientDisconnected(packageName: String, callerUid: Int)

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

open Unit

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

open Unit

Called when a request completes.

open Boolean

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

open Boolean

Checks if the update check operation should be throttled.

Inherited functions

From android.content.ComponentCallbacks
From android.content.ComponentCallbacks2
open Unit
onTrimMemory(level: Int)
From android.content.Context
open Boolean
bindIsolatedService(
    service: Intent,
    flags: Context.BindServiceFlags,
    instanceName: String,
    executor: Executor,
    conn: ServiceConnection
)
open Boolean
bindIsolatedService(
    service: Intent,
    flags: Int,
    instanceName: String,
    executor: Executor,
    conn: ServiceConnection
)
open Boolean
bindService(
    service: Intent,
    conn: ServiceConnection,
    flags: Context.BindServiceFlags
)
open Boolean
bindService(service: Intent, conn: ServiceConnection, flags: Int)
open Boolean
bindService(
    service: Intent,
    flags: Context.BindServiceFlags,
    executor: Executor,
    conn: ServiceConnection
)
open Boolean
bindService(
    service: Intent,
    flags: Int,
    executor: Executor,
    conn: ServiceConnection
)
open Boolean
bindServiceAsUser(
    service: Intent,
    conn: ServiceConnection,
    flags: Context.BindServiceFlags,
    user: UserHandle
)
open Boolean
bindServiceAsUser(
    service: Intent,
    conn: ServiceConnection,
    flags: Int,
    user: UserHandle
)
open Int
open Int
open IntArray
open Int
open Int
checkCallingUriPermission(uri: Uri, modeFlags: Int)
open IntArray
open Int
checkContentUriPermissionFull(uri: Uri, pid: Int, uid: Int, modeFlags: Int)
open Int
checkPermission(permission: String, pid: Int, uid: Int)
open Int
open Int
checkUriPermission(uri: Uri, pid: Int, uid: Int, modeFlags: Int)
open Int
checkUriPermission(
    uri: Uri?,
    readPermission: String?,
    writePermission: String?,
    pid: Int,
    uid: Int,
    modeFlags: Int
)
open IntArray
checkUriPermissions(
    uris: MutableList<Uri>,
    pid: Int,
    uid: Int,
    modeFlags: Int
)
open Unit

This function is deprecated. Deprecated in Java

open Context
createAttributionContext(attributionTag: String?)
open Context
createConfigurationContext(overrideConfiguration: Configuration)
open Context
createContext(contextParams: ContextParams)
open Context
open Context
open Context
open Context
open Context
createPackageContext(packageName: String, flags: Int)
open Context
createWindowContext(type: Int, options: Bundle?)
open Context
createWindowContext(display: Display, type: Int, options: Bundle?)
open Array<String>
open Boolean
open Boolean
open Boolean
open Unit
enforceCallingOrSelfPermission(permission: String, message: String?)
open Unit
enforceCallingOrSelfUriPermission(
    uri: Uri,
    modeFlags: Int,
    message: String
)
open Unit
enforceCallingPermission(permission: String, message: String?)
open Unit
enforceCallingUriPermission(uri: Uri, modeFlags: Int, message: String)
open Unit
enforcePermission(permission: String, pid: Int, uid: Int, message: String?)
open Unit
enforceUriPermission(
    uri: Uri,
    pid: Int,
    uid: Int,
    modeFlags: Int,
    message: String
)
open Unit
enforceUriPermission(
    uri: Uri?,
    readPermission: String?,
    writePermission: String?,
    pid: Int,
    uid: Int,
    modeFlags: Int,
    message: String?
)
open Array<String>
Int
ColorStateList
open File
open File
getDir(name: String, mode: Int)
Drawable?
open File?
open Array<File>
open File
open SharedPreferences
String
getString(resId: Int)
String
getString(resId: Int, vararg formatArgs: Any)
open Any
T
<T : Any> getSystemService(serviceClass: Class<T>)
open String?
getSystemServiceName(serviceClass: Class<*>)
CharSequence
getText(resId: Int)
open Unit
grantUriPermission(toPackage: String, uri: Uri, modeFlags: Int)
open Boolean
moveDatabaseFrom(sourceContext: Context, name: String)
open Boolean
moveSharedPreferencesFrom(sourceContext: Context, name: String)
TypedArray
TypedArray
TypedArray
TypedArray
obtainStyledAttributes(
    set: AttributeSet?,
    attrs: IntArray,
    defStyleAttr: Int,
    defStyleRes: Int
)
open FileInputStream
open FileOutputStream
openFileOutput(name: String, mode: Int)
open SQLiteDatabase
openOrCreateDatabase(
    name: String,
    mode: Int,
    factory: SQLiteDatabase.CursorFactory
)
open SQLiteDatabase
openOrCreateDatabase(
    name: String,
    mode: Int,
    factory: SQLiteDatabase.CursorFactory,
    errorHandler: DatabaseErrorHandler?
)
open Drawable

This function is deprecated. Deprecated in Java

open Unit
open Unit
open Intent?
open Intent?
registerReceiver(
    receiver: BroadcastReceiver?,
    filter: IntentFilter,
    flags: Int
)
open Intent?
registerReceiver(
    receiver: BroadcastReceiver?,
    filter: IntentFilter,
    broadcastPermission: String?,
    scheduler: Handler?
)
open Intent?
registerReceiver(
    receiver: BroadcastReceiver?,
    filter: IntentFilter,
    broadcastPermission: String?,
    scheduler: Handler?,
    flags: Int
)
open Unit

This function is deprecated. Deprecated in Java

open Unit

This function is deprecated. Deprecated in Java

open Unit
open Unit
open Unit
revokeUriPermission(uri: Uri, modeFlags: Int)
open Unit
revokeUriPermission(targetPackage: String, uri: Uri, modeFlags: Int)
open Unit
open Unit
sendBroadcast(intent: Intent, receiverPermission: String?)
open Unit
sendBroadcast(intent: Intent, receiverPermission: String?, options: Bundle?)
open Unit
open Unit
sendBroadcastAsUser(
    intent: Intent,
    user: UserHandle,
    receiverPermission: String?
)
open Unit
sendBroadcastWithMultiplePermissions(
    intent: Intent,
    receiverPermissions: Array<String>
)
open Unit
sendOrderedBroadcast(intent: Intent, receiverPermission: String?)
open Unit
sendOrderedBroadcast(
    intent: Intent,
    receiverPermission: String?,
    options: Bundle?
)
open Unit
sendOrderedBroadcast(
    intent: Intent,
    receiverPermission: String?,
    resultReceiver: BroadcastReceiver?,
    scheduler: Handler?,
    initialCode: Int,
    initialData: String?,
    initialExtras: Bundle?
)
open Unit
sendOrderedBroadcast(
    intent: Intent,
    receiverPermission: String?,
    options: Bundle?,
    resultReceiver: BroadcastReceiver?,
    scheduler: Handler?,
    initialCode: Int,
    initialData: String?,
    initialExtras: Bundle?
)
open Unit
sendOrderedBroadcast(
    intent: Intent,
    receiverPermission: String?,
    receiverAppOp: String?,
    resultReceiver: BroadcastReceiver?,
    scheduler: Handler?,
    initialCode: Int,
    initialData: String?,
    initialExtras: Bundle?
)
open Unit
sendOrderedBroadcastAsUser(
    intent: Intent,
    user: UserHandle,
    receiverPermission: String?,
    resultReceiver: BroadcastReceiver?,
    scheduler: Handler?,
    initialCode: Int,
    initialData: String?,
    initialExtras: Bundle?
)
open Unit

This function is deprecated. Deprecated in Java

open Unit
sendStickyBroadcast(intent: Intent, options: Bundle?)

This function is deprecated. Deprecated in Java

open Unit

This function is deprecated. Deprecated in Java

open Unit
sendStickyOrderedBroadcast(
    intent: Intent,
    resultReceiver: BroadcastReceiver?,
    scheduler: Handler?,
    initialCode: Int,
    initialData: String?,
    initialExtras: Bundle?
)

This function is deprecated. Deprecated in Java

open Unit
sendStickyOrderedBroadcastAsUser(
    intent: Intent,
    user: UserHandle,
    resultReceiver: BroadcastReceiver?,
    scheduler: Handler?,
    initialCode: Int,
    initialData: String?,
    initialExtras: Bundle?
)

This function is deprecated. Deprecated in Java

open Unit
setTheme(resid: Int)
open Unit

This function is deprecated. Deprecated in Java

open Unit

This function is deprecated. Deprecated in Java

open Unit
open Unit
startActivities(intents: Array<Intent>, options: Bundle?)
open Unit
open Unit
startActivity(intent: Intent, options: Bundle?)
open ComponentName?
open Boolean
startInstrumentation(
    className: ComponentName,
    profileFile: String?,
    arguments: Bundle?
)
open Unit
startIntentSender(
    intent: IntentSender,
    fillInIntent: Intent?,
    flagsMask: Int,
    flagsValues: Int,
    extraFlags: Int
)
open Unit
startIntentSender(
    intent: IntentSender,
    fillInIntent: Intent?,
    flagsMask: Int,
    flagsValues: Int,
    extraFlags: Int,
    options: Bundle?
)
open ComponentName?
startService(service: Intent)
open Boolean
open Unit
open Unit
open Unit
open Unit
open Unit
updateServiceGroup(conn: ServiceConnection, group: Int, importance: Int)
From android.content.ContextWrapper
open Unit
open Unit
sendOrderedBroadcast(
    intent: Intent,
    initialCode: Int,
    receiverPermission: String?,
    receiverAppOp: String?,
    resultReceiver: BroadcastReceiver?,
    scheduler: Handler?,
    initialData: String?,
    initialExtras: Bundle?,
    options: Bundle?
)
From android.app.Service
open Unit
open Unit
open Unit
onRebind(intent: Intent)
open Unit
onStart(intent: Intent, startId: Int)

This function is deprecated. Deprecated in Java

open Int
onStartCommand(intent: Intent, flags: Int, startId: Int)
open Unit
onTaskRemoved(rootIntent: Intent)
open Unit
onTimeout(startId: Int)
open Unit
onTimeout(startId: Int, fgsType: Int)
Unit
startForeground(id: Int, notification: Notification)
Unit
startForeground(
    id: Int,
    notification: Notification,
    foregroundServiceType: Int
)
Unit
stopForeground(notificationBehavior: Int)
Unit
stopForeground(removeNotification: Boolean)

This function is deprecated. Deprecated in Java

Unit
Unit
stopSelf(startId: Int)
Boolean
stopSelfResult(startId: Int)

Public constructors

UpdateInfoService

Added in 1.0.0-alpha03
UpdateInfoService()

Public functions

dump

Added in 1.0.0-alpha03
open fun dump(fd: FileDescriptor?, writer: PrintWriter?, args: Array<String>?): Unit

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
final fun onBind(intent: Intent?): IBinder?

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
final fun onUnbind(intent: Intent?): Boolean

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 functions

fetchUpdates

protected abstract suspend fun fetchUpdates(): List<UpdateInfo>

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
List<UpdateInfo>

A list of UpdateInfo objects currently available for the device.

getCallerUid

Added in 1.0.0-alpha03
protected open fun getCallerUid(): Int

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 open fun onClientConnected(packageName: String, callerUid: Int): Unit

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
packageName: String

The explicitly verified package name of the client application.

callerUid: Int

The Kernel-verified Linux UID of the client process.

onClientDisconnected

Added in 1.0.0-alpha03
protected open fun onClientDisconnected(packageName: String, callerUid: Int): Unit

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
packageName: String

The explicitly verified package name of the client application.

callerUid: Int

The Kernel-verified Linux UID of the client process.

onFetchFailed

Added in 1.0.0-alpha03
protected open fun onFetchFailed(e: Exception): Unit

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
e: Exception

The exception caught by the base class during the operation.

onRequestCompleted

Added in 1.0.0-alpha03
protected open fun onRequestCompleted(telemetry: UpdateCheckTelemetry): Unit

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
telemetry: UpdateCheckTelemetry

The UpdateCheckTelemetry containing metrics and outcomes.

shouldFetchUpdates

Added in 1.0.0-alpha03
protected open fun shouldFetchUpdates(): Boolean

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 open fun shouldThrottle(): Boolean

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).