blob: 9432caefc8c413c5ab901eeef78c3aaedc95aa9e [file] [log] [blame]
/*
* Copyright (C) 2020 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.am;
import static android.car.test.mocks.AndroidMockitoHelper.mockAmGetCurrentUser;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
import android.car.hardware.power.CarPowerManager;
import android.car.test.mocks.AbstractExtendedMockitoTestCase;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.view.Display;
import com.android.car.CarLocalServices;
import com.android.car.CarServiceUtils;
import com.android.car.user.CarUserService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.stubbing.OngoingStubbing;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public final class FixedActivityServiceTest extends AbstractExtendedMockitoTestCase {
private static final long RECHECK_INTERVAL_MARGIN_MS = 600;
private final int mValidDisplayId = 1;
@Mock
private Context mContext;
@Mock
private IActivityManager mActivityManager;
@Mock
private IActivityTaskManager mActivityTaskManager;
@Mock
private UserManager mUserManager;
@Mock
private DisplayManager mDisplayManager;
@Mock
private PackageManager mPackageManager;
@Mock
private CarUserService mCarUserService;
@Mock
private CarPowerManager mCarPowerManager;
private FixedActivityService mFixedActivityService;
@Override
protected void onSessionBuilder(CustomMockitoSessionBuilder builder) {
builder
.spyStatic(ActivityManager.class)
.spyStatic(CarLocalServices.class);
}
@Before
public void setUp() {
when(mContext.getPackageManager()).thenReturn(mPackageManager);
doReturn(mCarUserService).when(() -> CarLocalServices.getService(CarUserService.class));
doReturn(mCarPowerManager).when(() -> CarLocalServices.createCarPowerManager(mContext));
mFixedActivityService = new FixedActivityService(mContext, mActivityManager,
mActivityTaskManager, mUserManager, mDisplayManager);
}
@After
public void tearDown() {
if (mFixedActivityService != null) {
mFixedActivityService.release();
}
CarServiceUtils.finishAllHandlerTasks();
}
@Test
public void testStartFixedActivityModeForDisplayAndUser_noRunningActivity()
throws Exception {
int userId = 10;
ActivityOptions options = new ActivityOptions(new Bundle());
Intent intent = expectComponentAvailable("test_package", "com.test.dude", userId);
mockAmGetCurrentUser(userId);
expectNoActivityStack();
// No running activities
boolean ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent,
options, mValidDisplayId, userId);
verify(mContext).startActivityAsUser(eq(intent), any(Bundle.class),
eq(UserHandle.of(userId)));
assertThat(ret).isTrue();
}
@Test
public void testStartFixedActivityModeForDisplayAndUser_alreadyRunningActivity()
throws Exception {
int userId = 10;
int[] userIds = new int[] { userId };
int[] taskIds = new int[] { 1234 };
ActivityOptions options = new ActivityOptions(new Bundle());
Intent intent = expectComponentAvailable("test_package", "com.test.dude", userId);
mockAmGetCurrentUser(userId);
expectActivityStackInfo(
createEmptyStackInfo(),
createStackInfoList(intent, userIds, mValidDisplayId, taskIds)
);
// No running activities
boolean ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent,
options, mValidDisplayId, userId);
verify(mContext).startActivityAsUser(eq(intent), any(Bundle.class),
eq(UserHandle.of(userId)));
assertThat(ret).isTrue();
ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent,
options, mValidDisplayId, userId);
// startActivityAsUser should not called at this time. So, total called count is 1.
verify(mContext).startActivityAsUser(eq(intent), any(Bundle.class),
eq(UserHandle.of(userId)));
assertThat(ret).isTrue();
}
@Test
public void testStartFixedActivityModeForDisplayAndUser_runNewActivity() throws Exception {
int userId = 10;
int[] userIds = new int[] { userId };
int[] taskIds = new int[] { 1234 };
ActivityOptions options = new ActivityOptions(new Bundle());
Intent intent = expectComponentAvailable("test_package", "com.test.dude", userId);
Intent anotherIntent = expectComponentAvailable("test_package_II", "com.test.dude_II",
userId);
mockAmGetCurrentUser(userId);
expectActivityStackInfo(
createEmptyStackInfo(),
createStackInfoList(intent, userIds, mValidDisplayId, taskIds)
);
// No running activities
boolean ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent,
options, mValidDisplayId, userId);
assertThat(ret).isTrue();
// Start activity with new package
ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(anotherIntent,
options, mValidDisplayId, userId);
verify(mContext).startActivityAsUser(eq(anotherIntent), any(Bundle.class),
eq(UserHandle.of(userId)));
assertThat(ret).isTrue();
}
@Test
public void testStartFixedActivityModeForDisplay_relaunchWithPackageUpdated() throws Exception {
int userId = 10;
int[] userIds = new int[] { userId };
int[] taskIds = new int[] { 1234 };
String packageName = "test_package";
String className = "com.test.dude";
ActivityOptions options = new ActivityOptions(new Bundle());
ArgumentCaptor<BroadcastReceiver> receiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
Intent intent = expectComponentAvailable(packageName, className, userId);
mockAmGetCurrentUser(userId);
expectActivityStackInfo(
createEmptyStackInfo(),
createStackInfoList(intent, userIds, mValidDisplayId, taskIds)
);
// No running activities
boolean ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent,
options, mValidDisplayId, userId);
verify(mContext).registerReceiverAsUser(receiverCaptor.capture(), eq(UserHandle.ALL),
any(IntentFilter.class), eq(null), eq(null));
verify(mContext).startActivityAsUser(eq(intent), any(Bundle.class),
eq(UserHandle.of(userId)));
assertThat(ret).isTrue();
// Update package
SystemClock.sleep(RECHECK_INTERVAL_MARGIN_MS);
int appId = 987;
BroadcastReceiver receiver = receiverCaptor.getValue();
Intent packageIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
packageIntent.setData(new Uri.Builder().path(packageName).build());
packageIntent.putExtra(Intent.EXTRA_UID, UserHandle.getUid(userId, appId));
receiver.onReceive(mContext, packageIntent);
verify(mContext).startActivityAsUser(eq(intent), any(Bundle.class),
eq(UserHandle.of(userId)));
SystemClock.sleep(RECHECK_INTERVAL_MARGIN_MS);
ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent,
options, mValidDisplayId, userId);
// Activity should not be launched.
verify(mContext).startActivityAsUser(eq(intent), any(Bundle.class),
eq(UserHandle.of(userId)));
assertThat(ret).isTrue();
}
@Test
public void testStartFixedActivityModeForDisplayAndUser_runOnDifferentDisplay()
throws Exception {
int userId = 10;
ActivityOptions options = new ActivityOptions(new Bundle());
Intent intent = expectComponentAvailable("test_package", "com.test.dude", userId);
Intent anotherIntent = expectComponentAvailable("test_package_II", "com.test.dude_II",
userId);
mockAmGetCurrentUser(userId);
expectNoActivityStack();
// No running activities
boolean ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent,
options, mValidDisplayId, userId);
assertThat(ret).isTrue();
int anotherValidDisplayId = mValidDisplayId + 1;
ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(anotherIntent,
options, anotherValidDisplayId, userId);
verify(mContext).startActivityAsUser(eq(anotherIntent), any(Bundle.class),
eq(UserHandle.of(userId)));
assertThat(ret).isTrue();
}
@Test
public void testStartFixedActivityModeForDisplayAndUser_invalidDisplay() {
int userId = 10;
Intent intent = new Intent(Intent.ACTION_MAIN);
ActivityOptions options = new ActivityOptions(new Bundle());
int invalidDisplayId = Display.DEFAULT_DISPLAY;
boolean ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent, options,
invalidDisplayId, userId);
assertThat(ret).isFalse();
}
@Test
public void testStartFixedActivityModeForDisplayAndUser_notAllowedUser() {
int currentUserId = 10;
int notAllowedUserId = 11;
Intent intent = new Intent(Intent.ACTION_MAIN);
ActivityOptions options = new ActivityOptions(new Bundle());
int displayId = mValidDisplayId;
mockAmGetCurrentUser(currentUserId);
expectNoProfileUser(currentUserId);
boolean ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent, options,
displayId, notAllowedUserId);
assertThat(ret).isFalse();
}
@Test
public void testStartFixedActivityModeForDisplayAndUser_invalidComponent() throws Exception {
int userId = 10;
ActivityOptions options = new ActivityOptions(new Bundle());
Intent invalidIntent = expectComponentUnavailable("test_package", "com.test.dude", userId);
mockAmGetCurrentUser(userId);
boolean ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(invalidIntent,
options, mValidDisplayId, userId);
assertThat(ret).isFalse();
}
@Test
public void testStopFixedActivityMode() throws Exception {
int userId = 10;
ActivityOptions options = new ActivityOptions(new Bundle());
Intent intent = expectComponentAvailable("test_package", "com.test.dude", userId);
mockAmGetCurrentUser(userId);
expectNoActivityStack();
// Start an activity
boolean ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent,
options, mValidDisplayId, userId);
// To check if monitoring is started.
verify(mActivityManager).registerTaskStackListener(any(TaskStackListener.class));
assertThat(ret).isTrue();
mFixedActivityService.stopFixedActivityMode(mValidDisplayId);
verify(mActivityManager).unregisterTaskStackListener(any(TaskStackListener.class));
}
@Test
public void testStopFixedActivityMode_invalidDisplayId() throws Exception {
mFixedActivityService.stopFixedActivityMode(Display.DEFAULT_DISPLAY);
verify(mActivityManager, never()).unregisterTaskStackListener(any(TaskStackListener.class));
}
@Test
public void testHandleTaskFocusChanged_returnForcusBack() throws Exception {
int userId = 10;
int testTaskId = 1234;
int[] userIds = new int[]{userId};
int[] taskIds = new int[]{testTaskId};
ActivityOptions options = new ActivityOptions(new Bundle());
Intent intent = expectComponentAvailable("test_package", "com.test.dude", userId);
mockAmGetCurrentUser(userId);
expectActivityStackInfo(createStackInfoList(intent, userIds, mValidDisplayId, taskIds));
// Make FixedActivityService to update the taskIds.
boolean ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent,
options, mValidDisplayId, userId);
assertThat(ret).isTrue();
Intent homeIntent = expectComponentAvailable("home", "homeActivity", userId);
int homeTaskId = 4567;
when(mActivityTaskManager.getAllStackInfosOnDisplay(Display.DEFAULT_DISPLAY)).thenReturn(
createStackInfoList(
homeIntent, userIds, Display.DEFAULT_DISPLAY, new int[]{homeTaskId}));
mFixedActivityService.handleTaskFocusChanged(testTaskId, true);
verify(mActivityTaskManager).setFocusedTask(homeTaskId);
}
// If there are two tasks in one display id, then there is a bug to invalidate
// RunningActivityInfo.taskId examining 2nd task and it prevents from checking FixedActivity
// in handleTaskFocusChanged(). This test makes sure if the bug doesn't exist.
@Test
public void testHandleTaskFocusChanged_returnForcusBackInTwoStackInfos() throws Exception {
int userId = 10;
int testTaskId = 1234;
int[] userIds = new int[]{userId};
int[] taskIds = new int[]{testTaskId};
int[] taskIds2 = new int[]{testTaskId + 1111};
ActivityOptions options = new ActivityOptions(new Bundle());
Intent intent = expectComponentAvailable("test_package", "com.test.dude", userId);
Intent intent2 = expectComponentAvailable("test_package2", "com.test.others", userId);
mockAmGetCurrentUser(userId);
expectActivityStackInfo(Arrays.asList(
createStackInfo(intent, userIds, mValidDisplayId, taskIds),
createStackInfo(intent2, userIds, mValidDisplayId, taskIds2)));
// Make FixedActivityService to update the taskIds.
boolean ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent,
options, mValidDisplayId, userId);
assertThat(ret).isTrue();
Intent homeIntent = expectComponentAvailable("home", "homeActivity", userId);
int homeTaskId = 4567;
when(mActivityTaskManager.getAllStackInfosOnDisplay(Display.DEFAULT_DISPLAY)).thenReturn(
createStackInfoList(
homeIntent, userIds, Display.DEFAULT_DISPLAY, new int[]{homeTaskId}));
mFixedActivityService.handleTaskFocusChanged(testTaskId, true);
verify(mActivityTaskManager).setFocusedTask(homeTaskId);
}
@Test
public void testHandleTaskFocusChanged_noForcusBack() throws Exception {
int userId = 10;
int testTaskId = 1234;
int[] userIds = new int[]{userId};
int[] taskIds = new int[]{testTaskId};
ActivityOptions options = new ActivityOptions(new Bundle());
Intent intent = expectComponentAvailable("test_package", "com.test.dude", userId);
mockAmGetCurrentUser(userId);
expectActivityStackInfo(createStackInfoList(intent, userIds, mValidDisplayId, taskIds));
// Make FixedActivityService to update the taskIds.
boolean ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(intent,
options, mValidDisplayId, userId);
assertThat(ret).isTrue();
mFixedActivityService.handleTaskFocusChanged(testTaskId + 1, true);
verify(mActivityTaskManager, never()).setFocusedTask(anyInt());
}
private void expectNoProfileUser(@UserIdInt int userId) {
when(mUserManager.getEnabledProfileIds(userId)).thenReturn(new int[0]);
}
private Intent expectComponentUnavailable(String pkgName, String className,
@UserIdInt int userId) throws Exception {
Intent intent = new Intent(Intent.ACTION_MAIN);
ComponentName component = new ComponentName(pkgName, className);
intent.setComponent(component);
ActivityInfo activityInfo = new ActivityInfo();
// To make sure there is no matched activity
activityInfo.name = component.getClassName() + ".unavailable";
PackageInfo packageInfo = new PackageInfo();
packageInfo.activities = new ActivityInfo[] { activityInfo };
when(mPackageManager.getPackageInfoAsUser(component.getPackageName(),
PackageManager.GET_ACTIVITIES, userId)).thenReturn(packageInfo);
return intent;
}
private Intent expectComponentAvailable(String pkgName, String className, @UserIdInt int userId)
throws Exception {
Intent intent = new Intent(Intent.ACTION_MAIN);
ComponentName component = new ComponentName(pkgName, className);
intent.setComponent(component);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.name = component.getClassName();
PackageInfo packageInfo = new PackageInfo();
packageInfo.activities = new ActivityInfo[] { activityInfo };
when(mPackageManager.getPackageInfoAsUser(component.getPackageName(),
PackageManager.GET_ACTIVITIES, userId)).thenReturn(packageInfo);
return intent;
}
private void expectNoActivityStack() throws Exception {
when(mActivityManager.getAllStackInfos()).thenReturn(createEmptyStackInfo());
}
private void expectActivityStackInfo(List<StackInfo> ...stackInfos) throws Exception {
OngoingStubbing<List<StackInfo>> stub = when(mActivityManager.getAllStackInfos());
for (List<StackInfo> stackInfo : stackInfos) {
stub = stub.thenReturn(stackInfo);
}
}
private List<StackInfo> createEmptyStackInfo() {
return new ArrayList<StackInfo>();
}
private StackInfo createStackInfo(Intent intent, @UserIdInt int[] userIds, int displayId,
int[] taskIds) {
StackInfo stackInfo = new StackInfo();
stackInfo.taskUserIds = userIds;
stackInfo.topActivity = intent.getComponent().clone();
stackInfo.visible = true;
stackInfo.displayId = displayId;
stackInfo.taskIds = taskIds;
return stackInfo;
}
private List<StackInfo> createStackInfoList(Intent intent, @UserIdInt int[] userIds,
int displayId, int[] taskIds) {
return Arrays.asList(createStackInfo(intent, userIds, displayId, taskIds));
}
}