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