Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a72d7e53 authored by Galia Peycheva's avatar Galia Peycheva Committed by Android (Google) Code Review
Browse files

Merge changes Id71b4cbc,I8dff0a12,Ife3d1390 into rvc-dev

* changes:
  Add startDreamActivity request verification
  Make DreamService use an Activity
  Clean up dreamland
parents 61b8ac42 52e8922e
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -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,
+59 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -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);
}
+141 −125
Original line number Diff line number Diff line
@@ -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,21 +904,26 @@ 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*/);
                // 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,69 +991,98 @@ 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;
        mDreamToken = null;
        mCanDoze = false;
    }
    }

    /**
     * Called when the Dream is ready to be shown.
     *
     * 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");
        }

        mDispatchAfterOnAttachedToWindow = () -> {
            if (mWindow != null || mWindowless) {
                mStarted = true;
                try {
                    onDreamingStarted();
                } finally {
                    try {
                        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) {
            mWindow = new PhoneWindow(this);
            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);
            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
                    | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
                    | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
                    | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
                    );
@@ -1047,44 +1090,21 @@ public class DreamService extends Service implements Window.Callback {
        // 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() {
        mWindow.getDecorView().addOnAttachStateChangeListener(
                new View.OnAttachStateChangeListener() {
                    @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();
                        }
                    }
                    public void onViewAttachedToWindow(View v) {
                        mDispatchAfterOnAttachedToWindow.run();
                    }

                    @Override
                    public void onViewDetachedFromWindow(View v) {
                        finish();
                    }
                });
    }
@@ -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());
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -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" />
Loading