Skip to content

Commit

Permalink
Pie controls: Introducing a pie delivery service
Browse files Browse the repository at this point in the history
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 omnirom#1:
* The pie service is currently disabled by default and needs to be
  enabled by device overlays (see config.xml / config_allowPieService).

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

Patch Set omnirom#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 omnirom#5:
* Rebase

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

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

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

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

Change-Id: I6a4a4635bed420e800a3230457ee690131116a11
  • Loading branch information
jdoll authored and maniac103 committed May 15, 2013
1 parent cdd9897 commit 9f11bd1
Show file tree
Hide file tree
Showing 16 changed files with 1,675 additions and 27 deletions.
3 changes: 3 additions & 0 deletions Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
14 changes: 14 additions & 0 deletions core/java/android/service/pie/IPieActivationListener.aidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

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);
}
15 changes: 15 additions & 0 deletions core/java/android/service/pie/IPieHostCallback.aidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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();
}
20 changes: 20 additions & 0 deletions core/java/android/service/pie/IPieService.aidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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);

}
193 changes: 193 additions & 0 deletions core/java/android/service/pie/PieManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* 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());
}
}

}
5 changes: 5 additions & 0 deletions core/java/android/service/pie/package.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<body>

{@hide}

</body>
42 changes: 42 additions & 0 deletions core/java/com/android/internal/util/pie/PiePosition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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 com.android.internal.util.pie;

/**
* Defines the positions in which pie controls may appear and gestures may be recognized by the
* pie service.
* This defines an index and an flag for each position.
*/
public enum PiePosition {
LEFT(0, 0),
BOTTOM(1, 1),
RIGHT(2, 1),
TOP(3, 0);

PiePosition(int index, int factor) {
INDEX = index;
FLAG = (0x01<<index);
FACTOR = factor;
}

public final int INDEX;
public final int FLAG;
/**
* This is 1 when the position is not at the axis (like {@link PiePosition.RIGHT} is
* at {@code Layout.getWidth()} not at {@code 0}).
*/
public final int FACTOR;
}
3 changes: 3 additions & 0 deletions core/res/res/values/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1141,4 +1141,7 @@
<!-- Integer to configure panel auto brightness mode when changed -->
<integer name="config_panelAutoBrightnessValue">-1</integer>

<!-- True if the pie service should be started at system start. This is must be true
to support pie controls. -->
<bool name="config_allowPieService">true</bool>
</resources>
11 changes: 11 additions & 0 deletions core/res/res/values/dimens.xml
Original file line number Diff line number Diff line change
Expand Up @@ -349,4 +349,15 @@
<!-- Left padding of num pad key on keyguard -->
<dimen name="kg_num_pad_key_padding_left">10dp</dimen>

<!-- Pie service: Distance a swipe must travel to be recognized as pie swipe.
Used by the pie service. -->
<dimen name="pie_trigger_distance">10dp</dimen>

<!-- Pie service: This is the distance a swipe can travel orthogonally to its actual swipe
direction to be still recognized as pie swipe.
Used by the pie service. -->
<dimen name="pie_perpendicular_distance">15dp</dimen>

<!-- Pie service: Thickness of the active trigger fields around the screen border -->
<dimen name="pie_trigger_thickness">6dp</dimen>
</resources>
6 changes: 6 additions & 0 deletions core/res/res/values/symbols.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1993,4 +1993,10 @@
<java-symbol type="string" name="symbol_picker_Y" />
<java-symbol type="string" name="symbol_picker_z" />
<java-symbol type="string" name="symbol_picker_Z" />

<!-- Pie Controls -->
<java-symbol type="bool" name="config_allowPieService" />
<java-symbol type="dimen" name="pie_trigger_thickness" />
<java-symbol type="dimen" name="pie_trigger_distance" />
<java-symbol type="dimen" name="pie_perpendicular_distance" />
</resources>
21 changes: 21 additions & 0 deletions services/java/com/android/server/SystemServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import com.android.server.input.InputManagerService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
import com.android.server.pie.PieService;
import com.android.server.pm.Installer;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.UserManagerService;
Expand Down Expand Up @@ -383,6 +384,7 @@ public void run() {
TextServicesManagerService tsms = null;
LockSettingsService lockSettings = null;
DreamManagerService dreamy = null;
PieService pieService = null;

// Bring up services needed for UI.
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
Expand Down Expand Up @@ -818,6 +820,17 @@ public void run() {
} catch (Throwable e) {
Slog.e(TAG, "Failure starting AssetRedirectionManager Service", e);
}

if (context.getResources().getBoolean(
com.android.internal.R.bool.config_allowPieService)) {
try {
Slog.i(TAG, "Pie Delivery Service");
pieService = new PieService(context, wm, inputManager);
ServiceManager.addService("pieservice", pieService);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Pie Delivery Service Service", e);
}
}
}

// make sure the ADB_ENABLED setting value matches the secure property value
Expand Down Expand Up @@ -910,6 +923,14 @@ public void run() {
reportWtf("making Display Manager Service ready", e);
}

if (pieService != null) {
try {
pieService.systemReady();
} catch (Throwable e) {
reportWtf("making Pie Delivery Service ready", e);
}
}

IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_APP_LAUNCH_FAILURE);
filter.addAction(Intent.ACTION_APP_LAUNCH_FAILURE_RESET);
Expand Down
Loading

0 comments on commit 9f11bd1

Please sign in to comment.