1/*
2 * Copyright (C) 2016 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
17package com.android.car.vehiclehal.test;
18
19import static java.lang.Integer.toHexString;
20import static junit.framework.Assert.assertEquals;
21import static junit.framework.Assert.assertNotNull;
22import static junit.framework.Assert.fail;
23
24import com.google.android.collect.Lists;
25
26import android.hardware.automotive.vehicle.V2_0.IVehicle;
27import android.hardware.automotive.vehicle.V2_0.IVehicleCallback;
28import android.hardware.automotive.vehicle.V2_0.StatusCode;
29import android.hardware.automotive.vehicle.V2_0.SubscribeOptions;
30import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
31import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
32import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
33import android.os.RemoteException;
34
35import java.util.ArrayList;
36import java.util.HashMap;
37import java.util.List;
38import java.util.Map;
39
40/**
41 * Mocked implementation of {@link IVehicle}.
42 */
43public class MockedVehicleHal extends IVehicle.Stub {
44    /**
45     * Interface for handler of each property.
46     */
47    public interface VehicleHalPropertyHandler {
48        default void onPropertySet(VehiclePropValue value) {}
49        default VehiclePropValue onPropertyGet(VehiclePropValue value) { return null; }
50        default void onPropertySubscribe(int property, int zones, float sampleRate) {}
51        default void onPropertyUnsubscribe(int property) {}
52
53        VehicleHalPropertyHandler NOP = new VehicleHalPropertyHandler() {};
54    }
55
56    private final Map<Integer, VehicleHalPropertyHandler> mPropertyHandlerMap = new HashMap<>();
57    private final Map<Integer, VehiclePropConfig> mConfigs = new HashMap<>();
58    private final Map<Integer, List<IVehicleCallback>> mSubscribers = new HashMap<>();
59
60    public synchronized void addProperties(VehiclePropConfig... configs) {
61        for (VehiclePropConfig config : configs) {
62            addProperty(config, new DefaultPropertyHandler(config, null));
63        }
64    }
65
66    public synchronized void addProperty(VehiclePropConfig config,
67            VehicleHalPropertyHandler handler) {
68        mPropertyHandlerMap.put(config.prop, handler);
69        mConfigs.put(config.prop, config);
70    }
71
72    public synchronized void addStaticProperty(VehiclePropConfig config,
73            VehiclePropValue value) {
74        addProperty(config, new StaticPropertyHandler(value));
75    }
76
77    public synchronized void injectEvent(VehiclePropValue value) {
78        List<IVehicleCallback> callbacks = mSubscribers.get(value.prop);
79        assertNotNull("Injecting event failed for property: " + value.prop
80                        + ". No listeners found", callbacks);
81        for (IVehicleCallback callback : callbacks) {
82            try {
83                callback.onPropertyEvent(Lists.newArrayList(value));
84            } catch (RemoteException e) {
85                e.printStackTrace();
86                fail("Remote exception while injecting events.");
87            }
88        }
89    }
90
91    public synchronized void injectError(int errorCode, int propertyId, int areaId) {
92        List<IVehicleCallback> callbacks = mSubscribers.get(propertyId);
93        assertNotNull("Injecting error failed for property: " + propertyId
94                        + ". No listeners found", callbacks);
95        for (IVehicleCallback callback : callbacks) {
96            try {
97                callback.onPropertySetError(errorCode, propertyId, areaId);
98            } catch (RemoteException e) {
99                e.printStackTrace();
100                fail("Remote exception while injecting errors.");
101            }
102        }
103    }
104
105    @Override
106    public synchronized ArrayList<VehiclePropConfig> getAllPropConfigs() {
107        return new ArrayList<>(mConfigs.values());
108    }
109
110    @Override
111    public synchronized void getPropConfigs(ArrayList<Integer> props, getPropConfigsCallback cb) {
112        ArrayList<VehiclePropConfig> res = new ArrayList<>();
113        for (Integer prop : props) {
114            VehiclePropConfig config = mConfigs.get(prop);
115            if (config == null) {
116                cb.onValues(StatusCode.INVALID_ARG, new ArrayList<>());
117                return;
118            }
119            res.add(config);
120        }
121        cb.onValues(StatusCode.OK, res);
122    }
123
124    @Override
125    public synchronized void get(VehiclePropValue requestedPropValue, getCallback cb) {
126        VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(requestedPropValue.prop);
127        if (handler == null) {
128            cb.onValues(StatusCode.INVALID_ARG, null);
129        } else {
130            cb.onValues(StatusCode.OK, handler.onPropertyGet(requestedPropValue));
131        }
132    }
133
134    @Override
135    public synchronized int set(VehiclePropValue propValue) {
136        VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(propValue.prop);
137        if (handler == null) {
138            return StatusCode.INVALID_ARG;
139        } else {
140            handler.onPropertySet(propValue);
141            return StatusCode.OK;
142        }
143    }
144
145    @Override
146    public synchronized int subscribe(IVehicleCallback callback,
147            ArrayList<SubscribeOptions> options) {
148        for (SubscribeOptions opt : options) {
149            VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(opt.propId);
150            if (handler == null) {
151                return StatusCode.INVALID_ARG;
152            }
153
154            handler.onPropertySubscribe(opt.propId, opt.vehicleAreas, opt.sampleRate);
155            List<IVehicleCallback>  subscribers = mSubscribers.get(opt.propId);
156            if (subscribers == null) {
157                subscribers = new ArrayList<>();
158                mSubscribers.put(opt.propId, subscribers);
159            }
160            subscribers.add(callback);
161        }
162        return StatusCode.OK;
163    }
164
165    @Override
166    public synchronized int unsubscribe(IVehicleCallback callback, int propId) {
167        VehicleHalPropertyHandler handler = mPropertyHandlerMap.get(propId);
168        if (handler == null) {
169            return StatusCode.INVALID_ARG;
170        }
171
172        handler.onPropertyUnsubscribe(propId);
173        List<IVehicleCallback>  subscribers = mSubscribers.get(propId);
174        if (subscribers != null) {
175            subscribers.remove(callback);
176            if (subscribers.size() == 0) {
177                mSubscribers.remove(propId);
178            }
179        }
180        return StatusCode.OK;
181    }
182
183    @Override
184    public String debugDump() {
185        return null;
186    }
187
188    public static class FailingPropertyHandler implements VehicleHalPropertyHandler {
189        @Override
190        public void onPropertySet(VehiclePropValue value) {
191            fail("Unexpected onPropertySet call");
192        }
193
194        @Override
195        public VehiclePropValue onPropertyGet(VehiclePropValue value) {
196            fail("Unexpected onPropertyGet call");
197            return null;
198        }
199
200        @Override
201        public void onPropertySubscribe(int property, int zones, float sampleRate) {
202            fail("Unexpected onPropertySubscribe call");
203        }
204
205        @Override
206        public void onPropertyUnsubscribe(int property) {
207            fail("Unexpected onPropertyUnsubscribe call");
208        }
209    }
210
211    public static class StaticPropertyHandler extends FailingPropertyHandler {
212        private final VehiclePropValue mValue;
213
214        public StaticPropertyHandler(VehiclePropValue value) {
215            mValue = value;
216        }
217
218        @Override
219        public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
220            return mValue;
221        }
222    }
223
224    public static class DefaultPropertyHandler implements VehicleHalPropertyHandler {
225        private final VehiclePropConfig mConfig;
226        private VehiclePropValue mValue;
227        private boolean mSubscribed = false;
228
229        public DefaultPropertyHandler(VehiclePropConfig config, VehiclePropValue initialValue) {
230            mConfig = config;
231            mValue = initialValue;
232        }
233
234        @Override
235        public synchronized void onPropertySet(VehiclePropValue value) {
236            assertEquals(mConfig.prop, value.prop);
237            assertEquals(VehiclePropertyAccess.WRITE, mConfig.access & VehiclePropertyAccess.WRITE);
238            mValue = value;
239        }
240
241        @Override
242        public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
243            assertEquals(mConfig.prop, value.prop);
244            assertEquals(VehiclePropertyAccess.READ, mConfig.access & VehiclePropertyAccess.READ);
245            return mValue;
246        }
247
248        @Override
249        public synchronized void onPropertySubscribe(int property, int zones, float sampleRate) {
250            assertEquals(mConfig.prop, property);
251            mSubscribed = true;
252        }
253
254        @Override
255        public synchronized void onPropertyUnsubscribe(int property) {
256            assertEquals(mConfig.prop, property);
257            if (!mSubscribed) {
258                throw new IllegalArgumentException("Property was not subscribed 0x"
259                        + toHexString( property));
260            }
261            mSubscribed = false;
262        }
263    }
264}
265