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

Commit 3703d693 authored by Bryce Lee's avatar Bryce Lee Committed by Android (Google) Code Review
Browse files

Merge "Protect against null mDreamToken." into main

parents a4e6d185 ada31b5a
Loading
Loading
Loading
Loading
+68 −154
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import static android.service.dreams.Flags.dreamHandlesBeingObscured;
import static android.service.dreams.Flags.dreamHandlesConfirmKeys;
import static android.service.dreams.Flags.startAndStopDozingInBackground;

import android.Manifest;
import android.annotation.FlaggedApi;
import android.annotation.IdRes;
import android.annotation.IntDef;
@@ -255,6 +254,7 @@ public class DreamService extends Service implements Window.Callback {
            "android.service.dream.DreamService.dream_overlay_component";

    private final IDreamManager mDreamManager;
    private final Handler mHandler;
    private IBinder mDreamToken;
    private Window mWindow;
    private Activity mActivity;
@@ -323,89 +323,19 @@ public class DreamService extends Service implements Window.Callback {
        /** Returns the associated service info */
        ServiceInfo getServiceInfo();

        /** Returns the handler to be used for any posted operation */
        Handler getHandler();

        /** Returns the package manager */
        PackageManager getPackageManager();

        /** Returns the resources */
        Resources getResources();

        /** Returns a specialized handler to ensure Runnables are not suspended */
        WakefulHandler getWakefulHandler();
    }

    /**
     * {@link WakefulHandler} is an interface for defining an object that helps post work without
     * being interrupted by doze state.
     *
     * @hide
     */
    public interface WakefulHandler {
        /** Posts a {@link Runnable} to be ran on the underlying {@link Handler}. */
        void postIfNeeded(Runnable r);

        /**
         * Returns the underlying {@link Handler}. Should only be used for passing the handler into
         * a function and not for directly calling methods on it.
         */
        Handler getHandler();
    }

    /**
     * {@link WakefulHandlerImpl} ensures work on a handler is not suspended by wrapping the call
     * with a partial wakelock. Note that this is only needed for Doze DreamService implementations.
     * In this case, the component should have wake lock permissions. When such permission is not
     * available, this class behaves like an ordinary handler.
     */
    private static final class WakefulHandlerImpl implements WakefulHandler {
        private static final String SERVICE_HANDLER_WAKE_LOCK_TAG = "dream:service:handler";
        private Context mContext;
        private Handler mHandler;

        private PowerManager.WakeLock mWakeLock;

        private PowerManager.WakeLock getWakeLock() {
            if (mContext.checkCallingOrSelfPermission(Manifest.permission.WAKE_LOCK)
                    != PERMISSION_GRANTED) {
                return null;
            }

            final PowerManager powerManager = mContext.getSystemService(PowerManager.class);

            if (powerManager == null) {
                return null;
            }

            return powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                    SERVICE_HANDLER_WAKE_LOCK_TAG);
        }

        WakefulHandlerImpl(Context context) {
            mContext = context;
            mHandler = new Handler(Looper.getMainLooper());
            mWakeLock = getWakeLock();
        }

        @Override
        public void postIfNeeded(Runnable r) {
            if (mHandler.getLooper().isCurrentThread()) {
                r.run();
            } else if (mWakeLock != null) {
                mHandler.post(mWakeLock.wrap(r));
            } else {
                mHandler.post(r);
            }
        }

        @Override
        public Handler getHandler() {
            return mHandler;
        }
    }

    private static final class DefaultInjector implements Injector {
        private Context mContext;
        private Class<?> mClassName;
        private WakefulHandler mWakefulHandler;

        public void init(Context context) {
            mContext = context;
@@ -449,14 +379,8 @@ public class DreamService extends Service implements Window.Callback {
        }

        @Override
        public WakefulHandler getWakefulHandler() {
            synchronized (this) {
                if (mWakefulHandler == null) {
                    mWakefulHandler = new WakefulHandlerImpl(mContext);
                }
            }

            return mWakefulHandler;
        public Handler getHandler() {
            return new Handler(Looper.getMainLooper());
        }

        @Override
@@ -485,6 +409,7 @@ public class DreamService extends Service implements Window.Callback {
        mInjector = injector;
        mInjector.init(this);
        mDreamManager = mInjector.getDreamManager();
        mHandler = mInjector.getHandler();
    }

    /**
@@ -527,7 +452,7 @@ public class DreamService extends Service implements Window.Callback {

                        // Simply wake up in the case the device is not locked.
                        if (!keyguardManager.isKeyguardLocked()) {
                            wakeUp(false);
                            wakeUp();
                            return true;
                        }

@@ -549,11 +474,11 @@ public class DreamService extends Service implements Window.Callback {

        if (!mInteractive) {
            if (mDebug) Slog.v(mTag, "Waking up on keyEvent");
            wakeUp(false);
            wakeUp();
            return true;
        } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            if (mDebug) Slog.v(mTag, "Waking up on back key");
            wakeUp(false);
            wakeUp();
            return true;
        }
        return mWindow.superDispatchKeyEvent(event);
@@ -564,7 +489,7 @@ public class DreamService extends Service implements Window.Callback {
    public boolean dispatchKeyShortcutEvent(KeyEvent event) {
        if (!mInteractive) {
            if (mDebug) Slog.v(mTag, "Waking up on keyShortcutEvent");
            wakeUp(false);
            wakeUp();
            return true;
        }
        return mWindow.superDispatchKeyShortcutEvent(event);
@@ -577,7 +502,7 @@ public class DreamService extends Service implements Window.Callback {
        // but finish()es on any other kind of activity
        if (!mInteractive && event.getActionMasked() == MotionEvent.ACTION_UP) {
            if (mDebug) Slog.v(mTag, "Waking up on touchEvent");
            wakeUp(false);
            wakeUp();
            return true;
        }
        return mWindow.superDispatchTouchEvent(event);
@@ -588,7 +513,7 @@ public class DreamService extends Service implements Window.Callback {
    public boolean dispatchTrackballEvent(MotionEvent event) {
        if (!mInteractive) {
            if (mDebug) Slog.v(mTag, "Waking up on trackballEvent");
            wakeUp(false);
            wakeUp();
            return true;
        }
        return mWindow.superDispatchTrackballEvent(event);
@@ -599,7 +524,7 @@ public class DreamService extends Service implements Window.Callback {
    public boolean dispatchGenericMotionEvent(MotionEvent event) {
        if (!mInteractive) {
            if (mDebug) Slog.v(mTag, "Waking up on genericMotionEvent");
            wakeUp(false);
            wakeUp();
            return true;
        }
        return mWindow.superDispatchGenericMotionEvent(event);
@@ -997,18 +922,10 @@ public class DreamService extends Service implements Window.Callback {
        }
    }

    private void postIfNeeded(Runnable runnable) {
        // The handler is based on the populated context is not ready at construction time.
        // therefore we fetch on demand.
        mInjector.getWakefulHandler().postIfNeeded(runnable);
    }
    private synchronized void updateDoze() {
        final IBinder dreamToken = mDreamToken;

    /**
     * Updates doze state. Note that this must be called on the mHandler.
     */
    private void updateDoze() {
        postIfNeeded(() -> {
            if (mDreamToken == null) {
        if (dreamToken == null) {
            Slog.w(mTag, "Updating doze without a dream token.");
            return;
        }
@@ -1020,12 +937,12 @@ public class DreamService extends Service implements Window.Callback {
                        + " mDozeScreenBrightnessFloat=" + mDozeScreenBrightnessFloat);
                if (startAndStopDozingInBackground()) {
                    mDreamManager.startDozingOneway(
                                mDreamToken, mDozeScreenState, mDozeScreenStateReason,
                            dreamToken, mDozeScreenState, mDozeScreenStateReason,
                            mDozeScreenBrightnessFloat, mDozeScreenBrightness,
                            mUseNormalBrightnessForDoze);
                } else {
                    mDreamManager.startDozing(
                                mDreamToken, mDozeScreenState, mDozeScreenStateReason,
                            dreamToken, mDozeScreenState, mDozeScreenStateReason,
                            mDozeScreenBrightnessFloat, mDozeScreenBrightness,
                            mUseNormalBrightnessForDoze);
                }
@@ -1033,7 +950,6 @@ public class DreamService extends Service implements Window.Callback {
                // system server died
            }
        }
        });
    }

    /**
@@ -1049,20 +965,18 @@ public class DreamService extends Service implements Window.Callback {
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    public void stopDozing() {
        postIfNeeded(() -> {
            if (mDreamToken == null) {
                return;
            }

        if (mDozing) {
            mDozing = false;
            try {
                    mDreamManager.stopDozing(mDreamToken);
                final IBinder dreamToken = mDreamToken;

                if (dreamToken != null) {
                    mDreamManager.stopDozing(dreamToken);
                }
            } catch (RemoteException ex) {
                // system server died
            }
        }
        });
    }

    /**
@@ -1292,7 +1206,7 @@ public class DreamService extends Service implements Window.Callback {
                final long token = Binder.clearCallingIdentity();
                try {
                    // Simply finish dream when exit is requested.
                    postIfNeeded(() -> finishInternal());
                    mHandler.post(() -> finish());
                } finally {
                    Binder.restoreCallingIdentity(token);
                }
@@ -1398,13 +1312,9 @@ public class DreamService extends Service implements Window.Callback {
     * </p>
     */
    public final void finish() {
        postIfNeeded(this::finishInternal);
    }

    private void finishInternal() {
        // If there is an active overlay connection, signal that the dream is ending before
        // continuing. Note that the overlay cannot rely on the unbound state, since another
        // dream might have bound to it in the meantime.
        // continuing. Note that the overlay cannot rely on the unbound state, since another dream
        // might have bound to it in the meantime.
        if (mOverlayConnection != null) {
            mOverlayConnection.addConsumer(overlay -> {
                try {
@@ -1433,7 +1343,8 @@ public class DreamService extends Service implements Window.Callback {
        }
        mFinished = true;

        if (mDreamToken == null) {
        final IBinder dreamToken = mDreamToken;
        if (dreamToken == null) {
            if (mDebug) Slog.v(mTag, "finish() called when not attached.");
            stopSelf();
            return;
@@ -1443,9 +1354,9 @@ public class DreamService extends Service implements Window.Callback {
            // finishSelf will unbind the dream controller from the dream service. This will
            // trigger DreamService.this.onDestroy and DreamService.this will die.
            if (startAndStopDozingInBackground()) {
                mDreamManager.finishSelfOneway(mDreamToken, true /*immediate*/);
                mDreamManager.finishSelfOneway(dreamToken, true /*immediate*/);
            } else {
                mDreamManager.finishSelf(mDreamToken, true /*immediate*/);
                mDreamManager.finishSelf(dreamToken, true /*immediate*/);
            }
        } catch (RemoteException ex) {
            // system server died
@@ -1460,7 +1371,7 @@ public class DreamService extends Service implements Window.Callback {
     * </p>
     */
    public final void wakeUp() {
        postIfNeeded(()-> wakeUp(false));
        wakeUp(false);
    }

    /**
@@ -1532,10 +1443,14 @@ public class DreamService extends Service implements Window.Callback {
                    Slog.w(mTag, "WakeUp was called before the dream was attached.");
                } else {
                    try {
                        final IBinder dreamToken = mDreamToken;

                        if (dreamToken != null) {
                            if (startAndStopDozingInBackground()) {
                            mDreamManager.finishSelfOneway(mDreamToken, false /*immediate*/);
                                mDreamManager.finishSelfOneway(dreamToken, false /*immediate*/);
                            } else {
                            mDreamManager.finishSelf(mDreamToken, false /*immediate*/);
                                mDreamManager.finishSelf(dreamToken, false /*immediate*/);
                            }
                        }
                    } catch (RemoteException ex) {
                        // system server died
@@ -1662,7 +1577,7 @@ public class DreamService extends Service implements Window.Callback {
        if (mActivity != null && !mActivity.isFinishing()) {
            mActivity.finishAndRemoveTask();
        } else {
            finishInternal();
            finish();
        }

        mDreamToken = null;
@@ -1736,7 +1651,7 @@ public class DreamService extends Service implements Window.Callback {
            i.setPackage(mInjector.getDreamPackageName());
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
            DreamActivity.setCallback(i,
                    new DreamActivityCallbacks(mDreamToken, new WeakReference<>(this)));
                    new DreamActivityCallbacks(dreamToken, new WeakReference<>(this)));
            final ServiceInfo serviceInfo = mInjector.getServiceInfo();
            final CharSequence title = fetchDreamLabel(mInjector.getPackageManager(),
                    mInjector.getResources(), serviceInfo, isPreviewMode);
@@ -1822,7 +1737,7 @@ public class DreamService extends Service implements Window.Callback {
                            // the window reference in order to fully release the DreamActivity.
                            mWindow = null;
                            mActivity = null;
                            finishInternal();
                            finish();
                        }

                        if (mOverlayConnection != null && mDreamStartOverlayConsumer != null) {
@@ -1909,8 +1824,7 @@ public class DreamService extends Service implements Window.Callback {

    @Override
    protected void dump(final FileDescriptor fd, PrintWriter pw, final String[] args) {
        DumpUtils.dumpAsync(mInjector.getWakefulHandler().getHandler(),
                (pw1, prefix) -> dumpOnHandler(fd, pw1, args), pw, "", 1000);
        DumpUtils.dumpAsync(mHandler, (pw1, prefix) -> dumpOnHandler(fd, pw1, args), pw, "", 1000);
    }

    /** @hide */
@@ -1966,7 +1880,7 @@ public class DreamService extends Service implements Window.Callback {
                return;
            }

            service.postIfNeeded(() -> consumer.accept(service));
            service.mHandler.post(() -> consumer.accept(service));
        }

        @Override
+8 −12
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Looper;
@@ -175,9 +176,7 @@ public class TestDreamEnvironment {
        @Mock
        private ServiceInfo mServiceInfo;

        @Mock
        private DreamService.WakefulHandler mWakefulHandler;

        private final Handler mHandler;
        private final IDreamManager mDreamManager;
        private final DreamOverlayConnectionHandler mDreamOverlayConnectionHandler;

@@ -186,6 +185,7 @@ public class TestDreamEnvironment {
                DreamOverlayConnectionHandler dreamOverlayConnectionHandler,
                boolean shouldShowComplications) {
            MockitoAnnotations.initMocks(this);
            mHandler = new Handler(looper);
            mDreamManager = dreamManager;
            mDreamOverlayConnectionHandler = dreamOverlayConnectionHandler;
            mServiceInfo.packageName = FAKE_DREAM_PACKAGE_NAME;
@@ -198,10 +198,6 @@ public class TestDreamEnvironment {
                    .thenReturn(FAKE_DREAM_SETTINGS_ACTIVITY);
            when(mPackageManager.extractPackageItemInfoAttributes(any(), any(), any(), any()))
                    .thenReturn(mAttributes);
            doAnswer(invocation -> {
                ((Runnable) invocation.getArgument(0)).run();
                return null;
            }).when(mWakefulHandler).postIfNeeded(any());
        }
        @Override
        public void init(Context context) {
@@ -238,6 +234,11 @@ public class TestDreamEnvironment {
            return mServiceInfo;
        }

        @Override
        public Handler getHandler() {
            return mHandler;
        }

        @Override
        public PackageManager getPackageManager() {
            return mPackageManager;
@@ -247,11 +248,6 @@ public class TestDreamEnvironment {
        public Resources getResources() {
            return mResources;
        }

        @Override
        public DreamService.WakefulHandler getWakefulHandler() {
            return mWakefulHandler;
        }
    }

    @Mock