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 17 package android.car.hardware.property; 18 19 import static java.lang.Integer.toHexString; 20 21 import android.annotation.Nullable; 22 import android.car.Car; 23 import android.car.CarNotConnectedException; 24 import android.car.hardware.CarPropertyConfig; 25 import android.car.hardware.CarPropertyValue; 26 import android.os.Handler; 27 import android.os.IBinder; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.os.RemoteException; 31 import android.util.Log; 32 33 import com.android.internal.annotations.GuardedBy; 34 35 import java.lang.ref.WeakReference; 36 import java.util.List; 37 38 /** 39 * API for creating Car*Manager 40 * @hide 41 */ 42 public class CarPropertyManagerBase { 43 private final boolean mDbg; 44 private final Handler mHandler; 45 private final ICarProperty mService; 46 private final String mTag; 47 48 @GuardedBy("mLock") 49 private ICarPropertyEventListener mListenerToService; 50 @GuardedBy("mLock") 51 private CarPropertyEventCallback mCallback; 52 53 private final Object mLock = new Object(); 54 55 /** Callback functions for property events */ 56 public interface CarPropertyEventCallback { 57 /** Called when a property is updated */ onChangeEvent(CarPropertyValue value)58 void onChangeEvent(CarPropertyValue value); 59 60 /** Called when an error is detected with a property */ onErrorEvent(int propertyId, int zone)61 void onErrorEvent(int propertyId, int zone); 62 } 63 64 private final static class EventCallbackHandler extends Handler { 65 /** Constants handled in the handler */ 66 private static final int MSG_GENERIC_EVENT = 0; 67 68 private final WeakReference<CarPropertyManagerBase> mMgr; 69 EventCallbackHandler(CarPropertyManagerBase mgr, Looper looper)70 EventCallbackHandler(CarPropertyManagerBase mgr, Looper looper) { 71 super(looper); 72 mMgr = new WeakReference<>(mgr); 73 } 74 75 @Override handleMessage(Message msg)76 public void handleMessage(Message msg) { 77 switch (msg.what) { 78 case MSG_GENERIC_EVENT: 79 CarPropertyManagerBase mgr = mMgr.get(); 80 if (mgr != null) { 81 mgr.dispatchEventToClient((CarPropertyEvent) msg.obj); 82 } 83 break; 84 default: 85 Log.e("EventtCallbackHandler", "Event type not handled: " + msg); 86 break; 87 } 88 } 89 } 90 91 /** 92 * Get an instance of the CarPropertyManagerBase. 93 */ CarPropertyManagerBase(IBinder service, Handler handler, boolean dbg, String tag)94 public CarPropertyManagerBase(IBinder service, Handler handler, boolean dbg, 95 String tag) { 96 mDbg = dbg; 97 mTag = tag; 98 mService = ICarProperty.Stub.asInterface(service); 99 mHandler = new EventCallbackHandler(this, handler.getLooper()); 100 } 101 registerCallback(CarPropertyEventCallback callback)102 public void registerCallback(CarPropertyEventCallback callback) 103 throws CarNotConnectedException { 104 synchronized (mLock) { 105 if (mCallback != null) { 106 throw new IllegalStateException("Callback is already registered."); 107 } 108 109 mCallback = callback; 110 mListenerToService = new ICarPropertyEventListener.Stub() { 111 @Override 112 public void onEvent(CarPropertyEvent event) throws RemoteException { 113 handleEvent(event); 114 } 115 }; 116 } 117 118 try { 119 mService.registerListener(mListenerToService); 120 } catch (RemoteException ex) { 121 Log.e(mTag, "Could not connect: ", ex); 122 throw new CarNotConnectedException(ex); 123 } catch (IllegalStateException ex) { 124 Car.checkCarNotConnectedExceptionFromCarService(ex); 125 } 126 } 127 unregisterCallback()128 public void unregisterCallback() { 129 ICarPropertyEventListener listenerToService; 130 synchronized (mLock) { 131 listenerToService = mListenerToService; 132 mCallback = null; 133 mListenerToService = null; 134 } 135 136 if (listenerToService == null) { 137 Log.w(mTag, "unregisterListener: listener was not registered"); 138 return; 139 } 140 141 try { 142 mService.unregisterListener(listenerToService); 143 } catch (RemoteException ex) { 144 Log.e(mTag, "Failed to unregister listener", ex); 145 //ignore 146 } catch (IllegalStateException ex) { 147 Car.hideCarNotConnectedExceptionFromCarService(ex); 148 } 149 } 150 151 /** 152 * Returns the list of properties available. 153 * 154 * @return Caller must check the property type and typecast to the appropriate subclass 155 * (CarPropertyBooleanProperty, CarPropertyFloatProperty, CarrPropertyIntProperty) 156 */ getPropertyList()157 public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException { 158 try { 159 return mService.getPropertyList(); 160 } catch (RemoteException e) { 161 Log.e(mTag, "Exception in getPropertyList", e); 162 throw new CarNotConnectedException(e); 163 } 164 } 165 166 /** 167 * Returns value of a bool property 168 * 169 * @param prop Property ID to get 170 * @param area Area of the property to get 171 */ getBooleanProperty(int prop, int area)172 public boolean getBooleanProperty(int prop, int area) throws CarNotConnectedException { 173 CarPropertyValue<Boolean> carProp = getProperty(Boolean.class, prop, area); 174 return carProp != null ? carProp.getValue() : false; 175 } 176 177 /** 178 * Returns value of a float property 179 * 180 * @param prop Property ID to get 181 * @param area Area of the property to get 182 */ getFloatProperty(int prop, int area)183 public float getFloatProperty(int prop, int area) throws CarNotConnectedException { 184 CarPropertyValue<Float> carProp = getProperty(Float.class, prop, area); 185 return carProp != null ? carProp.getValue() : 0f; 186 } 187 188 /** 189 * Returns value of a integer property 190 * 191 * @param prop Property ID to get 192 * @param area Zone of the property to get 193 */ getIntProperty(int prop, int area)194 public int getIntProperty(int prop, int area) throws CarNotConnectedException { 195 CarPropertyValue<Integer> carProp = getProperty(Integer.class, prop, area); 196 return carProp != null ? carProp.getValue() : 0; 197 } 198 199 @Nullable 200 @SuppressWarnings("unchecked") getProperty(Class<E> clazz, int propId, int area)201 public <E> CarPropertyValue<E> getProperty(Class<E> clazz, int propId, int area) 202 throws CarNotConnectedException { 203 if (mDbg) { 204 Log.d(mTag, "getProperty, propId: 0x" + toHexString(propId) 205 + ", area: 0x" + toHexString(area) + ", class: " + clazz); 206 } 207 try { 208 CarPropertyValue<E> propVal = mService.getProperty(propId, area); 209 if (propVal != null && propVal.getValue() != null) { 210 Class<?> actualClass = propVal.getValue().getClass(); 211 if (actualClass != clazz) { 212 throw new IllegalArgumentException("Invalid property type. " + "Expected: " 213 + clazz + ", but was: " + actualClass); 214 } 215 } 216 return propVal; 217 } catch (RemoteException e) { 218 Log.e(mTag, "getProperty failed with " + e.toString() 219 + ", propId: 0x" + toHexString(propId) + ", area: 0x" + toHexString(area), e); 220 throw new CarNotConnectedException(e); 221 } 222 } 223 setProperty(Class<E> clazz, int propId, int area, E val)224 public <E> void setProperty(Class<E> clazz, int propId, int area, E val) 225 throws CarNotConnectedException { 226 if (mDbg) { 227 Log.d(mTag, "setProperty, propId: 0x" + toHexString(propId) 228 + ", area: 0x" + toHexString(area) + ", class: " + clazz + ", val: " + val); 229 } 230 try { 231 mService.setProperty(new CarPropertyValue<>(propId, area, val)); 232 } catch (RemoteException e) { 233 Log.e(mTag, "setProperty failed with " + e.toString(), e); 234 throw new CarNotConnectedException(e); 235 } 236 } 237 238 /** 239 * Modifies a property. If the property modification doesn't occur, an error event shall be 240 * generated and propagated back to the application. 241 * 242 * @param prop Property ID to modify 243 * @param area Area to apply the modification. 244 * @param val Value to set 245 */ setBooleanProperty(int prop, int area, boolean val)246 public void setBooleanProperty(int prop, int area, boolean val) 247 throws CarNotConnectedException { 248 setProperty(Boolean.class, prop, area, val); 249 } 250 setFloatProperty(int prop, int area, float val)251 public void setFloatProperty(int prop, int area, float val) throws CarNotConnectedException { 252 setProperty(Float.class, prop, area, val); 253 } 254 setIntProperty(int prop, int area, int val)255 public void setIntProperty(int prop, int area, int val) throws CarNotConnectedException { 256 setProperty(Integer.class, prop, area, val); 257 } 258 dispatchEventToClient(CarPropertyEvent event)259 private void dispatchEventToClient(CarPropertyEvent event) { 260 CarPropertyEventCallback listener; 261 synchronized (mLock) { 262 listener = mCallback; 263 } 264 265 if (listener == null) { 266 Log.e(mTag, "Listener died, not dispatching event."); 267 return; 268 } 269 270 CarPropertyValue propVal = event.getCarPropertyValue(); 271 switch(event.getEventType()) { 272 case CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE: 273 listener.onChangeEvent(propVal); 274 break; 275 case CarPropertyEvent.PROPERTY_EVENT_ERROR: 276 listener.onErrorEvent(propVal.getPropertyId(), propVal.getAreaId()); 277 break; 278 default: 279 throw new IllegalArgumentException(); 280 } 281 } 282 handleEvent(CarPropertyEvent event)283 private void handleEvent(CarPropertyEvent event) { 284 mHandler.sendMessage(mHandler.obtainMessage(EventCallbackHandler.MSG_GENERIC_EVENT, event)); 285 } 286 287 /** @hide */ onCarDisconnected()288 public void onCarDisconnected() { 289 290 ICarPropertyEventListener listenerToService; 291 synchronized (mLock) { 292 listenerToService = mListenerToService; 293 } 294 295 if (listenerToService != null) { 296 unregisterCallback(); 297 } 298 } 299 } 300