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;
17
18import static org.junit.Assert.assertEquals;
19import static org.junit.Assert.fail;
20
21import android.hardware.automotive.vehicle.V2_0.VehicleApPowerBootupReason;
22import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateConfigFlag;
23import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReport;
24import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReq;
25import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateReqIndex;
26import android.hardware.automotive.vehicle.V2_0.VehicleApPowerStateShutdownParam;
27import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
28import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
29import android.os.SystemClock;
30import android.support.test.annotation.UiThreadTest;
31import android.support.test.filters.MediumTest;
32import android.support.test.runner.AndroidJUnit4;
33
34import com.android.car.systeminterface.DisplayInterface;
35import com.android.car.systeminterface.SystemInterface;
36import com.android.car.vehiclehal.VehiclePropValueBuilder;
37import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
38
39import com.google.android.collect.Lists;
40
41import org.junit.Test;
42import org.junit.runner.RunWith;
43
44import java.util.ArrayList;
45import java.util.LinkedList;
46import java.util.concurrent.Semaphore;
47import java.util.concurrent.TimeUnit;
48
49@RunWith(AndroidJUnit4.class)
50@MediumTest
51public class CarPowerManagementTest extends MockedCarTestBase {
52
53    private final PowerStatePropertyHandler mPowerStateHandler = new PowerStatePropertyHandler();
54    private final MockDisplayInterface mMockDisplayInterface = new MockDisplayInterface();
55
56    @Override
57    protected synchronized SystemInterface.Builder getSystemInterfaceBuilder() {
58        SystemInterface.Builder builder = super.getSystemInterfaceBuilder();
59        return builder.withDisplayInterface(mMockDisplayInterface);
60    }
61
62    private void setupPowerPropertyAndStart(boolean allowSleep) throws Exception {
63        addProperty(VehicleProperty.AP_POWER_STATE_REQ, mPowerStateHandler)
64                .setConfigArray(Lists.newArrayList(
65                        allowSleep ? VehicleApPowerStateConfigFlag.ENABLE_DEEP_SLEEP_FLAG : 0));
66        addProperty(VehicleProperty.AP_POWER_STATE_REPORT, mPowerStateHandler);
67
68        addStaticProperty(VehicleProperty.AP_POWER_BOOTUP_REASON,
69                VehiclePropValueBuilder.newBuilder(VehicleProperty.AP_POWER_BOOTUP_REASON)
70                        .addIntValue(VehicleApPowerBootupReason.USER_POWER_ON)
71                        .build());
72
73        reinitializeMockedHal();
74    }
75
76    @Test
77    @UiThreadTest
78    public void testImmediateShutdown() throws Exception {
79        setupPowerPropertyAndStart(true);
80        assertBootComplete();
81        mPowerStateHandler.sendPowerState(
82                VehicleApPowerStateReq.SHUTDOWN_PREPARE,
83                VehicleApPowerStateShutdownParam.SHUTDOWN_IMMEDIATELY);
84        mPowerStateHandler.waitForStateSetAndGetAll(DEFAULT_WAIT_TIMEOUT_MS,
85                VehicleApPowerStateReport.SHUTDOWN_START);
86        mPowerStateHandler.sendPowerState(VehicleApPowerStateReq.ON_FULL, 0);
87    }
88
89    @Test
90    @UiThreadTest
91    public void testDisplayOnOff() throws Exception {
92        setupPowerPropertyAndStart(true);
93        assertBootComplete();
94        for (int i = 0; i < 2; i++) {
95            mPowerStateHandler.sendPowerState(VehicleApPowerStateReq.ON_DISP_OFF, 0);
96            mMockDisplayInterface.waitForDisplayState(false);
97            mPowerStateHandler.sendPowerState(VehicleApPowerStateReq.ON_FULL, 0);
98            mMockDisplayInterface.waitForDisplayState(true);
99        }
100    }
101
102    /* TODO make deep sleep work to test this
103    @Test public void testSleepEntry() throws Exception {
104        assertBootComplete();
105        mPowerStateHandler.sendPowerState(
106                VehicleApPowerState.SHUTDOWN_PREPARE,
107                VehicleApPowerStateShutdownParam.CAN_SLEEP);
108        assertResponse(VehicleApPowerSetState.DEEP_SLEEP_ENTRY, 0);
109        assertResponse(VehicleApPowerSetState.DEEP_SLEEP_EXIT, 0);
110        mPowerStateHandler.sendPowerState(
111                VehicleApPowerState.ON_FULL,
112                0);
113    }*/
114
115    private void assertResponse(int expectedResponseState, int expectedResponseParam)
116            throws Exception {
117        LinkedList<int[]> setEvents = mPowerStateHandler.waitForStateSetAndGetAll(
118                DEFAULT_WAIT_TIMEOUT_MS, expectedResponseState);
119        int[] last = setEvents.getLast();
120        assertEquals(expectedResponseState, last[0]);
121        assertEquals(expectedResponseParam, last[1]);
122    }
123
124    private void assertBootComplete() throws Exception {
125        mPowerStateHandler.waitForSubscription(DEFAULT_WAIT_TIMEOUT_MS);
126        LinkedList<int[]> setEvents = mPowerStateHandler.waitForStateSetAndGetAll(
127                DEFAULT_WAIT_TIMEOUT_MS, VehicleApPowerStateReport.BOOT_COMPLETE);
128        int[] first = setEvents.getFirst();
129        assertEquals(VehicleApPowerStateReport.BOOT_COMPLETE, first[0]);
130        assertEquals(0, first[1]);
131    }
132
133    private final class MockDisplayInterface implements DisplayInterface {
134        private boolean mDisplayOn = true;
135        private final Semaphore mDisplayStateWait = new Semaphore(0);
136
137        @Override
138        public void setDisplayBrightness(int brightness) {}
139
140        @Override
141        public synchronized void setDisplayState(boolean on) {
142            mDisplayOn = on;
143            mDisplayStateWait.release();
144        }
145
146        boolean waitForDisplayState(boolean expectedState)
147            throws Exception {
148            if (expectedState == mDisplayOn) {
149                return true;
150            }
151            mDisplayStateWait.tryAcquire(MockedCarTestBase.SHORT_WAIT_TIMEOUT_MS,
152                    TimeUnit.MILLISECONDS);
153            return expectedState == mDisplayOn;
154        }
155
156        @Override
157        public void startDisplayStateMonitoring(CarPowerManagementService service) {}
158
159        @Override
160        public void stopDisplayStateMonitoring() {}
161    }
162
163    private class PowerStatePropertyHandler implements VehicleHalPropertyHandler {
164
165        private int mPowerState = VehicleApPowerStateReq.ON_FULL;
166        private int mPowerParam = 0;
167
168        private final Semaphore mSubscriptionWaitSemaphore = new Semaphore(0);
169        private final Semaphore mSetWaitSemaphore = new Semaphore(0);
170        private LinkedList<int[]> mSetStates = new LinkedList<>();
171
172        @Override
173        public void onPropertySet(VehiclePropValue value) {
174            ArrayList<Integer> v = value.value.int32Values;
175            synchronized (this) {
176                mSetStates.add(new int[] {
177                        v.get(VehicleApPowerStateReqIndex.STATE),
178                        v.get(VehicleApPowerStateReqIndex.ADDITIONAL)
179                });
180            }
181            mSetWaitSemaphore.release();
182        }
183
184        @Override
185        public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
186            return VehiclePropValueBuilder.newBuilder(VehicleProperty.AP_POWER_STATE_REQ)
187                    .setTimestamp(SystemClock.elapsedRealtimeNanos())
188                    .addIntValue(mPowerState, mPowerParam)
189                    .build();
190        }
191
192        @Override
193        public void onPropertySubscribe(int property, float sampleRate) {
194            mSubscriptionWaitSemaphore.release();
195        }
196
197        @Override
198        public void onPropertyUnsubscribe(int property) {
199            //ignore
200        }
201
202        private synchronized void setCurrentState(int state, int param) {
203            mPowerState = state;
204            mPowerParam = param;
205        }
206
207        private void waitForSubscription(long timeoutMs) throws Exception {
208            if (!mSubscriptionWaitSemaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
209                fail("waitForSubscription timeout");
210            }
211        }
212
213        private LinkedList<int[]> waitForStateSetAndGetAll(long timeoutMs, int expectedSet)
214                throws Exception {
215            while (true) {
216                if (!mSetWaitSemaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
217                    fail("waitForStateSetAndGetAll timeout");
218                }
219                synchronized (this) {
220                    boolean found = false;
221                    for (int[] state : mSetStates) {
222                        if (state[0] == expectedSet) {
223                            found = true;
224                        }
225                    }
226                    if (found) {
227                        LinkedList<int[]> res = mSetStates;
228                        mSetStates = new LinkedList<>();
229                        return res;
230                    }
231                }
232            }
233        }
234
235        private void sendPowerState(int state, int param) {
236            getMockedVehicleHal().injectEvent(
237                    VehiclePropValueBuilder.newBuilder(VehicleProperty.AP_POWER_STATE_REQ)
238                            .setTimestamp(SystemClock.elapsedRealtimeNanos())
239                            .addIntValue(state, param)
240                            .build());
241        }
242    }
243}
244