Chapter 62: Device Form Factors¶
Android is not a single-device operating system. The same platform that powers phones also drives car dashboards, living-room televisions, wrist-worn wearables, and -- increasingly -- head-worn XR devices. Each form factor imposes radically different constraints -- an instrument cluster must never crash, a TV must respond to a D-pad, a watch must survive on a tiny battery for days -- yet all of them share the core Android framework. This chapter dissects how AOSP adapts itself to its most divergent device form factors: Android Automotive OS (AAOS), Android TV, Wear OS, and the emerging Android XR surface. It then turns to the Software Defined Vehicle (SDV) platform that arrives in Android 17 -- a separate, vehicle-spanning stack that runs a headless "Core" VM of vehicle service bundles beside AAOS, with its own middleware, SOME/IP transport, and gateway into the AAOS image. We will trace each vertical from the HAL layer through system services, window management, specialized UI shells, and the SDV service fabric, quoting real source code and referencing actual file paths throughout.
62.1 Automotive (AAOS)¶
Android Automotive OS is the most architecturally ambitious form-factor adaptation in AOSP. Unlike Android Auto (a phone-projection protocol), AAOS runs as the primary head-unit operating system. It controls HVAC, reads vehicle telemetry, manages multiple displays for driver and passengers, handles power states tied to ignition, and enforces driver-distraction restrictions. The core automotive code lives under:
packages/services/Car/ -- CarService and all car-specific system services
packages/apps/Car/ -- Car launcher, SystemUI, Settings, Cluster
hardware/interfaces/automotive/ -- Vehicle HAL, EVS, audio control, CAN
62.1.1 CarService: The Central Automotive Daemon¶
CarService is a persistent Android service that bootstraps all car-specific subsystems. Its
lifecycle begins in CarServiceImpl, which is the actual Service subclass:
// packages/services/Car/service/src/com/android/car/CarServiceImpl.java
@Keep
public class CarServiceImpl extends ProxiedService {
public static final String CAR_SERVICE_INIT_TIMING_TAG = "CAR.InitTiming";
public static final int CAR_SERVICE_INIT_TIMING_MIN_DURATION_MS = 5;
private ICarImpl mICarImpl;
private VehicleStub mVehicle;
@Override
public void onCreate() {
// ...
mVehicle = VehicleStub.newVehicleStub();
mICarImpl = new ICarImpl.Builder()
.setServiceContext(this)
.setBuiltInContext(getBuiltinPackageContext())
.setVehicle(mVehicle)
.setSystemInterface(
SystemInterface.Builder.defaultSystemInterface(this).build())
.setVehicleInterfaceName(mVehicleInterfaceName)
.build();
mICarImpl.init();
ServiceManagerHelper.addService("car_service", mICarImpl);
SystemPropertiesHelper.set("boot.car_service_created", "1");
}
}
The key pattern: CarServiceImpl creates a VehicleStub (the connection to the Vehicle HAL),
then constructs ICarImpl, which is the actual ICar.Stub binder service that clients connect
to. The car_service name is published to ServiceManager, making it globally accessible.
ICarImpl is where every individual car subsystem is instantiated. The constructor uses a
CarServiceCreator helper that manages dependency injection and tracing:
// packages/services/Car/service/src/com/android/car/ICarImpl.java
public class ICarImpl extends ICar.Stub {
private final VehicleHal mHal;
private final CarPowerManagementService mCarPowerManagementService;
private final CarOccupantZoneService mCarOccupantZoneService;
private final FixedActivityService mFixedActivityService;
private final CarAudioService mCarAudioService;
private final CarPropertyService mCarPropertyService;
private final CarDrivingStateService mCarDrivingStateService;
private final CarUxRestrictionsManagerService mCarUXRestrictionsService;
private final InstrumentClusterService mInstrumentClusterService;
private final ClusterHomeService mClusterHomeService;
private final CarEvsService mCarEvsService;
private final GarageModeService mGarageModeService;
private final CarBluetoothService mCarBluetoothService;
private final CarUserService mCarUserService;
private final CarWatchdogService mCarWatchdogService;
private final CarTelemetryService mCarTelemetryService;
private final CarActivityService mCarActivityService;
private final CarOccupantConnectionService mCarOccupantConnectionService;
private final CarRemoteDeviceService mCarRemoteDeviceService;
private final CarRemoteAccessService mCarRemoteAccessService;
// ... approximately 40 services total
}
The initialization ordering is critical. The HAL must be up before property services, which must
be up before driving-state services, which must be up before UX-restrictions. The
CarServiceCreator.createService() method ensures each service is added to a global list in
construction order:
// packages/services/Car/service/src/com/android/car/ICarImpl.java
private ICarImpl(Builder builder) {
// ...
mHal = constructWithTrace(t, VehicleHal.class,
() -> new VehicleHal(mContext, builder.mVehicle), allServices);
mCarPropertyService = carServiceCreator.createService(
CarPropertyService.class,
() -> new CarPropertyService.Builder()
.setContext(mContext)
.setPropertyHalService(mHal.getPropertyHal())
.build());
mCarDrivingStateService = carServiceCreator.createService(
CarDrivingStateService.class,
() -> new CarDrivingStateService(mContext, mCarPropertyService));
mCarOccupantZoneService = carServiceCreator.createService(
CarOccupantZoneService.class,
() -> new CarOccupantZoneService(mContext));
mCarUXRestrictionsService = carServiceCreator.createService(
CarUxRestrictionsManagerService.class,
() -> new CarUxRestrictionsManagerService(mContext, mCarDrivingStateService,
mCarPropertyService, mCarOccupantZoneService));
// ...
}
The following diagram shows the overall CarService architecture:
graph TB
subgraph "CarService Process"
CSI["CarServiceImpl<br/>extends ProxiedService"]
ICI["ICarImpl<br/>extends ICar.Stub"]
subgraph "Vehicle Abstraction"
VS[VehicleStub]
AVS[AidlVehicleStub]
HVS[HidlVehicleStub]
FVS[FakeVehicleStub]
end
subgraph "HAL Layer"
VH[VehicleHal]
PHal[PowerHalService]
PropHal[PropertyHalService]
IHal[InputHalService]
UHal[UserHalService]
CHal[ClusterHalService]
EHal[EvsHalService]
end
subgraph "Car Services"
CPS[CarPropertyService]
CPMS[CarPowerManagementService]
COZS[CarOccupantZoneService]
CDSS[CarDrivingStateService]
CUXS[CarUxRestrictionsService]
CAS[CarAudioService]
CBS[CarBluetoothService]
CUS[CarUserService]
FAS[FixedActivityService]
GMS[GarageModeService]
ICS[InstrumentClusterService]
CHS[ClusterHomeService]
CEVS[CarEvsService]
CTS[CarTelemetryService]
CWS[CarWatchdogService]
CACS[CarActivityService]
end
end
subgraph "Hardware"
VHAL["Vehicle HAL<br/>hardware/interfaces/automotive/vehicle/"]
EVS_HAL["EVS HAL<br/>Camera subsystem"]
AC_HAL[AudioControl HAL]
end
CSI --> ICI
ICI --> VS
VS --> AVS
VS --> HVS
VS --> FVS
ICI --> VH
VH --> PHal
VH --> PropHal
VH --> IHal
VH --> UHal
VH --> CHal
VH --> EHal
VH --> VS
CPS --> PropHal
CPMS --> PHal
ICS --> CHal
CHS --> CHal
CEVS --> EHal
AVS --> VHAL
HVS --> VHAL
CEVS --> EVS_HAL
CAS --> AC_HAL
62.1.2 The Vehicle HAL¶
The Vehicle HAL is the boundary between Android and the vehicle's electronic control units (ECUs).
It defines a property-based abstraction: every piece of vehicle data (speed, gear, HVAC
temperature, door lock status) is exposed as a VehicleProperty with a property ID, area ID,
value type, and change mode.
The AIDL interface is defined at:
hardware/interfaces/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl
@VintfStability
interface IVehicle {
const long INVALID_MEMORY_ID = 0;
const int MAX_SHARED_MEMORY_FILES_PER_CLIENT = 3;
VehiclePropConfigs getAllPropConfigs();
VehiclePropConfigs getPropConfigs(in int[] props);
void getValues(IVehicleCallback callback, in GetValueRequests requests);
void setValues(IVehicleCallback callback, in SetValueRequests requests);
void subscribe(in IVehicleCallback callback, in SubscribeOptions[] options,
int maxSharedMemoryFileCount);
void unsubscribe(in IVehicleCallback callback, in int[] propIds);
void returnSharedMemory(in IVehicleCallback callback, long sharedMemoryId);
SupportedValuesListResults getSupportedValuesLists(in List<PropIdAreaId> propIdAreaIds);
MinMaxSupportedValueResults getMinMaxSupportedValue(in List<PropIdAreaId> propIdAreaIds);
void registerSupportedValueChangeCallback(
in IVehicleCallback callback, in List<PropIdAreaId> propIdAreaIds);
void unregisterSupportedValueChangeCallback(
in IVehicleCallback callback, in List<PropIdAreaId> propIdAreaIds);
}
Key design decisions in the Vehicle HAL:
-
Asynchronous operations:
getValues()andsetValues()use callbacks, not blocking returns. This prevents the car service from stalling on slow ECU communication. -
Shared memory for large payloads: The
returnSharedMemorymechanism allows efficient transfer of bulk sensor data without repeated binder parceling. -
Property change modes: Properties are either
STATIC(never change, like VIN number),ON_CHANGE(event-driven, like door state), orCONTINUOUS(polled, like vehicle speed). -
Area IDs: Properties can be scoped to vehicle areas. HVAC temperature might have different area IDs for driver-side and passenger-side. Seat-related properties use
VehicleAreaSeatbit masks.
On the Java side, VehicleHal dispatches incoming property events to specialized
HalServiceBase implementations:
// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java
public class VehicleHal implements VehicleHalCallback, CarSystemService {
private final PowerHalService mPowerHal;
private final PropertyHalService mPropertyHal;
private final InputHalService mInputHal;
private final VmsHalService mVmsHal;
private final UserHalService mUserHal;
private final DiagnosticHalService mDiagnosticHal;
private final ClusterHalService mClusterHalService;
private final EvsHalService mEvsHal;
private final TimeHalService mTimeHalService;
// ...
}
Each HalServiceBase is responsible for subscribing to the VHAL properties it cares about,
converting raw HalPropValue events into higher-level data, and routing that data to the
corresponding Car*Service.
The event dispatch mechanism uses specialized DispatchList classes that route events
to the correct HalServiceBase on its dedicated executor:
// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java
private final class HalEventsDispatchList extends
DispatchList<HalServiceBase, HalPropValue> {
@Override
protected void dispatchToClient(HalServiceBase service,
List<HalPropValue> events) {
var eventsCopy = List.copyOf(events);
var executor = getExecutorForService(service);
if (executor == null) return;
executor.execute(() -> {
service.onHalEvents(eventsCopy);
});
}
}
private final class PropertySetErrorDispatchList extends
DispatchList<HalServiceBase, VehiclePropError> {
@Override
protected void dispatchToClient(HalServiceBase service,
List<VehiclePropError> events) {
var eventsCopy = List.copyOf(events);
var executor = getExecutorForService(service);
if (executor == null) return;
executor.execute(() -> {
service.onPropertySetError(eventsCopy);
});
}
}
The subscription system tracks rates and variable update rate (VUR) settings per property-area pair:
// packages/services/Car/service/src/com/android/car/hal/VehicleHal.java
/* package */ static final class HalSubscribeOptions {
private final int mHalPropId;
private final int[] mAreaIds;
private final float mUpdateRateHz;
private final boolean mEnableVariableUpdateRate;
private final float mResolution;
HalSubscribeOptions(int halPropId, int[] areaIds, float updateRateHz,
boolean enableVariableUpdateRate, float resolution) {
mHalPropId = halPropId;
mAreaIds = areaIds;
mUpdateRateHz = updateRateHz;
mEnableVariableUpdateRate = enableVariableUpdateRate;
mResolution = resolution;
}
}
Variable Update Rate (VUR) is an important optimization: when enabled, the VHAL only delivers events when the property value actually changes by more than the specified resolution, even if the polling rate would trigger more frequent deliveries. This reduces CPU and binder overhead for high-frequency properties like vehicle speed.
The VehicleStub abstraction supports both AIDL and legacy HIDL interfaces:
// packages/services/Car/service/src/com/android/car/VehicleStub.java
public abstract class VehicleStub {
public interface SubscriptionClient {
void subscribe(SubscribeOptions[] options)
throws RemoteException, ServiceSpecificException;
void unsubscribe(int prop)
throws RemoteException, ServiceSpecificException;
void registerSupportedValuesChange(List<PropIdAreaId> propIdAreaIds);
void unregisterSupportedValuesChange(List<PropIdAreaId> propIdAreaIds);
}
// ...
}
The concrete implementations AidlVehicleStub and HidlVehicleStub handle protocol-specific
details. A FakeVehicleStub (SimulationVehicleStub) exists for testing without real hardware.
graph LR
subgraph "VehicleStub Hierarchy"
VS["VehicleStub<br/>abstract"]
AVS[AidlVehicleStub]
HVS[HidlVehicleStub]
FVS[FakeVehicleStub]
end
subgraph "VHAL Property Model"
VP["VehicleProperty<br/>ID + AreaID"]
VT["Value Types<br/>INT32, FLOAT, STRING,<br/>INT32_VEC, MIXED"]
CM["Change Modes<br/>STATIC, ON_CHANGE,<br/>CONTINUOUS"]
AT["Area Types<br/>GLOBAL, WINDOW,<br/>MIRROR, SEAT,<br/>DOOR, WHEEL"]
end
VS --> AVS
VS --> HVS
VS --> FVS
VP --> VT
VP --> CM
VP --> AT
62.1.3 Car Property System¶
The car property system is the primary interface for applications to read and write vehicle data.
CarPropertyService exposes properties to apps through CarPropertyManager:
// packages/services/Car/service/src/com/android/car/CarPropertyService.java
// (Referenced via ICarImpl constructor)
mCarPropertyService = carServiceCreator.createService(
CarPropertyService.class,
() -> new CarPropertyService.Builder()
.setContext(mContext)
.setPropertyHalService(mHal.getPropertyHal())
.build());
The property flow from app to hardware:
sequenceDiagram
participant App as Application
participant CPM as CarPropertyManager
participant CPS as CarPropertyService
participant PHS as PropertyHalService
participant VH as VehicleHal
participant VS as VehicleStub
participant VHAL as Vehicle HAL (Native)
participant ECU as Vehicle ECU
App->>CPM: getProperty(PERF_VEHICLE_SPEED, GLOBAL_AREA)
CPM->>CPS: getProperty() [Binder IPC]
CPS->>PHS: getProperty()
PHS->>VH: get(halPropValue)
VH->>VS: getValues(callback, requests)
VS->>VHAL: getValues() [AIDL/HIDL]
VHAL->>ECU: CAN/LIN bus read
ECU-->>VHAL: Raw value
VHAL-->>VS: GetValueResult
VS-->>VH: Callback with result
VH-->>PHS: HalPropValue
PHS-->>CPS: CarPropertyValue
CPS-->>CPM: CarPropertyValue [Binder]
CPM-->>App: CarPropertyValue<Float>
For subscription-based access (event-driven properties like speed or gear), apps register callbacks that fire when the HAL pushes new values:
sequenceDiagram
participant App as Application
participant CPM as CarPropertyManager
participant CPS as CarPropertyService
participant PHS as PropertyHalService
participant VH as VehicleHal
participant VHAL as Vehicle HAL
App->>CPM: registerCallback(PERF_VEHICLE_SPEED, rate)
CPM->>CPS: registerListener()
CPS->>PHS: subscribeProperty()
PHS->>VH: subscribeProperty()
VH->>VHAL: subscribe(options)
Note over VHAL: Vehicle speed changes
VHAL-->>VH: onPropertyEvent()
VH-->>PHS: onHalEvents()
PHS-->>CPS: onPropertyChange()
CPS-->>CPM: onChangeEvent [Binder callback]
CPM-->>App: onChangeEvent(CarPropertyValue)
62.1.4 Occupant Zones¶
Multi-zone vehicles have separate displays and user sessions for different seating positions.
CarOccupantZoneService manages the mapping between physical seat positions, displays, Android
users, and input devices.
// packages/services/Car/service/src/com/android/car/CarOccupantZoneService.java
public final class CarOccupantZoneService extends ICarOccupantZone.Stub
implements CarServiceBase {
public static final class DisplayConfig {
public final int displayType;
public final int occupantZoneId;
public final int[] inputTypes;
DisplayConfig(int displayType, int occupantZoneId, IntArray inputTypes) {
this.displayType = displayType;
this.occupantZoneId = occupantZoneId;
this.inputTypes = inputTypes == null
? EMPTY_INPUT_SUPPORT_TYPES : inputTypes.toArray();
}
}
@VisibleForTesting
static class OccupantConfig {
public int userId = CarOccupantZoneManager.INVALID_USER_ID;
public final ArrayList<DisplayInfo> displayInfos = new ArrayList<>();
public int audioZoneId = CarAudioManager.INVALID_AUDIO_ZONE;
}
/** key : zoneId */
@GuardedBy("mLock")
private final SparseArray<OccupantConfig> mActiveOccupantConfigs = new SparseArray<>();
@GuardedBy("mLock")
private int mDriverZoneId = OccupantZoneInfo.INVALID_ZONE_ID;
}
The occupant zone model has several key concepts:
-
OccupantZoneInfo: Represents a physical seating position (driver, front passenger, rear left, rear right, etc.), each identified by a unique zone ID.
-
DisplayConfig: Maps a display type (main, instrument cluster, HUD) to an occupant zone and specifies what input types that display supports.
-
OccupantConfig: The runtime state linking a zone to an Android user ID, a set of displays, and an audio zone.
graph TB
subgraph "Occupant Zone Model"
subgraph "Zone 0: Driver"
Z0["OccupantZoneInfo<br/>zoneId=0, DRIVER"]
D0["Display: Main<br/>displayType=MAIN"]
D1["Display: Cluster<br/>displayType=INSTRUMENT_CLUSTER"]
U0["User: owner (userId=0)"]
A0[Audio Zone 0]
end
subgraph "Zone 1: Front Passenger"
Z1["OccupantZoneInfo<br/>zoneId=1, FRONT_PASSENGER"]
D2["Display: Passenger<br/>displayType=MAIN"]
U1["User: passenger (userId=10)"]
A1[Audio Zone 1]
end
subgraph "Zone 2: Rear Left"
Z2["OccupantZoneInfo<br/>zoneId=2, REAR_LEFT"]
D3["Display: Rear Left<br/>displayType=MAIN"]
U2["User: child (userId=11)"]
A2[Audio Zone 2]
end
end
Z0 --> D0
Z0 --> D1
Z0 --> U0
Z0 --> A0
Z1 --> D2
Z1 --> U1
Z1 --> A1
Z2 --> D3
Z2 --> U2
Z2 --> A2
The service listens for display changes and user lifecycle events to dynamically reconfigure zones:
// packages/services/Car/service/src/com/android/car/CarOccupantZoneService.java
@VisibleForTesting
final UserLifecycleListener mUserLifecycleListener = event -> {
if (DBG) Slogf.d(TAG, "onEvent(%s)", event);
boolean isUserSwitching =
(event.getEventType() == USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
handleUserChange(isUserSwitching);
};
@VisibleForTesting
final DisplayManager.DisplayListener mDisplayListener =
new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(int displayId) {
handleDisplayChange(displayId);
}
@Override
public void onDisplayRemoved(int displayId) {
handleDisplayChange(displayId);
}
@Override
public void onDisplayChanged(int displayId) {
// nothing to do
}
};
When a display is hotplugged (a rear-seat entertainment screen is connected, for example), the service re-evaluates the zone-to-display mapping and notifies all registered callbacks.
The init() method shows the full initialization sequence:
// packages/services/Car/service/src/com/android/car/CarOccupantZoneService.java
@Override
public void init() {
Car car = new Car(mContext, /* service= */null, /* handler= */ null);
CarInfoManager infoManager = new CarInfoManager(car,
CarLocalServices.getService(CarPropertyService.class));
int driverSeat = infoManager.getDriverSeat();
synchronized (mLock) {
mDriverSeat = driverSeat;
parseOccupantZoneConfigsLocked(); // Read zone config from RRO
parseDisplayConfigsLocked(); // Map displays to zones
handleActiveDisplaysLocked(); // Activate connected displays
handleAudioZoneChangesLocked(); // Set up audio routing
handleUserChangesLocked(); // Assign users to zones
}
mCarUserService = CarLocalServices.getService(CarUserService.class);
UserLifecycleEventFilter userEventFilter =
new UserLifecycleEventFilter.Builder()
.addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING)
.addEventType(USER_LIFECYCLE_EVENT_TYPE_STOPPING)
.build();
mCarUserService.addUserLifecycleListener(userEventFilter,
mUserLifecycleListener);
mDisplayManager.registerDisplayListener(mDisplayListener,
new Handler(Looper.getMainLooper()));
CarServiceHelperWrapper.getInstance().runOnConnection(
() -> doSyncWithCarServiceHelper(
/* updateDisplay= */ true, /* updateUser= */ true));
}
The occupant zone configuration is read from the RRO config resource
config_occupant_zones. If this resource is empty, the service automatically creates a
single driver zone. The configuration specifies seat positions, display types, and input
support for each zone.
The profile user assignment feature (mEnableProfileUserAssignmentForMultiDisplay) allows
different Android user profiles to be assigned to different zones. A child profile might
be assigned to the rear-seat display while the primary user controls the driver display.
This requires both the enableProfileUserAssignmentForMultiDisplay config boolean and the
FEATURE_MANAGED_USERS system feature.
62.1.5 Instrument Cluster¶
The instrument cluster is the display behind the steering wheel. AAOS supports rendering
navigation, phone-call, and media information on this display. The
InstrumentClusterService binds to a vendor-provided rendering service:
// packages/services/Car/service/src/com/android/car/cluster/InstrumentClusterService.java
@SystemApi
public class InstrumentClusterService implements CarServiceBase, KeyEventListener,
ClusterNavigationService.ClusterNavigationServiceCallback {
private static final long RENDERER_SERVICE_WAIT_TIMEOUT_MS = 5000;
private static final long RENDERER_WAIT_MAX_RETRY = 2;
private final Context mContext;
private final CarInputService mCarInputService;
private final ClusterNavigationService mClusterNavigationService;
// ...
@GuardedBy("mLock")
private IInstrumentCluster mRendererService;
}
The newer ClusterHomeService provides a more modern approach where the cluster display runs
a full Android activity (the "Cluster Home" app), and content is rendered via
ClusterHalService communicating cluster state through VHAL properties.
The sample cluster application lives at:
packages/apps/Car/Cluster/ClusterOsDouble/
This ClusterOsDouble acts as a testing app for the Cluster2 framework, handling Cluster VHAL properties and performing Cluster OS role functions. It includes:
ClusterOsDoubleActivity-- Main activity displaying cluster informationNavStateController-- Handles navigation state displayClusterViewModel-- ViewModel for cluster data- Sensor integration classes for vehicle telemetry visualization
The cluster rendering flow:
sequenceDiagram
participant NavApp as Navigation App
participant CNS as ClusterNavigationService
participant ICS as InstrumentClusterService
participant CHS as ClusterHalService
participant Renderer as Cluster Renderer (Vendor App)
participant ClusterDisplay as Instrument Cluster Display
NavApp->>CNS: sendNavigationState(bundle)
CNS->>ICS: onNavigationStateChanged()
ICS->>Renderer: IInstrumentClusterNavigation.onNavigationStateChanged() (via getNavigationService())
Renderer->>ClusterDisplay: Render navigation turn card
Note over CHS,Renderer: Cluster state changes via VHAL
CHS-->>ICS: onClusterStateChanged()
ICS->>Renderer: Update cluster mode
62.1.6 FixedActivityService¶
In automotive, certain displays must always show specific activities. The driver's instrument
cluster must always show the cluster UI; a rear-seat entertainment screen might always show a
media player. FixedActivityService guarantees that a designated activity is always in the
foreground on a given display, re-launching it if it crashes or is covered.
The service uses multiple monitoring mechanisms to detect when the fixed activity is no longer visible and needs to be relaunched:
// packages/services/Car/service/src/com/android/car/am/FixedActivityService.java
/**
* Monitors top activity for a display and guarantee activity in fixed mode is
* re-launched if it has crashed or gone to background for whatever reason.
*
* This component also monitors the update of the target package and re-launch
* it once update is complete.
*/
public final class FixedActivityService implements CarServiceBase {
private static final long RECHECK_INTERVAL_MS = 500;
private static final int MAX_NUMBER_OF_CONSECUTIVE_CRASH_RETRY = 5;
private static final long CRASH_FORGET_INTERVAL_MS = 2 * 60 * 1000; // 2 mins
private static class RunningActivityInfo {
@NonNull public final Intent intent;
@NonNull public final Bundle activityOptions;
@UserIdInt public final int userId;
public boolean isVisible;
public boolean isStarted;
public long lastLaunchTimeMs;
public int consecutiveRetries;
public int taskId = INVALID_TASK_ID;
public int previousTaskId = INVALID_TASK_ID;
public boolean inBackground;
}
}
The service maintains RunningActivityInfo records per display. Every 500ms it rechecks
whether the expected activity is on top. If the activity has crashed more than 5 times
consecutively, it backs off. After 2 minutes without a crash, the consecutive-retry counter
resets.
The monitoring infrastructure is comprehensive. FixedActivityService registers four
different event sources to detect when intervention is needed:
// packages/services/Car/service/src/com/android/car/am/FixedActivityService.java
// 1. Process lifecycle monitoring
private final ProcessObserverCallback mProcessObserver = new ProcessObserverCallback() {
@Override
public void onForegroundActivitiesChanged(int pid, int uid,
boolean foregroundActivities) {
launchIfNecessary();
}
@Override
public void onProcessDied(int pid, int uid) {
launchIfNecessary();
}
};
// 2. Package update monitoring
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
|| Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
// Reset crash counter and relaunch
}
}
};
// 3. Display state monitoring
private final DisplayListener mDisplayListener = new DisplayListener() {
@Override
public void onDisplayChanged(int displayId) {
launchForDisplay(displayId);
}
};
// 4. Power state monitoring
private final CarPowerManager.CarPowerStateListener mCarPowerStateListener =
(state) -> {
if (state != CarPowerManager.STATE_ON) return;
// Reset crash counters and relaunch on power on
launchIfNecessary();
};
The mRunningActivities SparseArray maps display IDs to their RunningActivityInfo. The
default capacity is 1, optimized for the common case of a single instrument cluster:
// key: displayId
@GuardedBy("mLock")
private final SparseArray<RunningActivityInfo> mRunningActivities =
new SparseArray<>(/* capacity= */ 1); // default to one cluster only case
When launchIfNecessary() fires, it checks each monitored display, compares the current top
activity against the expected activity, and calls startActivity() if they differ. The
activityOptions Bundle in RunningActivityInfo contains the display ID targeting
information.
stateDiagram-v2
[*] --> Monitoring: startFixedActivityModeForDisplay
Monitoring --> ActivityVisible: Top activity matches
ActivityVisible --> ActivityGone: Activity crashes/backgrounded
ActivityGone --> Relaunching: recheck timer fires 500ms
Relaunching --> ActivityVisible: startActivity succeeds
Relaunching --> CrashBackoff: consecutiveRetries > 5
CrashBackoff --> Relaunching: CRASH_FORGET_INTERVAL 2min
Monitoring --> [*]: stopFixedActivityMode
62.1.7 CarActivityService¶
CarActivityService manages activity placement across multiple displays, handles launch-on-
display routing, and provides the CarSystemUIProxy interface that lets Car SystemUI control
task organization:
// packages/services/Car/service/src/com/android/car/am/CarActivityService.java
public class CarActivityService extends ICarActivityService.Stub
implements CarServiceBase {
// Manages per-display task placement, blocking activities for
// distraction optimization, and SystemUI proxy registration
}
This service works closely with CarPackageManagerService to enforce which activities are
allowed on which displays based on driving state and UX restrictions.
The CarActivityService provides several critical capabilities:
-
Display-specific activity launching: When an app targets a specific occupant zone, the service routes the activity to the correct display using
ActivityOptions: -
SystemUI proxy registration: Car SystemUI registers itself as a proxy through
ICarSystemUIProxy, allowing the service to control task presentation: -
Blocking activity management: When a non-distraction-optimized activity attempts to display while driving, the service intercepts and replaces it with a blocking activity that shows a safety message. The display ID is passed via:
-
Task mirroring and movement: Tasks can be moved between displays (e.g., moving a passenger's navigation session to the driver's display).
sequenceDiagram
participant App as Application
participant AMS as ActivityManagerService
participant CAS as CarActivityService
participant CPMS2 as CarPackageManagerService
participant CUXS2 as CarUxRestrictionsService
participant Display as Target Display
App->>AMS: startActivity(intent, displayId)
AMS->>CAS: Check display routing
CAS->>CPMS2: isActivityAllowed(activity, displayId)?
CPMS2->>CUXS2: getCurrentRestrictions(displayId)
alt Activity is distraction-optimized
CUXS2-->>CPMS2: Restrictions OK
CPMS2-->>CAS: Allowed
CAS-->>AMS: Proceed
AMS->>Display: Launch activity
else Activity is NOT distraction-optimized AND driving
CUXS2-->>CPMS2: Restrictions ACTIVE
CPMS2-->>CAS: Blocked
CAS->>AMS: Launch blocking activity instead
AMS->>Display: Show blocking UI
end
62.1.8 Car Power Management¶
Automotive power management differs fundamentally from mobile. A car's power state is driven
by the vehicle's ignition system, not a user pressing a power button. The
CarPowerManagementService manages transitions between power states:
// packages/services/Car/service/src/com/android/car/power/CarPowerManagementService.java
public class CarPowerManagementService extends ICarPower.Stub implements
CarServiceBase, PowerHalService.PowerEventListener {
// Power state constants
private static final int ACTION_ON_FINISH_SHUTDOWN = 0;
private static final int ACTION_ON_FINISH_DEEP_SLEEP = 1;
private static final int ACTION_ON_FINISH_HIBERNATION = 2;
// Suspend retry with exponential backoff
private static final long INITIAL_SUSPEND_RETRY_INTERVAL_MS = 10;
private static final long MAX_RETRY_INTERVAL_MS = 100;
// Garage mode constraints
private static final int MIN_GARAGE_MODE_DURATION_MS = 15 * 60 * 1000; // 15 min
}
The automotive power state machine:
stateDiagram-v2
[*] --> WaitForVhal: Boot
WaitForVhal --> On: VHAL ready
On --> ShutdownPrepare: AP_POWER_STATE_REQ = SHUTDOWN_PREPARE
On --> PreShutdownPrepare: PRE_SHUTDOWN_PREPARE
PreShutdownPrepare --> ShutdownPrepare: Listeners complete
ShutdownPrepare --> GarageMode: canPostpone=true
ShutdownPrepare --> Shutdown: canPostpone=false
GarageMode --> DeepSleep: Garage tasks complete + DEEP_SLEEP requested
GarageMode --> Hibernation: HIBERNATION requested
GarageMode --> Shutdown: SHUTDOWN requested
DeepSleep --> WaitForVhal: Resume from suspend
Hibernation --> WaitForVhal: Resume from hibernate
Shutdown --> [*]: Power off
note right of GarageMode
Runs deferred tasks:
App updates,
Data sync,
Optimization jobs
end note
The power policy system controls which hardware components are powered on in each state. For example, during deep sleep, displays and audio might be off, but the cellular modem stays on for remote access:
// packages/services/Car/service/src/com/android/car/power/CarPowerManagementService.java
private static final String WIFI_STATE_FILENAME = "wifi_state";
private static final String TETHERING_STATE_FILENAME = "tethering_state";
private static final String COMPONENT_STATE_MODIFIED = "forcibly_disabled";
private static final String COMPONENT_STATE_ORIGINAL = "original";
Power policy definitions interact with the native power policy daemon at:
android.frameworks.automotive.powerpolicy.internal.ICarPowerPolicySystemNotification
62.1.9 Garage Mode¶
Garage Mode is the period after the driver turns off the ignition but before the vehicle fully shuts down. During this window, AAOS performs maintenance tasks:
// packages/services/Car/service/src/com/android/car/garagemode/GarageModeService.java
/**
* Main service container for car Garage Mode.
* Garage Mode enables idle time in cars.
*/
public class GarageModeService implements CarServiceBase {
private final GarageModeController mController;
// ...
}
The GarageModeController is the brain of garage mode, implementing ICarPowerStateListener
to respond to power state transitions:
// packages/services/Car/service/src/com/android/car/garagemode/GarageModeController.java
public class GarageModeController extends ICarPowerStateListener.Stub {
private final GarageMode mGarageMode;
private CarPowerManagementService mCarPowerService;
public void init() {
mCarPowerService = CarLocalServices.getService(
CarPowerManagementService.class);
mCarPowerService.registerInternalListener(GarageModeController.this);
mGarageMode.init();
}
@Override
public void onStateChanged(int state, long expirationTimeMs) {
switch (state) {
case CarPowerManager.STATE_SHUTDOWN_CANCELLED:
resetGarageMode(null);
break;
case CarPowerManager.STATE_SHUTDOWN_ENTER:
case CarPowerManager.STATE_SUSPEND_ENTER:
case CarPowerManager.STATE_HIBERNATION_ENTER:
resetGarageMode(() -> {
mCarPowerService.completeHandlingPowerStateChange(state,
GarageModeController.this);
});
break;
case CarPowerManager.STATE_SHUTDOWN_PREPARE:
initiateGarageMode(
() -> mCarPowerService.completeHandlingPowerStateChange(
state, GarageModeController.this));
break;
default:
break;
}
}
}
The critical state transition is STATE_SHUTDOWN_PREPARE, which triggers
initiateGarageMode(). When garage mode completes (either all jobs finish or the timeout
expires), it calls completeHandlingPowerStateChange() to signal that the power service
can proceed with the actual shutdown or suspend.
The controller coordinates with JobScheduler to run deferred jobs that have
the REQUIRE_DEVICE_IDLE constraint. OEMs configure the maximum garage mode duration
through the system property android.car.garagemodeduration. The minimum enforced duration
is 15 minutes, ensuring enough time for critical updates.
Garage mode also handles edge cases:
-
STATE_SHUTDOWN_CANCELLED: If the driver turns the ignition back on during shutdown preparation, garage mode is immediately cancelled. -
STATE_SUSPEND_ENTER/STATE_HIBERNATION_ENTER: Different paths for deep sleep vs. hibernate, both requiring garage mode cleanup before proceeding. -
The completion callback pattern ensures the power state machine does not proceed until garage mode has properly cleaned up.
sequenceDiagram
participant Ignition as Vehicle Ignition
participant VHAL as Vehicle HAL
participant CPMS as CarPowerManagementService
participant GMS as GarageModeService
participant GMC as GarageModeController
participant JS as JobScheduler
Ignition->>VHAL: Ignition OFF
VHAL->>CPMS: AP_POWER_STATE_REQ = SHUTDOWN_PREPARE
CPMS->>GMS: enterGarageMode()
GMS->>GMC: enterGarageMode()
GMC->>JS: Schedule deferred jobs
Note over GMC,JS: Jobs run: app updates, data sync, optimization
GMC-->>GMS: All jobs complete / timeout
GMS-->>CPMS: Garage mode finished
CPMS->>VHAL: AP_POWER_STATE_REPORT = DEEP_SLEEP_ENTRY
Note over VHAL: System enters deep sleep
62.1.10 Car Audio Multi-Zone Architecture¶
Automotive audio is fundamentally more complex than phone audio. A car may have separate
speaker zones for driver, passenger, and rear seats, each playing different media. The
CarAudioService manages this through audio zone abstraction:
graph TB
subgraph "Car Audio Architecture"
subgraph "Apps"
NavAudio["Navigation Audio<br/>Turn-by-turn"]
MediaD["Media Player<br/>Driver zone"]
MediaP["Media Player<br/>Passenger zone"]
Phone["Phone Audio<br/>Driver zone"]
end
subgraph "CarAudioService"
ZM["Zone Manager<br/>Route audio by zone"]
FG["Focus Group<br/>Per-zone focus"]
DG["Duck Group<br/>Cross-zone ducking"]
end
subgraph "AudioControl HAL"
AC["AudioControl<br/>hardware/interfaces/<br/>automotive/audiocontrol/"]
end
subgraph "Speaker Zones"
SZ0["Driver Speakers<br/>Audio Zone 0"]
SZ1["Passenger Speakers<br/>Audio Zone 1"]
SZ2["Rear Speakers<br/>Audio Zone 2"]
end
end
NavAudio --> ZM
MediaD --> ZM
MediaP --> ZM
Phone --> ZM
ZM --> FG
ZM --> DG
ZM --> AC
AC --> SZ0
AC --> SZ1
AC --> SZ2
Audio zones are mapped to occupant zones, so each passenger gets independent volume control and audio focus. Navigation audio in the driver zone can duck the driver's music without affecting the passenger's audio.
62.1.11 Driver Distraction and UX Restrictions¶
AAOS enforces safety by restricting UI complexity while driving. The
CarDrivingStateService monitors vehicle speed and gear to determine whether the car is
parked, idling, or moving. The CarUxRestrictionsManagerService translates driving state into
concrete UX restrictions that apps must obey:
graph LR
CPS["CarPropertyService<br/>PERF_VEHICLE_SPEED<br/>GEAR_SELECTION"] --> CDSS["CarDrivingStateService<br/>PARKED / IDLING / MOVING"]
CDSS --> CUXRS[CarUxRestrictionsService]
CUXRS --> UXR["UX Restrictions<br/>NO_TEXT_INPUT<br/>NO_FILTERING<br/>LIMIT_STRING_LENGTH<br/>NO_VIDEO<br/>LIMIT_CONTENT"]
UXR --> Apps["Applications<br/>must check restrictions"]
UXR --> CPMS2["CarPackageManagerService<br/>blocks non-compliant activities"]
When the driving state is MOVING, activities that are not marked as distraction-optimized
are blocked and replaced with a blocking activity that informs the user.
62.1.12 Car-Specific SystemUI¶
AAOS replaces the phone's SystemUI with a car-specific variant located at:
packages/apps/Car/SystemUI/
This variant replaces the status bar with a car-specific system bar, adds HVAC controls, volume controls tailored for multi-zone audio, and a user picker for multi-user vehicles.
// packages/apps/Car/SystemUI/src/com/android/systemui/car/systembar/CarSystemBar.java
@SysUISingleton
public class CarSystemBar implements CoreStartable {
private final CarSystemBarController mCarSystemBarController;
@Inject
public CarSystemBar(CarSystemBarController carSystemBarController) {
mCarSystemBarController = carSystemBarController;
}
@Override
public void start() {
mCarSystemBarController.init();
}
}
The Car SystemUI connects to CarService through CarServiceProvider:
// packages/apps/Car/SystemUI/src/com/android/systemui/car/CarServiceProvider.java
@Singleton
public class CarServiceProvider {
@Inject
public CarServiceProvider(@CarSysUIDumpable Context context) {
mCar = Car.createCar(mContext, null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
(car, ready) -> {
synchronized (mCarLock) {
synchronized (mListeners) {
mIsCarReady = ready;
mCar = car;
if (ready) {
for (CarServiceOnConnectedListener listener : mListeners) {
listener.onConnected(mCar);
}
}
}
}
});
}
}
Key Car SystemUI components:
| Component | Directory | Purpose |
|---|---|---|
| System Bar | car/systembar/ |
Navigation bar replacement with car-specific buttons |
| HVAC Panel | car/hvac/ |
Climate control overlay |
| Volume UI | car/volume/ |
Multi-zone audio volume control |
| User Picker | car/userpicker/ |
Switch between vehicle occupant users |
| Keyguard | car/keyguard/ |
Car-specific lock screen |
| Notifications | car/notification/ |
Automotive notification handling |
| Status Icons | car/statusicon/ |
Vehicle status indicators |
The HVAC module demonstrates how Car SystemUI integrates with vehicle properties:
packages/apps/Car/SystemUI/src/com/android/systemui/car/hvac/
HvacButtonController.java -- Handles HVAC button interactions
HvacPanelOverlayViewMediator.java -- Manages HVAC panel visibility
HvacView.java -- Base HVAC view
HvacPanelView.java -- Full HVAC panel layout
TemperatureControlView.java -- Temperature adjustment widget
referenceui/
FanSpeedBar.java -- Fan speed control
FanDirectionButtons.java -- Air direction buttons
62.1.13 Car Launcher¶
The automotive launcher is significantly different from the phone launcher. It provides a home screen designed for large touchscreens with minimal distraction:
packages/apps/Car/Launcher/
libs/
appgrid/lib/src/com/android/car/carlauncher/
AppLauncherUtils.java -- App listing and filtering
AppItem.java -- Data model for launcher items
LauncherItemDiffCallback.java -- Efficient list updates
recyclerview/
AppGridAdapter.java -- Grid display adapter
AppGridLayoutManager.java -- Car-specific grid layout
docklib/src/com/android/car/docklib/
events/DockEventsReceiver.java -- Dock state handling
task/DockTaskStackChangeListener.java -- Task stack monitoring
The Car Launcher differs from phone Launcher3 in several fundamental ways:
-
No home screen widgets: The automotive home screen emphasizes quick app access and essential information (maps, media) rather than customizable widget grids.
-
Dock-based navigation: The dock at the bottom provides persistent access to navigation, phone, media, and app grid.
-
Task stack awareness: The
DockTaskStackChangeListenermonitors the task stack to keep the dock state synchronized with what is actually running. -
Package filtering:
AppLauncherUtilsfilters the app list to show only distraction-optimized applications when driving restrictions are active. -
Multi-display awareness: The launcher must account for activities launching on different displays (driver vs passenger) and adjust its behavior accordingly.
graph TB
subgraph "Car Launcher Components"
subgraph HomeScreen["Home Screen"]
Maps["Maps Card<br/>Always visible"]
Media["Media Card<br/>Now playing"]
AppGrid["App Grid<br/>Filtered by UX state"]
end
subgraph DockGroup["Dock"]
NavBtn[Navigation]
PhoneBtn[Phone]
MediaBtn[Media]
GridBtn[All Apps]
end
subgraph Infra["Infrastructure"]
ALU["AppLauncherUtils<br/>App filtering"]
DER["DockEventsReceiver<br/>Dock state"]
TSL["DockTaskStackChangeListener<br/>Task monitoring"]
end
end
DockGroup --> HomeScreen
Infra --> HomeScreen
Infra --> DockGroup
Many of the apps the launcher lists are "templated" car apps written against the Android-for-Cars App Library, which run inside a separate host process rather than drawing their own UI. Android 17 bundles that host as a prebuilt: packages/apps/Car/TemplatesPrebuilt/Android.bp declares an android_app_import named CarAppHost for CarAppHost.apk, installed platform-signed (certificate: "platform") and privileged. There is no source for the host in AOSP; the project ships only the build glue that imports the binary APK, so an AAOS build pulls in the host without building it.
62.1.14 External View System (EVS)¶
The Exterior View System provides camera-based features like rearview, surround view, and
parking assistance. The EVS HAL is defined at:
hardware/interfaces/automotive/evs/
It supports both AIDL (current) and HIDL (legacy 1.1) interfaces. The CarEvsService in
CarService manages camera lifecycle, display routing, and integrates with the occupant zone
system to determine which display should show the camera feed.
62.1.15 Automotive HAL Directory Structure¶
The full set of automotive HAL interfaces:
hardware/interfaces/automotive/
vehicle/ -- Vehicle property abstraction (AIDL + HIDL 2.0)
evs/ -- Exterior View System cameras
audiocontrol/ -- Multi-zone audio routing
can/ -- CAN bus interface
ivn_android_device/ -- In-Vehicle Networking
occupant_awareness/ -- Occupant detection (presence, attention)
remoteaccess/ -- Remote wake and task execution
62.1.16 Product Configuration¶
Automotive product builds are configured through makefiles in:
packages/services/Car/car_product/build/
# packages/services/Car/car_product/build/car.mk
PRODUCT_PACKAGES += \
Bluetooth \
CarActivityResolver \
CarDeveloperOptions \
CarSettingsIntelligence \
CarManagedProvisioning \
StatementService \
SystemUpdater
PRODUCT_PROPERTY_OVERRIDES += \
ro.carrier=unknown \
ro.hardware.type=automotive
The ro.hardware.type=automotive property is the fundamental flag that tells the framework
this is an automotive build. Feature flags, SEPolicy, and overlay configurations branch
on this property throughout the system.
Runtime Resource Overlays (RROs) customize the look and feel:
packages/services/Car/car_product/rro/
CarSystemUIRRO/ -- SystemUI visual overrides
DriveModeSportRRO/ -- Sport driving mode theme
DriveModeEcoRRO/ -- Eco driving mode theme
overlay-config/
androidRRO/ -- Framework resource overrides
SettingsProviderRRO/ -- Default settings values
TelecommRRO/ -- Telecom UI adjustments
oem-design-tokens/
OEMDesignTokenRRO/ -- OEM visual design tokens
62.2 Android TV¶
Android TV transforms Android into a 10-foot UI experience. The framework additions focus on three areas: a TV Input Framework (TIF) for managing broadcast and HDMI sources, HDMI-CEC control for device coordination, and a specialized windowing system for D-pad navigation and picture-in-picture.
62.2.1 TV Input Framework (TIF) Architecture¶
The TV Input Framework is the cornerstone of Android TV. It abstracts TV input sources --
built-in tuners, HDMI ports, IP streams, and third-party inputs -- into a uniform model.
The key system service is TvInputManagerService:
// frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java
public final class TvInputManagerService extends SystemService {
private static final String TAG = "TvInputManagerService";
private static final String DVB_DIRECTORY = "/dev/dvb";
// ...
}
The TIF has four layers:
graph TB
subgraph "Application Layer"
TVApp[TV App / Live TV]
TVView["TvView<br/>android.media.tv.TvView"]
TIM["TvInputManager<br/>Client API"]
end
subgraph "Framework Layer"
TIMS["TvInputManagerService<br/>System Service"]
TIHM["TvInputHardwareManager<br/>Hardware input routing"]
TIH["TvInputHal<br/>Native HAL bridge"]
TRMS["TunerResourceManagerService<br/>Tuner resource arbitration"]
end
subgraph "Provider Layer"
TIS1["TvInputService<br/>Built-in Tuner"]
TIS2["TvInputService<br/>HDMI Input"]
TIS3["TvInputService<br/>IP TV Provider"]
TIS4["TvInputService<br/>Third-party"]
end
subgraph "HAL Layer"
TVHAL["tv_input HAL<br/>hardware/interfaces/tv/input/"]
TunerHAL["Tuner HAL<br/>hardware/interfaces/tv/tuner/"]
CECHAL["CEC HAL<br/>hardware/interfaces/tv/cec/"]
end
TVApp --> TVView
TVView --> TIM
TIM --> TIMS
TIMS --> TIHM
TIHM --> TIH
TIMS --> TIS1
TIMS --> TIS2
TIMS --> TIS3
TIMS --> TIS4
TIH --> TVHAL
TIS1 --> TunerHAL
TIHM --> CECHAL
TIMS --> TRMS
62.2.2 TvInputService¶
TvInputService is the abstract base class that all TV input providers must extend. It follows
a pattern similar to InputMethodService -- each provider runs as a bound service that creates
sessions on demand:
// frameworks/base/media/java/android/media/tv/TvInputService.java
public abstract class TvInputService extends Service {
public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService";
public static final String SERVICE_META_DATA = "android.media.tv.input";
// Priority hint use case types for tuner resource management
@IntDef(prefix = "PRIORITY_HINT_USE_CASE_TYPE_",
value = {PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND,
PRIORITY_HINT_USE_CASE_TYPE_SCAN,
PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK,
PRIORITY_HINT_USE_CASE_TYPE_LIVE,
PRIORITY_HINT_USE_CASE_TYPE_RECORD})
public @interface PriorityHintUseCaseType {}
}
Each TvInputService creates Session objects that handle individual tuning requests.
The session lifecycle:
sequenceDiagram
participant User as User
participant TVApp as TV Application
participant TvView as TvView
participant TIMS as TvInputManagerService
participant TIS as TvInputService
participant Session as TvInputService.Session
User->>TVApp: Select channel
TVApp->>TvView: tune(inputId, channelUri)
TvView->>TIMS: createSession(inputId)
TIMS->>TIS: Bind to service
TIS->>Session: onCreateSession()
Session-->>TIMS: Session created
TIMS->>Session: onSetSurface(surface)
TIMS->>Session: onTune(channelUri)
Session->>Session: Start decoding/rendering
Session-->>TvView: Video frames on surface
Note over Session: Channel change
User->>TVApp: Next channel
TVApp->>TvView: tune(inputId, newChannelUri)
TvView->>TIMS: tune(sessionToken, newChannelUri)
TIMS->>Session: onTune(newChannelUri)
A TvInputService declares itself in the manifest with the BIND_TV_INPUT permission:
<service android:name=".MyTvInputService"
android:permission="android.permission.BIND_TV_INPUT">
<intent-filter>
<action android:name="android.media.tv.TvInputService" />
</intent-filter>
<meta-data android:name="android.media.tv.input"
android:resource="@xml/tv_input" />
</service>
62.2.3 TvInputManagerService Internals¶
Looking deeper at TvInputManagerService, the service manages per-user state and handles
DVB device discovery:
// frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java
public final class TvInputManagerService extends SystemService {
private static final String DVB_DIRECTORY = "/dev/dvb";
// DVB frontend device patterns:
// Format 1: /dev/dvb%d.frontend%d
// Format 2: /dev/dvb/adapter%d/frontend%d
private static final Pattern sFrontEndDevicePattern =
Pattern.compile("^dvb([0-9]+)\\.frontend([0-9]+)$");
private static final Pattern sAdapterDirPattern =
Pattern.compile("^adapter([0-9]+)$");
private static final Pattern sFrontEndInAdapterDirPattern =
Pattern.compile("^frontend([0-9]+)$");
private final TvInputHardwareManager mTvInputHardwareManager;
private final UserManager mUserManager;
@GuardedBy("mLock")
private int mCurrentUserId = UserHandle.USER_SYSTEM;
@GuardedBy("mLock")
private String mOnScreenInputId = null;
@GuardedBy("mLock")
private SessionState mOnScreenSessionState = null;
// Per-user state management
@GuardedBy("mLock")
private final SparseArray<UserState> mUserStates = new SparseArray<>();
@GuardedBy("mLock")
private final Map<String, SessionState> mSessionIdToSessionStateMap =
new HashMap<>();
private HdmiControlManager mHdmiControlManager = null;
private HdmiTvClient mHdmiTvClient = null;
private MediaQualityManager mMediaQualityManager = null;
}
The service constructor initializes the HDMI-CEC integration:
// frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java
public TvInputManagerService(Context context) {
super(context);
mTvInputHardwareManager = new TvInputHardwareManager(context,
new HardwareListener());
mHdmiControlManager = mContext.getSystemService(HdmiControlManager.class);
if (mHdmiControlManager != null) {
mHdmiTvClient = mHdmiControlManager.getTvClient();
}
// ...
}
@Override
public void onStart() {
publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
// Register for CEC active source management:
// Monitors SCREEN_ON/SCREEN_OFF to claim active source status
}
When the TV wakes up, the service sends a delayed message to claim CEC active source status. This message is cancelled if the TV switches inputs or goes back to sleep, preventing unnecessary CEC traffic.
62.2.4 TvInputHardwareManager¶
TvInputHardwareManager bridges the framework with physical TV input hardware. It manages
HDMI port connections, routes audio/video, and tracks hardware-backed TV inputs:
// frameworks/base/services/core/java/com/android/server/tv/TvInputHardwareManager.java
class TvInputHardwareManager implements TvInputHal.Callback {
private final TvInputHal mHal = new TvInputHal(this);
@GuardedBy("mLock")
private final SparseArray<Connection> mConnections = new SparseArray<>();
@GuardedBy("mLock")
private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
@GuardedBy("mLock")
private final List<HdmiDeviceInfo> mHdmiDeviceList = new ArrayList<>();
/* A map from a device ID to the matching TV input ID. */
@GuardedBy("mLock")
private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
/* A map from a HDMI logical address to the matching TV input ID. */
@GuardedBy("mLock")
private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
}
When an HDMI device is connected or disconnected, the hardware manager receives callbacks from the HDMI-CEC service and updates the input list accordingly. This enables automatic input source discovery -- when a user plugs in a Blu-ray player, it appears as a TV input without manual configuration.
62.2.5 Tuner Resource Manager¶
The TunerResourceManagerService arbitrates access to limited hardware tuner resources
(frontends, demuxes, LNBs, CAS sessions) among competing clients:
// frameworks/base/services/core/java/com/android/server/tv/
// tunerresourcemanager/TunerResourceManagerService.java
public class TunerResourceManagerService extends SystemService
implements IBinder.DeathRecipient {
public static final int INVALID_CLIENT_ID = -1;
private static final int MAX_CLIENT_PRIORITY = 1000;
}
The resource manager uses a priority system. Live TV viewing gets higher priority than background recording. When a higher-priority client needs a tuner that is already in use, the resource manager can reclaim it from the lower-priority client:
graph TB
subgraph "Tuner Resources"
FE["Frontend Resources<br/>DVB-T, DVB-S, ATSC, etc."]
DMX["Demux Resources<br/>TS demultiplexing"]
LNB["LNB Resources<br/>Satellite dish control"]
CAS["CAS Sessions<br/>Conditional access / DRM"]
CICAM["CI-CAM Resources<br/>Common Interface modules"]
end
subgraph "Clients by Priority"
LIVE["Live TV Playback<br/>Priority: HIGH"]
REC["Recording<br/>Priority: MEDIUM"]
SCAN["Channel Scan<br/>Priority: MEDIUM"]
BG["Background Tasks<br/>Priority: LOW"]
end
subgraph "Tuner Resource Manager"
TRM[TunerResourceManagerService]
PRI["Priority Arbitration<br/>foreground > background<br/>use-case hints"]
RCL["Resource Reclaim<br/>IResourcesReclaimListener"]
end
LIVE --> TRM
REC --> TRM
SCAN --> TRM
BG --> TRM
TRM --> PRI
PRI --> RCL
TRM --> FE
TRM --> DMX
TRM --> LNB
TRM --> CAS
TRM --> CICAM
Resource types managed by the service:
frameworks/base/services/core/java/com/android/server/tv/tunerresourcemanager/
FrontendResource.java -- Tuner frontend (demodulator) resources
DemuxResource.java -- Demultiplexer resources
LnbResource.java -- Low-noise block (satellite) resources
CasResource.java -- Conditional Access System resources
CiCamResource.java -- Common Interface CAM resources
ClientProfile.java -- Client registration and priority
UseCasePriorityHints.java -- Use-case to priority mapping
TunerResourceBasic.java -- Base resource class
62.2.6 HDMI-CEC¶
Consumer Electronics Control (CEC) allows HDMI-connected devices to control each other.
When you turn on a TV, CEC can automatically turn on the connected soundbar and switch
inputs. The CEC HAL is defined at:
hardware/interfaces/tv/cec/1.0/IHdmiCec.hal
interface IHdmiCec {
addLogicalAddress(CecLogicalAddress addr) generates (Result result);
clearLogicalAddress();
getPhysicalAddress() generates (Result result, uint16_t addr);
sendMessage(CecMessage message) generates (SendMessageResult result);
setCallback(IHdmiCecCallback callback);
getCecVersion() generates (int32_t version);
getVendorId() generates (uint32_t vendorId);
getPortInfo() generates (vec<HdmiPortInfo> infos);
setOption(OptionKey key, bool value);
setLanguage(string language);
enableAudioReturnChannel(int32_t portId, bool enable);
isConnected(int32_t portId) generates (bool status);
};
The Java-side CEC implementation lives in the HDMI control service:
frameworks/base/services/core/java/com/android/server/hdmi/
HdmiCecLocalDeviceTv.java -- TV-type CEC device implementation
HdmiCecLocalDevice.java -- Base CEC device
HdmiCecMessage.java -- CEC message representation
HdmiCecMessageBuilder.java -- Message construction helpers
HdmiControlService.java -- Main HDMI control service
HdmiCecStandbyModeHandler.java -- Standby mode CEC handling
ActiveSourceHandler.java -- Active source switching
DeviceSelectActionFromTv.java -- Device selection flow
RoutingControlAction.java -- Input routing
ArcInitiationActionFromAvr.java -- Audio Return Channel setup
NewDeviceAction.java -- New device discovery
The HdmiCecLocalDeviceTv represents the TV endpoint in CEC communication:
// frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// Whether ARC is available. "true" means ARC is established between
// TV and AVR as audio receiver.
@ServiceThreadOnly
private boolean mArcEstablished = false;
// Stores whether ARC feature is enabled per port.
private final SparseBooleanArray mArcFeatureEnabled = new SparseBooleanArray();
}
CEC message flow for "one-touch play" (user presses Play on a Blu-ray remote):
sequenceDiagram
participant BluRay as Blu-ray Player
participant CEC_BUS as CEC Bus
participant TV_HAL as TV CEC HAL
participant HDMI_SVC as HdmiControlService
participant TV_DEV as HdmiCecLocalDeviceTv
participant TIMS as TvInputManagerService
participant TVApp as TV Application
BluRay->>CEC_BUS: <Active Source> (physical addr)
BluRay->>CEC_BUS: <Image View On>
CEC_BUS->>TV_HAL: CEC message received
TV_HAL->>HDMI_SVC: onCecMessageReceived()
HDMI_SVC->>TV_DEV: handleActiveSource()
TV_DEV->>TIMS: Switch to HDMI input
TIMS->>TVApp: Input state changed
TVApp->>TVApp: Switch to Blu-ray input
Note over TV_DEV: If TV was in standby
TV_DEV->>TV_DEV: handleImageViewOn()
TV_DEV->>TV_DEV: Wake up display
62.2.7 TV HAL Interfaces¶
The complete TV HAL surface:
hardware/interfaces/tv/
input/ -- TV input hardware abstraction
cec/
1.0/ -- CEC HAL v1.0 (HIDL)
IHdmiCec.hal
IHdmiCecCallback.hal
types.hal
1.1/ -- CEC HAL v1.1 (HIDL, adds CEC 2.0)
IHdmiCec.hal
IHdmiCecCallback.hal
types.hal
hdmi/ -- HDMI connection management
tuner/ -- Digital TV tuner HAL (AIDL)
aidl/ -- Frontends, demuxes, filters, DVRs
mediaquality/ -- Media quality processing HAL
The Tuner HAL (AIDL-based) provides a comprehensive digital TV stack:
graph TB
subgraph "Tuner HAL Components"
FE["Frontend<br/>Demodulation<br/>DVB-T/S/C, ATSC,<br/>ISDB, DTMB"]
DMX["Demux<br/>TS Demultiplexing<br/>PID filtering"]
FILT["Filters<br/>Section, PES,<br/>Audio, Video,<br/>Record, Download"]
DVR["DVR<br/>Playback from<br/>file/Record to file"]
DESC["Descrambler<br/>CA decryption"]
LNB2["LNB<br/>Satellite dish<br/>control"]
end
FE --> DMX
DMX --> FILT
FILT --> DVR
DESC --> DMX
LNB2 --> FE
62.2.8 TV Picture-in-Picture (PIP)¶
Android TV has its own PIP implementation tailored for the big-screen experience. Unlike the phone PIP (which shows a small floating window), TV PIP places the secondary content in a fixed position appropriate for the lean-back experience:
// frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
public class TvPipController implements PipTransitionController.PipTransitionCallback,
TvPipBoundsController.PipBoundsListener, TvPipMenuController.Delegate,
DisplayController.OnDisplaysChangedListener, ConfigurationChangeListener,
UserChangeListener {
private static final String TAG = "TvPipController";
}
The TV PIP implementation consists of:
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/
TvPipController.java -- Main PIP controller for TV
TvPipBoundsState.java -- PIP window position/size state
TvPipBoundsAlgorithm.java -- Position calculation for TV layout
TvPipBoundsController.java -- Coordinates bounds changes
TvPipMenuController.java -- PIP overlay menu (play/close/etc.)
TvPipMenuView.java -- Menu visual layout
TvPipNotificationController.java -- Notification when PIP is active
TvPipTransition.java -- Animations for PIP enter/exit
TvPipAction.java -- PIP action definitions
TvPipCustomAction.java -- App-provided custom actions
TvPipActionsProvider.java -- Action list management
TvPipSystemAction.java -- System-level PIP actions
TvPipBackgroundView.java -- Dimmed background behind PIP
TvPipInterpolators.java -- Animation curves
TvPipMenuEduTextDrawer.java -- Educational tooltip rendering
The TvPipController maintains a clear state machine for PIP lifecycle:
// frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"STATE_"}, value = {
STATE_NO_PIP,
STATE_PIP,
STATE_PIP_MENU,
})
public @interface State {}
private static final int STATE_NO_PIP = 0; // No PIP window
private static final int STATE_PIP = 1; // PIP at normal position
private static final int STATE_PIP_MENU = 2; // PIP menu open, window centered
static final String ACTION_SHOW_PIP_MENU =
"com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU";
static final String ACTION_CLOSE_PIP =
"com.android.wm.shell.pip.tv.notification.action.CLOSE_PIP";
static final String ACTION_MOVE_PIP =
"com.android.wm.shell.pip.tv.notification.action.MOVE_PIP";
static final String ACTION_TOGGLE_EXPANDED_PIP =
"com.android.wm.shell.pip.tv.notification.action.TOGGLE_EXPANDED_PIP";
static final String ACTION_TO_FULLSCREEN =
"com.android.wm.shell.pip.tv.notification.action.FULLSCREEN";
The TV PIP state machine:
stateDiagram-v2
[*] --> STATE_NO_PIP
STATE_NO_PIP --> STATE_PIP: App enters PIP mode
STATE_PIP --> STATE_PIP_MENU: User presses select on PIP
STATE_PIP_MENU --> STATE_PIP: Menu dismissed
STATE_PIP_MENU --> STATE_NO_PIP: User closes PIP
STATE_PIP --> STATE_NO_PIP: App exits PIP
STATE_PIP_MENU --> [*]: ACTION_TO_FULLSCREEN returns to full screen
note right of STATE_PIP
Window at corner position,
D-pad left/right to select PIP,
Background content visible
end note
note right of STATE_PIP_MENU
Window moves to center,
Scaled up slightly,
Action buttons visible,
Background dimmed
end note
The controller collaborates with an extensive set of components. The constructor takes over 20 dependencies, demonstrating the complexity of TV PIP management:
// frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
private TvPipController(
Context context,
ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
PipDisplayLayoutState pipDisplayLayoutState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
PipTransitionState pipTransitionState,
PipAppOpsListener pipAppOpsListener,
PipTaskOrganizer pipTaskOrganizer,
PipTransitionController pipTransitionController,
TvPipMenuController tvPipMenuController,
PipMediaController pipMediaController,
TvPipNotificationController pipNotificationController,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayController displayController,
WindowManagerShellWrapper wmShellWrapper,
Handler mainHandler,
ShellExecutor mainExecutor) {
// ... initialization of all components
}
TV PIP key differences from phone PIP:
- Position is typically a fixed corner, not user-draggable
- Menu is accessed via D-pad, not touch gestures
- Background content dims to avoid visual competition
- Actions include media controls (play/pause) prominent in the menu
- Broadcast-based actions (
ACTION_SHOW_PIP_MENU,ACTION_CLOSE_PIP) allow the notification system to control PIP remotely
The TvPipModule provides Dagger dependency injection:
// frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
@Module(includes = {
WMShellBaseModule.class,
Pip1SharedModule.class})
public abstract class TvPipModule {
@WMSingleton
@Provides
static Optional<Pip> providePip(
Context context,
ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
// ... many dependencies
) { /* ... */ }
}
62.2.9 D-pad Navigation¶
Android TV uses D-pad (directional pad) navigation instead of touch. This fundamentally changes how focus management works in the framework. The key infrastructure:
-
Focus search algorithm:
View.focusSearch()usesFocusFinderto determine which view should receive focus when the user presses Up/Down/Left/Right. -
Touch mode: TV devices are always in "non-touch" mode. Views must handle focus state drawing (focused rings, highlights) explicitly.
-
BrowseFragment / Leanback library: The
androidx.leanbacklibrary provides pre-built UI components optimized for D-pad navigation:BrowseFragment,DetailsFragment,SearchFragment,PlaybackFragment. -
Sound feedback: D-pad presses trigger audible click sounds for spatial feedback.
graph LR
subgraph "D-pad Input Flow"
Remote["TV Remote<br/>D-pad"]
InputReader["InputReader<br/>KeyEvent"]
WMS["WindowManagerService<br/>Key dispatch"]
FocusedWindow["Focused Window<br/>ViewRootImpl"]
FocusFinder["FocusFinder<br/>Next focus calculation"]
NewFocus["New Focused View<br/>Highlight drawn"]
end
Remote --> InputReader
InputReader --> WMS
WMS --> FocusedWindow
FocusedWindow --> FocusFinder
FocusFinder --> NewFocus
62.2.10 TvSettings¶
Android TV uses a specialized settings application rather than the standard phone Settings app.
The TV Settings app (packages/apps/TvSettings/ -- typically vendor-specific) provides a
sidebar navigation pattern appropriate for D-pad control, with large text and high-contrast
visual design.
TV-specific settings include:
- Input management: HDMI input naming and ordering
- Display and Sound: Resolution, HDR, Audio output (HDMI ARC, Bluetooth, etc.)
- CEC controls: Enable/disable HDMI-CEC, one-touch play, system audio
- Screen saver (Daydream): Ambient mode displays (photos, clock, etc.)
- Accessibility: Large text, high contrast, TalkBack navigation
To let OEM TV apps such as TvSettings and tuner apps reach platform capabilities without
holding full platform-signature access, the TV form factor ships a thin system-API bridge
library, the java_sdk_library com.android.libraries.tv.tvsystem
(frameworks/opt/tv/tvsystem/), which exposes TV-flavored shims over hidden system APIs
such as TvUserManager, TvAudioManager, TvWifiManager, and TvPackageInstaller.
62.2.11 TV Interactive App Framework¶
The TV Interactive App framework extends TIF to support hybrid broadcast/broadband (HBB-TV), interactive advertisements, and two-screen experiences:
frameworks/base/services/core/java/com/android/server/tv/interactive/
TvInteractiveAppManagerService.java
This service manages TvInteractiveAppService instances that can overlay interactive content
on top of TV video streams, responding to both broadcast signals and internet data.
62.2.12 Media Quality HAL¶
The Media Quality HAL (hardware/interfaces/tv/mediaquality/) enables TV-specific video
processing features:
- Picture mode presets (Standard, Cinema, Vivid, Game)
- Dynamic backlight control
- Content-adaptive processing
- Ambient light backlight adjustment
// frameworks/base/media/java/android/media/quality/AmbientBacklightSettings.java
// -- TV ambient backlight configuration
// frameworks/base/media/java/android/media/quality/AmbientBacklightEvent.java
// -- Ambient backlight event notifications
62.3 Wear OS¶
Wear OS adapts Android for wrist-worn devices with tiny circular displays, extreme battery constraints, and a UI paradigm centered on glanceable information. While much of Wear OS's proprietary implementation lives outside AOSP (in Google Play Services for Wear), the framework-level adaptations are visible in the base platform.
62.3.1 Round Display Support¶
The most visible Wear adaptation is support for circular displays. The framework provides several mechanisms:
Window Insets for Round Screens
The WindowInsets system reports whether the display is round via
WindowInsets.isRound(). Apps use this to adjust padding so content is not clipped
by the curved edges:
graph TB
subgraph "Round Display Handling"
Display["Round Display<br/>diameter: 390px"]
Insets["WindowInsets<br/>isRound()=true"]
WIC["WatchViewStub /<br/>BoxInsetLayout"]
SafeArea["Safe Content Area<br/>Inscribed square"]
end
Display --> Insets
Insets --> WIC
WIC --> SafeArea
The BoxInsetLayout (from the Wear support library) automatically applies insets to child
views, ensuring content stays within the inscribed rectangle of a circular display. Without
this, content at the edges would be clipped or unreadable.
Configuration Reporting
The framework reports Configuration.UI_MODE_TYPE_WATCH for Wear devices. This allows
apps and the system to branch behavior:
int uiMode = context.getResources().getConfiguration().uiMode;
boolean isWatch = (uiMode & Configuration.UI_MODE_TYPE_MASK)
== Configuration.UI_MODE_TYPE_WATCH;
Resource qualifiers (-watch, -round, -notround) enable dimension, layout, and drawable
overrides per display shape.
62.3.2 Ambient Mode and Always-On Display¶
Wear devices support an ambient mode where the watch face continues to be visible but in a low-power state. This involves:
- Reduced refresh rate: The display drops to 1 Hz or lower update rate.
-
Limited color palette: The screen switches to a grayscale or limited-color mode to reduce OLED pixel power consumption.
-
Simplified rendering: Watch faces switch from full-color interactive mode to a simplified ambient rendering.
The AmbientModeSupport class (from the Wear support library) provides the lifecycle
callbacks:
stateDiagram-v2
[*] --> Interactive: Wrist raise / tap
Interactive --> Ambient: Timeout / wrist down
Ambient --> Interactive: Wrist raise / tap
Ambient --> Off: Extended inactivity
Off --> Interactive: Button press / wrist raise
note right of Interactive
Full color rendering,
Full frame rate,
Touch input active,
Sensor sampling active
end note
note right of Ambient
Simplified rendering,
1 Hz refresh,
Grayscale / low color,
No touch input,
Reduced sensor sampling
end note
62.3.3 Burn-in Protection¶
OLED displays on watches are susceptible to burn-in if static pixels remain illuminated continuously. The framework implements burn-in protection through:
-
Pixel shifting: In ambient mode, the entire display content shifts by a few pixels periodically (every minute or so). This is handled at the WindowManager level.
-
Outline-only rendering: Watch faces in ambient mode use outlined digits rather than filled shapes, reducing the number of lit pixels.
-
Low-bit ambient: Some displays support a true low-bit mode where each pixel is either fully on or fully off (no anti-aliasing), further reducing burn-in risk.
The watch face framework exposes burn-in protection information via WatchFaceService:
graph LR
subgraph "Burn-in Protection Strategies"
PS["Pixel Shift<br/>Content moves<br/>periodically"]
OL["Outline Rendering<br/>No solid fills<br/>in ambient"]
LB["Low-bit Mode<br/>1-bit per pixel<br/>no anti-aliasing"]
TC["Time Limiting<br/>Screen off after<br/>extended ambient"]
end
subgraph "Implementation Points"
WM["WindowManager<br/>Applies pixel offset"]
WFS["WatchFaceService<br/>Ambient drawing mode"]
DP["Display Policy<br/>Screen timeout"]
end
PS --> WM
OL --> WFS
LB --> WFS
TC --> DP
62.3.4 Watch Face Framework¶
Watch faces are the most distinctive Wear UI element. The framework defines a
WatchFaceService that extends WallpaperService to provide always-visible, continuously
updating face rendering:
The watch face lifecycle:
sequenceDiagram
participant WFS as WatchFaceService
participant Engine as Engine (WallpaperService.Engine)
participant Canvas as Canvas / GL Surface
participant WM as WindowManager
participant AMS as AmbientModeSupport
Note over WFS: Service starts
WFS->>Engine: onCreateEngine()
Engine->>Canvas: onSurfaceCreated()
Engine->>Engine: onDraw() [interactive mode]
Note over AMS: User lowers wrist
AMS->>Engine: onEnterAmbient(burnInProtection)
Engine->>Canvas: Draw simplified face
Engine->>Engine: Reduce update frequency to 1/min
Note over AMS: User raises wrist
AMS->>Engine: onExitAmbient()
Engine->>Canvas: Draw full interactive face
Engine->>Engine: Resume normal update frequency
Note over WM: Burn-in protection active
WM->>WM: Apply pixel shift offset
Watch face complications (small data displays showing weather, steps, battery, etc.) are provided through the Complication API:
graph TB
subgraph "Watch Face Complications"
WF["Watch Face<br/>WatchFaceService"]
CP1["Complication Provider 1<br/>Weather"]
CP2["Complication Provider 2<br/>Step Count"]
CP3["Complication Provider 3<br/>Battery"]
CP4["Complication Provider 4<br/>Next Calendar Event"]
end
subgraph "Complication Types"
SHORT["SHORT_TEXT<br/>72F"]
LONG["LONG_TEXT<br/>Meeting at 2:00 PM"]
ICON["ICON<br/>Small icon"]
RANGE["RANGED_VALUE<br/>Progress arc"]
IMG["SMALL_IMAGE<br/>Photo or icon"]
end
WF --> CP1
WF --> CP2
WF --> CP3
WF --> CP4
CP1 --> SHORT
CP2 --> SHORT
CP3 --> RANGE
CP4 --> LONG
62.3.5 Tiles API¶
Wear OS Tiles provide glanceable information surfaces that users swipe between from the
watch face. Unlike full activities, Tiles are declaratively defined using a layout DSL and
updated by a TileService:
graph LR
subgraph "Tiles Architecture"
TS["TileService<br/>Provider app"]
TR["TileRenderer<br/>Layout rendering"]
TH["Tile Host<br/>System UI"]
end
subgraph "Tile Lifecycle"
REQ["onTileRequest()"]
RES["onResourcesRequest()"]
UPD["Update interval<br/>or user swipe"]
end
TH --> REQ
REQ --> TS
TS --> RES
RES --> TR
TR --> TH
UPD --> REQ
Tiles are built using a protobuf-based layout schema:
- LayoutElement: Row, Column, Box, Spacer, Image, Text, Arc
- TimelineEntry: Tiles can define time-based layouts that automatically switch
- Clickable: Elements can trigger actions (launch activity, send message)
62.3.6 Reduced Windowing¶
Wear OS significantly simplifies the windowing system compared to phone:
-
Single-task model: Only one activity is visible at a time. There is no split-screen, freeform, or PIP support.
-
No navigation bar: The system back gesture is handled by the physical button or a swipe gesture, not an on-screen button.
-
Simplified recent apps: The recent apps list is either absent or a simple vertical scroll, not the full phone-style overview.
-
Reduced display areas: No status bar in the traditional sense. Notifications appear as cards swiped in from the bottom.
graph TB
subgraph "Phone Windowing"
PStatusBar[Status Bar]
PContent[App Content Area]
PNavBar[Navigation Bar]
PSplit[Split Screen Support]
PPip[PIP Support]
PFreeform[Freeform Windows]
end
subgraph "Wear Windowing"
WFace["Watch Face<br/>always behind"]
WContent["Single App<br/>full screen"]
WNotif["Notification Cards<br/>swipe up"]
WTiles["Tiles<br/>swipe left/right"]
end
subgraph "Simplifications"
NoSplit[No split screen]
NoPip[No PIP]
NoFreeform[No freeform]
NoNavBar[No navigation bar]
end
62.3.7 Battery Optimization for Wearables¶
Wear OS employs aggressive battery optimization beyond standard Android:
-
Doze on wrist-down: When the accelerometer detects the wrist is lowered, the device enters a doze-like state much faster than a phone would.
-
Network efficiency: Wearable devices preferentially route network requests through a connected phone (Bluetooth proxy) rather than using their own Wi-Fi or cellular radio, saving significant power.
-
Sensor batching: Sensors batch readings and deliver them in bursts rather than continuously, allowing the processor to sleep between batches.
-
Reduced background activity:
JobSchedulerconstraints are tighter on Wear. Fewer concurrent background services are allowed. -
Bedtime mode: A special mode that disables always-on display, notifications, and tilt-to-wake during sleep hours.
graph TB
subgraph "Wear Battery Optimization Stack"
subgraph "Hardware Level"
OLED["OLED Display<br/>Per-pixel power control"]
ULP["Ultra-Low-Power<br/>Co-processor"]
BLE["Bluetooth LE<br/>Low-energy comms"]
end
subgraph "Framework Level"
AOD["Always-On Display<br/>1Hz update, grayscale"]
BIP["Burn-in Protection<br/>Pixel shifting"]
DOZE["Aggressive Doze<br/>Wrist-down trigger"]
BATCH["Sensor Batching<br/>Periodic bulk delivery"]
PROXY["BT Network Proxy<br/>Route through phone"]
end
subgraph "App Level"
COMP["Complications<br/>Push updates, not poll"]
TILES["Tiles<br/>Declarative, no Activity"]
AMBI["Ambient Mode<br/>Simplified rendering"]
end
end
OLED --> AOD
ULP --> DOZE
BLE --> PROXY
AOD --> AMBI
DOZE --> BATCH
62.3.8 Wear-Specific Resource Qualifiers and Configuration¶
Wear devices use a comprehensive set of resource qualifiers for adapting layouts:
| Qualifier | Values | Purpose |
|---|---|---|
-watch |
N/A | Applied to watch devices |
-round |
N/A | Round display shape |
-notround |
N/A | Square or rectangular display |
UI_MODE_TYPE_WATCH |
6 | Configuration UI mode |
smallestScreenWidthDp |
~180-220dp | Typical watch screen sizes |
The framework reports several watch-specific configuration values:
// Configuration checks in framework code:
boolean isWatch = (config.uiMode & Configuration.UI_MODE_TYPE_MASK)
== Configuration.UI_MODE_TYPE_WATCH;
// Screen shape check:
boolean isRound = config.isScreenRound();
// Typical watch display metrics:
// 390x390 pixels at ~300+ dpi for round
// 320x320 pixels at ~280 dpi for smaller models
Layout adaptations for round displays follow a specific pattern:
graph TB
subgraph "Round Display Layout Strategy"
subgraph "Full Circle"
FC["Total display area<br/>pi * r^2"]
end
subgraph "Safe Rectangle"
SR["Inscribed square<br/>side = diameter / sqrt(2)<br/>~70.7% of diameter"]
end
subgraph "Content Zones"
CZ1["Center: primary content<br/>Full readable area"]
CZ2["Edges: decorative only<br/>Arc progress, bezels"]
end
end
FC --> SR
SR --> CZ1
FC --> CZ2
Apps targeting Wear must account for the ~30% of screen area near the edges of a round
display being partially clipped. The BoxInsetLayout and curved text APIs help manage
this constraint automatically.
62.3.9 Wearable Sensing Framework¶
AOSP includes a framework for wearable-specific sensing capabilities:
frameworks/base/services/core/java/com/android/server/wearable/
WearableSensingManagerService.java -- System service
WearableSensingManagerPerUserService.java -- Per-user management
RemoteWearableSensingService.java -- Remote service connection
WearableSensingSecureChannel.java -- Secure data channel
WearableSensingShellCommand.java -- Debug shell commands
// frameworks/base/core/java/android/app/wearable/WearableSensingManager.java
// -- Client API for wearable sensing
// frameworks/base/core/java/android/service/wearable/WearableSensingService.java
// -- Service that processes wearable sensor data
The wearable sensing framework provides a secure channel for processing sensitive health and activity data from wearable sensors. It supports:
- Accelerometer and gyroscope data for activity recognition
- Heart rate and SpO2 monitoring
- Fall detection algorithms
- Context-aware ambient computing
graph TB
subgraph "Wearable Sensing Architecture"
Sensors["Wearable Sensors<br/>Accel, Gyro, HR, SpO2"]
WSS["WearableSensingService<br/>On-device processing"]
SC["Secure Channel<br/>WearableSensingSecureChannel"]
WSMS["WearableSensingManagerService<br/>System service"]
WSM["WearableSensingManager<br/>Client API"]
App[Health/Fitness App]
end
Sensors --> WSS
WSS --> SC
SC --> WSMS
WSMS --> WSM
WSM --> App
62.4 Form Factor Customization Points¶
The key architectural insight across all three form factors is that AOSP does not use
compile-time #ifdef branching. Instead, customization is achieved through runtime
overlays, Dagger module substitution, product configuration, and abstraction layers. This
section catalogs the specific customization points.
62.4.1 SystemUI Variants¶
SystemUI is the most visibly customized component. AOSP provides three variants:
frameworks/base/packages/SystemUI/ -- Phone/tablet SystemUI (default)
packages/apps/Car/SystemUI/ -- Automotive SystemUI
(vendor-specific)/TvSystemUI/ -- TV SystemUI (vendor-provided)
The phone SystemUI is the default and most feature-rich. Car SystemUI replaces it entirely with automotive-specific UI components. TV SystemUI is typically much simpler, focusing on a minimal notification system and settings access.
The selection is made at build time through product configuration:
# For automotive builds:
PRODUCT_PACKAGES += CarSystemUI
# Instead of the default:
# PRODUCT_PACKAGES += SystemUI
62.4.2 WMShell Module Variants¶
The Window Manager Shell provides Dagger module variants for different form factors. The base module is shared, with form-factor-specific modules layered on top:
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/
WMShellBaseModule.java -- Shared dependencies (all form factors)
WMShellModule.java -- Phone/tablet specific
TvWMShellModule.java -- TV specific
WMShellConcurrencyModule.java -- Thread pool configuration
The TvWMShellModule substitutes TV-specific implementations:
// frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/
// TvWMShellModule.java
@Module(includes = {TvPipModule.class})
public class TvWMShellModule {
@WMSingleton
@Provides
@DynamicOverride
static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() {
return new TvStartingWindowTypeAlgorithm();
}
@WMSingleton
@Provides
@DynamicOverride
static SplitScreenController provideSplitScreenController(/* ... */) {
return new TvSplitScreenController(/* ... */);
}
}
Key substitutions made by TvWMShellModule:
| Component | Phone | TV |
|---|---|---|
| StartingWindowTypeAlgorithm | Default | TvStartingWindowTypeAlgorithm |
| SplitScreenController | SplitScreenController | TvSplitScreenController |
| PIP | Pip1Module / Pip2Module | TvPipModule |
| PIP Controller | PipController | TvPipController |
| PIP Bounds | PipBoundsAlgorithm | TvPipBoundsAlgorithm |
graph TB
subgraph "WMShell Module Architecture"
BASE["WMShellBaseModule<br/>Shared infrastructure"]
subgraph "Form Factor Modules"
PHONE["WMShellModule<br/>Phone/Tablet"]
TV["TvWMShellModule<br/>TV"]
AUTO["Car uses separate<br/>SystemUI entirely"]
end
subgraph "PIP Modules"
PIP1["Pip1Module<br/>Phone PIP v1"]
PIP2["Pip2Module<br/>Phone PIP v2"]
TVPIP["TvPipModule<br/>TV PIP"]
end
end
BASE --> PHONE
BASE --> TV
PHONE --> PIP1
PHONE --> PIP2
TV --> TVPIP
62.4.3 Device Overlays (Runtime Resource Overlays)¶
Runtime Resource Overlays (RROs) are the primary mechanism for visual and behavioral customization without source code changes. Each form factor uses RROs extensively:
Automotive RROs:
packages/services/Car/car_product/rro/
CarSystemUIRRO/ -- SystemUI visual overrides
DriveModeSportRRO/ -- Sport mode visuals
DriveModeEcoRRO/ -- Eco mode visuals
overlay-config/
androidRRO/ -- Framework defaults
SettingsProviderRRO/ -- Settings provider defaults
CertInstallerRRO/ -- Certificate installer
TelecommRRO/ -- Telecom UI
oem-design-tokens/
OEMDesignTokenRRO/ -- OEM design system tokens
OEMDesignTokenFrameworkResRRO/ -- Framework token overlays
OEMDesignTokenCarUiPluginRRO/ -- Car UI plugin tokens
Distant Display RROs (for secondary screens):
packages/services/Car/car_product/distant_display/rro/
distant_display_rro.mk
MediashellRRO/
CarServiceRRO/
DriverUiRRO/
RROs work by overlaying resource values at runtime without modifying the target APK. An overlay package declares which target package and resources it overrides:
<!-- Example: CarSystemUIRRO/AndroidManifest.xml -->
<manifest>
<overlay android:targetPackage="com.android.systemui"
android:isStatic="true"
android:priority="10" />
</manifest>
The overlay can then replace any resource -- colors, dimensions, layouts, strings, booleans -- in the target package. This is how OEMs customize the look and feel of the car UI without forking SystemUI source code.
62.4.4 Product Configuration¶
Product configuration is where form-factor selection begins. The build system reads makefile variables to determine which packages, overlays, and properties to include.
Automotive product configuration:
# packages/services/Car/car_product/build/car_base.mk
# packages/services/Car/car_product/build/car.mk
# Key automotive properties:
PRODUCT_PROPERTY_OVERRIDES += \
ro.hardware.type=automotive
# Key automotive packages:
PRODUCT_PACKAGES += \
CarService \
CarSystemUI \
CarLauncher \
CarSettings
The car_product/build/ directory hierarchy:
| File | Purpose |
|---|---|
car.mk |
Common packages for all car builds |
car_base.mk |
Base product definition |
car_product.mk |
Full product packages |
car_system.mk |
System partition packages |
car_system_ext.mk |
System extension packages |
car_vendor.mk |
Vendor partition packages |
car_generic_system.mk |
Generic system image |
TV product configuration typically includes:
# (vendor-specific or device-specific makefile)
PRODUCT_PROPERTY_OVERRIDES += \
ro.hardware.type=tv
PRODUCT_PACKAGES += \
TvSettings \
TvSystemUI \
TvProvider \
TvLauncher
Wear product configuration typically includes:
# (vendor-specific)
PRODUCT_PROPERTY_OVERRIDES += \
config.override_forced_orient=true \
config.override_forced_orient_value=0
# Watch-specific features
PRODUCT_PACKAGES += \
WearSettings \
ClockworkHome \
WearSystemUI
62.4.5 Feature Flags and Configuration¶
Beyond properties and overlays, form-factor behavior is controlled through:
- PackageManager feature flags: Each form factor declares features in
system/etc/ permissions/:
<!-- Automotive -->
<feature name="android.hardware.type.automotive" />
<!-- TV -->
<feature name="android.software.leanback" />
<feature name="android.hardware.type.television" />
<!-- Wear -->
<feature name="android.hardware.type.watch" />
- Config resources:
frameworks/base/core/res/res/values/config.xmlcontains hundreds of configurable values. Form-factor overlays change these:
<!-- Example: config_supportsPictureInPicture -->
<!-- Phone: true, Watch: false -->
<!-- config_hasAutomotiveDock: true for automotive -->
- SELinux policies: Each form factor has specific SELinux policies:
62.4.6 How OEMs Customize Per Form Factor¶
The OEM customization stack for any form factor follows a layered pattern:
graph TB
subgraph "Customization Stack (Bottom to Top)"
AOSP["AOSP Base<br/>frameworks/base, packages/"]
FF["Form Factor Layer<br/>packages/services/Car/<br/>TV/Wear framework additions"]
PROD["Product Configuration<br/>device/vendor/product.mk<br/>Package selection, properties"]
RRO["Runtime Resource Overlays<br/>Visual customization<br/>Default value overrides"]
OEM_APK["OEM Replacement APKs<br/>Custom Launcher, SystemUI<br/>Custom Settings"]
VENDOR["Vendor Partition<br/>HAL implementations<br/>Proprietary services"]
end
AOSP --> FF
FF --> PROD
PROD --> RRO
RRO --> OEM_APK
OEM_APK --> VENDOR
Specific OEM customization patterns by form factor:
Automotive OEM Customization:
- Custom
IVehicleHAL implementation mapping to their specific ECU protocol - Custom instrument cluster renderer bound by
InstrumentClusterService - Custom HVAC control panel via RRO on Car SystemUI
- Custom car launcher with brand-specific widgets
- OEM design tokens for brand-consistent visual identity
- Custom audio routing through AudioControl HAL
TV OEM Customization:
- Custom
TvInputServiceimplementations for proprietary tuner hardware - Custom TV launcher with brand-specific content recommendations
- Custom CEC behavior for their specific device ecosystem
- Picture quality processing via Media Quality HAL
- Custom remote control integration
Wear OEM Customization:
- Custom watch face packs
- Custom sensor implementations for health features
- Custom tiles for device-specific features
- Battery optimization tuning for specific hardware
- Custom complications providers for device sensors
62.4.7 Multi-Display Architecture Across Form Factors¶
Multi-display support varies dramatically across form factors:
graph TB
subgraph "Phone"
PD1[Primary Display]
PD2[Optional: Cast / External]
end
subgraph "Automotive"
AD1[Driver Main Display]
AD2[Instrument Cluster]
AD3[Passenger Display]
AD4[Rear Seat Left]
AD5[Rear Seat Right]
AD6[HUD Display]
end
subgraph "TV"
TD1["Main TV Output<br/>HDMI"]
TD2["Optional PIP<br/>Same display, separate task"]
end
subgraph "Wear"
WD1["Single Round Display<br/>Always-on capable"]
end
Automotive has the most complex multi-display needs, which is why the occupant zone system exists exclusively in the Car framework. TV handles multi-content through PIP on a single display. Wear has the simplest model with a single, small display.
62.4.8 Service Registration Differences¶
Each form factor registers different system services during SystemServer startup:
graph TB
subgraph "Common Services (All Form Factors)"
AMS[ActivityManagerService]
WMS[WindowManagerService]
PMS[PackageManagerService]
IPMS[InputMethodManagerService]
NMS[NotificationManagerService]
end
subgraph "Automotive Additional Services"
CS["CarService<br/>~40 internal services"]
OAS[OccupantAwarenessService]
VHAL_S[Vehicle HAL Service]
end
subgraph "TV Additional Services"
TIMS2[TvInputManagerService]
TRMS2[TunerResourceManagerService]
HDMI[HdmiControlService]
end
subgraph "TV Optional Library Services<br/>(com.android.libraries.tv.tvsystem)"
TVWS["TvWatchdogService<br/>(not started by SystemServer)"]
end
subgraph "Wear Additional Services"
WSMS2[WearableSensingManagerService]
ACM[AmbientContextManagerService]
end
The services are conditionally started based on device features:
// Automotive: started when ro.hardware.type == automotive
// CarService is started as a persistent service
// TV: started when this is a TV device (isTv = leanback feature) OR
// the FEATURE_LIVE_TV feature is present -- NOT gated on FEATURE_LEANBACK alone
// (frameworks/base/services/java/com/android/server/SystemServer.java)
if (isTv || pm.hasSystemFeature(PackageManager.FEATURE_LIVE_TV)) {
mSystemServiceManager.startService(TvInputManagerService.class);
}
// HDMI-CEC: started on TV devices
if (pm.hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
// HdmiControlService starts
}
// TunerResourceManagerService: gated on FEATURE_TUNER
if (pm.hasSystemFeature(PackageManager.FEATURE_TUNER)) {
// TunerResourceManagerService starts
}
62.4.9 Input Model Differences¶
Each form factor has a fundamentally different input model:
graph TB
subgraph "Phone Input"
Touch[Multi-touch Screen]
Gesture["System Gestures<br/>Back, Home, Recents"]
VoiceP[Voice Assistant]
end
subgraph "Automotive Input"
TouchA["Touchscreen<br/>Driver + Passenger"]
Rotary["Rotary Controller<br/>Knob input"]
Steering[Steering Wheel Buttons]
VoiceA["Voice Assistant<br/>Primary while driving"]
end
subgraph "TV Input"
DPad["D-pad Remote<br/>Up/Down/Left/Right/Select"]
VoiceT[Voice Remote]
GamePad[Game Controller]
Mouse["Air Mouse / Pointer<br/>Optional"]
end
subgraph "Wear Input"
TouchW["Touch Screen<br/>Limited due to size"]
Crown["Rotary Side Button<br/>Scroll/Navigation"]
Buttons["Physical Buttons<br/>Back, Home"]
WristG[Wrist Gestures]
end
For automotive, the RotaryController app (packages/apps/Car/RotaryController/) handles
rotary knob input, translating it into focus navigation similar to D-pad on TV. The
CarInputService in CarService manages input routing across occupant zones:
62.5 Android 17: Automotive Windowing, Visibility Barriers, and the Road to SDV¶
Android 17 invests heavily in the automotive form factor. Most of the new code in
packages/services/Car/ for this release is not about new vehicle properties or new HALs --
it is about windowing. AAOS head units increasingly run several apps side by side on a single
large display (a map next to a media player next to a climate panel), drive multiple physical
displays for multiple occupants, and present each display through an OEM-authored, fully
configurable layout. The phone WindowManager Shell was never designed for any of this, so 17
ships a dedicated automotive shell library and a declarative panel framework on top of it. This
section walks the three pieces that landed for 17 -- the Car-WindowManager-Shell library, the
auto visibility barrier, and the Scalable UI panel framework -- and then points to where the
larger Software Defined Vehicle (SDV) story is told.
62.5.1 The Car WindowManager Shell Library¶
Phone and tablet windowing is built from frameworks/base/libs/WindowManager/Shell/
(WMShellModule, WMShellBaseModule), as Section 62.4.2 described. Automotive needs a different
model: every container is a multi-window root task (there is no single fullscreen task that owns
the display), containers must be layered and bounded explicitly by the system UI, and a container
must be hideable without painting an opaque activity on top of it. Android 17 factors this into a
standalone library, Car-WindowManager-Shell, declared in
packages/services/Car/libs/car-wm-shell-lib/Android.bp. Its public surface is small and lives
under packages/services/Car/libs/car-wm-shell-lib/src/com/android/wm/shell/automotive/.
The central abstraction is the AutoTaskStack -- a task stack that is always in multi-window
mode -- and its concrete form, the RootTaskStack:
// packages/services/Car/libs/car-wm-shell-lib/src/com/android/wm/shell/automotive/
// AutoTaskStack.kt
/**
* Represents an auto task stack, which is always in multi-window mode.
*/
interface AutoTaskStack {
val id: Int
val displayId: Int
var leash: SurfaceControl
val name: String
}
data class AutoTaskStackState(
val bounds: Rect = Rect(),
val isAboveBarrier: Boolean,
val layer: Int
)
data class RootTaskStack(
override val id: Int,
override val displayId: Int,
override var leash: SurfaceControl,
override val name: String,
var rootTaskInfo: ActivityManager.RunningTaskInfo
) : AutoTaskStack
Two fields in AutoTaskStackState are the heart of the new model. layer gives the system UI
explicit Z-order control over containers (the phone shell mostly relies on activity order).
isAboveBarrier ties the container to the visibility barrier described in Section 62.5.2: a
stack below the barrier is hidden by WindowManager without any occluding surface. The bounds
field lets the system UI place and resize each container deterministically, which is what makes
fixed multi-pane car layouts possible.
Clients drive the shell through the AutoTaskStackController interface:
// packages/services/Car/libs/car-wm-shell-lib/src/com/android/wm/shell/automotive/
// AutoTaskStackController.kt
interface AutoTaskStackController {
var autoTransitionHandlerDelegate: AutoTaskStackTransitionHandlerDelegate?
val taskStackStateMap: Map<Int, AutoTaskStackState>
fun createRootTaskStack(
displayId: Int,
name: String,
listener: RootTaskStackListener
): RootTaskStack?
fun destroyTaskStack(taskStackId: Int)
fun setDefaultRootTaskStackOnDisplay(displayId: Int, rootTaskStackId: Int?)
@ShellMainThread
fun startTransition(transaction: AutoTaskStackTransaction): IBinder?
}
The controller follows a transaction-and-transition discipline rather than imperative window
moves. A caller composes an AutoTaskStackTransaction (reparent a task into a stack, change a
stack's AutoTaskStackState, send a PendingIntent into a stack) and calls startTransition;
WindowManager then drives the change through a normal Shell transition, calling back into the
caller's AutoTaskStackTransitionHandlerDelegate (handleRequest, startAnimation,
onTransitionConsumed, mergeAnimation) so the animation stays in sync with the underlying
window operations. setDefaultRootTaskStackOnDisplay registers a stack as the launch root for a
display, so newly started activities are routed into a managed container instead of going
fullscreen.
Container lifecycle is reported through RootTaskStackListener, which adapts
ShellTaskOrganizer.TaskListener so that callbacks split cleanly between the root container and
its child tasks:
// packages/services/Car/libs/car-wm-shell-lib/src/com/android/wm/shell/automotive/
// RootTaskStackListener.kt
interface RootTaskStackListener : ShellTaskOrganizer.TaskListener {
fun onRootTaskStackAppeared(rootTaskStack: RootTaskStack) {}
fun onRootTaskStackInfoChanged(rootTaskStack: RootTaskStack) {}
fun onRootTaskStackDestroyed(rootTaskStack: RootTaskStack) {}
}
On the CarService side, CarActivityService grew its own lightweight RootTaskListener
interface plus registerRootTaskListener/unregisterRootTaskListener so that automotive system
services can observe root-task appear/vanish events without depending on the shell library
directly:
// packages/services/Car/service/src/com/android/car/am/CarActivityService.java
/** Listener for root task callbacks. */
public interface RootTaskListener {
void onRootTaskVanished(String name);
void onRootTaskAppeared(String name);
}
The library and shell architecture:
graph TB
subgraph "System UI Process"
DELEG["AutoTaskStackTransitionHandlerDelegate<br/>(implemented by Car SystemUI)"]
CTRL["AutoTaskStackController<br/>car-wm-shell-lib"]
REPO["AutoTaskRepository<br/>tracks stacks + barrier tokens"]
BAR["AutoVisibilityBarrierController"]
end
subgraph "WindowManager / Shell Core"
STO["ShellTaskOrganizer"]
TR["Transitions"]
WM["WindowManagerService"]
end
subgraph "Containers on a Display"
RTS1["RootTaskStack A<br/>isAboveBarrier=true, layer=2"]
RTS2["RootTaskStack B<br/>isAboveBarrier=true, layer=1"]
BARRIER["Visibility Barrier Task<br/>(empty, Shell-organized)"]
RTS3["RootTaskStack C<br/>isAboveBarrier=false (hidden)"]
end
DELEG --> CTRL
CTRL --> STO
CTRL --> TR
BAR --> STO
BAR --> REPO
CTRL --> REPO
STO --> WM
TR --> WM
CTRL --> RTS1
CTRL --> RTS2
BAR --> BARRIER
CTRL --> RTS3
62.5.2 The Auto Visibility Barrier¶
A recurring automotive problem is hiding a container reliably. On phones a task is hidden because
something opaque covers it; in a multi-pane car layout there may be nothing opaque to cover a pane
you want to dismiss, and leaving it visible underneath is both a UX and a driver-distraction
problem. Android 17 solves this with the visibility barrier, implemented by
AutoVisibilityBarrierController under
packages/services/Car/libs/car-wm-shell-lib/src/com/android/wm/shell/automotive/visibilitybarrier/.
The mechanism is deliberately simple. The controller creates one empty, Shell-organized task per
display -- the barrier -- and relies on a WindowManager rule that siblings ordered below the
barrier are made invisible. Any container can therefore be hidden by ordering it under the
barrier (its AutoTaskStackState.isAboveBarrier becomes false) with no occluding activity
required. The class documentation states the contract directly:
// packages/services/Car/libs/car-wm-shell-lib/src/com/android/wm/shell/automotive/
// visibilitybarrier/AutoVisibilityBarrierController.kt
/**
* Controller to manage the visibility barrier for each display.
*
* The visibility barrier is an empty Shell-organized task. Siblings below the visibility barrier
* are made invisible by the WindowManager. This provides a reliable mechanism to hide tasks
* without needing to occlude them with a fullscreen visible activity.
*
* Note: A home activity can co-exist with the visibility barrier and either will not affect the
* other.
*/
@WMSingleton
class AutoVisibilityBarrierController @Inject constructor(/* ... */) :
DisplayController.OnDisplaysChangedListener, AutoShellInitializable {
override fun initialize() {
if (!Flags.enableAutoVisibilityBarrier()) {
return
}
// ...register for display add/remove and create a barrier per display
}
}
The controller listens for displays through DisplayController.OnDisplaysChangedListener and
maintains one barrier per display, recording each barrier's token in AutoTaskRepository and
dropping it on onTaskVanished. Like nearly every new windowing behavior in 17, it is gated by
an aconfig flag so OEMs and the platform can stage the rollout:
# packages/services/Car/libs/car-wm-shell-lib/aconfig/flags.aconfig
package: "com.android.wm.shell.automotive"
container: "system_ext"
flag {
name: "enable_mumd_car_wm_shell"
namespace: "car_framework"
description: "Enables proxy and host implementation in MUMD car-wm-shell for automotive"
}
flag {
name: "enable_auto_visibility_barrier"
namespace: "car_framework"
description: "Enables the dedicated auto visibility barrier controller on all displays"
}
The enable_mumd_car_wm_shell flag is the second half of the story: in a Multi-User
Multi-Display (MUMD) vehicle, the shell runs a proxy-and-host split so that the per-occupant
system UI processes can each drive their own display's containers through the same
AutoTaskStackController API.
62.5.3 Scalable UI: Declarative Car Panels¶
The shell library gives Car SystemUI primitives, but OEMs do not want to write transition code.
Android 17 layers a declarative Scalable UI framework over the shell so a head-unit layout is
described as a set of configurable panels with states, variants, and animations, rather than
imperative window calls. The framework spans two locations: the reusable model and panel library
at packages/apps/Car/systemlibs/car-scalable-ui-lib/ (package com.android.car.scalableui,
providing Event, PanelTransaction, Panel, and the variant/keyframe model), and the
SystemUI wiring at packages/apps/Car/SystemUI/src/com/android/systemui/car/wm/scalableui/.
The bridge class is PanelAutoTaskStackTransitionHandlerDelegate -- it implements the shell's
AutoTaskStackTransitionHandlerDelegate and translates WindowManager transitions into panel
events. Its companion pieces, documented in
packages/apps/Car/SystemUI/src/com/android/systemui/car/wm/scalableui/README.md, are:
| Component | Role |
|---|---|
PanelAutoTaskStackTransitionHandlerDelegate |
Bridge between Shell transitions and Scalable UI |
PanelTransitionCoordinator |
Orchestrates panel animations and state changes |
EventDispatcher |
Maps system events to PanelTransactions |
PanelConfigReader / ActionConfigReader |
Read declarative panel and action configuration |
TaskPanel / DecorPanel / SysUIPanel |
The panel container types |
The README draws the distinction that makes the framework efficient: window state (visibility,
size, position, Z-order) is heavyweight and goes through WindowManager via an
AutoTaskStackTransaction, while surface properties (alpha, scale, translation, crop) are
animated cheaply on SurfaceFlinger through AutoSurfaceTransaction. A pure surface change (for
example, fading a panel) never round-trips through WindowManager.
sequenceDiagram
participant Src as App launch / button event
participant WM as WindowManager / Shell
participant Delegate as PanelAutoTaskStackTransitionHandlerDelegate
participant Dispatch as EventDispatcher
participant Coord as PanelTransitionCoordinator
participant Surf as SurfaceFlinger
Src->>WM: Window change initiates transition
WM->>Delegate: handleRequest(transition, request)
Delegate->>Dispatch: Event from TransitionRequestInfo
Dispatch-->>Delegate: PanelTransaction
Delegate->>Coord: Build AutoTaskStackTransaction
Coord-->>Delegate: Transaction
Delegate-->>WM: Return transaction
WM->>Delegate: startAnimation(...)
Delegate->>Coord: Reconcile state + animate
Coord->>Surf: AutoSurfaceTransaction (alpha/scale/crop)
Scalable UI is where the secondary-display work also lands. The distant-display and driver/
distant-display ("dewd") system UIs wire the framework in explicitly -- for example
packages/services/Car/car_product/distant_display/apps/CarDistantDisplaySystemUI/ constructs its
initializer with setScalableUIWMInitializer(...) and setScalableUIEventDispatcher(...), and the
packages/services/Car/car_product/dewd/ product carries its own Scalable UI sample RROs. As with
the shell pieces, Scalable UI is staged behind flags so a given product can opt in per display
(and MUMD products deliberately disable it on the per-occupant path while it matures).
62.5.4 Cross-Reference: Software Defined Vehicle¶
The windowing work above is the part of the 17 automotive story that lives inside CarService and
Car SystemUI. The larger architectural shift in this release is the Software Defined Vehicle
(SDV) platform -- a separate, vehicle-spanning stack (under trees such as
system/software_defined_vehicle/, hardware/sdv/, and device/google/sdv*) that decouples
vehicle functions from fixed ECUs, runs services across virtualized domains, and adds a gateway
and middleware layer between Android and the rest of the car. SDV is a subsystem in its own right,
not a CarService feature, so it is covered in its own sections of this chapter: Section 62.7
walks the SDV platform architecture (the headless Core VM, the service-bundle model, and the
control-plane agents that supervise it), and Section 62.8 walks the SDV middleware and vehicle
communication fabric (VSIDL, the three middleware agents, SOME/IP, and the gateway).
For this chapter the takeaway is the boundary: CarService, the Vehicle HAL, occupant zones, and
the new Car-WindowManager-Shell/Scalable UI windowing stack remain the Android-side automotive
platform; the SDV sections (§62.7 and §62.8) pick up where the vehicle abstraction leaves off.
62.6 Android XR¶
Automotive, TV, and Wear are the form factors AOSP ships and supports as full vertical stacks today. Android 17 adds the first platform-level groundwork for a fourth one: Android XR, the head-worn headset and glasses form factor built on the Khronos OpenXR standard. It is worth understanding this groundwork precisely, because it is easy to overstate. The blunt version, which the rest of this section backs up file by file: Android XR is not merged into AOSP. Android 17 upstreams only the API-usage surface XR apps compile against -- the feature strings, manifest properties, tracking permissions and AppOps, the vendored OpenXR headers, and a few telemetry atom definitions -- not a working XR stack. There is no XR runtime, no OpenXR loader, no compositor, no scene/spatial SDK, no XR HAL, and no XR device or emulator target in the AOSP 17 tree. Those pieces live off-tree: a vendor runtime on the device implements the OpenXR ABI, and the separately-distributed Jetpack XR SDK gives apps the high-level scene API. The sections below walk what AOSP 17 actually contains -- and keep insisting on the distinction, because the upstream footprint is small enough to be mistaken for the whole thing.
62.6.1 What "Android XR" Is in AOSP 17 -- and What It Isn't¶
Almost the entire in-tree XR surface is hidden behind a single aconfig flag. The flag is declared
in frameworks/base/core/java/android/content/pm/xr.aconfig:
flag {
namespace: "xr"
name: "xr_manifest_entries"
description: "Adds manifest entries used by Android XR"
bug: "364416355"
is_exported: true
}
The flag is is_exported, so the @FlaggedApi("android.xr.xr_manifest_entries") annotation can be
applied to public API in frameworks/base. Its default value is disabled: there is no release
override for xr_manifest_entries anywhere under build/release, so on a stock AOSP 17 build the
flag stays off and the XR API surface, permissions, and manifest properties it gates are inert. A
device that actually ships XR turns the flag on through its release configuration.
What is not in the tree is everything that would make a headset boot:
- No XR HAL. There is no
hardware/interfaces/xr/and no XR HAL AIDL/HIDL package. - No XR runtime or OpenXR loader. The compositor and the loader that resolves the OpenXR ABI to a runtime live on the device, supplied by the vendor (see Section 62.6.2).
- No XR system service. There is no XR equivalent of
CarServiceorTvInputManagerServiceunderframeworks/base/services; nothing implements anandroid.xr.*runtime service. - No XR device, lunch target, or emulator. AOSP 17 has no XR reference device and no XR
emulator image. (Do not mistake
emu64xrfor one -- that is a RISC-V binary-translation emulator target, unrelated to XR; likewiseIDvris TV tuner,VrrConfigis variable refresh rate,RtcpXris VoIP statistics, anduxris an automotive UX-restrictions abbreviation. None of these are Android XR.)
The headset context for this groundwork surfaces only in telemetry: the statsd atom file
frameworks/proto_logging/stats/atoms/xr/recorder/xr_recorder_extension_atoms.proto logs XR
recorder state changes and, in its logging-source comment, references the "Moohan" headset that
the first Android XR devices are built around. The atom is a logging definition, not a runtime --
it is another instance of the platform reserving vocabulary ahead of the device.
The intended division of labor mirrors how the other form factors handle hardware-specific stacks: the platform defines the contract (the OpenXR ABI via vendored headers, the feature strings apps declare, the tracking permissions the framework enforces), a vendor runtime implements the OpenXR ABI on the device, and the off-tree Jetpack XR SDK gives apps a high-level scene API on top. Only the contract lives in AOSP 17. Put plainly: Android 17 upstreams the API-usage surface XR apps compile against, not a working XR stack.
How small that footprint really is shows up in the 16-to-17 changeset. Across that window AOSP
gains exactly one new XR project: external/openxr-sdk (the OpenXR SDK pinned to
release-1.1.50, covered in Section 62.6.2). The platform commits that mention "spatial,"
"head-tracking," or "passthrough" in the same window are almost all about spatial audio -- the
audio Spatializer (frameworks/av/services/audiopolicy) and frameworks/av/media/libheadtracking (Section 62.6.8) -- not about
XR. In other words, the XR platform-code footprint added in 17 is deliberately minimal: a vendored
header package and a flag-gated contract, with the runtime left entirely to the vendor.
62.6.2 The OpenXR SDK in the Tree¶
The one piece of XR code vendored into AOSP 17 is the Khronos OpenXR SDK at
external/openxr-sdk/. Its METADATA pins the version to release-1.1.50, and
external/openxr-sdk/include/openxr/openxr.h defines the matching API version:
#define XR_MAKE_VERSION(major, minor, patch) \
((((major) & 0xffffULL) << 48) | (((minor) & 0xffffULL) << 32) | ((patch) & 0xffffffffULL))
#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 50)
What matters for AOSP is what the build exposes. external/openxr-sdk/Android.bp declares exactly
one module:
That is the entire built surface -- the OpenXR headers, nothing more. The SDK's loader source
under external/openxr-sdk/src/loader/ is present in the checkout but is not built by
Android.bp: there is no cc_library for it. And grepping the whole AOSP 17 tree for modules that
depend on openxr_headers returns zero in-tree consumers -- no platform module compiles against
the OpenXR ABI.
Headers-only is the deliberate shape. The OpenXR ABI is a contract between an application (or a higher-level SDK) and a runtime: on a real headset the runtime and the loader that finds it are supplied by the vendor, the same way a GPU vendor supplies the Vulkan ICD rather than AOSP shipping one. Vendoring just the headers makes the standard XR ABI available to native code that opts in, without committing AOSP to a particular runtime implementation.
62.6.3 The Flag-Gated XR API Surface¶
On top of the headers, Android 17 adds a public Java API surface that lets an app declare what XR
capabilities it needs and describe how it wants to be presented. All of it is gated by
@FlaggedApi("android.xr.xr_manifest_entries") and is therefore inert on a stock build, but it is
present in frameworks/base/core/api/current.txt, so it is part of the public SDK contract.
The feature strings live in frameworks/base/core/java/android/content/pm/PackageManager.java. An
app names them in <uses-feature> to require an XR-capable device:
FEATURE_XR_API_OPENXR("android.software.xr.api.openxr") -- the device exposes an OpenXR runtime. Its declared version is encoded asmajor << 16 | minor(major in bits 31-16, minor in bits 15-0), so OpenXR 1.1 is0x00010001. The same encoding is used by the feature XML in Section 62.6.5.FEATURE_XR_API_SPATIAL("android.software.xr.api.spatial") -- the device supports the spatial (system-composited) presentation model.FEATURE_XR_INPUT_CONTROLLER("android.hardware.xr.input.controller"),FEATURE_XR_INPUT_HAND_TRACKING("android.hardware.xr.input.hand_tracking"), andFEATURE_XR_INPUT_EYE_TRACKING("android.hardware.xr.input.eye_tracking") -- the available XR input modalities.FEATURE_XR_PERIPHERAL("android.hardware.type.xr_peripheral") -- this one is gated by a separate flag,@FlaggedApi(com.android.microxr.Flags.FLAG_XR_GLASSES_FEATURE)("com.android.microxr.xr_glasses_feature"), reflecting the lighter glasses peripheral form factor rather than a full headset.
How an XR activity is presented is described with window properties in
frameworks/base/core/java/android/view/XrWindowProperties.java. The central one is
PROPERTY_XR_ACTIVITY_START_MODE ("android.window.PROPERTY_XR_ACTIVITY_START_MODE"), whose value
selects one of (each a full XR_ACTIVITY_START_MODE_* constant, abbreviated here by suffix):
FULL_SPACE_UNMANAGED-- the activity renders its own scene graph and "controls its own scene graph," the mode documented for activities that use OpenXR directly to draw their world.FULL_SPACE_MANAGED-- the system renders the activity from a scene graph it composites.HOME_SPACE-- the activity sits in the shared home-space environment alongside other windows.UNDEFINED-- the reset/default value.
Two more properties tune presentation: PROPERTY_XR_BOUNDARY_TYPE_RECOMMENDED (the recommended
play-area boundary type) and PROPERTY_XR_USES_CUSTOM_FULL_SPACE_MANAGED_ANIMATION (the app draws
its own enter/exit animation in managed full-space mode). Separately,
DisplayManager.DISPLAY_CATEGORY_XR_PROJECTED
("android.hardware.display.category.XR_PROJECTED",
frameworks/base/core/java/android/hardware/display/DisplayManager.java) lets a caller enumerate
projected XR displays.
This is purely declarative API. There is no android.xr.* runtime service or scene SDK in AOSP 17
to act on these declarations -- the runtime that reads the start mode and composites the scene is
the off-tree vendor stack, and the high-level scene API apps write against is the off-tree Jetpack
XR SDK.
The following diagram shows the flag-gated XR contract: what an app declares, the flag that gates it, the platform's in-tree declarations, and the off-tree pieces (shown dashed) that actually run the experience.
graph TB
subgraph App["XR Application (manifest)"]
UF["<uses-feature><br/>FEATURE_XR_API_OPENXR / SPATIAL<br/>FEATURE_XR_INPUT_* / XR_PERIPHERAL"]
PROP["<property><br/>PROPERTY_XR_ACTIVITY_START_MODE<br/>(FULL_SPACE_UNMANAGED / MANAGED / HOME_SPACE)"]
PERM["<uses-permission><br/>EYE/FACE/HAND/HEAD_TRACKING<br/>SCENE_UNDERSTANDING_*"]
end
FLAG["aconfig flag<br/>android.xr.xr_manifest_entries<br/>(disabled by default in AOSP)"]
subgraph Platform["AOSP 17 in-tree (contract only)"]
PM["PackageManager FEATURE_XR_* strings<br/>(core/api/current.txt)"]
XWP["XrWindowProperties<br/>start mode / boundary / animation"]
MAN["AndroidManifest.xml<br/>XR tracking permissions + groups"]
HDR["external/openxr-sdk<br/>openxr_headers (headers only)"]
end
subgraph Vendor["Off-tree (NOT in AOSP)"]
RT["Vendor OpenXR runtime + loader<br/>compositor"]
SDK["Jetpack XR SDK<br/>(scene / spatial APIs)"]
end
UF --> FLAG
PROP --> FLAG
PERM --> FLAG
FLAG --> PM
FLAG --> XWP
FLAG --> MAN
HDR -.ABI.-> RT
SDK -.app-facing.-> UF
RT -.implements contract.-> Platform
62.6.4 XR Privacy: Tracking Permissions and AppOps¶
The most concrete and consequential XR surface in AOSP 17 is the privacy machinery. A headset
continuously senses the wearer's body and surroundings -- where the eyes look, the geometry of the
face, the pose of the hands, the orientation of the head, and a reconstructed mesh of the room.
That is a class of data Android had no permissions for, so 17 introduces a dedicated XR
tracking permission family in frameworks/base/core/res/AndroidManifest.xml, every entry carrying
android:featureFlag="android.xr.xr_manifest_entries".
The runtime (dangerous) permissions -- the ones an app must request and the user must grant -- are:
| Permission | What it exposes |
|---|---|
EYE_TRACKING_COARSE / EYE_TRACKING_FINE |
gaze direction at two fidelities |
FACE_TRACKING |
facial geometry / expression |
HAND_TRACKING |
hand and finger pose |
HEAD_TRACKING |
head pose and orientation |
SCENE_UNDERSTANDING_COARSE / SCENE_UNDERSTANDING_FINE |
reconstructed environment geometry |
Alongside these, Android 17 defines XR-specific permission-groups -- XR_TRACKING, plus the more
sensitive XR_EYE_SENSITIVE and XR_TRACKING_SENSITIVE -- intended for the eye and fine-tracking
data, which are especially identifying, so it can be surfaced separately from coarse tracking. (In
the manifest the individual permissions still sit in the UNDEFINED group; the XR grouping is
applied at runtime, not via a static permissionGroup attribute.) A
second tier of signature|privileged permissions is reserved for the system and OEM components:
EYE_CALIBRATION, FACE_TRACKING_CALIBRATION, IMPORT_XR_ANCHOR, and
XR_TRACKING_IN_BACKGROUND (the right to keep tracking the wearer while not in the foreground,
which is exactly why it is privileged).
Each runtime tracking permission is paired with an AppOp in
frameworks/base/core/java/android/app/AppOpsManager.java, so the framework can audit and revoke
access at runtime, per app, the same way it does for camera and location:
| AppOp constant | Op string |
|---|---|
OP_EYE_TRACKING_COARSE / OP_EYE_TRACKING_FINE |
android:eye_tracking_coarse / android:eye_tracking_fine |
OP_FACE_TRACKING |
android:face_tracking |
OP_HAND_TRACKING |
android:hand_tracking |
OP_HEAD_TRACKING |
android:head_tracking |
OP_SCENE_UNDERSTANDING_COARSE / OP_SCENE_UNDERSTANDING_FINE |
android:scene_understanding_coarse / android:scene_understanding_fine |
The reason XR needs an entirely new permission class rather than reusing camera or sensor permissions is that the data is qualitatively different. Eye gaze is biometric and reveals attention and intent; face and hand tracking reconstruct the body; scene understanding builds a persistent 3D map of the user's home. None of these map cleanly onto "take a photo" or "read the accelerometer," and they carry distinct consent and retention concerns, so the platform models each as its own permission with its own AppOp. The permissions and AppOps ship in 17 even though no in-tree runtime produces the data yet -- the privacy contract is deliberately in place before any device can sense it.
The following diagram shows the body- and environment-tracking permission-to-AppOp model and where the (off-tree) runtime sits relative to it.
graph LR
subgraph Sensing["What a headset senses"]
EYE["Gaze"]
FACE["Face geometry"]
HAND["Hand pose"]
HEAD["Head pose"]
SCENE["Room mesh"]
end
subgraph Perms["Dangerous permissions (AndroidManifest.xml)"]
PE["EYE_TRACKING_COARSE/FINE"]
PF["FACE_TRACKING"]
PH["HAND_TRACKING"]
PHd["HEAD_TRACKING"]
PS["SCENE_UNDERSTANDING_COARSE/FINE"]
end
subgraph Ops["AppOps (AppOpsManager.java)"]
OE["OP_EYE_TRACKING_*"]
OF["OP_FACE_TRACKING"]
OH["OP_HAND_TRACKING"]
OHd["OP_HEAD_TRACKING"]
OS["OP_SCENE_UNDERSTANDING_*"]
end
RT["Off-tree vendor OpenXR runtime<br/>(produces the data; NOT in AOSP)"]
EYE --> PE --> OE
FACE --> PF --> OF
HAND --> PH --> OH
HEAD --> PHd --> OHd
SCENE --> PS --> OS
RT -.guarded by.-> Ops
62.6.5 Feature Declarations and Device Opt-In¶
For the <uses-feature> strings of Section 62.6.3 to mean anything, a device has to declare the
matching system features. The declaration XML for that lives in frameworks/native/data/etc/:
| File | Feature declared | Version |
|---|---|---|
android.software.xr.api.openxr-1_0.xml |
android.software.xr.api.openxr |
65536 (= 0x00010000, OpenXR 1.0) |
android.software.xr.api.openxr-1_1.xml |
android.software.xr.api.openxr |
65537 (= 0x00010001, OpenXR 1.1) |
android.software.xr.api.openxr-1_2.xml |
android.software.xr.api.openxr |
65538 (= 0x00010002, OpenXR 1.2) |
android.software.xr.api.spatial-1.xml |
android.software.xr.api.spatial |
1 |
android.hardware.xr.input.controller.xml |
android.hardware.xr.input.controller |
(none) |
android.hardware.xr.input.hand_tracking.xml |
android.hardware.xr.input.hand_tracking |
(none) |
android.hardware.xr.input.eye_tracking.xml |
android.hardware.xr.input.eye_tracking |
(none) |
The OpenXR version attribute is the same major << 16 | minor integer encoding used by
FEATURE_XR_API_OPENXR -- a device declares the highest OpenXR version its runtime supports, and
PackageManager lets an app require at least that version.
Crucially, AOSP installs none of these by default. They are not listed in the permission
makefiles that base and handheld products copy; they sit in frameworks/native/data/etc/ purely as
opt-in fragments. An XR device pulls the ones it supports into its system image with
PRODUCT_COPY_FILES in its own device makefile, the same mechanism every optional hardware feature
uses. Because there is no XR HAL, no XR input backing, and no lunch target in AOSP 17, declaring
these features only becomes meaningful on a vendor build that also supplies the runtime; on a stock
build there is nothing to declare them against.
62.6.6 MicroXR: The XR Glasses Split¶
Everything in Sections 62.6.1 through 62.6.5 is the headset story, gated by the android.xr
flag xr_manifest_entries. Android 17 carves out a second, lighter form factor at the flag level:
XR glasses, which ride a separate aconfig package, com.android.microxr.
The split starts in the flag itself. The glasses flag is declared in a file of its own,
frameworks/base/core/java/android/content/pm/glasses.aconfig:
package: "com.android.microxr"
container: "system"
flag {
namespace: "xr"
name: "xr_glasses_feature"
description: "Adds features used by Android XR Glasses"
bug: "430302860"
is_exported: true
}
Like xr_manifest_entries, xr_glasses_feature is is_exported (so it can gate public API) and is
disabled by default -- there is no release override for it under build/release, so on a stock
AOSP 17 build the glasses surface is inert. The two flags are independent: full XR headsets ride
android.xr / xr_manifest_entries (Section 62.6.3), and XR glasses ride
com.android.microxr / xr_glasses_feature. The build wiring sits in
frameworks/base/AconfigFlags.bp, under a // XR - Glasses comment, which declares
com.android.microxr.flags-aconfig from core/java/android/content/pm/glasses.aconfig and a
com.android.microxr.flags-aconfig-java library whose apex_available lists both the platform and
com.android.permission -- so the glasses flag is reachable from the permission module, not just
the framework.
The one public API the glasses flag gates is a device-class feature string in
frameworks/base/core/java/android/content/pm/PackageManager.java (around lines 4596-4604):
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: An XR peripheral is defined as a full stack Android device with
* or without a display, with or without inputs, and no user-installable apps. XR peripherals
* are worn on the user's body and likely require a companion device for user interactions.
*/
@FlaggedApi(com.android.microxr.Flags.FLAG_XR_GLASSES_FEATURE)
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_XR_PERIPHERAL = "android.hardware.type.xr_peripheral";
Two things about FEATURE_XR_PERIPHERAL matter. First, it is gated by
com.android.microxr.Flags.FLAG_XR_GLASSES_FEATURE ("com.android.microxr.xr_glasses_feature"),
not by the headset flag. Second, and easy to miss, it is an android.hardware.type.* string -- a
device-class marker, a peer of FEATURE_PC (android.hardware.type.pc), FEATURE_WATCH, and
FEATURE_TELEVISION -- not an android.software.xr.* / android.hardware.xr.* capability like
the headset features in Section 62.6.3. It says "this device is an XR peripheral," the way other
features say "this device is a PC" or "a watch." And unlike the headset capability features, it
ships no opt-in feature XML in frameworks/native/data/etc/: there is no
android.hardware.type.xr_peripheral.xml fragment for a device to copy in, so a glasses device
declares the class through its own configuration rather than by pulling a stock fragment.
Where in-tree code actually acts on the glasses class¶
FEATURE_XR_PERIPHERAL is almost entirely a contract -- but not only a contract. Unlike the
headset feature strings, which nothing in AOSP reads, a handful of modules already branch on the XR
peripheral class, each treating glasses as a constrained device alongside WATCH, TV, and
AUTOMOTIVE:
- Wi-Fi.
WifiGlobalscaches the class at construction:mIsXrPeripheral = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_XR_PERIPHERAL)(packages/modules/Wifi/service/java/com/android/server/wifi/WifiGlobals.java:104), then consults the flag to tune Wi-Fi behavior for the peripheral. - Bluetooth.
Util.isXrDevice()(packages/modules/Bluetooth/android/app/src/com/android/bluetooth/Util.kt:266) is defined right next toisTv()andisWatch()and resolves tohasSystemFeature(FEATURE_XR_PERIPHERAL). Its consumer,media_audio/sink/MediaAudioServer.kt:605, lumps XR devices with IoT and TV devices (if (context.isIotDevice() || context.isTv() || context.isXrDevice())) when deciding media-audio sink behavior. - MediaProvider.
ProcessingUtils(packages/providers/MediaProvider/src/com/android/providers/media/localsearch/ProcessingUtils.java:112) disables on-device media processing on XR peripherals, listingFEATURE_XR_PERIPHERALalongsideFEATURE_WATCH,FEATURE_AUTOMOTIVE,FEATURE_TELEVISION, andFEATURE_EMBEDDEDas device types that "can possibly support UI for search services" only on phones, tablets, and PCs.
That is the whole of it: three modules that special-case glasses as a stripped-down device class. There is no MicroXR runtime, service, app, HAL, or device target behind the flag -- the behavior is "do less" gating, the same shape as the existing WATCH/TV/AUTOMOTIVE checks.
MicroXR telemetry: the glasses counterpart to the headset recorder atoms¶
Where the headset groundwork surfaces only as the "Moohan" recorder atoms (Section 62.6.1), the
glasses groundwork surfaces as a parallel statsd atom family under
frameworks/proto_logging/stats/atoms/microxr/microxr_extension_atoms.proto (with shared enums in
frameworks/proto_logging/stats/enums/microxr/enums.proto). These atoms read as a portrait of the
glasses hardware Google plans to ship:
- Wear state.
MicroXrDonDoffStateChangedlogs aDonDoffStateofDONNED(worn),DOFFED(not worn), orDISABLED-- the don/doff detection a body-worn device needs. - Capture.
MicroXrPhotoCaptured/MicroXrVideoCapturedlog captures triggered by a button press on the glasses or by a voice command (PhotoOrVideoTrigger=BUTTON/VOICE), including latency through a "Darklight" HDR capture pipeline and an HDR-vs-non-HDRCaptureMode. - A separate MCU co-processor. A cluster of atoms reports the glasses' microcontroller as a
distinct chip from the application processor:
MicroXrMcuCrashOccurred(crash dump or watchdog failure),MicroXrMcuPowerDeepSleepInfo,MicroXrMcuBootTimeReported, andMicroXrMcuMemorySnapshotReported. - Power and updates.
MicroXrApWakeupReported(the MCU waking the application processor) andMicroXrOtaReported(over-the-air update outcomes) round out the set.
This is Google/vendor glasses telemetry living in proto_logging -- statsd definitions, not a
public framework API an app can call -- and, exactly as with the headset side, there is no
MicroXR module, service, app, HAL, or device target anywhere in AOSP to produce or consume it. The
atoms reserve the logging vocabulary ahead of the hardware, the same pattern the headset XR
contract follows.
62.6.7 The Legacy VR Framework Android XR Succeeds¶
Android XR is not the platform's first attempt at head-worn computing. The earlier Daydream-era
VR framework is, by Android 17, almost entirely gone. The android.hardware.vr@1.0 IVr HAL was
removed -- there is no hardware/interfaces/vr/ tree (only a stale name in a Watchdog HAL-monitor
list remains). The orchestrating VrManagerService is gone from frameworks/base/services; only
the internal VrManagerInternal interface used by ActivityManager/WindowManager survives.
What remains is a set of inert API remnants kept for source compatibility:
android.app.VrManager(frameworks/base/core/java/android/app/VrManager.java) -- still a@SystemApi, but its calls reach a system service that no longer exists.android.service.vr.VrListenerService-- the abstract service apps once extended.Vr2dDisplay(frameworks/base/services/core/java/com/android/server/vr/Vr2dDisplay.java) -- the virtual display that hosted 2D apps inside a VR scene.PackageManager.FEATURE_VR_MODE("android.software.vr.mode"), now@Deprecated, plus the still-declaredFEATURE_VR_MODE_HIGH_PERFORMANCE("android.hardware.vr.high_performance") andFEATURE_VR_HEADTRACKING("android.hardware.vr.headtracking"), which no AOSP device populates.Activity.setVrModeEnabled(...)(frameworks/base/core/java/android/app/Activity.java) still exists, routing throughActivityClient.setVrModeand throwingPackageManager.NameNotFoundExceptionif the requestedVrListenerServiceis not installed or enabled; with the VR HAL and service removed, the path no longer activates a VR mode.
The contrast is the point. The old VR framework was an Android-side mode toggle layered on a thin HAL; Android XR is architecturally distinct -- it is built on the cross-vendor OpenXR ABI, defines a body- and environment-tracking privacy model, and pushes the runtime and compositor entirely to the vendor side. The 17 tree reflects a clean break: the Daydream-era code is being retired to inert remnants while the OpenXR-based contract is laid down fresh.
62.6.8 Cross-Reference: Head-Tracked Spatial Audio¶
One subsystem XR builds on does already ship in AOSP, and it predates XR: the audio
Spatializer. Chapter 15 (Audio System, Section 15.2.17) covers the SpatializerThread, the
specialized MixerThread in AudioFlinger that renders spatial audio and drives HAL latency modes
for low-latency head-tracked audio. Head-tracked spatialization is exactly the kind of platform
capability an XR runtime composes with -- a headset wants room-anchored sound that stays fixed as
the wearer turns their head -- but it is not XR-exclusive: the Spatializer serves regular phones,
tablets, and TVs with spatial-audio output and head-tracking headphones. It is an example of an
adjacent, already-real subsystem that Android XR will lean on, rather than a piece of the XR
groundwork that 17 newly added.
62.6.9 What's Next: Predicting Android XR's Path into AOSP¶
Everything above is what AOSP 17 contains. This section is the opposite: it is an informed prediction of how the rest of the stack might land upstream, read off what 17 already stages and what is conspicuously missing. It is forward-looking, not a published roadmap -- Google has not committed to any of it in the tree, so treat each item as "this is the shape the gaps suggest," hedged with likely and would, not as a statement of plans.
The reasoning is straightforward: 17 lays down a contract with no implementation, and a contract with no implementation is the kind of thing that usually precedes the implementation. The most likely next steps, each tied to something already half-present:
- The flags flip on.
xr_manifest_entries(headsets) andxr_glasses_feature(glasses) are bothis_exportedand default-off with no release override (Sections 62.6.1, 62.6.6). The cheapest possible change once devices ship is a release override that flips them on for XR products, activating the API surface that is already merged. This needs no new code at all. - A loader/runtime built from the vendored SDK.
external/openxr-sdkships the OpenXR headers only -- the loader source is present in the checkout but not built byAndroid.bp(Section 62.6.2). The obvious next move is acc_library(or similar) that actually compiles the OpenXR loader from that already-vendored source, giving AOSP an in-tree way to resolve the OpenXR ABI to a runtime instead of leaving both to the vendor. - An XR HAL under
hardware/interfaces/. There is nohardware/interfaces/xr/today (Section 62.6.1). A device-tracking/compositor HAL contract is the natural place for the platform-vendor boundary to be specified, the way every other form factor has one; if XR is to be more than a vendor black box, anxr(ormicroxr) HAL package would likely appear here. - Real compositor support in WindowManager/SurfaceFlinger.
XrWindowPropertiesalready lets an app declareFULL_SPACE_UNMANAGED/FULL_SPACE_MANAGED/HOME_SPACEstart modes (Section 62.6.3), but nothing in-tree composites them. Honoring those modes would mean WindowManager and SurfaceFlinger gaining genuine full-space / home-space compositor paths behindXrWindowProperties, rather than the declarations being inert. - A scene/spatial system service. The
android.xr.*runtime and scene classes live only in the off-tree Jetpack XR SDK today; AOSP has no XR system service (Section 62.6.1). If the scene model is to be a platform guarantee rather than an app-bundled library, some of that runtime would likely move or be mirrored into a system service underframeworks/base/services, the roleCarServiceandTvInputManagerServiceplay for their form factors. - Feature XMLs installed by default. The opt-in fragments in
frameworks/native/data/etc/(Section 62.6.5) are copied by no product today. An XR reference product would add them to itsPRODUCT_COPY_FILES, and the glasses class -- which has no fragment at all today (Section 62.6.6) -- would likely gain one if it is to be declared the stock way. - An XR device / emulator (lunch) target. There is no XR reference device or emulator image in 17, which means there is nothing to run XR CTS against (Section 62.6.1). A lunch target -- a reference device or an emulator image -- is what would let the contract above be tested in CI, so it is a plausible precondition for any of the rest to be exercised upstream.
None of this is promised by the 17 tree. The point of cataloguing it is that the absences are specific and the staged pieces are specific, so the missing layers can be named precisely: a built loader, a HAL, compositor paths, a system service, default-installed feature XMLs, and a test target. If and when Android XR lands in AOSP as a vertical stack the way Automotive, TV, and Wear have, these are the slots it would fill.
62.7 Software Defined Vehicle: Architecture¶
Android 17's marquee device-support addition is the Software Defined Vehicle (SDV) platform: an entire new top-level source tree (system/software_defined_vehicle/), a flagship reference device (device/google/sdv), a new HAL/AIDL contract package (hardware/sdv/interfaces), and an automotive display-safety service (packages/services/display_safety). SDV is not "Android in the dashboard" the way Android Automotive OS (AAOS) is. It is a headless vehicle operating system: a "Core" VM runs the vehicle's safety- and power-relevant services with no UI at all, and one or more AAOS In-Vehicle Infotainment (IVI) VMs — plus non-Android automotive ECUs — talk to it over a service fabric. The Core VM has no display, no launcher, and no apps in the AAOS sense; it hosts service bundles, units of vehicle functionality described in a new interface language and generated to Rust, supervised by a lifecycle manager and an orchestrator that bring bundles up and down as the vehicle changes power and driving state.
This section is the architecture overview. It walks the headless Core VM, the service-bundle model and the VSIDL-to-Rust toolchain, the four control-plane agents that supervise bundles (orchestration, lifecycle management, the service-bundles registry, and the health monitor), the update manager, the vehicle power-state manager (vpm), the automotive display-safety runtime, and how an AAOS IVI VM integrates through the SDV Gateway. The wire-level transport — the middleware comm stack, SOME/IP, the VSIDL grammar, and the gateway's network plumbing — is the subject of Section 62.8; this section cross-references it rather than duplicating it.
62.7.1 What "Software Defined Vehicle" Means Here¶
The Headless Core VM¶
The defining architectural decision of SDV is that the vehicle's services run in a VM with no user interface. device/google/sdv/sdv_core_base/sdv_core_base.mk states it directly in its header comment: "Software-Defined Vehicle (SDV) is a headless vehicle Android OS." The Core VM is Android — it boots init, it runs Binder, it uses APEX modules — but it ships none of SystemUI, no launcher, and no AAOS app stack. Its job is to host vehicle service bundles and the agents that supervise them.
The Cuttlefish targets make the headlessness concrete. SDV is designed to run as multiple cooperating VMs on one host: a Core VM and one or more IVI VMs. device/google/sdv/cuttlefish_multi_tenancy/ carries example multi-VM launch configurations that boot several VM instances, each given a distinct androidboot.sdv.instance_name via bootconfig and each running with no GPU (gpu_mode: "none") because there is nothing to draw. The VMs reach each other over a virtual network, and SDV-RPC traffic is pinned to a dedicated VLAN named by the androidboot.sdv.rpc.interface bootconfig property (default sdv_rpc), configured through the SDV_RPC_INTERFACE build variable (device/google/sdv/sdv_core_base/BoardConfig.mk; system/software_defined_vehicle/sdv_gateway/README.md).
The Four Trees¶
SDV is deliberately spread across four locations in the tree, each with a distinct role:
system/software_defined_vehicle/— the platform itself: 16 subrepos holding the agents, the middleware comm stack, the VSIDL toolchain, and shared libraries. This is where the running code lives.device/google/sdv— the reference device. It composes the platform code into lunch targets (sdv_core_base,sdv_ivi_base, and friends) and decides which agents and APEXes land in which VM.hardware/sdv/interfaces— the stable contract package. Every cross-process boundary that needs version stability (the gateway, the registry, the lifecycle internal interface, vpm, telemetry, the RPC agent) has its@VintfStabilityAIDL frozen here underaidl_api/.packages/services/display_safety— the automotive Driver-UI runtime ("HARry") and its safety monitor, which runs on the IVI side.
This section treats system/software_defined_vehicle/ and the device/HAL composition; Section 62.8 treats the middleware and SOME/IP subrepos in depth.
The Layering¶
The high-level picture is a vehicle-service fabric beneath and beside AAOS. Bundles run in the Core VM; the comm stack carries their traffic; SOME/IP bridges across VMs and ECUs; and an AAOS IVI VM reaches the fabric through the SDV Gateway.
The SDV platform stack and the seam to AAOS
graph TB
subgraph CoreVM["SDV Core VM (headless, no UI)"]
ORCH["Orchestration agent<br/>(orch_config.textproto)"]
LCM["Lifecycle Manager<br/>+ bundle runner"]
REG["Service Bundles Registry<br/>(IRegistry)"]
HM["Health Monitor<br/>(heartbeats)"]
UM["Update Manager"]
VPM["Vehicle Power Manager<br/>(sdv_vpm_agent)"]
BUNDLE["Service bundles<br/>(VSIDL-generated Rust)"]
end
subgraph Comm["SDV middleware comm stack (Section 62.8)"]
SD["Service Discovery<br/>(sd_agent)"]
DT["Data Tunnel<br/>(pub/sub)"]
RPC["RPC agent<br/>(IRpcAgent)"]
end
subgraph Wire["Cross-VM / cross-ECU (Section 62.8)"]
SOMEIP["SOME/IP stack<br/>+ broker (vsomeip)"]
ECU["External ECUs<br/>(non-Android)"]
end
subgraph IVI["AAOS IVI VM"]
GW["SDV Gateway<br/>(ISdvGateway + vhal_proxy)"]
VHAL["Vehicle HAL<br/>(sdv-emulator-service)"]
CAR["CarService<br/>(packages/services/Car)"]
DS["Display Safety / HARry<br/>(packages/services/display_safety)"]
end
ORCH --> LCM
LCM --> BUNDLE
REG --- ORCH
VPM --> ORCH
BUNDLE --> SD
BUNDLE --> DT
BUNDLE --> RPC
HM -.heartbeats.-> BUNDLE
SD --> SOMEIP
DT --> SOMEIP
RPC --> SOMEIP
SOMEIP <--> ECU
GW --> RPC
GW --> DT
VHAL --> GW
CAR --> VHAL
DS --> GW
62.7.2 The Service Bundle Model¶
What a Service Bundle Is¶
The unit of deployment in the Core VM is the service bundle, not the Android app or the standalone daemon. A service bundle is a shared library — VSIDL-generated Rust compiled to a .so — that the lifecycle manager loads into a host process and drives through a fixed lifecycle. Bundles ship inside APEX modules; the APEX carries the bundle's native library plus a manifest entry describing where everything lives.
The metadata contract is the SdvServiceBundleManifest proto (system/software_defined_vehicle/service_bundles_registry/proto/sdv_service_bundles_manifest.proto). Each entry carries the bundle name, a version_number and version_name, the native_library_path (relative to the APEX root), and a set of optional config paths: orchestration_config_path, scheduling_config_path, health_config_path, diagnostics_config_path, user_config_path, vsidl_schemas_path, external_protocol_mapping_path (the SOME/IP mapping), and authorization_policy_path (which superseded the deprecated access_control_list_policy_path). The proto reserves three field-number ranges by audience — low numbers for bundle execution, a middle range for the SDV agents, and a high range for OEM custom metadata — so that a bundle's manifest never collides between layers.
VSIDL Generates Rust¶
Service interfaces are not written in AIDL. They are described in .vsidl service-bundle definitions plus .proto message schemas, and the vsidlc compiler (system/software_defined_vehicle/vsidl/vsidlc) walks the catalog and emits Rust middleware bindings into generated_rs/ directories. The companion someip_translation_generator emits the SOME/IP-to-proto translation code, and vsidl_rc_generator (the "Runtime Configuration generator") emits the runtime-configuration outputs that bundles consume through the VSIDL provider library (libsdv_config_provider). All three are host tools installed by the Core target (device/google/sdv/sdv_core_base/sdv_packages_core_services.mk lists vsidlc, vsidl_rc_generator, and someip_translation_generator under SDV_CORE_SERVICES_HOST_PACKAGES). On-device, the sdv_vsidl_provider_agent (APEX com.android.sdv.vsidl_provider) serves bundle VSIDL schemas at runtime. This is the SDV equivalent of AIDL stub generation, and the full grammar and transport mapping belong to Section 62.8.
A concrete .vsidl makes the shape clear. The vehicle power manager's bundle definition (system/software_defined_vehicle/vpm/stable/vsidl/vpm.vsidl) declares a service_bundle named VpmSystemServiceBundle with three interface slots: a server exporting the RPC service com.android.sdv.vpm.VpmSystemService, a client of com.android.sdv.vpm.client.PowerNotificationService, and a Data Tunnel publisher of com.android.sdv.vpm.vehicle.VehicleStateChange. A bundle therefore declares, in one place, what it serves over RPC, what it consumes, and what it publishes on the pub/sub fabric.
Fully Qualified Instance Names¶
Bundles can run in multiple instances, so SDV identifies a running unit by a Fully Qualified Instance Name (FQIN). In the orchestrator's common crate (system/software_defined_vehicle/orchestration/common/src/fqin.rs) an FQIN is three fields — package_name, service_bundle_name, and instance_name — formatted as package/bundle/instance. The orchestrator's FQIN converts to the lifecycle manager's ServiceFqin representation, which additionally carries a VM name (local-vm for the current VM). The FQIN is the key the control-plane agents use everywhere: the lifecycle manager keys its process table by it, the orchestrator keys its desired-state map by it, and the health monitor keys heartbeat tracking by it.
The service-bundle model, from APEX to running instance
flowchart TD
subgraph Author["Authoring (host)"]
VSIDL[".vsidl + .proto catalog"]
VSIDLC["vsidlc / someip_translation_generator"]
RS["generated_rs/<br/>(Rust bindings)"]
VSIDL --> VSIDLC --> RS
end
subgraph Pkg["Packaging"]
SO["bundle .so<br/>(native_library_path)"]
MAN["SdvServiceBundleManifest<br/>(name, version, config paths)"]
APEX["APEX module"]
RS --> SO
SO --> APEX
MAN --> APEX
end
subgraph Run["Core VM runtime"]
REG2["Service Bundles Registry<br/>scans APEXes -> caches metadata"]
ORCH2["Orchestrator<br/>desired state per FQIN"]
LCM2["Lifecycle Manager<br/>launch / start / stop / shutdown"]
RUNNER["lifecycle_service_bundle_runner<br/>(one process per instance)"]
APEX --> REG2
REG2 --> ORCH2
REG2 --> LCM2
ORCH2 --> LCM2
LCM2 --> RUNNER
RUNNER --> INST["Loaded bundle instance<br/>(FQIN package/bundle/instance)"]
end
62.7.3 The Service Bundles Registry¶
The first control-plane agent in the boot order is the Service Bundles Registry. Its README (system/software_defined_vehicle/service_bundles_registry/README.md) gives it three jobs: scan, detect, and cache the metadata of locally available SDV service bundles; verify their security restrictions; and provide that cached metadata to a limited set of SDV agents and automotive services. It is the catalog the rest of the control plane reads from — the orchestrator and the lifecycle manager both ask the registry "what bundles exist and where are their config files" before they can do anything.
The registry's public interface is the stable, @VintfStability IRegistry.aidl (hardware/sdv/interfaces/service_bundles_registry/google/sdv/service_bundles_registry/IRegistry.aidl). The frozen contract is small: a single method getAvailableServiceBundlesMetadata() returning a List<ServiceBundleMetadata>. The interface is currently at frozen API version 3 (the versioned snapshots live under hardware/sdv/interfaces/service_bundles_registry/aidl_api/google.sdv.service_bundles_registry/, with 3/ being the current frozen version). The ServiceBundleMetadata parcelable mirrors the manifest proto: it carries name, versionNumber, versionName, packageName, nativeLibraryPath, and the nullable config-path fields (orchestrationConfigPath, healthConfigPath, authorizationPolicyPath, vsidlSchemasPath, and the rest), plus a customMetadata array of KeyValuePair.
The agent binary is sdv_service_bundles_registry_agent, installed by the Core target and registered as a system service rather than shipped in its own APEX. When the registry has finished its scan and is ready to serve, it registers the binder service descriptor google.sdv.service_bundles_registry.IRegistry/default and sets the system property ro.sdv.sbr.state.ready to true — a readiness signal the other agents wait on (system/software_defined_vehicle/service_bundles_registry/src/registry/binder.rs).
62.7.4 Lifecycle Management¶
The Lifecycle Manager's Job¶
The Lifecycle Manager (system/software_defined_vehicle/lifecycle_management/) is the agent that actually launches, starts, stops, and kills bundle processes. Its README describes it as the central dispatcher through which SDV agents control service-bundle lifecycles. Other agents — chiefly the orchestrator — drive it through the ILifecycleManager AIDL (system/software_defined_vehicle/lifecycle_management/aidl/google/sdv/lifecycle/ILifecycleManager.aidl):
launchService(ServiceFqin)— bring a bundle to the CREATED state (loaded, constructed, but not yet running).startService(ServiceFqin)— transition CREATED to STARTED (running).stopService(ServiceFqin)— transition STARTED back to CREATED (stopped but still loaded).shutdownService(ServiceFqin)— gracefully bring a bundle to DESTROYED.killService(ServiceFqin)— forcefully stop a bundle.getServiceBundleState(ServiceFqin)— return the currentIServiceBundleState.
The two persistent states a loaded bundle can hold are defined in IServiceBundleState.aidl as an int-backed enum: CREATED = 1 (reached after onCreate or after onStop) and STARTED = 2 (reached after onStart). Error returns use the ResponseCode enum (SERVICE_NOT_FOUND, PERMISSION_DENIED, OPERATION_FAILED, VALUE_CORRUPTED, INVALID_ARGUMENT, INTERNAL_ERROR).
The Bundle Lifecycle and the Runner¶
A bundle's own code sees the lifecycle through the IService interface, which is frozen in the stable HAL package (hardware/sdv/interfaces/lifecycle_management/aidl/google/sdv/lifecycle/internal/IService.aidl). It is four callbacks that mirror a native constructor/destructor pair around a start/stop pair: onCreate(), onStart(), onStop(), onDestroy(). onCreate and onDestroy are guaranteed to be called exactly once; onStart/onStop can cycle. The matching IServiceManager (same package) is how a bundle process registers itself back with the manager: registerService(ServiceFqin, IService), unregisterService(ServiceFqin), and getPid().
The mechanism that turns a bundle library into a running process is the lifecycle_service_bundle_runner. The lifecycle agent does not load bundle code into its own address space; instead, for each instance it spawns a fresh lifecycle_service_bundle_runner process, passing the bundle's native-library path and the FQIN as arguments (system/software_defined_vehicle/lifecycle_management/service_bundle_runner/src/main.rs). The runner dynamically loads the bundle .so from its APEX, starts a binder thread pool, registers an IService back with the manager via IServiceManager, and then runs the bundle's executor on its main thread. When the agent launches an instance it allocates a per-bundle user ID, creates the bundle's data directory, applies the SELinux domain, spawns the runner, and waits (with a registration timeout) for the runner to register before considering the launch successful (system/software_defined_vehicle/lifecycle_management/src/lifecycle_manager/agent.rs). Isolation is therefore per instance: one process, one UID, one SELinux context per running bundle. The agent binary sdv_lifecycle_agent and the lifecycle_service_bundle_runner are both system binaries installed by the Core target.
The service-bundle lifecycle state machine
stateDiagram-v2
[*] --> Destroyed
Destroyed --> Created : launchService -> onCreate
Created --> Started : startService -> onStart
Started --> Created : stopService -> onStop
Created --> Destroyed : shutdownService -> onDestroy
Started --> Destroyed : shutdownService -> onStop then onDestroy
Created --> Destroyed : killService forced
Started --> Destroyed : killService forced
Destroyed --> [*]
62.7.5 Orchestration¶
Desired State, Not Imperative Control¶
The Orchestrator is the brain of the Core VM control plane. Where the lifecycle manager is imperative ("launch this, start that"), the orchestrator is declarative: it holds a desired lifecycle state for every bundle instance, recomputes that desired state whenever the vehicle changes mode, and drives the lifecycle manager until reality matches. Its README (system/software_defined_vehicle/orchestration/README.md) calls it "an SDV agent responsible for managing the lifecycle of service bundles based on orchestrator configurations." The binary is sdv_orchestration_agent, shipped in the APEX com.android.sdv.orchestrator.
At startup the orchestrator's Agent::new() (system/software_defined_vehicle/orchestration/engine/src/agent.rs) reads a VM-level config path from the system property persist.sdv.orchestrator_config_path, fetches per-bundle orchestration configs from the registry, builds an Evaluator from the combined configuration, restores the previously persisted modes from backup, and constructs the OrchestratorEngine. The engine sets ro.sdv.orchestrator.state.ready once it is up.
Modes, Conditions, and the Evaluator¶
Orchestration configuration is textproto. The schema lives in system/software_defined_vehicle/orchestration/distributed_config/src/protos/ as vm_config.proto, service_bundle_config.proto, and the shared common.proto. A ServiceBundleConfig lists the bundle's instances and a set of InstancesStateConfiguration entries; each entry pairs an optional condition with the instance states it wants (created, started, destroyed). The condition is a boolean expression over the system's current modes — common.proto defines Condition as a oneof of power_state, vehicle_state, custom_state, and the logical operators not/and/or. The Rust side mirrors this with a Condition enum (distributed_config/src/condition.rs) carrying State { mode, state }, And, Or, Not, and Empty (always true), evaluated with three-valued logic so partially-undefined conditions behave sanely.
A mode is what the orchestrator tracks (orchestration/common/src/mode.rs): Power, Vehicle, and OEM-defined Custom(String). When a mode changes, the Evaluator updates its orchestration state (with timestamp validation so stale updates are dropped) and evaluates every bundle's conditions to produce a fresh HashMap<Fqin, InstanceState> of what each instance should be. Where several conditions apply to one instance, the states merge by precedence: Destroyed beats Started beats Created (orchestration/common/src/instance_state.rs). The "config can live VM-wide or per-bundle" design is why this subtree is named distributed config (orchestration/distributed_config/README.md): a bundle ships its own slice of orchestration policy in its APEX, and the orchestrator stitches the slices together at startup.
Enforcement, Crashes, and Retry¶
The engine's enforce_instances_states() (orchestration/engine/src/engine.rs) reconciles desired state against reality, calling the lifecycle manager concurrently across instances up to a thread cap (default 12). Each instance is managed by an InstanceManager (orchestration/engine/src/instance_manager.rs) that tracks the last known state and classifies failures as transient (retry), persistent (kill, then retry), or permanent (give up). It is bounded by a RetryConfiguration (orchestration/common/src/retry_configuration.rs) carrying max_retries.
The orchestrator publishes instance state outward through IOrchestrationAgent.aidl: a subscriber registers an IServiceBundleInstanceStateChangeListener, and the orchestrator calls back onServiceBundleInstanceStateChanged(fqin, newRecoveryState, expectedLifecycleState). Two enums carry that state. ServiceBundleInstanceLifecycleState is the intent — STARTED, CREATED, or DESTROYED. ServiceBundleInstanceRecoveryState is the health — OPERATIONAL (reached its required state), RETRYING (recovering from a crash or a failed transition), or RETRY_FAILED (the orchestrator has given up). Because the lifecycle manager can itself crash, the orchestrator holds its connection through a PersistentBinderConnection (orchestration/persistent_binder/lib.rs) that relinks death notifications and reconnects transparently; after a lifecycle-manager crash the engine processes a Recovery event (orchestration/common/src/mode.rs) to re-enforce every instance.
How the orchestrator reconciles desired state on a mode change
sequenceDiagram
participant VPM as Vehicle Power Manager
participant ORCH as Orchestrator engine
participant EVAL as Evaluator
participant LCM as Lifecycle Manager
participant RUN as Bundle instance
VPM->>ORCH: power / vehicle mode update
ORCH->>EVAL: update_orchestration_state(mode)
EVAL-->>ORCH: HashMap FQIN to desired InstanceState
ORCH->>ORCH: enforce_instances_states (concurrent)
ORCH->>LCM: launchService / startService / stopService / shutdownService
LCM->>RUN: onCreate / onStart / onStop / onDestroy
RUN-->>LCM: state reached
LCM-->>ORCH: ServiceBundleState
ORCH-->>ORCH: notify listeners (lifecycle + recovery state)
62.7.6 Health Monitoring¶
The Health Monitor (system/software_defined_vehicle/health_monitor/) is, per its README, "a VM-internal service which is responsible for monitoring heartbeats from critical services and generating VM health report." It is the watchdog tier beneath the orchestrator: the orchestrator decides what should run, the health monitor notices when something that is running has gone silent. The binary is sdv_health_monitor, shipped in the APEX com.android.sdv.health.
Bundles opt into monitoring by registering a heartbeat configuration rather than being watched implicitly. The monitor's registration path (system/software_defined_vehicle/health_monitor/src/hb_explicit_registration.rs) takes a RegisterConfiguration keyed by FQIN, and the configuration itself (hb_config.rs) is four numbers: initial_delay_ms (grace period between start and the first expected heartbeat), period_ms (how often the bundle should beat), num_periods (how many beats may be missed before the bundle is considered unhealthy), and task_duration_ms (the expected length of the bundle's work). A bundle is marked unhealthy when no heartbeat has arrived within period_ms * num_periods + task_duration_ms. The monitor tracks each bundle through a small state machine (sb_recovery_monitor.rs): Normal while healthy, Recovering once recovery has been triggered, and FailedRecovery if recovery did not restore it. The health verdict feeds back into the orchestrator's recovery/retry handling, so a bundle that stops beating is restarted by the same machinery that restarts one that crashed.
62.7.7 Vehicle Power-State Manager (vpm)¶
Power and Vehicle States¶
The Vehicle Power-state Manager (vpm) is the agent that owns the VM's relationship to the vehicle's power and driving state, and it is the upstream that drives the orchestrator's Power and Vehicle modes. The agent binary is sdv_vpm_agent (system/software_defined_vehicle/vpm/android/sdv/vpm/Android.bp), packaged in the APEX com.android.sdv.vpm.
vpm's state vocabulary is defined as VSIDL protos. The power side (system/software_defined_vehicle/vpm/stable/vsidl/power.proto) defines PowerStateReport with a full suspend/resume lifecycle: POWER_OFF_EXIT (cold boot), SUSPEND_TO_RAM_EXIT / SUSPEND_TO_DISK_EXIT (resume), ON (running normally), the _ENTER states that begin a shutdown or suspend, WAIT_FOR_FINISH (the VM has done initial cleanup and is waiting for the OEM's go/cancel signal), SHUTDOWN_CANCELLED, and the _POST_FINISH states where SDV agents do their final cleanup before the platform powers off or suspends. The comments are precise about who may rely on whom in each phase — for instance, during POWER_OFF_ENTER agents must stay up because OEM applications may still need them, but during POWER_OFF_POST_FINISH everyone cleans up.
The vehicle side (system/software_defined_vehicle/vpm/stable/vsidl/vehicle.proto) defines VpmVehicleState as a ladder of vehicle activity: LOW_POWER (car off from the user's view but the power-control unit still sees it), SOFTWARE_UPDATE, PARK (a few ECUs powered for a specific activity), LIFE_ON_BOARD (comfort ECUs, customer present), VEHICLE_ON (engine ECUs powered, driving not yet possible), and TRACTION_ON (driving possible). These are the values an orchestration condition matches against when it gates a bundle by vehicle_state.
The OEM-Facing and Client-Facing Interfaces¶
vpm exposes two faces. The OEM-facing RPC service VpmSystemService lets the OEM's platform integration set the vehicle state and request power transitions (the requests are TURN_ON, PREPARE_SHUTDOWN, CANCEL_SHUTDOWN, FINISH_SHUTDOWN, with a ShutdownType of POWER_OFF, SUSPEND_TO_RAM, or SUSPEND_TO_DISK). After a successful vehicle-state change, vpm publishes the new state on the Data Tunnel topic com.android.sdv.vpm.vehicle.VehicleStateChange so any bundle can react. The bundle-facing client side is the stable HAL AIDL: IPowerStateClientApi.aidl (hardware/sdv/interfaces/vehicle_power_manager/aidl/google/sdv/vpm/IPowerStateClientApi.aidl) lets a client subscribeToPowerStateReport(IPowerStateReportListener) and receive PowerStateReport callbacks. This is the path by which the orchestrator (and any power-aware bundle) learns of power transitions and recomputes desired state.
62.7.8 Update Manager¶
The Update Manager (system/software_defined_vehicle/update_manager/) handles both system (partition) updates and service-bundle (APEX) updates for the VM. The agent binary is sdv_update_manager_agent, shipped in the APEX com.android.sdv.update_manager. Its interfaces are VSIDL service definitions (update_manager/catalog/update_manager_agent.vsidl and update_manager_client.vsidl) exposing an UpdateManagerService and a client-side UpdateManagerListenerService for status callbacks.
The update model is a small state machine over a payload. The payload proto (update_manager/catalog/payload.proto) distinguishes a SystemUpdatePayload (a path to an OTA image, with optional offset/size) from a ServiceBundleUpdatePayload (one or more APEX paths plus a boot_attempts count for retry). The service proto (update_manager/catalog/update_manager_service.proto) drives them through Prepare, Activate, Commit, and Rollback, with Suspend/Resume available for system updates and UninstallApex for removing a bundle. Crucially, the update path is power-aware: the service proto documents that if the Update Manager is in the PREPARE state and vpm signals the VM is suspending or powering off, the update is suspended — the same power modes that gate bundle lifecycles also gate the update flow.
62.7.9 The Platform Layer and Shared Common Code¶
platform¶
The system/software_defined_vehicle/platform/ tree is the native foundation the agents and bundles build on. Its README describes it as "native libraries and wrappers ... Log & Trace, Time Sync and others," and the subtree carries those wrappers in C, C++, and Rust flavors. The most load-bearing is platform/status/, which defines the SDV error/status API: libsdv_status (a C ABI-stable core), libsdv_status_cpp (the C++ SdvStatus/SdvStatusOr wrappers), and libsdv_status_rs (the Rust SdvStatus/SdvResult types) — the result type every agent returns. Alongside it the platform tree carries logging and tracing libraries, a power library, open-DICE initialization, and adbd_auth glue, giving every SDV component the same observability, error-handling, and attestation primitives regardless of which language it is written in.
common¶
The system/software_defined_vehicle/common/ tree holds shared infrastructure. The piece worth naming is common/lib_dump/, a thin wrapper around libbinder_rust that exposes the ISdvAgent.aidl interface (common/lib_dump/aidl/google/sdv/agent/ISdvAgent.aidl) so every agent gets uniform dumpsys support — a single, simple interface that the registry, orchestrator, lifecycle manager, and the rest implement so an operator can dump any agent the same way. The tree also carries shared protos, vendored third-party code, and the performance_image_generator used to build the SDV "performance" image variants (sdv_core_perf_cf).
62.7.10 Display Safety: the HARry Driver-UI Runtime¶
What Display Safety Is¶
On the IVI side, packages/services/display_safety implements the automotive Driver-UI runtime and its safety enforcement. It is a large Rust workspace (the root packages/services/display_safety/Cargo.toml enumerates dozens of crates) split into three tiers: a framework/ of reusable rendering, audio, layout, and monitoring crates; a reference/ implementation (harry-app, the safety-monitor, and ADAS visualization); and a service/ layer that bridges the UI to the SDV fabric. The motivation is regulatory: a driver-facing display must not show distracting or non-compliant content while the vehicle is in motion, and the cluster/Driver-UI must render deterministically. The framework's graphics path wraps the Impeller engine (framework/graphics/impeller), drives layout through a Taffy-based engine (framework/har-layout), and instruments itself with a performance-monitoring crate (framework/har-monitoring).
The Safety Monitor¶
The distraction-and-compliance enforcement lives in packages/services/display_safety/reference/safety-monitor, which builds the har_safety_monitor binary. It captures the rendered screen, takes vehicle data over gRPC, and runs a set of pluggable algorithms over the result — a static-pixel check, a TFLite inference path for ML-based classification, and correlation/computer-vision filters — to decide whether what is on screen is safe for the current vehicle state, issuing verdicts back over a gRPC control interface. It is, in effect, an independent referee watching the Driver-UI's output.
The SDV Service Bundle Bridge¶
The seam between this IVI-side Rust runtime and the SDV fabric is packages/services/display_safety/service/har-sdv-service, which builds libhar_sdv_service_bundle — an SDV service bundle. Its Android.bp depends on the bundle's generated SDV middleware/comms bindings (libsdv_mw_rs_com_sdv_google_display_safety_har_sdv_service_bundle, plus the core SDV libraries libsdv_rs and libsdv_log_rust) on one side and on the gRPC services (libhar_grpc_services, generated from vehicledata.proto and driverui.proto) on the other, so it publishes vehicle data and serves the Driver-UI over gRPC through the SDV middleware. Vehicle data flows in from a publisher service bundle (service/vehicledata/), through the SDV fabric, into the HARry app and the safety monitor.
The whole runtime ships as APEXes built only for SDV/display-safety products: com.google.display_safety.har carries the harry_app, the har_safety_monitor, and the rendering assets, while com.sdv.google.display_safety.services_bundle.apex carries the service-bundle .sos and their orchestration/ACL configs. The product wiring lives in device/google/sdv_display_safety, whose makefiles (sdv_harry_common.mk, sdv_ivi_cf_ds.mk, sdv_ivi_arm64_ds.mk, sdv_media_har_cf.mk) layer the display-safety stack onto the IVI and media products and pull in the AAOS DriverUI app from packages/services/Car.
62.7.11 Integrating AAOS Through the SDV Gateway¶
The Gateway as the IVI's Door to the Fabric¶
The AAOS IVI VM is a full Android Automotive image; it is not built from SDV-aware code top to bottom. So how does CarService, or a Vehicle HAL service, reach vehicle data that physically lives in another VM? Through the SDV Gateway. The gateway (system/software_defined_vehicle/sdv_gateway/, contract in hardware/sdv/interfaces/sdv_gateway/) is a @VintfStability AIDL service that runs on the IVI VM and gives non-SDV-aware native and Java clients a controlled entry point into the comm stack.
The entry interface ISdvGateway.aidl is intentionally tiny — getVersion() and createSession() — and all the work happens on the returned ISdvGatewaySession. A session is per-process and isolated; through it a client calls initComms(InitCommsParams) to bring up bidirectional communication with remote SDV services, then registerRpcServer(...) / findRpcServerByName(...) to expose or locate RPC servers in Service Discovery, and createPublication(...) / subscribeToPublicationByName(...) to use the Data Tunnel pub/sub. The session also exposes the calling app's ServiceIdentity, an authorization service, and handles to the underlying Service Discovery and Data Tunnel agents. In other words, the gateway is the IVI-side adaptor that turns "I am an ordinary Android service" into "I am a participant in the SDV fabric" — without the IVI client linking the full SDV middleware.
Gating: the Gateway Config¶
Because the gateway hands ordinary Android processes the keys to the vehicle fabric, access is allowlisted. The gateway requires a config file installed at /vendor/etc/sdv_gateway_config.json that declares, per process UID, which SDV package names (the second element of the FQIN) that UID's native service is permitted to use when calling initComms (system/software_defined_vehicle/sdv_gateway/README.md). The format maps a UID to an array of allowed package names — for example a UID 2942 allowed to use com.oemspecific.vhal; a UID of -1 grants a package name to all UIDs. The README recommends defining unique AIDs for the gateway's native clients and restricting each to only the package names it needs. An empty config blocks every native application from using the gateway. The reference config (device/google/sdv/sdv_ivi_base/sdv_gateway_config.json) ships with only the propagation flags (propagate_rpc_network_changes_to_data_tunnel, propagate_rpc_network_changes_to_service_discovery, both false), meaning the reference image's separate VLANs for RPC, Service Discovery, and Data Tunnel are kept independent.
VHAL Proxy: Vehicle Properties Across VMs¶
The concrete CarService integration is the Vehicle HAL. On the IVI VM the SDV products wire a SDV-specific VHAL — device/google/sdv/sdv_ivi_cf/sdv_ivi_cf.mk sets LOCAL_VHAL_PRODUCT_PACKAGE := android.hardware.automotive.vehicle@V1-sdv-emulator-service. That VHAL uses the gateway's vhal_proxy library (system/software_defined_vehicle/sdv_gateway/vhal_proxy/libvhal_proxy). The VhalProxy class reads and writes Android VehiclePropValues by translating them to and from SDV proto messages and routing them over the gateway: ReadMessages/WriteMessages move properties, Subscribe/Unsubscribe register for incoming updates, and the proxy's config (a JSON of protobuf descriptors and property-to-service-unit mappings) decides which property maps to which SDV publication and whether each is an ACTION_SUBSCRIBE or ACTION_PUBLISH. CarService, sitting above the VHAL exactly as it does on a normal automotive build, is therefore unaware that the vehicle property it reads originated in a service bundle in the Core VM: the gateway and vhal_proxy make the cross-VM hop invisible.
The IVI's SDV-facing services are installed by device/google/sdv/sdv_ivi_base/sdv_packages_ivi_services.mk (the gateway, libvhal_proxy, the gateway networking service, and the SDV IVI runtime) and started by device/google/sdv/sdv_ivi_base/sdv.agents.rc, which brings up the gateway, Service Discovery, and RPC agents in order once the SDV network is ready. The transport beneath them — RPC, Data Tunnel, SOME/IP across VMs and to external ECUs — is the subject of Section 62.8.
How a CarService VHAL read reaches a Core VM service bundle
flowchart LR
CAR["CarService<br/>(packages/services/Car)"] --> VHAL["sdv-emulator-service<br/>(Vehicle HAL)"]
VHAL --> PROXY["libvhal_proxy<br/>(VehiclePropValue to proto)"]
PROXY --> SESS["ISdvGatewaySession<br/>(initComms / subscribe)"]
SESS --> GWCFG["sdv_gateway_config.json<br/>(UID to package allowlist)"]
SESS --> COMM["RPC / Data Tunnel agents"]
COMM --> XVM["SOME/IP across VMs<br/>(Section 62.8)"]
XVM --> BUNDLE["Vehicle service bundle<br/>in SDV Core VM"]
62.7.12 Composing It All: the Reference Device¶
device/google/sdv ties the platform into buildable products. OEM products are meant to inherit one SDV "base" target plus a vendor target (device/google/sdv/README.md). The bases are sdv_base (comm stack only), sdv_core_base (the full set of Core services), sdv_media_base (Core plus media APIs), and sdv_ivi_base (an AAOS IVI capable of talking to SDV services on other VMs). The canonical "what runs in the Core VM" list is device/google/sdv/sdv_core_base/sdv_packages_core_services.mk: the lifecycle client libraries, orch_config.textproto, sdv_lifecycle_agent, sdv_orchestration_agent, sdv_service_bundles_registry_agent, lifecycle_service_bundle_runner, sdv_someip_broker_agent_comms, sdv_update_manager_agent, sdv_health_monitor, sdv_vsidl_provider_agent, the comm-stack agents (dt_agent, rpcagent, sdv_sd_agent), and the matching APEXes (com.android.sdv.health, com.android.sdv.orchestrator, com.android.sdv.update_manager, com.android.sdv.vsidl_provider, com.android.sdv.dt). The same file demands a SOME/IP broker config and warns if no SOME/IP agent is installed, because a Core VM with no transport agent cannot talk to anything.
The sample lunch targets (device/google/sdv/AndroidProducts.mk) are the Cuttlefish and ARM64 instances of these bases: sdv_core_cf, sdv_core_perf_cf, sdv_core_tiny_cf, sdv_ivi_cf, sdv_media_cf, sdv_media_har_arm64, and their *_arm64/*_cf peers. (The display-safety media-HAR Cuttlefish target sdv_media_har_cf lives in a separate device tree, device/google/sdv_display_safety/, not in device/google/sdv/.) Booting a Core VM plus an IVI VM together — as device/google/sdv/cuttlefish_multi_tenancy/ configures — is the smallest end-to-end SDV system: a headless Core hosting bundles, an AAOS IVI reaching them through the gateway, and the comm fabric between.
62.8 SDV Middleware and Vehicle Communication¶
Section 62.7 introduced the Software Defined Vehicle (SDV) platform that arrives in Android 17: a headless vehicle Android OS where a Core VM runs vehicle services with no UI, alongside one or more Android Automotive OS (AAOS) In-Vehicle Infotainment (IVI) VMs and non-Android automotive ECUs. That section covered the architecture overview, the Core VM, and the orchestration that drives bundle lifecycle. This section goes one layer down, into the communication fabric that ties all of those pieces together: the VSIDL interface-definition language and its Rust code generator, the three-agent middleware (Service Discovery, Data Tunnel, RPC) with its secure mesh, the SOME/IP stack that carries cross-VM and cross-ECU traffic, the SDV Gateway that lets ordinary AAOS apps and the VHAL reach the fabric, and the automotive-domain service catalog (diagnostics, configuration, calibration, vehicle mode, user profile) layered on top. The source lives almost entirely under system/software_defined_vehicle/, with the stable contracts in hardware/sdv/interfaces/.
62.8.1 The Shape of the Fabric¶
If Binder is how processes talk inside one Android VM (Chapter 9), the SDV middleware is how Service Bundles talk across VMs and out to physically separate ECUs. The design borrows Android's idioms — AIDL contracts, a registry, identity-aware calls — but stretches them over a network of mutually distrusting compute nodes inside a single vehicle.
Three concepts recur throughout this section:
- Service Bundle — the SDV unit of deployment and the analogue of an Android service. A bundle publishes topics, subscribes to topics, and offers or consumes RPC services. Its interface is described in VSIDL (§62.8.2).
- FQIN (Fully Qualified Instance Name) — the vehicle-wide identity of a bundle instance.
ServiceFqin(hardware/sdv/interfaces/middleware/service_discovery/google/sdv/identity/ServiceFqin.aidl) is four strings:sdvVmName,sdvPackageName,serviceBundleName, andserviceInstanceName(the last assigned by the Orchestrator at load time). The FQIN is what gets baked into TLS certificates so the mesh can authenticate peers. - SID (Service ID) — a 64-bit numeric identity that fast lookups use at runtime.
ServiceIdentity(.../identity/ServiceIdentity.aidl) pairs thelong sidwith an EC public key and the human-readable FQIN.
The three middleware agents¶
Inside any one VM, a Service Bundle reaches the fabric through three agents, each a Binder service defined in hardware/sdv/interfaces/middleware/:
- Service Discovery (
service_discovery/) registers, finds, and watches service units and topics. - Data Tunnel (
data_tunnel/) carries named-topic publish/subscribe traffic over Android FastMessageQueues (FMQ). - RPC (
rpc/) carries request/response calls over sockets.
A bundle does not connect to those agents one by one. The Lifecycle Manager (§62.7.4) hands each bundle a single use-once ContextInitializationToken (hardware/sdv/interfaces/middleware/ctx/aidl/google/sdv/comms/ContextInitializationToken.aidl) that bundles all four Binder connections it needs:
// Source: hardware/sdv/interfaces/middleware/ctx/aidl/google/sdv/comms/ContextInitializationToken.aidl:31
parcelable ContextInitializationToken {
ServiceIdentity identity;
IServiceRegistrationAgent sr_agent;
IServiceDiscoveryAgent sd_agent;
IAgentService dt_agent; // Data Tunnel
IRpcAgent rpc_agent;
}
The comment on the token states its lifecycle plainly: it is "the use-once Context initialisation value issued by LifecycleManager to Service Bundles that enables them to create the SDV SDK Context object." From that token the bundle builds its SDK Context, and everything else flows from there.
The overall layering, from the bundle down to the wire, looks like this.
How a Service Bundle reaches the fabric, and how the fabric reaches other VMs and ECUs
graph TB
subgraph App["Service Bundle (Rust, VSIDL generated_rs)"]
SDK["SDV SDK Context<br/>(from ContextInitializationToken)"]
end
subgraph Agents["Per-VM middleware agents (Binder)"]
SD["Service Discovery<br/>(sd_agent, Rust)"]
DT["Data Tunnel<br/>(dt_agent, C++, FMQ)"]
RPC["RPC agent<br/>(IRpcAgent, sockets)"]
end
subgraph Sec["Identity and security"]
ID["Identity Agent<br/>(SID + FQIN)"]
CA["Certificate Authority<br/>(per-VM, mTLS)"]
AZ["Authz service<br/>(ACL / permission)"]
end
subgraph Wire["Cross-VM and cross-ECU"]
BROK["SOME/IP broker<br/>(Rust)"]
STACK["SOME/IP stack agent<br/>(C++, vsomeip)"]
ECU["External ECUs<br/>(non-Android)"]
end
SDK --> SD
SDK --> DT
SDK --> RPC
SD --> ID
SD --> CA
SD --> AZ
SD --> BROK
DT --> BROK
RPC --> BROK
BROK --> STACK
STACK <--> ECU
62.8.2 VSIDL: Describing Services and Generating Rust¶
SDV does not hand-write the marshalling code that moves messages between bundles. It describes services in a Vehicle Service Interface Definition Language (VSIDL) and generates the middleware bindings, the same way AIDL generates Binder stubs. The toolchain lives under system/software_defined_vehicle/vsidl/ and is written in Rust.
The .vsidl and .proto catalog¶
A catalog is a directory holding two kinds of files: .proto files that define message types, and .vsidl files that define service bundles. The compiler README spells out the split: "From .proto files it reads message names, rpc interfaces, and type definitions. From .vsidl files it reads service bundle definitions" (system/software_defined_vehicle/vsidl/vsidlc/README.md).
A .vsidl file is itself textproto, conforming to the grammar in system/software_defined_vehicle/vsidl/language/src/protos/sdv/vsidl/v1/syntax.proto. A bundle declares the topics it publishes and subscribes to and the RPC services it serves or calls:
// Source: system/software_defined_vehicle/samples/vsidl/complex/catalog/complex_message_publisher.vsidl:18
package: "com.android.sdv.sample.complex"
service_bundle {
name: "ComplexMessagePublisher"
publisher {
message: "ComplexMessage"
topic: "complex-message"
capacity: 50
}
server {
service: "ComplexMessageRPC"
channel: "complex-message-rpc"
}
}
A subscriber/client bundle is the mirror image: a subscriber block naming the same topic, and a client block naming the same RPC channel. Topics are named — complex-message here — and that name is the rendezvous point the two halves use without ever knowing each other's FQIN at authoring time.
vsidlc and generated_rs¶
The compiler vsidlc (system/software_defined_vehicle/vsidl/vsidlc/) walks the catalog recursively and emits Rust middleware bindings into an output/generated_rs directory (per its README). Internally it runs a small pipeline of generation steps — service-bundle bindings, RPC bindings, diagnostics bindings, and a generated Android.bp — under system/software_defined_vehicle/vsidl/vsidlc/src/rust/steps/. The output is the SDV equivalent of an AIDL stub: typed publisher/subscriber/server/client handles the bundle code links against, so application logic never touches the wire format directly.
A companion tool, vsidl_rc_generator (system/software_defined_vehicle/vsidl/vsidl_rc_generator/), produces the runtime configuration the agents load rather than the code the bundle links. Its README lists the outputs: "Schemas of Protobuf messages used in the catalog, SOME/IP mapping files, [and] Diagnostic declarations," serialized as vsidl-config.binpb, someip-config.binpb, and diagnostics-config.binpb.
SOME/IP translation modes¶
Because a topic may have to cross onto a SOME/IP bus to reach a non-Android ECU, message types carry a translation mode that decides how the SOME/IP layer treats their bytes. The parser recognizes three modes (system/software_defined_vehicle/vsidl/language/src/parser/converter.rs): INTERPRET_AS_BYTES, DYNAMIC_LIBRARY, and a default REFLECTION. The someip_translation_generator (system/software_defined_vehicle/some_ip/someip_translation_generator/) reads these tags and emits translation code: its README documents a static-lib mode that handles messages tagged INTERPRET_AS_BYTES (the bytes go on the wire as-is) and a dyn-lib mode for messages tagged DYNAMIC_LIBRARY (translation code is compiled into a shared library). This is the seam where SDV's protobuf-shaped messages meet SOME/IP's fixed wire layout.
The VSIDL provider agent¶
Catalog metadata also has to be queryable at runtime — sometimes from a different VM. sdv_vsidl_provider_agent (system/software_defined_vehicle/vsidl/provider/agent/sdv/) is an RPC service that answers descriptor queries: publication descriptors, RPC method descriptors, message descriptors, and diagnostics declarations. Its client library (system/software_defined_vehicle/vsidl/provider/clientlib/) can source that metadata three ways — from local config files, from on-device APEXes, or by delegating to another VM's provider agent — so a tool or bundle can introspect a service bundle that lives on a peer VM. There is an ivi/ variant of the agent for the IVI side as well.
62.8.3 The Middleware: Discovery, Data Tunnel, RPC, and the Secure Mesh¶
With VSIDL covering the contract, the runtime fabric is the three agents plus the identity and security layer beneath them. The implementations live under system/software_defined_vehicle/middleware/; the contracts under hardware/sdv/interfaces/middleware/.
Service Discovery¶
IServiceRegistrationAgent registers a service unit and returns a one-use RegistrationToken; IServiceDiscoveryAgent finds and watches units. The registration call carries the unit name, its UnitType, an ACL, and application metadata (.../service_discovery/discovery/IServiceRegistrationAgent.aidl), and discovery offers both type-based and name-based lookups plus topic enumeration:
// Source: hardware/sdv/interfaces/middleware/service_discovery/google/sdv/service_discovery/discovery/IServiceDiscoveryAgent.aidl
ServiceUnitDefinition getServiceUnit(in ServiceFqin fqin, in String unitName);
// ... plus listServiceUnitsByType/ByName, fetchPublishersByTopicName, listTopics
A third interface, ITransportSupportAgent, lets a transport (such as the SOME/IP broker) redeem a RegistrationToken for the full ServiceUnitDefinition and attach transport-specific metadata to it. That indirection is how the wire layer learns where to actually send bytes for a logically-registered service. The Service Discovery agent itself, sdv_sd_agent, is Rust (system/software_defined_vehicle/middleware/service_discovery/sdv_sd_agent/srcs/main.rs).
Data Tunnel¶
Data Tunnel is named-topic pub/sub. IAgentService (hardware/sdv/interfaces/middleware/data_tunnel/aidl/google/sdv/data_tunnel/IAgentService.aidl) has a publisher register a publication — handing over an MQDescriptor for the FastMessageQueue it will write into — and subscribers attach by unit identifier or by topic name:
Connect(long sid, out ClientDescriptor)establishes the per-client channel.RegisterPublication(RegistrationToken, MQDescriptor<byte,...>, out PublicationDescriptor)registers a topic backed by an FMQ the publisher allocates.SubscribeToPublicationExtended(SubscriptionParams, out SubscriptionResult)subscribes with a readiness listener.GetLastMessageByTopic(UnitType, String topicName, out byte[])reads the most recent value of a topic.
Using FMQ means same-VM pub/sub is effectively zero-copy through shared memory; cross-VM topics ride the SOME/IP broker instead. The Data Tunnel agent is C++, and it ships with a companion APEX com.android.sdv.dt that carries the ACLs governing inter-VM Data Tunnel communication (system/software_defined_vehicle/middleware/data_tunnel/apex/Android.bp).
RPC¶
RPC is socket-based request/response. IRpcAgent (hardware/sdv/interfaces/middleware/rpc/google/sdv/rpc/IRpcAgent.aidl) is small and pointed: a server redeems a RegistrationToken to get a socket to listen on, and a client asks for a connection to a named server:
// Source: hardware/sdv/interfaces/middleware/rpc/google/sdv/rpc/IRpcAgent.aidl:26
interface IRpcAgent {
ParcelFileDescriptor registerServer(in RegistrationToken token);
void registerServerPort(in RegistrationToken token, int port);
ParcelFileDescriptor getFdConnection(in long sid, @utf8InCpp String unitName);
@utf8InCpp String getAddressConnection(in long sid, @utf8InCpp String unitName);
@utf8InCpp String getNetworkInterfaceName();
}
getNetworkInterfaceName() returns the network interface the agent binds to — by default the dedicated SDV-RPC VLAN discussed in §62.8.4.
Identity, the certificate mesh, and authorization¶
The agents above are only safe because of a security layer that runs beneath them. It has three parts, all under hardware/sdv/interfaces/middleware/service_discovery/google/sdv/:
- Identity (
identity/IIdentityAgent.aidl) mints and verifiesServiceIdentityrecords:createIdentity(EcPublicKey, ServiceFqin, ...),verifyIdentity(...), and lookups by SID, by FQIN, or by OS process identifier. - Certificate Authority (
ca/ICertificateAuthority.aidl) issues X.509 certificates for FQINs.requestCertification(String request)takes a PEM PKCS#10 request whose subject-alternative DNS name encodes the FQIN;addAuthoritiesListener(...)lets a peer learn as VMs join or leave. The CA is gated on boot state —isEnabled()returns false in the UNLOCKED boot mode and true when LOCKED. - Authorization (
authz/IAuthzService.aidl) answersisAuthorized(subject_fqin, object_fqin, object_service_unit_name)so the middleware can deny calls between bundles that policy does not permit.
The certificate material is EC (P-256); the helper that builds the self-signed per-VM root encodes the BASE32 FQIN into the certificate subject and subject-alternative name (system/software_defined_vehicle/middleware/crypto_rpc/src/cert.rs). The crypto_rpc library README states the coupling directly: it "enables TLS for RPC," and "Service Discovery and SDV RPC library depend on each other in terms of X509 certificate signing and usage."
The result is the secure mesh: each SDV VM runs its own CA, and the set of CAs is shared across VMs so any node can validate any peer's certificate. IMeshStatus (.../mesh/IMeshStatus.aidl) reports whether this VM is connected to every other VM declared in the vehicle's vvmconfig (isComplete()) and the per-peer PeerConnectionStatus. Mesh provisioning writes a truststore file /vvmtruststore/uds_pubs via IUdsPubsProvisioner (.../mesh/provisioning/IUdsPubsProvisioner.aidl), and that step is only available in the UNLOCKED boot mode — the device is provisioned, then locked.
The security and identity layer beneath the three agents
graph LR
BUNDLE["Service Bundle"] -->|register| SR["IServiceRegistrationAgent"]
SR -->|"createIdentity()"| ID["IIdentityAgent<br/>(SID + FQIN)"]
SR -->|"requestCertification()"| CA["ICertificateAuthority<br/>(per-VM root, P-256)"]
BUNDLE -->|"call peer"| AZ["IAuthzService<br/>(isAuthorized)"]
CA -->|"share roots"| MESH["Secure mesh<br/>(IMeshStatus, uds_pubs)"]
MESH -->|"mTLS peers"| PEER["Other SDV VMs"]
AZ -->|"allow / deny"| RPC["RPC / Data Tunnel<br/>over mTLS"]
62.8.4 SOME/IP: Crossing VM and ECU Boundaries¶
Same-VM traffic stays in Binder and FMQ. The moment a topic or RPC has to reach another VM or a non-Android ECU, it goes onto SOME/IP — the AUTOSAR automotive service protocol — through two cooperating processes under system/software_defined_vehicle/some_ip/.
The stack agent and vsomeip¶
sdv_someip_stack_agent is C++. It wraps the open-source vsomeip library: StackImpl constructs vsomeip::runtime::get() and creates a vsomeip application (system/software_defined_vehicle/some_ip/vsomeip_stack/src/stack.cpp), then exposes a Binder interface, ISomeIpStack. The wire configuration — which SOME/IP service IDs and instance IDs this node offers, their TCP/UDP ports, and the service-discovery multicast group — lives in system/software_defined_vehicle/some_ip/vsomeip_stack/vsomeip_config.json, the standard vsomeip configuration format.
ISomeIpStack (hardware/sdv/interfaces/some_ip/stack_agent/aidl/google/sdv/someip/ISomeIpStack.aidl) is the boundary between SDV's world and the SOME/IP wire, and it speaks in raw byte[] payloads on both sides:
// Source: hardware/sdv/interfaces/some_ip/stack_agent/aidl/google/sdv/someip/ISomeIpStack.aidl
byte[] rpc_transact(in SomeIpService service, char method_id, in byte[] payload); // sync RPC
void rpc_oneway (in SomeIpService service, char method_id, in byte[] payload); // fire-and-forget
oneway void monitor_service(in SomeIpService service); // track availability
oneway void publish(in SomeIpService service, char event_id, in byte[] payload); // emit event
void subscribe_eventgroup(in SomeIpService service, char eventgroup, in char[] event_ids);
A SomeIpService is the SOME/IP triple — a 16-bit service_id, a 16-bit instance_id, and a SomeIpServiceVersion (byte major, int minor) — defined in SomeIpService.aidl and SomeIpServiceVersion.aidl in the same directory.
The broker¶
The stack agent only knows SOME/IP. Mapping SDV's topics, RPC channels, and protobuf messages onto SOME/IP services, events, and method IDs is the job of sdv_someip_broker_agent_comms, which is Rust. Its module header states its purpose: "This agent is responsible for enabling communication between SOME/IP communication and SDV" (system/software_defined_vehicle/some_ip/broker_agent_comms/src/main.rs). The broker has sub-modules for service discovery, pub/sub, and RPC, plus a translator that converts between SOME/IP bytes and SDV types using the mappings generated by vsidl_rc_generator (§62.8.2). It connects to the stack agent over Binder (google.sdv.someip.ISomeIpStack/default) and registers callbacks so SOME/IP events, availability changes, and inbound RPC requests are routed back into the SDV agents.
Callbacks: how traffic flows in both directions¶
ISomeIpStack is paired with three callback interfaces the broker registers so the flow is bidirectional:
ISomeIpServiceAvailabilityCallback— the stack tells the broker when a SOME/IP service appears or disappears (SOME/IP service discovery).IEventNotificationCallback— the stack delivers a subscribed SOME/IP event up to the broker, which fans it out to Data Tunnel subscribers.IRpcRequestCallback—byte[] onRpcRequest(SomeIpService, char method_id, byte[] payload): the stack hands an inbound SOME/IP RPC request to the broker and sends the returned bytes back as the response.
A separate, tiny interface, ISomeIpLoadIndicators (hardware/sdv/interfaces/some_ip/load_indicators/aidl/.../ISomeIpLoadIndicators.aidl), reports a single int pendingSomeIpEventCounter: zero means idle, a positive value is the depth of the unprocessed SOME/IP event queue, and a negative value signals an error — a cheap real-time backpressure signal the stack agent samples periodically.
The round trip for an outbound RPC and an inbound event
sequenceDiagram
participant B as Service Bundle
participant RA as RPC agent / Data Tunnel
participant BR as SOME/IP broker (Rust)
participant ST as SOME/IP stack agent (C++)
participant E as External ECU
Note over B,E: Outbound RPC (SDV bundle calls an ECU)
B->>RA: call remote service
RA->>BR: route by FQIN
BR->>ST: rpc_transact(service, method_id, bytes)
ST->>E: SOME/IP request over vsomeip
E-->>ST: SOME/IP response
ST-->>BR: response bytes
BR-->>RA: translated reply
RA-->>B: result
Note over B,E: Inbound event (ECU notifies the vehicle)
E->>ST: SOME/IP event
ST->>BR: onEvent(service, event_id, bytes)
BR->>RA: publish to Data Tunnel topic
RA->>B: deliver to subscriber
The SDV-RPC VLAN¶
SDV-RPC traffic rides a dedicated VLAN so it can be isolated and policed separately from ordinary networking. The interface name is set either as a bootconfig variable, androidboot.sdv.rpc.interface=sdv_rpc, or via the SDV_RPC_INTERFACE build variable, and the reference Cuttlefish targets (sdv_core_cf, sdv_ivi_cf) default it to sdv_rpc (system/software_defined_vehicle/sdv_gateway/README.md). At runtime the gateway and networking services read it from the ro.boot.sdv.rpc.interface system property (system/software_defined_vehicle/sdv_gateway/service/cpp/SdvGatewayService.cpp).
62.8.5 The SDV Gateway: Bringing the IVI and the VHAL onto the Fabric¶
The middleware so far assumes SDV-aware Rust bundles built from VSIDL. But an AAOS IVI VM is full of ordinary Java apps and a Vehicle HAL that know nothing about FQINs, registration tokens, or the secure mesh. The SDV Gateway (system/software_defined_vehicle/sdv_gateway/) is the adapter that lets those non-SDV-aware clients reach the fabric. It is implemented mainly in C++ (the service/, libsdvgateway, and vhal_proxy pieces) with Java for the networking service and client SDK. Section 62.7.11 introduced the gateway from the AAOS side; this section details its interfaces.
The session model¶
A client first obtains the gateway, then opens a session. ISdvGateway (hardware/sdv/interfaces/sdv_gateway/google/sdv/gateway/ISdvGateway.aidl) is deliberately minimal — getVersion() and createSession() — and a process may hold only one session at a time. ISdvGatewaySession (.../ISdvGatewaySession.aidl) is the substantial interface; it mirrors the three middleware agents but for an untrusted caller:
initComms(InitCommsParams)— establishes the session's comms.InitCommsParams(.../InitCommsParams.aidl) carries the caller'sPublicKeyplus the three FQIN strings it is asking to use:sdvPackageName,serviceBundleName,serviceInstanceName.registerRpcServer(RegisterRpcServerParams)/findRpcServerByName(FindRpcServerByNameParams)— the gateway side of RPC; the find result returns aSocketAddress, a subject-alternative name, and the peer VM name.createPublication(CreatePublicationParams)returningIDataTunnelPublication, andsubscribeToPublicationByName(...)— the gateway side of Data Tunnel.rpcCredentialsConfig()returningRpcCredentialsConfigResult(auseInsecureRpcflag plus the subject-alternative name),requestCertificateChain(...), andsetAuthoritiesListener(...)— the gateway side of the certificate mesh.
Status comes back as SdvGatewayStatusCode (.../SdvGatewayStatusCode.aidl), a gRPC-style enum (OK, CANCELLED, … UNAUTHENTICATED).
The privileged interfaces¶
Behind the public session, the gateway also exposes a set of privileged interfaces under hardware/sdv/interfaces/sdv_gateway/google/sdv/privileged/, reserved for trusted system services rather than arbitrary apps:
IPrivilegedGatewayNetworking(privileged/gatewaynetworking/) — used only by the Javasdv_gatewaynetworking_service. It suppliesgetRpcNetworkInterfaceName(),setRpcNetworkHandle(long),onCarPowerStateChanged(CarPowerState), and listener registration.CarPowerStateenumerates the AAOS power states (WAIT_FOR_VHAL,ON,SHUTDOWN_PREPARE,SUSPEND_ENTER,HIBERNATION_ENTER, …) so the fabric can react to vehicle power transitions.IPrivilegedIdentityAgent,IPrivilegedServiceRegistrationAgent,IPrivilegedServiceDiscoveryAgent— the same identity/registration/discovery operations as §62.8.3, but taking an explicit process identifier argument so the gateway can act on behalf of a specific client process rather than its own.
The gateway AIDL is versioned and frozen under hardware/sdv/interfaces/sdv_gateway/aidl_api/: the public google.sdv.gateway package is at API v3 (with v1/v2 snapshots retained), and the privileged packages are at mixed versions — gatewaynetworking at v3, identity and service_discovery at v2 — the same backward-compatibility discipline as any stable AIDL HAL (Chapter 10).
The VHAL proxy¶
The flagship gateway client is the VHAL proxy (system/software_defined_vehicle/sdv_gateway/vhal_proxy/libvhal_proxy/). It lets a Vehicle HAL service surface vehicle properties as Data Tunnel topics and vice versa. Per its README, VhalProxy "handles parsing a VHAL proxy configuration file, subscribing to the services defined via Data Tunnel Publishers, and reading protobuf messages from those services. For every message, this library handles converting the protobuf value for each property ID and area ID pair, and creating a corresponding VehiclePropValue." Its API surface is four calls: ReadMessages, WriteMessages, Subscribe, and Unsubscribe. The config maps protobuf message types to VHAL property/area pairs and a Data Tunnel action (subscribe or publish) — a sample lives at system/software_defined_vehicle/samples/vhal_proxy/sdv_emulated_vhal/VhalProxySampleConfig.json.
This is what makes a stock AAOS CarService (§62.1) work over SDV: CarService reads vehicle properties from the VHAL exactly as on a one-VM device, and the VHAL proxy quietly sources those properties from vehicle services running in the Core VM, through the gateway, over the fabric.
The UID allowlist¶
Because the gateway lets an untrusted process claim an SDV package name (the second FQIN element), it must police which process may claim which name. That is the job of sdv_gateway_config.json, installed at /vendor/etc/sdv_gateway_config.json. The README is explicit: the file contains "the SDV package names (the second element of the FQIN) that native service (e.g. VHAL) are allowed to use when calling initComms," and "an empty config would prevent all native applications from using the SDV Gateway."
The allowlist is keyed by process UID (the same key §62.7.11 uses):
// Source: system/software_defined_vehicle/sdv_gateway/README.md (config example)
{
"allowed_native_packagename": {
"2942": [ "com.oemspecific.vhal" ]
},
"propagate_rpc_network_changes_to_data_tunnel": false,
"propagate_rpc_network_changes_to_service_discovery": false
}
UID 2942 here may register only the com.oemspecific.vhal SDV package name; a UID of -1 grants a name to all UIDs. The README recommends giving each native gateway client a unique AID and allowlisting only the names it truly needs, "to add another layer of security against the compromise or misuse of service discovery identities." The two propagate_rpc_network_changes_* flags, both false in the reference device/google/sdv/sdv_ivi_base/sdv_gateway_config.json, control whether an SDV-RPC VLAN change is pushed to the Data Tunnel and Service Discovery agents (relevant only when those agents share the RPC interface rather than running on separate VLANs).
62.8.6 Automotive Services: the Domain Catalog¶
The fabric so far is domain-agnostic plumbing. On top of it, system/software_defined_vehicle/automotive_services/ ships the automotive-specific service catalog. The repo README names the five: "Diagnostics, Configuration, Calibration, Vehicle Mode, User Profile." These are defined in VSIDL/proto and implemented in Rust as ordinary service bundles riding the Data Tunnel and RPC agents.
Diagnostics (ISO 14229-1 / AUTOSAR)¶
Diagnostics is the most developed of the five and the one that most clearly shows SDV adopting an automotive standard rather than inventing one. Its README states the lineage outright: "APIs are based on ISO 14229-1:2020 and AUTOSAR Diagnostic Event Manager standards" (system/software_defined_vehicle/automotive_services/diagnostics/README.md), and links the ISO 14229-1:2020 (UDS) and AUTOSAR DEM specifications as external references.
The interface set under system/software_defined_vehicle/automotive_services/diagnostics/vsidl/v1/ maps directly onto UDS:
diagnostics_manager_service.proto— the RPC API a service uses to query connection parameters from the Diagnostics Manager.connection.proto—SessionType(default, programming, extended-diagnostic, safety-system-diagnostic sessions per UDS) andConnectionParameters(source/target address, session type).event.proto— fault events whoseStatusenum (PASS, FAIL, PRE_PASS, PRE_FAIL) follows the AUTOSAR Diagnostic Event Manager, with anOperationCycle(START/STOP/RESTART) and anEnableConditionpublished over Data Tunnel.response_code.proto— the ISO 14229-1 negative-response codes.- Per-service interfaces for the standard UDS routines:
routine_control_service.proto,io_control_service.proto,ecu_reset_service.proto,security_access_service.proto,authentication_service.proto,file_transfer_service.proto, andfault_listener_service.proto.
A reference sdv_diagnostics_agent (system/software_defined_vehicle/automotive_services/diagnostics/tests/agent/src/main.rs) wires these together and listens for DoIP (Diagnostics over IP, ISO 13400) traffic — the path an external diagnostic tester uses to reach the vehicle.
Configuration, Calibration, Vehicle Mode, User Profile¶
The remaining four follow the same pattern — proto-defined VSIDL services, Rust implementations:
- Configuration / Calibration (ConCal) (
automotive_services/concal/) lets services be reconfigured and calibrated at runtime.ConCalCalibrationService(concal/catalog/concal_calibration_service.proto) exposesGetCalibrationConfigIds,StartCalibration,UpdateConfigForCalibration, andFinishCalibration, with companion registration, update, and notification services. - User Profile (
automotive_services/user_preferences/) manages per-user settings.UserPreferencesManagementService(user_preferences/vsidl/v1/user_preferences_management_service.proto) offersRequestSettingsChange,SubscribeToSettingsChangeAndGetSettings, andUnsubscribeFromSettingsChange, alongside admin, registry, and change-notifier services. - Vehicle Mode is named in the catalog README as one of the five automotive services; vehicle power-state handling itself lives in the Vehicle Power Manager (
system/software_defined_vehicle/vpm/), covered with orchestration in §62.7.7.
62.8.7 Samples and Tools¶
SDV ships an unusually large body of runnable examples and host tooling, because a distributed multi-VM fabric is hard to learn from contracts alone.
Samples¶
system/software_defined_vehicle/samples/ holds reference bundles, each with its own README:
- quickstart (
samples/quickstart/) packages two bundles (aManagerand aMonitor) into an APEX, withvsidlc-generated Rust middleware, demonstrating the end-to-end flow from.vsidlto a deployable, lifecycle-launched bundle. - qos_scheduling (
samples/qos_scheduling/) shows scheduling profiles affecting CPU-bound bundles. - tracing instruments both C++ (Perfetto SDK) and Rust (the
tracingcrate pluslibatrace_rust) bundles and shows AIDL calls auto-emitting ATrace events. - sdv_gateway (
samples/sdv_gateway/) is an IVI app reaching SDV services through the gateway client library, exercising Service Discovery, Data Tunnel, and RPC together with TLS. - oem_partition_update_client demonstrates the OEM A/B partition update flow (prepare, activate, commit, rollback) via an
OemUpdatercrate. - cujs (
samples/cujs/) is a large set of Critical User Journeys — pub/sub, RPC, pub/sub-with-power-suspend, multi-publisher, and robustness scenarios — that double as integration tests across VMs.
Host tools¶
system/software_defined_vehicle/tools/ holds the host-side, mostly-Rust tooling that supports the build and provisioning flows:
- regenerator (
tools/regenerator/) re-runsvsidlcto refresh generated middleware catalogs, driven byCATALOG_UPDATEtextproto files that record the output path, dependency catalogs, and generator flags. - vvmconfig_generator (
tools/vvmconfig_generator/) converts a human-readable JSON vehicle-VM topology into the CBORvvmconfigblob the Service Discovery agent consumes — JSON in git, binary CBOR at build time. - sdv_provisioning_tool (
tools/sdv_provisioning_tool/) drives secure-mesh provisioning: it waits until the mesh is complete, writes/vvmtruststore/uds_pubs, and returns its SHA-256 as the factory trust anchor. - vhal_json_generator (
tools/vhal_json_generator/) is a Rust host binary that reads VSIDL VHAL mappings and emits the JSON config the VHAL proxy (§62.8.5) loads. - test_uds_certs_generator (
tools/test_uds_certs_generator/) generatesuds_certsfiles for development and testing of the mesh.
62.9 Try It¶
Exercise 62.1: Explore CarService Services¶
List all services registered by ICarImpl:
# On an automotive emulator or device:
adb shell dumpsys car_service --services
# List all VHAL properties:
adb shell dumpsys car_service --hal
# Check occupant zone configuration:
adb shell dumpsys car_service --occ-zone
Examine the CarService initialization timing:
# View initialization trace:
adb shell dumpsys car_service --print-timing
# Or look at the system property:
adb shell getprop boot.car_service_created
Exercise 62.2: Inspect Vehicle HAL Properties¶
Query vehicle properties using the car service shell:
# Get vehicle speed:
adb shell cmd car_service get-property-value PERF_VEHICLE_SPEED
# Get gear selection:
adb shell cmd car_service get-property-value GEAR_SELECTION
# List all property configs (or one config by name/id):
adb shell cmd car_service get-carpropertyconfig
# Set HVAC temperature (on emulator):
# set-property-value <PROPERTY> <areaId> <data>
adb shell cmd car_service set-property-value HVAC_TEMPERATURE_SET 49 22.5
Exercise 62.3: Trace a VHAL Property Event¶
Use atrace to follow a property change through the stack:
# Start tracing with car_service tag:
adb shell atrace --async_start -c -b 8192 car_service
# Trigger a property change (e.g., change gear on emulator)
# ... interact with the emulator's vehicle controls ...
# Stop tracing:
adb shell atrace --async_stop > car_trace.html
# Look for spans:
# VehicleHal.onPropertyEvent
# PropertyHalService.onHalEvents
# CarPropertyService.onPropertyChange
Exercise 62.4: Examine TV Input Framework¶
On a TV emulator or device:
# List registered TV inputs:
adb shell dumpsys tv_input
# List tuner resources:
adb shell dumpsys tv_tuner_resource_mgr
# Check HDMI-CEC state:
adb shell dumpsys hdmi_control
# Send a CEC message (requires root):
adb shell cmd hdmi_control cec_setting set hdmi_cec_enabled 1
Exercise 62.5: Investigate TV PIP Behavior¶
On a TV emulator:
# Check PIP state:
adb shell dumpsys activity service SystemUIService | grep -A 20 "PipController"
# Enter PIP mode for current activity:
adb shell input keyevent KEYCODE_WINDOW
# D-pad navigation while in PIP:
adb shell input keyevent KEYCODE_DPAD_LEFT
adb shell input keyevent KEYCODE_DPAD_RIGHT
Exercise 62.6: Check Device Form Factor¶
Determine the form factor programmatically:
# Check hardware type:
adb shell getprop ro.hardware.type
# Returns: "automotive", "tv", "watch", or empty (phone)
# Check UI mode:
adb shell dumpsys uimode | grep "mCurUiMode"
# Check features:
adb shell pm list features | grep -E "automotive|leanback|watch"
# android.hardware.type.automotive
# android.software.leanback
# android.hardware.type.watch
Exercise 62.7: Inspect Automotive Power States¶
On an automotive emulator:
# Check current power state:
adb shell dumpsys car_service --power
# Simulate garage mode:
adb shell cmd car_service garage-mode on
# Check garage mode status:
adb shell cmd car_service garage-mode query
# Simulate shutdown prepare:
adb shell cmd car_service power-off --skip-garagemode
Exercise 62.8: Explore Car SystemUI Components¶
# List Car SystemUI services:
adb shell dumpsys activity services com.android.systemui | grep car
# Check system bar state:
adb shell dumpsys car_service --act
# Inspect HVAC properties used by SystemUI:
adb shell cmd car_service get-property-value HVAC_TEMPERATURE_SET
adb shell cmd car_service get-property-value HVAC_FAN_SPEED
Exercise 62.9: Examine Occupant Zones¶
# Dump occupant zone configuration:
adb shell dumpsys car_service --occ-zone
# Output shows:
# - Zone definitions (driver, passenger, rear)
# - Display assignments per zone
# - User assignments per zone
# - Audio zone mappings
# - Input type support per display
Exercise 62.10: Build Automotive Emulator Image¶
# Set up the build environment:
source build/envsetup.sh
# Choose an automotive target:
lunch sdk_car_x86_64-userdebug
# Build the image:
m -j$(nproc)
# Launch the automotive emulator:
emulator -no-snapshot
Exercise 62.11: Trace CEC Message Handling¶
# Enable CEC debug logging:
adb shell setprop log.tag.HdmiCecLocalDeviceTv DEBUG
adb shell setprop log.tag.HdmiControlService DEBUG
# Watch CEC messages:
adb logcat -s HdmiControlService:D HdmiCecLocalDeviceTv:D
# Simulate a CEC device connection (if supported):
adb shell cmd hdmi_control onetouchplay
Exercise 62.12: Investigate WMShell Module Selection¶
Examine which WMShell module is active:
# On phone:
adb shell dumpsys activity service SystemUIService | grep "WMShell"
# On TV:
adb shell dumpsys activity service SystemUIService | grep "TvPip"
# Inspect the PIP implementation class:
adb shell dumpsys activity service SystemUIService | grep -A 5 "Pip"
Compare the TvWMShellModule dependencies with the default WMShellModule by examining:
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
Exercise 62.13: Measure Wear Battery Impact of AOD¶
On a Wear emulator or device:
# Check display state:
adb shell dumpsys display | grep -A 5 "mScreenState"
# Monitor battery drain:
adb shell dumpsys battery
# Toggle ambient mode:
adb shell input keyevent KEYCODE_SLEEP
adb shell input keyevent KEYCODE_WAKEUP
# Check power stats:
adb shell dumpsys batterystats --checkin
Exercise 62.14: Compare RRO Layering¶
Examine how RROs stack across an automotive product:
# List all overlays:
adb shell cmd overlay list
# Check overlay state for SystemUI:
adb shell cmd overlay list com.android.systemui
# Dump overlay priority ordering:
adb shell dumpsys overlay | grep -A 3 "car"
Exercise 62.15: Source Code Exploration Tasks¶
Study the following files to understand form-factor abstractions:
-
CarService bootstrap chain:
packages/services/Car/service/src/com/android/car/CarServiceImpl.javapackages/services/Car/service/src/com/android/car/ICarImpl.javapackages/services/Car/service/src/com/android/car/VehicleStub.java
-
Vehicle HAL interface:
-
hardware/interfaces/automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl -
TV Input Framework:
frameworks/base/media/java/android/media/tv/TvInputService.javaframeworks/base/services/core/java/com/android/server/tv/TvInputManagerService.javaframeworks/base/services/core/java/com/android/server/tv/TvInputHardwareManager.java
-
HDMI-CEC:
hardware/interfaces/tv/cec/1.0/IHdmiCec.halframeworks/base/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
-
WMShell TV variant:
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.javaframeworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.javaframeworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
-
Car SystemUI:
packages/apps/Car/SystemUI/src/com/android/systemui/car/CarServiceProvider.javapackages/apps/Car/SystemUI/src/com/android/systemui/car/systembar/CarSystemBar.java
-
Occupant Zones:
packages/services/Car/service/src/com/android/car/CarOccupantZoneService.java
-
Fixed Activity:
packages/services/Car/service/src/com/android/car/am/FixedActivityService.java
-
Automotive Power:
packages/services/Car/service/src/com/android/car/power/CarPowerManagementService.javapackages/services/Car/service/src/com/android/car/garagemode/GarageModeService.java
-
Wearable Sensing: -
frameworks/base/services/core/java/com/android/server/wearable/WearableSensingManagerService.java-frameworks/base/core/java/android/app/wearable/WearableSensingManager.java
Exercise 62.16: Explore the SDV Platform (Architecture)¶
These exercises use the AOSP 17 source tree; none require building a vehicle. Run them from the root of a synced android17-release checkout.
-
List the Core VM's agents. Open
device/google/sdv/sdv_core_base/sdv_packages_core_services.mkand map eachSDV_CORE_SERVICES_PACKAGESentry to a subsection of §62.7. Which entries are agents, which are APEXes, and which are host build tools? -
Read a real service-bundle definition. Open
system/software_defined_vehicle/vpm/stable/vsidl/vpm.vsidl. Identify the bundle'sserver,client, andpublisherslots. Then opensystem/software_defined_vehicle/vpm/stable/vsidl/power.protoandvehicle.protoand list thePowerStateReportandVpmVehicleStateenum values. Which of these would an orchestrationconditionmatch against? -
Trace the lifecycle contract. Read
system/software_defined_vehicle/lifecycle_management/aidl/google/sdv/lifecycle/ILifecycleManager.aidlandhardware/sdv/interfaces/lifecycle_management/aidl/google/sdv/lifecycle/internal/IService.aidl. Match eachILifecycleManagermethod (launchService,startService,stopService,shutdownService) to theIServicecallback it triggers (onCreate,onStart,onStop,onDestroy). -
Inspect the orchestration config schema. Open
system/software_defined_vehicle/orchestration/distributed_config/src/protos/service_bundle_config.protoandcommon.proto. How does anInstancesStateConfigurationpair aconditionwith desired instance states? What are the three logical operators aConditioncan use? -
Find the frozen registry API. List
hardware/sdv/interfaces/service_bundles_registry/aidl_api/google.sdv.service_bundles_registry/and confirm the current frozen version. Open the version-3IRegistry.aidland confirm it exposes onlygetAvailableServiceBundlesMetadata(). -
Read the gateway allowlist. Open
system/software_defined_vehicle/sdv_gateway/README.mdanddevice/google/sdv/sdv_ivi_base/sdv_gateway_config.json. Explain whatallowed_native_packagenamegates, and why a UID of-1is a security risk worth avoiding. -
Find the display-safety bundle. In
packages/services/display_safety/service/har-sdv-service/Android.bp, find thelibhar_sdv_service_bundlemodule and list its dependencies. Which dependency is the SDV middleware, and which is the gRPC service layer?
Exercise 62.17: Explore the SDV Communication Fabric (Middleware)¶
These commands assume an SDV Core VM or a Cuttlefish SDV target (sdv_core_cf, sdv_ivi_cf) and a checked-out tree at system/software_defined_vehicle/.
-
Read a service bundle's contract. Open
system/software_defined_vehicle/samples/vsidl/complex/catalog/complex_message_publisher.vsidland its subscriber peer. Identify the sharedtopicname and the shared RPCchannel— that is the only thing the two halves agree on at authoring time. -
See what
vsidlcwould generate. Readsystem/software_defined_vehicle/vsidl/vsidlc/README.md, then look at the generation steps undersystem/software_defined_vehicle/vsidl/vsidlc/src/rust/steps/. Note that the output goes intooutput/generated_rs, the SDV analogue of AIDL stubs. -
Trace the SOME/IP boundary. Read
hardware/sdv/interfaces/some_ip/stack_agent/aidl/google/sdv/someip/ISomeIpStack.aidl. Findrpc_transact,publish, andsubscribe_eventgroup, and confirm that every payload crossing this interface is a rawbyte[]— translation happens in the broker, not the stack. -
Inspect the gateway allowlist. Read
device/google/sdv/sdv_ivi_base/sdv_gateway_config.jsonand the "Service config" section ofsystem/software_defined_vehicle/sdv_gateway/README.md. Work out which UID is allowed to claim which SDV package name, and what an empty config would do. -
Map a UDS routine to a proto. Open
system/software_defined_vehicle/automotive_services/diagnostics/vsidl/v1/connection.protoand find theSessionTypeenum. Match each value against ISO 14229-1's diagnostic session types (default, programming, extended, safety-system). -
Check the mesh on a running target. On a Core VM,
adb shelland list the SDV agent services withservice list | grep sdv(ordumpsys), then look for the Data Tunnel APEXcom.android.sdv.dt. Inspecting/vvmtruststore/shows the mesh truststore the provisioning tool wrote.
Summary¶
Form Factor Comparison Matrix¶
| Aspect | Phone | Automotive | TV | Wear |
|---|---|---|---|---|
| Primary input | Touch | Touch + Rotary + Voice | D-pad remote | Touch + Crown button |
| Display count | 1-2 | 2-6+ | 1 | 1 |
| Display shape | Rectangle | Rectangle | Rectangle | Round or square |
| SystemUI | Default SystemUI | Car SystemUI | TV SystemUI | Wear SystemUI |
| Launcher | Launcher3 | Car Launcher | TV Launcher | Clock/Tiles |
| WMShell module | WMShellModule | N/A (own SystemUI) | TvWMShellModule | Simplified |
| PIP support | Full (touch, drag) | None (safety) | TV PIP (D-pad) | None |
| Split screen | Yes | Per-display | TV Split | No |
| Navigation | Gesture/3-button | System bar buttons | D-pad focus | Swipe + button |
| Power model | Battery + charger | Vehicle ignition | Wall power | Small battery |
| Special power state | Doze | Garage Mode | Screen saver | Ambient/AOD |
| Multi-user | Single active | Per-zone users | Single | Single |
| Safety restrictions | None | UX restrictions while driving | None | None |
| Key HAL interfaces | Standard | Vehicle, EVS, AudioControl | TvInput, CEC, Tuner | Sensors |
| Feature flag | (default) | android.hardware.type.automotive | android.software.leanback | android.hardware.type.watch |
| hardware.type | (none) | automotive | tv | watch |
Key Source Trees by Form Factor¶
| Form Factor | Service Code | Apps | HAL Interfaces | Product Config |
|---|---|---|---|---|
| Automotive | packages/services/Car/service/ |
packages/apps/Car/ |
hardware/interfaces/automotive/ |
packages/services/Car/car_product/ |
| TV | frameworks/base/services/core/.../tv/ |
(vendor-specific) | hardware/interfaces/tv/ |
(vendor-specific) |
| Wear | frameworks/base/services/core/.../wearable/ |
(vendor-specific) | Standard + sensors | (vendor-specific) |
Android's form-factor extensibility is one of its most impressive architectural features. Rather than maintaining separate codebases, the platform uses a layered customization approach:
-
HAL abstraction separates hardware-specific details (Vehicle HAL, TV tuner HAL, CEC HAL) from framework logic.
-
System services extend the base framework with form-factor-specific functionality (CarService with ~40 subsystems, TvInputManagerService, TunerResourceManagerService).
-
Dagger module substitution (WMShellModule vs. TvWMShellModule) swaps entire subsystem implementations at build time without forking the base code.
-
SystemUI replacement (Car SystemUI, TV SystemUI) provides completely different user shells while sharing the underlying window management infrastructure.
-
Runtime Resource Overlays enable visual and behavioral customization without source changes, organized in product-specific overlay trees.
-
Product configuration (makefiles, properties, feature flags) wires everything together, selecting which packages, overlays, and policies constitute a complete form-factor product.
The automotive stack is by far the most complex, with multi-zone displays, vehicle power management, driver distraction enforcement, and a complete Vehicle HAL abstraction. TV adds the TIF, CEC, and specialized PIP. Wear strips away complexity, simplifying windowing and adding round-display and battery-optimization support. All three prove that Android's architecture -- despite its complexity -- is genuinely modular enough to serve radically different device categories from a single codebase.
Software Defined Vehicle¶
The SDV platform (§62.7 and §62.8) is the next step beyond AAOS: rather than running the whole vehicle stack inside one Android image, it splits the vehicle into a headless Core VM of service bundles and one or more AAOS IVI VMs that reach them over a service fabric.
- SDV is a headless vehicle OS. The Core VM runs Android with no UI
(
device/google/sdv/sdv_core_base/sdv_core_base.mk), hosting service bundles and the agents that supervise them, while AAOS IVI VMs and external ECUs talk to it over a service fabric. - The unit of deployment is the service bundle, a VSIDL-generated Rust
.soshipped in an APEX with anSdvServiceBundleManifestentry, identified at runtime by a Fully Qualified Instance Name (package/bundle/instance). - VSIDL replaces AIDL for bundle interfaces.
vsidlcand its companions generate Rust middleware bindings (generated_rs) and SOME/IP translation from.vsidl+.protocatalogs; per-message translation modes (INTERPRET_AS_BYTES,DYNAMIC_LIBRARY) decide how a type crosses onto SOME/IP. - Four control-plane agents supervise bundles. The Service Bundles Registry (
IRegistry, frozen at v3) catalogs them; the Lifecycle Manager (ILifecycleManager+ theIServicecallbacks, vialifecycle_service_bundle_runnerprocesses) launches/starts/stops them; the Orchestrator holds a desired state per FQIN and reconciles it on every mode change with retry/recovery semantics; the Health Monitor watches per-bundle heartbeats and feeds failures back into recovery. - vpm drives the modes. The vehicle power-state manager (
sdv_vpm_agent) defines thePowerStateReportandVpmVehicleStateladders and publishes transitions, which are exactly what orchestration conditions match against; the Update Manager is power-aware in the same way. - The communication fabric is a network-spanning version of Android's IPC idioms. Three
Binder agents — Service Discovery (Rust), Data Tunnel (C++, named-topic pub/sub over FMQ), and
RPC (socket-based) — are handed to a bundle as one
ContextInitializationToken. Beneath them, a per-VM certificate authority, identity agent, and authorization service form a mutually-authenticated secure mesh over mTLS with P-256 certificates that encode the FQIN. - SOME/IP carries cross-VM and cross-ECU traffic. A C++
vsomeipstack agent presentsISomeIpStack(rawbyte[]payloads), and a Rust broker maps SDV topics/RPC/messages onto SOME/IP services, events, and methods. - AAOS integrates through the SDV Gateway.
ISdvGateway/ISdvGatewaySession, plus privileged interfaces, give ordinary IVI native/Java clients and the VHAL proxy a UID-allowlisted door into the fabric (/vendor/etc/sdv_gateway_config.json);libvhal_proxymakes a Core-VM vehicle property look like an ordinary VHAL property to CarService, with SDV-RPC traffic on a dedicated VLAN. - The domain catalog and tooling round it out.
automotive_services/layers Diagnostics (ISO 14229-1 / AUTOSAR DEM, DoIP), Configuration, Calibration, Vehicle Mode, and User Profile on top, whilesamples/andtools/provide runnable references and the host-side codegen/provisioning toolchain. Display safety (HARry) is an IVI-side Rust runtime whoselibhar_sdv_service_bundlejoins the SDV fabric, with ahar_safety_monitorenforcing distraction/compliance constraints, shipped as SDV-only APEXes.
Key Source Files Reference¶
SDV platform (§62.7) and middleware (§62.8):
| File | Purpose |
|---|---|
device/google/sdv/sdv_core_base/sdv_packages_core_services.mk |
Canonical list of agents/APEXes installed in the Core VM |
device/google/sdv/AndroidProducts.mk |
SDV lunch targets (Core/IVI/Media, cf and arm64) |
device/google/sdv/sdv_core_base/sdv_core_base.mk |
Declares SDV a headless vehicle Android OS |
system/software_defined_vehicle/service_bundles_registry/proto/sdv_service_bundles_manifest.proto |
Service-bundle manifest schema |
hardware/sdv/interfaces/service_bundles_registry/google/sdv/service_bundles_registry/IRegistry.aidl |
Registry stable AIDL (frozen v3) |
system/software_defined_vehicle/lifecycle_management/aidl/google/sdv/lifecycle/ILifecycleManager.aidl |
Lifecycle control interface |
hardware/sdv/interfaces/lifecycle_management/aidl/google/sdv/lifecycle/internal/IService.aidl |
Bundle lifecycle callbacks (onCreate/onStart/onStop/onDestroy) |
system/software_defined_vehicle/lifecycle_management/service_bundle_runner/src/main.rs |
The per-instance bundle runner process |
system/software_defined_vehicle/orchestration/engine/src/engine.rs |
Orchestrator reconciliation engine |
system/software_defined_vehicle/health_monitor/src/hb_config.rs |
Heartbeat monitoring configuration |
system/software_defined_vehicle/vpm/stable/vsidl/power.proto |
PowerStateReport enum |
system/software_defined_vehicle/vpm/stable/vsidl/vehicle.proto |
VpmVehicleState enum |
hardware/sdv/interfaces/vehicle_power_manager/aidl/google/sdv/vpm/IPowerStateClientApi.aidl |
Client power-state subscription AIDL |
system/software_defined_vehicle/update_manager/catalog/update_manager_service.proto |
Update Manager state machine |
hardware/sdv/interfaces/middleware/ctx/aidl/google/sdv/comms/ContextInitializationToken.aidl |
The use-once token bundling a Service Bundle's four agent connections |
hardware/sdv/interfaces/middleware/service_discovery/google/sdv/identity/ServiceFqin.aidl |
Vehicle-wide bundle identity (the FQIN) |
hardware/sdv/interfaces/middleware/rpc/google/sdv/rpc/IRpcAgent.aidl |
Socket-based RPC agent contract |
hardware/sdv/interfaces/middleware/data_tunnel/aidl/google/sdv/data_tunnel/IAgentService.aidl |
Named-topic pub/sub over FMQ |
hardware/sdv/interfaces/middleware/service_discovery/google/sdv/ca/ICertificateAuthority.aidl |
Per-VM CA for the secure mesh |
system/software_defined_vehicle/vsidl/vsidlc/README.md |
VSIDL compiler: catalog to generated_rs |
system/software_defined_vehicle/vsidl/language/src/protos/sdv/vsidl/v1/syntax.proto |
The VSIDL textproto grammar |
hardware/sdv/interfaces/some_ip/stack_agent/aidl/google/sdv/someip/ISomeIpStack.aidl |
SOME/IP stack boundary (raw bytes) |
system/software_defined_vehicle/some_ip/broker_agent_comms/src/main.rs |
SOME/IP-to-SDV broker (Rust) |
hardware/sdv/interfaces/sdv_gateway/google/sdv/gateway/ISdvGatewaySession.aidl |
Gateway session for non-SDV-aware clients |
system/software_defined_vehicle/sdv_gateway/README.md |
Gateway config, UID allowlist, SDV-RPC VLAN |
system/software_defined_vehicle/sdv_gateway/vhal_proxy/libvhal_proxy/README.md |
VHAL proxy: properties to Data Tunnel topics |
system/software_defined_vehicle/automotive_services/diagnostics/README.md |
Diagnostics: ISO 14229-1 / AUTOSAR DEM |
packages/services/display_safety/service/har-sdv-service/Android.bp |
The display-safety SDV service bundle |