1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.hal;
18 
19 import static android.car.evs.CarEvsManager.SERVICE_TYPE_REARVIEW;
20 import static android.car.evs.CarEvsManager.SERVICE_TYPE_SURROUNDVIEW;
21 import static android.hardware.automotive.vehicle.VehicleProperty.CAMERA_SERVICE_CURRENT_STATE;
22 import static android.hardware.automotive.vehicle.VehicleProperty.EVS_SERVICE_REQUEST;
23 
24 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
25 
26 import android.car.builtin.util.Slogf;
27 import android.car.evs.CarEvsManager.CarEvsServiceState;
28 import android.car.evs.CarEvsManager.CarEvsServiceType;
29 import android.hardware.automotive.vehicle.EvsServiceRequestIndex;
30 import android.hardware.automotive.vehicle.EvsServiceState;
31 import android.hardware.automotive.vehicle.EvsServiceType;
32 import android.os.ServiceSpecificException;
33 import android.util.Log;
34 import android.util.SparseArray;
35 
36 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
37 import com.android.internal.annotations.GuardedBy;
38 
39 import java.io.PrintWriter;
40 import java.util.Collection;
41 import java.util.List;
42 
43 /*
44  * Translates HAL events, CarEvsService is interested in, into the higher-level semantic
45  * information.
46  */
47 public class EvsHalService extends HalServiceBase {
48 
49     private static final String TAG = EvsHalService.class.getSimpleName();
50     private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG);
51 
52     private static final int[] SUPPORTED_PROPERTIES = new int[] {
53         CAMERA_SERVICE_CURRENT_STATE,
54         EVS_SERVICE_REQUEST,
55     };
56 
57     private final Object mLock = new Object();
58 
59     @GuardedBy("mLock")
60     private final SparseArray<HalPropConfig> mProperties = new SparseArray();
61 
62     private final VehicleHal mHal;
63     private final HalPropValueBuilder mPropValueBuilder;
64 
65     @GuardedBy("mLock")
66     private EvsHalEventListener mListener;
67 
68     private boolean mIsEvsServiceRequestSupported;
69     private boolean mIsCameraServiceCurrentStateSupported;
70 
EvsHalService(VehicleHal hal)71     public EvsHalService(VehicleHal hal) {
72         mHal = hal;
73         mPropValueBuilder = hal.getHalPropValueBuilder();
74     }
75 
76     /**
77      * Interface to be implemented by any client that wants to get notified upon a new EVS service
78      * request.
79      */
80     public interface EvsHalEventListener {
81         /** Called when a value of monitoring VHAL property gets changed */
onEvent(@arEvsServiceType int id, boolean on)82         void onEvent(@CarEvsServiceType int id, boolean on);
83     }
84 
85     /**
86      * Sets the event listener to receive Vehicle's EVS-related events.
87      *
88      * @param listener {@link EvsHalEventListener}
89      * @throws IllegalStateException if none of required VHAL properties are not supported
90      *         on this device.
91      */
setListener(EvsHalEventListener listener)92     public void setListener(EvsHalEventListener listener) {
93         if (!mIsEvsServiceRequestSupported) {
94             throw new IllegalStateException(
95                     "Any of required VHAL properties are not supported.");
96         }
97 
98         synchronized (mLock) {
99             mListener = listener;
100         }
101 
102         mHal.subscribePropertySafe(this, EVS_SERVICE_REQUEST);
103     }
104 
105     /** Returns whether {@code EVS_SERVICE_REQUEST} is supported */
isEvsServiceRequestSupported()106     public boolean isEvsServiceRequestSupported() {
107         return mIsEvsServiceRequestSupported;
108     }
109 
110     /** Returns whether {@code CAMERA_SERVICE_CURRENT_STATE} is supported */
isCameraServiceCurrentStateSupported()111     public boolean isCameraServiceCurrentStateSupported() {
112         return mIsCameraServiceCurrentStateSupported;
113     }
114 
115     @Override
init()116     public void init() {
117         synchronized (mLock) {
118             for (int i = 0; i < mProperties.size(); i++) {
119                 HalPropConfig config = mProperties.valueAt(i);
120                 if (VehicleHal.isPropertySubscribable(config)) {
121                     mHal.subscribePropertySafe(this, config.getPropId());
122                 }
123             }
124         }
125     }
126 
127     @Override
release()128     public void release() {
129         synchronized (mLock) {
130             mListener = null;
131             mProperties.clear();
132         }
133     }
134 
135     @Override
getAllSupportedProperties()136     public int[] getAllSupportedProperties() {
137         return SUPPORTED_PROPERTIES;
138     }
139 
140     @Override
takeProperties(Collection<HalPropConfig> configs)141     public void takeProperties(Collection<HalPropConfig> configs) {
142         if (configs.isEmpty()) {
143             return;
144         }
145 
146         synchronized (mLock) {
147             for (HalPropConfig config : configs) {
148                 mProperties.put(config.getPropId(), config);
149             }
150 
151             mIsEvsServiceRequestSupported = mProperties.contains(EVS_SERVICE_REQUEST);
152             mIsCameraServiceCurrentStateSupported =
153                     mProperties.contains(CAMERA_SERVICE_CURRENT_STATE);
154         }
155     }
156 
157     @Override
onHalEvents(List<HalPropValue> values)158     public void onHalEvents(List<HalPropValue> values) {
159         EvsHalEventListener listener;
160         synchronized (mLock) {
161             listener = mListener;
162         }
163 
164         if (listener == null) {
165             Slogf.w(TAG, "EVS Hal event occurs while the listener is null.");
166             return;
167         }
168 
169         dispatchHalEvents(values, listener);
170     }
171 
172     /**
173      * Reports the current state of CarEvsService.
174      *
175      * @param state {@link android.car.evs.CarEvsManager.CarEvsServiceState} value that represents
176      *              the current state of {@code CarEvsService}.
177      */
reportCurrentState(@arEvsServiceState int[] state)178     public void reportCurrentState(@CarEvsServiceState int[] state) {
179         HalPropValue currentStates = mPropValueBuilder.build(CAMERA_SERVICE_CURRENT_STATE,
180                 /* areaId= */ 0, /* values= */ state);
181 
182         try {
183             mHal.set(currentStates);
184         } catch (ServiceSpecificException | IllegalArgumentException e) {
185             Slogf.e(TAG, "Failed to set a hal property: %s, err: %s", currentStates, e);
186         }
187     }
188 
dispatchHalEvents(List<HalPropValue> values, EvsHalEventListener listener)189     private void dispatchHalEvents(List<HalPropValue> values, EvsHalEventListener listener) {
190         for (int i = 0; i < values.size(); ++i) {
191             HalPropValue v = values.get(i);
192             boolean on = false;
193             @CarEvsServiceType int type;
194             switch (v.getPropId()) {
195                 case EVS_SERVICE_REQUEST:
196                     // Check
197                     // android.hardware.automotive.vehicle.VehicleProperty.EVS_SERVICE_REQUEST
198                     try {
199                         int rawServiceType = v.getInt32Value(EvsServiceRequestIndex.TYPE);
200                         type = rawServiceType == EvsServiceType.REARVIEW
201                                 ? SERVICE_TYPE_REARVIEW : SERVICE_TYPE_SURROUNDVIEW;
202                         on = v.getInt32Value(EvsServiceRequestIndex.STATE) == EvsServiceState.ON;
203                         if (DBG) {
204                             Slogf.d(TAG,
205                                     "Received EVS_SERVICE_REQUEST: type = " + type + " on = " + on);
206                         }
207                     } catch (IndexOutOfBoundsException e) {
208                         Slogf.e(TAG, "Received invalid EVS_SERVICE_REQUEST, missing type or state,"
209                                 + " int32Values: " + v.dumpInt32Values());
210                         break;
211                     }
212                     listener.onEvent(type, on);
213                     break;
214 
215                 case CAMERA_SERVICE_CURRENT_STATE:
216                     // Nothing to do with this write-only property.
217                     break;
218 
219                 default:
220                     if (DBG) {
221                         Slogf.d(TAG, "Received unknown property change: " + v);
222                     }
223                     break;
224             }
225         }
226     }
227 
228     @Override
229     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(PrintWriter writer)230     public void dump(PrintWriter writer) {
231         writer.println("*EVSHALSERVICE*");
232         writer.printf("Use EVS_SERVICE_REQUEST: %b\n", isEvsServiceRequestSupported());
233         writer.printf("Use CAMERA_SERVICE_CURRENT_STATE: %b\n",
234                 isCameraServiceCurrentStateSupported());
235     }
236 }
237