SmartspaceManager initial implementation

Bug: 176851064
Test: Deployed on Phone,CTS tests
Change-Id: I3e03628d4d0e0aec0b6442f0ee8949ac378a1572
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0a0f77e..fbfd354 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -138,6 +138,7 @@
     field public static final String MANAGE_ROTATION_RESOLVER = "android.permission.MANAGE_ROTATION_RESOLVER";
     field public static final String MANAGE_SEARCH_UI = "android.permission.MANAGE_SEARCH_UI";
     field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
+    field public static final String MANAGE_SMARTSPACE = "android.permission.MANAGE_SMARTSPACE";
     field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
     field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
     field public static final String MANAGE_TEST_NETWORKS = "android.permission.MANAGE_TEST_NETWORKS";
@@ -1493,6 +1494,169 @@
 
 }
 
+package android.app.smartspace {
+
+  public final class SmartspaceAction implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public CharSequence getContentDescription();
+    method @Nullable public android.os.Bundle getExtras();
+    method @Nullable public android.graphics.drawable.Icon getIcon();
+    method @NonNull public String getId();
+    method @Nullable public android.content.Intent getIntent();
+    method @Nullable public android.app.PendingIntent getPendingIntent();
+    method @Nullable public CharSequence getSubtitle();
+    method @NonNull public CharSequence getTitle();
+    method @Nullable public android.os.UserHandle getUserHandle();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceAction> CREATOR;
+  }
+
+  public static final class SmartspaceAction.Builder {
+    ctor public SmartspaceAction.Builder(@NonNull String, @NonNull String);
+    method @NonNull public android.app.smartspace.SmartspaceAction build();
+    method @NonNull public android.app.smartspace.SmartspaceAction.Builder setContentDescription(@Nullable CharSequence);
+    method @NonNull public android.app.smartspace.SmartspaceAction.Builder setExtras(@Nullable android.os.Bundle);
+    method @NonNull public android.app.smartspace.SmartspaceAction.Builder setIcon(@Nullable android.graphics.drawable.Icon);
+    method @NonNull public android.app.smartspace.SmartspaceAction.Builder setIntent(@Nullable android.content.Intent);
+    method @NonNull public android.app.smartspace.SmartspaceAction.Builder setPendingIntent(@Nullable android.app.PendingIntent);
+    method @NonNull public android.app.smartspace.SmartspaceAction.Builder setSubtitle(@Nullable CharSequence);
+    method @NonNull public android.app.smartspace.SmartspaceAction.Builder setUserHandle(@Nullable android.os.UserHandle);
+  }
+
+  public final class SmartspaceConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.os.Bundle getExtras();
+    method @NonNull public String getPackageName();
+    method @NonNull public int getSmartspaceTargetCount();
+    method @NonNull public String getUiSurface();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceConfig> CREATOR;
+  }
+
+  public static final class SmartspaceConfig.Builder {
+    ctor public SmartspaceConfig.Builder(@NonNull android.content.Context, @NonNull String);
+    method @NonNull public android.app.smartspace.SmartspaceConfig build();
+    method @NonNull public android.app.smartspace.SmartspaceConfig.Builder setExtras(@NonNull android.os.Bundle);
+    method @NonNull public android.app.smartspace.SmartspaceConfig.Builder setSmartspaceTargetCount(int);
+  }
+
+  public final class SmartspaceManager {
+    method @NonNull public android.app.smartspace.SmartspaceSession createSmartspaceSession(@NonNull android.app.smartspace.SmartspaceConfig);
+  }
+
+  public final class SmartspaceSession implements java.lang.AutoCloseable {
+    method public void close();
+    method public void destroy();
+    method protected void finalize();
+    method public void notifySmartspaceEvent(@NonNull android.app.smartspace.SmartspaceTargetEvent);
+    method public void registerSmartspaceUpdates(@NonNull java.util.concurrent.Executor, @NonNull android.app.smartspace.SmartspaceSession.Callback);
+    method public void requestSmartspaceUpdate();
+    method public void unregisterSmartspaceUpdates(@NonNull android.app.smartspace.SmartspaceSession.Callback);
+  }
+
+  public static interface SmartspaceSession.Callback {
+    method public void onTargetsAvailable(@NonNull java.util.List<android.app.smartspace.SmartspaceTarget>);
+  }
+
+  public final class SmartspaceSessionId implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public String getId();
+    method @NonNull public int getUserId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceSessionId> CREATOR;
+  }
+
+  public final class SmartspaceTarget implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.List<android.app.smartspace.SmartspaceAction> getActionChips();
+    method @Nullable public String getAssociatedSmartspaceTargetId();
+    method @Nullable public android.app.smartspace.SmartspaceAction getBaseAction();
+    method @NonNull public android.content.ComponentName getComponentName();
+    method @NonNull public long getCreationTimeMillis();
+    method @NonNull public long getExpiryTimeMillis();
+    method @NonNull public int getFeatureType();
+    method @Nullable public android.app.smartspace.SmartspaceAction getHeaderAction();
+    method @NonNull public java.util.List<android.app.smartspace.SmartspaceAction> getIconGrid();
+    method @NonNull public float getScore();
+    method @Nullable public android.net.Uri getSliceUri();
+    method @NonNull public String getSmartspaceTargetId();
+    method @Nullable public String getSourceNotificationKey();
+    method @NonNull public android.os.UserHandle getUserHandle();
+    method @Nullable public android.appwidget.AppWidgetProviderInfo getWidgetId();
+    method @NonNull public boolean isSensitive();
+    method @NonNull public boolean shouldShowExpanded();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceTarget> CREATOR;
+    field public static final int FEATURE_ALARM = 7; // 0x7
+    field public static final int FEATURE_BEDTIME_ROUTINE = 16; // 0x10
+    field public static final int FEATURE_CALENDAR = 2; // 0x2
+    field public static final int FEATURE_COMMUTE_TIME = 3; // 0x3
+    field public static final int FEATURE_CONSENT = 11; // 0xb
+    field public static final int FEATURE_ETA_MONITORING = 18; // 0x12
+    field public static final int FEATURE_FITNESS_TRACKING = 17; // 0x11
+    field public static final int FEATURE_FLIGHT = 4; // 0x4
+    field public static final int FEATURE_LOYALTY_CARD = 14; // 0xe
+    field public static final int FEATURE_MEDIA = 15; // 0xf
+    field public static final int FEATURE_MISSED_CALL = 19; // 0x13
+    field public static final int FEATURE_ONBOARDING = 8; // 0x8
+    field public static final int FEATURE_PACKAGE_TRACKING = 20; // 0x14
+    field public static final int FEATURE_REMINDER = 6; // 0x6
+    field public static final int FEATURE_SHOPPING_LIST = 13; // 0xd
+    field public static final int FEATURE_SPORTS = 9; // 0x9
+    field public static final int FEATURE_STOCK_PRICE_CHANGE = 12; // 0xc
+    field public static final int FEATURE_STOPWATCH = 22; // 0x16
+    field public static final int FEATURE_TIMER = 21; // 0x15
+    field public static final int FEATURE_TIPS = 5; // 0x5
+    field public static final int FEATURE_UNDEFINED = 0; // 0x0
+    field public static final int FEATURE_UPCOMING_ALARM = 23; // 0x17
+    field public static final int FEATURE_WEATHER = 1; // 0x1
+    field public static final int FEATURE_WEATHER_ALERT = 10; // 0xa
+  }
+
+  public static final class SmartspaceTarget.Builder {
+    ctor public SmartspaceTarget.Builder(@NonNull String, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
+    method @NonNull public android.app.smartspace.SmartspaceTarget build();
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setActionChips(@NonNull java.util.List<android.app.smartspace.SmartspaceAction>);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setAssociatedSmartspaceTargetId(@NonNull String);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setBaseAction(@NonNull android.app.smartspace.SmartspaceAction);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setCreationTimeMillis(@NonNull long);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setExpiryTimeMillis(@NonNull long);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setFeatureType(@NonNull int);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setHeaderAction(@NonNull android.app.smartspace.SmartspaceAction);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setIconGrid(@NonNull java.util.List<android.app.smartspace.SmartspaceAction>);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setScore(@NonNull float);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSensitive(@NonNull boolean);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setShouldShowExpanded(@NonNull boolean);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSliceUri(@NonNull android.net.Uri);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setSourceNotificationKey(@NonNull String);
+    method @NonNull public android.app.smartspace.SmartspaceTarget.Builder setWidgetId(@NonNull android.appwidget.AppWidgetProviderInfo);
+  }
+
+  public final class SmartspaceTargetEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public int getEventType();
+    method @Nullable public String getSmartspaceActionId();
+    method @Nullable public android.app.smartspace.SmartspaceTarget getSmartspaceTarget();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceTargetEvent> CREATOR;
+    field public static final int EVENT_TARGET_BLOCK = 5; // 0x5
+    field public static final int EVENT_TARGET_DISMISS = 4; // 0x4
+    field public static final int EVENT_TARGET_INTERACTION = 1; // 0x1
+    field public static final int EVENT_TARGET_IN_VIEW = 2; // 0x2
+    field public static final int EVENT_TARGET_OUT_OF_VIEW = 3; // 0x3
+    field public static final int EVENT_UI_SURFACE_IN_VIEW = 6; // 0x6
+    field public static final int EVENT_UI_SURFACE_OUT_OF_VIEW = 7; // 0x7
+  }
+
+  public static final class SmartspaceTargetEvent.Builder {
+    ctor public SmartspaceTargetEvent.Builder(int);
+    method @NonNull public android.app.smartspace.SmartspaceTargetEvent build();
+    method @NonNull public android.app.smartspace.SmartspaceTargetEvent.Builder setSmartspaceActionId(@NonNull String);
+    method @NonNull public android.app.smartspace.SmartspaceTargetEvent.Builder setSmartspaceTarget(@NonNull android.app.smartspace.SmartspaceTarget);
+  }
+
+}
+
 package android.app.time {
 
   public final class TimeManager {
@@ -1953,6 +2117,7 @@
     field public static final String ROLLBACK_SERVICE = "rollback";
     field public static final String SEARCH_UI_SERVICE = "search_ui";
     field public static final String SECURE_ELEMENT_SERVICE = "secure_element";
+    field public static final String SMARTSPACE_SERVICE = "smartspace";
     field public static final String STATS_MANAGER = "stats";
     field public static final String STATUS_BAR_SERVICE = "statusbar";
     field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
@@ -10057,6 +10222,21 @@
 
 }
 
+package android.service.smartspace {
+
+  public abstract class SmartspaceService extends android.app.Service {
+    ctor public SmartspaceService();
+    method @MainThread public abstract void notifySmartspaceEvent(@NonNull android.app.smartspace.SmartspaceSessionId, @NonNull android.app.smartspace.SmartspaceTargetEvent);
+    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public abstract void onCreateSmartspaceSession(@NonNull android.app.smartspace.SmartspaceConfig, @NonNull android.app.smartspace.SmartspaceSessionId);
+    method @MainThread public abstract void onDestroy(@NonNull android.app.smartspace.SmartspaceSessionId);
+    method public abstract void onDestroySmartspaceSession(@NonNull android.app.smartspace.SmartspaceSessionId);
+    method @MainThread public abstract void onRequestSmartspaceUpdate(@NonNull android.app.smartspace.SmartspaceSessionId);
+    method public final void updateSmartspaceTargets(@NonNull android.app.smartspace.SmartspaceSessionId, @NonNull java.util.List<android.app.smartspace.SmartspaceTarget>);
+  }
+
+}
+
 package android.service.storage {
 
   public abstract class ExternalStorageService extends android.app.Service {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 8b3cee4..58f6c52 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -6,9 +6,11 @@
 BuilderSetStyle: android.net.IpSecTransform.Builder#buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex):
     Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.IpSecTransform.Builder.buildTunnelModeTransform(java.net.InetAddress,android.net.IpSecManager.SecurityParameterIndex)
 
+
 ExecutorRegistration: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler):
     Registration methods should have overload that accepts delivery Executor: `setOnRtpRxNoticeListener`
-    
+
+
 GenericException: android.app.prediction.AppPredictor#finalize():
     
 GenericException: android.hardware.location.ContextHubClient#finalize():
@@ -21,6 +23,8 @@
 
 IntentBuilderName: android.app.search.SearchAction#getIntent():
     
+IntentBuilderName: android.app.smartspace.SmartspaceAction#getIntent():
+    Methods creating an Intent should be named `create<Foo>Intent()`, was `getIntent`
 
 
 KotlinKeyword: android.app.Notification#when:
@@ -83,6 +87,10 @@
     
 
 
+OnNameExpected: android.service.smartspace.SmartspaceService#notifySmartspaceEvent(android.app.smartspace.SmartspaceSessionId, android.app.smartspace.SmartspaceTargetEvent):
+    Methods implemented by developers should follow the on<Something> style, was `notifySmartspaceEvent`
+
+
 ProtectedMember: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context):
     
 ProtectedMember: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]):
@@ -187,11 +195,10 @@
     
 SamShouldBeLast: android.media.AudioRouting#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
     
