Adding tests for assistant stack.

Bug: 30999386
Test: android.server.cts.ActivityManagerAssistantStackTests

Change-Id: Ife68f9029ef7cfc8565f1b4ed9e205236f3caded
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
index a37be98..49384e2 100755
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
@@ -21,8 +21,8 @@
 
     <!-- virtual display test permissions -->
     <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
-
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" />
 
     <application>
         <activity android:name=".TestActivity"
@@ -283,6 +283,29 @@
                 <action android:name="android.server.cts.LAUNCH_BROADCAST_ACTION"/>
             </intent-filter>
         </receiver>
+
+        <activity android:name=".AssistantActivity"
+            android:exported="true" />
+        <activity android:name=".LaunchAssistantActivityFromSession"
+            android:taskAffinity="nobody.but.LaunchAssistantActivityFromSession"
+            android:exported="true" />
+        <activity android:name=".LaunchAssistantActivityIntoAssistantStack"
+            android:taskAffinity="nobody.but.LaunchAssistantActivityIntoAssistantStack"
+            android:exported="true" />
+
+        <service android:name=".AssistantVoiceInteractionService"
+                 android:permission="android.permission.BIND_VOICE_INTERACTION"
+                 android:exported="true">
+            <meta-data android:name="android.voice_interaction"
+                       android:resource="@xml/interaction_service" />
+            <intent-filter>
+                <action android:name="android.service.voice.VoiceInteractionService" />
+            </intent-filter>
+        </service>
+
+        <service android:name=".AssistantVoiceInteractionSessionService"
+                 android:permission="android.permission.BIND_VOICE_INTERACTION"
+                 android:exported="true" />
     </application>
 </manifest>
 
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml
new file mode 100644
index 0000000..7cf92a0
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:sessionService="android.server.cts.AssistantVoiceInteractionSessionService"
+    android:recognitionService="android.server.cts.AssistantVoiceInteractionSessionService"
+    android:supportsAssist="true" />
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantActivity.java
new file mode 100644
index 0000000..e15f6d0
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantActivity.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 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 android.server.cts;
+
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class AssistantActivity extends Activity {
+
+    // Launches the given activity in onResume
+    public static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
+    // Finishes this activity in onResume, this happens after EXTRA_LAUNCH_NEW_TASK
+    public static final String EXTRA_FINISH_SELF = "finish_self";
+    // Attempts to enter picture-in-picture in onResume
+    public static final String EXTRA_ENTER_PIP = "enter_pip";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Launch the new activity if requested
+        if (getIntent().hasExtra(EXTRA_LAUNCH_NEW_TASK)) {
+            Intent i = new Intent();
+            i.setComponent(new ComponentName(this, getPackageName() + "."
+                    + getIntent().getStringExtra(EXTRA_LAUNCH_NEW_TASK)));
+            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            startActivity(i);
+        }
+
+        // Enter pip if requested
+        if (getIntent().hasExtra(EXTRA_ENTER_PIP)) {
+            try {
+                enterPictureInPictureMode();
+            } catch (IllegalStateException e) {
+                finish();
+                return;
+            }
+        }
+
+        // Finish this activity if requested
+        if (getIntent().hasExtra(EXTRA_FINISH_SELF)) {
+            finish();
+        }
+    }
+
+    /**
+     * Launches a new instance of the AssistantActivity directly into the assistant stack.
+     */
+    static void launchActivityIntoAssistantStack(Activity caller, Bundle extras) {
+        final Intent intent = new Intent(caller, AssistantActivity.class);
+        intent.setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
+        if (extras != null) {
+            intent.putExtras(extras);
+        }
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchStackId(6 /* ActivityManager.StackId.ASSISTANT_STACK_ID */);
+        caller.startActivity(intent, options.toBundle());
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionService.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionService.java
new file mode 100644
index 0000000..51c2348
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionService.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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 android.server.cts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionService;
+import android.util.Log;
+
+public class AssistantVoiceInteractionService extends VoiceInteractionService {
+
+    private static final String TAG = AssistantVoiceInteractionService.class.getSimpleName();
+
+    private boolean mReady;
+
+    @Override
+    public void onReady() {
+        super.onReady();
+        mReady = true;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (!isActiveService(this, new ComponentName(this, getClass()))) {
+            Log.wtf(TAG, "**** Not starting AssistantVoiceInteractionService because" +
+                    " it is not set as the current voice interaction service");
+            stopSelf();
+            return START_NOT_STICKY;
+        }
+        if (mReady) {
+            Bundle extras = intent.getExtras() != null ? intent.getExtras() : new Bundle();
+            showSession(extras, 0);
+        }
+        return START_NOT_STICKY;
+    }
+
+    /**
+     * Starts the assistant voice interaction service, which initiates a new session that starts
+     * the assistant activity.
+     */
+    public static void launchAssistantActivity(Context context, Bundle extras) {
+        Intent i = new Intent(context, AssistantVoiceInteractionService.class);
+        if (extras != null) {
+            i.putExtras(extras);
+        }
+        context.startService(i);
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionSessionService.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionSessionService.java
new file mode 100644
index 0000000..e711ac4
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionSessionService.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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 android.server.cts;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionSession;
+import android.service.voice.VoiceInteractionSessionService;
+
+public class AssistantVoiceInteractionSessionService extends VoiceInteractionSessionService {
+
+    @Override
+    public VoiceInteractionSession onNewSession(Bundle args) {
+        return new VoiceInteractionSession(this) {
+            @Override
+            public void onPrepareShow(Bundle args, int showFlags) {
+                setUiEnabled(false);
+            }
+
+            @Override
+            public void onShow(Bundle args, int showFlags) {
+                Intent i = new Intent(AssistantVoiceInteractionSessionService.this,
+                        AssistantActivity.class);
+                if (args != null) {
+                    i.putExtras(args);
+                }
+                startAssistantActivity(i);
+            }
+        };
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityFromSession.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityFromSession.java
new file mode 100644
index 0000000..2d562ff
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityFromSession.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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 android.server.cts;
+
+import android.app.Activity;
+
+public class LaunchAssistantActivityFromSession extends Activity {
+    @Override
+    protected void onResume() {
+        super.onResume();
+        AssistantVoiceInteractionService.launchAssistantActivity(this, getIntent().getExtras());
+        finishAndRemoveTask();
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityIntoAssistantStack.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityIntoAssistantStack.java
new file mode 100644
index 0000000..af610be
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityIntoAssistantStack.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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 android.server.cts;
+
+import android.app.Activity;
+
+public class LaunchAssistantActivityIntoAssistantStack extends Activity {
+    @Override
+    protected void onResume() {
+        super.onResume();
+        AssistantActivity.launchActivityIntoAssistantStack(this, getIntent().getExtras());
+        finishAndRemoveTask();
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
index e9b57ff..defcf4e 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
@@ -57,7 +57,7 @@
     private static final String EXTRA_ENTER_PIP_ON_PAUSE = "enter_pip_on_pause";
     // Starts the activity (component name) provided by the value at the end of onCreate
     private static final String EXTRA_START_ACTIVITY = "start_activity";
-    // Finishes the activity at the end of onCreate (after EXTRA_START_ACTIVITY is handled)
+    // Finishes the activity at the end of onResume (after EXTRA_START_ACTIVITY is handled)
     private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
     // Calls enterPictureInPicture() again after onPictureInPictureModeChanged(false) is called
     private static final String EXTRA_REENTER_PIP_ON_EXIT = "reenter_pip_on_exit";
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java
index dc633f2..eb08861 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java
@@ -16,6 +16,10 @@
 
 package android.server.cts;
 
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.os.Bundle;
 
@@ -26,6 +30,18 @@
     // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
     private static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
 
+    // Finishes the activity
+    private static final String ACTION_FINISH_SELF = "android.server.cts.TestActivity.finish_self";
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent != null && intent.getAction().equals(ACTION_FINISH_SELF)) {
+                finish();
+            }
+        }
+    };
+
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -40,10 +56,17 @@
     @Override
     protected void onResume() {
         super.onResume();
+        registerReceiver(mReceiver, new IntentFilter(ACTION_FINISH_SELF));
         dumpDisplaySize(getResources().getConfiguration());
     }
 
     @Override
+    protected void onPause() {
+        super.onPause();
+        unregisterReceiver(mReceiver);
+    }
+
+    @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         dumpDisplaySize(newConfig);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java
new file mode 100644
index 0000000..ae94603
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2017 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 android.server.cts;
+
+import static android.server.cts.ActivityManagerState.STATE_RESUMED;
+import static android.server.cts.ActivityManagerState.STATE_STOPPED;
+
+/**
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test android.server.cts.ActivityManagerAssistantStackTests
+ */
+public class ActivityManagerAssistantStackTests extends ActivityManagerTestBase {
+
+    private static final String VOICE_INTERACTION_SERVICE = "AssistantVoiceInteractionService";
+
+    private static final String TEST_ACTIVITY = "TestActivity";
+    private static final String DOCKED_ACTIVITY = "DockedActivity";
+    private static final String ASSISTANT_ACTIVITY = "AssistantActivity";
+    private static final String LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION =
+            "LaunchAssistantActivityFromSession";
+    private static final String LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK =
+            "LaunchAssistantActivityIntoAssistantStack";
+    private static final String PIP_ACTIVITY = "PipActivity";
+
+    private static final String EXTRA_ENTER_PIP = "enter_pip";
+    private static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
+    private static final String EXTRA_FINISH_SELF = "finish_self";
+
+    private static final String TEST_ACTIVITY_ACTION_FINISH_SELF =
+            "android.server.cts.TestActivity.finish_self";
+
+    public void testLaunchingAssistantActivityIntoAssistantStack() throws Exception {
+        // Enable the assistant and launch an assistant activity
+        enableAssistant();
+        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
+        mAmWmState.waitForValidState(mDevice, ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
+
+        // Ensure that the activity launched in the fullscreen assistant stack
+        assertAssistantStackExists();
+        assertTrue("Expected assistant stack to be fullscreen",
+                mAmWmState.getAmState().getStackById(ASSISTANT_STACK_ID).isFullscreen());
+
+        disableAssistant();
+    }
+
+    public void testAssistantStackZOrder() throws Exception {
+        // Launch a pinned stack task
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+        mAmWmState.assertContainsStack("Must contain pinned stack.", PINNED_STACK_ID);
+
+        // Dock a task
+        launchActivity(TEST_ACTIVITY);
+        launchActivityInDockStack(DOCKED_ACTIVITY);
+        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+                FULLSCREEN_WORKSPACE_STACK_ID);
+        mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
+
+        // Enable the assistant and launch an assistant activity, ensure it is on top
+        enableAssistant();
+        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
+        mAmWmState.waitForValidState(mDevice, ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
+        assertAssistantStackExists();
+
+        mAmWmState.assertFrontStack("Pinned stack should be on top.", PINNED_STACK_ID);
+        mAmWmState.assertFocusedStack("Assistant stack should be focused.", ASSISTANT_STACK_ID);
+
+        disableAssistant();
+    }
+
+    public void testAssistantStackLaunchNewTask() throws Exception {
+        enableAssistant();
+        assertAssistantStackCanLaunchAndReturnFromNewTask();
+        disableAssistant();
+    }
+
+    public void testAssistantStackLaunchNewTaskWithDockedStack() throws Exception {
+        // Dock a task
+        launchActivity(TEST_ACTIVITY);
+        launchActivityInDockStack(DOCKED_ACTIVITY);
+        mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+                FULLSCREEN_WORKSPACE_STACK_ID);
+        mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
+
+        enableAssistant();
+        assertAssistantStackCanLaunchAndReturnFromNewTask();
+        disableAssistant();
+    }
+
+    private void assertAssistantStackCanLaunchAndReturnFromNewTask() throws Exception {
+        // Enable the assistant and launch an assistant activity which will launch a new task
+        enableAssistant();
+        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                EXTRA_LAUNCH_NEW_TASK, TEST_ACTIVITY);
+        disableAssistant();
+
+        // Ensure that the fullscreen stack is on top and the test activity is now visible
+        mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+        mAmWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
+        mAmWmState.assertFrontStack("Fullscreen stack should be on top.",
+                FULLSCREEN_WORKSPACE_STACK_ID);
+        mAmWmState.assertFocusedStack("Fullscreen stack should be focused.",
+                FULLSCREEN_WORKSPACE_STACK_ID);
+
+        // Now, tell it to finish itself and ensure that the assistant stack is brought back forward
+        executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH_SELF);
+        mAmWmState.waitForFocusedStack(mDevice, ASSISTANT_STACK_ID);
+        mAmWmState.assertFrontStack("Assistant stack should be on top.", ASSISTANT_STACK_ID);
+        mAmWmState.assertFocusedStack("Assistant stack should be focused.", ASSISTANT_STACK_ID);
+    }
+
+    public void testAssistantStackFinishToPreviousApp() throws Exception {
+        // Launch an assistant activity on top of an existing fullscreen activity, and ensure that
+        // the fullscreen activity is still visible and on top after the assistant activity finishes
+        launchActivity(TEST_ACTIVITY);
+        enableAssistant();
+        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                EXTRA_FINISH_SELF, "true");
+        disableAssistant();
+        mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+        mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY, STATE_RESUMED);
+        mAmWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
+        mAmWmState.assertFrontStack("Fullscreen stack should be on top.",
+                FULLSCREEN_WORKSPACE_STACK_ID);
+        mAmWmState.assertFocusedStack("Fullscreen stack should be focused.",
+                FULLSCREEN_WORKSPACE_STACK_ID);
+    }
+
+    public void testDisallowEnterPiPFromAssistantStack() throws Exception {
+        enableAssistant();
+        launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
+                EXTRA_ENTER_PIP, "true");
+        disableAssistant();
+        mAmWmState.waitForValidState(mDevice, ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
+        mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.", PINNED_STACK_ID);
+    }
+
+    /**
+     * Asserts that the assistant stack exists.
+     */
+    private void assertAssistantStackExists() throws Exception {
+        mAmWmState.assertContainsStack("Must contain assistant stack.", ASSISTANT_STACK_ID);
+    }
+
+    /**
+     * Asserts that the assistant stack does not exist.
+     */
+    private void assertAssistantStackDoesNotExist() throws Exception {
+        mAmWmState.assertDoesNotContainStack("Must not contain assistant stack.",
+                ASSISTANT_STACK_ID);
+    }
+
+    /**
+     * Sets the system voice interaction service.
+     */
+    private void enableAssistant() throws Exception {
+        executeShellCommand("settings put secure voice_interaction_service " +
+                getActivityComponentName(VOICE_INTERACTION_SERVICE));
+    }
+
+    /**
+     * Resets the system voice interaction service.
+     */
+    private void disableAssistant() throws Exception {
+        executeShellCommand("settings delete secure voice_interaction_service " +
+                getActivityComponentName(VOICE_INTERACTION_SERVICE));
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
index 298b030..d2ee551 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
@@ -63,13 +63,19 @@
     /** ID of stack that always on top (always visible) when it exist. */
     public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
 
+    /** Recents activity stack ID. */
+    public static final int RECENTS_STACK_ID = PINNED_STACK_ID + 1;
+
+    /** Assistant activity stack ID.  This stack is fullscreen and non-resizeable. */
+    public static final int ASSISTANT_STACK_ID = RECENTS_STACK_ID + 1;
+
     protected static final int[] ALL_STACK_IDS_BUT_HOME = {
             FULLSCREEN_WORKSPACE_STACK_ID, FREEFORM_WORKSPACE_STACK_ID, DOCKED_STACK_ID,
-            PINNED_STACK_ID
+            PINNED_STACK_ID, ASSISTANT_STACK_ID
     };
 
     protected static final int[] ALL_STACK_IDS_BUT_HOME_AND_FULLSCREEN = {
-            FREEFORM_WORKSPACE_STACK_ID, DOCKED_STACK_ID, PINNED_STACK_ID
+            FREEFORM_WORKSPACE_STACK_ID, DOCKED_STACK_ID, PINNED_STACK_ID, ASSISTANT_STACK_ID
     };
 
     private static final String TASK_ID_PREFIX = "taskId";