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.content.BroadcastReceiver;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.os.Bundle;
33 import android.os.RemoteCallback;
34 import android.os.UserHandle;
35 import android.os.UserManager;
36 import android.platform.test.annotations.Presubmit;
37 
38 import androidx.test.platform.app.InstrumentationRegistry;
39 
40 import org.junit.After;
41 import org.junit.Before;
42 import org.junit.Test;
43 
44 import java.util.concurrent.CountDownLatch;
45 import java.util.concurrent.TimeUnit;
46 
47 @Presubmit
48 public class StartActivityAsUserTests {
49     static final String EXTRA_CALLBACK = "callback";
50     static final String KEY_USER_ID = "user id";
51 
52     private static final String PACKAGE = "android.server.wm.cts";
53     private static final String CLASS = "android.server.wm.StartActivityAsUserActivity";
54     private static final int INVALID_STACK = -1;
55     private static final boolean SUPPORTS_MULTIPLE_USERS = UserManager.supportsMultipleUsers();
56 
57     private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
58     private final ActivityManager mAm = mContext.getSystemService(ActivityManager.class);
59 
60     private int mSecondUserId;
61     private WindowManagerStateHelper mAmWmState = new WindowManagerStateHelper();
62 
63     @Before
createSecondUser()64     public void createSecondUser() {
65         assumeTrue(SUPPORTS_MULTIPLE_USERS);
66 
67         final String output = runShellCommand("pm create-user --profileOf " + mContext.getUserId()
68                 + " user2");
69         mSecondUserId = Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim());
70         assertThat(mSecondUserId).isNotEqualTo(0);
71         runShellCommand("pm install-existing --user " + mSecondUserId + " android.server.wm.cts");
72     }
73 
74     @After
removeSecondUser()75     public void removeSecondUser() {
76         runShellCommand("pm remove-user " + mSecondUserId);
77     }
78 
79     @Test
startActivityValidUser()80     public void startActivityValidUser() throws Throwable {
81         int[] secondUser= {-1};
82         CountDownLatch latch = new CountDownLatch(1);
83         RemoteCallback cb = new RemoteCallback((Bundle result) -> {
84             secondUser[0] = result.getInt(KEY_USER_ID);
85             latch.countDown();
86         });
87 
88         final Intent intent = new Intent(mContext, StartActivityAsUserActivity.class);
89         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
90         intent.putExtra(EXTRA_CALLBACK, cb);
91 
92         final CountDownLatch returnToOriginalUserLatch = new CountDownLatch(1);
93         mContext.registerReceiver(new BroadcastReceiver() {
94             @Override
95             public void onReceive(Context context, Intent intent) {
96                 mContext.unregisterReceiver(this);
97                 returnToOriginalUserLatch.countDown();
98             }
99         }, new IntentFilter(Intent.ACTION_USER_FOREGROUND));
100 
101         UserHandle secondUserHandle = UserHandle.of(mSecondUserId);
102 
103         try {
104             runWithShellPermissionIdentity(() -> {
105                 mContext.startActivityAsUser(intent, secondUserHandle);
106                 mAm.switchUser(secondUserHandle);
107                 try {
108                     latch.await(5, TimeUnit.SECONDS);
109                 } finally {
110                     mAm.switchUser(mContext.getUser());
111                 }
112             });
113         } catch (RuntimeException e) {
114             throw e.getCause();
115         }
116 
117         assertThat(secondUser[0]).isEqualTo(mSecondUserId);
118 
119         // Avoid the race between switch-user and remove-user.
120         returnToOriginalUserLatch.await(20, TimeUnit.SECONDS);
121     }
122 
123     @Test
startActivityInvalidUser()124     public void startActivityInvalidUser() {
125         UserHandle secondUserHandle = UserHandle.of(mSecondUserId * 100);
126         int[] stackId = {-1};
127 
128         final Intent intent = new Intent(mContext, StartActivityAsUserActivity.class);
129         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
130 
131         runWithShellPermissionIdentity(() -> {
132             mContext.startActivityAsUser(intent, secondUserHandle);
133             WindowManagerState amState = mAmWmState;
134             amState.computeState();
135             ComponentName componentName = ComponentName.createRelative(PACKAGE, CLASS);
136             stackId[0] = amState.getRootTaskIdByActivity(componentName);
137         });
138 
139         assertThat(stackId[0]).isEqualTo(INVALID_STACK);
140     }
141 }
142