13cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal/* 23cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * Copyright (C) 2015 The Android Open Source Project 33cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * 43cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * Licensed under the Apache License, Version 2.0 (the "License"); 53cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * you may not use this file except in compliance with the License. 63cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * You may obtain a copy of the License at 73cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * 83cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * http://www.apache.org/licenses/LICENSE-2.0 93cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * 103cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * Unless required by applicable law or agreed to in writing, software 113cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * distributed under the License is distributed on an "AS IS" BASIS, 123cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * See the License for the specific language governing permissions and 143cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * limitations under the License. 153cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal */ 163cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 173cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwalpackage com.android.car; 183cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 19e54ac276796c6535558f8444d882adecd19ce2bdKeun-young Parkimport android.car.Car; 20e54ac276796c6535558f8444d882adecd19ce2bdKeun-young Parkimport android.car.hardware.radio.CarRadioEvent; 21e54ac276796c6535558f8444d882adecd19ce2bdKeun-young Parkimport android.car.hardware.radio.CarRadioPreset; 22e54ac276796c6535558f8444d882adecd19ce2bdKeun-young Parkimport android.car.hardware.radio.ICarRadio; 23e54ac276796c6535558f8444d882adecd19ce2bdKeun-young Parkimport android.car.hardware.radio.ICarRadioEventListener; 243cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwalimport android.content.Context; 253cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwalimport android.content.pm.PackageManager; 263cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwalimport android.os.IBinder; 273cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwalimport android.os.Process; 283cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwalimport android.os.RemoteException; 293cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwalimport android.util.Log; 303cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 313cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwalimport com.android.car.hal.VehicleHal; 323cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwalimport com.android.car.hal.RadioHalService; 333cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 343cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwalimport java.io.PrintWriter; 353cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwalimport java.util.HashMap; 363cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 373cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwalpublic class CarRadioService extends ICarRadio.Stub 383cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal implements CarServiceBase, RadioHalService.RadioListener { 393cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal public static boolean DBG = true; 403cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal public static String TAG = CarLog.TAG_RADIO + ".CarRadioService"; 413cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 423cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal private RadioHalService mRadioHal; 433cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal private final HashMap<IBinder, ICarRadioEventListener> mListenersMap = 443cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal new HashMap<IBinder, ICarRadioEventListener>(); 453cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal private final HashMap<IBinder, RadioDeathRecipient> mDeathRecipientMap = 463cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal new HashMap<IBinder, RadioDeathRecipient>(); 473cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal private final Context mContext; 483cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 493cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal public CarRadioService(Context context) { 503cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mRadioHal = VehicleHal.getInstance().getRadioHal(); 513cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mContext = context; 523cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 533cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 543cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal class RadioDeathRecipient implements IBinder.DeathRecipient { 553cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal private String TAG = CarRadioService.TAG + ".RadioDeathRecipient"; 563cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal private IBinder mListenerBinder; 573cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 583cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal RadioDeathRecipient(IBinder listenerBinder) { 593cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mListenerBinder = listenerBinder; 603cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 613cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 623cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal /** 633cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * Client died. Remove the listener from HAL service and unregister if this is the last 643cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal * client. 653cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal */ 663cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal @Override 673cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal public void binderDied() { 683cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (DBG) { 693cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal Log.d(TAG, "binderDied " + mListenerBinder); 703cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 713cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mListenerBinder.unlinkToDeath(this, 0); 723cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal CarRadioService.this.unregisterListenerLocked(mListenerBinder); 733cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 743cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 753cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal void release() { 763cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mListenerBinder.unlinkToDeath(this, 0); 773cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 783cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 793cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 803cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal @Override 813cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal public synchronized void init() { 823cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 833cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 843cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal @Override 853cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal public synchronized void release() { 863cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal for (IBinder listenerBinder : mListenersMap.keySet()) { 873cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal RadioDeathRecipient deathRecipient = mDeathRecipientMap.get(listenerBinder); 883cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal deathRecipient.release(); 893cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 903cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mDeathRecipientMap.clear(); 913cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mListenersMap.clear(); 923cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 933cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 943cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal @Override 953cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal public void dump(PrintWriter writer) { 963cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 973cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 983cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal @Override 993cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal public int getPresetCount() { 1003cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal return mRadioHal.getPresetCount(); 1013cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1023cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1033cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal @Override 104e54ac276796c6535558f8444d882adecd19ce2bdKeun-young Park public synchronized void registerListener(ICarRadioEventListener listener) { 1053cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (DBG) { 1063cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal Log.d(TAG, "registerListener"); 1073cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1083cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (listener == null) { 1093cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal Log.e(TAG, "registerListener: Listener is null."); 1103cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal throw new IllegalStateException("listener cannot be null."); 1113cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1123cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1133cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal IBinder listenerBinder = listener.asBinder(); 1143cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (mListenersMap.containsKey(listenerBinder)) { 1153cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal // Already registered, nothing to do. 1163cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal return; 1173cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1183cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1193cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal RadioDeathRecipient deathRecipient = new RadioDeathRecipient(listenerBinder); 1203cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal try { 1213cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal listenerBinder.linkToDeath(deathRecipient, 0); 1223cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } catch (RemoteException e) { 1233cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal Log.e(TAG, "Failed to link death for recipient. " + e); 1243cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG); 1253cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1263cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mDeathRecipientMap.put(listenerBinder, deathRecipient); 1273cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1283cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (mListenersMap.isEmpty()) { 1293cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mRadioHal.registerListener(this); 1303cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1313cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1323cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mListenersMap.put(listenerBinder, listener); 1333cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1343cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1353cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal @Override 1363cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal public synchronized void unregisterListener(ICarRadioEventListener listener) { 1373cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (DBG) { 1383cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal Log.d(TAG, "unregisterListener"); 1393cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1403cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1413cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (listener == null) { 1423cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal Log.e(TAG, "unregisterListener: Listener is null."); 1433cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal throw new IllegalArgumentException("Listener is null"); 1443cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1453cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1463cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal IBinder listenerBinder = listener.asBinder(); 1473cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (!mListenersMap.containsKey(listenerBinder)) { 1483cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal Log.e(TAG, "unregisterListener: Listener was not previously registered."); 1493cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1503cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal unregisterListenerLocked(listenerBinder); 1513cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1523cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1533cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal // Removes the listenerBinder from the current state. 1543cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal // The function assumes that the binder will exist both in listeners and death recipients list. 1553cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal private void unregisterListenerLocked(IBinder listenerBinder) { 1563cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal Object status = mListenersMap.remove(listenerBinder); 1573cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (status == null) throw new IllegalStateException( 1583cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal "Map must contain the event listener."); 1593cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1603cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal // If there is a state muck up, the release() call will throw an exception automagically. 1613cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mDeathRecipientMap.get(listenerBinder).release(); 1623cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mDeathRecipientMap.remove(listenerBinder); 1633cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1643cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (mListenersMap.isEmpty()) { 1653cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal mRadioHal.unregisterListener(); 1663cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1673cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1683cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1693cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal @Override 1703cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal public CarRadioPreset getPreset(int index) { 1713cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (DBG) { 1723cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal Log.d(TAG, "getPreset " + index); 1733cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1743cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal return mRadioHal.getRadioPreset(index); 1753cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1763cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1773cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal @Override 1783cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal public boolean setPreset(CarRadioPreset preset) { 1793cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal checkRadioPremissions(); 1803cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (DBG) { 1813cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal Log.d(TAG, "setPreset " + preset); 1823cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1833cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal boolean status = mRadioHal.setRadioPreset(preset); 1843cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (status == false) { 1853cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal Log.e(TAG, "setPreset failed!"); 1863cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1873cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal return status; 1883cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 1893cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 1903cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal @Override 1913cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal public synchronized void onEvent(CarRadioEvent event) { 1923cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal for (ICarRadioEventListener l : mListenersMap.values()) { 1933cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal try { 1943cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal l.onEvent(event); 1953cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } catch (RemoteException ex) { 1963cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal // If we could not send a record, its likely the connection snapped. Let the binder 1973cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal // death handle the situation. 1983cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal Log.e(TAG, "onEvent calling failed: " + ex); 1993cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 2003cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 2013cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 2023cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal 2033cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal private void checkRadioPremissions() { 2043cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal if (getCallingUid() != Process.SYSTEM_UID && 205e54ac276796c6535558f8444d882adecd19ce2bdKeun-young Park mContext.checkCallingOrSelfPermission(Car.PERMISSION_CAR_RADIO) != 2063cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal PackageManager.PERMISSION_GRANTED) { 2073cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal throw new SecurityException("requires system app or " + 208e54ac276796c6535558f8444d882adecd19ce2bdKeun-young Park Car.PERMISSION_CAR_RADIO); 2093cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 2103cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal } 2113cf096ae0d992d22cfba1b0711af2211c511a9feSanket Agarwal} 212