blob: 17dd26ed18e90f531599172a38073008cfa7458f [file] [log] [blame]
Andrii Kulian40eda672019-07-30 15:05:57 -07001/*
2 * Copyright (C) 2019 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
17package com.android.server.wm;
18
Hyangseok Chaeed0624e2019-11-07 10:15:46 +090019import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
jorgegil@google.com06bc3232019-10-31 14:51:22 -070020import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
Andrii Kulian40eda672019-07-30 15:05:57 -070021import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
Hyangseok Chaeed0624e2019-11-07 10:15:46 +090022import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
23import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
jorgegil@google.com06bc3232019-10-31 14:51:22 -070024import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
Andrii Kulian40eda672019-07-30 15:05:57 -070025
Evan Roskyddedfd42019-10-04 13:38:38 -070026import static org.junit.Assert.assertEquals;
jorgegil@google.com06bc3232019-10-31 14:51:22 -070027import static org.junit.Assert.assertNull;
Andrii Kulian40eda672019-07-30 15:05:57 -070028import static org.junit.Assert.assertTrue;
jorgegil@google.com06bc3232019-10-31 14:51:22 -070029import static org.mockito.ArgumentMatchers.anyBoolean;
30import static org.mockito.ArgumentMatchers.anyString;
31import static org.mockito.Mockito.times;
Hyangseok Chaeed0624e2019-11-07 10:15:46 +090032import static org.mockito.Mockito.when;
Andrii Kulian40eda672019-07-30 15:05:57 -070033
34import android.app.Activity;
Hyangseok Chaeed0624e2019-11-07 10:15:46 +090035import android.app.PictureInPictureParams;
jorgegil@google.com06bc3232019-10-31 14:51:22 -070036import android.app.servertransaction.ClientTransaction;
37import android.app.servertransaction.EnterPipRequestedItem;
Evan Rosky82207082019-10-08 17:28:29 -070038import android.content.res.Configuration;
Evan Roskyddedfd42019-10-04 13:38:38 -070039import android.graphics.Rect;
Hyangseok Chaeed0624e2019-11-07 10:15:46 +090040import android.os.IBinder;
jorgegil@google.com06bc3232019-10-31 14:51:22 -070041import android.os.RemoteException;
Evan Rosky82207082019-10-08 17:28:29 -070042import android.view.IDisplayWindowListener;
Andrii Kulian40eda672019-07-30 15:05:57 -070043
44import androidx.test.filters.MediumTest;
45
46import org.junit.Before;
47import org.junit.Test;
Riddle Hsu73f53572019-09-23 23:13:01 +080048import org.junit.runner.RunWith;
jorgegil@google.com06bc3232019-10-31 14:51:22 -070049import org.mockito.ArgumentCaptor;
Hyangseok Chaeed0624e2019-11-07 10:15:46 +090050import org.mockito.MockitoSession;
Andrii Kulian40eda672019-07-30 15:05:57 -070051
Evan Rosky82207082019-10-08 17:28:29 -070052import java.util.ArrayList;
53
Andrii Kulian40eda672019-07-30 15:05:57 -070054/**
55 * Tests for the {@link ActivityTaskManagerService} class.
56 *
57 * Build/Install/Run:
58 * atest WmTests:ActivityTaskManagerServiceTests
59 */
60@MediumTest
Riddle Hsu73f53572019-09-23 23:13:01 +080061@RunWith(WindowTestRunner.class)
Andrii Kulian40eda672019-07-30 15:05:57 -070062public class ActivityTaskManagerServiceTests extends ActivityTestsBase {
63
jorgegil@google.com06bc3232019-10-31 14:51:22 -070064 private final ArgumentCaptor<ClientTransaction> mClientTransactionCaptor =
65 ArgumentCaptor.forClass(ClientTransaction.class);
66
Andrii Kulian40eda672019-07-30 15:05:57 -070067 @Before
68 public void setUp() throws Exception {
69 doReturn(false).when(mService).isBooting();
70 doReturn(true).when(mService).isBooted();
71 }
72
73 /** Verify that activity is finished correctly upon request. */
74 @Test
75 public void testActivityFinish() {
Louis Chang149d5c82019-12-30 09:47:39 +080076 final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
Wale Ogunwale85fb19a2019-12-05 10:41:05 +090077 final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
Andrii Kulian40eda672019-07-30 15:05:57 -070078 assertTrue("Activity must be finished", mService.finishActivity(activity.appToken,
79 0 /* resultCode */, null /* resultData */,
80 Activity.DONT_FINISH_TASK_WITH_ACTIVITY));
81 assertTrue(activity.finishing);
82
83 assertTrue("Duplicate activity finish request must also return 'true'",
84 mService.finishActivity(activity.appToken, 0 /* resultCode */,
85 null /* resultData */, Activity.DONT_FINISH_TASK_WITH_ACTIVITY));
86 }
Evan Roskyddedfd42019-10-04 13:38:38 -070087
88 @Test
jorgegil@google.com06bc3232019-10-31 14:51:22 -070089 public void testOnPictureInPictureRequested() throws RemoteException {
Louis Chang149d5c82019-12-30 09:47:39 +080090 final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
jorgegil@google.com06bc3232019-10-31 14:51:22 -070091 final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
jorgegil@google.coma7221712020-04-15 13:16:58 -070092 final ClientLifecycleManager mockLifecycleManager = mock(ClientLifecycleManager.class);
93 doReturn(mockLifecycleManager).when(mService).getLifecycleManager();
jorgegil@google.com06bc3232019-10-31 14:51:22 -070094 doReturn(true).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean());
95
96 mService.requestPictureInPictureMode(activity.token);
97
jorgegil@google.coma7221712020-04-15 13:16:58 -070098 verify(mockLifecycleManager).scheduleTransaction(mClientTransactionCaptor.capture());
jorgegil@google.com06bc3232019-10-31 14:51:22 -070099 final ClientTransaction transaction = mClientTransactionCaptor.getValue();
100 // Check that only an enter pip request item callback was scheduled.
101 assertEquals(1, transaction.getCallbacks().size());
102 assertTrue(transaction.getCallbacks().get(0) instanceof EnterPipRequestedItem);
103 // Check the activity lifecycle state remains unchanged.
104 assertNull(transaction.getLifecycleStateRequest());
105 }
106
107 @Test(expected = IllegalStateException.class)
108 public void testOnPictureInPictureRequested_cannotEnterPip() throws RemoteException {
Louis Chang149d5c82019-12-30 09:47:39 +0800109 final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
jorgegil@google.com06bc3232019-10-31 14:51:22 -0700110 final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
111 ClientLifecycleManager lifecycleManager = mService.getLifecycleManager();
112 doNothing().when(lifecycleManager).scheduleTransaction(any());
113 doReturn(false).when(activity).checkEnterPictureInPictureState(anyString(), anyBoolean());
114
115 mService.requestPictureInPictureMode(activity.token);
116
117 // Check enter no transactions with enter pip requests are made.
118 verify(lifecycleManager, times(0)).scheduleTransaction(any());
119 }
120
121 @Test
Evan Rosky82207082019-10-08 17:28:29 -0700122 public void testDisplayWindowListener() {
123 final ArrayList<Integer> added = new ArrayList<>();
124 final ArrayList<Integer> changed = new ArrayList<>();
125 final ArrayList<Integer> removed = new ArrayList<>();
126 IDisplayWindowListener listener = new IDisplayWindowListener.Stub() {
127 @Override
128 public void onDisplayAdded(int displayId) {
129 added.add(displayId);
130 }
131
132 @Override
133 public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
134 changed.add(displayId);
135 }
136
137 @Override
138 public void onDisplayRemoved(int displayId) {
139 removed.add(displayId);
140 }
141 };
142 mService.mWindowManager.registerDisplayWindowListener(listener);
143 // Check that existing displays call added
144 assertEquals(1, added.size());
145 assertEquals(0, changed.size());
146 assertEquals(0, removed.size());
147 added.clear();
148 // Check adding a display
Louis Chang677921f2019-12-06 16:44:24 +0800149 DisplayContent newDisp1 = new TestDisplayContent.Builder(mService, 600, 800).build();
Evan Rosky82207082019-10-08 17:28:29 -0700150 assertEquals(1, added.size());
151 assertEquals(0, changed.size());
152 assertEquals(0, removed.size());
153 added.clear();
154 // Check that changes are reported
155 Configuration c = new Configuration(newDisp1.getRequestedOverrideConfiguration());
156 c.windowConfiguration.setBounds(new Rect(0, 0, 1000, 1300));
157 newDisp1.onRequestedOverrideConfigurationChanged(c);
Louis Chang149d5c82019-12-30 09:47:39 +0800158 mService.mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */,
Evan Rosky82207082019-10-08 17:28:29 -0700159 newDisp1.mDisplayId, false /* markFrozenIfConfigChanged */,
160 false /* deferResume */);
161 assertEquals(0, added.size());
162 assertEquals(1, changed.size());
163 assertEquals(0, removed.size());
164 changed.clear();
165 // Check that removal is reported
166 newDisp1.remove();
167 assertEquals(0, added.size());
168 assertEquals(0, changed.size());
169 assertEquals(1, removed.size());
170 }
Hyangseok Chaeed0624e2019-11-07 10:15:46 +0900171
172 /*
173 a test to verify b/144045134 - ignore PIP mode request for destroyed activity.
174 mocks r.getParent() to return null to cause NPE inside enterPipRunnable#run() in
175 ActivityTaskMangerservice#enterPictureInPictureMode(), which rebooted the device.
176 It doesn't fully simulate the issue's reproduce steps, but this should suffice.
177 */
178 @Test
179 public void testEnterPipModeWhenRecordParentChangesToNull() {
180 MockitoSession mockSession = mockitoSession()
181 .initMocks(this)
182 .mockStatic(ActivityRecord.class)
183 .startMocking();
184
185 ActivityRecord record = mock(ActivityRecord.class);
186 IBinder token = mock(IBinder.class);
187 PictureInPictureParams params = mock(PictureInPictureParams.class);
188 record.pictureInPictureArgs = params;
189
190 //mock operations in private method ensureValidPictureInPictureActivityParamsLocked()
191 when(ActivityRecord.forTokenLocked(token)).thenReturn(record);
192 doReturn(true).when(record).supportsPictureInPicture();
193 doReturn(false).when(params).hasSetAspectRatio();
194
195 //mock other operations
196 doReturn(true).when(record)
197 .checkEnterPictureInPictureState("enterPictureInPictureMode", false);
198 doReturn(false).when(mService).isInPictureInPictureMode(any());
199 doReturn(false).when(mService).isKeyguardLocked();
200
201 //to simulate NPE
202 doReturn(null).when(record).getParent();
203
204 mService.enterPictureInPictureMode(token, params);
205 //if record's null parent is not handled gracefully, test will fail with NPE
206
207 mockSession.finishMocking();
208 }
Andrii Kulian40eda672019-07-30 15:05:57 -0700209}
210