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

Commit 9f11bd11 authored by Jens Doll's avatar Jens Doll Committed by Danny Baumann
Browse files

Pie controls: Introducing a pie delivery service

To make pie controls more reliable, it is neccessary to detect
trigger actions directly from the input stream.
This commit introduces a new system service, that filters all
input events in front of the input dispatcher to detect pie activations.

This commit introduces:
* A new system server local API in the input manager service to register
  secondary input filters. These filters are behind the default
  accessibility filter but before the input event dispatching of
  the android framework.
* A new system service, that binds to the new API to listen for pie
  activation gestures.
* A non-public manager class providing access to the newly created pie
  service.

The service manager name of the service is "pieservice". The non-public
AIDL interface of the service is IPieService.aidl. To register a new
pie activation listener the INJECT_INPUT permission is needed. The
service state can be dumped by the "dumpsys pieservice" command.

Note: This commit only introduces the pie service. There is another
commit, that binds the actual pie controls to the pie service.

Patch Set #1:
* The pie service is currently disabled by default and needs to be
  enabled by device overlays (see config.xml / config_allowPieService).

Patch Set #2:
* Activation fixes
* Debug dump improvements

Patch Set #4:
* Added systrace support (TRACE_INPUT_TAG)
* Switch default to enable service on all devices.
* Moved Position to com.internal.android.utils.pie.*
* Some more code rearrangements

Patch Set #5:
* Rebase

Patch Set #6:
* Cover more corner cases on PieInputFilter
* Adjust gesture time out

Patch Set #7:
* Do not send events that are from the past
* Recycle all events

Patch Set #8:
* Handle binder died events in PieService correctly

Patch Set #10:
* Simplified locking
* SYSTEM_UI_FLAG_HIDE_NAVIGATION support
* Fixed ADW Lauchner bug

Change-Id: I6a4a4635bed420e800a3230457ee690131116a11
parent cdd98977
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -145,6 +145,9 @@ LOCAL_SRC_FILES += \
	core/java/android/os/IVibratorService.aidl \
	core/java/android/service/dreams/IDreamManager.aidl \
	core/java/android/service/dreams/IDreamService.aidl \
	core/java/android/service/pie/IPieService.aidl \
	core/java/android/service/pie/IPieActivationListener.aidl \
	core/java/android/service/pie/IPieHostCallback.aidl \
	core/java/android/service/wallpaper/IWallpaperConnection.aidl \
	core/java/android/service/wallpaper/IWallpaperEngine.aidl \
	core/java/android/service/wallpaper/IWallpaperService.aidl \
+14 −0
Original line number Diff line number Diff line

package android.service.pie;

import android.view.InputEvent;

/** @hide */
interface IPieActivationListener {

    /** Called when a gesture is detected that fits to the pie activation gesture. At this point in
     * time gesture detection is disabled. Call IPieHostCallback.restoreState() to
     * recover from this.
     */
    oneway void onPieActivation(int touchX, int touchY, int positionIndex, int flags);
}
 No newline at end of file
+15 −0
Original line number Diff line number Diff line
package android.service.pie;

/** @hide */
interface IPieHostCallback {

    /** After being activated, this allows the pie control to steal focus from the current
     * window
     */
    boolean gainTouchFocus(IBinder windowToken);

    /** Turns listening for pie activation gestures on again, after it was disabled during
     * the call to the listener.
     */
    oneway void restoreListenerState();
}
 No newline at end of file
+20 −0
Original line number Diff line number Diff line
package android.service.pie;

import android.service.pie.IPieActivationListener;
import android.service.pie.IPieHostCallback;

/** @hide */
interface IPieService {

    /** Register a listener for pie activation gestures. Initially the listener
     * is set to listen for no position. Use updatePieActivationListener() to
     * bind the listener to positions.
     * Use the returned IPieHostCallback to manipulate the state after activation.
     */
    IPieHostCallback registerPieActivationListener(in IPieActivationListener listener);

    /** Update the listener to react on gestures in the given positions.
     */
    void updatePieActivationListener(in IBinder listener, int positionFlags);

}
 No newline at end of file
+193 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The CyanogenMod Project (Jens Doll)
 *
 * 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.pie;

import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.service.pie.IPieService;
import android.util.Slog;

import com.android.internal.util.pie.PiePosition;

/**
 * This is a simple Manager class for pie service on the application side. The application need
 * {@code INJECT_EVENTS} permission to register {@code PieActivationListener}s.<br>
 * See {@link IPieService} for more information.
 *
 * @see IPieService
 * @hide
 */
