Make DreamService use an Activity

Currently, the DreamService uses a floating Window to display the
content of the dream on the screen. This introduces difficulties in the
interactions with the Assistant application, which is an activity.
By design, if the Assistant is invoked while the device is dreaming, the
Assistant should be shown on top. However, since floating windows are
always drawn on top of all activities, the Assistant can't be shown on
top of the Dream.

Here, we migrate the implementation of the DreamService to use an
Activity (DreamActivity). Since the screensaver application is not part
of the framework, we can't declare the activity in their AndroidManifest.xml.
Therefore, we start the dream activity with a dedicated method in
ActivityTaskManagerService.

Bug: 133216167
Test: 1. m && ./vendor/google/tools/flashall
      2. Go to Settings > Device Preferences > Screensaver > Start now
      3. Verify dream appears
      4. Click any key to wake up the dream
      5. Verify that dream disappears

Merged-In: I8dff0a124cd1b41fb925a73528305431b49ee06d
Change-Id: I8dff0a124cd1b41fb925a73528305431b49ee06d
(cherry picked from commit 148478a85e25c86e4b333295dec03aadedc17e1a)
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 5b61402..a55b2c8 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -98,6 +98,14 @@
             in ProfilerInfo profilerInfo, in Bundle options, int userId);
     boolean startNextMatchingActivity(in IBinder callingActivity,
             in Intent intent, in Bundle options);
+
+    /**
+    *  The DreamActivity has to be started in a special way that does not involve the PackageParser.
+    *  The DreamActivity is a framework component inserted in the dream application process. Hence,
+    *  it is not declared in the application's manifest and cannot be parsed. startDreamActivity
+    *  creates the activity and starts it without reaching out to the PackageParser.
+    */
+    boolean startDreamActivity(in Intent intent);
     int startActivityIntentSender(in IApplicationThread caller,
             in IIntentSender target, in IBinder whitelistToken, in Intent fillInIntent,
             in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
new file mode 100644
index 0000000..8cdd24e
--- /dev/null
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.dreams;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * The Activity used by the {@link DreamService} to draw screensaver content
+ * on the screen. This activity runs in dream application's process, but is started by a
+ * specialized method: {@link com.android.server.wm.ActivityTaskManagerService#startDreamActivity}.
+ * Hence, it does not have to be declared in the dream application's manifest.
+ *
+ * We use an activity as the dream canvas, because it interacts easier with other activities on
+ * the screen (compared to a hover window). However, the DreamService is in charge of the dream and
+ * it receives all Window.Callbacks from its main window. Since a window can have only one callback
+ * receiver, the activity will not receive any window callbacks.
+ *
+ * Prior to the DreamActivity, the DreamService used to work with a hovering window and give the
+ * screensaver application control over that window. The DreamActivity is a replacement to that
+ * hover window. Using an activity allows for better-defined interactions with the rest of the
+ * activities on screen. The switch to DreamActivity should be transparent to the screensaver
+ * application, i.e. the application will still use DreamService APIs and not notice that the
+ * system is using an activity behind the scenes.
+ *
+ * @hide
+ */
+public class DreamActivity extends Activity {
+    static final String EXTRA_CALLBACK = "binder";
+
+    public DreamActivity() {}
+
+    @Override
+    public void onCreate(@Nullable Bundle bundle) {
+        super.onCreate(bundle);
+
+        DreamService.DreamServiceWrapper callback =
+                (DreamService.DreamServiceWrapper) getIntent().getIBinderExtra(EXTRA_CALLBACK);
+
+        if (callback != null) {
+            callback.onActivityCreated(this);
+        }
+    }
+}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 96a89bc..b31238c 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -15,25 +15,29 @@
  */
 package android.service.dreams;
 
+import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
+
 import android.annotation.IdRes;
 import android.annotation.LayoutRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Activity;
+import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
 import android.app.Service;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.ColorDrawable;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
+import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.view.ActionMode;
@@ -48,10 +52,8 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
-import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
 
-import com.android.internal.policy.PhoneWindow;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.DumpUtils.Dump;
 
@@ -176,10 +178,11 @@
      */
     public static final String DREAM_META_DATA = "android.service.dream";
 
-    private final IDreamManager mSandman;
-    private final Handler mHandler = new Handler();
-    private IBinder mWindowToken;
+    private final IDreamManager mDreamManager;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private IBinder mDreamToken;
     private Window mWindow;
+    private Activity mActivity;
     private boolean mInteractive;
     private boolean mLowProfile = true;
     private boolean mFullscreen;
@@ -195,8 +198,11 @@
 
     private boolean mDebug = false;
 
+    private DreamServiceWrapper mDreamServiceWrapper;
+    private Runnable mDispatchAfterOnAttachedToWindow;
+
     public DreamService() {
-        mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
+        mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
     }
 
     /**
@@ -670,14 +676,14 @@
     }
 
     private void updateDoze() {
-        if (mWindowToken == null) {
-            Slog.w(TAG, "Updating doze without a window token.");
+        if (mDreamToken == null) {
+            Slog.w(TAG, "Updating doze without a dream token.");
             return;
         }
 
         if (mDozing) {
             try {
-                mSandman.startDozing(mWindowToken, mDozeScreenState, mDozeScreenBrightness);
+                mDreamManager.startDozing(mDreamToken, mDozeScreenState, mDozeScreenBrightness);
             } catch (RemoteException ex) {
                 // system server died
             }
@@ -700,7 +706,7 @@
         if (mDozing) {
             mDozing = false;
             try {
-                mSandman.stopDozing(mWindowToken);
+                mDreamManager.stopDozing(mDreamToken);
             } catch (RemoteException ex) {
                 // system server died
             }
@@ -875,14 +881,15 @@
      * </p>
      */
     public void onWakeUp() {
-        finish();
+        mActivity.finishAndRemoveTask();
     }
 
     /** {@inheritDoc} */
     @Override
     public final IBinder onBind(Intent intent) {
         if (mDebug) Slog.v(TAG, "onBind() intent = " + intent);
-        return new DreamServiceWrapper();
+        mDreamServiceWrapper = new DreamServiceWrapper();
+        return mDreamServiceWrapper;
     }
 
     /**
@@ -895,20 +902,29 @@
     public final void finish() {
         if (mDebug) Slog.v(TAG, "finish(): mFinished=" + mFinished);
 
+        if (mActivity == null) {
+            Slog.w(TAG, "Finish was called before the dream was attached.");
+            return;
+        }
+
+        // In case the activity is not finished yet, do it now. This can happen if someone calls
+        // finish() directly, without going through wakeUp().
+        if (!mActivity.isFinishing()) {
+            mActivity.finishAndRemoveTask();
+            return;
+        }
+
         if (!mFinished) {
             mFinished = true;
 
-            if (mWindowToken == null) {
-                Slog.w(TAG, "Finish was called before the dream was attached.");
-            } else {
-                try {
-                    mSandman.finishSelf(mWindowToken, true /*immediate*/);
-                } catch (RemoteException ex) {
-                    // system server died
-                }
+            try {
+                // finishSelf will unbind the dream controller from the dream service. This will
+                // trigger DreamService.this.onDestroy and DreamService.this will die.
+                mDreamManager.finishSelf(mDreamToken, true /*immediate*/);
+            } catch (RemoteException ex) {
+                // system server died
             }
 
