/* * Copyright (C) 2021 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.hal; import static android.car.evs.CarEvsManager.SERVICE_TYPE_REARVIEW; import static android.car.evs.CarEvsManager.SERVICE_TYPE_SURROUNDVIEW; import static android.hardware.automotive.vehicle.VehicleProperty.CAMERA_SERVICE_CURRENT_STATE; import static android.hardware.automotive.vehicle.VehicleProperty.EVS_SERVICE_REQUEST; import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; import android.car.builtin.util.Slogf; import android.car.evs.CarEvsManager.CarEvsServiceState; import android.car.evs.CarEvsManager.CarEvsServiceType; import android.hardware.automotive.vehicle.EvsServiceRequestIndex; import android.hardware.automotive.vehicle.EvsServiceState; import android.hardware.automotive.vehicle.EvsServiceType; import android.os.ServiceSpecificException; import android.util.Log; import android.util.SparseArray; import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; import java.util.Collection; import java.util.List; /* * Translates HAL events, CarEvsService is interested in, into the higher-level semantic * information. */ public class EvsHalService extends HalServiceBase { private static final String TAG = EvsHalService.class.getSimpleName(); private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG); private static final int[] SUPPORTED_PROPERTIES = new int[] { CAMERA_SERVICE_CURRENT_STATE, EVS_SERVICE_REQUEST, }; private final Object mLock = new Object(); @GuardedBy("mLock") private final SparseArray mProperties = new SparseArray(); private final VehicleHal mHal; private final HalPropValueBuilder mPropValueBuilder; @GuardedBy("mLock") private EvsHalEventListener mListener; private boolean mIsEvsServiceRequestSupported; private boolean mIsCameraServiceCurrentStateSupported; public EvsHalService(VehicleHal hal) { mHal = hal; mPropValueBuilder = hal.getHalPropValueBuilder(); } /** * Interface to be implemented by any client that wants to get notified upon a new EVS service * request. */ public interface EvsHalEventListener { /** Called when a value of monitoring VHAL property gets changed */ void onEvent(@CarEvsServiceType int id, boolean on); } /** * Sets the event listener to receive Vehicle's EVS-related events. * * @param listener {@link EvsHalEventListener} * @throws IllegalStateException if none of required VHAL properties are not supported * on this device. */ public void setListener(EvsHalEventListener listener) { if (!mIsEvsServiceRequestSupported) { throw new IllegalStateException( "Any of required VHAL properties are not supported."); } synchronized (mLock) { mListener = listener; } mHal.subscribePropertySafe(this, EVS_SERVICE_REQUEST); } /** Returns whether {@code EVS_SERVICE_REQUEST} is supported */ public boolean isEvsServiceRequestSupported() { return mIsEvsServiceRequestSupported; } /** Returns whether {@code CAMERA_SERVICE_CURRENT_STATE} is supported */ public boolean isCameraServiceCurrentStateSupported() { return mIsCameraServiceCurrentStateSupported; } @Override public void init() { synchronized (mLock) { for (int i = 0; i < mProperties.size(); i++) { HalPropConfig config = mProperties.valueAt(i); if (VehicleHal.isPropertySubscribable(config)) { mHal.subscribePropertySafe(this, config.getPropId()); } } } } @Override public void release() { synchronized (mLock) { mListener = null; mProperties.clear(); } } @Override public int[] getAllSupportedProperties() { return SUPPORTED_PROPERTIES; } @Override public void takeProperties(Collection configs) { if (configs.isEmpty()) { return; } synchronized (mLock) { for (HalPropConfig config : configs) { mProperties.put(config.getPropId(), config); } mIsEvsServiceRequestSupported = mProperties.contains(EVS_SERVICE_REQUEST); mIsCameraServiceCurrentStateSupported = mProperties.contains(CAMERA_SERVICE_CURRENT_STATE); } } @Override public void onHalEvents(List values) { EvsHalEventListener listener; synchronized (mLock) { listener = mListener; } if (listener == null) { Slogf.w(TAG, "EVS Hal event occurs while the listener is null."); return; } dispatchHalEvents(values, listener); } /** * Reports the current state of CarEvsService. * * @param state {@link android.car.evs.CarEvsManager.CarEvsServiceState} value that represents * the current state of {@code CarEvsService}. */ public void reportCurrentState(@CarEvsServiceState int[] state) { HalPropValue currentStates = mPropValueBuilder.build(CAMERA_SERVICE_CURRENT_STATE, /* areaId= */ 0, /* values= */ state); try { mHal.set(currentStates); } catch (ServiceSpecificException | IllegalArgumentException e) { Slogf.e(TAG, "Failed to set a hal property: %s, err: %s", currentStates, e); } } private void dispatchHalEvents(List values, EvsHalEventListener listener) { for (int i = 0; i < values.size(); ++i) { HalPropValue v = values.get(i); boolean on = false; @CarEvsServiceType int type; switch (v.getPropId()) { case EVS_SERVICE_REQUEST: // Check // android.hardware.automotive.vehicle.VehicleProperty.EVS_SERVICE_REQUEST try { int rawServiceType = v.getInt32Value(EvsServiceRequestIndex.TYPE); type = rawServiceType == EvsServiceType.REARVIEW ? SERVICE_TYPE_REARVIEW : SERVICE_TYPE_SURROUNDVIEW; on = v.getInt32Value(EvsServiceRequestIndex.STATE) == EvsServiceState.ON; if (DBG) { Slogf.d(TAG, "Received EVS_SERVICE_REQUEST: type = " + type + " on = " + on); } } catch (IndexOutOfBoundsException e) { Slogf.e(TAG, "Received invalid EVS_SERVICE_REQUEST, missing type or state," + " int32Values: " + v.dumpInt32Values()); break; } listener.onEvent(type, on); break; case CAMERA_SERVICE_CURRENT_STATE: // Nothing to do with this write-only property. break; default: if (DBG) { Slogf.d(TAG, "Received unknown property change: " + v); } break; } } } @Override @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) public void dump(PrintWriter writer) { writer.println("*EVSHALSERVICE*"); writer.printf("Use EVS_SERVICE_REQUEST: %b\n", isEvsServiceRequestSupported()); writer.printf("Use CAMERA_SERVICE_CURRENT_STATE: %b\n", isCameraServiceCurrentStateSupported()); } }