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 android.support.car.hardware;
18
19import android.content.Context;
20import android.support.annotation.RestrictTo;
21import android.support.car.CarNotConnectedException;
22import java.util.Collection;
23import java.util.HashSet;
24import java.util.LinkedList;
25import java.util.Set;
26
27import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
28
29/**
30 *  @hide
31 */
32@RestrictTo(GROUP_ID)
33public class CarSensorManagerEmbedded extends CarSensorManager {
34    private static final String TAG = "CarSensorsProxy";
35
36    private final android.car.hardware.CarSensorManager mManager;
37    private final CarSensorsProxy mCarSensorsProxy;
38    private final LinkedList<OnSensorChangedListenerProxy> mListeners = new LinkedList<>();
39
40    public CarSensorManagerEmbedded(Object manager, Context context) {
41        mManager = (android.car.hardware.CarSensorManager) manager;
42        mCarSensorsProxy = new CarSensorsProxy(this, context);
43    }
44
45    @Override
46    public int[] getSupportedSensors() throws CarNotConnectedException {
47        try {
48            Set<Integer> sensorsSet = new HashSet<Integer>();
49            for (Integer sensor : mManager.getSupportedSensors()) {
50                sensorsSet.add(sensor);
51            }
52            for (Integer proxySensor : mCarSensorsProxy.getSupportedSensors()) {
53                sensorsSet.add(proxySensor);
54            }
55            return toIntArray(sensorsSet);
56        } catch (android.car.CarNotConnectedException e) {
57            throw new CarNotConnectedException(e);
58        }
59    }
60
61    private static int[] toIntArray(Collection<Integer> collection) {
62        int len = collection.size();
63        int[] arr = new int[len];
64        int arrIndex = 0;
65        for (Integer item : collection) {
66            arr[arrIndex] = item;
67            arrIndex++;
68        }
69        return arr;
70    }
71
72    @Override
73    public boolean isSensorSupported(int sensorType) throws CarNotConnectedException {
74        try {
75            return mManager.isSensorSupported(sensorType)
76                    || mCarSensorsProxy.isSensorSupported(sensorType);
77        } catch (android.car.CarNotConnectedException e) {
78            throw new CarNotConnectedException(e);
79        }
80    }
81
82    private boolean isSensorProxied(int sensorType) throws CarNotConnectedException {
83        try {
84            return !mManager.isSensorSupported(sensorType)
85                    && mCarSensorsProxy.isSensorSupported(sensorType);
86        } catch (android.car.CarNotConnectedException e) {
87            throw new CarNotConnectedException(e);
88        }
89    }
90
91    @Override
92    public boolean addListener(OnSensorChangedListener listener, int sensorType,
93            int rate) throws CarNotConnectedException, IllegalArgumentException {
94        if (isSensorProxied(sensorType)) {
95            return mCarSensorsProxy.registerSensorListener(listener, sensorType, rate);
96        }
97        OnSensorChangedListenerProxy proxy = null;
98        synchronized (this) {
99            proxy = findListenerLocked(listener);
100            if (proxy == null) {
101                proxy = new OnSensorChangedListenerProxy(listener, sensorType, this);
102                mListeners.add(proxy);
103            } else {
104                proxy.sensors.add(sensorType);
105            }
106        }
107        try {
108            return mManager.registerListener(proxy, sensorType, rate);
109        } catch (android.car.CarNotConnectedException e) {
110            throw new CarNotConnectedException(e);
111        }
112    }
113
114    @Override
115    public void removeListener(OnSensorChangedListener listener) {
116        mCarSensorsProxy.unregisterSensorListener(listener);
117        OnSensorChangedListenerProxy proxy = null;
118        synchronized (this) {
119            proxy = findListenerLocked(listener);
120            if (proxy == null) {
121                return;
122            }
123            mListeners.remove(proxy);
124        }
125        mManager.unregisterListener(proxy);
126    }
127
128    @Override
129    public void removeListener(OnSensorChangedListener listener, int sensorType) {
130        mCarSensorsProxy.unregisterSensorListener(listener, sensorType);
131        OnSensorChangedListenerProxy proxy = null;
132        synchronized (this) {
133            proxy = findListenerLocked(listener);
134            if (proxy == null) {
135                return;
136            }
137            proxy.sensors.remove(sensorType);
138            if (proxy.sensors.isEmpty()) {
139                mListeners.remove(proxy);
140            }
141        }
142        mManager.unregisterListener(proxy, sensorType);
143    }
144
145    @Override
146    public CarSensorEvent getLatestSensorEvent(int type) throws CarNotConnectedException {
147        if (isSensorProxied(type)) {
148            return mCarSensorsProxy.getLatestSensorEvent(type);
149        }
150        try {
151            return convert(mManager.getLatestSensorEvent(type));
152        } catch (android.car.CarNotConnectedException e) {
153            throw new CarNotConnectedException(e);
154        }
155    }
156
157    @Override
158    public CarSensorConfig getSensorConfig(@SensorType int type)
159        throws CarNotConnectedException {
160        try {
161            return convert(mManager.getSensorConfig(type));
162        } catch (android.car.CarNotConnectedException e) {
163            throw new CarNotConnectedException(e);
164        }
165    }
166
167    @Override
168    public void onCarDisconnected() {
169        //nothing to do
170    }
171
172    private OnSensorChangedListenerProxy findListenerLocked(OnSensorChangedListener listener) {
173        for (OnSensorChangedListenerProxy proxy : mListeners) {
174            if (proxy.listener == listener) {
175                return proxy;
176            }
177        }
178        return null;
179    }
180
181    private static CarSensorEvent convert(android.car.hardware.CarSensorEvent event) {
182        if (event == null) {
183            return null;
184        }
185        return new CarSensorEvent(event.sensorType, event.timestamp, event.floatValues,
186                event.intValues, event.longValues);
187    }
188
189    private static CarSensorConfig convert(android.car.hardware.CarSensorConfig cfg) {
190        if (cfg == null) {
191            return null;
192        }
193        return new CarSensorConfig(cfg.getType(), cfg.getBundle());
194    }
195
196    private static class OnSensorChangedListenerProxy
197            implements android.car.hardware.CarSensorManager.OnSensorChangedListener {
198
199        public final OnSensorChangedListener listener;
200        public final Set<Integer> sensors = new HashSet<>();
201        public final CarSensorManager manager;
202
203        OnSensorChangedListenerProxy(OnSensorChangedListener listener, int sensor,
204                CarSensorManager manager) {
205            this.listener = listener;
206            this.sensors.add(sensor);
207            this.manager = manager;
208        }
209
210        @Override
211        public void onSensorChanged(android.car.hardware.CarSensorEvent event) {
212            CarSensorEvent newEvent = convert(event);
213            listener.onSensorChanged(manager, newEvent);
214        }
215    }
216}
217