-            stopSelf(); // if launched via any other means
         }
     }
 
@@ -938,11 +954,11 @@
             // Now tell the system we are waking gently, unless we already told
             // it we were finishing immediately.
             if (!fromSystem && !mFinished) {
-                if (mWindowToken == null) {
+                if (mActivity == null) {
                     Slog.w(TAG, "WakeUp was called before the dream was attached.");
                 } else {
                     try {
-                        mSandman.finishSelf(mWindowToken, false /*immediate*/);
+                        mDreamManager.finishSelf(mDreamToken, false /*immediate*/);
                     } catch (RemoteException ex) {
                         // system server died
                     }
@@ -977,20 +993,12 @@
             onDreamingStopped();
         }
 
-        if (mWindow != null) {
-            // force our window to be removed synchronously
-            if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager");
-            mWindow.getWindowManager().removeViewImmediate(mWindow.getDecorView());
-            mWindow = null;
+        if (mActivity != null && !mActivity.isFinishing()) {
+            mActivity.finishAndRemoveTask();
         }
 
-        if (mWindowToken != null) {
-            // the following will print a log message if it finds any other leaked windows
-            WindowManagerGlobal.getInstance().closeAll(mWindowToken,
-                    this.getClass().getName(), "Dream");
-            mWindowToken = null;
-            mCanDoze = false;
-        }
+        mDreamToken = null;
+        mCanDoze = false;
     }
 
     /**
@@ -998,95 +1006,107 @@
      *
      * Must run on mHandler.
      *
-     * @param windowToken A window token that will allow a window to be created in the correct layer.
+     * @param dreamToken Token for this dream service.
      * @param started A callback that will be invoked once onDreamingStarted has completed.
      */
-    private final void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started) {
-        if (mWindowToken != null) {
-            Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken);
+    private void attach(IBinder dreamToken, boolean canDoze, IRemoteCallback started) {
+        if (mActivity != null) {
+            Slog.e(TAG, "attach() called when dream with token=" + mDreamToken
+                    + " already attached");
             return;
         }
         if (mFinished || mWaking) {
             Slog.w(TAG, "attach() called after dream already finished");
             try {
-                mSandman.finishSelf(windowToken, true /*immediate*/);
+                mDreamManager.finishSelf(dreamToken, true /*immediate*/);
             } catch (RemoteException ex) {
                 // system server died
             }
             return;
         }
 
-        mWindowToken = windowToken;
+        mDreamToken = dreamToken;
         mCanDoze = canDoze;
         if (mWindowless && !mCanDoze) {
             throw new IllegalStateException("Only doze dreams can be windowless");
         }
-        if (!mWindowless) {
-            mWindow = new PhoneWindow(this);
-            mWindow.setCallback(this);
-            mWindow.requestFeature(Window.FEATURE_NO_TITLE);
-            mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000));
-            mWindow.setFormat(PixelFormat.OPAQUE);
 
-            if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s",
-                    windowToken, WindowManager.LayoutParams.TYPE_DREAM));
-
-            WindowManager.LayoutParams lp = mWindow.getAttributes();
-            lp.type = WindowManager.LayoutParams.TYPE_DREAM;
-            lp.token = windowToken;
-            lp.windowAnimations = com.android.internal.R.style.Animation_Dream;
-            lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                        | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
-                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                        | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
-                        | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
-                        | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
-                        | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
-                        );
-            mWindow.setAttributes(lp);
-            // Workaround: Currently low-profile and in-window system bar backgrounds don't go
-            // along well. Dreams usually don't need such bars anyways, so disable them by default.
-            mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
-            mWindow.setWindowManager(null, windowToken, "dream", true);
-
-            applySystemUiVisibilityFlags(
-                    (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
-                    View.SYSTEM_UI_FLAG_LOW_PROFILE);
-
-            try {
-                getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes());
-            } catch (WindowManager.BadTokenException ex) {
-                // This can happen because the dream manager service will remove the token
-                // immediately without necessarily waiting for the dream to start.
-                // We should receive a finish message soon.
-                Slog.i(TAG, "attach() called after window token already removed, dream will "
-                        + "finish soon");
-                mWindow = null;
-                return;
-            }
-        }
-        // We need to defer calling onDreamingStarted until after onWindowAttached,
-        // which is posted to the handler by addView, so we post onDreamingStarted
-        // to the handler also.  Need to watch out here in case detach occurs before
-        // this callback is invoked.
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (mWindow != null || mWindowless) {
-                    if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()");
-                    mStarted = true;
+        mDispatchAfterOnAttachedToWindow = () -> {
+            if (mWindow != null || mWindowless) {
+                mStarted = true;
+                try {
+                    onDreamingStarted();
+                } finally {
                     try {
-                        onDreamingStarted();
-                    } finally {
-                        try {
-                            started.sendResult(null);
-                        } catch (RemoteException e) {
-                            throw e.rethrowFromSystemServer();
-                        }
+                        started.sendResult(null);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
                     }
                 }
             }
-        });
+        };
+
+        // We need to defer calling onDreamingStarted until after the activity is created.
+        // If the dream is windowless, we can call it immediately. Otherwise, we wait
+        // for the DreamActivity to report onActivityCreated via
+        // DreamServiceWrapper.onActivityCreated.
+        if (!mWindowless) {
+            Intent i = new Intent(this, DreamActivity.class);
+            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            i.putExtra(DreamActivity.EXTRA_CALLBACK, mDreamServiceWrapper);
+
+            try {
+                if (!ActivityTaskManager.getService().startDreamActivity(i)) {
+                    detach();
+                    return;
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Could not connect to activity task manager to start dream activity");
+                e.rethrowFromSystemServer();
+            }
+        } else {
+            mDispatchAfterOnAttachedToWindow.run();
+        }
+    }
+
+    private void onWindowCreated(Window w) {
+        mWindow = w;
+        mWindow.setCallback(this);
+        mWindow.setType(TYPE_DREAM);
+        mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+
+        WindowManager.LayoutParams lp = mWindow.getAttributes();
+        lp.windowAnimations = com.android.internal.R.style.Animation_Dream;
+        lp.flags |= (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+                    | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                    | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+                    | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
+                    | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+                    | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
+                    | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
+                    );
+        mWindow.setAttributes(lp);
+        // Workaround: Currently low-profile and in-window system bar backgrounds don't go
+        // along well. Dreams usually don't need such bars anyways, so disable them by default.
+        mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+
+        applySystemUiVisibilityFlags(
+                (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
+                View.SYSTEM_UI_FLAG_LOW_PROFILE);
+
+        mWindow.getDecorView().addOnAttachStateChangeListener(
+                new View.OnAttachStateChangeListener() {
+                    @Override
+                    public void onViewAttachedToWindow(View v) {
+                        mDispatchAfterOnAttachedToWindow.run();
+                    }
+
+                    @Override
+                    public void onViewDetachedFromWindow(View v) {
+                        finish();
+                    }
+                });
     }
 
     private boolean getWindowFlagValue(int flag, boolean defaultValue) {
@@ -1131,10 +1151,10 @@
     /** @hide */
     protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print(TAG + ": ");
-        if (mWindowToken == null) {
+        if (mFinished) {
             pw.println("stopped");
         } else {
-            pw.println("running (token=" + mWindowToken + ")");
+            pw.println("running (dreamToken=" + mDreamToken + ")");
         }
         pw.println("  window: " + mWindow);
         pw.print("  flags:");
@@ -1156,11 +1176,16 @@
         return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
     }
 
-    private final class DreamServiceWrapper extends IDreamService.Stub {
+    /**
+     * The DreamServiceWrapper is used as a gateway to the system_server, where DreamController
+     * uses it to control the DreamService. It is also used to receive callbacks from the
+     * DreamActivity.
+     */
+    final class DreamServiceWrapper extends IDreamService.Stub {
         @Override
-        public void attach(final IBinder windowToken, final boolean canDoze,
+        public void attach(final IBinder dreamToken, final boolean canDoze,
                 IRemoteCallback started) {
-            mHandler.post(() -> DreamService.this.attach(windowToken, canDoze, started));
+            mHandler.post(() -> DreamService.this.attach(dreamToken, canDoze, started));
         }
 
         @Override
@@ -1172,5 +1197,11 @@
         public void wakeUp() {
             mHandler.post(() -> DreamService.this.wakeUp(true /*fromSystem*/));
         }
+
+        /** @hide */
+        void onActivityCreated(DreamActivity a) {
+            mActivity = a;
+            onWindowCreated(a.getWindow());
+        }
     }
 }
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d595459..b2b3241 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1612,6 +1612,7 @@
   <java-symbol type="style" name="TextAppearance.SlidingTabNormal" />
   <java-symbol type="style" name="Theme.DeviceDefault.Dialog.NoFrame" />
   <java-symbol type="style" name="Theme.IconMenu" />
+  <java-symbol type="style" name="Theme.Dream" />
   <java-symbol type="style" name="Theme.DeviceDefault.VoiceInteractionSession" />
   <java-symbol type="style" name="Pointer" />
   <java-symbol type="style" name="LargePointer" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 5d9cb48..2ef0c92 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -701,6 +701,11 @@
         <item name="windowNoDisplay">true</item>
     </style>
 
+    <style name="Theme.Dream">
+        <item name="windowBackground">@null</item>
+        <item name="windowDisablePreview">true</item>
+    </style>
+
     <!-- Default theme for dialog windows and activities (on API level 10 and lower),
          which is used by the
          {@link android.app.Dialog} class.  This changes the window to be
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 2ed4f39..5320453 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -16,9 +16,6 @@
 
 package com.android.server.dreams;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -136,14 +133,6 @@
             MetricsLogger.visible(mContext,
                     mCurrentDream.mCanDoze ? MetricsEvent.DOZING : MetricsEvent.DREAMING);
 
-            try {
-                mIWindowManager.addWindowToken(token, TYPE_DREAM, DEFAULT_DISPLAY);
-            } catch (RemoteException ex) {
-                Slog.e(TAG, "Unable to add window token for dream.", ex);
-                stopDream(true /*immediate*/);
-                return;
-            }
-
             Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
             intent.setComponent(name);
             intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
@@ -212,9 +201,6 @@
             }
 
             if (oldDream.mService != null) {
-                // Tell the dream that it's being stopped so that
-                // it can shut down nicely before we yank its window token out from
-                // under it.
                 try {
                     oldDream.mService.detach();
                 } catch (RemoteException ex) {
@@ -234,12 +220,6 @@
             }
             oldDream.releaseWakeLockIfNeeded();
 
-            try {
-                mIWindowManager.removeWindowToken(oldDream.mToken, DEFAULT_DISPLAY);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Error removing window token for dream.", ex);
-            }
-
             mHandler.post(() -> mListener.onDreamStopped(oldDream.mToken));
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
@@ -355,4 +335,4 @@
             }
         };
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 600a125..4ea5b2d 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -336,6 +336,7 @@
         int filterCallingUid;
         PendingIntentRecord originatingPendingIntent;
         boolean allowBackgroundActivityStart;
+        boolean isDream;
 
         /**
          * If set to {@code true}, allows this activity start to look into
@@ -387,6 +388,7 @@
             filterCallingUid = UserHandle.USER_NULL;
             originatingPendingIntent = null;
             allowBackgroundActivityStart = false;
+            isDream = false;
         }
 
         /**
@@ -427,6 +429,7 @@
             filterCallingUid = request.filterCallingUid;
             originatingPendingIntent = request.originatingPendingIntent;
             allowBackgroundActivityStart = request.allowBackgroundActivityStart;
+            isDream = request.isDream;
         }
 
         /**
@@ -970,7 +973,7 @@
                 restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
                         callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
                         request.originatingPendingIntent, request.allowBackgroundActivityStart,
-                        intent);
+                        request.isDream, intent);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             }
@@ -1180,13 +1183,18 @@
     boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
             final String callingPackage, int realCallingUid, int realCallingPid,
             WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
-            boolean allowBackgroundActivityStart, Intent intent) {
+            boolean allowBackgroundActivityStart, boolean isDream, Intent intent) {
         // don't abort for the most important UIDs
         final int callingAppId = UserHandle.getAppId(callingUid);
         if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
                 || callingAppId == Process.NFC_UID) {
             return false;
         }
+
+        // don't abort if this is the dream activity
+        if (isDream) {
+            return false;
+        }
         // don't abort if the callingUid has a visible window or is a persistent system process
         final int callingUidProcState = mService.getUidState(callingUid);
         final boolean callingUidHasAnyVisibleWindow =
@@ -2686,6 +2694,11 @@
         return this;
     }
 
+    ActivityStarter setIsDream(boolean isDream) {
+        mRequest.isDream = isDream;
+        return this;
+    }
+
     void dump(PrintWriter pw, String prefix) {
         prefix = prefix + "  ";
         pw.print(prefix);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5392257..dfc9aa8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -212,6 +212,7 @@
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
+import android.service.dreams.DreamActivity;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionManagerInternal;
 import android.sysprop.DisplayProperties;
@@ -1231,6 +1232,40 @@
     }
 
     @Override
+    public boolean startDreamActivity(Intent intent) {
+        final ActivityInfo a = new ActivityInfo();
+        a.theme = com.android.internal.R.style.Theme_Dream;
+        a.exported = true;
+        a.name = DreamActivity.class.getName();
+
+        final WindowProcessController process = mProcessMap.getProcess(Binder.getCallingPid());
+
+        a.packageName = process.mInfo.packageName;
+        a.applicationInfo = process.mInfo;
+        a.processName = process.mInfo.processName;
+        a.uiOptions = process.mInfo.uiOptions;
+        a.taskAffinity = "android:" + a.packageName + "/dream";
+        a.enabled = true;
+        a.launchMode = ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+
+        a.persistableMode = ActivityInfo.PERSIST_NEVER;
+        a.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+        a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            getActivityStartController().obtainStarter(intent, "dream")
+                    .setActivityInfo(a)
+                    .setIsDream(true)
+                    .execute();
+            return true;
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
     public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
             String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
@@ -2403,7 +2438,7 @@
         final ActivityStarter starter = getActivityStartController().obtainStarter(
                 null /* intent */, "moveTaskToFront");
         if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1,
-                -1, callerApp, null, false, null)) {
+                -1, callerApp, null, false, false, null)) {
             if (!isBackgroundActivityStartsEnabled()) {
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 8fa8119..4cce212 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -111,7 +111,7 @@
                 final ActivityStarter starter = mService.getActivityStartController().obtainStarter(
                         null /* intent */, "moveToFront");
                 if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid,
-                        callingPackage, -1, -1, callerApp, null, false, null)) {
+                        callingPackage, -1, -1, callerApp, null, false, false, null)) {
                     if (!mService.isBackgroundActivityStartsEnabled()) {
                         return;
                     }