blob: 02ab7c95cb1a0f7a4c5bf299512844cf1c91aabb [file] [log] [blame]
Steve Paik461ecc62016-06-08 15:28:32 -07001/*
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;
18
19import android.car.Car;
20import android.car.hardware.CarPropertyConfig;
21import android.car.hardware.CarPropertyValue;
22import android.car.hardware.property.CarPropertyEvent;
23import android.car.hardware.property.ICarProperty;
24import android.car.hardware.property.ICarPropertyEventListener;
25import android.content.Context;
26import android.os.IBinder;
27import android.os.RemoteException;
28import android.util.Log;
29
30import com.android.car.hal.PropertyHalServiceBase;
31
32import java.io.PrintWriter;
Steve Paik461ecc62016-06-08 15:28:32 -070033import java.util.List;
34import java.util.Map;
Pavel Maltsev634e1ff2016-07-14 15:41:26 -070035import java.util.concurrent.ConcurrentHashMap;
Steve Paik461ecc62016-06-08 15:28:32 -070036
37/**
38 * This class implements the binder interface for ICarProperty.aidl to make it easier to create
Pavel Maltsev634e1ff2016-07-14 15:41:26 -070039 * multiple managers that deal with Vehicle Properties. To create a new service, simply extend
Steve Paik461ecc62016-06-08 15:28:32 -070040 * this class and call the super() constructor with the appropriate arguments for the new service.
Pavel Maltsev634e1ff2016-07-14 15:41:26 -070041 * {@link CarHvacService} shows the basic usage.
Steve Paik461ecc62016-06-08 15:28:32 -070042 */
43public class CarPropertyServiceBase extends ICarProperty.Stub
44 implements CarServiceBase, PropertyHalServiceBase.PropertyHalListener {
45 private final Context mContext;
46 private final boolean mDbg;
Pavel Maltsev634e1ff2016-07-14 15:41:26 -070047 private final Map<IBinder, PropertyDeathRecipient> mDeathRecipientMap =
48 new ConcurrentHashMap<>();
Steve Paik461ecc62016-06-08 15:28:32 -070049 private final PropertyHalServiceBase mHal;
Pavel Maltsev634e1ff2016-07-14 15:41:26 -070050 private final Map<IBinder, ICarPropertyEventListener> mListenersMap = new ConcurrentHashMap<>();
Steve Paik461ecc62016-06-08 15:28:32 -070051 private final String mPermission;
52 private final String mTag;
53
Pavel Maltsev634e1ff2016-07-14 15:41:26 -070054 private final Object mLock = new Object();
55
Steve Paik461ecc62016-06-08 15:28:32 -070056 public CarPropertyServiceBase(Context context, PropertyHalServiceBase hal, String permission,
57 boolean dbg, String tag) {
58 mContext = context;
59 mHal = hal;
60 mPermission = permission;
61 mDbg = dbg;
62 mTag = tag + ".service";
63 }
64
65 class PropertyDeathRecipient implements IBinder.DeathRecipient {
66 private IBinder mListenerBinder;
67
68 PropertyDeathRecipient(IBinder listenerBinder) {
69 mListenerBinder = listenerBinder;
70 }
71
72 /**
73 * Client died. Remove the listener from HAL service and unregister if this is the last
74 * client.
75 */
76 @Override
77 public void binderDied() {
78 if (mDbg) {
79 Log.d(mTag, "binderDied " + mListenerBinder);
80 }
81 CarPropertyServiceBase.this.unregisterListenerLocked(mListenerBinder);
82 }
83
84 void release() {
85 mListenerBinder.unlinkToDeath(this, 0);
86 }
87 }
88
89 @Override
Pavel Maltsev634e1ff2016-07-14 15:41:26 -070090 public void init() {
Steve Paik461ecc62016-06-08 15:28:32 -070091 }
92
93 @Override
Pavel Maltsev634e1ff2016-07-14 15:41:26 -070094 public void release() {
Steve Paik461ecc62016-06-08 15:28:32 -070095 for (PropertyDeathRecipient recipient : mDeathRecipientMap.values()) {
96 recipient.release();
97 }
98 mDeathRecipientMap.clear();
99 mListenersMap.clear();
100 }
101
102 @Override
103 public void dump(PrintWriter writer) {
Steve Paik461ecc62016-06-08 15:28:32 -0700104 }
105
106 @Override
Pavel Maltsev634e1ff2016-07-14 15:41:26 -0700107 public void registerListener(ICarPropertyEventListener listener) {
Steve Paik461ecc62016-06-08 15:28:32 -0700108 if (mDbg) {
109 Log.d(mTag, "registerListener");
110 }
111 ICarImpl.assertPermission(mContext, mPermission);
112 if (listener == null) {
113 Log.e(mTag, "registerListener: Listener is null.");
114 throw new IllegalArgumentException("listener cannot be null.");
115 }
116
117 IBinder listenerBinder = listener.asBinder();
Steve Paik461ecc62016-06-08 15:28:32 -0700118
Pavel Maltsev634e1ff2016-07-14 15:41:26 -0700119 synchronized (mLock) {
120 if (mListenersMap.containsKey(listenerBinder)) {
121 // Already registered, nothing to do.
122 return;
123 }
Steve Paik461ecc62016-06-08 15:28:32 -0700124
Pavel Maltsev634e1ff2016-07-14 15:41:26 -0700125 PropertyDeathRecipient deathRecipient = new PropertyDeathRecipient(listenerBinder);
126 try {
127 listenerBinder.linkToDeath(deathRecipient, 0);
128 } catch (RemoteException e) {
129 Log.e(mTag, "Failed to link death for recipient. " + e);
130 throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG);
131 }
132 mDeathRecipientMap.put(listenerBinder, deathRecipient);
Steve Paik461ecc62016-06-08 15:28:32 -0700133
Pavel Maltsev634e1ff2016-07-14 15:41:26 -0700134 if (mListenersMap.isEmpty()) {
135 mHal.setListener(this);
136 }
137
138 mListenersMap.put(listenerBinder, listener);
139 }
Steve Paik461ecc62016-06-08 15:28:32 -0700140 }
141
142 @Override
Pavel Maltsev634e1ff2016-07-14 15:41:26 -0700143 public void unregisterListener(ICarPropertyEventListener listener) {
Steve Paik461ecc62016-06-08 15:28:32 -0700144 if (mDbg) {
145 Log.d(mTag, "unregisterListener");
146 }
147 ICarImpl.assertPermission(mContext, mPermission);
148 if (listener == null) {
149 Log.e(mTag, "unregisterListener: Listener is null.");
150 throw new IllegalArgumentException("Listener is null");
151 }
152
153 IBinder listenerBinder = listener.asBinder();
Pavel Maltsev634e1ff2016-07-14 15:41:26 -0700154 synchronized (mLock) {
155 if (!mListenersMap.containsKey(listenerBinder)) {
156 Log.e(mTag, "unregisterListener: Listener was not previously registered.");
157 }
158 unregisterListenerLocked(listenerBinder);
Steve Paik461ecc62016-06-08 15:28:32 -0700159 }
Steve Paik461ecc62016-06-08 15:28:32 -0700160 }
161
162 // Removes the listenerBinder from the current state.
163 // The function assumes that binder will exist both in listeners and death recipients list.
164 private void unregisterListenerLocked(IBinder listenerBinder) {
165 boolean found = mListenersMap.remove(listenerBinder) != null;
166
167 if (found) {
168 mDeathRecipientMap.get(listenerBinder).release();
169 mDeathRecipientMap.remove(listenerBinder);
170 }
171
172 if (mListenersMap.isEmpty()) {
173 mHal.setListener(null);
174 }
175 }
176
177 @Override
Pavel Maltsev634e1ff2016-07-14 15:41:26 -0700178 public List<CarPropertyConfig> getPropertyList() {
Steve Paik461ecc62016-06-08 15:28:32 -0700179 ICarImpl.assertPermission(mContext, mPermission);
180 return mHal.getPropertyList();
181 }
182
183 @Override
Pavel Maltsev634e1ff2016-07-14 15:41:26 -0700184 public CarPropertyValue getProperty(int prop, int zone) {
Steve Paik461ecc62016-06-08 15:28:32 -0700185 ICarImpl.assertPermission(mContext, mPermission);
186 return mHal.getProperty(prop, zone);
187 }
188
189 @Override
Pavel Maltsev634e1ff2016-07-14 15:41:26 -0700190 public void setProperty(CarPropertyValue prop) {
Steve Paik461ecc62016-06-08 15:28:32 -0700191 ICarImpl.assertPermission(mContext, mPermission);
192 mHal.setProperty(prop);
193 }
194
Pavel Maltsev2a8c56d2016-12-14 11:58:14 -0800195 private ICarPropertyEventListener[] getListeners() {
196 synchronized (mLock) {
197 int size = mListenersMap.values().size();
198 return mListenersMap.values().toArray(new ICarPropertyEventListener[size]);
199 }
200 }
201
Steve Paik461ecc62016-06-08 15:28:32 -0700202 // Implement PropertyHalListener interface
203 @Override
Pavel Maltsev634e1ff2016-07-14 15:41:26 -0700204 public void onPropertyChange(CarPropertyEvent event) {
Pavel Maltsev2a8c56d2016-12-14 11:58:14 -0800205 for (ICarPropertyEventListener listener : getListeners()) {
Steve Paik461ecc62016-06-08 15:28:32 -0700206 try {
Pavel Maltsev2a8c56d2016-12-14 11:58:14 -0800207 listener.onEvent(event);
Steve Paik461ecc62016-06-08 15:28:32 -0700208 } catch (RemoteException ex) {
209 // If we could not send a record, its likely the connection snapped. Let the binder
210 // death handle the situation.
211 Log.e(mTag, "onEvent calling failed: " + ex);
212 }
213 }
214 }
215
216 @Override
Pavel Maltsev2a8c56d2016-12-14 11:58:14 -0800217 public void onPropertySetError(int property, int area) {
218 for (ICarPropertyEventListener listener : getListeners()) {
219 try {
220 listener.onEvent(createErrorEvent(property, area));
221 } catch (RemoteException ex) {
222 // If we could not send a record, its likely the connection snapped. Let the binder
223 // death handle the situation.
224 Log.e(mTag, "onEvent calling failed: " + ex);
225 }
226 }
227 }
228
229 private static CarPropertyEvent createErrorEvent(int property, int area) {
230 return new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_ERROR,
231 new CarPropertyValue<>(property, area, null));
Steve Paik461ecc62016-06-08 15:28:32 -0700232 }
233}