Merge changes Id71b4cbc,I8dff0a12,Ife3d1390 into rvc-dev

* changes:
  Add startDreamActivity request verification
  Make DreamService use an Activity
  Clean up dreamland
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 4e3aa7d..266a06a 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/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
index ff7cef9..41fdd0b 100644
--- a/core/java/android/service/dreams/DreamManagerInternal.java
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -16,6 +16,8 @@
 
 package android.service.dreams;
 
+import android.content.ComponentName;
+
 /**
  * Dream manager local system service interface.
  *
@@ -42,4 +44,13 @@
      * Called by the power manager to determine whether a dream is running.
      */
     public abstract boolean isDreaming();
+
+    /**
+     * Called by the ActivityTaskManagerService to verify that the startDreamActivity
+     * request comes from the current active dream component.
+     *
+     * @param doze If true returns the current active doze component. Otherwise, returns the
+     *             active dream component.
+     */
+    public abstract ComponentName getActiveDreamComponent(boolean doze);
 }
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index de4a551..28f4929 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));
     }
 
     /**
@@ -602,6 +608,8 @@
      * Marks this dream as windowless.  Only available to doze dreams.
      *
      * @hide
+     *
+     * TODO: Remove @UnsupportedAppUsage.
      */
     @UnsupportedAppUsage
     public void setWindowless(boolean windowless) {
@@ -670,14 +678,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 +708,7 @@
         if (mDozing) {
             mDozing = false;
             try {
-                mSandman.stopDozing(mWindowToken);
+                mDreamManager.stopDozing(mDreamToken);
             } catch (RemoteException ex) {
                 // system server died
             }
@@ -875,14 +883,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 +904,25 @@
     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.");
+        } else if (!mActivity.isFinishing()) {
+            // In case the activity is not finished yet, do it now. This can happen if someone calls
+            // finish() directly, without going through wakeUp().
+            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 +952,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 +991,14 @@
             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();
+        } else {
+            finish();
         }
 
-        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,36 +1176,32 @@
         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(new Runnable() {
-                @Override
-                public void run() {
-                    DreamService.this.attach(windowToken, canDoze, started);
-                }
-            });
+            mHandler.post(() -> DreamService.this.attach(dreamToken, canDoze, started));
         }
 
         @Override
         public void detach() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    DreamService.this.detach();
-                }
-            });
+            mHandler.post(DreamService.this::detach);
         }
 
         @Override
         public void wakeUp() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    DreamService.this.wakeUp(true /*fromSystem*/);
-                }
-            });
+            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 1c6aba7..7690b94 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 fbad8de..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 com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -27,10 +24,10 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
 import android.os.IRemoteCallback;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.IBinder.DeathRecipient;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -38,15 +35,14 @@
 import android.service.dreams.IDreamService;
 import android.util.Slog;
 import android.view.IWindowManager;
-import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
 import java.io.PrintWriter;
 import java.util.NoSuchElementException;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-
 /**
  * Internal controller for starting and stopping the current dream and managing related state.
  *
@@ -86,12 +82,9 @@
         }
     };
 
-    private final Runnable mStopStubbornDreamRunnable = new Runnable() {
-        @Override
-        public void run() {
-            Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted");
-            stopDream(true /*immediate*/);
-        }
+    private final Runnable mStopStubbornDreamRunnable = () -> {
+        Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted");
+        stopDream(true /*immediate*/);
     };
 
     public DreamController(Context context, Handler handler, Listener listener) {
@@ -140,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);
@@ -216,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) {
@@ -238,18 +220,7 @@
             }
             oldDream.releaseWakeLockIfNeeded();
 
-            try {
-                mIWindowManager.removeWindowToken(oldDream.mToken, DEFAULT_DISPLAY);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Error removing window token for dream.", ex);
-            }
-
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mListener.onDreamStopped(oldDream.mToken);
-                }
-            });
+            mHandler.post(() -> mListener.onDreamStopped(oldDream.mToken));
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
@@ -313,13 +284,10 @@
         // May be called on any thread.
         @Override
         public void binderDied() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mService = null;
-                    if (mCurrentDream == DreamRecord.this) {
-                        stopDream(true /*immediate*/);
-                    }
+            mHandler.post(() -> {
+                mService = null;
+                if (mCurrentDream == DreamRecord.this) {
+                    stopDream(true /*immediate*/);
                 }
             });
         }