-SamShouldBeLast: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler):
-    SAM-compatible parameters (such as parameter 2, "listener", in android.media.MediaPlayer.setOnRtpRxNoticeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
-    
 SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
     
+SamShouldBeLast: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler):
+    SAM-compatible parameters (such as parameter 2, "listener", in android.media.MediaPlayer.setOnRtpRxNoticeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.media.MediaRecorder#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
     
 SamShouldBeLast: android.media.MediaRecorder#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback):
@@ -258,3 +265,5 @@
     Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `setUserHandle`
 UserHandleName: android.app.search.SearchTarget.Builder#setUserHandle(android.os.UserHandle):
     
+UserHandleName: android.app.smartspace.SmartspaceAction.Builder#setUserHandle(android.os.UserHandle):
+    Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `setUserHandle`
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index d5f0149..518feee 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -34,6 +34,7 @@
 import android.app.role.RoleFrameworkInitializer;
 import android.app.search.SearchUiManager;
 import android.app.slice.SliceManager;
+import android.app.smartspace.SmartspaceManager;
 import android.app.time.TimeManager;
 import android.app.timedetector.TimeDetector;
 import android.app.timedetector.TimeDetectorImpl;
@@ -1223,6 +1224,16 @@
                 }
             });
 
+        registerService(Context.SMARTSPACE_SERVICE, SmartspaceManager.class,
+            new CachedServiceFetcher<SmartspaceManager>() {
+                @Override
+                public SmartspaceManager createService(ContextImpl ctx)
+                    throws ServiceNotFoundException {
+                    IBinder b = ServiceManager.getService(Context.SMARTSPACE_SERVICE);
+                    return b == null ? null : new SmartspaceManager(ctx);
+                }
+            });
+
         registerService(Context.APP_PREDICTION_SERVICE, AppPredictionManager.class,
                 new CachedServiceFetcher<AppPredictionManager>() {
             @Override
diff --git a/core/java/android/app/smartspace/ISmartspaceCallback.aidl b/core/java/android/app/smartspace/ISmartspaceCallback.aidl
new file mode 100644
index 0000000..df105f9
--- /dev/null
+++ b/core/java/android/app/smartspace/ISmartspaceCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.app.smartspace;
+
+import android.content.pm.ParceledListSlice;
+
+/**
+ * @hide
+ */
+oneway interface ISmartspaceCallback {
+
+    void onResult(in ParceledListSlice result);
+}
diff --git a/core/java/android/app/smartspace/ISmartspaceManager.aidl b/core/java/android/app/smartspace/ISmartspaceManager.aidl
new file mode 100644
index 0000000..e7ec889
--- /dev/null
+++ b/core/java/android/app/smartspace/ISmartspaceManager.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.app.smartspace;
+
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.ISmartspaceCallback;
+import android.content.pm.ParceledListSlice;
+
+/**
+ * @hide
+ */
+interface ISmartspaceManager {
+
+    void createSmartspaceSession(in SmartspaceConfig config, in SmartspaceSessionId sessionId,
+            in IBinder token);
+
+    void notifySmartspaceEvent(in SmartspaceSessionId sessionId, in SmartspaceTargetEvent event);
+
+    void requestSmartspaceUpdate(in SmartspaceSessionId sessionId);
+
+    void registerSmartspaceUpdates(in SmartspaceSessionId sessionId,
+            in ISmartspaceCallback callback);
+
+    void unregisterSmartspaceUpdates(in SmartspaceSessionId sessionId,
+            in ISmartspaceCallback callback);
+
+    void destroySmartspaceSession(in SmartspaceSessionId sessionId);
+}
diff --git a/core/java/android/app/smartspace/SmartspaceAction.java b/core/java/android/app/smartspace/SmartspaceAction.java
new file mode 100644
index 0000000..033cda1
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceAction.java
@@ -0,0 +1,353 @@
+/*
+ * 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.app.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * A {@link SmartspaceAction} represents an action which can be taken by a user by tapping on either
+ * the title, the subtitle or on the icon. Supported instances are Intents, PendingIntents or a
+ * ShortcutInfo (by putting the ShortcutInfoId in the bundle). These actions can be called from
+ * another process or within the client process.
+ *
+ * Clients can also receive conditional Intents/PendingIntents in the extras bundle which are
+ * supposed to be fired when the conditions are met. For example, a user can invoke a dismiss/block
+ * action on a game score card but the intention is to only block the team and not the entire
+ * feature.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceAction implements Parcelable {
+
+    private static final String TAG = "SmartspaceAction";
+
+    /** A unique Id of this {@link SmartspaceAction}. */
+    @NonNull
+    private final String mId;
+
+    /** An Icon which can be displayed in the UI. */
+    @Nullable
+    private final Icon mIcon;
+
+    /** Title associated with an action. */
+    @NonNull
+    private final CharSequence mTitle;
+
+    /** Subtitle associated with an action. */
+    @Nullable
+    private final CharSequence mSubtitle;
+
+    @Nullable
+    private final CharSequence mContentDescription;
+
+    @Nullable
+    private final PendingIntent mPendingIntent;
+
+    @Nullable
+    private final Intent mIntent;
+
+    @Nullable
+    private final UserHandle mUserHandle;
+
+    @Nullable
+    private Bundle mExtras;
+
+    SmartspaceAction(Parcel in) {
+        mId = in.readString();
+        mIcon = in.readTypedObject(Icon.CREATOR);
+        mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+        mIntent = in.readTypedObject(Intent.CREATOR);
+        mUserHandle = in.readTypedObject(UserHandle.CREATOR);
+        mExtras = in.readTypedObject(Bundle.CREATOR);
+    }
+
+    private SmartspaceAction(
+            @NonNull String id,
+            @Nullable Icon icon,
+            @NonNull CharSequence title,
+            @Nullable CharSequence subtitle,
+            @Nullable CharSequence contentDescription,
+            @Nullable PendingIntent pendingIntent,
+            @Nullable Intent intent,
+            @Nullable UserHandle userHandle,
+            @Nullable Bundle extras) {
+        mId = Objects.requireNonNull(id);
+        mIcon = icon;
+        mTitle = Objects.requireNonNull(title);
+        mSubtitle = subtitle;
+        mContentDescription = contentDescription;
+        mPendingIntent = pendingIntent;
+        mIntent = intent;
+        mUserHandle = userHandle;
+        mExtras = extras;
+    }
+
+    /**
+     * Returns the unique id of this object.
+     */
+    public @NonNull String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns an icon representing the action.
+     */
+    public @Nullable Icon getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Returns a title representing the action.
+     */
+    public @NonNull CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Returns a subtitle representing the action.
+     */
+    public @Nullable CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    /**
+     * Returns a content description representing the action.
+     */
+    public @Nullable CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+    /**
+     * Returns the action intent.
+     */
+    public @Nullable PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
+
+    /**
+     * Returns the intent.
+     */
+    public @Nullable Intent getIntent() {
+        return mIntent;
+    }
+
+    /**
+     * Returns the user handle.
+     */
+    public @Nullable UserHandle getUserHandle() {
+        return mUserHandle;
+    }
+
+    /**
+     * Returns the extra bundle for this object.
+     */
+    public @Nullable Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SmartspaceAction)) return false;
+        SmartspaceAction that = (SmartspaceAction) o;
+        return mId.equals(that.mId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mId);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(mId);
+        out.writeTypedObject(mIcon, flags);
+        TextUtils.writeToParcel(mTitle, out, flags);
+        TextUtils.writeToParcel(mSubtitle, out, flags);
+        TextUtils.writeToParcel(mContentDescription, out, flags);
+        out.writeTypedObject(mPendingIntent, flags);
+        out.writeTypedObject(mIntent, flags);
+        out.writeTypedObject(mUserHandle, flags);
+        out.writeBundle(mExtras);
+    }
+
+    @Override
+    public String toString() {
+        return "SmartspaceAction{"
+                + "mId='" + mId + '\''
+                + ", mIcon=" + mIcon
+                + ", mTitle=" + mTitle
+                + ", mSubtitle=" + mSubtitle
+                + ", mContentDescription=" + mContentDescription
+                + ", mPendingIntent=" + mPendingIntent
+                + ", mIntent=" + mIntent
+                + ", mUserHandle=" + mUserHandle
+                + ", mExtras=" + mExtras
+                + '}';
+    }
+
+    public static final @NonNull Creator<SmartspaceAction> CREATOR =
+            new Creator<SmartspaceAction>() {
+                public SmartspaceAction createFromParcel(Parcel in) {
+                    return new SmartspaceAction(in);
+                }
+                public SmartspaceAction[] newArray(int size) {
+                    return new SmartspaceAction[size];
+                }
+            };
+
+    /**
+     * A builder for Smartspace action object.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        @NonNull
+        private String mId;
+
+        @Nullable
+        private Icon mIcon;
+
+        @NonNull
+        private CharSequence mTitle;
+
+        @Nullable
+        private CharSequence mSubtitle;
+
+        @Nullable
+        private CharSequence mContentDescription;
+
+        @Nullable
+        private PendingIntent mPendingIntent;
+
+        @Nullable
+        private Intent mIntent;
+
+        @Nullable
+        private UserHandle mUserHandle;
+
+        @Nullable
+        private Bundle mExtras;
+
+        /**
+         * Id and title are required.
+         */
+        public Builder(@NonNull String id, @NonNull String title) {
+            mId = Objects.requireNonNull(id);
+            mTitle = Objects.requireNonNull(title);
+        }
+
+        /**
+         * Sets the icon.
+         */
+        @NonNull
+        public Builder setIcon(
+                @Nullable Icon icon) {
+            mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Sets the subtitle.
+         */
+        @NonNull
+        public Builder setSubtitle(
+                @Nullable CharSequence subtitle) {
+            mSubtitle = subtitle;
+            return this;
+        }
+
+        /**
+         * Sets the content description.
+         */
+        @NonNull
+        public Builder setContentDescription(
+                @Nullable CharSequence contentDescription) {
+            mContentDescription = contentDescription;
+            return this;
+        }
+
+        /**
+         * Sets the pending intent.
+         */
+        @NonNull
+        public Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
+            mPendingIntent = pendingIntent;
+            return this;
+        }
+
+        /**
+         * Sets the user handle.
+         */
+        @NonNull
+        public Builder setUserHandle(@Nullable UserHandle userHandle) {
+            mUserHandle = userHandle;
+            return this;
+        }
+
+        /**
+         * Sets the intent.
+         */
+        @NonNull
+        public Builder setIntent(@Nullable Intent intent) {
+            mIntent = intent;
+            return this;
+        }
+
+        /**
+         * Sets the extra.
+         */
+        @NonNull
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds a new SmartspaceAction instance.
+         *
+         * @throws IllegalStateException if no target is set
+         */
+        @NonNull
+        public SmartspaceAction build() {
+            return new SmartspaceAction(mId, mIcon, mTitle, mSubtitle, mContentDescription,
+                    mPendingIntent, mIntent, mUserHandle, mExtras);
+        }
+    }
+}
diff --git a/core/java/android/app/smartspace/SmartspaceConfig.aidl b/core/java/android/app/smartspace/SmartspaceConfig.aidl
new file mode 100644
index 0000000..136b6f4
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceConfig.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.smartspace;
+
+parcelable SmartspaceConfig;
\ No newline at end of file
diff --git a/core/java/android/app/smartspace/SmartspaceConfig.java b/core/java/android/app/smartspace/SmartspaceConfig.java
new file mode 100644
index 0000000..1f9cbb5
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceConfig.java
@@ -0,0 +1,202 @@
+/*
+ * 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.app.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A {@link SmartspaceConfig} instance is supposed to be created by a smartspace client for each
+ * UISurface. The client can specify some initialization conditions for the UISurface like its name,
+ * expected number of smartspace cards etc. The clients can also specify if they want periodic
+ * updates or their desired maximum refresh frequency.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceConfig implements Parcelable {
+
+    /**
+     * The least number of smartspace targets expected to be predicted by the backend. The backend
+     * will always try to satisfy this threshold but it is not guaranteed to always meet it.
+     */
+    private final int mSmartspaceTargetCount;
+
+    /**
+     * A {@link mUiSurface} is the name of the surface which will be used to display the cards. A
+     * few examples are homescreen, lockscreen, aod etc.
+     */
+    @NonNull
+    private final String mUiSurface;
+
+    /** Package name of the client. */
+    @NonNull
+    private String mPackageName;
+
+    /** Send other client UI configurations in extras.
+     *
+     * This can include:
+     *
+     *  - Desired maximum update frequency
+     *  - Request to get periodic updates
+     *  - Request to support multiple clients for the same UISurface.
+     */
+    @Nullable
+    private final Bundle mExtras;
+
+    private SmartspaceConfig(@NonNull String uiSurface, int numPredictedTargets,
+            @NonNull String packageName, @Nullable Bundle extras) {
+        mUiSurface = uiSurface;
+        mSmartspaceTargetCount = numPredictedTargets;
+        mPackageName = packageName;
+        mExtras = extras;
+    }
+
+    private SmartspaceConfig(Parcel parcel) {
+        mUiSurface = parcel.readString();
+        mSmartspaceTargetCount = parcel.readInt();
+        mPackageName = parcel.readString();
+        mExtras = parcel.readBundle();
+    }
+
+    /** Returns the package name of the prediction context. */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /** Returns the number of smartspace targets requested by the user. */
+    @NonNull
+    public int getSmartspaceTargetCount() {
+        return mSmartspaceTargetCount;
+    }
+
+    /** Returns the UISurface requested by the client. */
+    @NonNull
+    public String getUiSurface() {
+        return mUiSurface;
+    }
+
+    @Nullable
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mUiSurface);
+        dest.writeInt(mSmartspaceTargetCount);
+        dest.writeString(mPackageName);
+        dest.writeBundle(mExtras);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SmartspaceConfig that = (SmartspaceConfig) o;
+        return mSmartspaceTargetCount == that.mSmartspaceTargetCount
+                && Objects.equals(mUiSurface, that.mUiSurface)
+                && Objects.equals(mPackageName, that.mPackageName)
+                && Objects.equals(mExtras, that.mExtras);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSmartspaceTargetCount, mUiSurface, mPackageName, mExtras);
+    }
+
+    /**
+     * @see Creator
+     */
+    @NonNull
+    public static final Creator<SmartspaceConfig> CREATOR =
+            new Creator<SmartspaceConfig>() {
+                public SmartspaceConfig createFromParcel(Parcel parcel) {
+                    return new SmartspaceConfig(parcel);
+                }
+
+                public SmartspaceConfig[] newArray(int size) {
+                    return new SmartspaceConfig[size];
+                }
+            };
+
+    /**
+     * A builder for {@link SmartspaceConfig}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        @NonNull
+        private int mSmartspaceTargetCount = 5; // Default count is 5
+        @NonNull
+        private final String mUiSurface;
+        @NonNull
+        private final String mPackageName;
+        @NonNull
+        private Bundle mExtras = Bundle.EMPTY;
+
+        /**
+         * @param context The {@link Context} which is used to fetch the package name.
+         * @param uiSurface the UI Surface name associated with this context.
+         * @hide
+         */
+        @SystemApi
+        public Builder(@NonNull Context context, @NonNull String uiSurface) {
+            mPackageName = context.getPackageName();
+            this.mUiSurface = uiSurface;
+        }
+
+        /**
+         * Used to set the expected number of cards for this context.
+         */
+        @NonNull
+        public Builder setSmartspaceTargetCount(int smartspaceTargetCount) {
+            this.mSmartspaceTargetCount = smartspaceTargetCount;
+            return this;
+        }
+
+        /**
+         * Used to send a bundle containing extras for the {@link SmartspaceConfig}.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle extras) {
+            this.mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Returns an instance of {@link SmartspaceConfig}.
+         */
+        @NonNull
+        public SmartspaceConfig build() {
+            return new SmartspaceConfig(mUiSurface, mSmartspaceTargetCount, mPackageName, mExtras);
+        }
+    }
+}
diff --git a/core/java/android/app/smartspace/SmartspaceManager.java b/core/java/android/app/smartspace/SmartspaceManager.java
new file mode 100644
index 0000000..ff5eb10
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceManager.java
@@ -0,0 +1,64 @@
+/*
+ * 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.app.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+
+import java.util.Objects;
+
+/**
+ * Smartspace is a container in Android which is used to show contextual content powered by the
+ * intelligence service running on the device. A smartspace container can be on AoD, lockscreen or
+ * on the homescreen and can show personalized cards which are either derived from on device or
+ * online signals.
+ *
+ * {@link SmartspaceManager} is a system service that provides methods to create Smartspace session
+ * clients. An instance of this class is returned when a client calls
+ * <code> context.getSystemService("smartspace"); </code>.
+ *
+ * After receiving the service, a client must call
+ * {@link SmartspaceManager#createSmartspaceSession(SmartspaceConfig)} with a corresponding
+ * {@link SmartspaceConfig} to get an instance of {@link SmartspaceSession}.
+ * This session is then a client's point of contact with the api. They can send events, request for
+ * updates using the session. It is client's duty to call {@link SmartspaceSession#destroy()} to
+ * destroy the session once they no longer need it.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceManager {
+
+    private final Context mContext;
+
+    /**
+     * @hide
+     */
+    public SmartspaceManager(Context context) {
+        mContext = Objects.requireNonNull(context);
+    }
+
+    /**
+     * Creates a new Smartspace session.
+     */
+    @NonNull
+    public SmartspaceSession createSmartspaceSession(
+            @NonNull SmartspaceConfig smartspaceConfig) {
+        return new SmartspaceSession(mContext, smartspaceConfig);
+    }
+}
diff --git a/core/java/android/app/smartspace/SmartspaceSession.java b/core/java/android/app/smartspace/SmartspaceSession.java
new file mode 100644
index 0000000..16def61
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceSession.java
@@ -0,0 +1,284 @@
+/*
+ * 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.app.smartspace;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.smartspace.ISmartspaceCallback.Stub;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import dalvik.system.CloseGuard;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+/**
+ * Client API to share information about the Smartspace UI state and execute query.
+ *
+ * <p>
+ * Usage: <pre> {@code
+ *
+ * class MyActivity {
+ *    private SmartspaceSession mSmartspaceSession;
+ *
+ *    void onCreate() {
+ *         mSmartspaceSession = mSmartspaceManager.createSmartspaceSession(smartspaceConfig)
+ *         mSmartspaceSession.registerSmartspaceUpdates(...)
+ *    }
+ *
+ *    void onStart() {
+ *        mSmartspaceSession.requestSmartspaceUpdate()
+ *    }
+ *
+ *    void onTouch(...) OR
+ *    void onStateTransitionStarted(...) OR
+ *    void onResume(...) OR
+ *    void onStop(...) {
+ *        mSmartspaceSession.notifyEvent(event);
+ *    }
+ *
+ *    void onDestroy() {
+ *        mSmartspaceSession.unregisterPredictionUpdates()
+ *        mSmartspaceSession.destroy();
+ *    }
+ *
+ * }</pre>
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceSession implements AutoCloseable {
+
+    private static final String TAG = SmartspaceSession.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    private final android.app.smartspace.ISmartspaceManager mInterface;
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+    private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
+
+    private final SmartspaceSessionId mSessionId;
+    private final ArrayMap<Callback, CallbackWrapper> mRegisteredCallbacks = new ArrayMap<>();
+    private final IBinder mToken = new Binder();
+
+    /**
+     * Creates a new Smartspace ui client.
+     * <p>
+     * The caller should call {@link SmartspaceSession#destroy()} to dispose the client once it
+     * no longer used.
+     *
+     * @param context          the {@link Context} of the user of this {@link SmartspaceSession}.
+     * @param smartspaceConfig the Smartspace context.
+     */
+    // b/177858121 Create weak reference child objects to not leak context.
+    SmartspaceSession(@NonNull Context context, @NonNull SmartspaceConfig smartspaceConfig) {
+        IBinder b = ServiceManager.getService(Context.SMARTSPACE_SERVICE);
+        mInterface = android.app.smartspace.ISmartspaceManager.Stub.asInterface(b);
+        mSessionId = new SmartspaceSessionId(
+                context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId());
+        try {
+            mInterface.createSmartspaceSession(smartspaceConfig, mSessionId, mToken);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to cerate Smartspace session", e);
+            e.rethrowFromSystemServer();
+        }
+
+        mCloseGuard.open("close");
+    }
+
+    /**
+     * Notifies the Smartspace service of a Smartspace target event.
+     *
+     * @param event The {@link SmartspaceTargetEvent} that represents the Smartspace target event.
+     */
+    public void notifySmartspaceEvent(@NonNull SmartspaceTargetEvent event) {
+        if (mIsClosed.get()) {
+            throw new IllegalStateException("This client has already been destroyed.");
+        }
+        try {
+            mInterface.notifySmartspaceEvent(mSessionId, event);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to notify event", e);
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Requests the smartspace service for an update.
+     */
+    public void requestSmartspaceUpdate() {
+        if (mIsClosed.get()) {
+            throw new IllegalStateException("This client has already been destroyed.");
+        }
+        try {
+            mInterface.requestSmartspaceUpdate(mSessionId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to request update.", e);
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Requests the smartspace service provide continuous updates of smartspace cards via the
+     * provided callback, until the given callback is unregistered.
+     *
+     * @param callbackExecutor The callback executor to use when calling the callback.
+     * @param callback         The Callback to be called when updates of Smartspace targets are
+     *                         available.
+     */
+    public void registerSmartspaceUpdates(@NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull Callback callback) {
+        if (mIsClosed.get()) {
+            throw new IllegalStateException("This client has already been destroyed.");
+        }
+
+        if (mRegisteredCallbacks.containsKey(callback)) {
+            // Skip if this callback is already registered
+            return;
+        }
+        try {
+            final CallbackWrapper callbackWrapper = new CallbackWrapper(callbackExecutor,
+                    callback::onTargetsAvailable);
+            mRegisteredCallbacks.put(callback, callbackWrapper);
+            mInterface.registerSmartspaceUpdates(mSessionId, callbackWrapper);
+            mInterface.requestSmartspaceUpdate(mSessionId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to register for smartspace updates", e);
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Requests the smartspace service to stop providing continuous updates to the provided
+     * callback until the callback is re-registered.
+     *
+     * @see {@link SmartspaceSession#registerSmartspaceUpdates(Executor, Callback)}.
+     *
+     * @param callback The callback to be unregistered.
+     */
+    public void unregisterSmartspaceUpdates(@NonNull Callback callback) {
+        if (mIsClosed.get()) {
+            throw new IllegalStateException("This client has already been destroyed.");
+        }
+
+        if (!mRegisteredCallbacks.containsKey(callback)) {
+            // Skip if this callback was never registered
+            return;
+        }
+        try {
+            final CallbackWrapper callbackWrapper = mRegisteredCallbacks.remove(callback);
+            mInterface.unregisterSmartspaceUpdates(mSessionId, callbackWrapper);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to unregister for smartspace updates", e);
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Destroys the client and unregisters the callback. Any method on this class after this call
+     * will throw {@link IllegalStateException}.
+     */
+    public void destroy() {
+        if (!mIsClosed.getAndSet(true)) {
+            mCloseGuard.close();
+
+            // Do destroy;
+            try {
+                mInterface.destroySmartspaceSession(mSessionId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to notify Smartspace target event", e);
+                e.rethrowFromSystemServer();
+            }
+        } else {
+            throw new IllegalStateException("This client has already been destroyed.");
+        }
+    }
+
+    @Override
+    protected void finalize() {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            if (!mIsClosed.get()) {
+                destroy();
+            }
+        } finally {
+            try {
+                super.finalize();
+            } catch (Throwable throwable) {
+                throwable.printStackTrace();
+            }
+        }
+    }
+
+    @Override
+    public void close() {
+        try {
+            finalize();
+        } catch (Throwable throwable) {
+            throwable.printStackTrace();
+        }
+    }
+
+    /**
+     * Callback for receiving smartspace updates.
+     */
+    public interface Callback {
+
+        /**
+         * Called when a new set of smartspace targets are available.
+         *
+         * @param targets Sorted list of smartspace targets.
+         */
+        void onTargetsAvailable(@NonNull List<SmartspaceTarget> targets);
+    }
+
+    static class CallbackWrapper extends Stub {
+
+        private final Consumer<List<SmartspaceTarget>> mCallback;
+        private final Executor mExecutor;
+
+        CallbackWrapper(@NonNull Executor callbackExecutor,
+                @NonNull Consumer<List<SmartspaceTarget>> callback) {
+            mCallback = callback;
+            mExecutor = callbackExecutor;
+        }
+
+        @Override
+        public void onResult(ParceledListSlice result) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG) {
+                    Log.d(TAG, "CallbackWrapper.onResult result=" + result.getList());
+                }
+                mExecutor.execute(() -> mCallback.accept(result.getList()));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/smartspace/SmartspaceSessionId.aidl b/core/java/android/app/smartspace/SmartspaceSessionId.aidl
new file mode 100644
index 0000000..a864412
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceSessionId.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.smartspace;
+
+parcelable SmartspaceSessionId;
\ No newline at end of file
diff --git a/core/java/android/app/smartspace/SmartspaceSessionId.java b/core/java/android/app/smartspace/SmartspaceSessionId.java
new file mode 100644
index 0000000..5220c35
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceSessionId.java
@@ -0,0 +1,114 @@
+/*
+ * 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.app.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * The id for an Smartspace session. See {@link SmartspaceSession}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceSessionId implements Parcelable {
+
+    @NonNull
+    private final String mId;
+
+    @NonNull
+    private final int mUserId;
+
+    /**
+     * Creates a new id for a Smartspace session.
+     *
+     * @hide
+     */
+    public SmartspaceSessionId(@NonNull final String id, @NonNull final int userId) {
+        mId = id;
+        mUserId = userId;
+    }
+
+    private SmartspaceSessionId(Parcel p) {
+        mId = p.readString();
+        mUserId = p.readInt();
+    }
+
+    /**
+     * Returns a {@link String} Id of this sessionId.
+     */
+    @Nullable
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the userId associated with this sessionId.
+     */
+    @NonNull
+    public int getUserId() {
+        return mUserId;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (!getClass().equals(o != null ? o.getClass() : null)) return false;
+
+        SmartspaceSessionId other = (SmartspaceSessionId) o;
+        return mId.equals(other.mId) && mUserId == other.mUserId;
+    }
+
+    @Override
+    public String toString() {
+        return "SmartspaceSessionId{"
+                + "mId='" + mId + '\''
+                + ", mUserId=" + mUserId
+                + '}';
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mId, mUserId);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mId);
+        dest.writeInt(mUserId);
+    }
+
+    public static final @NonNull Creator<SmartspaceSessionId> CREATOR =
+            new Creator<SmartspaceSessionId>() {
+                public SmartspaceSessionId createFromParcel(Parcel parcel) {
+                    return new SmartspaceSessionId(parcel);
+                }
+
+                public SmartspaceSessionId[] newArray(int size) {
+                    return new SmartspaceSessionId[size];
+                }
+            };
+}
diff --git a/core/java/android/app/smartspace/SmartspaceTarget.aidl b/core/java/android/app/smartspace/SmartspaceTarget.aidl
new file mode 100644
index 0000000..3442cf2
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceTarget.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.smartspace;
+
+parcelable SmartspaceTarget;
\ No newline at end of file
diff --git a/core/java/android/app/smartspace/SmartspaceTarget.java b/core/java/android/app/smartspace/SmartspaceTarget.java
new file mode 100644
index 0000000..ce5040e
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceTarget.java
@@ -0,0 +1,660 @@
+/*
+ * 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.app.smartspace;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A {@link SmartspaceTarget} is a data class which holds all properties necessary to inflate a
+ * smartspace card. It contains data and related metadata which is supposed to be utilized by
+ * smartspace clients based on their own UI/UX requirements. Some of the properties have
+ * {@link SmartspaceAction} as their type because they can have associated actions.
+ *
+ * <p><b>NOTE: </b>
+ * If {@link mWidgetId} is set, it should be preferred over all other properties.
+ * Else, if {@link mSliceUri} is set, it should be preferred over all other data properties.
+ * Otherwise, the instance should be treated as a data object.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceTarget implements Parcelable {
+
+    /** A unique Id for an instance of {@link SmartspaceTarget}. */
+    @NonNull
+    private final String mSmartspaceTargetId;
+
+    /** A {@link SmartspaceAction} for the header in the Smartspace card. */
+    @Nullable
+    private final SmartspaceAction mHeaderAction;
+
+    /** A {@link SmartspaceAction} for the base action in the Smartspace card. */
+    @Nullable
+    private final SmartspaceAction mBaseAction;
+
+    /** A timestamp indicating when the card was created. */
+    @NonNull
+    private final long mCreationTimeMillis;
+
+    /**
+     * A timestamp indicating when the card should be removed from view, in case the service
+     * disconnects or restarts.
+     */
+    @NonNull
+    private final long mExpiryTimeMillis;
+
+    /** A score assigned to a target. */
+    @NonNull
+    private final float mScore;
+
+    /** A {@link List<SmartspaceAction>} containing all action chips. */
+    @NonNull
+    private final List<SmartspaceAction> mActionChips;
+
+    /** A {@link List<SmartspaceAction>} containing all icons for the grid. */
+    @NonNull
+    private final List<SmartspaceAction> mIconGrid;
+
+    /**
+     * {@link FeatureType} indicating the feature type of this card.
+     *
+     * @see FeatureType
+     */
+    @FeatureType
+    @NonNull
+    private final int mFeatureType;
+
+    /**
+     * Indicates whether the content is sensitive. Certain UI surfaces may choose to skip rendering
+     * real content until the device is unlocked.
+     */
+    @NonNull
+    private final boolean mSensitive;
+
+    /** Indicating if the UI should show this target in its expanded state. */
+    @NonNull
+    private final boolean mShouldShowExpanded;
+
+    /** A Notification key if the target was generated using a notification. */
+    @Nullable
+    private final String mSourceNotificationKey;
+
+    /** {@link ComponentName} for this target. */
+    @NonNull
+    private final ComponentName mComponentName;
+
+    /** {@link UserHandle} for this target. */
+    @NonNull
+    private final UserHandle mUserHandle;
+
+    /** Target Ids of other {@link SmartspaceTarget}s if they are associated with this target. */
+    @Nullable
+    private final String mAssociatedSmartspaceTargetId;
+
+    /** {@link Uri} Slice Uri if this target is a slice. */
+    @Nullable
+    private final Uri mSliceUri;
+
+    /** {@link AppWidgetProviderInfo} if this target is a widget. */
+    @Nullable
+    private final AppWidgetProviderInfo mWidgetId;
+
+    public static final int FEATURE_UNDEFINED = 0;
+    public static final int FEATURE_WEATHER = 1;
+    public static final int FEATURE_CALENDAR = 2;
+    public static final int FEATURE_COMMUTE_TIME = 3;
+    public static final int FEATURE_FLIGHT = 4;
+    public static final int FEATURE_TIPS = 5;
+    public static final int FEATURE_REMINDER = 6;
+    public static final int FEATURE_ALARM = 7;
+    public static final int FEATURE_ONBOARDING = 8;
+    public static final int FEATURE_SPORTS = 9;
+    public static final int FEATURE_WEATHER_ALERT = 10;
+    public static final int FEATURE_CONSENT = 11;
+    public static final int FEATURE_STOCK_PRICE_CHANGE = 12;
+    public static final int FEATURE_SHOPPING_LIST = 13;
+    public static final int FEATURE_LOYALTY_CARD = 14;
+    public static final int FEATURE_MEDIA = 15;
+    public static final int FEATURE_BEDTIME_ROUTINE = 16;
+    public static final int FEATURE_FITNESS_TRACKING = 17;
+    public static final int FEATURE_ETA_MONITORING = 18;
+    public static final int FEATURE_MISSED_CALL = 19;
+    public static final int FEATURE_PACKAGE_TRACKING = 20;
+    public static final int FEATURE_TIMER = 21;
+    public static final int FEATURE_STOPWATCH = 22;
+    public static final int FEATURE_UPCOMING_ALARM = 23;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"FEATURE_"}, value = {
+            FEATURE_UNDEFINED,
+            FEATURE_WEATHER,
+            FEATURE_CALENDAR,
+            FEATURE_COMMUTE_TIME,
+            FEATURE_FLIGHT,
+            FEATURE_TIPS,
+            FEATURE_REMINDER,
+            FEATURE_ALARM,
+            FEATURE_ONBOARDING,
+            FEATURE_SPORTS,
+            FEATURE_WEATHER_ALERT,
+            FEATURE_CONSENT,
+            FEATURE_STOCK_PRICE_CHANGE,
+            FEATURE_SHOPPING_LIST,
+            FEATURE_LOYALTY_CARD,
+            FEATURE_MEDIA,
+            FEATURE_BEDTIME_ROUTINE,
+            FEATURE_FITNESS_TRACKING,
+            FEATURE_ETA_MONITORING,
+            FEATURE_MISSED_CALL,
+            FEATURE_PACKAGE_TRACKING,
+            FEATURE_TIMER,
+            FEATURE_STOPWATCH,
+            FEATURE_UPCOMING_ALARM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FeatureType {
+    }
+
+    private SmartspaceTarget(Parcel in) {
+        this.mSmartspaceTargetId = in.readString();
+        this.mHeaderAction = in.readTypedObject(SmartspaceAction.CREATOR);
+        this.mBaseAction = in.readTypedObject(SmartspaceAction.CREATOR);
+        this.mCreationTimeMillis = in.readLong();
+        this.mExpiryTimeMillis = in.readLong();
+        this.mScore = in.readFloat();
+        this.mActionChips = in.createTypedArrayList(SmartspaceAction.CREATOR);
+        this.mIconGrid = in.createTypedArrayList(SmartspaceAction.CREATOR);
+        this.mFeatureType = in.readInt();
+        this.mSensitive = in.readBoolean();
+        this.mShouldShowExpanded = in.readBoolean();
+        this.mSourceNotificationKey = in.readString();
+        this.mComponentName = in.readTypedObject(ComponentName.CREATOR);
+        this.mUserHandle = in.readTypedObject(UserHandle.CREATOR);
+        this.mAssociatedSmartspaceTargetId = in.readString();
+        this.mSliceUri = in.readTypedObject(Uri.CREATOR);
+        this.mWidgetId = in.readTypedObject(AppWidgetProviderInfo.CREATOR);
+    }
+
+    private SmartspaceTarget(String smartspaceTargetId,
+            SmartspaceAction headerAction, SmartspaceAction baseAction, long creationTimeMillis,
+            long expiryTimeMillis, float score,
+            List<SmartspaceAction> actionChips,
+            List<SmartspaceAction> iconGrid, int featureType, boolean sensitive,
+            boolean shouldShowExpanded, String sourceNotificationKey,
+            ComponentName componentName, UserHandle userHandle,
+            String associatedSmartspaceTargetId, Uri sliceUri,
+            AppWidgetProviderInfo widgetId) {
+        mSmartspaceTargetId = smartspaceTargetId;
+        mHeaderAction = headerAction;
+        mBaseAction = baseAction;
+        mCreationTimeMillis = creationTimeMillis;
+        mExpiryTimeMillis = expiryTimeMillis;
+        mScore = score;
+        mActionChips = actionChips;
+        mIconGrid = iconGrid;
+        mFeatureType = featureType;
+        mSensitive = sensitive;
+        mShouldShowExpanded = shouldShowExpanded;
+        mSourceNotificationKey = sourceNotificationKey;
+        mComponentName = componentName;
+        mUserHandle = userHandle;
+        mAssociatedSmartspaceTargetId = associatedSmartspaceTargetId;
+        mSliceUri = sliceUri;
+        mWidgetId = widgetId;
+    }
+
+    /**
+     * Returns the Id of the target.
+     */
+    @NonNull
+    public String getSmartspaceTargetId() {
+        return mSmartspaceTargetId;
+    }
+
+    /**
+     * Returns the header action of the target.
+     */
+    @Nullable
+    public SmartspaceAction getHeaderAction() {
+        return mHeaderAction;
+    }
+
+    /**
+     * Returns the base action of the target.
+     */
+    @Nullable
+    public SmartspaceAction getBaseAction() {
+        return mBaseAction;
+    }
+
+    /**
+     * Returns the creation time of the target.
+     */
+    @NonNull
+    public long getCreationTimeMillis() {
+        return mCreationTimeMillis;
+    }
+
+    /**
+     * Returns the expiry time of the target.
+     */
+    @NonNull
+    public long getExpiryTimeMillis() {
+        return mExpiryTimeMillis;
+    }
+
+    /**
+     * Returns the score of the target.
+     */
+    @NonNull
+    public float getScore() {
+        return mScore;
+    }
+
+    /**
+     * Return the action chips of the target.
+     */
+    @NonNull
+    public List<SmartspaceAction> getActionChips() {
+        return mActionChips;
+    }
+
+    /**
+     * Return the icons of the target.
+     */
+    @NonNull
+    public List<SmartspaceAction> getIconGrid() {
+        return mIconGrid;
+    }
+
+    /**
+     * Returns the feature type of the target.
+     */
+    @NonNull
+    public int getFeatureType() {
+        return mFeatureType;
+    }
+
+    /**
+     * Returns whether the target is sensitive or not.
+     */
+    @NonNull
+    public boolean isSensitive() {
+        return mSensitive;
+    }
+
+    /**
+     * Returns whether the target should be shown in expanded state.
+     */
+    @NonNull
+    public boolean shouldShowExpanded() {
+        return mShouldShowExpanded;
+    }
+
+    /**
+     * Returns the source notification key of the target.
+     */
+    @Nullable
+    public String getSourceNotificationKey() {
+        return mSourceNotificationKey;
+    }
+
+    /**
+     * Returns the component name of the target.
+     */
+    @NonNull
+    public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    /**
+     * Returns the user handle of the target.
+     */
+    @NonNull
+    public UserHandle getUserHandle() {
+        return mUserHandle;
+    }
+
+    /**
+     * Returns the id of a target associated with this instance.
+     */
+    @Nullable
+    public String getAssociatedSmartspaceTargetId() {
+        return mAssociatedSmartspaceTargetId;
+    }
+
+    /**
+     * Returns the slice uri, if the target is a slice.
+     */
+    @Nullable
+    public Uri getSliceUri() {
+        return mSliceUri;
+    }
+
+    /**
+     * Returns the AppWidgetProviderInfo, if the target is a widget.
+     */
+    @Nullable
+    public AppWidgetProviderInfo getWidgetId() {
+        return mWidgetId;
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    @NonNull
+    public static final Creator<SmartspaceTarget> CREATOR = new Creator<SmartspaceTarget>() {
+        @Override
+        public SmartspaceTarget createFromParcel(Parcel source) {
+            return new SmartspaceTarget(source);
+        }
+
+        @Override
+        public SmartspaceTarget[] newArray(int size) {
+            return new SmartspaceTarget[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(this.mSmartspaceTargetId);
+        dest.writeTypedObject(this.mHeaderAction, flags);
+        dest.writeTypedObject(this.mBaseAction, flags);
+        dest.writeLong(this.mCreationTimeMillis);
+        dest.writeLong(this.mExpiryTimeMillis);
+        dest.writeFloat(this.mScore);
+        dest.writeTypedList(this.mActionChips);
+        dest.writeTypedList(this.mIconGrid);
+        dest.writeInt(this.mFeatureType);
+        dest.writeBoolean(this.mSensitive);
+        dest.writeBoolean(this.mShouldShowExpanded);
+        dest.writeString(this.mSourceNotificationKey);
+        dest.writeTypedObject(this.mComponentName, flags);
+        dest.writeTypedObject(this.mUserHandle, flags);
+        dest.writeString(this.mAssociatedSmartspaceTargetId);
+        dest.writeTypedObject(this.mSliceUri, flags);
+        dest.writeTypedObject(this.mWidgetId, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "SmartspaceTarget{"
+                + "mSmartspaceTargetId='" + mSmartspaceTargetId + '\''
+                + ", mHeaderAction=" + mHeaderAction
+                + ", mBaseAction=" + mBaseAction
+                + ", mCreationTimeMillis=" + mCreationTimeMillis
+                + ", mExpiryTimeMillis=" + mExpiryTimeMillis
+                + ", mScore=" + mScore
+                + ", mActionChips=" + mActionChips
+                + ", mIconGrid=" + mIconGrid
+                + ", mFeatureType=" + mFeatureType
+                + ", mSensitive=" + mSensitive
+                + ", mShouldShowExpanded=" + mShouldShowExpanded
+                + ", mSourceNotificationKey='" + mSourceNotificationKey + '\''
+                + ", mComponentName=" + mComponentName
+                + ", mUserHandle=" + mUserHandle
+                + ", mAssociatedSmartspaceTargetId='" + mAssociatedSmartspaceTargetId + '\''
+                + ", mSliceUri=" + mSliceUri
+                + ", mWidgetId=" + mWidgetId
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SmartspaceTarget that = (SmartspaceTarget) o;
+        return mCreationTimeMillis == that.mCreationTimeMillis
+                && mExpiryTimeMillis == that.mExpiryTimeMillis
+                && Float.compare(that.mScore, mScore) == 0
+                && mFeatureType == that.mFeatureType
+                && mSensitive == that.mSensitive
+                && mShouldShowExpanded == that.mShouldShowExpanded
+                && mSmartspaceTargetId.equals(that.mSmartspaceTargetId)
+                && Objects.equals(mHeaderAction, that.mHeaderAction)
+                && Objects.equals(mBaseAction, that.mBaseAction)
+                && Objects.equals(mActionChips, that.mActionChips)
+                && Objects.equals(mIconGrid, that.mIconGrid)
+                && Objects.equals(mSourceNotificationKey, that.mSourceNotificationKey)
+                && mComponentName.equals(that.mComponentName)
+                && mUserHandle.equals(that.mUserHandle)
+                && Objects.equals(mAssociatedSmartspaceTargetId,
+                that.mAssociatedSmartspaceTargetId)
+                && Objects.equals(mSliceUri, that.mSliceUri)
+                && Objects.equals(mWidgetId, that.mWidgetId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSmartspaceTargetId, mHeaderAction, mBaseAction, mCreationTimeMillis,
+                mExpiryTimeMillis, mScore, mActionChips, mIconGrid, mFeatureType, mSensitive,
+                mShouldShowExpanded, mSourceNotificationKey, mComponentName, mUserHandle,
+                mAssociatedSmartspaceTargetId, mSliceUri, mWidgetId);
+    }
+
+    /**
+     * A builder for {@link SmartspaceTarget} object.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        private final String mSmartspaceTargetId;
+        private SmartspaceAction mHeaderAction;
+        private SmartspaceAction mBaseAction;
+        private long mCreationTimeMillis;
+        private long mExpiryTimeMillis;
+        private float mScore;
+        private List<SmartspaceAction> mActionChips = new ArrayList<>();
+        private List<SmartspaceAction> mIconGrid = new ArrayList<>();
+        private int mFeatureType;
+        private boolean mSensitive;
+        private boolean mShouldShowExpanded;
+        private String mSourceNotificationKey;
+        private final ComponentName mComponentName;
+        private final UserHandle mUserHandle;
+        private String mAssociatedSmartspaceTargetId;
+        private Uri mSliceUri;
+        private AppWidgetProviderInfo mWidgetId;
+
+        /**
+         * A builder for {@link SmartspaceTarget}.
+         *
+         * @param smartspaceTargetId the id of this target
+         * @param componentName      the componentName of this target
+         * @param userHandle         the userHandle of this target
+         */
+        public Builder(@NonNull String smartspaceTargetId,
+                @NonNull ComponentName componentName, @NonNull UserHandle userHandle) {
+            this.mSmartspaceTargetId = smartspaceTargetId;
+            this.mComponentName = componentName;
+            this.mUserHandle = userHandle;
+        }
+
+        /**
+         * Sets the header action.
+         */
+        @NonNull
+        public Builder setHeaderAction(@NonNull SmartspaceAction headerAction) {
+            this.mHeaderAction = headerAction;
+            return this;
+        }
+
+        /**
+         * Sets the base action.
+         */
+        @NonNull
+        public Builder setBaseAction(@NonNull SmartspaceAction baseAction) {
+            this.mBaseAction = baseAction;
+            return this;
+        }
+
+        /**
+         * Sets the creation time.
+         */
+        @NonNull
+        public Builder setCreationTimeMillis(@NonNull long creationTimeMillis) {
+            this.mCreationTimeMillis = creationTimeMillis;
+            return this;
+        }
+
+        /**
+         * Sets the expiration time.
+         */
+        @NonNull
+        public Builder setExpiryTimeMillis(@NonNull long expiryTimeMillis) {
+            this.mExpiryTimeMillis = expiryTimeMillis;
+            return this;
+        }
+
+        /**
+         * Sets the score.
+         */
+        @NonNull
+        public Builder setScore(@NonNull float score) {
+            this.mScore = score;
+            return this;
+        }
+
+        /**
+         * Sets the action chips.
+         */
+        @NonNull
+        public Builder setActionChips(@NonNull List<SmartspaceAction> actionChips) {
+            this.mActionChips = actionChips;
+            return this;
+        }
+
+        /**
+         * Sets the icon grid.
+         */
+        @NonNull
+        public Builder setIconGrid(@NonNull List<SmartspaceAction> iconGrid) {
+            this.mIconGrid = iconGrid;
+            return this;
+        }
+
+        /**
+         * Sets the feature type.
+         */
+        @NonNull
+        public Builder setFeatureType(@NonNull int featureType) {
+            this.mFeatureType = featureType;
+            return this;
+        }
+
+        /**
+         * Sets whether the contents are sensitive.
+         */
+        @NonNull
+        public Builder setSensitive(@NonNull boolean sensitive) {
+            this.mSensitive = sensitive;
+            return this;
+        }
+
+        /**
+         * Sets whether to show the card as expanded.
+         */
+        @NonNull
+        public Builder setShouldShowExpanded(@NonNull boolean shouldShowExpanded) {
+            this.mShouldShowExpanded = shouldShowExpanded;
+            return this;
+        }
+
+        /**
+         * Sets the source notification key.
+         */
+        @NonNull
+        public Builder setSourceNotificationKey(@NonNull String sourceNotificationKey) {
+            this.mSourceNotificationKey = sourceNotificationKey;
+            return this;
+        }
+
+        /**
+         * Sets the associated smartspace target id.
+         */
+        @NonNull
+        public Builder setAssociatedSmartspaceTargetId(
+                @NonNull String associatedSmartspaceTargetId) {
+            this.mAssociatedSmartspaceTargetId = associatedSmartspaceTargetId;
+            return this;
+        }
+
+        /**
+         * Sets the slice uri.
+         *
+         * <p><b>NOTE: </b> If {@link mWidgetId} is also set, {@link mSliceUri} should be ignored.
+         */
+        @NonNull
+        public Builder setSliceUri(@NonNull Uri sliceUri) {
+            this.mSliceUri = sliceUri;
+            return this;
+        }
+
+        /**
+         * Sets the widget id.
+         *
+         * <p><b>NOTE: </b> If {@link mWidgetId} is set, all other @Nullable params should be
+         * ignored.
+         */
+        @NonNull
+        public Builder setWidgetId(@NonNull AppWidgetProviderInfo widgetId) {
+            this.mWidgetId = widgetId;
+            return this;
+        }
+
+        /**
+         * Builds a new {@link SmartspaceTarget}.
+         *
+         * @throws IllegalStateException when non null fields are set as null.
+         */
+        @NonNull
+        public SmartspaceTarget build() {
+            if (mSmartspaceTargetId == null
+                    || mComponentName == null
+                    || mUserHandle == null) {
+                throw new IllegalStateException("Please assign a value to all @NonNull args.");
+            }
+            return new SmartspaceTarget(mSmartspaceTargetId,
+                    mHeaderAction, mBaseAction, mCreationTimeMillis, mExpiryTimeMillis, mScore,
+                    mActionChips, mIconGrid, mFeatureType, mSensitive, mShouldShowExpanded,
+                    mSourceNotificationKey, mComponentName, mUserHandle,
+                    mAssociatedSmartspaceTargetId, mSliceUri, mWidgetId);
+        }
+    }
+}
diff --git a/core/java/android/app/smartspace/SmartspaceTargetEvent.aidl b/core/java/android/app/smartspace/SmartspaceTargetEvent.aidl
new file mode 100644
index 0000000..e797a9b
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.smartspace;
+
+parcelable SmartspaceTargetEvent;
diff --git a/core/java/android/app/smartspace/SmartspaceTargetEvent.java b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
new file mode 100644
index 0000000..1e0653d
--- /dev/null
+++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
@@ -0,0 +1,204 @@
+/*
+ * 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.app.smartspace;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A representation of a smartspace event.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceTargetEvent implements Parcelable {
+
+    /**
+     * User interacted with the target.
+     */
+    public static final int EVENT_TARGET_INTERACTION = 1;
+
+    /**
+     * Smartspace target was brought into view.
+     */
+    public static final int EVENT_TARGET_IN_VIEW = 2;
+    /**
+     * Smartspace target went out of view.
+     */
+    public static final int EVENT_TARGET_OUT_OF_VIEW = 3;
+    /**
+     * A dismiss action was issued by the user.
+     */
+    public static final int EVENT_TARGET_DISMISS = 4;
+    /**
+     * A block action was issued by the user.
+     */
+    public static final int EVENT_TARGET_BLOCK = 5;
+    /**
+     * The Ui surface came into view.
+     */
+    public static final int EVENT_UI_SURFACE_IN_VIEW = 6;
+    /**
+     * The Ui surface went out of view.
+     */
+    public static final int EVENT_UI_SURFACE_OUT_OF_VIEW = 7;
+
+    /**
+     * @see Parcelable.Creator
+     */
+    @NonNull
+    public static final Creator<SmartspaceTargetEvent> CREATOR =
+            new Creator<SmartspaceTargetEvent>() {
+                public SmartspaceTargetEvent createFromParcel(Parcel parcel) {
+                    return new SmartspaceTargetEvent(parcel);
+                }
+
+                public SmartspaceTargetEvent[] newArray(int size) {
+                    return new SmartspaceTargetEvent[size];
+                }
+            };
+
+    @Nullable
+    private final SmartspaceTarget mSmartspaceTarget;
+
+    @Nullable
+    private final String mSmartspaceActionId;
+
+    @EventType
+    private final int mEventType;
+
+    private SmartspaceTargetEvent(@Nullable SmartspaceTarget smartspaceTarget,
+            @Nullable String smartspaceActionId,
+            @EventType int eventType) {
+        mSmartspaceTarget = smartspaceTarget;
+        mSmartspaceActionId = smartspaceActionId;
+        mEventType = eventType;
+    }
+
+    private SmartspaceTargetEvent(Parcel parcel) {
+        mSmartspaceTarget = parcel.readParcelable(null);
+        mSmartspaceActionId = parcel.readString();
+        mEventType = parcel.readInt();
+    }
+
+    /**
+     * Get the {@link SmartspaceTarget} associated with this event.
+     */
+    @Nullable
+    public SmartspaceTarget getSmartspaceTarget() {
+        return mSmartspaceTarget;
+    }
+
+    /**
+     * Get the action id of the Smartspace Action associated with this event.
+     */
+    @Nullable
+    public String getSmartspaceActionId() {
+        return mSmartspaceActionId;
+    }
+
+    /**
+     * Get the {@link EventType} of this event.
+     */
+    @NonNull
+    @EventType
+    public int getEventType() {
+        return mEventType;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeParcelable(mSmartspaceTarget, flags);
+        dest.writeString(mSmartspaceActionId);
+        dest.writeInt(mEventType);
+    }
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"EVENT_"}, value = {
+            EVENT_TARGET_INTERACTION,
+            EVENT_TARGET_IN_VIEW,
+            EVENT_TARGET_OUT_OF_VIEW,
+            EVENT_TARGET_DISMISS,
+            EVENT_TARGET_BLOCK,
+            EVENT_UI_SURFACE_IN_VIEW,
+            EVENT_UI_SURFACE_OUT_OF_VIEW
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EventType {
+    }
+
+    /**
+     * A builder for {@link SmartspaceTargetEvent}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final class Builder {
+        @EventType
+        private final int mEventType;
+        @Nullable
+        private SmartspaceTarget mSmartspaceTarget;
+        @Nullable
+        private String mSmartspaceActionId;
+
+        /**
+         * A builder for {@link SmartspaceTargetEvent}.
+         */
+        public Builder(@EventType int eventType) {
+            mEventType = eventType;
+        }
+
+        /**
+         * Sets the SmartspaceTarget for this event.
+         */
+        @NonNull
+        public Builder setSmartspaceTarget(@NonNull SmartspaceTarget smartspaceTarget) {
+            mSmartspaceTarget = smartspaceTarget;
+            return this;
+        }
+
+        /**
+         * Sets the Smartspace action id for this event.
+         */
+        @NonNull
+        public Builder setSmartspaceActionId(@NonNull String smartspaceActionId) {
+            mSmartspaceActionId = smartspaceActionId;
+            return this;
+        }
+
+        /**
+         * Builds a new {@link SmartspaceTargetEvent} instance.
+         */
+        @NonNull
+        public SmartspaceTargetEvent build() {
+            return new SmartspaceTargetEvent(mSmartspaceTarget, mSmartspaceActionId, mEventType);
+        }
+    }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0c50446..c0e0635 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4615,6 +4615,18 @@
     public static final String SEARCH_UI_SERVICE = "search_ui";
 
     /**
+     * Used for getting the smartspace service.
+     *
+     * <p><b>NOTE: </b> this service is optional; callers of
+     * {@code Context.getSystemServiceName(SMARTSPACE_SERVICE)} should check for {@code null}.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    @SystemApi
+    public static final String SMARTSPACE_SERVICE = "smartspace";
+
+    /**
      * Use with {@link #getSystemService(String)} to access the
      * {@link com.android.server.voiceinteraction.SoundTriggerService}.
      *
diff --git a/core/java/android/service/smartspace/ISmartspaceService.aidl b/core/java/android/service/smartspace/ISmartspaceService.aidl
new file mode 100644
index 0000000..c9c6807
--- /dev/null
+++ b/core/java/android/service/smartspace/ISmartspaceService.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.service.smartspace;
+
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.ISmartspaceCallback;
+import android.content.pm.ParceledListSlice;
+
+/**
+ * Interface from the system to Smartspace service.
+ *
+ * @hide
+ */
+oneway interface ISmartspaceService {
+
+    void onCreateSmartspaceSession(in SmartspaceConfig context, in SmartspaceSessionId sessionId);
+
+    void notifySmartspaceEvent(in SmartspaceSessionId sessionId, in SmartspaceTargetEvent event);
+
+    void requestSmartspaceUpdate(in SmartspaceSessionId sessionId);
+
+    void registerSmartspaceUpdates(in SmartspaceSessionId sessionId,
+            in ISmartspaceCallback callback);
+
+    void unregisterSmartspaceUpdates(in SmartspaceSessionId sessionId,
+            in ISmartspaceCallback callback);
+
+    void onDestroySmartspaceSession(in SmartspaceSessionId sessionId);
+}
diff --git a/core/java/android/service/smartspace/SmartspaceService.java b/core/java/android/service/smartspace/SmartspaceService.java
new file mode 100644
index 0000000..09b7310
--- /dev/null
+++ b/core/java/android/service/smartspace/SmartspaceService.java
@@ -0,0 +1,313 @@
+/*
+ * 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.service.smartspace;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.smartspace.ISmartspaceCallback;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceTarget;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.Intent;
+import android.content.pm.ParceledListSlice;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.service.smartspace.ISmartspaceService.Stub;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A service used to share the lifecycle of smartspace UI (open, close, interaction)
+ * and also to return smartspace result on a query.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class SmartspaceService extends Service {
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     *
+     * <p>The service must also require the {@link android.permission#MANAGE_SMARTSPACE}
+     * permission.
+     *
+     * @hide
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.service.smartspace.SmartspaceService";
+    private static final boolean DEBUG = false;
+    private static final String TAG = "SmartspaceService";
+    private final ArrayMap<SmartspaceSessionId, ArrayList<CallbackWrapper>> mSessionCallbacks =
+            new ArrayMap<>();
+    private Handler mHandler;
+
+    private final android.service.smartspace.ISmartspaceService mInterface = new Stub() {
+
+        @Override
+        public void onCreateSmartspaceSession(SmartspaceConfig smartspaceConfig,
+                SmartspaceSessionId sessionId) {
+            mHandler.sendMessage(
+                    obtainMessage(SmartspaceService::doCreateSmartspaceSession,
+                            SmartspaceService.this, smartspaceConfig, sessionId));
+        }
+
+        @Override
+        public void notifySmartspaceEvent(SmartspaceSessionId sessionId,
+                SmartspaceTargetEvent event) {
+            mHandler.sendMessage(
+                    obtainMessage(SmartspaceService::notifySmartspaceEvent,
+                            SmartspaceService.this, sessionId, event));
+        }
+
+        @Override
+        public void requestSmartspaceUpdate(SmartspaceSessionId sessionId) {
+            mHandler.sendMessage(
+                    obtainMessage(SmartspaceService::doRequestPredictionUpdate,
+                            SmartspaceService.this, sessionId));
+        }
+
+        @Override
+        public void registerSmartspaceUpdates(SmartspaceSessionId sessionId,
+                ISmartspaceCallback callback) {
+            mHandler.sendMessage(
+                    obtainMessage(SmartspaceService::doRegisterSmartspaceUpdates,
+                            SmartspaceService.this, sessionId, callback));
+        }
+
+        @Override
+        public void unregisterSmartspaceUpdates(SmartspaceSessionId sessionId,
+                ISmartspaceCallback callback) {
+            mHandler.sendMessage(
+                    obtainMessage(SmartspaceService::doUnregisterSmartspaceUpdates,
+                            SmartspaceService.this, sessionId, callback));
+        }
+
+        @Override
+        public void onDestroySmartspaceSession(SmartspaceSessionId sessionId) {
+
+            mHandler.sendMessage(
+                    obtainMessage(SmartspaceService::doDestroy,
+                            SmartspaceService.this, sessionId));
+        }
+    };
+
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.d(TAG, "onCreate mSessionCallbacks: " + mSessionCallbacks);
+        mHandler = new Handler(Looper.getMainLooper(), null, true);
+    }
+
+    @Override
+    @NonNull
+    public final IBinder onBind(@NonNull Intent intent) {
+        Log.d(TAG, "onBind mSessionCallbacks: " + mSessionCallbacks);
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mInterface.asBinder();
+        }
+        Slog.w(TAG, "Tried to bind to wrong intent (should be "
+                + SERVICE_INTERFACE + ": " + intent);
+        return null;
+    }
+
+    private void doCreateSmartspaceSession(@NonNull SmartspaceConfig config,
+            @NonNull SmartspaceSessionId sessionId) {
+        Log.d(TAG, "doCreateSmartspaceSession mSessionCallbacks: " + mSessionCallbacks);
+        mSessionCallbacks.put(sessionId, new ArrayList<>());
+        onCreateSmartspaceSession(config, sessionId);
+    }
+
+    /**
+     * Gets called when the client calls <code> SmartspaceManager#createSmartspaceSession </code>.
+     */
+    public abstract void onCreateSmartspaceSession(@NonNull SmartspaceConfig config,
+            @NonNull SmartspaceSessionId sessionId);
+
+    /**
+     * Gets called when the client calls <code> SmartspaceSession#notifySmartspaceEvent </code>.
+     */
+    @MainThread
+    public abstract void notifySmartspaceEvent(@NonNull SmartspaceSessionId sessionId,
+            @NonNull SmartspaceTargetEvent event);
+
+    /**
+     * Gets called when the client calls <code> SmartspaceSession#requestSmartspaceUpdate </code>.
+     */
+    @MainThread
+    public abstract void onRequestSmartspaceUpdate(@NonNull SmartspaceSessionId sessionId);
+
+    private void doRegisterSmartspaceUpdates(@NonNull SmartspaceSessionId sessionId,
+            @NonNull ISmartspaceCallback callback) {
+        Log.d(TAG, "doRegisterSmartspaceUpdates mSessionCallbacks: " + mSessionCallbacks);
+        final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+        if (callbacks == null) {
+            Slog.e(TAG, "Failed to register for updates for unknown session: " + sessionId);
+            return;
+        }
+
+        final CallbackWrapper wrapper = findCallbackWrapper(callbacks, callback);
+        if (wrapper == null) {
+            callbacks.add(new CallbackWrapper(callback,
+                    callbackWrapper ->
+                            mHandler.post(
+                                    () -> removeCallbackWrapper(callbacks, callbackWrapper))));
+        }
+    }
+
+    private void doUnregisterSmartspaceUpdates(@NonNull SmartspaceSessionId sessionId,
+            @NonNull ISmartspaceCallback callback) {
+        Log.d(TAG, "doUnregisterSmartspaceUpdates mSessionCallbacks: " + mSessionCallbacks);
+        final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+        if (callbacks == null) {
+            Slog.e(TAG, "Failed to unregister for updates for unknown session: " + sessionId);
+            return;
+        }
+
+        final CallbackWrapper wrapper = findCallbackWrapper(callbacks, callback);
+        if (wrapper != null) {
+            removeCallbackWrapper(callbacks, wrapper);
+        }
+    }
+
+    private void doRequestPredictionUpdate(@NonNull SmartspaceSessionId sessionId) {
+        Log.d(TAG, "doRequestPredictionUpdate mSessionCallbacks: " + mSessionCallbacks);
+        // Just an optimization, if there are no callbacks, then don't bother notifying the service
+        final ArrayList<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+        if (callbacks != null && !callbacks.isEmpty()) {
+            onRequestSmartspaceUpdate(sessionId);
+        }
+    }
+
+    /**
+     * Finds the callback wrapper for the given callback.
+     */
+    private CallbackWrapper findCallbackWrapper(ArrayList<CallbackWrapper> callbacks,
+            ISmartspaceCallback callback) {
+        for (int i = callbacks.size() - 1; i >= 0; i--) {
+            if (callbacks.get(i).isCallback(callback)) {
+                return callbacks.get(i);
+            }
+        }
+        return null;
+    }
+
+    private void removeCallbackWrapper(
+            ArrayList<CallbackWrapper> callbacks, CallbackWrapper wrapper) {
+        if (callbacks == null) {
+            return;
+        }
+        callbacks.remove(wrapper);
+    }
+
+    /**
+     * Gets called when the client calls <code> SmartspaceManager#destroy() </code>.
+     */
+    public abstract void onDestroySmartspaceSession(@NonNull SmartspaceSessionId sessionId);
+
+    private void doDestroy(@NonNull SmartspaceSessionId sessionId) {
+        Log.d(TAG, "doDestroy mSessionCallbacks: " + mSessionCallbacks);
+        super.onDestroy();
+        mSessionCallbacks.remove(sessionId);
+        onDestroySmartspaceSession(sessionId);
+    }
+
+    /**
+     * Used by the prediction factory to send back results the client app. The can be called
+     * in response to {@link #onRequestSmartspaceUpdate(SmartspaceSessionId)} or proactively as
+     * a result of changes in predictions.
+     */
+    public final void updateSmartspaceTargets(@NonNull SmartspaceSessionId sessionId,
+            @NonNull List<SmartspaceTarget> targets) {
+        Log.d(TAG, "updateSmartspaceTargets mSessionCallbacks: " + mSessionCallbacks);
+        List<CallbackWrapper> callbacks = mSessionCallbacks.get(sessionId);
+        if (callbacks != null) {
+            for (CallbackWrapper callback : callbacks) {
+                callback.accept(targets);
+            }
+        }
+    }
+
+    /**
+     * Destroys a smartspace session.
+     */
+    @MainThread
+    public abstract void onDestroy(@NonNull SmartspaceSessionId sessionId);
+
+    private static final class CallbackWrapper implements Consumer<List<SmartspaceTarget>>,
+            IBinder.DeathRecipient {
+
+        private final Consumer<CallbackWrapper> mOnBinderDied;
+        private ISmartspaceCallback mCallback;
+
+        CallbackWrapper(ISmartspaceCallback callback,
+                @Nullable Consumer<CallbackWrapper> onBinderDied) {
+            mCallback = callback;
+            mOnBinderDied = onBinderDied;
+            try {
+                mCallback.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to link to death: " + e);
+            }
+        }
+
+        public boolean isCallback(@NonNull ISmartspaceCallback callback) {
+            if (mCallback == null) {
+                Slog.e(TAG, "Callback is null, likely the binder has died.");
+                return false;
+            }
+            return mCallback.equals(callback);
+        }
+
+        @Override
+        public void accept(List<SmartspaceTarget> smartspaceTargets) {
+            try {
+                if (mCallback != null) {
+                    if (DEBUG) {
+                        Slog.d(TAG,
+                                "CallbackWrapper.accept smartspaceTargets=" + smartspaceTargets);
+                    }
+                    mCallback.onResult(new ParceledListSlice(smartspaceTargets));
+                }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Error sending result:" + e);
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            mCallback.asBinder().unlinkToDeath(this, 0);
+            mCallback = null;
+            if (mOnBinderDied != null) {
+                mOnBinderDied.accept(this);
+            }
+        }
+    }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4a0a35d..c4309266 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5168,6 +5168,11 @@
     <permission android:name="android.permission.MANAGE_SEARCH_UI"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows an application to manage the smartspace service.
+     @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.MANAGE_SMARTSPACE"
+        android:protectionLevel="signature" />
+
     <!-- Allows an app to set the theme overlay in /vendor/overlay
          being used.
          @hide  <p>Not for use by third-party applications.</p> -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5e0cda6..01b8efa 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3800,6 +3800,15 @@
 -->
     <string name="config_defaultSearchUiService" translatable="false"></string>
 
+    <!-- The package name for the system's smartspace service.
+     This service returns smartspace results.
+
+     This service must be trusted, as it can be activated without explicit consent of the user.
+     If no service with the specified name exists on the device, smartspace will be disabled.
+     Example: "com.android.intelligence/.SmartspaceService"
+-->
+    <string name="config_defaultSmartspaceService" translatable="false"></string>
+
     <!-- The package name for the system's speech recognition service.
          This service must be trusted, as it can be activated without explicit consent of the user.
          Example: "com.android.speech/.RecognitionService"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dfccdf4..815330f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3498,6 +3498,7 @@
   <java-symbol type="string" name="config_defaultAppPredictionService" />
   <java-symbol type="string" name="config_defaultContentSuggestionsService" />
   <java-symbol type="string" name="config_defaultSearchUiService" />
+  <java-symbol type="string" name="config_defaultSmartspaceService" />
   <java-symbol type="string" name="config_defaultMusicRecognitionService" />
   <java-symbol type="string" name="config_defaultAttentionService" />
   <java-symbol type="string" name="config_defaultRotationResolverService" />
diff --git a/services/Android.bp b/services/Android.bp
index f6bb72a..f0cab1e 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -28,6 +28,7 @@
         ":services.profcollect-sources",
         ":services.restrictions-sources",
         ":services.searchui-sources",
+        ":services.smartspace-sources",
         ":services.speech-sources",
         ":services.startop.iorap-sources",
         ":services.systemcaptions-sources",
@@ -76,6 +77,7 @@
         "services.profcollect",
         "services.restrictions",
         "services.searchui",
+        "services.smartspace",
         "services.speech",
         "services.startop",
         "services.systemcaptions",
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d28c3cc..b8088c4 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -341,6 +341,8 @@
             "com.android.server.contentsuggestions.ContentSuggestionsManagerService";
     private static final String SEARCH_UI_MANAGER_SERVICE_CLASS =
             "com.android.server.searchui.SearchUiManagerService";
+    private static final String SMARTSPACE_MANAGER_SERVICE_CLASS =
+            "com.android.server.smartspace.SmartspaceManagerService";
     private static final String DEVICE_IDLE_CONTROLLER_CLASS =
             "com.android.server.DeviceIdleController";
     private static final String BLOB_STORE_MANAGER_SERVICE_CLASS =
@@ -1670,6 +1672,12 @@
             mSystemServiceManager.startService(SEARCH_UI_MANAGER_SERVICE_CLASS);
             t.traceEnd();
 
+            // Smartspace manager service
+            // TODO: add deviceHasConfigString(context, R.string.config_defaultSmartspaceService)
+            t.traceBegin("StartSmartspaceService");
+            mSystemServiceManager.startService(SMARTSPACE_MANAGER_SERVICE_CLASS);
+            t.traceEnd();
+
             t.traceBegin("InitConnectivityModuleConnector");
             try {
                 ConnectivityModuleConnector.getInstance().init(context);
diff --git a/services/smartspace/Android.bp b/services/smartspace/Android.bp
new file mode 100644
index 0000000..fcf780d
--- /dev/null
+++ b/services/smartspace/Android.bp
@@ -0,0 +1,13 @@
+filegroup {
+    name: "services.smartspace-sources",
+    srcs: ["java/**/*.java"],
+    path: "java",
+    visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+    name: "services.smartspace",
+    defaults: ["platform_service_defaults"],
+    srcs: [":services.smartspace-sources"],
+    libs: ["services.core"],
+}
diff --git a/services/smartspace/java/com/android/server/smartspace/RemoteSmartspaceService.java b/services/smartspace/java/com/android/server/smartspace/RemoteSmartspaceService.java
new file mode 100644
index 0000000..3b5a5a5
--- /dev/null
+++ b/services/smartspace/java/com/android/server/smartspace/RemoteSmartspaceService.java
@@ -0,0 +1,112 @@
+/*
+ * 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 com.android.server.smartspace;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.service.smartspace.ISmartspaceService;
+import android.text.format.DateUtils;
+
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+
+
+/**
+ * Proxy to the {@link android.service.smartspace.SmartspaceService} implementation in another
+ * process.
+ */
+public class RemoteSmartspaceService extends
+        AbstractMultiplePendingRequestsRemoteService<RemoteSmartspaceService,
+                ISmartspaceService> {
+
+    private static final String TAG = "RemoteSmartspaceService";
+
+    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+
+    private final RemoteSmartspaceServiceCallbacks mCallback;
+
+    public RemoteSmartspaceService(Context context, String serviceInterface,
+            ComponentName componentName, int userId,
+            RemoteSmartspaceServiceCallbacks callback, boolean bindInstantServiceAllowed,
+            boolean verbose) {
+        super(context, serviceInterface, componentName, userId, callback,
+                context.getMainThreadHandler(),
+                bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
+                verbose, /* initialCapacity= */ 1);
+        mCallback = callback;
+    }
+
+    @Override
+    protected ISmartspaceService getServiceInterface(IBinder service) {
+        return ISmartspaceService.Stub.asInterface(service);
+    }
+
+    @Override
+    protected long getTimeoutIdleBindMillis() {
+        return PERMANENT_BOUND_TIMEOUT_MS;
+    }
+
+    @Override
+    protected long getRemoteRequestMillis() {
+        return TIMEOUT_REMOTE_REQUEST_MILLIS;
+    }
+
+    /**
+     * Schedules a request to bind to the remote service.
+     */
+    public void reconnect() {
+        super.scheduleBind();
+    }
+
+    /**
+     * Schedule async request on remote service.
+     */
+    public void scheduleOnResolvedService(@NonNull AsyncRequest<ISmartspaceService> request) {
+        scheduleAsyncRequest(request);
+    }
+
+    /**
+     * Execute async request on remote service immediately instead of sending it to Handler queue.
+     */
+    public void executeOnResolvedService(@NonNull AsyncRequest<ISmartspaceService> request) {
+        executeAsyncRequest(request);
+    }
+
+    /**
+     * Failure callback
+     */
+    public interface RemoteSmartspaceServiceCallbacks
+            extends VultureCallback<RemoteSmartspaceService> {
+
+        /**
+         * Notifies a the failure or timeout of a remote call.
+         */
+        void onFailureOrTimeout(boolean timedOut);
+
+        /**
+         * Notifies change in connected state of the remote service.
+         */
+        void onConnectedStateChanged(boolean connected);
+    }
+
+    @Override // from AbstractRemoteService
+    protected void handleOnConnectedStateChanged(boolean connected) {
+        if (mCallback != null) {
+            mCallback.onConnectedStateChanged(connected);
+        }
+    }
+}
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
new file mode 100644
index 0000000..169b85e
--- /dev/null
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
@@ -0,0 +1,184 @@
+/*
+ * 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 com.android.server.smartspace;
+
+import static android.Manifest.permission.MANAGE_SMARTSPACE;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.content.Context.SMARTSPACE_SERVICE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+import android.app.smartspace.ISmartspaceCallback;
+import android.app.smartspace.ISmartspaceManager;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import java.io.FileDescriptor;
+import java.util.function.Consumer;
+
+/**
+ * A service used to return smartspace targets given a query.
+ */
+public class SmartspaceManagerService extends
+        AbstractMasterSystemService<SmartspaceManagerService, SmartspacePerUserService> {
+
+    private static final String TAG = SmartspaceManagerService.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+
+    private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+
+    public SmartspaceManagerService(Context context) {
+        super(context, new FrameworkResourcesServiceNameResolver(context,
+                        com.android.internal.R.string.config_defaultSmartspaceService), null,
+                PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
+        mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+    }
+
+    @Override
+    protected SmartspacePerUserService newServiceLocked(int resolvedUserId, boolean disabled) {
+        return new SmartspacePerUserService(this, mLock, resolvedUserId);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(SMARTSPACE_SERVICE, new SmartspaceManagerStub());
+    }
+
+    @Override
+    protected void enforceCallingPermissionForManagement() {
+        getContext().enforceCallingPermission(MANAGE_SMARTSPACE, TAG);
+    }
+
+    @Override // from AbstractMasterSystemService
+    protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
+        final SmartspacePerUserService service = peekServiceForUserLocked(userId);
+        if (service != null) {
+            service.onPackageUpdatedLocked();
+        }
+    }
+
+    @Override // from AbstractMasterSystemService
+    protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
+        final SmartspacePerUserService service = peekServiceForUserLocked(userId);
+        if (service != null) {
+            service.onPackageRestartedLocked();
+        }
+    }
+
+    @Override
+    protected int getMaximumTemporaryServiceDurationMs() {
+        return MAX_TEMP_SERVICE_DURATION_MS;
+    }
+
+    private class SmartspaceManagerStub extends ISmartspaceManager.Stub {
+
+        @Override
+        public void createSmartspaceSession(@NonNull SmartspaceConfig smartspaceConfig,
+                @NonNull SmartspaceSessionId sessionId, @NonNull IBinder token) {
+            runForUserLocked("createSmartspaceSession", sessionId, (service) ->
+                    service.onCreateSmartspaceSessionLocked(smartspaceConfig, sessionId, token));
+        }
+
+        @Override
+        public void notifySmartspaceEvent(SmartspaceSessionId sessionId,
+                SmartspaceTargetEvent event) {
+            runForUserLocked("notifySmartspaceEvent", sessionId,
+                    (service) -> service.notifySmartspaceEventLocked(sessionId, event));
+        }
+
+        @Override
+        public void requestSmartspaceUpdate(SmartspaceSessionId sessionId) {
+            runForUserLocked("requestSmartspaceUpdate", sessionId,
+                    (service) -> service.requestSmartspaceUpdateLocked(sessionId));
+        }
+
+        @Override
+        public void registerSmartspaceUpdates(@NonNull SmartspaceSessionId sessionId,
+                @NonNull ISmartspaceCallback callback) {
+            runForUserLocked("registerSmartspaceUpdates", sessionId,
+                    (service) -> service.registerSmartspaceUpdatesLocked(sessionId, callback));
+        }
+
+        @Override
+        public void unregisterSmartspaceUpdates(SmartspaceSessionId sessionId,
+                ISmartspaceCallback callback) {
+            runForUserLocked("unregisterSmartspaceUpdates", sessionId,
+                    (service) -> service.unregisterSmartspaceUpdatesLocked(sessionId, callback));
+        }
+
+        @Override
+        public void destroySmartspaceSession(@NonNull SmartspaceSessionId sessionId) {
+            runForUserLocked("destroySmartspaceSession", sessionId,
+                    (service) -> service.onDestroyLocked(sessionId));
+        }
+
+        public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+                @Nullable FileDescriptor err,
+                @NonNull String[] args, @Nullable ShellCallback callback,
+                @NonNull ResultReceiver resultReceiver) {
+            new SmartspaceManagerServiceShellCommand(SmartspaceManagerService.this)
+                    .exec(this, in, out, err, args, callback, resultReceiver);
+        }
+
+        private void runForUserLocked(@NonNull final String func,
+                @NonNull final SmartspaceSessionId sessionId,
+                @NonNull final Consumer<SmartspacePerUserService> c) {
+            ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
+            final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                    sessionId.getUserId(), false, ALLOW_NON_FULL, null, null);
+
+            if (DEBUG) {
+                Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid());
+            }
+            if (!(mServiceNameResolver.isTemporary(userId)
+                    || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
+
+                String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid();
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    final SmartspacePerUserService service = getServiceForUserLocked(userId);
+                    c.accept(service);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+}
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerServiceShellCommand.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerServiceShellCommand.java
new file mode 100644
index 0000000..4143418e
--- /dev/null
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerServiceShellCommand.java
@@ -0,0 +1,84 @@
+/*
+ * 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 com.android.server.smartspace;
+
+import android.annotation.NonNull;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * The shell command implementation for the SmartspaceManagerService.
+ */
+public class SmartspaceManagerServiceShellCommand extends ShellCommand {
+
+    private static final String TAG =
+            SmartspaceManagerServiceShellCommand.class.getSimpleName();
+
+    private final SmartspaceManagerService mService;
+
+    public SmartspaceManagerServiceShellCommand(@NonNull SmartspaceManagerService service) {
+        mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        switch (cmd) {
+            case "set": {
+                final String what = getNextArgRequired();
+                switch (what) {
+                    case "temporary-service": {
+                        final int userId = Integer.parseInt(getNextArgRequired());
+                        String serviceName = getNextArg();
+                        if (serviceName == null) {
+                            mService.resetTemporaryService(userId);
+                            pw.println("SmartspaceService temporarily reset. ");
+                            return 0;
+                        }
+                        final int duration = Integer.parseInt(getNextArgRequired());
+                        mService.setTemporaryService(userId, serviceName, duration);
+                        pw.println("SmartspaceService temporarily set to " + serviceName
+                                + " for " + duration + "ms");
+                        break;
+                    }
+                }
+            }
+            break;
+            default:
+                return handleDefaultCommands(cmd);
+        }
+        return 0;
+    }
+
+    @Override
+    public void onHelp() {
+        try (PrintWriter pw = getOutPrintWriter()) {
+            pw.println("SmartspaceManagerService commands:");
+            pw.println("  help");
+            pw.println("    Prints this help text.");
+            pw.println("");
+            pw.println("  set temporary-service USER_ID [COMPONENT_NAME DURATION]");
+            pw.println("    Temporarily (for DURATION ms) changes the service implemtation.");
+            pw.println("    To reset, call with just the USER_ID argument.");
+            pw.println("");
+        }
+    }
+}
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java b/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java
new file mode 100644
index 0000000..db43468
--- /dev/null
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java
@@ -0,0 +1,411 @@
+/*
+ * 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 com.android.server.smartspace;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.app.smartspace.ISmartspaceCallback;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceSessionId;
+import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.service.smartspace.ISmartspaceService;
+import android.service.smartspace.SmartspaceService;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AbstractRemoteService;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+/**
+ * Per-user instance of {@link SmartspaceManagerService}.
+ */
+public class SmartspacePerUserService extends
+        AbstractPerUserSystemService<SmartspacePerUserService, SmartspaceManagerService>
+        implements RemoteSmartspaceService.RemoteSmartspaceServiceCallbacks {
+
+    private static final String TAG = SmartspacePerUserService.class.getSimpleName();
+    @GuardedBy("mLock")
+    private final ArrayMap<SmartspaceSessionId, SmartspaceSessionInfo> mSessionInfos =
+            new ArrayMap<>();
+    @Nullable
+    @GuardedBy("mLock")
+    private RemoteSmartspaceService mRemoteService;
+    /**
+     * When {@code true}, remote service died but service state is kept so it's restored after
+     * the system re-binds to it.
+     */
+    @GuardedBy("mLock")
+    private boolean mZombie;
+
+    protected SmartspacePerUserService(SmartspaceManagerService master,
+            Object lock, int userId) {
+        super(master, lock, userId);
+    }
+
+    @Override // from PerUserSystemService
+    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+            throws NameNotFoundException {
+
+        ServiceInfo si;
+        try {
+            si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                    PackageManager.GET_META_DATA, mUserId);
+        } catch (RemoteException e) {
+            throw new NameNotFoundException("Could not get service for " + serviceComponent);
+        }
+        // TODO(b/177858728): must check that either the service is from a system component,
+        // or it matches a service set by shell cmd (so it can be used on CTS tests and when
+        // OEMs are implementing the real service and also verify the proper permissions
+        return si;
+    }
+
+    @GuardedBy("mLock")
+    @Override // from PerUserSystemService
+    protected boolean updateLocked(boolean disabled) {
+        final boolean enabledChanged = super.updateLocked(disabled);
+        if (enabledChanged) {
+            if (!isEnabledLocked()) {
+                // Clear the remote service for the next call
+                updateRemoteServiceLocked();
+            }
+        }
+        return enabledChanged;
+    }
+
+    /**
+     * Notifies the service of a new smartspace session.
+     */
+    @GuardedBy("mLock")
+    public void onCreateSmartspaceSessionLocked(@NonNull SmartspaceConfig smartspaceConfig,
+            @NonNull SmartspaceSessionId sessionId, @NonNull IBinder token) {
+        final boolean serviceExists = resolveService(sessionId,
+                s -> s.onCreateSmartspaceSession(smartspaceConfig, sessionId));
+
+        if (serviceExists && !mSessionInfos.containsKey(sessionId)) {
+            final SmartspaceSessionInfo sessionInfo = new SmartspaceSessionInfo(
+                    sessionId, smartspaceConfig, token, () -> {
+                synchronized (mLock) {
+                    onDestroyLocked(sessionId);
+                }
+            });
+            if (sessionInfo.linkToDeath()) {
+                mSessionInfos.put(sessionId, sessionInfo);
+            } else {
+                // destroy the session if calling process is already dead
+                onDestroyLocked(sessionId);
+            }
+        }
+    }
+
+    /**
+     * Records an smartspace event to the service.
+     */
+    @GuardedBy("mLock")
+    public void notifySmartspaceEventLocked(@NonNull SmartspaceSessionId sessionId,
+            @NonNull SmartspaceTargetEvent event) {
+        final SmartspaceSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId, s -> s.notifySmartspaceEvent(sessionId, event));
+    }
+
+    /**
+     * Requests the service to return smartspace results of an input query.
+     */
+    @GuardedBy("mLock")
+    public void requestSmartspaceUpdateLocked(@NonNull SmartspaceSessionId sessionId) {
+        final SmartspaceSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId,
+                s -> s.requestSmartspaceUpdate(sessionId));
+    }
+
+    /**
+     * Registers a callback for continuous updates of predicted apps or shortcuts.
+     */
+    @GuardedBy("mLock")
+    public void registerSmartspaceUpdatesLocked(@NonNull SmartspaceSessionId sessionId,
+            @NonNull ISmartspaceCallback callback) {
+        final SmartspaceSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return;
+        final boolean serviceExists = resolveService(sessionId,
+                s -> s.registerSmartspaceUpdates(sessionId, callback));
+        if (serviceExists) {
+            sessionInfo.addCallbackLocked(callback);
+        }
+    }
+
+    /**
+     * Unregisters a callback for continuous updates of predicted apps or shortcuts.
+     */
+    @GuardedBy("mLock")
+    public void unregisterSmartspaceUpdatesLocked(@NonNull SmartspaceSessionId sessionId,
+            @NonNull ISmartspaceCallback callback) {
+        final SmartspaceSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return;
+        final boolean serviceExists = resolveService(sessionId,
+                s -> s.unregisterSmartspaceUpdates(sessionId, callback));
+        if (serviceExists) {
+            sessionInfo.removeCallbackLocked(callback);
+        }
+    }
+
+    /**
+     * Notifies the service of the end of an existing smartspace session.
+     */
+    @GuardedBy("mLock")
+    public void onDestroyLocked(@NonNull SmartspaceSessionId sessionId) {
+        if (isDebug()) {
+            Slog.d(TAG, "onDestroyLocked(): sessionId=" + sessionId);
+        }
+        final SmartspaceSessionInfo sessionInfo = mSessionInfos.remove(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId, s -> s.onDestroySmartspaceSession(sessionId));
+        sessionInfo.destroy();
+    }
+
+    @Override
+    public void onFailureOrTimeout(boolean timedOut) {
+        if (isDebug()) {
+            Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut);
+        }
+        // Do nothing, we are just proxying to the smartspace ui service
+    }
+
+    @Override
+    public void onConnectedStateChanged(boolean connected) {
+        if (isDebug()) {
+            Slog.d(TAG, "onConnectedStateChanged(): connected=" + connected);
+        }
+        if (connected) {
+            synchronized (mLock) {
+                if (mZombie) {
+                    // Validation check - shouldn't happen
+                    if (mRemoteService == null) {
+                        Slog.w(TAG, "Cannot resurrect sessions because remote service is null");
+                        return;
+                    }
+                    mZombie = false;
+                    resurrectSessionsLocked();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onServiceDied(RemoteSmartspaceService service) {
+        if (isDebug()) {
+            Slog.w(TAG, "onServiceDied(): service=" + service);
+        }
+        synchronized (mLock) {
+            mZombie = true;
+        }
+        updateRemoteServiceLocked();
+    }
+
+    @GuardedBy("mLock")
+    private void updateRemoteServiceLocked() {
+        if (mRemoteService != null) {
+            mRemoteService.destroy();
+            mRemoteService = null;
+        }
+    }
+
+    void onPackageUpdatedLocked() {
+        if (isDebug()) {
+            Slog.v(TAG, "onPackageUpdatedLocked()");
+        }
+        destroyAndRebindRemoteService();
+    }
+
+    void onPackageRestartedLocked() {
+        if (isDebug()) {
+            Slog.v(TAG, "onPackageRestartedLocked()");
+        }
+        destroyAndRebindRemoteService();
+    }
+
+    private void destroyAndRebindRemoteService() {
+        if (mRemoteService == null) {
+            return;
+        }
+
+        if (isDebug()) {
+            Slog.d(TAG, "Destroying the old remote service.");
+        }
+        mRemoteService.destroy();
+        mRemoteService = null;
+
+        synchronized (mLock) {
+            mZombie = true;
+        }
+        mRemoteService = getRemoteServiceLocked();
+        if (mRemoteService != null) {
+            if (isDebug()) {
+                Slog.d(TAG, "Rebinding to the new remote service.");
+            }
+            mRemoteService.reconnect();
+        }
+    }
+
+    /**
+     * Called after the remote service connected, it's used to restore state from a 'zombie'
+     * service (i.e., after it died).
+     */
+    private void resurrectSessionsLocked() {
+        final int numSessions = mSessionInfos.size();
+        if (isDebug()) {
+            Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on "
+                    + numSessions + " sessions.");
+        }
+
+        for (SmartspaceSessionInfo sessionInfo : mSessionInfos.values()) {
+            sessionInfo.resurrectSessionLocked(this, sessionInfo.mToken);
+        }
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    protected boolean resolveService(
+            @NonNull final SmartspaceSessionId sessionId,
+            @NonNull final AbstractRemoteService.AsyncRequest<ISmartspaceService> cb) {
+
+        final RemoteSmartspaceService service = getRemoteServiceLocked();
+        if (service != null) {
+            service.executeOnResolvedService(cb);
+        }
+        return service != null;
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private RemoteSmartspaceService getRemoteServiceLocked() {
+        if (mRemoteService == null) {
+            final String serviceName = getComponentNameLocked();
+            if (serviceName == null) {
+                if (mMaster.verbose) {
+                    Slog.v(TAG, "getRemoteServiceLocked(): not set");
+                }
+                return null;
+            }
+            ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+
+            mRemoteService = new RemoteSmartspaceService(getContext(),
+                    SmartspaceService.SERVICE_INTERFACE, serviceComponent, mUserId, this,
+                    mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+        }
+
+        return mRemoteService;
+    }
+
+    private static final class SmartspaceSessionInfo {
+        private static final boolean DEBUG = false;  // Do not submit with true
+        @NonNull
+        final IBinder mToken;
+        @NonNull
+        final IBinder.DeathRecipient mDeathRecipient;
+        @NonNull
+        private final SmartspaceSessionId mSessionId;
+        @NonNull
+        private final SmartspaceConfig mSmartspaceConfig;
+        private final RemoteCallbackList<ISmartspaceCallback> mCallbacks =
+                new RemoteCallbackList<ISmartspaceCallback>() {
+                    @Override
+                    public void onCallbackDied(ISmartspaceCallback callback) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "Binder died for session Id=" + mSessionId
+                                    + " and callback=" + callback.asBinder());
+                        }
+                        if (mCallbacks.getRegisteredCallbackCount() == 0) {
+                            destroy();
+                        }
+                    }
+                };
+
+        SmartspaceSessionInfo(
+                @NonNull final SmartspaceSessionId id,
+                @NonNull final SmartspaceConfig context,
+                @NonNull final IBinder token,
+                @NonNull final IBinder.DeathRecipient deathRecipient) {
+            if (DEBUG) {
+                Slog.d(TAG, "Creating SmartspaceSessionInfo for session Id=" + id);
+            }
+            mSessionId = id;
+            mSmartspaceConfig = context;
+            mToken = token;
+            mDeathRecipient = deathRecipient;
+        }
+
+        void addCallbackLocked(ISmartspaceCallback callback) {
+            if (DEBUG) {
+                Slog.d(TAG, "Storing callback for session Id=" + mSessionId
+                        + " and callback=" + callback.asBinder());
+            }
+            mCallbacks.register(callback);
+        }
+
+        void removeCallbackLocked(ISmartspaceCallback callback) {
+            if (DEBUG) {
+                Slog.d(TAG, "Removing callback for session Id=" + mSessionId
+                        + " and callback=" + callback.asBinder());
+            }
+            mCallbacks.unregister(callback);
+        }
+
+        boolean linkToDeath() {
+            try {
+                mToken.linkToDeath(mDeathRecipient, 0);
+            } catch (RemoteException e) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Caller is dead before session can be started, sessionId: "
+                            + mSessionId);
+                }
+                return false;
+            }
+            return true;
+        }
+
+        void destroy() {
+            if (DEBUG) {
+                Slog.d(TAG, "Removing all callbacks for session Id=" + mSessionId
+                        + " and " + mCallbacks.getRegisteredCallbackCount() + " callbacks.");
+            }
+            if (mToken != null) {
+                mToken.unlinkToDeath(mDeathRecipient, 0);
+            }
+            mCallbacks.kill();
+        }
+
+        void resurrectSessionLocked(SmartspacePerUserService service, IBinder token) {
+            int callbackCount = mCallbacks.getRegisteredCallbackCount();
+            if (DEBUG) {
+                Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
+                        + ") for session Id=" + mSessionId + " and "
+                        + callbackCount + " callbacks.");
+            }
+            service.onCreateSmartspaceSessionLocked(mSmartspaceConfig, mSessionId, token);
+        }
+    }
+}