blob: a1ff59f779844600fc82a78013408880c7e4d062 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.vms;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.car.userlib.CarUserManagerHelper;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Handler;
import android.os.UserHandle;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VmsClientManagerTest {
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock
private Context mContext;
@Mock
private PackageManager mPackageManager;
@Mock
private Resources mResources;
@Mock
private CarUserManagerHelper mUserManager;
private UserInfo mUserInfo;
@Mock
private VmsClientManager.ConnectionListener mConnectionListener;
private VmsClientManager mClientManager;
@Captor
private ArgumentCaptor<ServiceConnection> mConnectionCaptor;
@Before
public void setUp() {
resetContext();
when(mPackageManager.isPackageAvailable(any())).thenReturn(true);
when(mResources.getInteger(
com.android.car.R.integer.millisecondsBeforeRebindToVmsPublisher)).thenReturn(
5);
when(mResources.getStringArray(
com.android.car.R.array.vmsPublisherSystemClients)).thenReturn(
new String[]{
"com.google.android.apps.vms.test/.VmsSystemClient"
});
when(mResources.getStringArray(
com.android.car.R.array.vmsPublisherUserClients)).thenReturn(
new String[]{
"com.google.android.apps.vms.test/.VmsUserClient"
});
mUserInfo = new UserInfo(10, "Driver", 0);
when(mUserManager.getCurrentForegroundUserInfo()).thenReturn(mUserInfo);
mClientManager = new VmsClientManager(mContext, mUserManager);
mClientManager.registerConnectionListener(mConnectionListener);
}
@After
public void tearDown() throws Exception {
Thread.sleep(10); // Time to allow for delayed rebinds to settle
verify(mContext, atLeast(0)).getResources();
verify(mContext, atLeast(0)).getPackageManager();
verifyNoMoreInteractions(mContext);
}
@Test
public void testInit() {
mClientManager.init();
// Verify registration of boot completed receiver
ArgumentCaptor<IntentFilter> bootFilterCaptor = ArgumentCaptor.forClass(IntentFilter.class);
verify(mContext).registerReceiver(eq(mClientManager.mBootCompletedReceiver),
bootFilterCaptor.capture());
IntentFilter bootFilter = bootFilterCaptor.getValue();
assertEquals(1, bootFilter.countActions());
assertTrue(bootFilter.hasAction(Intent.ACTION_LOCKED_BOOT_COMPLETED));
// Verify registration of user switch receiver
ArgumentCaptor<IntentFilter> userFilterCaptor = ArgumentCaptor.forClass(IntentFilter.class);
verify(mContext).registerReceiverAsUser(eq(mClientManager.mUserSwitchReceiver),
eq(UserHandle.ALL), userFilterCaptor.capture(), isNull(), isNull());
IntentFilter userEventFilter = userFilterCaptor.getValue();
assertEquals(2, userEventFilter.countActions());
assertTrue(userEventFilter.hasAction(Intent.ACTION_USER_SWITCHED));
assertTrue(userEventFilter.hasAction(Intent.ACTION_USER_UNLOCKED));
}
@Test
public void testRelease() {
mClientManager.release();
// Verify both receivers are unregistered
verify(mContext).unregisterReceiver(mClientManager.mBootCompletedReceiver);
verify(mContext).unregisterReceiver(mClientManager.mUserSwitchReceiver);
}
@Test
public void testLockedBootCompleted() {
notifyLockedBootCompleted();
notifyLockedBootCompleted();
// Multiple events should only trigger a single bind, when successful
verifySystemBind(1);
}
@Test
public void testLockedBootCompleted_BindFailed() {
when(mContext.bindServiceAsUser(any(), any(), anyInt(), any(), any())).thenReturn(false);
notifyLockedBootCompleted();
notifyLockedBootCompleted();
// Failure state will trigger another attempt on event
verifySystemBind(2);
}
@Test
public void testLockedBootCompleted_BindException() {
when(mContext.bindServiceAsUser(any(), any(), anyInt(), any(), any())).thenThrow(
new SecurityException());
notifyLockedBootCompleted();
notifyLockedBootCompleted();
// Failure state will trigger another attempt on event
verifySystemBind(2);
}
@Test
public void testUserSwitched() {
notifyUserSwitched();
notifyUserSwitched();
// Multiple events should only trigger a single bind, when successful
verifyUserBind(1);
}
@Test
public void testUserSwitched_BindFailed() {
when(mContext.bindServiceAsUser(any(), any(), anyInt(), any(), any())).thenReturn(false);
notifyUserSwitched();
notifyUserSwitched();
// Failure state will trigger another attempt on event
verifyUserBind(2);
}
@Test
public void testUserSwitched_BindException() {
when(mContext.bindServiceAsUser(any(), any(), anyInt(), any(), any())).thenThrow(
new SecurityException());
notifyUserSwitched();
notifyUserSwitched();
// Failure state will trigger another attempt on event
verifyUserBind(2);
}
@Test
public void testUserUnlocked() {
notifyUserUnlocked();
notifyUserUnlocked();
// Multiple events should only trigger a single bind, when successful
verifyUserBind(1);
}
@Test
public void testUserUnlocked_BindFailed() {
when(mContext.bindServiceAsUser(any(), any(), anyInt(), any(), any())).thenReturn(false);
notifyUserUnlocked();
notifyUserUnlocked();
// Failure state will trigger another attempt on event
verifyUserBind(2);
}
@Test
public void testUserUnlocked_BindException() {
when(mContext.bindServiceAsUser(any(), any(), anyInt(), any(), any())).thenThrow(
new SecurityException());
notifyUserUnlocked();
notifyUserUnlocked();
// Failure state will trigger another attempt on event
verifyUserBind(2);
}
@Test
public void testUserSwitchedAndUnlocked() {
notifyUserSwitched();
notifyUserUnlocked();
// Multiple events should only trigger a single bind, when successful
verifyUserBind(1);
}
@Test
public void testUserSwitchedToSystemUser() {
mUserInfo = new UserInfo(UserHandle.USER_SYSTEM, "Owner", 0);
when(mUserManager.getCurrentForegroundUserInfo()).thenReturn(mUserInfo);
notifyUserSwitched();
// System user should not trigger any binding
verifyUserBind(0);
}
@Test
public void testUnregisterConnectionListener() {
mClientManager.unregisterConnectionListener(mConnectionListener);
notifyLockedBootCompleted();
verifySystemBind(1);
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, new Binder());
verifyZeroInteractions(mConnectionListener);
}
@Test
public void testOnSystemServiceConnected() {
notifyLockedBootCompleted();
verifySystemBind(1);
resetContext();
Binder binder = new Binder();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, binder);
verify(mConnectionListener).onClientConnected(
eq("com.google.android.apps.vms.test/com.google.android.apps.vms.test"
+ ".VmsSystemClient U=0"),
eq(binder));
}
@Test
public void testOnUserServiceConnected() {
notifyUserSwitched();
verifyUserBind(1);
resetContext();
Binder binder = new Binder();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, binder);
verify(mConnectionListener).onClientConnected(
eq("com.google.android.apps.vms.test/com.google.android.apps.vms.test"
+ ".VmsUserClient U=10"),
eq(binder));
}
@Test
public void testOnSystemServiceDisconnected() throws Exception {
notifyLockedBootCompleted();
verifySystemBind(1);
resetContext();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, new Binder());
connection.onServiceDisconnected(null);
verify(mContext).unbindService(connection);
verify(mConnectionListener).onClientDisconnected(
eq("com.google.android.apps.vms.test/com.google.android.apps.vms.test"
+ ".VmsSystemClient U=0"));
Thread.sleep(10);
verifySystemBind(1);
}
@Test
public void testOnSystemServiceDisconnected_ServiceNotConnected() throws Exception {
notifyLockedBootCompleted();
verifySystemBind(1);
resetContext();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceDisconnected(null);
verify(mContext).unbindService(connection);
verifyZeroInteractions(mConnectionListener);
Thread.sleep(10);
verifySystemBind(1);
}
@Test
public void testOnUserServiceDisconnected() throws Exception {
notifyUserSwitched();
verifyUserBind(1);
resetContext();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, new Binder());
connection.onServiceDisconnected(null);
verify(mContext).unbindService(connection);
verify(mConnectionListener).onClientDisconnected(
eq("com.google.android.apps.vms.test/com.google.android.apps.vms.test"
+ ".VmsUserClient U=10"));
Thread.sleep(10);
verifyUserBind(1);
}
@Test
public void testOnUserServiceDisconnected_ServiceNotConnected() throws Exception {
notifyUserSwitched();
verifyUserBind(1);
resetContext();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceDisconnected(null);
verify(mContext).unbindService(connection);
verifyZeroInteractions(mConnectionListener);
Thread.sleep(10);
verifyUserBind(1);
}
@Test
public void testOnSystemServiceBindingDied() throws Exception {
notifyLockedBootCompleted();
verifySystemBind(1);
resetContext();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, new Binder());
connection.onBindingDied(null);
verify(mContext).unbindService(connection);
verify(mConnectionListener).onClientDisconnected(
eq("com.google.android.apps.vms.test/com.google.android.apps.vms.test"
+ ".VmsSystemClient U=0"));
Thread.sleep(10);
verifySystemBind(1);
}
@Test
public void testOnSystemServiceBindingDied_ServiceNotConnected() throws Exception {
notifyLockedBootCompleted();
verifySystemBind(1);
resetContext();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onBindingDied(null);
verify(mContext).unbindService(connection);
verifyZeroInteractions(mConnectionListener);
Thread.sleep(10);
verifySystemBind(1);
}
@Test
public void testOnUserServiceBindingDied() throws Exception {
notifyUserSwitched();
verifyUserBind(1);
resetContext();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, new Binder());
connection.onBindingDied(null);
verify(mContext).unbindService(connection);
verify(mConnectionListener).onClientDisconnected(
eq("com.google.android.apps.vms.test/com.google.android.apps.vms.test"
+ ".VmsUserClient U=10"));
Thread.sleep(10);
verifyUserBind(1);
}
@Test
public void testOnUserServiceBindingDied_ServiceNotConnected() throws Exception {
notifyUserSwitched();
verifyUserBind(1);
resetContext();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onBindingDied(null);
verify(mContext).unbindService(connection);
verifyZeroInteractions(mConnectionListener);
Thread.sleep(10);
verifyUserBind(1);
}
@Test
public void testOnSystemServiceNullBinding() throws Exception {
notifyLockedBootCompleted();
verifySystemBind(1);
resetContext();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, new Binder());
connection.onNullBinding(null);
verify(mContext).unbindService(connection);
verify(mConnectionListener).onClientDisconnected(
eq("com.google.android.apps.vms.test/com.google.android.apps.vms.test"
+ ".VmsSystemClient U=0"));
}
@Test
public void testOnSystemServiceNullBinding_ServiceNotConnected() throws Exception {
notifyLockedBootCompleted();
verifySystemBind(1);
resetContext();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onNullBinding(null);
verify(mContext).unbindService(connection);
verifyZeroInteractions(mConnectionListener);
}
@Test
public void testOnUserServiceNullBinding() throws Exception {
notifyUserSwitched();
verifyUserBind(1);
resetContext();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, new Binder());
connection.onNullBinding(null);
verify(mContext).unbindService(connection);
verify(mConnectionListener).onClientDisconnected(
eq("com.google.android.apps.vms.test/com.google.android.apps.vms.test"
+ ".VmsUserClient U=10"));
}
@Test
public void testOnUserServiceNullBinding_ServiceNotConnected() throws Exception {
notifyUserSwitched();
verifyUserBind(1);
resetContext();
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onNullBinding(null);
verify(mContext).unbindService(connection);
verifyZeroInteractions(mConnectionListener);
}
@Test
public void testOnUserSwitched_UserChange() {
notifyUserSwitched();
verifyUserBind(1);
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, new Binder());
resetContext();
reset(mConnectionListener);
mUserInfo = new UserInfo(11, "Driver", 0);
when(mUserManager.getCurrentForegroundUserInfo()).thenReturn(mUserInfo);
notifyUserSwitched();
verify(mContext).unbindService(connection);
verify(mConnectionListener).onClientDisconnected(
eq("com.google.android.apps.vms.test/com.google.android.apps.vms.test"
+ ".VmsUserClient U=10"));
verifyBind(1, "com.google.android.apps.vms.test/.VmsUserClient",
mUserInfo.getUserHandle());
}
@Test
public void testOnUserSwitched_UserChange_ToSystemUser() {
notifyUserSwitched();
verifyUserBind(1);
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, new Binder());
resetContext();
reset(mConnectionListener);
mUserInfo = new UserInfo(UserHandle.USER_SYSTEM, "Owner", 0);
when(mUserManager.getCurrentForegroundUserInfo()).thenReturn(mUserInfo);
notifyUserSwitched();
verify(mContext).unbindService(connection);
verify(mConnectionListener).onClientDisconnected(
eq("com.google.android.apps.vms.test/com.google.android.apps.vms.test"
+ ".VmsUserClient U=10"));
// User processes will not be bound for system user
verifyBind(0, "com.google.android.apps.vms.test/.VmsUserClient",
mUserInfo.getUserHandle());
}
@Test
public void testOnUserSwitched_UserChange_ServiceNotConnected() {
notifyUserSwitched();
verifyUserBind(1);
ServiceConnection connection = mConnectionCaptor.getValue();
resetContext();
mUserInfo = new UserInfo(11, "Driver", 0);
when(mUserManager.getCurrentForegroundUserInfo()).thenReturn(mUserInfo);
notifyUserSwitched();
verify(mContext).unbindService(connection);
verifyBind(1, "com.google.android.apps.vms.test/.VmsUserClient",
mUserInfo.getUserHandle());
}
@Test
public void testOnUserUnlocked_UserChange() {
notifyUserUnlocked();
verifyUserBind(1);
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, new Binder());
resetContext();
reset(mConnectionListener);
mUserInfo = new UserInfo(11, "Driver", 0);
when(mUserManager.getCurrentForegroundUserInfo()).thenReturn(mUserInfo);
notifyUserUnlocked();
verify(mContext).unbindService(connection);
verify(mConnectionListener).onClientDisconnected(
eq("com.google.android.apps.vms.test/com.google.android.apps.vms.test"
+ ".VmsUserClient U=10"));
verifyBind(1, "com.google.android.apps.vms.test/.VmsUserClient",
mUserInfo.getUserHandle());
}
@Test
public void testOnUserLocked_UserChange_ToSystemUser() {
notifyUserUnlocked();
verifyUserBind(1);
ServiceConnection connection = mConnectionCaptor.getValue();
connection.onServiceConnected(null, new Binder());
resetContext();
reset(mConnectionListener);
mUserInfo = new UserInfo(UserHandle.USER_SYSTEM, "Owner", 0);
when(mUserManager.getCurrentForegroundUserInfo()).thenReturn(mUserInfo);
notifyUserUnlocked();
verify(mContext).unbindService(connection);
verify(mConnectionListener).onClientDisconnected(
eq("com.google.android.apps.vms.test/com.google.android.apps.vms.test"
+ ".VmsUserClient U=10"));
// User processes will not be bound for system user
verifyBind(0, "com.google.android.apps.vms.test/.VmsUserClient",
mUserInfo.getUserHandle());
}
@Test
public void testOnUserUnlocked_UserChange_ServiceNotConnected() {
notifyUserUnlocked();
verifyUserBind(1);
ServiceConnection connection = mConnectionCaptor.getValue();
resetContext();
mUserInfo = new UserInfo(11, "Driver", 0);
when(mUserManager.getCurrentForegroundUserInfo()).thenReturn(mUserInfo);
notifyUserUnlocked();
verify(mContext).unbindService(connection);
verifyBind(1, "com.google.android.apps.vms.test/.VmsUserClient",
mUserInfo.getUserHandle());
}
private void resetContext() {
reset(mContext);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.bindServiceAsUser(any(), any(), anyInt(), any(), any())).thenReturn(true);
when(mContext.getResources()).thenReturn(mResources);
}
private void notifyLockedBootCompleted() {
mClientManager.mBootCompletedReceiver.onReceive(mContext,
new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED));
}
private void notifyUserSwitched() {
mClientManager.mUserSwitchReceiver.onReceive(mContext,
new Intent(Intent.ACTION_USER_SWITCHED));
}
private void notifyUserUnlocked() {
mClientManager.mUserSwitchReceiver.onReceive(mContext,
new Intent(Intent.ACTION_USER_UNLOCKED));
}
private void verifySystemBind(int times) {
verifyBind(times, "com.google.android.apps.vms.test/.VmsSystemClient",
UserHandle.SYSTEM);
}
private void verifyUserBind(int times) {
verifyBind(times, "com.google.android.apps.vms.test/.VmsUserClient",
mUserInfo.getUserHandle());
}
private void verifyBind(int times, String componentName,
UserHandle user) {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(times)).bindServiceAsUser(
intentCaptor.capture(),
mConnectionCaptor.capture(),
eq(Context.BIND_AUTO_CREATE), any(Handler.class), eq(user));
if (times > 0) {
assertEquals(
ComponentName.unflattenFromString(componentName),
intentCaptor.getValue().getComponent());
}
}
}