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
17package com.android.car.test;
18
19import android.car.Car;
20import android.car.CarProjectionManager;
21import android.hardware.automotive.vehicle.V2_0.VehicleHwKeyInputAction;
22import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
23import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
24import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
25import android.os.SystemClock;
26import android.test.suitebuilder.annotation.MediumTest;
27import android.util.Log;
28import android.view.KeyEvent;
29
30import com.android.car.vehiclehal.VehiclePropValueBuilder;
31import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
32
33import java.util.HashMap;
34import java.util.concurrent.Semaphore;
35import java.util.concurrent.TimeUnit;
36
37@MediumTest
38public class CarProjectionManagerTest extends MockedCarTestBase {
39    private static final String TAG = CarProjectionManagerTest.class.getSimpleName();
40
41    private final Semaphore mLongAvailable = new Semaphore(0);
42    private final Semaphore mAvailable = new Semaphore(0);
43
44    private final CarProjectionManager.CarProjectionListener mListener =
45            new CarProjectionManager.CarProjectionListener() {
46                @Override
47                public void onVoiceAssistantRequest(boolean fromLongPress) {
48                    if (fromLongPress) {
49                        mLongAvailable.release();
50                    } else {
51                        mAvailable.release();
52                    }
53                }
54            };
55
56    private CarProjectionManager mManager;
57
58    @Override
59    protected synchronized void configureMockedHal() {
60        addProperty(VehicleProperty.HW_KEY_INPUT, new PropertyHandler())
61                .setAccess(VehiclePropertyAccess.READ);
62    }
63
64    @Override
65    protected void setUp() throws Exception {
66        super.setUp();
67        mManager = (CarProjectionManager) getCar().getCarManager(Car.PROJECTION_SERVICE);
68    }
69
70    public void testShortPressListener() throws Exception {
71        mManager.registerProjectionListener(
72                mListener,
73                CarProjectionManager.PROJECTION_VOICE_SEARCH);
74        assertEquals(0, mAvailable.availablePermits());
75        assertEquals(0, mLongAvailable.availablePermits());
76        sendVoiceKey(false);
77        assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS));
78        assertEquals(0, mLongAvailable.availablePermits());
79    }
80
81    public void testLongPressListener() throws Exception {
82        mManager.registerProjectionListener(
83                mListener,
84                CarProjectionManager.PROJECTION_LONG_PRESS_VOICE_SEARCH);
85        assertEquals(0, mLongAvailable.availablePermits());
86        assertEquals(0, mAvailable.availablePermits());
87        sendVoiceKey(true);
88        assertTrue(mLongAvailable.tryAcquire(2L, TimeUnit.SECONDS));
89        assertEquals(0, mAvailable.availablePermits());
90    }
91
92    public void testMixedPressListener() throws Exception {
93        mManager.registerProjectionListener(
94                mListener,
95                CarProjectionManager.PROJECTION_LONG_PRESS_VOICE_SEARCH
96                | CarProjectionManager.PROJECTION_VOICE_SEARCH);
97        assertEquals(0, mLongAvailable.availablePermits());
98        assertEquals(0, mAvailable.availablePermits());
99        sendVoiceKey(true);
100        assertTrue(mLongAvailable.tryAcquire(2L, TimeUnit.SECONDS));
101        assertEquals(0, mAvailable.availablePermits());
102
103        assertEquals(0, mLongAvailable.availablePermits());
104        assertEquals(0, mAvailable.availablePermits());
105        sendVoiceKey(false);
106        assertTrue(mAvailable.tryAcquire(2L, TimeUnit.SECONDS));
107        assertEquals(0, mLongAvailable.availablePermits());
108    }
109
110    public void sendVoiceKey(boolean isLong) throws InterruptedException {
111        int[] values = {VehicleHwKeyInputAction.ACTION_DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, 0, 0};
112
113        VehiclePropValue injectValue =
114                VehiclePropValueBuilder.newBuilder(VehicleProperty.HW_KEY_INPUT)
115                        .setTimestamp(SystemClock.elapsedRealtimeNanos())
116                        .addIntValue(values)
117                        .build();
118
119        getMockedVehicleHal().injectEvent(injectValue);
120
121        if (isLong) {
122            Thread.sleep(1200); // Long press is > 1s.
123        }
124
125        int[] upValues = {VehicleHwKeyInputAction.ACTION_UP, KeyEvent.KEYCODE_VOICE_ASSIST, 0, 0 };
126
127        injectValue = VehiclePropValueBuilder.newBuilder(VehicleProperty.HW_KEY_INPUT)
128                .setTimestamp(SystemClock.elapsedRealtimeNanos())
129                .addIntValue(upValues)
130                .build();
131
132        getMockedVehicleHal().injectEvent(injectValue);
133    }
134
135
136    private class PropertyHandler implements VehicleHalPropertyHandler {
137        HashMap<Integer, VehiclePropValue> mMap = new HashMap<>();
138
139        @Override
140        public synchronized void onPropertySet(VehiclePropValue value) {
141            Log.d(TAG, "onPropertySet:" + value);
142            mMap.put(value.prop, value);
143        }
144
145        @Override
146        public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
147            Log.d(TAG, "onPropertyGet:" + value);
148            VehiclePropValue currentValue = mMap.get(value.prop);
149            // VNS will call get method when subscribe is called, just return empty value.
150            return currentValue != null ? currentValue : value;
151        }
152
153        @Override
154        public synchronized void onPropertySubscribe(int property, int zones, float sampleRate) {
155            Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate);
156        }
157
158        @Override
159        public synchronized void onPropertyUnsubscribe(int property) {
160            Log.d(TAG, "onPropertyUnSubscribe property " + property);
161        }
162    }
163}
164