@@ -327,16 +295,13 @@
         // May be called on any thread.
         @Override
         public void onServiceConnected(ComponentName name, final IBinder service) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mConnected = true;
-                    if (mCurrentDream == DreamRecord.this && mService == null) {
-                        attach(IDreamService.Stub.asInterface(service));
-                        // Wake lock will be released once dreaming starts.
-                    } else {
-                        releaseWakeLockIfNeeded();
-                    }
+            mHandler.post(() -> {
+                mConnected = true;
+                if (mCurrentDream == DreamRecord.this && mService == null) {
+                    attach(IDreamService.Stub.asInterface(service));
+                    // Wake lock will be released once dreaming starts.
+                } else {
+                    releaseWakeLockIfNeeded();
                 }
             });
         }
@@ -344,13 +309,10 @@
         // May be called on any thread.
         @Override
         public void onServiceDisconnected(ComponentName name) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mService = null;
-                    if (mCurrentDream == DreamRecord.this) {
-                        stopDream(true /*immediate*/);
-                    }
+            mHandler.post(() -> {
+                mService = null;
+                if (mCurrentDream == DreamRecord.this) {
+                    stopDream(true /*immediate*/);
                 }
             });
         }
@@ -373,4 +335,4 @@
             }
         };
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 3052e3c..a74be13 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -268,6 +268,10 @@
         }
     }
 
+    private ComponentName getActiveDreamComponentInternal(boolean doze) {
+        return chooseDreamForUser(doze, ActivityManager.getCurrentUser());
+    }
+
     private ComponentName chooseDreamForUser(boolean doze, int userId) {
         if (doze) {
             ComponentName dozeComponent = getDozeComponent(userId);
@@ -671,6 +675,11 @@
         public boolean isDreaming() {
             return isDreamingInternal();
         }
+
+        @Override
+        public ComponentName getActiveDreamComponent(boolean doze) {
+            return getActiveDreamComponentInternal(doze);
+        }
     }
 
     private final Runnable mSystemPropertiesChanged = new Runnable() {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a467e73..5603a3f 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -335,6 +335,7 @@
         int filterCallingUid;
         PendingIntentRecord originatingPendingIntent;
         boolean allowBackgroundActivityStart;
+        boolean isDream;
 
         /**
          * If set to {@code true}, allows this activity start to look into
@@ -386,6 +387,7 @@
             filterCallingUid = UserHandle.USER_NULL;
             originatingPendingIntent = null;
             allowBackgroundActivityStart = false;
+            isDream = false;
         }
 
         /**
@@ -426,6 +428,7 @@
             filterCallingUid = request.filterCallingUid;
             originatingPendingIntent = request.originatingPendingIntent;
             allowBackgroundActivityStart = request.allowBackgroundActivityStart;
+            isDream = request.isDream;
         }
 
         /**
@@ -969,7 +972,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);
             }
@@ -1179,13 +1182,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 =
@@ -2669,6 +2677,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 9458939..693a5e4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -212,6 +212,8 @@
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
+import android.service.dreams.DreamActivity;
+import android.service.dreams.DreamManagerInternal;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionManagerInternal;
 import android.sysprop.DisplayProperties;
@@ -1231,6 +1233,52 @@
     }
 
     @Override
+    public boolean startDreamActivity(Intent intent) {
+        final WindowProcessController process = mProcessMap.getProcess(Binder.getCallingPid());
+        final long origId = Binder.clearCallingIdentity();
+
+        // The dream activity is only called for non-doze dreams.
+        final ComponentName currentDream = LocalServices.getService(DreamManagerInternal.class)
+                .getActiveDreamComponent(/* doze= */ false);
+
+        if (currentDream == null || currentDream.getPackageName() == null
+                || !currentDream.getPackageName().equals(process.mInfo.packageName)) {
+            Slog.e(TAG, "Calling package is not the current dream package. "
+                    + "Aborting startDreamActivity...");
+            return false;
+        }
+
+        final ActivityInfo a = new ActivityInfo();
+        a.theme = com.android.internal.R.style.Theme_Dream;
+        a.exported = true;
+        a.name = DreamActivity.class.getName();
+
+
+        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;
+
+        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 +2451,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;
                     }