CTS tests for the smartspace service

Test: atest CtsSmartspaceServiceTestCases
Bug: 176851064
Change-Id: I20e740e74eeb969dadcb22c0c83aa0b9942bab97
diff --git a/tests/smartspace/Android.bp b/tests/smartspace/Android.bp
new file mode 100644
index 0000000..33b33c5
--- /dev/null
+++ b/tests/smartspace/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2019 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.
+
+android_test {
+    name: "CtsSmartspaceServiceTestCases",
+    defaults: ["cts_defaults"],
+    static_libs: [
+        "androidx.annotation_annotation",
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+        "truth-prebuilt",
+    ],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+}
diff --git a/tests/smartspace/AndroidManifest.xml b/tests/smartspace/AndroidManifest.xml
new file mode 100644
index 0000000..4a0658d
--- /dev/null
+++ b/tests/smartspace/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.smartspace.cts"
+          android:targetSandboxVersion="2">
+
+    <uses-permission android:name="android.permission.MANAGE_SMARTSPACE"/>
+
+    <application>
+        <service android:name=".CtsSmartspaceService"
+                 android:exported="true"
+                 android:label="CtsDummySmartspaceService">
+            <intent-filter>
+                <!-- This constant must match SmartspaceService.SERVICE_INTERFACE -->
+                <action android:name="android.service.smartspace.SmartspaceService"/>
+            </intent-filter>
+        </service>
+
+        <!-- TODO(b/111701043): Update with required permissions -->
+        <uses-library android:name="android.test.runner"/>
+
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:label="CTS tests for the App Prediction Framework APIs."
+                     android:targetPackage="android.smartspace.cts">
+    </instrumentation>
+
+</manifest>
diff --git a/tests/smartspace/AndroidTest.xml b/tests/smartspace/AndroidTest.xml
new file mode 100644
index 0000000..ad3aeeb
--- /dev/null
+++ b/tests/smartspace/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<configuration description="Config for Smartspace CTS tests.">
+    <option name="test-suite-tag" value="cts"/>
+    <option name="config-descriptor:metadata" key="component" value="framework"/>
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app"/>
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi"/>
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user"/>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="CtsSmartspaceServiceTestCases.apk"/>
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="android.smartspace.cts"/>
+        <!-- 20x default timeout of 600sec -->
+        <option name="shell-timeout" value="12000000"/>
+    </test>
+
+</configuration>
diff --git a/tests/smartspace/OWNERS b/tests/smartspace/OWNERS
new file mode 100644
index 0000000..4a61d75
--- /dev/null
+++ b/tests/smartspace/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 487497
+srazdan@google.com
+alexmang@google.com
diff --git a/tests/smartspace/TEST_MAPPING b/tests/smartspace/TEST_MAPPING
new file mode 100644
index 0000000..58b65d1
--- /dev/null
+++ b/tests/smartspace/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsSmartspaceServiceTestCases"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/smartspace/src/android/smartspace/cts/CtsSmartspaceService.java b/tests/smartspace/src/android/smartspace/cts/CtsSmartspaceService.java
new file mode 100644
index 0000000..a285511
--- /dev/null
+++ b/tests/smartspace/src/android/smartspace/cts/CtsSmartspaceService.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2021 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.smartspace.cts;
+
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.os.Process;
+import android.service.smartspace.SmartspaceService;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+public class CtsSmartspaceService extends SmartspaceService {
+
+    private static final boolean DEBUG = true;
+    public static final String EXTRA_REPORTER = "extra_reporter";
+    public static final String MY_PACKAGE = "android.smartspace.cts";
+    public static final String SERVICE_NAME = MY_PACKAGE + "/."
+            + CtsSmartspaceService.class.getSimpleName();
+    private static final String TAG = CtsSmartspaceService.class.getSimpleName();
+
+    private static Watcher sWatcher;
+
+    private final ArrayMap<SmartspaceSessionId, List<SmartspaceTarget>> targets = new ArrayMap<>();
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+//        Log.d(TAG, "onCreate mSessionCallbacks: " + mSessionCallbacks);
+        if (DEBUG) Log.d(TAG, "onCreate");
+    }
+
+    @Override
+    public void onCreateSmartspaceSession(SmartspaceConfig config, SmartspaceSessionId sessionId) {
+//        Log.d(TAG, "onCreateSmartspaceSession mSessionCallbacks: " + mSessionCallbacks);
+        if (DEBUG) Log.d(TAG, "onCreateSmartspaceSession");
+
+        if (sWatcher.verifier != null) {
+            Log.e(TAG, "onCreateSmartspaceSession, trying to set verifier when it already exists");
+        }
+        targets.put(sessionId, new ArrayList<>());
+        sWatcher.verifier = Mockito.mock(CtsSmartspaceService.class);
+        sWatcher.created.countDown();
+    }
+
+    @Override
+    public void notifySmartspaceEvent(SmartspaceSessionId sessionId, SmartspaceTargetEvent event) {
+//        Log.d(TAG, "notifySmartspaceEvent mSessionCallbacks: " + mSessionCallbacks);
+        if (DEBUG){
+            Log.d(TAG, "notifySmartspaceEvent sessionId=" + sessionId + ", event=" + event.toString());
+        }
+        if(event.getSmartspaceTarget() != null) {
+            targets.get(sessionId).add(event.getSmartspaceTarget());
+        }
+        sWatcher.verifier.notifySmartspaceEvent(sessionId, event);
+    }
+
+    @Override
+    public void onRequestSmartspaceUpdate(SmartspaceSessionId sessionId) {
+//        Log.d(TAG, "onRequestSmartspaceUpdate mSessionCallbacks: " + mSessionCallbacks);
+        if (DEBUG){
+            Log.d(TAG, "onRequestSmartspaceUpdate sessionId=" + sessionId);
+        }
+        List<SmartspaceTarget> returnList = targets.get(sessionId);
+        if(returnList == null) {
+            returnList = new ArrayList<>();
+        }
+        updateSmartspaceTargets(sessionId, returnList);
+        sWatcher.verifier.onRequestSmartspaceUpdate(sessionId);
+    }
+
+    @Override
+    public void onDestroySmartspaceSession(SmartspaceSessionId sessionId) {
+//        Log.d(TAG, "onDestroySmartspaceSession mSessionCallbacks: " + mSessionCallbacks);
+        if (DEBUG) Log.d(TAG, "onDestroySmartspaceSession");
+        targets.remove(sessionId);
+        super.onDestroy();
+        sWatcher.destroyed.countDown();
+    }
+
+    @Override
+    public void onDestroy(SmartspaceSessionId sessionId) {
+//        Log.d(TAG, "onDestroy mSessionCallbacks: " + mSessionCallbacks);
+        if (DEBUG) Log.d(TAG, "onDestroy");
+        super.onDestroy();
+        sWatcher.destroyed.countDown();
+    }
+
+
+    public static Watcher setWatcher() {
+        if (DEBUG) {
+            Log.d(TAG, "");
+            Log.d(TAG, "----------------------------------------------");
+            Log.d(TAG, " setWatcher");
+        }
+        if (sWatcher != null) {
+            throw new IllegalStateException("Set watcher with watcher already set");
+        }
+        sWatcher = new Watcher();
+        return sWatcher;
+    }
+
+    public static void clearWatcher() {
+        if (DEBUG) Log.d(TAG, "clearWatcher");
+        sWatcher = null;
+    }
+
+    public static final class Watcher {
+        public CountDownLatch created = new CountDownLatch(1);
+        public CountDownLatch destroyed = new CountDownLatch(1);
+        public CountDownLatch queried = new CountDownLatch(1);
+        public CountDownLatch queriedTwice = new CountDownLatch(2);
+
+        /**
+         * Can be used to verify that API specific service methods are called. Not a real mock as
+         * the system isn't talking to this directly, it has calls proxied to it.
+         */
+        public CtsSmartspaceService verifier;
+
+        public List<SmartspaceTarget> mSmartspaceTargets;
+
+        public void setTargets(List<SmartspaceTarget> targets) {
+            mSmartspaceTargets = targets;
+        }
+    }
+}
diff --git a/tests/smartspace/src/android/smartspace/cts/SmartspaceManagerTest.java b/tests/smartspace/src/android/smartspace/cts/SmartspaceManagerTest.java
new file mode 100644
index 0000000..936a835
--- /dev/null
+++ b/tests/smartspace/src/android/smartspace/cts/SmartspaceManagerTest.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2020 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.smartspace.cts;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceManager;
+import android.app.smartspace.SmartspaceSession;
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Process;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.RequiredServiceRule;
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link SmartspaceManager}
+ *
+ * atest CtsSearchUiServiceTestCases
+ */
+@RunWith(AndroidJUnit4.class)
+public class SmartspaceManagerTest {
+
+    private static final String TAG = "SmartspaceManagerTest";
+    private static final boolean DEBUG = false;
+
+    private static final long VERIFY_TIMEOUT_MS = 5_000;
+    private static final long SERVICE_LIFECYCLE_TIMEOUT_MS = 20_000;
+    private static final String TEST_UI_SURFACE = "homescreen";
+    private static final String SMARTSPACE_ACTION_ID = "dummy_action_id";
+    private static final int TEST_NUM_PREDICTIONS = 10;
+    private static final String TEST_LAUNCH_LOCATION = "testCollapsedLocation";
+    private static final int TEST_ACTION = 2;
+
+    @Rule
+    public final RequiredServiceRule mRequiredServiceRule =
+            new RequiredServiceRule(Context.SMARTSPACE_SERVICE);
+
+    private SmartspaceManager mManager;
+    private SmartspaceSession mClient;
+    private CtsSmartspaceService.Watcher mWatcher;
+
+    @Before
+    public void setUp() throws Exception {
+        mWatcher = CtsSmartspaceService.setWatcher();
+        mManager = getContext().getSystemService(SmartspaceManager.class);
+        setService(CtsSmartspaceService.SERVICE_NAME);
+        SmartspaceConfig config = createSmartspaceConfig(TEST_UI_SURFACE);
+        mClient = createSmartspaceSession(config);
+        await(mWatcher.created, "Waiting for onCreate()");
+        reset(mWatcher.verifier);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        Log.d(TAG, "Starting tear down, watcher is: " + mWatcher);
+        mClient.destroy();
+        setService(null);
+        await(mWatcher.destroyed, "Waiting for onDestroy()");
+
+        mWatcher = null;
+        CtsSmartspaceService.clearWatcher();
+    }
+
+    @Test
+    public void testCreateSmartspaceSession() {
+        assertNotNull(mClient);
+        assertNotNull(mWatcher.verifier);
+    }
+
+    @Test
+    public void testDestroySession() {
+        SmartspaceSession localClient = createSmartspaceSession(createSmartspaceConfig("surface"));
+        localClient.destroy();
+        await(mWatcher.destroyed, "Waiting for onDestroy()");
+    }
+
+    @Test
+    public void testRequestSmartspaceUpdate() {
+        // Send a request for a smartspace update
+        SmartspaceTarget testTarget = SmartspaceTestUtils.getBasicSmartspaceTarget("id",
+                SmartspaceTestUtils.getTestComponentName(), Process.myUserHandle());
+        SmartspaceTargetEvent testEvent = new SmartspaceTargetEvent.Builder(
+                SmartspaceTargetEvent.EVENT_TARGET_INTERACTION).setSmartspaceTarget(
+                testTarget).setSmartspaceActionId("id").build();
+        mClient.notifySmartspaceEvent(testEvent);
+        mClient.registerSmartspaceUpdates(Executors.newSingleThreadExecutor(),
+                targets -> {
+                    if (targets.size() > 0 && targets.get(0).equals(testTarget)) {
+                        mWatcher.queried.countDown();
+                    }
+                });
+        mClient.requestSmartspaceUpdate();
+        // Verify that the API received it
+        verify(mWatcher.verifier, timeout(VERIFY_TIMEOUT_MS).times(2))
+                .onRequestSmartspaceUpdate(any());
+        await(mWatcher.queried, "Waiting for updateSmartspaceTargets()");
+    }
+
+    @Test
+    public void testRequestSmartspaceUpdateForMultipleSessions() {
+        SmartspaceTarget testTarget = SmartspaceTestUtils.getBasicSmartspaceTarget("id",
+                SmartspaceTestUtils.getTestComponentName(), Process.myUserHandle());
+        SmartspaceTargetEvent testEvent = new SmartspaceTargetEvent.Builder(
+                SmartspaceTargetEvent.EVENT_TARGET_INTERACTION).setSmartspaceTarget(
+                testTarget).setSmartspaceActionId("id").build();
+
+        SmartspaceConfig config1 = createSmartspaceConfig("surface 1");
+        SmartspaceSession client1 = createSmartspaceSession(config1);
+        SmartspaceConfig config2 = createSmartspaceConfig("surface 2");
+        SmartspaceSession client2 = createSmartspaceSession(config2);
+        client1.registerSmartspaceUpdates(Executors.newSingleThreadExecutor(),
+                targets -> {
+                    // Counting down only if the returned list only contains test target.
+                    if (targets.size() > 0 && targets.get(0).equals(testTarget)) {
+                        mWatcher.queriedTwice.countDown();
+                    }
+                });
+        client2.registerSmartspaceUpdates(Executors.newSingleThreadExecutor(),
+                targets -> {
+                    // Counting down only if the returned list is empty.
+                    if (targets.isEmpty()) {
+                        mWatcher.queriedTwice.countDown();
+                    }
+                });
+        // Notifying the event only for client1.
+        client1.notifySmartspaceEvent(testEvent);
+        // Requesting update for both the clients
+        client1.requestSmartspaceUpdate();
+        client2.requestSmartspaceUpdate();
+        // Verify that the API received it 4 times, twice for each client, once while registering
+        // and once while requesting.
+        verify(mWatcher.verifier, timeout(VERIFY_TIMEOUT_MS).times(4))
+                .onRequestSmartspaceUpdate(any());
+        await(mWatcher.queriedTwice, "Waiting for updateSmartspaceTargets() to be called twice");
+    }
+
+    @Test
+    public void testNotifySmartspaceEvent() {
+        ComponentName componentName = new ComponentName("package_name", "class_name");
+        SmartspaceTarget target = new SmartspaceTarget.Builder("id",
+                componentName, Process.myUserHandle()).build();
+
+        SmartspaceTargetEvent smartspaceTargetEvent = new SmartspaceTargetEvent.Builder(
+                SmartspaceTargetEvent.EVENT_TARGET_BLOCK).setSmartspaceActionId(
+                SMARTSPACE_ACTION_ID).setSmartspaceTarget(target).build();
+
+        ArgumentCaptor<SmartspaceTargetEvent> arg = ArgumentCaptor.forClass(
+                SmartspaceTargetEvent.class);
+        // Send a request to notifySmartspaceUpdate
+        mClient.notifySmartspaceEvent(smartspaceTargetEvent);
+        // Verify that the API received it.
+        verify(mWatcher.verifier, timeout(VERIFY_TIMEOUT_MS))
+                .notifySmartspaceEvent(any(), arg.capture());
+        assertEquals(arg.getValue().getSmartspaceActionId(), SMARTSPACE_ACTION_ID);
+        assertEquals(arg.getValue().getEventType(), SmartspaceTargetEvent.EVENT_TARGET_BLOCK);
+        assertEquals(arg.getValue().getSmartspaceTarget().getComponentName(), componentName);
+        assertEquals(arg.getValue().getSmartspaceTarget().getUserHandle(), Process.myUserHandle());
+        assertEquals(arg.getValue().getSmartspaceTarget().getSmartspaceTargetId(), "id");
+
+    }
+
+    private void setService(String service) {
+        Log.d(TAG, "Setting smartspace service to " + service);
+        int userId = Process.myUserHandle().getIdentifier();
+        if (service != null) {
+            runShellCommand("cmd smartspace set temporary-service "
+                    + userId + " " + service + " 60000");
+        } else {
+            runShellCommand("cmd smartspace set temporary-service " + userId);
+        }
+    }
+
+    private SmartspaceSession createSmartspaceSession(SmartspaceConfig config) {
+        return mManager.createSmartspaceSession(config);
+    }
+
+    private SmartspaceConfig createSmartspaceConfig(String uiSurface) {
+        return new SmartspaceConfig.Builder(
+                InstrumentationRegistry.getTargetContext(),
+                uiSurface).setSmartspaceTargetCount(TEST_NUM_PREDICTIONS).build();
+    }
+
+    private void await(@NonNull CountDownLatch latch, @NonNull String message) {
+        try {
+            assertWithMessage(message).that(
+                    latch.await(SERVICE_LIFECYCLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new IllegalStateException("Interrupted while: " + message);
+        }
+    }
+
+    private void runShellCommand(String command) {
+        Log.d(TAG, "runShellCommand(): " + command);
+        try {
+            SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
+        } catch (Exception e) {
+            throw new RuntimeException("Command '" + command + "' failed: ", e);
+        }
+    }
+
+    public static class ConsumerVerifier implements
+            Consumer<List<SmartspaceTarget>> {
+
+        private static List<SmartspaceTarget> mExpectedTargets;
+
+        public ConsumerVerifier(List<SmartspaceTarget> targets) {
+            mExpectedTargets = targets;
+        }
+
+        @Override
+        public void accept(List<SmartspaceTarget> actualTargets) {
+            if (DEBUG) {
+                Log.d(TAG, "ConsumerVerifier.accept targets.size= " + actualTargets.size());
+                Log.d(TAG, "ConsumerVerifier.accept target(1).packageName=" + actualTargets.get(
+                        0).getComponentName().getPackageName());
+            }
+            Assert.assertArrayEquals(actualTargets.toArray(), mExpectedTargets.toArray());
+        }
+    }
+
+    private static class RequestVerifier implements SmartspaceSession.Callback {
+        @Override
+        public void onTargetsAvailable(List<SmartspaceTarget> targets) {
+
+        }
+    }
+}
diff --git a/tests/smartspace/src/android/smartspace/cts/SmartspaceTestUtils.java b/tests/smartspace/src/android/smartspace/cts/SmartspaceTestUtils.java
new file mode 100644
index 0000000..d2a5ffb
--- /dev/null
+++ b/tests/smartspace/src/android/smartspace/cts/SmartspaceTestUtils.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 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.smartspace.cts;
+
+import android.app.smartspace.SmartspaceTarget;
+import android.content.ComponentName;
+import android.os.UserHandle;
+
+public class SmartspaceTestUtils {
+    public static SmartspaceTarget getBasicSmartspaceTarget(String id, ComponentName componentName, UserHandle userHandle){
+        return new SmartspaceTarget.Builder(id, componentName, userHandle).build();
+    }
+
+    public static ComponentName getTestComponentName() {
+        return new ComponentName("package name", "class name");
+    }
+}