1 /* 2 * Copyright (C) 2015 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.radio; 18 19 import android.annotation.SystemApi; 20 import android.car.Car; 21 import android.car.CarManagerBase; 22 import android.car.CarNotConnectedException; 23 import android.hardware.radio.RadioManager; 24 import android.os.Handler; 25 import android.os.IBinder; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.os.RemoteException; 29 import android.util.Log; 30 31 import com.android.internal.annotations.GuardedBy; 32 33 import java.lang.ref.WeakReference; 34 35 /** 36 * Car Radio manager. 37 * 38 * This API works in conjunction with the {@link RadioManager.java} and provides 39 * features additional to the ones provided in there. It supports: 40 * 41 * 1. Capability to control presets. 42 * @hide 43 */ 44 @SystemApi 45 public class CarRadioManager implements CarManagerBase { 46 public final static boolean DBG = true; 47 public final static String TAG = "CarRadioManager"; 48 49 // Constants handled in the handler (see mHandler below). 50 private final static int MSG_RADIO_EVENT = 0; 51 52 private int mCount = 0; 53 private final ICarRadio mService; 54 @GuardedBy("this") 55 private CarRadioEventListener mListener = null; 56 @GuardedBy("this") 57 private CarRadioEventListenerToService mListenerToService = null; 58 private static final class EventCallbackHandler extends Handler { 59 WeakReference<CarRadioManager> mMgr; 60 61 EventCallbackHandler(CarRadioManager mgr, Looper looper) { 62 super(looper); 63 mMgr = new WeakReference<CarRadioManager>(mgr); 64 } 65 66 @Override 67 public void handleMessage(Message msg) { 68 switch (msg.what) { 69 case MSG_RADIO_EVENT: 70 CarRadioManager mgr = mMgr.get(); 71 if (mgr != null) { 72 mgr.dispatchEventToClient((CarRadioEvent) msg.obj); 73 } 74 break; 75 default: 76 Log.e(TAG, "Event type not handled?" + msg); 77 } 78 } 79 } 80 81 private final Handler mHandler; 82 83 private static class CarRadioEventListenerToService extends ICarRadioEventListener.Stub { 84 private final WeakReference<CarRadioManager> mManager; 85 86 public CarRadioEventListenerToService(CarRadioManager manager) { 87 mManager = new WeakReference<CarRadioManager>(manager); 88 } 89 90 @Override 91 public void onEvent(CarRadioEvent event) { 92 CarRadioManager manager = mManager.get(); 93 if (manager != null) { 94 manager.handleEvent(event); 95 } 96 } 97 } 98 99 100 /** Listener for car radio events. 101 */ 102 public interface CarRadioEventListener { 103 /** 104 * Called when there is a preset value is reprogrammed. 105 */ 106 void onEvent(final CarRadioEvent event); 107 } 108 109 /** 110 * Get an instance of the CarRadioManager. 111 * 112 * Should not be obtained directly by clients, use {@link Car.getCarManager()} instead. 113 * @hide 114 */ 115 public CarRadioManager(IBinder service, Looper looper) throws CarNotConnectedException { 116 mService = ICarRadio.Stub.asInterface(service); 117 mHandler = new EventCallbackHandler(this, looper); 118 119 // Populate the fixed values. 120 try { 121 mCount = mService.getPresetCount(); 122 } catch (RemoteException ex) { 123 Log.e(TAG, "Could not connect: " + ex.toString()); 124 throw new CarNotConnectedException(ex); 125 } 126 } 127 128 /** 129 * Register {@link CarRadioEventListener} to get radio unit changes. 130 */ 131 public synchronized void registerListener(CarRadioEventListener listener) 132 throws CarNotConnectedException { 133 if (mListener != null) { 134 throw new IllegalStateException("Listener already registered. Did you call " + 135 "registerListener() twice?"); 136 } 137 138 mListener = listener; 139 try { 140 mListenerToService = new CarRadioEventListenerToService(this); 141 mService.registerListener(mListenerToService); 142 } catch (RemoteException ex) { 143 // Do nothing. 144 Log.e(TAG, "Could not connect: " + ex.toString()); 145 throw new CarNotConnectedException(ex); 146 } catch (IllegalStateException ex) { 147 Car.checkCarNotConnectedExceptionFromCarService(ex); 148 } 149 } 150 151 /** 152 * Unregister {@link CarRadioEventListener}. 153 */ 154 public synchronized void unregisterListener() throws CarNotConnectedException { 155 if (DBG) { 156 Log.d(TAG, "unregisterListener"); 157 } 158 try { 159 mService.unregisterListener(mListenerToService); 160 } catch (RemoteException ex) { 161 Log.e(TAG, "Could not connect: " + ex.toString()); 162 throw new CarNotConnectedException(ex); 163 } 164 mListenerToService = null; 165 mListener = null; 166 } 167 168 /** 169 * Get the number of (hard) presets supported by car radio unit. 170 * 171 * @return: A positive value if the call succeeded, -1 if it failed. 172 */ 173 public int getPresetCount() { 174 return mCount; 175 } 176 177 /** 178 * Get preset value for a specific radio preset. 179 * @return: a {@link CarRadioPreset} object, {@link null} if the call failed. 180 */ 181 public CarRadioPreset getPreset(int presetNumber) throws CarNotConnectedException { 182 if (DBG) { 183 Log.d(TAG, "getPreset"); 184 } 185 try { 186 CarRadioPreset preset = mService.getPreset(presetNumber); 187 return preset; 188 } catch (RemoteException ex) { 189 Log.e(TAG, "getPreset failed with " + ex.toString()); 190 throw new CarNotConnectedException(ex); 191 } 192 } 193 194 /** 195 * Set the preset value to a specific radio preset. 196 * 197 * In order to ensure that the preset value indeed get updated, wait for event on the listener 198 * registered via registerListener(). 199 * 200 * @return: {@link boolean} value which returns true if the request succeeded and false 201 * otherwise. Common reasons for the failure could be: 202 * a) Preset is invalid (the preset number is out of range from {@link getPresetCount()}. 203 * b) Listener is not set correctly, since otherwise the user of this API cannot confirm if the 204 * request succeeded. 205 */ 206 public boolean setPreset(CarRadioPreset preset) throws IllegalArgumentException, 207 CarNotConnectedException { 208 try { 209 return mService.setPreset(preset); 210 } catch (RemoteException ex) { 211 throw new CarNotConnectedException(ex); 212 } 213 } 214 215 private void dispatchEventToClient(CarRadioEvent event) { 216 CarRadioEventListener listener; 217 synchronized (this) { 218 listener = mListener; 219 } 220 if (listener != null) { 221 listener.onEvent(event); 222 } else { 223 Log.e(TAG, "Listener died, not dispatching event."); 224 } 225 } 226 227 private void handleEvent(CarRadioEvent event) { 228 mHandler.sendMessage(mHandler.obtainMessage(MSG_RADIO_EVENT, event)); 229 } 230 231 /** @hide */ 232 @Override 233 public synchronized void onCarDisconnected() { 234 mListener = null; 235 mListenerToService = null; 236 } 237 } 238