/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.car; import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; import android.annotation.NonNull; import android.car.Car; import android.car.builtin.os.ServiceManagerHelper; import android.car.builtin.util.Slogf; import android.car.occupantawareness.IOccupantAwarenessEventCallback; import android.car.occupantawareness.OccupantAwarenessDetection; import android.car.occupantawareness.OccupantAwarenessDetection.VehicleOccupantRole; import android.car.occupantawareness.SystemStatusEvent; import android.car.occupantawareness.SystemStatusEvent.DetectionTypeFlags; import android.content.Context; import android.hardware.automotive.occupant_awareness.IOccupantAwareness; import android.hardware.automotive.occupant_awareness.IOccupantAwarenessClientCallback; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Log; import android.util.proto.ProtoOutputStream; import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; import com.android.car.internal.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.lang.ref.WeakReference; /** * A service that listens to an Occupant Awareness Detection system across a HAL boundary and * exposes the data to system clients in Android via a {@link * android.car.occupantawareness.OccupantAwarenessManager}. * *
The service exposes the following detections types: * *
Capabilities are static for a given vehicle configuration and need only be queried once * per vehicle. Once capability is determined, clients should query system status to see if the * subsystem is currently ready to serve. * *
Requires {@link android.car.Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE} read * permissions to access. * * @param role {@link VehicleOccupantRole} to query for. * @return Flags indicating supported capabilities for the role. */ public @DetectionTypeFlags int getCapabilityForRole(@VehicleOccupantRole int role) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE); connectToHalServiceIfNotConnected(false); // Grab a copy of 'mOasHal' to avoid sitting on the lock longer than is necessary. IOccupantAwareness hal; synchronized (mLock) { hal = mOasHal; } if (hal != null) { try { return hal.getCapabilityForRole(role); } catch (RemoteException e) { Slogf.e(TAG, "getCapabilityForRole() HAL invocation failed: " + e, e); synchronized (mLock) { mOasHal = null; } return SystemStatusEvent.DETECTION_TYPE_NONE; } } else { Slogf.e(TAG, "getCapabilityForRole(): No HAL interface has been provided. Cannot get" + " capabilities"); return SystemStatusEvent.DETECTION_TYPE_NONE; } } /** * Registers a {@link IOccupantAwarenessEventCallback} to be notified for changes in the system * state. * *
Requires {@link android.car.Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE} read * permissions to access. * * @param listener {@link IOccupantAwarenessEventCallback} listener to register. */ @Override public void registerEventListener(@NonNull IOccupantAwarenessEventCallback listener) { CarServiceUtils.assertPermission(mContext, Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE); connectToHalServiceIfNotConnected(false); synchronized (mLock) { if (mOasHal == null) { Slogf.e(TAG, "Attempting to register a listener, but could not connect to HAL."); return; } logd("Registering a new listener"); mListeners.register(listener); // After the first client connects, request that the detection graph start. if (mListeners.getRegisteredCallbackCount() == 1) { startDetectionGraph(); } } } /** * Unregister the given {@link IOccupantAwarenessEventCallback} listener from receiving events. * *
Requires {@link android.car.Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE} read
* permissions to access.
*
* @param listener {@link IOccupantAwarenessEventCallback} client to unregister.
*/
@Override
public void unregisterEventListener(@NonNull IOccupantAwarenessEventCallback listener) {
CarServiceUtils.assertPermission(mContext,
Car.PERMISSION_READ_CAR_OCCUPANT_AWARENESS_STATE);
connectToHalServiceIfNotConnected(false);
synchronized (mLock) {
mListeners.unregister(listener);
}
// When the last client disconnects, request that the detection graph stop.
handleClientDisconnected();
}
/** Processes a detection event and propagates it to registered clients. */
@VisibleForTesting
void processStatusEvent(@NonNull SystemStatusEvent statusEvent) {
int idx = mListeners.beginBroadcast();
while (idx-- > 0) {
IOccupantAwarenessEventCallback listener = mListeners.getBroadcastItem(idx);
try {
listener.onStatusChanged(statusEvent);
} catch (RemoteException e) {
// It's likely the connection snapped. Let binder death handle the situation.
Slogf.e(TAG, "onStatusChanged() invocation failed: " + e, e);
}
}
mListeners.finishBroadcast();
}
/** Processes a detection event and propagates it to registered clients. */
@VisibleForTesting
void processDetectionEvent(@NonNull OccupantAwarenessDetection detection) {
int idx = mListeners.beginBroadcast();
while (idx-- > 0) {
IOccupantAwarenessEventCallback listener = mListeners.getBroadcastItem(idx);
try {
listener.onDetectionEvent(detection);
} catch (RemoteException e) {
// It's likely the connection snapped. Let binder death handle the situation.
Slogf.e(TAG, "onDetectionEvent() invocation failed: " + e, e);
}
}
mListeners.finishBroadcast();
}
/** Handle client disconnections, possibly stopping the detection graph. */
void handleClientDisconnected() {
// If the last client disconnects, requests that the graph stops.
synchronized (mLock) {
if (mListeners.getRegisteredCallbackCount() == 0) {
stopDetectionGraph();
}
}
}
private static void logd(String msg) {
if (DBG) {
Slogf.d(TAG, msg);
}
}
/**
* Class that implements the listener interface and gets called back from the {@link
* android.hardware.automotive.occupant_awareness.IOccupantAwarenessClientCallback} across the
* binder interface.
*/
private static class ChangeListenerToHalService extends IOccupantAwarenessClientCallback.Stub {
private final WeakReference