1 /* 2 * Copyright (C) 2023 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 17 package com.android.server.soundtrigger; 18 19 import static android.os.PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED; 20 import static android.os.PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED; 21 import static android.os.PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY; 22 23 import static com.android.server.soundtrigger.DeviceStateHandler.SoundTriggerDeviceState; 24 import static com.android.server.soundtrigger.DeviceStateHandler.SoundTriggerDeviceState.*; 25 26 import static com.google.common.truth.Truth.assertThat; 27 28 import android.os.SystemClock; 29 30 import androidx.test.filters.FlakyTest; 31 import androidx.test.runner.AndroidJUnit4; 32 33 import com.android.internal.annotations.GuardedBy; 34 import com.android.server.utils.EventLogger; 35 36 import org.junit.Before; 37 import org.junit.Test; 38 import org.junit.runner.RunWith; 39 40 import java.util.concurrent.CountDownLatch; 41 import java.util.concurrent.TimeUnit; 42 43 @RunWith(AndroidJUnit4.class) 44 public final class DeviceStateHandlerTest { 45 private final long CONFIRM_NO_EVENT_WAIT_MS = 1000; 46 // A wait substantially less than the duration we delay phone notifications by 47 private final long PHONE_DELAY_BRIEF_WAIT_MS = 48 DeviceStateHandler.CALL_INACTIVE_MSG_DELAY_MS / 4; 49 50 private DeviceStateHandler mHandler; 51 private DeviceStateHandler.DeviceStateListener mDeviceStateCallback; 52 53 private final Object mLock = new Object(); 54 55 @GuardedBy("mLock") 56 private SoundTriggerDeviceState mState; 57 58 @GuardedBy("mLock") 59 private CountDownLatch mLatch; 60 61 private EventLogger mEventLogger; 62 63 @Before setup()64 public void setup() { 65 // Reset the state prior to each test 66 mEventLogger = new EventLogger(256, "test logger"); 67 synchronized (mLock) { 68 mLatch = new CountDownLatch(1); 69 } 70 mDeviceStateCallback = 71 (SoundTriggerDeviceState state) -> { 72 synchronized (mLock) { 73 mState = state; 74 mLatch.countDown(); 75 } 76 }; 77 mHandler = new DeviceStateHandler(Runnable::run, mEventLogger); 78 mHandler.onPhoneCallStateChanged(false); 79 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); 80 mHandler.registerListener(mDeviceStateCallback); 81 try { 82 waitAndAssertState(ENABLE); 83 } catch (InterruptedException e) { 84 throw new RuntimeException(e); 85 } 86 } 87 waitAndAssertState(SoundTriggerDeviceState state)88 private void waitAndAssertState(SoundTriggerDeviceState state) throws InterruptedException { 89 CountDownLatch latch; 90 synchronized (mLock) { 91 latch = mLatch; 92 } 93 latch.await(); 94 synchronized (mLock) { 95 assertThat(mState).isEqualTo(state); 96 mLatch = new CountDownLatch(1); 97 } 98 } 99 waitToConfirmNoEventReceived()100 private void waitToConfirmNoEventReceived() throws InterruptedException { 101 CountDownLatch latch; 102 synchronized (mLock) { 103 latch = mLatch; 104 } 105 // Check that we time out 106 assertThat(latch.await(CONFIRM_NO_EVENT_WAIT_MS, TimeUnit.MILLISECONDS)).isFalse(); 107 } 108 109 @Test onPowerModeChangedCritical_receiveStateChange()110 public void onPowerModeChangedCritical_receiveStateChange() throws Exception { 111 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); 112 waitAndAssertState(CRITICAL); 113 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); 114 waitAndAssertState(ENABLE); 115 } 116 117 @Test onPowerModeChangedDisabled_receiveStateChange()118 public void onPowerModeChangedDisabled_receiveStateChange() throws Exception { 119 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); 120 waitAndAssertState(DISABLE); 121 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); 122 waitAndAssertState(ENABLE); 123 } 124 125 @Test onPowerModeChangedMultiple_receiveStateChange()126 public void onPowerModeChangedMultiple_receiveStateChange() throws Exception { 127 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); 128 waitAndAssertState(DISABLE); 129 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); 130 waitAndAssertState(CRITICAL); 131 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); 132 waitAndAssertState(DISABLE); 133 } 134 135 @Test onPowerModeSameState_noStateChange()136 public void onPowerModeSameState_noStateChange() throws Exception { 137 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); 138 waitAndAssertState(DISABLE); 139 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); 140 waitToConfirmNoEventReceived(); 141 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); 142 waitAndAssertState(CRITICAL); 143 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); 144 waitToConfirmNoEventReceived(); 145 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); 146 waitAndAssertState(ENABLE); 147 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); 148 waitToConfirmNoEventReceived(); 149 } 150 151 @Test onPhoneCall_receiveStateChange()152 public void onPhoneCall_receiveStateChange() throws Exception { 153 mHandler.onPhoneCallStateChanged(true); 154 waitAndAssertState(DISABLE); 155 mHandler.onPhoneCallStateChanged(false); 156 waitAndAssertState(ENABLE); 157 } 158 159 @Test onPhoneCall_receiveStateChangeIsDelayed()160 public void onPhoneCall_receiveStateChangeIsDelayed() throws Exception { 161 mHandler.onPhoneCallStateChanged(true); 162 waitAndAssertState(DISABLE); 163 long beforeTime = SystemClock.uptimeMillis(); 164 mHandler.onPhoneCallStateChanged(false); 165 waitAndAssertState(ENABLE); 166 long afterTime = SystemClock.uptimeMillis(); 167 assertThat(afterTime - beforeTime).isAtLeast(DeviceStateHandler.CALL_INACTIVE_MSG_DELAY_MS); 168 } 169 170 @Test onPhoneCallEnterExitEnter_receiveNoStateChange()171 public void onPhoneCallEnterExitEnter_receiveNoStateChange() throws Exception { 172 mHandler.onPhoneCallStateChanged(true); 173 waitAndAssertState(DISABLE); 174 mHandler.onPhoneCallStateChanged(false); 175 SystemClock.sleep(PHONE_DELAY_BRIEF_WAIT_MS); 176 mHandler.onPhoneCallStateChanged(true); 177 waitToConfirmNoEventReceived(); 178 } 179 180 @Test onBatteryCallbackDuringPhoneWait_receiveStateChangeDelayed()181 public void onBatteryCallbackDuringPhoneWait_receiveStateChangeDelayed() throws Exception { 182 mHandler.onPhoneCallStateChanged(true); 183 waitAndAssertState(DISABLE); 184 mHandler.onPhoneCallStateChanged(false); 185 SystemClock.sleep(PHONE_DELAY_BRIEF_WAIT_MS); 186 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); 187 waitAndAssertState(CRITICAL); 188 // Ensure we don't get an ENABLE event after 189 waitToConfirmNoEventReceived(); 190 } 191 192 @Test onBatteryChangeWhenInPhoneCall_receiveNoStateChange()193 public void onBatteryChangeWhenInPhoneCall_receiveNoStateChange() throws Exception { 194 mHandler.onPhoneCallStateChanged(true); 195 waitAndAssertState(DISABLE); 196 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_ENABLED); 197 waitToConfirmNoEventReceived(); 198 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); 199 waitToConfirmNoEventReceived(); 200 } 201 202 @Test whenBatteryCriticalChangeDuringCallAfterPhoneCall_receiveCriticalStateChange()203 public void whenBatteryCriticalChangeDuringCallAfterPhoneCall_receiveCriticalStateChange() 204 throws Exception { 205 mHandler.onPhoneCallStateChanged(true); 206 waitAndAssertState(DISABLE); 207 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); 208 waitToConfirmNoEventReceived(); 209 mHandler.onPhoneCallStateChanged(false); 210 waitAndAssertState(CRITICAL); 211 } 212 213 @Test whenBatteryDisableDuringCallAfterPhoneCallBatteryEnable_receiveStateChange()214 public void whenBatteryDisableDuringCallAfterPhoneCallBatteryEnable_receiveStateChange() 215 throws Exception { 216 mHandler.onPhoneCallStateChanged(true); 217 waitAndAssertState(DISABLE); 218 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); 219 waitToConfirmNoEventReceived(); 220 mHandler.onPhoneCallStateChanged(false); 221 waitToConfirmNoEventReceived(); 222 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); 223 waitAndAssertState(CRITICAL); 224 } 225 226 @Test whenPhoneCallDuringBatteryDisable_receiveNoStateChange()227 public void whenPhoneCallDuringBatteryDisable_receiveNoStateChange() throws Exception { 228 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_ALL_DISABLED); 229 waitAndAssertState(DISABLE); 230 mHandler.onPhoneCallStateChanged(true); 231 waitToConfirmNoEventReceived(); 232 mHandler.onPhoneCallStateChanged(false); 233 waitToConfirmNoEventReceived(); 234 } 235 236 @Test whenPhoneCallDuringBatteryCritical_receiveStateChange()237 public void whenPhoneCallDuringBatteryCritical_receiveStateChange() throws Exception { 238 mHandler.onPowerModeChanged(SOUND_TRIGGER_MODE_CRITICAL_ONLY); 239 waitAndAssertState(CRITICAL); 240 mHandler.onPhoneCallStateChanged(true); 241 waitAndAssertState(DISABLE); 242 mHandler.onPhoneCallStateChanged(false); 243 waitAndAssertState(CRITICAL); 244 } 245 246 // This test could be flaky, but we want to verify that we only delay notification if 247 // we are exiting a call, NOT if we are entering a call. 248 @FlakyTest 249 @Test whenPhoneCallReceived_receiveStateChangeFast()250 public void whenPhoneCallReceived_receiveStateChangeFast() throws Exception { 251 mHandler.onPhoneCallStateChanged(true); 252 CountDownLatch latch; 253 synchronized (mLock) { 254 latch = mLatch; 255 } 256 assertThat(latch.await(PHONE_DELAY_BRIEF_WAIT_MS, TimeUnit.MILLISECONDS)).isTrue(); 257 synchronized (mLock) { 258 assertThat(mState).isEqualTo(DISABLE); 259 } 260 } 261 } 262