blob: 0359096892bb9f759c68e9a4cd88054214cc2b07 [file] [log] [blame]
/*
* Copyright (C) 2016 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.server.am;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.app.IActivityManager;
import android.app.ITaskStackListener;
import android.app.Instrumentation.ActivityMonitor;
import android.app.TaskStackListener;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Resources.Theme;
import android.os.RemoteException;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.UiDevice;
import android.text.TextUtils;
import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class TaskStackChangedListenerTest {
private IActivityManager mService;
private ITaskStackListener mTaskStackListener;
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static boolean sTaskStackChangedCalled;
private static boolean sActivityBResumed;
@Before
public void setUp() throws Exception {
mService = ActivityManager.getService();
}
@After
public void tearDown() throws Exception {
mService.unregisterTaskStackListener(mTaskStackListener);
mTaskStackListener = null;
}
@Test
public void testTaskStackChanged_afterFinish() throws Exception {
registerTaskStackChangedListener(new TaskStackListener() {
@Override
public void onTaskStackChanged() throws RemoteException {
synchronized (sLock) {
sTaskStackChangedCalled = true;
}
}
});
Context ctx = InstrumentationRegistry.getContext();
ctx.startActivity(new Intent(ctx, ActivityA.class));
UiDevice.getInstance(getInstrumentation()).waitForIdle();
synchronized (sLock) {
Assert.assertTrue(sTaskStackChangedCalled);
}
Assert.assertTrue(sActivityBResumed);
}
@Test
public void testTaskDescriptionChanged() throws Exception {
final Object[] params = new Object[2];
final CountDownLatch latch = new CountDownLatch(1);
registerTaskStackChangedListener(new TaskStackListener() {
int taskId = -1;
@Override
public void onTaskCreated(int taskId, ComponentName componentName)
throws RemoteException {
this.taskId = taskId;
}
@Override
public void onTaskDescriptionChanged(int taskId, TaskDescription td)
throws RemoteException {
if (this.taskId == taskId && !TextUtils.isEmpty(td.getLabel())) {
params[0] = taskId;
params[1] = td;
latch.countDown();
}
}
});
final Activity activity = startTestActivity(ActivityTaskDescriptionChange.class);
waitForCallback(latch);
assertEquals(activity.getTaskId(), params[0]);
assertEquals("Test Label", ((TaskDescription) params[1]).getLabel());
}
@Test
public void testActivityRequestedOrientationChanged() throws Exception {
final int[] params = new int[2];
final CountDownLatch latch = new CountDownLatch(1);
registerTaskStackChangedListener(new TaskStackListener() {
@Override
public void onActivityRequestedOrientationChanged(int taskId,
int requestedOrientation) {
params[0] = taskId;
params[1] = requestedOrientation;
latch.countDown();
}
});
final Activity activity = startTestActivity(ActivityRequestedOrientationChange.class);
waitForCallback(latch);
assertEquals(activity.getTaskId(), params[0]);
assertEquals(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, params[1]);
}
@Test
/**
* Tests for onTaskCreated, onTaskMovedToFront, onTaskRemoved and onTaskRemovalStarted.
*/
public void testTaskChangeCallBacks() throws Exception {
final Object[] params = new Object[2];
final CountDownLatch taskCreatedLaunchLatch = new CountDownLatch(1);
final CountDownLatch taskMovedToFrontLatch = new CountDownLatch(1);
final CountDownLatch taskRemovedLatch = new CountDownLatch(1);
final CountDownLatch taskRemovalStartedLatch = new CountDownLatch(1);
final CountDownLatch onDetachedFromWindowLatch = new CountDownLatch(1);
registerTaskStackChangedListener(new TaskStackListener() {
@Override
public void onTaskCreated(int taskId, ComponentName componentName)
throws RemoteException {
params[0] = taskId;
params[1] = componentName;
taskCreatedLaunchLatch.countDown();
}
@Override
public void onTaskMovedToFront(int taskId) throws RemoteException {
params[0] = taskId;
taskMovedToFrontLatch.countDown();
}
@Override
public void onTaskRemovalStarted(int taskId) {
params[0] = taskId;
taskRemovalStartedLatch.countDown();
}
@Override
public void onTaskRemoved(int taskId) throws RemoteException {
params[0] = taskId;
taskRemovedLatch.countDown();
}
});
final ActivityTaskChangeCallbacks activity =
(ActivityTaskChangeCallbacks) startTestActivity(ActivityTaskChangeCallbacks.class);
final int id = activity.getTaskId();
// Test for onTaskCreated.
waitForCallback(taskCreatedLaunchLatch);
assertEquals(id, params[0]);
ComponentName componentName = (ComponentName) params[1];
assertEquals(ActivityTaskChangeCallbacks.class.getName(), componentName.getClassName());
// Test for onTaskMovedToFront.
assertEquals(1, taskMovedToFrontLatch.getCount());
mService.moveTaskToFront(id, 0, null);
waitForCallback(taskMovedToFrontLatch);
assertEquals(activity.getTaskId(), params[0]);
// Test for onTaskRemovalStarted.
assertEquals(1, taskRemovalStartedLatch.getCount());
activity.finishAndRemoveTask();
waitForCallback(taskRemovalStartedLatch);
// onTaskRemovalStarted happens before the activity's window is removed.
assertFalse(activity.onDetachedFromWindowCalled);
assertEquals(id, params[0]);
// Test for onTaskRemoved.
assertEquals(1, taskRemovedLatch.getCount());
waitForCallback(taskRemovedLatch);
assertEquals(id, params[0]);
assertTrue(activity.onDetachedFromWindowCalled);
}
/**
* Starts the provided activity and returns the started instance.
*/
private Activity startTestActivity(Class<?> activityClass) {
final Context context = InstrumentationRegistry.getContext();
final ActivityMonitor monitor =
new ActivityMonitor(activityClass.getName(), null, false);
InstrumentationRegistry.getInstrumentation().addMonitor(monitor);
context.startActivity(new Intent(context, activityClass));
final Activity activity = monitor.waitForActivityWithTimeout(1000);
if (activity == null) {
throw new RuntimeException("Timed out waiting for Activity");
}
return activity;
}
private void registerTaskStackChangedListener(ITaskStackListener listener) throws Exception {
mTaskStackListener = listener;
mService.registerTaskStackListener(listener);
}
private void waitForCallback(CountDownLatch latch) {
try {
final boolean result = latch.await(2, TimeUnit.SECONDS);
if (!result) {
throw new RuntimeException("Timed out waiting for task stack change notification");
}
}catch (InterruptedException e) {}
}
public static class ActivityA extends Activity {
private boolean mActivityBLaunched = false;
@Override
protected void onPostResume() {
super.onPostResume();
if (mActivityBLaunched) {
return;
}
mActivityBLaunched = true;
finish();
startActivity(new Intent(this, ActivityB.class));
}
}
public static class ActivityB extends Activity {
@Override
protected void onPostResume() {
super.onPostResume();
synchronized (sLock) {
sTaskStackChangedCalled = false;
}
sActivityBResumed = true;
finish();
}
}
public static class ActivityRequestedOrientationChange extends Activity {
@Override
protected void onPostResume() {
super.onPostResume();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
finish();
}
}
public static class ActivityTaskDescriptionChange extends Activity {
@Override
protected void onPostResume() {
super.onPostResume();
setTaskDescription(new TaskDescription("Test Label"));
finish();
}
}
public static class ActivityTaskChangeCallbacks extends Activity {
public boolean onDetachedFromWindowCalled = false;;
@Override
public void onDetachedFromWindow() {
onDetachedFromWindowCalled = true;
}
}
}