• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 android.server.wm;
18 
19 import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
20 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 
24 import static org.junit.Assume.assumeTrue;
25 
26 import android.app.ActivityManager;
27 import android.app.ActivityOptions;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.os.Bundle;
32 import android.os.RemoteCallback;
33 import android.os.UserHandle;
34 import android.os.UserManager;
35 import android.platform.test.annotations.Presubmit;
36 import android.util.Log;
37 
38 import androidx.test.platform.app.InstrumentationRegistry;
39 
40 import org.junit.AfterClass;
41 import org.junit.Before;
42 import org.junit.BeforeClass;
43 import org.junit.Test;
44 
45 import java.util.concurrent.CountDownLatch;
46 import java.util.concurrent.TimeUnit;
47 
48 @Presubmit
49 public class StartActivityAsUserTests {
50     static final String EXTRA_CALLBACK = "callback";
51     static final String KEY_USER_ID = "user id";
52 
53     private static final String PACKAGE = "android.server.wm.cts";
54     private static final String CLASS = "android.server.wm.StartActivityAsUserActivity";
55     private static final int INVALID_STACK = -1;
56     private static final boolean SUPPORTS_MULTIPLE_USERS = UserManager.supportsMultipleUsers();
57 
58     private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
59     private final ActivityManager mAm = mContext.getSystemService(ActivityManager.class);
60 
61     private static int sSecondUserId;
62     private static boolean sCloneProfileSupported = true;
63 
64     private WindowManagerStateHelper mAmWmState = new WindowManagerStateHelper();
65     private static final String TAG = StartActivityAsUserTests.class.getSimpleName();
66 
67     @BeforeClass
createSecondUser()68     public static void createSecondUser() {
69         if (!SUPPORTS_MULTIPLE_USERS) {
70             return;
71         }
72 
73         final Context context = InstrumentationRegistry.getInstrumentation().getContext();
74         final String output = runShellCommand(
75                 "pm create-user --user-type android.os.usertype.profile.CLONE --profileOf "
76                         + context.getUserId() + " user2");
77 
78         try {
79             sSecondUserId = Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim());
80         } catch (StringIndexOutOfBoundsException |  NumberFormatException e) {
81             // AAOS does not support clone profile creation. This causes a parsing exception
82             // TODO (b/282975911)
83             Log.e(TAG, "Failed to create user of type android.os.usertype.profile.CLONE");
84             sCloneProfileSupported = false;
85         }
86 
87         if (sSecondUserId == 0) {
88             return;
89         }
90         runShellCommand("pm install-existing --user " + sSecondUserId + " android.server.wm.cts");
91         runShellCommand("am start-user -w " + sSecondUserId);
92     }
93 
94     @AfterClass
removeSecondUser()95     public static void removeSecondUser() {
96         if (sSecondUserId == 0) {
97             return;
98         }
99         runShellCommand("am stop-user -w -f " + sSecondUserId);
100         runShellCommand("pm remove-user " + sSecondUserId);
101         sSecondUserId = 0;
102     }
103 
104     @Before
checkMultipleUsersNotSupportedOrSecondUserCreated()105     public void checkMultipleUsersNotSupportedOrSecondUserCreated() {
106         assumeTrue(SUPPORTS_MULTIPLE_USERS);
107         assumeTrue(sCloneProfileSupported);
108         assertThat(sSecondUserId).isNotEqualTo(0);
109     }
110 
111     @Test
startActivityValidUser()112     public void startActivityValidUser() throws Throwable {
113         verifyStartActivityAsValidUser(false /* withOptions */);
114     }
115 
116     @Test
startActivityInvalidUser()117     public void startActivityInvalidUser() {
118         verifyStartActivityAsInvalidUser(false /* withOptions */);
119     }
120 
121     @Test
startActivityAsValidUserWithOptions()122     public void startActivityAsValidUserWithOptions() throws Throwable {
123         verifyStartActivityAsValidUser(true /* withOptions */);
124     }
125 
126     @Test
startActivityAsInvalidUserWithOptions()127     public void startActivityAsInvalidUserWithOptions() {
128         verifyStartActivityAsInvalidUser(true /* withOptions */);
129     }
130 
verifyStartActivityAsValidUser(boolean withOptions)131     private void verifyStartActivityAsValidUser(boolean withOptions) throws Throwable {
132         int[] secondUser = {-1};
133         CountDownLatch latch = new CountDownLatch(1);
134         RemoteCallback cb = new RemoteCallback((Bundle result) -> {
135             secondUser[0] = result.getInt(KEY_USER_ID);
136             latch.countDown();
137         });
138 
139         final Intent intent = new Intent(mContext, StartActivityAsUserActivity.class);
140         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
141         intent.putExtra(EXTRA_CALLBACK, cb);
142         UserHandle secondUserHandle = UserHandle.of(sSecondUserId);
143 
144         runWithShellPermissionIdentity(() -> {
145             if (withOptions) {
146                 mContext.startActivityAsUser(intent, ActivityOptions.makeBasic().toBundle(),
147                         secondUserHandle);
148             } else {
149                 mContext.startActivityAsUser(intent, secondUserHandle);
150             }
151         });
152 
153         latch.await(5, TimeUnit.SECONDS);
154         assertThat(secondUser[0]).isEqualTo(sSecondUserId);
155 
156         // The StartActivityAsUserActivity calls finish() in onCreate and here waits for the
157         // activity removed to prevent impacting other tests.
158         mAmWmState.waitForActivityRemoved(intent.getComponent());
159     }
160 
verifyStartActivityAsInvalidUser(boolean withOptions)161     private void verifyStartActivityAsInvalidUser(boolean withOptions) {
162         UserHandle secondUserHandle = UserHandle.of(sSecondUserId * 100);
163         int[] stackId = {-1};
164 
165         final Intent intent = new Intent(mContext, StartActivityAsUserActivity.class);
166         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
167 
168         runWithShellPermissionIdentity(() -> {
169             if (withOptions) {
170                 mContext.startActivityAsUser(intent, ActivityOptions.makeBasic().toBundle(),
171                         secondUserHandle);
172             } else {
173                 mContext.startActivityAsUser(intent, secondUserHandle);
174             }
175             WindowManagerState amState = mAmWmState;
176             amState.computeState();
177             ComponentName componentName = ComponentName.createRelative(PACKAGE, CLASS);
178             stackId[0] = amState.getRootTaskIdByActivity(componentName);
179         });
180 
181         assertThat(stackId[0]).isEqualTo(INVALID_STACK);
182     }
183 }
184