1 /* 2 * Copyright (C) 2022 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.telecom.tests; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.mockito.ArgumentMatchers.anyInt; 21 import static org.mockito.Mockito.eq; 22 import static org.mockito.Mockito.never; 23 import static org.mockito.Mockito.times; 24 import static org.mockito.Mockito.verify; 25 import static org.mockito.Mockito.when; 26 27 import android.content.Intent; 28 import android.media.session.MediaSession; 29 import android.view.KeyEvent; 30 31 import androidx.test.filters.MediumTest; 32 import androidx.test.filters.SmallTest; 33 34 import com.android.server.telecom.Call; 35 import com.android.server.telecom.CallsManager; 36 import com.android.server.telecom.HeadsetMediaButton; 37 import com.android.server.telecom.HeadsetMediaButton.MediaSessionWrapper; 38 import com.android.server.telecom.TelecomSystem; 39 40 import org.junit.After; 41 import org.junit.Before; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 import org.junit.runners.JUnit4; 45 import org.mockito.ArgumentCaptor; 46 import org.mockito.Mock; 47 import org.mockito.Mockito; 48 49 @RunWith(JUnit4.class) 50 public class HeadsetMediaButtonTest extends TelecomTestCase { 51 private static final int TEST_TIMEOUT_MILLIS = 1000; 52 53 private HeadsetMediaButton mHeadsetMediaButton; 54 private MediaSession.Callback mSessionCallback; 55 56 @Mock private CallsManager mMockCallsManager; 57 @Mock private HeadsetMediaButton.MediaSessionAdapter mMediaSessionAdapter; 58 private TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {}; 59 60 @Override 61 @Before setUp()62 public void setUp() throws Exception { 63 super.setUp(); 64 65 ArgumentCaptor<MediaSession.Callback> sessionCallbackArgument = 66 ArgumentCaptor.forClass(MediaSession.Callback.class); 67 68 mHeadsetMediaButton = new HeadsetMediaButton(mContext, mMockCallsManager, mLock, 69 mMediaSessionAdapter); 70 71 verify(mMediaSessionAdapter).setCallback(sessionCallbackArgument.capture()); 72 mSessionCallback = sessionCallbackArgument.getValue(); 73 } 74 75 @Override 76 @After tearDown()77 public void tearDown() throws Exception { 78 mHeadsetMediaButton = null; 79 super.tearDown(); 80 } 81 82 /** 83 * Nominal case; just add a call and remove it. 84 */ 85 @SmallTest 86 @Test testAddCall()87 public void testAddCall() { 88 Call regularCall = getRegularCall(); 89 90 when(mMockCallsManager.hasAnyCalls()).thenReturn(true); 91 mHeadsetMediaButton.onCallAdded(regularCall); 92 waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS); 93 verify(mMediaSessionAdapter).setActive(eq(true)); 94 // ... and thus we see how the original code isn't amenable to tests. 95 when(mMediaSessionAdapter.isActive()).thenReturn(true); 96 97 when(mMockCallsManager.hasAnyCalls()).thenReturn(false); 98 mHeadsetMediaButton.onCallRemoved(regularCall); 99 waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS); 100 verify(mMediaSessionAdapter).setActive(eq(false)); 101 } 102 103 /** 104 * Test a case where a regular call becomes an external call, and back again. 105 */ 106 @SmallTest 107 @Test testRegularCallThatBecomesExternal()108 public void testRegularCallThatBecomesExternal() { 109 Call regularCall = getRegularCall(); 110 111 // Start with a regular old call. 112 when(mMockCallsManager.hasAnyCalls()).thenReturn(true); 113 mHeadsetMediaButton.onCallAdded(regularCall); 114 waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS); 115 verify(mMediaSessionAdapter).setActive(eq(true)); 116 when(mMediaSessionAdapter.isActive()).thenReturn(true); 117 118 // Change so it is external. 119 when(regularCall.isExternalCall()).thenReturn(true); 120 when(mMockCallsManager.hasAnyCalls()).thenReturn(false); 121 mHeadsetMediaButton.onExternalCallChanged(regularCall, true); 122 // Expect to set session inactive. 123 waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS); 124 verify(mMediaSessionAdapter).setActive(eq(false)); 125 126 // For good measure lets make it non-external again. 127 when(regularCall.isExternalCall()).thenReturn(false); 128 when(mMockCallsManager.hasAnyCalls()).thenReturn(true); 129 mHeadsetMediaButton.onExternalCallChanged(regularCall, false); 130 // Expect to set session active. 131 waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS); 132 verify(mMediaSessionAdapter).setActive(eq(true)); 133 } 134 135 @MediumTest 136 @Test testExternalCallNotChangesState()137 public void testExternalCallNotChangesState() { 138 Call externalCall = getRegularCall(); 139 when(externalCall.isExternalCall()).thenReturn(true); 140 141 mHeadsetMediaButton.onCallAdded(externalCall); 142 waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS); 143 verify(mMediaSessionAdapter, never()).setActive(eq(true)); 144 145 mHeadsetMediaButton.onCallRemoved(externalCall); 146 waitForHandlerAction(mHeadsetMediaButton.getHandler(), TEST_TIMEOUT_MILLIS); 147 verify(mMediaSessionAdapter, never()).setActive(eq(false)); 148 } 149 150 @SmallTest 151 @Test testCallbackReceivesKeyEventUnaware()152 public void testCallbackReceivesKeyEventUnaware() { 153 mSessionCallback.onMediaButtonEvent(getKeyEventIntent( 154 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0, false)); 155 verify(mMockCallsManager, never()).onMediaButton(anyInt()); 156 } 157 158 @SmallTest 159 @Test testCallbackReceivesKeyEventShortClick()160 public void testCallbackReceivesKeyEventShortClick() { 161 mSessionCallback.onMediaButtonEvent(getKeyEventIntent( 162 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK, false)); 163 verify(mMockCallsManager, never()).onMediaButton(anyInt()); 164 165 mSessionCallback.onMediaButtonEvent(getKeyEventIntent( 166 KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK, false)); 167 verify(mMockCallsManager, times(1)).onMediaButton(HeadsetMediaButton.SHORT_PRESS); 168 } 169 170 @SmallTest 171 @Test testCallbackReceivesKeyEventLongClick()172 public void testCallbackReceivesKeyEventLongClick() { 173 mSessionCallback.onMediaButtonEvent(getKeyEventIntent( 174 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK, true)); 175 verify(mMockCallsManager, times(1)).onMediaButton(HeadsetMediaButton.LONG_PRESS); 176 177 mSessionCallback.onMediaButtonEvent(getKeyEventIntent( 178 KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK, false)); 179 verify(mMockCallsManager, times(1)).onMediaButton(HeadsetMediaButton.LONG_PRESS); 180 } 181 182 @SmallTest 183 @Test testMediaSessionWrapperSetActive()184 public void testMediaSessionWrapperSetActive() { 185 MediaSession session = Mockito.mock(MediaSession.class); 186 MediaSessionWrapper wrapper = mHeadsetMediaButton.new MediaSessionWrapper(session); 187 188 final boolean active = true; 189 wrapper.setActive(active); 190 verify(session).setActive(active); 191 } 192 193 @SmallTest 194 @Test testMediaSessionWrapperSetCallback()195 public void testMediaSessionWrapperSetCallback() { 196 MediaSession session = Mockito.mock(MediaSession.class); 197 MediaSessionWrapper wrapper = mHeadsetMediaButton.new MediaSessionWrapper(session); 198 199 wrapper.setCallback(mSessionCallback); 200 verify(session).setCallback(mSessionCallback); 201 } 202 203 @SmallTest 204 @Test testMediaSessionWrapperIsActive()205 public void testMediaSessionWrapperIsActive() { 206 MediaSession session = Mockito.mock(MediaSession.class); 207 MediaSessionWrapper wrapper = mHeadsetMediaButton.new MediaSessionWrapper(session); 208 209 final boolean active = true; 210 when(session.isActive()).thenReturn(active); 211 assertEquals(active, wrapper.isActive()); 212 } 213 214 /** 215 * @return a mock call instance of a regular non-external call. 216 */ getRegularCall()217 private Call getRegularCall() { 218 Call regularCall = Mockito.mock(Call.class); 219 when(regularCall.isExternalCall()).thenReturn(false); 220 return regularCall; 221 } 222 getKeyEventIntent(int action, int code, boolean longPress)223 private Intent getKeyEventIntent(int action, int code, boolean longPress) { 224 KeyEvent e = new KeyEvent(action, code); 225 if (longPress) { 226 e = KeyEvent.changeFlags(e, KeyEvent.FLAG_LONG_PRESS); 227 } 228 229 Intent intent = new Intent(); 230 intent.putExtra(Intent.EXTRA_KEY_EVENT, e); 231 return intent; 232 } 233 } 234