diff options
11 files changed, 323 insertions, 191 deletions
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 4e3aa7d8ebc0..266a06a7aac4 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -98,6 +98,14 @@ interface IActivityTaskManager { 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 000000000000..8cdd24e0884d --- /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 ff7cef9d945a..41fdd0bfe477 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 @@ public abstract class DreamManagerInternal { * 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 de4a5511fcdb..28f492982a41 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.ViewGroup; 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 class DreamService extends Service implements Window.Callback { */ 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 @@ public class DreamService extends Service implements Window.Callback { 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 @@ public class DreamService extends Service implements Window.Callback { * Marks this dream as windowless. Only available to doze dreams. * * @hide + * + * TODO: Remove @UnsupportedAppUsage. */ @UnsupportedAppUsage public void setWindowless(boolean windowless) { @@ -670,14 +678,14 @@ public class DreamService extends Service implements Window.Callback { } 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 @@ public class DreamService extends Service implements Window.Callback { if (mDozing) { mDozing = false; try { - mSandman.stopDozing(mWindowToken); + mDreamManager.stopDozing(mDreamToken); } catch (RemoteException ex) { // system server died } @@ -875,14 +883,15 @@ public class DreamService extends Service implements Window.Callback { * </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 class DreamService extends Service implements Window.Callback { 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 @@ public class DreamService extends Service implements Window.Callback { // 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 @@ public class DreamService extends Service implements Window.Callback { 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 @@ public class DreamService extends Service implements Window.Callback { * * 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); + mDispatchAfterOnAttachedToWindow = () -> { + if (mWindow != null || mWindowless) { + mStarted = true; + try { + onDreamingStarted(); + } finally { + try { + started.sendResult(null); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + }; - applySystemUiVisibilityFlags( - (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), - View.SYSTEM_UI_FLAG_LOW_PROFILE); + // 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 { - 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; + 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(); } - // 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; - try { - onDreamingStarted(); - } finally { - try { - started.sendResult(null); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + } + + 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 @@ public class DreamService extends Service implements Window.Callback { /** @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 @@ public class DreamService extends Service implements Window.Callback { 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 1c6aba75dc6c..7690b94906dd 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 5d9cb48aa5ed..2ef0c927cc61 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -701,6 +701,11 @@ please see themes_device_defaults.xml. <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 fbad8dede87d..532045320988 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.Binder; 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.DreamService; 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 @@ final class DreamController { } }; - 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 @@ final class DreamController { 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 @@ final class DreamController { } 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 @@ final class DreamController { } 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 @@ final class DreamController { // 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 @@ final class DreamController { // 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 @@ final class DreamController { // 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 @@ final class DreamController { } }; } -}
\ 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 3052e3cdcd21..a74be1357e30 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 @@ public final class DreamManagerService extends SystemService { } } + 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 final class DreamManagerService extends SystemService { 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 a467e736abcc..5603a3fb6c9c 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 @@ class ActivityStarter { int filterCallingUid; PendingIntentRecord originatingPendingIntent; boolean allowBackgroundActivityStart; + boolean isDream; /** * If set to {@code true}, allows this activity start to look into @@ -386,6 +387,7 @@ class ActivityStarter { filterCallingUid = UserHandle.USER_NULL; originatingPendingIntent = null; allowBackgroundActivityStart = false; + isDream = false; } /** @@ -426,6 +428,7 @@ class ActivityStarter { filterCallingUid = request.filterCallingUid; originatingPendingIntent = request.originatingPendingIntent; allowBackgroundActivityStart = request.allowBackgroundActivityStart; + isDream = request.isDream; } /** @@ -969,7 +972,7 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 945893986ae3..693a5e499552 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.WorkSource; 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 8fa811915fc2..4cce212cb779 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 @@ class AppTaskImpl extends IAppTask.Stub { 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; } |