public class PieManager {
    public static final String TAG = "PieManager";
    public static final boolean DEBUG = false;

    private static PieManager sInstance;

    private final IPieService mPs;

    public static abstract class PieActivationListener {
        private Handler mHandler;
        private IPieHostCallback mCallback;

        private class Delegator extends IPieActivationListener.Stub {
            public void onPieActivation(final int touchX, final int touchY, final int positionIndex, final int flags)
                    throws RemoteException {
                mHandler.post(new Runnable() {
                    public void run() {
                        PieActivationListener.this.onPieActivation(touchX, touchY, PiePosition.values()[positionIndex], flags);
                    }
                });
            }
        }
        private Delegator mDelegator;

        public PieActivationListener() {
            mHandler = new Handler(Looper.getMainLooper());
        }

        public PieActivationListener(Looper looper) {
            mHandler = new Handler(looper);
            mDelegator = new Delegator();
        }

        /* package */ void setHostCallback(IPieHostCallback hostCallback) {
            mCallback = hostCallback;
        }

        /**
         * Override this to receive activations from the pie service.
         *
         * @param touchX the last X position a touch event was registered.
         * @param touchY the last Y position a touch event was registered.
         * @param position the position of the activation.
         * @param flags currently 0.
         * @see IPieActivationListener#onPieActivation(int, int, int, int)
         */
        public abstract void onPieActivation(int touchX, int touchY, PiePosition position, int flags);

        /**
         * After being activated, this allows the pie control to steal focus from the current
         * window.
         *
         * @see IPieHostCallback#gainTouchFocus(IBinder)
         */
        public boolean gainTouchFocus(IBinder applicationWindowToken) {
            try {
                return mCallback.gainTouchFocus(applicationWindowToken);
            } catch (RemoteException e) {
                Slog.w(TAG, "gainTouchFocus failed: " + e.getMessage());
                /* fall through */
            }
            return false;
        }

        /**
         * Turns listening for pie activation gestures on again, after it was disabled during
         * the call to the listener.
         *
         * @see IPieHostCallback#restoreListenerState()
         */
        public void restoreListenerState() {
            if (DEBUG) {
                Slog.d(TAG, "restore listener state: " + Thread.currentThread().getName());
            }
            try {
                mCallback.restoreListenerState();
            } catch (RemoteException e) {
                Slog.w(TAG, "restoreListenerState failed: " + e.getMessage());
                /* fall through */
            }
        }
    }

    private PieManager(IPieService ps) {
        mPs = ps;
    }

    /**
     * Gets an instance of the pie manager.
     *
     * @return The pie manager instance.
     * @hide
     */
    public static PieManager getInstance() {
        synchronized (PieManager.class) {
            if (sInstance == null) {
                IBinder b = ServiceManager.getService("pieservice");
                sInstance = new PieManager(IPieService.Stub.asInterface(b));
            }
            return sInstance;
        }
    }

    /**
     * Checks if the pie service is present.
     * <p>
     * Since the service is only started at boot time and is bound to the system server, this
     * is constant for the devices up time.
     *
     * @return {@code true} when the pie service is running on this device.
     * @hide
     */
    public boolean isPresent() {
        return mPs != null;
    }

    /**
     * Register a listener for pie activation gestures. Initially the listener
     * is set to listen for no position. Use updatePieActivationListener() to
     * bind the listener to positions.
     *
     * @param listener is the activation listener.
     * @return {@code true} if the registration was successful.
     * @hide
     */
    public boolean setPieActivationListener(PieActivationListener listener) {
        if (DEBUG) {
            Slog.d(TAG, "Set pie activation listener");
        }
        try {
            IPieHostCallback callback = mPs.registerPieActivationListener(listener.mDelegator);
            listener.setHostCallback(callback);
            return true;
        } catch (RemoteException e) {
            Slog.e(TAG, "Failed to set pie activation listener: " + e.getMessage());
            return false;
        }
    }

    /**
     * Update the listener to react on gestures in the given positions.
     *
     * @param listener is a already registered listener.
     * @param positions is a bit mask describing the positions to listen to.
     * @hide
     */
    public void updatePieActivationListener(PieActivationListener listener, int positions) {
        if (DEBUG) {
            Slog.d(TAG, "Update pie activation listener: 0x" + Integer.toHexString(positions));
        }
        try {
            mPs.updatePieActivationListener(listener.mDelegator.asBinder(), positions);
        } catch (RemoteException e) {
            Slog.e(TAG, "Failed to update pie activation listener: " + e.getMessage());
        }
    }

}
Loading