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 */
16package com.android.car.test;
17
18import android.car.Car;
19import android.car.hardware.radio.CarRadioEvent;
20import android.car.hardware.radio.CarRadioManager;
21import android.car.hardware.radio.CarRadioManager.CarRadioEventListener;
22import android.car.hardware.radio.CarRadioPreset;
23import android.hardware.radio.RadioManager;
24import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
25import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
26import android.os.SystemClock;
27import android.test.suitebuilder.annotation.MediumTest;
28import android.util.Log;
29
30import com.google.android.collect.Lists;
31
32import com.android.car.vehiclehal.VehiclePropValueBuilder;
33import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
34
35import java.util.HashMap;
36import java.util.concurrent.Semaphore;
37import java.util.concurrent.TimeUnit;
38
39@MediumTest
40public class CarRadioManagerTest extends MockedCarTestBase {
41
42    private static final String TAG = CarRadioManagerTest.class.getSimpleName();
43
44    // Use this semaphore to block until the callback is heard of.
45    private Semaphore mAvailable;
46
47    private static final int NUM_PRESETS = 2;
48    private final HashMap<Integer, CarRadioPreset> mRadioPresets = new HashMap<>();
49
50    private CarRadioManager mCarRadioManager;
51
52    private class RadioPresetPropertyHandler implements VehicleHalPropertyHandler {
53        public RadioPresetPropertyHandler() { }
54
55        @Override
56        public synchronized void onPropertySet(VehiclePropValue value) {
57            assertEquals(value.prop, VehicleProperty.RADIO_PRESET);
58
59            Integer[] valueList = new Integer[4];
60            value.value.int32Values.toArray(valueList);
61            assertFalse(
62                "Index out of range: " + valueList[0] + " (0, " + NUM_PRESETS + ")",
63                valueList[0] < 1);
64            assertFalse(
65                "Index out of range: " + valueList[0] + " (0, " + NUM_PRESETS + ")",
66                valueList[0] > NUM_PRESETS);
67
68            CarRadioPreset preset =
69                new CarRadioPreset(valueList[0], valueList[1], valueList[2], valueList[3]);
70            mRadioPresets.put(valueList[0], preset);
71
72            // The test case must be waiting for the semaphore, if not we should throw exception.
73            if (mAvailable.availablePermits() != 0) {
74                Log.d(TAG, "Lock was free, should have been locked.");
75            }
76            mAvailable.release();
77        }
78
79        @Override
80        public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
81            assertEquals(value.prop, VehicleProperty.RADIO_PRESET);
82
83            Integer[] valueList = new Integer[4];
84            value.value.int32Values.toArray(valueList);
85
86            // Get the actual preset.
87            if (valueList[0] < 1 || valueList[0] > NUM_PRESETS) {
88                // VNS will call get method when subscribe is called, just return an empty
89                // value.
90                return value;
91            }
92            CarRadioPreset preset = mRadioPresets.get(valueList[0]);
93            return VehiclePropValueBuilder.newBuilder(VehicleProperty.RADIO_PRESET)
94                    .setTimestamp(SystemClock.elapsedRealtimeNanos())
95                    .addIntValue(
96                            preset.getPresetNumber(),
97                            preset.getBand(),
98                            preset.getChannel(),
99                            preset.getSubChannel())
100                    .build();
101        }
102
103        @Override
104        public synchronized void onPropertySubscribe(int property, int zones, float sampleRate) {
105            Log.d(TAG, "onPropertySubscribe property: " + property + " rate: " + sampleRate);
106            if (mAvailable.availablePermits() != 0) {
107                Log.d(TAG, "Lock was free, should have been locked.");
108                return;
109            }
110            mAvailable.release();
111        }
112
113        @Override
114        public synchronized void onPropertyUnsubscribe(int property) {
115        }
116    }
117
118    private class EventListener implements CarRadioEventListener {
119        public EventListener() { }
120
121        @Override
122        public void onEvent(CarRadioEvent event) {
123            // Print the event and release the lock.
124            Log.d(TAG, event.toString());
125            if (mAvailable.availablePermits() != 0) {
126                Log.e(TAG, "Lock should be taken.");
127                // Let the timeout fail the test here.
128                return;
129            }
130            mAvailable.release();
131        }
132    }
133
134    @Override
135    protected synchronized void configureMockedHal() {
136        addProperty(VehicleProperty.RADIO_PRESET, new RadioPresetPropertyHandler())
137                .setConfigArray(Lists.newArrayList(NUM_PRESETS));
138    }
139
140    @Override
141    protected void setUp() throws Exception {
142        super.setUp();
143        mAvailable = new Semaphore(0);
144        mCarRadioManager = (CarRadioManager) getCar().getCarManager(Car.RADIO_SERVICE);
145    }
146
147    public void testPresetCount() throws Exception {
148        int presetCount = mCarRadioManager.getPresetCount();
149        assertEquals("Preset count not same.", NUM_PRESETS, presetCount);
150    }
151
152    public void testSetAndGetPreset() throws Exception {
153        // Create a preset.
154        CarRadioPreset preset = new CarRadioPreset(1, RadioManager.BAND_FM, 1234, -1);
155        assertEquals("Lock should be freed by now.", 0, mAvailable.availablePermits());
156        // mAvailable.acquire(1);
157        mCarRadioManager.setPreset(preset);
158
159        // Wait for acquire to be available again, fail if timeout.
160        boolean success = mAvailable.tryAcquire(5L, TimeUnit.SECONDS);
161        assertEquals("Could not finish setting, timeout!", true, success);
162
163        // Test that get preset gives you the same element.
164        assertEquals(preset, mCarRadioManager.getPreset(1));
165    }
166
167    public void testSubscribe() throws Exception {
168        EventListener listener = new EventListener();
169        assertEquals("Lock should be freed by now.", 0, mAvailable.availablePermits());
170        mCarRadioManager.registerListener(listener);
171
172        // Wait for acquire to be available again, fail if timeout.
173        boolean success = mAvailable.tryAcquire(5L, TimeUnit.SECONDS);
174        assertEquals("addListener timeout", true, success);
175
176        // Inject an event and wait for its callback in onPropertySet.
177        CarRadioPreset preset = new CarRadioPreset(2, RadioManager.BAND_AM, 4321, -1);
178
179        VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.RADIO_PRESET)
180                .setTimestamp(SystemClock.elapsedRealtimeNanos())
181                .addIntValue(
182                        preset.getPresetNumber(),
183                        preset.getBand(),
184                        preset.getChannel(),
185                        preset.getSubChannel())
186                .build();
187        getMockedVehicleHal().injectEvent(v);
188
189        success = mAvailable.tryAcquire(5L, TimeUnit.SECONDS);
190        assertEquals("injectEvent, onEvent timeout!", true, success);
191    }
192}
193