Home | History | Annotate | Download | only in radio
      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 final class CarRadioManager implements CarManagerBase {
     46     private final static boolean DBG = false;
     47     private 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(String)} instead.
    113      * @hide
    114      */
    115     public CarRadioManager(IBinder service, Handler handler) throws CarNotConnectedException {
    116         mService = ICarRadio.Stub.asInterface(service);
    117         mHandler = new EventCallbackHandler(this, handler.getLooper());
    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() {
    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             //ignore
    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() throws CarNotConnectedException {
    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