diff options
77 files changed, 4798 insertions, 6901 deletions
diff --git a/api/current.xml b/api/current.xml index 5d71cad..7a86d05 100644 --- a/api/current.xml +++ b/api/current.xml @@ -4717,6 +4717,17 @@ visibility="public" > </field> +<field name="immersive" + type="int" + transient="false" + volatile="false" + value="16843457" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="inAnimation" type="int" transient="false" @@ -5916,17 +5927,6 @@ visibility="public" > </field> -<field name="kraken_resource_pad64" - type="int" - transient="false" - volatile="false" - value="16843457" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="kraken_resource_pad7" type="int" transient="false" @@ -154519,6 +154519,21 @@ <parameter name="flags" type="int"> </parameter> </method> +<method name="setPackageObbPath" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="packageName" type="java.lang.String"> +</parameter> +<parameter name="path" type="java.lang.String"> +</parameter> +</method> </class> <class name="MockResources" extends="android.content.res.Resources" diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index fc9bcf7..63bbf9c 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2657,6 +2657,15 @@ class ContextImpl extends Context { return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; } + @Override + public void setPackageObbPath(String packageName, String path) { + try { + mPM.setPackageObbPath(packageName, path); + } catch (RemoteException e) { + // Should never happen! + } + } + private final ContextImpl mContext; private final IPackageManager mPM; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 9939478..160a481 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -319,4 +319,6 @@ interface IPackageManager { boolean setInstallLocation(int loc); int getInstallLocation(); + + void setPackageObbPath(String packageName, String path); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 1a5b419..15a446b 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2193,4 +2193,17 @@ public abstract class PackageManager { */ public abstract void movePackage( String packageName, IPackageMoveObserver observer, int flags); + + /** + * Sets the Opaque Binary Blob (OBB) file location. + * <p> + * NOTE: The existence or format of this file is not currently checked, but + * it may be in the future. + * + * @param packageName Name of the package with which to associate the .obb + * file + * @param path Path on the filesystem to the .obb file + * @hide + */ + public abstract void setPackageObbPath(String packageName, String path); } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 0a2899f..1100886 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -535,6 +535,7 @@ public class Camera { * application does not need a particular callback, a null can be passed * instead of a callback method. * + * This method is only valid after {@link #startPreview()} has been called. * This method will stop the preview. Applications should not call {@link * #stopPreview()} before this. After jpeg callback is received, * applications can call {@link #startPreview()} to restart the preview. @@ -562,6 +563,7 @@ public class Camera { * application does not need a particular callback, a null can be passed * instead of a callback method. * + * This method is only valid after {@link #startPreview()} has been called. * This method will stop the preview. Applications should not call {@link * #stopPreview()} before this. After jpeg callback is received, * applications can call {@link #startPreview()} to restart the preview. diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 249ad62..6f12f19 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -216,20 +216,7 @@ public abstract class WallpaperService extends Service { @Override public void handleTouch(MotionEvent event, Runnable finishedCallback) { try { - synchronized (mLock) { - if (event.getAction() == MotionEvent.ACTION_MOVE) { - if (mPendingMove != null) { - mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove); - mPendingMove.recycle(); - } - mPendingMove = event; - } else { - mPendingMove = null; - } - Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, - event); - mCaller.sendMessage(msg); - } + dispatchPointer(event); } finally { finishedCallback.run(); } @@ -238,26 +225,6 @@ public abstract class WallpaperService extends Service { final BaseIWindow mWindow = new BaseIWindow() { @Override - public boolean onDispatchPointer(MotionEvent event, long eventTime, - boolean callWhenDone) { - synchronized (mLock) { - if (event.getAction() == MotionEvent.ACTION_MOVE) { - if (mPendingMove != null) { - mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove); - mPendingMove.recycle(); - } - mPendingMove = event; - } else { - mPendingMove = null; - } - Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, - event); - mCaller.sendMessage(msg); - } - return false; - } - - @Override public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets, boolean reportDraw, Configuration newConfig) { Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED, @@ -466,6 +433,22 @@ public abstract class WallpaperService extends Service { */ public void onSurfaceDestroyed(SurfaceHolder holder) { } + + private void dispatchPointer(MotionEvent event) { + synchronized (mLock) { + if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (mPendingMove != null) { + mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove); + mPendingMove.recycle(); + } + mPendingMove = event; + } else { + mPendingMove = null; + } + Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event); + mCaller.sendMessage(msg); + } + } void updateSurface(boolean forceRelayout, boolean forceReport) { if (mDestroyed) { @@ -523,10 +506,8 @@ public abstract class WallpaperService extends Service { mInputChannel); mCreated = true; - if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) { - InputQueue.registerInputChannel(mInputChannel, mInputHandler, - Looper.myQueue()); - } + InputQueue.registerInputChannel(mInputChannel, mInputHandler, + Looper.myQueue()); } mSurfaceHolder.mSurfaceLock.lock(); @@ -770,10 +751,8 @@ public abstract class WallpaperService extends Service { if (DEBUG) Log.v(TAG, "Removing window and destroying surface " + mSurfaceHolder.getSurface() + " of: " + this); - if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) { - if (mInputChannel != null) { - InputQueue.unregisterInputChannel(mInputChannel); - } + if (mInputChannel != null) { + InputQueue.unregisterInputChannel(mInputChannel); } mSession.remove(mWindow); @@ -782,13 +761,11 @@ public abstract class WallpaperService extends Service { mSurfaceHolder.mSurface.release(); mCreated = false; - if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) { - // Dispose the input channel after removing the window so the Window Manager - // doesn't interpret the input channel being closed as an abnormal termination. - if (mInputChannel != null) { - mInputChannel.dispose(); - mInputChannel = null; - } + // Dispose the input channel after removing the window so the Window Manager + // doesn't interpret the input channel being closed as an abnormal termination. + if (mInputChannel != null) { + mInputChannel.dispose(); + mInputChannel = null; } } } @@ -841,7 +818,7 @@ public abstract class WallpaperService extends Service { public void dispatchPointer(MotionEvent event) { if (mEngine != null) { - mEngine.mWindow.onDispatchPointer(event, event.getEventTime(), false); + mEngine.dispatchPointer(event); } } diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 3b09808..921018a 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -46,9 +46,6 @@ oneway interface IWindow { void resized(int w, int h, in Rect coveredInsets, in Rect visibleInsets, boolean reportDraw, in Configuration newConfig); - void dispatchKey(in KeyEvent event); - void dispatchPointer(in MotionEvent event, long eventTime, boolean callWhenDone); - void dispatchTrackball(in MotionEvent event, long eventTime, boolean callWhenDone); void dispatchAppVisibility(boolean visible); void dispatchGetNewSurface(); diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 4647fb4..7f10b76 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -109,10 +109,6 @@ interface IWindowSession { void getDisplayFrame(IWindow window, out Rect outDisplayFrame); void finishDrawing(IWindow window); - - void finishKey(IWindow window); - MotionEvent getPendingPointerMove(IWindow window); - MotionEvent getPendingTrackballMove(IWindow window); void setInTouchMode(boolean showFocus); boolean getInTouchMode(); diff --git a/core/java/android/view/RawInputEvent.java b/core/java/android/view/RawInputEvent.java deleted file mode 100644 index 3bbfea8..0000000 --- a/core/java/android/view/RawInputEvent.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2008 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.view; - -/** - * @hide - * This really belongs in services.jar; WindowManagerPolicy should go there too. - */ -public class RawInputEvent { - // Event class as defined by EventHub. - public static final int CLASS_KEYBOARD = 0x00000001; - public static final int CLASS_ALPHAKEY = 0x00000002; - public static final int CLASS_TOUCHSCREEN = 0x00000004; - public static final int CLASS_TRACKBALL = 0x00000008; - public static final int CLASS_TOUCHSCREEN_MT = 0x00000010; - public static final int CLASS_DPAD = 0x00000020; - - // More special classes for QueuedEvent below. - public static final int CLASS_CONFIGURATION_CHANGED = 0x10000000; - - // Event types. - - public static final int EV_SYN = 0x00; - public static final int EV_KEY = 0x01; - public static final int EV_REL = 0x02; - public static final int EV_ABS = 0x03; - public static final int EV_MSC = 0x04; - public static final int EV_SW = 0x05; - public static final int EV_LED = 0x11; - public static final int EV_SND = 0x12; - public static final int EV_REP = 0x14; - public static final int EV_FF = 0x15; - public static final int EV_PWR = 0x16; - public static final int EV_FF_STATUS = 0x17; - - // Platform-specific event types. - - public static final int EV_DEVICE_ADDED = 0x10000000; - public static final int EV_DEVICE_REMOVED = 0x20000000; - - // Special key (EV_KEY) scan codes for pointer buttons. - - public static final int BTN_FIRST = 0x100; - - public static final int BTN_MISC = 0x100; - public static final int BTN_0 = 0x100; - public static final int BTN_1 = 0x101; - public static final int BTN_2 = 0x102; - public static final int BTN_3 = 0x103; - public static final int BTN_4 = 0x104; - public static final int BTN_5 = 0x105; - public static final int BTN_6 = 0x106; - public static final int BTN_7 = 0x107; - public static final int BTN_8 = 0x108; - public static final int BTN_9 = 0x109; - - public static final int BTN_MOUSE = 0x110; - public static final int BTN_LEFT = 0x110; - public static final int BTN_RIGHT = 0x111; - public static final int BTN_MIDDLE = 0x112; - public static final int BTN_SIDE = 0x113; - public static final int BTN_EXTRA = 0x114; - public static final int BTN_FORWARD = 0x115; - public static final int BTN_BACK = 0x116; - public static final int BTN_TASK = 0x117; - - public static final int BTN_JOYSTICK = 0x120; - public static final int BTN_TRIGGER = 0x120; - public static final int BTN_THUMB = 0x121; - public static final int BTN_THUMB2 = 0x122; - public static final int BTN_TOP = 0x123; - public static final int BTN_TOP2 = 0x124; - public static final int BTN_PINKIE = 0x125; - public static final int BTN_BASE = 0x126; - public static final int BTN_BASE2 = 0x127; - public static final int BTN_BASE3 = 0x128; - public static final int BTN_BASE4 = 0x129; - public static final int BTN_BASE5 = 0x12a; - public static final int BTN_BASE6 = 0x12b; - public static final int BTN_DEAD = 0x12f; - - public static final int BTN_GAMEPAD = 0x130; - public static final int BTN_A = 0x130; - public static final int BTN_B = 0x131; - public static final int BTN_C = 0x132; - public static final int BTN_X = 0x133; - public static final int BTN_Y = 0x134; - public static final int BTN_Z = 0x135; - public static final int BTN_TL = 0x136; - public static final int BTN_TR = 0x137; - public static final int BTN_TL2 = 0x138; - public static final int BTN_TR2 = 0x139; - public static final int BTN_SELECT = 0x13a; - public static final int BTN_START = 0x13b; - public static final int BTN_MODE = 0x13c; - public static final int BTN_THUMBL = 0x13d; - public static final int BTN_THUMBR = 0x13e; - - public static final int BTN_DIGI = 0x140; - public static final int BTN_TOOL_PEN = 0x140; - public static final int BTN_TOOL_RUBBER = 0x141; - public static final int BTN_TOOL_BRUSH = 0x142; - public static final int BTN_TOOL_PENCIL = 0x143; - public static final int BTN_TOOL_AIRBRUSH = 0x144; - public static final int BTN_TOOL_FINGER = 0x145; - public static final int BTN_TOOL_MOUSE = 0x146; - public static final int BTN_TOOL_LENS = 0x147; - public static final int BTN_TOUCH = 0x14a; - public static final int BTN_STYLUS = 0x14b; - public static final int BTN_STYLUS2 = 0x14c; - public static final int BTN_TOOL_DOUBLETAP = 0x14d; - public static final int BTN_TOOL_TRIPLETAP = 0x14e; - - public static final int BTN_WHEEL = 0x150; - public static final int BTN_GEAR_DOWN = 0x150; - public static final int BTN_GEAR_UP = 0x151; - - public static final int BTN_LAST = 0x15f; - - // Relative axes (EV_REL) scan codes. - - public static final int REL_X = 0x00; - public static final int REL_Y = 0x01; - public static final int REL_Z = 0x02; - public static final int REL_RX = 0x03; - public static final int REL_RY = 0x04; - public static final int REL_RZ = 0x05; - public static final int REL_HWHEEL = 0x06; - public static final int REL_DIAL = 0x07; - public static final int REL_WHEEL = 0x08; - public static final int REL_MISC = 0x09; - public static final int REL_MAX = 0x0f; - - // Absolute axes (EV_ABS) scan codes. - - public static final int ABS_X = 0x00; - public static final int ABS_Y = 0x01; - public static final int ABS_Z = 0x02; - public static final int ABS_RX = 0x03; - public static final int ABS_RY = 0x04; - public static final int ABS_RZ = 0x05; - public static final int ABS_THROTTLE = 0x06; - public static final int ABS_RUDDER = 0x07; - public static final int ABS_WHEEL = 0x08; - public static final int ABS_GAS = 0x09; - public static final int ABS_BRAKE = 0x0a; - public static final int ABS_HAT0X = 0x10; - public static final int ABS_HAT0Y = 0x11; - public static final int ABS_HAT1X = 0x12; - public static final int ABS_HAT1Y = 0x13; - public static final int ABS_HAT2X = 0x14; - public static final int ABS_HAT2Y = 0x15; - public static final int ABS_HAT3X = 0x16; - public static final int ABS_HAT3Y = 0x17; - public static final int ABS_PRESSURE = 0x18; - public static final int ABS_DISTANCE = 0x19; - public static final int ABS_TILT_X = 0x1a; - public static final int ABS_TILT_Y = 0x1b; - public static final int ABS_TOOL_WIDTH = 0x1c; - public static final int ABS_VOLUME = 0x20; - public static final int ABS_MISC = 0x28; - public static final int ABS_MT_TOUCH_MAJOR = 0x30; - public static final int ABS_MT_TOUCH_MINOR = 0x31; - public static final int ABS_MT_WIDTH_MAJOR = 0x32; - public static final int ABS_MT_WIDTH_MINOR = 0x33; - public static final int ABS_MT_ORIENTATION = 0x34; - public static final int ABS_MT_POSITION_X = 0x35; - public static final int ABS_MT_POSITION_Y = 0x36; - public static final int ABS_MT_TOOL_TYPE = 0x37; - public static final int ABS_MT_BLOB_ID = 0x38; - public static final int ABS_MAX = 0x3f; - - // Switch events - public static final int SW_LID = 0x00; - - public static final int SYN_REPORT = 0; - public static final int SYN_CONFIG = 1; - public static final int SYN_MT_REPORT = 2; - - public int deviceId; - public int type; - public int scancode; - public int keycode; - public int flags; - public int value; - public long when; -} diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index d1a0f75..e4d1ae1 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -625,41 +625,6 @@ public class SurfaceView extends View { } } - public void dispatchKey(KeyEvent event) { - SurfaceView surfaceView = mSurfaceView.get(); - if (surfaceView != null) { - //Log.w("SurfaceView", "Unexpected key event in surface: " + event); - if (surfaceView.mSession != null && surfaceView.mSurface != null) { - try { - surfaceView.mSession.finishKey(surfaceView.mWindow); - } catch (RemoteException ex) { - } - } - } - } - - public void dispatchPointer(MotionEvent event, long eventTime, - boolean callWhenDone) { - Log.w("SurfaceView", "Unexpected pointer event in surface: " + event); - //if (mSession != null && mSurface != null) { - // try { - // //mSession.finishKey(mWindow); - // } catch (RemoteException ex) { - // } - //} - } - - public void dispatchTrackball(MotionEvent event, long eventTime, - boolean callWhenDone) { - Log.w("SurfaceView", "Unexpected trackball event in surface: " + event); - //if (mSession != null && mSurface != null) { - // try { - // //mSession.finishKey(mWindow); - // } catch (RemoteException ex) { - // } - //} - } - public void dispatchAppVisibility(boolean visible) { // The point of SurfaceView is to let the app control the surface. } @@ -686,7 +651,6 @@ public class SurfaceView extends View { private SurfaceHolder mSurfaceHolder = new SurfaceHolder() { private static final String LOG_TAG = "SurfaceHolder"; - private int mSaveCount; public boolean isCreating() { return mIsCreating; diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 1dc82e8..e980b17 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -556,18 +556,16 @@ public final class ViewRoot extends Handler implements ViewParent, "Unable to add window -- unknown error code " + res); } - if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) { - if (view instanceof RootViewSurfaceTaker) { - mInputQueueCallback = - ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); - } - if (mInputQueueCallback != null) { - mInputQueue = new InputQueue(mInputChannel); - mInputQueueCallback.onInputQueueCreated(mInputQueue); - } else { - InputQueue.registerInputChannel(mInputChannel, mInputHandler, - Looper.myQueue()); - } + if (view instanceof RootViewSurfaceTaker) { + mInputQueueCallback = + ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); + } + if (mInputQueueCallback != null) { + mInputQueue = new InputQueue(mInputChannel); + mInputQueueCallback.onInputQueueCreated(mInputQueue); + } else { + InputQueue.registerInputChannel(mInputChannel, mInputHandler, + Looper.myQueue()); } view.assignParent(this); @@ -1745,16 +1743,12 @@ public final class ViewRoot extends Handler implements ViewParent, } mSurface.release(); - if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) { - if (mInputChannel != null) { - if (mInputQueueCallback != null) { - mInputQueueCallback.onInputQueueDestroyed(mInputQueue); - mInputQueueCallback = null; - } else { - InputQueue.unregisterInputChannel(mInputChannel); - } - mInputChannel.dispose(); - mInputChannel = null; + if (mInputChannel != null) { + if (mInputQueueCallback != null) { + mInputQueueCallback.onInputQueueDestroyed(mInputQueue); + mInputQueueCallback = null; + } else { + InputQueue.unregisterInputChannel(mInputChannel); } } @@ -1763,13 +1757,11 @@ public final class ViewRoot extends Handler implements ViewParent, } catch (RemoteException e) { } - if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) { - // Dispose the input channel after removing the window so the Window Manager - // doesn't interpret the input channel being closed as an abnormal termination. - if (mInputChannel != null) { - mInputChannel.dispose(); - mInputChannel = null; - } + // Dispose the input channel after removing the window so the Window Manager + // doesn't interpret the input channel being closed as an abnormal termination. + if (mInputChannel != null) { + mInputChannel.dispose(); + mInputChannel = null; } } @@ -1869,105 +1861,22 @@ public final class ViewRoot extends Handler implements ViewParent, deliverKeyEvent((KeyEvent)msg.obj, true); break; case DISPATCH_POINTER: { - MotionEvent event = (MotionEvent)msg.obj; - boolean callWhenDone = msg.arg1 != 0; - - if (event == null) { - long timeBeforeGettingEvents; - if (MEASURE_LATENCY) { - timeBeforeGettingEvents = System.nanoTime(); - } - - event = getPendingPointerMotionEvent(); - - if (MEASURE_LATENCY && event != null) { - lt.sample("9 Client got events ", System.nanoTime() - event.getEventTimeNano()); - lt.sample("8 Client getting events ", timeBeforeGettingEvents - event.getEventTimeNano()); - } - callWhenDone = false; - } - if (event != null && mTranslator != null) { - mTranslator.translateEventInScreenToAppWindow(event); - } + MotionEvent event = (MotionEvent) msg.obj; try { - boolean handled; - if (mView != null && mAdded && event != null) { - - // enter touch mode on the down - boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN; - if (isDown) { - ensureTouchMode(true); - } - if(Config.LOGV) { - captureMotionLog("captureDispatchPointer", event); - } - if (mCurScrollY != 0) { - event.offsetLocation(0, mCurScrollY); - } - if (MEASURE_LATENCY) { - lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano()); - } - handled = mView.dispatchTouchEvent(event); - if (MEASURE_LATENCY) { - lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano()); - } - if (!handled && isDown) { - int edgeSlop = mViewConfiguration.getScaledEdgeSlop(); - - final int edgeFlags = event.getEdgeFlags(); - int direction = View.FOCUS_UP; - int x = (int)event.getX(); - int y = (int)event.getY(); - final int[] deltas = new int[2]; - - if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) { - direction = View.FOCUS_DOWN; - if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) { - deltas[0] = edgeSlop; - x += edgeSlop; - } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) { - deltas[0] = -edgeSlop; - x -= edgeSlop; - } - } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) { - direction = View.FOCUS_UP; - if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) { - deltas[0] = edgeSlop; - x += edgeSlop; - } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) { - deltas[0] = -edgeSlop; - x -= edgeSlop; - } - } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) { - direction = View.FOCUS_RIGHT; - } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) { - direction = View.FOCUS_LEFT; - } - - if (edgeFlags != 0 && mView instanceof ViewGroup) { - View nearest = FocusFinder.getInstance().findNearestTouchable( - ((ViewGroup) mView), x, y, direction, deltas); - if (nearest != null) { - event.offsetLocation(deltas[0], deltas[1]); - event.setEdgeFlags(0); - mView.dispatchTouchEvent(event); - } - } - } - } + deliverPointerEvent(event); } finally { - if (callWhenDone) { - finishMotionEvent(); - } - recycleMotionEvent(event); + event.recycle(); if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!"); - // Let the exception fall through -- the looper will catch - // it and take care of the bad app for us. } } break; - case DISPATCH_TRACKBALL: - deliverTrackballEvent((MotionEvent)msg.obj, msg.arg1 != 0); - break; + case DISPATCH_TRACKBALL: { + MotionEvent event = (MotionEvent) msg.obj; + try { + deliverTrackballEvent(event); + } finally { + event.recycle(); + } + } break; case DISPATCH_APP_VISIBILITY: handleAppVisibility(msg.arg1 != 0); break; @@ -2101,61 +2010,12 @@ public final class ViewRoot extends Handler implements ViewParent, } private void finishKeyEvent(KeyEvent event) { - if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) { - if (mFinishedCallback != null) { - mFinishedCallback.run(); - mFinishedCallback = null; - } - } else { - try { - sWindowSession.finishKey(mWindow); - } catch (RemoteException e) { - } + if (mFinishedCallback != null) { + mFinishedCallback.run(); + mFinishedCallback = null; } } - private void finishMotionEvent() { - if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) { - throw new IllegalStateException("Should not be reachable with native input dispatch."); - } - - try { - sWindowSession.finishKey(mWindow); - } catch (RemoteException e) { - } - } - - private void recycleMotionEvent(MotionEvent event) { - if (event != null) { - event.recycle(); - } - } - - private MotionEvent getPendingPointerMotionEvent() { - if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) { - throw new IllegalStateException("Should not be reachable with native input dispatch."); - } - - try { - return sWindowSession.getPendingPointerMove(mWindow); - } catch (RemoteException e) { - return null; - } - } - - private MotionEvent getPendingTrackballMotionEvent() { - if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) { - throw new IllegalStateException("Should not be reachable with native input dispatch."); - } - - try { - return sWindowSession.getPendingTrackballMove(mWindow); - } catch (RemoteException e) { - return null; - } - } - - /** * Something in the current window tells us we need to change the touch mode. For * example, we are not in touch mode, and the user touches the screen. @@ -2277,42 +2137,95 @@ public final class ViewRoot extends Handler implements ViewParent, return false; } + private void deliverPointerEvent(MotionEvent event) { + if (mTranslator != null) { + mTranslator.translateEventInScreenToAppWindow(event); + } + + boolean handled; + if (mView != null && mAdded) { - private void deliverTrackballEvent(MotionEvent event, boolean callWhenDone) { - if (event == null) { - event = getPendingTrackballMotionEvent(); - callWhenDone = false; + // enter touch mode on the down + boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN; + if (isDown) { + ensureTouchMode(true); + } + if(Config.LOGV) { + captureMotionLog("captureDispatchPointer", event); + } + if (mCurScrollY != 0) { + event.offsetLocation(0, mCurScrollY); + } + if (MEASURE_LATENCY) { + lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano()); + } + handled = mView.dispatchTouchEvent(event); + if (MEASURE_LATENCY) { + lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano()); + } + if (!handled && isDown) { + int edgeSlop = mViewConfiguration.getScaledEdgeSlop(); + + final int edgeFlags = event.getEdgeFlags(); + int direction = View.FOCUS_UP; + int x = (int)event.getX(); + int y = (int)event.getY(); + final int[] deltas = new int[2]; + + if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) { + direction = View.FOCUS_DOWN; + if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) { + deltas[0] = edgeSlop; + x += edgeSlop; + } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) { + deltas[0] = -edgeSlop; + x -= edgeSlop; + } + } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) { + direction = View.FOCUS_UP; + if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) { + deltas[0] = edgeSlop; + x += edgeSlop; + } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) { + deltas[0] = -edgeSlop; + x -= edgeSlop; + } + } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) { + direction = View.FOCUS_RIGHT; + } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) { + direction = View.FOCUS_LEFT; + } + + if (edgeFlags != 0 && mView instanceof ViewGroup) { + View nearest = FocusFinder.getInstance().findNearestTouchable( + ((ViewGroup) mView), x, y, direction, deltas); + if (nearest != null) { + event.offsetLocation(deltas[0], deltas[1]); + event.setEdgeFlags(0); + mView.dispatchTouchEvent(event); + } + } + } } + } + private void deliverTrackballEvent(MotionEvent event) { if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event); boolean handled = false; - try { - if (event == null) { - handled = true; - } else if (mView != null && mAdded) { - handled = mView.dispatchTrackballEvent(event); - if (!handled) { - // we could do something here, like changing the focus - // or something? - } - } - } finally { + if (mView != null && mAdded) { + handled = mView.dispatchTrackballEvent(event); if (handled) { - if (callWhenDone) { - finishMotionEvent(); - } - recycleMotionEvent(event); // If we reach this, we delivered a trackball event to mView and // mView consumed it. Because we will not translate the trackball // event into a key event, touch mode will not exit, so we exit // touch mode here. ensureTouchMode(false); - //noinspection ReturnInsideFinallyBlock return; } - // Let the exception fall through -- the looper will catch - // it and take care of the bad app for us. + + // Otherwise we could do something here, like changing the focus + // or something? } final TrackballAxis x = mTrackballAxisX; @@ -2327,95 +2240,86 @@ public final class ViewRoot extends Handler implements ViewParent, mLastTrackballTime = curTime; } - try { - final int action = event.getAction(); - final int metastate = event.getMetaState(); - switch (action) { - case MotionEvent.ACTION_DOWN: - x.reset(2); - y.reset(2); - deliverKeyEvent(new KeyEvent(curTime, curTime, - KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, - 0, metastate), false); - break; - case MotionEvent.ACTION_UP: - x.reset(2); - y.reset(2); - deliverKeyEvent(new KeyEvent(curTime, curTime, - KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, - 0, metastate), false); - break; - } - - if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step=" - + x.step + " dir=" + x.dir + " acc=" + x.acceleration - + " move=" + event.getX() - + " / Y=" + y.position + " step=" - + y.step + " dir=" + y.dir + " acc=" + y.acceleration - + " move=" + event.getY()); - final float xOff = x.collect(event.getX(), event.getEventTime(), "X"); - final float yOff = y.collect(event.getY(), event.getEventTime(), "Y"); - - // Generate DPAD events based on the trackball movement. - // We pick the axis that has moved the most as the direction of - // the DPAD. When we generate DPAD events for one axis, then the - // other axis is reset -- we don't want to perform DPAD jumps due - // to slight movements in the trackball when making major movements - // along the other axis. - int keycode = 0; - int movement = 0; - float accel = 1; - if (xOff > yOff) { - movement = x.generate((2/event.getXPrecision())); - if (movement != 0) { - keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT - : KeyEvent.KEYCODE_DPAD_LEFT; - accel = x.acceleration; - y.reset(2); - } - } else if (yOff > 0) { - movement = y.generate((2/event.getYPrecision())); - if (movement != 0) { - keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN - : KeyEvent.KEYCODE_DPAD_UP; - accel = y.acceleration; - x.reset(2); - } - } - - if (keycode != 0) { - if (movement < 0) movement = -movement; - int accelMovement = (int)(movement * accel); - if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement - + " accelMovement=" + accelMovement - + " accel=" + accel); - if (accelMovement > movement) { - if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: " - + keycode); - movement--; - deliverKeyEvent(new KeyEvent(curTime, curTime, - KeyEvent.ACTION_MULTIPLE, keycode, - accelMovement-movement, metastate), false); - } - while (movement > 0) { - if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: " - + keycode); - movement--; - curTime = SystemClock.uptimeMillis(); - deliverKeyEvent(new KeyEvent(curTime, curTime, - KeyEvent.ACTION_DOWN, keycode, 0, event.getMetaState()), false); - deliverKeyEvent(new KeyEvent(curTime, curTime, - KeyEvent.ACTION_UP, keycode, 0, metastate), false); - } - mLastTrackballTime = curTime; - } - } finally { - if (callWhenDone) { - finishMotionEvent(); - recycleMotionEvent(event); + final int action = event.getAction(); + final int metastate = event.getMetaState(); + switch (action) { + case MotionEvent.ACTION_DOWN: + x.reset(2); + y.reset(2); + deliverKeyEvent(new KeyEvent(curTime, curTime, + KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, + 0, metastate), false); + break; + case MotionEvent.ACTION_UP: + x.reset(2); + y.reset(2); + deliverKeyEvent(new KeyEvent(curTime, curTime, + KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, + 0, metastate), false); + break; + } + + if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step=" + + x.step + " dir=" + x.dir + " acc=" + x.acceleration + + " move=" + event.getX() + + " / Y=" + y.position + " step=" + + y.step + " dir=" + y.dir + " acc=" + y.acceleration + + " move=" + event.getY()); + final float xOff = x.collect(event.getX(), event.getEventTime(), "X"); + final float yOff = y.collect(event.getY(), event.getEventTime(), "Y"); + + // Generate DPAD events based on the trackball movement. + // We pick the axis that has moved the most as the direction of + // the DPAD. When we generate DPAD events for one axis, then the + // other axis is reset -- we don't want to perform DPAD jumps due + // to slight movements in the trackball when making major movements + // along the other axis. + int keycode = 0; + int movement = 0; + float accel = 1; + if (xOff > yOff) { + movement = x.generate((2/event.getXPrecision())); + if (movement != 0) { + keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT + : KeyEvent.KEYCODE_DPAD_LEFT; + accel = x.acceleration; + y.reset(2); + } + } else if (yOff > 0) { + movement = y.generate((2/event.getYPrecision())); + if (movement != 0) { + keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN + : KeyEvent.KEYCODE_DPAD_UP; + accel = y.acceleration; + x.reset(2); + } + } + + if (keycode != 0) { + if (movement < 0) movement = -movement; + int accelMovement = (int)(movement * accel); + if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement + + " accelMovement=" + accelMovement + + " accel=" + accel); + if (accelMovement > movement) { + if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: " + + keycode); + movement--; + deliverKeyEvent(new KeyEvent(curTime, curTime, + KeyEvent.ACTION_MULTIPLE, keycode, + accelMovement-movement, metastate), false); + } + while (movement > 0) { + if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: " + + keycode); + movement--; + curTime = SystemClock.uptimeMillis(); + deliverKeyEvent(new KeyEvent(curTime, curTime, + KeyEvent.ACTION_DOWN, keycode, 0, event.getMetaState()), false); + deliverKeyEvent(new KeyEvent(curTime, curTime, + KeyEvent.ACTION_UP, keycode, 0, metastate), false); } - // Let the exception fall through -- the looper will catch - // it and take care of the bad app for us. + mLastTrackballTime = curTime; } } @@ -2862,45 +2766,20 @@ public final class ViewRoot extends Handler implements ViewParent, private final InputHandler mInputHandler = new InputHandler() { public void handleKey(KeyEvent event, Runnable finishedCallback) { mFinishedCallback = finishedCallback; - - if (event.getAction() == KeyEvent.ACTION_DOWN) { - //noinspection ConstantConditions - if (false && event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) { - if (Config.LOGD) Log.d("keydisp", - "==================================================="); - if (Config.LOGD) Log.d("keydisp", "Focused view Hierarchy is:"); - debug(); - - if (Config.LOGD) Log.d("keydisp", - "==================================================="); - } - } - - Message msg = obtainMessage(DISPATCH_KEY); - msg.obj = event; - - if (LOCAL_LOGV) Log.v( - "ViewRoot", "sending key " + event + " to " + mView); - sendMessageAtTime(msg, event.getEventTime()); + dispatchKey(event); } public void handleTouch(MotionEvent event, Runnable finishedCallback) { finishedCallback.run(); - Message msg = obtainMessage(DISPATCH_POINTER); - msg.obj = event; - msg.arg1 = 0; - sendMessageAtTime(msg, event.getEventTime()); + dispatchPointer(event); } public void handleTrackball(MotionEvent event, Runnable finishedCallback) { finishedCallback.run(); - Message msg = obtainMessage(DISPATCH_TRACKBALL); - msg.obj = event; - msg.arg1 = 0; - sendMessageAtTime(msg, event.getEventTime()); + dispatchTrackball(event); } }; @@ -2927,22 +2806,18 @@ public final class ViewRoot extends Handler implements ViewParent, sendMessageAtTime(msg, event.getEventTime()); } - public void dispatchPointer(MotionEvent event, long eventTime, - boolean callWhenDone) { + public void dispatchPointer(MotionEvent event) { Message msg = obtainMessage(DISPATCH_POINTER); msg.obj = event; - msg.arg1 = callWhenDone ? 1 : 0; - sendMessageAtTime(msg, eventTime); + sendMessageAtTime(msg, event.getEventTime()); } - public void dispatchTrackball(MotionEvent event, long eventTime, - boolean callWhenDone) { + public void dispatchTrackball(MotionEvent event) { Message msg = obtainMessage(DISPATCH_TRACKBALL); msg.obj = event; - msg.arg1 = callWhenDone ? 1 : 0; - sendMessageAtTime(msg, eventTime); + sendMessageAtTime(msg, event.getEventTime()); } - + public void dispatchAppVisibility(boolean visible) { Message msg = obtainMessage(DISPATCH_APP_VISIBILITY); msg.arg1 = visible ? 1 : 0; @@ -3073,56 +2948,11 @@ public final class ViewRoot extends Handler implements ViewParent, } } - class EventCompletion extends Handler { - final IWindow mWindow; - final KeyEvent mKeyEvent; - final boolean mIsPointer; - final MotionEvent mMotionEvent; - - EventCompletion(Looper looper, IWindow window, KeyEvent key, - boolean isPointer, MotionEvent motion) { - super(looper); - mWindow = window; - mKeyEvent = key; - mIsPointer = isPointer; - mMotionEvent = motion; - sendEmptyMessage(0); - } - - @Override - public void handleMessage(Message msg) { - if (mKeyEvent != null) { - finishKeyEvent(mKeyEvent); - } else if (mIsPointer) { - boolean didFinish; - MotionEvent event = mMotionEvent; - if (event == null) { - event = getPendingPointerMotionEvent(); - didFinish = true; - } else { - didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE; - } - if (!didFinish) { - finishMotionEvent(); - } - } else { - MotionEvent event = mMotionEvent; - if (event == null) { - event = getPendingTrackballMotionEvent(); - } else { - finishMotionEvent(); - } - } - } - } - static class W extends IWindow.Stub { private final WeakReference<ViewRoot> mViewRoot; - private final Looper mMainLooper; public W(ViewRoot viewRoot, Context context) { mViewRoot = new WeakReference<ViewRoot>(viewRoot); - mMainLooper = context.getMainLooper(); } public void resized(int w, int h, Rect coveredInsets, @@ -3134,40 +2964,6 @@ public final class ViewRoot extends Handler implements ViewParent, } } - public void dispatchKey(KeyEvent event) { - final ViewRoot viewRoot = mViewRoot.get(); - if (viewRoot != null) { - viewRoot.dispatchKey(event); - } else { - Log.w("ViewRoot.W", "Key event " + event + " but no ViewRoot available!"); - viewRoot.new EventCompletion(mMainLooper, this, event, false, null); - } - } - - public void dispatchPointer(MotionEvent event, long eventTime, - boolean callWhenDone) { - final ViewRoot viewRoot = mViewRoot.get(); - if (viewRoot != null) { - if (MEASURE_LATENCY) { - // Note: eventTime is in milliseconds - ViewRoot.lt.sample("* ViewRoot b4 dispatchPtr", System.nanoTime() - eventTime * 1000000); - } - viewRoot.dispatchPointer(event, eventTime, callWhenDone); - } else { - viewRoot.new EventCompletion(mMainLooper, this, null, true, event); - } - } - - public void dispatchTrackball(MotionEvent event, long eventTime, - boolean callWhenDone) { - final ViewRoot viewRoot = mViewRoot.get(); - if (viewRoot != null) { - viewRoot.dispatchTrackball(event, eventTime, callWhenDone); - } else { - viewRoot.new EventCompletion(mMainLooper, this, null, false, event); - } - } - public void dispatchAppVisibility(boolean visible) { final ViewRoot viewRoot = mViewRoot.get(); if (viewRoot != null) { diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index be1f6d2..33757f0 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -78,12 +78,6 @@ public interface WindowManagerPolicy { public final static int FLAG_BRIGHT_HERE = 0x20000000; public final static boolean WATCH_POINTER = false; - - /** - * Temporary flag added during the transition to the new native input dispatcher. - * This will be removed when the old input dispatch code is deleted. - */ - public final static boolean ENABLE_NATIVE_INPUT_DISPATCH = true; // flags for interceptKeyTq /** @@ -555,23 +549,26 @@ public interface WindowManagerPolicy { public Animation createForceHideEnterAnimation(); /** - * Called from the key queue thread before a key is dispatched to the - * input thread. + * Called from the input reader thread before a key is enqueued. * * <p>There are some actions that need to be handled here because they * affect the power state of the device, for example, the power keys. * Generally, it's best to keep as little as possible in the queue thread * because it's the most fragile. + * @param whenNanos The event time in uptime nanoseconds. + * @param keyCode The key code. + * @param down True if the key is down. + * @param policyFlags The policy flags associated with the key. + * @param isScreenOn True if the screen is already on * - * @param event the raw input event as read from the driver - * @param screenIsOn true if the screen is already on * @return The bitwise or of the {@link #ACTION_PASS_TO_USER}, * {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags. */ - public int interceptKeyTq(RawInputEvent event, boolean screenIsOn); + public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, int policyFlags, + boolean isScreenOn); /** - * Called from the input thread before a key is dispatched to a window. + * Called from the input dispatcher thread before a key is dispatched to a window. * * <p>Allows you to define * behavior for keys that can not be overridden by applications or redirect @@ -583,16 +580,17 @@ public interface WindowManagerPolicy { * * @param win The window that currently has focus. This is where the key * event will normally go. - * @param code Key code. - * @param metaKeys bit mask of meta keys that are held. - * @param down Is this a key press (true) or release (false)? + * @param action The key event action. + * @param flags The key event flags. + * @param keyCode The key code. + * @param metaState bit mask of meta keys that are held. * @param repeatCount Number of times a key down has repeated. - * @param flags event's flags. + * @param policyFlags The policy flags associated with the key. * @return Returns true if the policy consumed the event and it should * not be further dispatched. */ - public boolean interceptKeyTi(WindowState win, int code, - int metaKeys, boolean down, int repeatCount, int flags); + public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags, + int keyCode, int metaState, int repeatCount, int policyFlags); /** * Called when layout of the windows is about to start. @@ -701,85 +699,15 @@ public interface WindowManagerPolicy { * Return whether the screen is currently on. */ public boolean isScreenOn(); - + /** - * Perform any initial processing of a low-level input event before the - * window manager handles special keys and generates a high-level event - * that is dispatched to the application. - * - * @param event The input event that has occurred. - * - * @return Return true if you have consumed the event and do not want - * further processing to occur; return false for normal processing. + * Tell the policy that the lid switch has changed state. + * @param whenNanos The time when the change occurred in uptime nanoseconds. + * @param lidOpen True if the lid is now open. */ - public boolean preprocessInputEventTq(RawInputEvent event); - public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen); /** - * Determine whether a given key code is used to cause an app switch - * to occur (most often the HOME key, also often ENDCALL). If you return - * true, then the system will go into a special key processing state - * where it drops any pending events that it cans and adjusts timeouts to - * try to get to this key as quickly as possible. - * - * <p>Note that this function is called from the low-level input queue - * thread, with either/or the window or input lock held; be very careful - * about what you do here. You absolutely should never acquire a lock - * that you would ever hold elsewhere while calling out into the window - * manager or view hierarchy. - * - * @param keycode The key that should be checked for performing an - * app switch before delivering to the application. - * - * @return Return true if this is an app switch key and special processing - * should happen; return false for normal processing. - */ - public boolean isAppSwitchKeyTqTiLwLi(int keycode); - - /** - * Determine whether a given key code is used for movement within a UI, - * and does not generally cause actions to be performed (normally the DPAD - * movement keys, NOT the DPAD center press key). This is called - * when {@link #isAppSwitchKeyTiLi} returns true to remove any pending events - * in the key queue that are not needed to switch applications. - * - * <p>Note that this function is called from the low-level input queue - * thread; be very careful about what you do here. - * - * @param keycode The key that is waiting to be delivered to the - * application. - * - * @return Return true if this is a purely navigation key and can be - * dropped without negative consequences; return false to keep it. - */ - public boolean isMovementKeyTi(int keycode); - - /** - * Given the current state of the world, should this relative movement - * wake up the device? - * - * @param device The device the movement came from. - * @param classes The input classes associated with the device. - * @param event The input event that occurred. - * @return - */ - public boolean isWakeRelMovementTq(int device, int classes, - RawInputEvent event); - - /** - * Given the current state of the world, should this absolute movement - * wake up the device? - * - * @param device The device the movement came from. - * @param classes The input classes associated with the device. - * @param event The input event that occurred. - * @return - */ - public boolean isWakeAbsMovementTq(int device, int classes, - RawInputEvent event); - - /** * Tell the policy if anyone is requesting that keyguard not come on. * * @param enabled Whether keyguard can be on or not. does not actually @@ -852,18 +780,6 @@ public interface WindowManagerPolicy { public void enableScreenAfterBoot(); /** - * Returns true if the user's cheek has been pressed against the phone. This is - * determined by comparing the event's size attribute with a threshold value. - * For example for a motion event like down or up or move, if the size exceeds - * the threshold, it is considered as cheek press. - * @param ev the motion event generated when the cheek is pressed - * against the phone - * @return Returns true if the user's cheek has been pressed against the phone - * screen resulting in an invalid motion event - */ - public boolean isCheekPressedAgainstScreen(MotionEvent ev); - - /** * Called every time the window manager is dispatching a pointer event. */ public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY); @@ -876,13 +792,6 @@ public interface WindowManagerPolicy { public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always); /** - * A special function that is called from the very low-level input queue - * to provide feedback to the user. Currently only called for virtual - * keys. - */ - public void keyFeedbackFromInput(KeyEvent event); - - /** * Called when we have stopped keeping the screen on because a window * requesting this is no longer visible. */ diff --git a/core/java/android/widget/CursorTreeAdapter.java b/core/java/android/widget/CursorTreeAdapter.java index 7b9b7bd..3fadf4c 100644 --- a/core/java/android/widget/CursorTreeAdapter.java +++ b/core/java/android/widget/CursorTreeAdapter.java @@ -134,14 +134,16 @@ public abstract class CursorTreeAdapter extends BaseExpandableListAdapter implem /** * Sets the group Cursor. * - * @param cursor The Cursor to set for the group. + * @param cursor The Cursor to set for the group. If there is an existing cursor + * it will be closed. */ public void setGroupCursor(Cursor cursor) { mGroupCursorHelper.changeCursor(cursor, false); } /** - * Sets the children Cursor for a particular group. + * Sets the children Cursor for a particular group. If there is an existing cursor + * it will be closed. * <p> * This is useful when asynchronously querying to prevent blocking the UI. * @@ -476,7 +478,7 @@ public abstract class CursorTreeAdapter extends BaseExpandableListAdapter implem mCursor.unregisterContentObserver(mContentObserver); mCursor.unregisterDataSetObserver(mDataSetObserver); - mCursor.deactivate(); + mCursor.close(); mCursor = null; } diff --git a/core/java/android/widget/SimpleCursorTreeAdapter.java b/core/java/android/widget/SimpleCursorTreeAdapter.java index a1c65f0..a033542 100644 --- a/core/java/android/widget/SimpleCursorTreeAdapter.java +++ b/core/java/android/widget/SimpleCursorTreeAdapter.java @@ -38,6 +38,10 @@ import android.view.View; * binding can be found, an {@link IllegalStateException} is thrown. */ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter { + + /** The name of the columns that contain the data to display for a group. */ + private String[] mGroupFromNames; + /** The indices of columns that contain data to display for a group. */ private int[] mGroupFrom; /** @@ -46,6 +50,9 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter */ private int[] mGroupTo; + /** The name of the columns that contain the data to display for a child. */ + private String[] mChildFromNames; + /** The indices of columns that contain data to display for a child. */ private int[] mChildFrom; /** @@ -171,38 +178,12 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter private void init(String[] groupFromNames, int[] groupTo, String[] childFromNames, int[] childTo) { + + mGroupFromNames = groupFromNames; mGroupTo = groupTo; + mChildFromNames = childFromNames; mChildTo = childTo; - - // Get the group cursor column indices, the child cursor column indices will come - // when needed - initGroupFromColumns(groupFromNames); - - // Get a temporary child cursor to init the column indices - if (getGroupCount() > 0) { - MyCursorHelper tmpCursorHelper = getChildrenCursorHelper(0, true); - if (tmpCursorHelper != null) { - initChildrenFromColumns(childFromNames, tmpCursorHelper.getCursor()); - deactivateChildrenCursorHelper(0); - } - } - } - - private void initFromColumns(Cursor cursor, String[] fromColumnNames, int[] fromColumns) { - for (int i = fromColumnNames.length - 1; i >= 0; i--) { - fromColumns[i] = cursor.getColumnIndexOrThrow(fromColumnNames[i]); - } - } - - private void initGroupFromColumns(String[] groupFromNames) { - mGroupFrom = new int[groupFromNames.length]; - initFromColumns(mGroupCursorHelper.getCursor(), groupFromNames, mGroupFrom); - } - - private void initChildrenFromColumns(String[] childFromNames, Cursor childCursor) { - mChildFrom = new int[childFromNames.length]; - initFromColumns(childCursor, childFromNames, mChildFrom); } /** @@ -257,13 +238,29 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter } } + private void initFromColumns(Cursor cursor, String[] fromColumnNames, int[] fromColumns) { + for (int i = fromColumnNames.length - 1; i >= 0; i--) { + fromColumns[i] = cursor.getColumnIndexOrThrow(fromColumnNames[i]); + } + } + @Override protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) { + if (mChildFrom == null) { + mChildFrom = new int[mChildFromNames.length]; + initFromColumns(cursor, mChildFromNames, mChildFrom); + } + bindView(view, context, cursor, mChildFrom, mChildTo); } @Override protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) { + if (mGroupFrom == null) { + mGroupFrom = new int[mGroupFromNames.length]; + initFromColumns(cursor, mGroupFromNames, mGroupFrom); + } + bindView(view, context, cursor, mGroupFrom, mGroupTo); } diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index b13d656..4da74e6 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -43,59 +43,6 @@ public class BaseIWindow extends IWindow.Stub { } } - public void dispatchKey(KeyEvent event) { - try { - mSession.finishKey(this); - } catch (RemoteException ex) { - } - } - - public boolean onDispatchPointer(MotionEvent event, long eventTime, - boolean callWhenDone) { - event.recycle(); - return false; - } - - public void dispatchPointer(MotionEvent event, long eventTime, - boolean callWhenDone) { - try { - if (event == null) { - event = mSession.getPendingPointerMove(this); - onDispatchPointer(event, eventTime, false); - } else if (callWhenDone) { - if (!onDispatchPointer(event, eventTime, true)) { - mSession.finishKey(this); - } - } else { - onDispatchPointer(event, eventTime, false); - } - } catch (RemoteException ex) { - } - } - - public boolean onDispatchTrackball(MotionEvent event, long eventTime, - boolean callWhenDone) { - event.recycle(); - return false; - } - - public void dispatchTrackball(MotionEvent event, long eventTime, - boolean callWhenDone) { - try { - if (event == null) { - event = mSession.getPendingTrackballMove(this); - onDispatchTrackball(event, eventTime, false); - } else if (callWhenDone) { - if (!onDispatchTrackball(event, eventTime, true)) { - mSession.finishKey(this); - } - } else { - onDispatchTrackball(event, eventTime, false); - } - } catch (RemoteException ex) { - } - } - public void dispatchAppVisibility(boolean visible) { } diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp index 961f806..847b5a5 100644 --- a/core/jni/android_os_MessageQueue.cpp +++ b/core/jni/android_os_MessageQueue.cpp @@ -53,7 +53,7 @@ private: NativeMessageQueue::NativeMessageQueue() { mPollLoop = PollLoop::getForThread(); if (mPollLoop == NULL) { - mPollLoop = new PollLoop(); + mPollLoop = new PollLoop(false); PollLoop::setForThread(mPollLoop); } } @@ -62,7 +62,7 @@ NativeMessageQueue::~NativeMessageQueue() { } bool NativeMessageQueue::pollOnce(int timeoutMillis) { - return mPollLoop->pollOnce(timeoutMillis); + return mPollLoop->pollOnce(timeoutMillis) != PollLoop::POLL_TIMEOUT; } void NativeMessageQueue::wake() { diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 27cb763..6c13da6 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1246,6 +1246,7 @@ <public type="attr" name="logo" id="0x010102be" /> <public type="attr" name="xlargeScreens" id="0x010102bf" /> <public type="attr" name="heavyWeight" id="0x010102c0" /> + <public type="attr" name="immersive" id="0x010102c1" /> <public-padding type="attr" name="kraken_resource_pad" end="0x01010300" /> <public-padding type="id" name="kraken_resource_pad" end="0x01020040" /> diff --git a/docs/html/sdk/download.jd b/docs/html/sdk/download.jd new file mode 100644 index 0000000..81b4ff6 --- /dev/null +++ b/docs/html/sdk/download.jd @@ -0,0 +1,4 @@ +sdk.redirect=true + +@jd:body + diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd index f5558ab..1a42e7f 100644 --- a/docs/html/sdk/eclipse-adt.jd +++ b/docs/html/sdk/eclipse-adt.jd @@ -48,6 +48,9 @@ href="#installing">Installing the ADT Plugin</a>, below. </p> how to update ADT to the latest version or how to uninstall it, if necessary. </p> +<p class="caution"><strong>Caution:</strong> There are known issues with the ADT plugin running with +Eclipse 3.6. Please stay on 3.5 until further notice.</p> + <h2 id="notes">Revisions</h2> <p>The sections below provide notes about successive releases of diff --git a/docs/html/sdk/requirements.jd b/docs/html/sdk/requirements.jd index cb9cdf3..d710b8e 100644 --- a/docs/html/sdk/requirements.jd +++ b/docs/html/sdk/requirements.jd @@ -23,6 +23,9 @@ installation notes</a>.</li> <h4 style="margin-top:.25em"><em>Eclipse IDE</em></h4> <ul> <li>Eclipse 3.4 (Ganymede) or 3.5 (Galileo) + <p class="caution"><strong>Caution:</strong> There are known issues with the ADT plugin +running with Eclipse 3.6. Please stay on 3.5 until further notice.</p> + </li> <li>Eclipse <a href="http://www.eclipse.org/jdt">JDT</a> plugin (included in most Eclipse IDE packages) </li> <li>If you need to install or update Eclipse, you can download it from <a diff --git a/include/media/EffectBassBoostApi.h b/include/media/EffectBassBoostApi.h new file mode 100644 index 0000000..b24a5f4 --- /dev/null +++ b/include/media/EffectBassBoostApi.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef ANDROID_EFFECTBASSBOOSTAPI_H_ +#define ANDROID_EFFECTBASSBOOSTAPI_H_ + +#include <media/EffectApi.h> + +#if __cplusplus +extern "C" { +#endif + +// TODO: include OpenSLES_IID.h instead +static const effect_uuid_t SL_IID_BASSBOOST_ = { 0x0634f220, 0xddd4, 0x11db, 0xa0fc, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }; +const effect_uuid_t * const SL_IID_BASSBOOST = &SL_IID_BASSBOOST_; + +/* enumerated parameter settings for BassBoost effect */ +typedef enum +{ + BASSBOOST_PARAM_STRENGTH_SUPPORTED, + BASSBOOST_PARAM_STRENGTH +} t_bassboost_params; + +#if __cplusplus +} // extern "C" +#endif + + +#endif /*ANDROID_EFFECTBASSBOOSTAPI_H_*/ diff --git a/include/media/EffectReverbApi.h b/include/media/EffectEnvironmentalReverbApi.h index 6371adb..d490f71 100644 --- a/include/media/EffectReverbApi.h +++ b/include/media/EffectEnvironmentalReverbApi.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_EFFECTREVERBAPI_H_ -#define ANDROID_EFFECTREVERBAPI_H_ +#ifndef ANDROID_EFFECTENVIRONMENTALREVERBAPI_H_ +#define ANDROID_EFFECTENVIRONMENTALREVERBAPI_H_ #include <media/EffectApi.h> @@ -27,14 +27,9 @@ extern "C" { static const effect_uuid_t SL_IID_ENVIRONMENTALREVERB_ = { 0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, { 0x4e, 0x23, 0x4d, 0x6, 0x83, 0x9e } }; const effect_uuid_t * const SL_IID_ENVIRONMENTALREVERB = &SL_IID_ENVIRONMENTALREVERB_; -static const effect_uuid_t SL_IID_PRESETREVERB_ = { 0x47382d60, 0xddd8, 0x11db, 0xbf3a, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }; -const effect_uuid_t * const SL_IID_PRESETREVERB = &SL_IID_PRESETREVERB_; - -/* enumerated parameter settings for Reverb effect */ +/* enumerated parameter settings for environmental reverb effect */ typedef enum { - REVERB_PARAM_BYPASS, - REVERB_PARAM_PRESET, // Parameters below are as defined in OpenSL ES specification for environmental reverb interface REVERB_PARAM_ROOM_LEVEL, // in millibels, range -6000 to 0 REVERB_PARAM_ROOM_HF_LEVEL, // in millibels, range -4000 to 0 @@ -46,17 +41,9 @@ typedef enum REVERB_PARAM_REVERB_DELAY, // in milliseconds, range 0 to 65 REVERB_PARAM_DIFFUSION, // in permilles, range 0 to 1000 REVERB_PARAM_DENSITY, // in permilles, range 0 to 1000 - REVERB_PARAM_PROPERTIES -} t_reverb_params; - - -typedef enum -{ - REVERB_PRESET_LARGE_HALL, - REVERB_PRESET_HALL, - REVERB_PRESET_CHAMBER, - REVERB_PRESET_ROOM, -} t_reverb_presets; + REVERB_PARAM_PROPERTIES, + REVERB_PARAM_BYPASS +} t_env_reverb_params; //t_reverb_properties is equal to SLEnvironmentalReverbSettings defined in OpenSL ES specification. typedef struct s_reverb_properties { @@ -79,4 +66,4 @@ typedef struct s_reverb_properties { #endif -#endif /*ANDROID_EFFECTREVERBAPI_H_*/ +#endif /*ANDROID_EFFECTENVIRONMENTALREVERBAPI_H_*/ diff --git a/include/media/EffectPresetReverbApi.h b/include/media/EffectPresetReverbApi.h new file mode 100644 index 0000000..34ffffe --- /dev/null +++ b/include/media/EffectPresetReverbApi.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef ANDROID_EFFECTPRESETREVERBAPI_H_ +#define ANDROID_EFFECTPRESETREVERBAPI_H_ + +#include <media/EffectApi.h> + +#if __cplusplus +extern "C" { +#endif + +// TODO: include OpenSLES_IID.h instead + +static const effect_uuid_t SL_IID_PRESETREVERB_ = { 0x47382d60, 0xddd8, 0x11db, 0xbf3a, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }; +const effect_uuid_t * const SL_IID_PRESETREVERB = &SL_IID_PRESETREVERB_; + +/* enumerated parameter settings for preset reverb effect */ +typedef enum +{ + REVERB_PARAM_PRESET +} t_preset_reverb_params; + + +typedef enum +{ + REVERB_PRESET_NONE, + REVERB_PRESET_SMALLROOM, + REVERB_PRESET_MEDIUMROOM, + REVERB_PRESET_LARGEROOM, + REVERB_PRESET_MEDIUMHALL, + REVERB_PRESET_LARGEHALL, + REVERB_PRESET_PLATE +} t_reverb_presets; + +#if __cplusplus +} // extern "C" +#endif + + +#endif /*ANDROID_EFFECTPRESETREVERBAPI_H_*/ diff --git a/include/media/EffectVirtualizerApi.h b/include/media/EffectVirtualizerApi.h new file mode 100644 index 0000000..601c384 --- /dev/null +++ b/include/media/EffectVirtualizerApi.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef ANDROID_EFFECTVIRTUALIZERAPI_H_ +#define ANDROID_EFFECTVIRTUALIZERAPI_H_ + +#include <media/EffectApi.h> + +#if __cplusplus +extern "C" { +#endif + +// TODO: include OpenSLES_IID.h instead +static const effect_uuid_t SL_IID_VIRTUALIZER_ = { 0x37cc2c00, 0xdddd, 0x11db, 0x8577, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }; +const effect_uuid_t * const SL_IID_VIRTUALIZER = &SL_IID_VIRTUALIZER_; + +/* enumerated parameter settings for virtualizer effect */ +typedef enum +{ + VIRTUALIZER_PARAM_STRENGTH_SUPPORTED, + VIRTUALIZER_PARAM_STRENGTH +} t_virtualizer_params; + +#if __cplusplus +} // extern "C" +#endif + + +#endif /*ANDROID_EFFECTVIRTUALIZERAPI_H_*/ diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h index 497965c..5e9e368 100644 --- a/include/media/MediaRecorderBase.h +++ b/include/media/MediaRecorderBase.h @@ -48,6 +48,7 @@ struct MediaRecorderBase { virtual status_t close() = 0; virtual status_t reset() = 0; virtual status_t getMaxAmplitude(int *max) = 0; + virtual status_t dump(int fd, const Vector<String16>& args) const = 0; private: MediaRecorderBase(const MediaRecorderBase &); diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h index c04105e..f75d80d 100644 --- a/include/media/PVMediaRecorder.h +++ b/include/media/PVMediaRecorder.h @@ -52,6 +52,7 @@ public: virtual status_t close(); virtual status_t reset(); virtual status_t getMaxAmplitude(int *max); + virtual status_t dump(int fd, const Vector<String16>& args) const; private: status_t doStop(); diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h index 781da35..03c8112 100644 --- a/include/ui/InputReader.h +++ b/include/ui/InputReader.h @@ -250,7 +250,13 @@ struct InputDevice { nsecs_t downTime; struct CurrentVirtualKeyState { - bool down; + enum Status { + STATUS_UP, + STATUS_DOWN, + STATUS_CANCELED + }; + + Status status; nsecs_t downTime; int32_t keyCode; int32_t scanCode; @@ -295,6 +301,7 @@ struct InputDevice { void calculatePointerIds(); bool isPointInsideDisplay(int32_t x, int32_t y) const; + const InputDevice::VirtualKey* findVirtualKeyHit() const; }; InputDevice(int32_t id, uint32_t classes, String8 name); @@ -390,11 +397,9 @@ public: virtual bool getDisplayInfo(int32_t displayId, int32_t* width, int32_t* height, int32_t* orientation) = 0; - /* Provides feedback for a virtual key. + /* Provides feedback for a virtual key down. */ - virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId, - int32_t action, int32_t flags, int32_t keyCode, - int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0; + virtual void virtualKeyDownFeedback() = 0; /* Intercepts a key event. * The policy can use this method as an opportunity to perform power management functions diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h new file mode 100644 index 0000000..075927c --- /dev/null +++ b/include/utils/ObbFile.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef OBBFILE_H_ +#define OBBFILE_H_ + +#include <stdint.h> + +#include <utils/RefBase.h> +#include <utils/String8.h> + +namespace android { + +class ObbFile : public RefBase { +protected: + virtual ~ObbFile(); + +public: + ObbFile(); + + bool readFrom(const char* filename); + bool readFrom(int fd); + bool writeTo(const char* filename); + bool writeTo(int fd); + + const char* getFileName() const { + return mFileName; + } + + const String8 getPackageName() const { + return mPackageName; + } + + int32_t getVersion() const { + return mVersion; + } + + void setPackageName(String8 packageName) { + mPackageName = packageName; + } + + void setVersion(int32_t version) { + mVersion = version; + } + + static inline uint32_t get4LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + + static inline void put4LE(unsigned char* buf, uint32_t val) { + buf[0] = val & 0xFF; + buf[1] = (val >> 8) & 0xFF; + buf[2] = (val >> 16) & 0xFF; + buf[3] = (val >> 24) & 0xFF; + } + +private: + /* Package name this ObbFile is associated with */ + String8 mPackageName; + + /* Package version this ObbFile is associated with */ + int32_t mVersion; + + const char* mFileName; + + size_t mFileSize; + + unsigned char* mReadBuf; + + bool parseObbFile(int fd); +}; + +} +#endif /* OBBFILE_H_ */ diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h index b3651ca..81230e8 100644 --- a/include/utils/PollLoop.h +++ b/include/utils/PollLoop.h @@ -42,7 +42,7 @@ protected: virtual ~PollLoop(); public: - PollLoop(); + PollLoop(bool allowNonCallbacks); /** * A callback that it to be invoked when an event occurs on a file descriptor. @@ -54,6 +54,12 @@ public: */ typedef bool (*Callback)(int fd, int events, void* data); + enum { + POLL_CALLBACK = ALOOPER_POLL_CALLBACK, + POLL_TIMEOUT = ALOOPER_POLL_TIMEOUT, + POLL_ERROR = ALOOPER_POLL_ERROR, + }; + /** * Performs a single call to poll() with optional timeout in milliseconds. * Invokes callbacks for all file descriptors on which an event occurred. @@ -61,16 +67,25 @@ public: * If the timeout is zero, returns immediately without blocking. * If the timeout is negative, waits indefinitely until awoken. * - * Returns true if a callback was invoked or if the loop was awoken by wake(). - * Returns false if a timeout or error occurred. + * Returns ALOOPER_POLL_CALLBACK if a callback was invoked. + * + * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given + * timeout expired. + * + * Returns ALOPER_POLL_ERROR if an error occurred. * - * This method must only be called on the main thread. + * Returns a value >= 0 containing a file descriptor if it has data + * and it has no callback function (requiring the caller here to handle it). + * In this (and only this) case outEvents and outData will contain the poll + * events and data associated with the fd. + * + * This method must only be called on the thread owning the PollLoop. * This method blocks until either a file descriptor is signalled, a timeout occurs, * or wake() is called. * This method does not return until it has finished invoking the appropriate callbacks * for all file descriptors that were signalled. */ - bool pollOnce(int timeoutMillis); + int32_t pollOnce(int timeoutMillis, int* outEvents = NULL, void** outData = NULL); /** * Wakes the loop asynchronously. @@ -81,6 +96,12 @@ public: void wake(); /** + * Control whether this PollLoop instance allows using IDs instead + * of callbacks. + */ + bool getAllowNonCallbacks() const; + + /** * Sets the callback for a file descriptor, replacing the existing one, if any. * It is an error to call this method with events == 0 or callback == NULL. * @@ -95,7 +116,8 @@ public: /** * Like setCallback(), but for the NDK callback function. */ - void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, void* data); + void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, + void* data); /** * Removes the callback for a file descriptor, if one exists. @@ -141,7 +163,9 @@ private: ALooper_callbackFunc* looperCallback; void* data; }; - + + const bool mAllowNonCallbacks; + Mutex mLock; bool mPolling; uint32_t mWaiters; @@ -155,7 +179,9 @@ private: Vector<RequestedCallback> mRequestedCallbacks; Vector<PendingCallback> mPendingCallbacks; // used privately by pollOnce - + Vector<PendingCallback> mPendingFds; // used privately by pollOnce + size_t mPendingFdsPos; + void openWakePipe(); void closeWakePipe(); diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index 8f6d1fe..f809cba 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -54,7 +54,7 @@ static inline nsecs_t now() { InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : mPolicy(policy) { - mPollLoop = new PollLoop(); + mPollLoop = new PollLoop(false); mInboundQueue.head.refCount = -1; mInboundQueue.head.type = EventEntry::TYPE_SENTINEL; @@ -299,14 +299,13 @@ void InputDispatcher::processKeyRepeatLockedInterruptible( uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK; if (entry->refCount == 1) { entry->eventTime = currentTime; - entry->downTime = currentTime; entry->policyFlags = policyFlags; entry->repeatCount += 1; } else { KeyEntry* newEntry = mAllocator.obtainKeyEntry(currentTime, entry->deviceId, entry->nature, policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode, - entry->metaState, entry->repeatCount + 1, currentTime); + entry->metaState, entry->repeatCount + 1, entry->downTime); mKeyRepeatState.lastKeyEntry = newEntry; mAllocator.releaseKeyEntry(entry); @@ -314,6 +313,10 @@ void InputDispatcher::processKeyRepeatLockedInterruptible( entry = newEntry; } + if (entry->repeatCount == 1) { + entry->flags |= KEY_EVENT_FLAG_LONG_PRESS; + } + mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatTimeout; #if DEBUG_OUTBOUND_EVENT_DETAILS diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index 899027c..fced15c 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -189,7 +189,7 @@ void InputDevice::TrackballState::reset() { void InputDevice::TouchScreenState::reset() { lastTouch.clear(); downTime = 0; - currentVirtualKey.down = false; + currentVirtualKey.status = CurrentVirtualKeyState::STATUS_UP; for (uint32_t i = 0; i < MAX_POINTERS; i++) { averagingTouchFilter.historyStart[i] = 0; @@ -746,6 +746,29 @@ bool InputDevice::TouchScreenState::isPointInsideDisplay(int32_t x, int32_t y) c && y <= parameters.yAxis.maxValue; } +const InputDevice::VirtualKey* InputDevice::TouchScreenState::findVirtualKeyHit() const { + int32_t x = currentTouch.pointers[0].x; + int32_t y = currentTouch.pointers[0].y; + for (size_t i = 0; i < virtualKeys.size(); i++) { + const InputDevice::VirtualKey& virtualKey = virtualKeys[i]; + +#if DEBUG_VIRTUAL_KEYS + LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " + "left=%d, top=%d, right=%d, bottom=%d", + x, y, + virtualKey.keyCode, virtualKey.scanCode, + virtualKey.hitLeft, virtualKey.hitTop, + virtualKey.hitRight, virtualKey.hitBottom); +#endif + + if (virtualKey.isHit(x, y)) { + return & virtualKey; + } + } + + return NULL; +} + // --- InputDevice::SingleTouchScreenState --- @@ -1269,81 +1292,76 @@ void InputReader::onTouchScreenChanged(nsecs_t when, bool InputReader::consumeVirtualKeyTouches(nsecs_t when, InputDevice* device, uint32_t policyFlags) { - if (device->touchScreen.currentVirtualKey.down) { + switch (device->touchScreen.currentVirtualKey.status) { + case InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_CANCELED: if (device->touchScreen.currentTouch.pointerCount == 0) { - // Pointer went up while virtual key was down. Send key up event. - device->touchScreen.currentVirtualKey.down = false; + // Pointer went up after virtual key canceled. + device->touchScreen.currentVirtualKey.status = + InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_UP; + } + return true; // consumed + case InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN: + if (device->touchScreen.currentTouch.pointerCount == 0) { + // Pointer went up while virtual key was down. + device->touchScreen.currentVirtualKey.status = + InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_UP; #if DEBUG_VIRTUAL_KEYS LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", device->touchScreen.currentVirtualKey.keyCode, device->touchScreen.currentVirtualKey.scanCode); #endif - dispatchVirtualKey(when, device, policyFlags, KEY_EVENT_ACTION_UP, KEY_EVENT_FLAG_FROM_SYSTEM | KEY_EVENT_FLAG_VIRTUAL_HARD_KEY); return true; // consumed } - int32_t x = device->touchScreen.currentTouch.pointers[0].x; - int32_t y = device->touchScreen.currentTouch.pointers[0].y; - if (device->touchScreen.isPointInsideDisplay(x, y) - || device->touchScreen.currentTouch.pointerCount != 1) { - // Pointer moved inside the display area or another pointer also went down. - // Send key cancellation. - device->touchScreen.currentVirtualKey.down = false; - -#if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", - device->touchScreen.currentVirtualKey.keyCode, - device->touchScreen.currentVirtualKey.scanCode); -#endif - - dispatchVirtualKey(when, device, policyFlags, KEY_EVENT_ACTION_UP, - KEY_EVENT_FLAG_FROM_SYSTEM | KEY_EVENT_FLAG_VIRTUAL_HARD_KEY - | KEY_EVENT_FLAG_CANCELED); - - // Clear the last touch data so we will consider the pointer as having just been - // pressed down when generating subsequent motion events. - device->touchScreen.lastTouch.clear(); - return false; // not consumed + if (device->touchScreen.currentTouch.pointerCount == 1) { + const InputDevice::VirtualKey* virtualKey = device->touchScreen.findVirtualKeyHit(); + if (virtualKey + && virtualKey->keyCode == device->touchScreen.currentVirtualKey.keyCode) { + // Pointer is still within the space of the virtual key. + return true; // consumed + } } - } else if (device->touchScreen.currentTouch.pointerCount == 1 - && device->touchScreen.lastTouch.pointerCount == 0) { - int32_t x = device->touchScreen.currentTouch.pointers[0].x; - int32_t y = device->touchScreen.currentTouch.pointers[0].y; - for (size_t i = 0; i < device->touchScreen.virtualKeys.size(); i++) { - const InputDevice::VirtualKey& virtualKey = device->touchScreen.virtualKeys[i]; + // Pointer left virtual key area or another pointer also went down. + // Send key cancellation. + device->touchScreen.currentVirtualKey.status = + InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_CANCELED; #if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " - "left=%d, top=%d, right=%d, bottom=%d", - x, y, - virtualKey.keyCode, virtualKey.scanCode, - virtualKey.hitLeft, virtualKey.hitTop, - virtualKey.hitRight, virtualKey.hitBottom); + LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", + device->touchScreen.currentVirtualKey.keyCode, + device->touchScreen.currentVirtualKey.scanCode); #endif + dispatchVirtualKey(when, device, policyFlags, KEY_EVENT_ACTION_UP, + KEY_EVENT_FLAG_FROM_SYSTEM | KEY_EVENT_FLAG_VIRTUAL_HARD_KEY + | KEY_EVENT_FLAG_CANCELED); + return true; // consumed - if (virtualKey.isHit(x, y)) { - device->touchScreen.currentVirtualKey.down = true; + default: + if (device->touchScreen.currentTouch.pointerCount == 1 + && device->touchScreen.lastTouch.pointerCount == 0) { + // Pointer just went down. Check for virtual key hit. + const InputDevice::VirtualKey* virtualKey = device->touchScreen.findVirtualKeyHit(); + if (virtualKey) { + device->touchScreen.currentVirtualKey.status = + InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN; device->touchScreen.currentVirtualKey.downTime = when; - device->touchScreen.currentVirtualKey.keyCode = virtualKey.keyCode; - device->touchScreen.currentVirtualKey.scanCode = virtualKey.scanCode; - + device->touchScreen.currentVirtualKey.keyCode = virtualKey->keyCode; + device->touchScreen.currentVirtualKey.scanCode = virtualKey->scanCode; #if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", - device->touchScreen.currentVirtualKey.keyCode, - device->touchScreen.currentVirtualKey.scanCode); + LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", + device->touchScreen.currentVirtualKey.keyCode, + device->touchScreen.currentVirtualKey.scanCode); #endif - dispatchVirtualKey(when, device, policyFlags, KEY_EVENT_ACTION_DOWN, KEY_EVENT_FLAG_FROM_SYSTEM | KEY_EVENT_FLAG_VIRTUAL_HARD_KEY); return true; // consumed } } + return false; // not consumed } - - return false; // not consumed } void InputReader::dispatchVirtualKey(nsecs_t when, @@ -1356,8 +1374,9 @@ void InputReader::dispatchVirtualKey(nsecs_t when, nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime; int32_t metaState = globalMetaState(); - mPolicy->virtualKeyFeedback(when, device->id, keyEventAction, keyEventFlags, - keyCode, scanCode, metaState, downTime); + if (keyEventAction == KEY_EVENT_ACTION_DOWN) { + mPolicy->virtualKeyDownFeedback(); + } int32_t policyActions = mPolicy->interceptKey(when, device->id, keyEventAction == KEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags); @@ -1852,7 +1871,7 @@ void InputReader::configureVirtualKeys(InputDevice* device) { uint32_t flags; if (mEventHub->scancodeToKeycode(device->id, virtualKey.scanCode, & keyCode, & flags)) { - LOGI(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); + LOGW(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); device->touchScreen.virtualKeys.pop(); // drop the key continue; } @@ -1933,7 +1952,8 @@ void InputReader::updateExportedVirtualKeyState() { for (size_t i = 0; i < mDevices.size(); i++) { InputDevice* device = mDevices.valueAt(i); if (device->isTouchScreen()) { - if (device->touchScreen.currentVirtualKey.down) { + if (device->touchScreen.currentVirtualKey.status + == InputDevice::TouchScreenState::CurrentVirtualKeyState::STATUS_DOWN) { keyCode = device->touchScreen.currentVirtualKey.keyCode; scanCode = device->touchScreen.currentVirtualKey.scanCode; } diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 7d4524a..2bb42ab 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -26,6 +26,7 @@ commonSources:= \ Debug.cpp \ FileMap.cpp \ Flattenable.cpp \ + ObbFile.cpp \ Pool.cpp \ RefBase.cpp \ ResourceTypes.cpp \ @@ -65,6 +66,11 @@ LOCAL_CFLAGS += -DMB_CUR_MAX=1 endif endif +ifeq ($(HOST_OS),darwin) +# MacOS doesn't have lseek64. However, off_t is 64-bit anyway. +LOCAL_CFLAGS += -DOFF_T_IS_64_BIT +endif + include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp new file mode 100644 index 0000000..3a4a03a --- /dev/null +++ b/libs/utils/ObbFile.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2010 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. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define LOG_TAG "ObbFile" +#include <utils/Log.h> +#include <utils/ObbFile.h> + +//#define DEBUG 1 + +#define kFooterTagSize 8 /* last two 32-bit integers */ + +#define kFooterMinSize 21 /* 32-bit signature version + * 32-bit package version + * 32-bit package name size + * 1-character package name + * 32-bit footer size + * 32-bit footer marker + */ + +#define kMaxBufSize 32768 /* Maximum file read buffer */ + +#define kSignature 0x01059983U /* ObbFile signature */ + +#define kSigVersion 1 /* We only know about signature version 1 */ + +/* offsets in version 1 of the header */ +#define kPackageVersionOffset 4 +#define kPackageNameLenOffset 8 +#define kPackageNameOffset 12 + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif + +/* + * Work around situations where off_t is 64-bit and use off64_t in + * situations where it's 32-bit. + */ +#ifdef OFF_T_IS_64_BIT +#define my_lseek64 lseek +typedef off_t my_off64_t; +#else +#define my_lseek64 lseek64 +typedef off64_t my_off64_t; +#endif + +namespace android { + +ObbFile::ObbFile() : + mVersion(-1) { +} + +ObbFile::~ObbFile() { +} + +bool ObbFile::readFrom(const char* filename) +{ + int fd; + bool success = false; + + fd = ::open(filename, O_RDONLY); + if (fd < 0) { + goto out; + } + success = readFrom(fd); + close(fd); + +out: + if (!success) { + LOGW("failed to read from %s\n", filename); + } + return success; +} + +bool ObbFile::readFrom(int fd) +{ + if (fd < 0) { + LOGW("failed to read file\n"); + return false; + } + + return parseObbFile(fd); +} + +bool ObbFile::parseObbFile(int fd) +{ + my_off64_t fileLength = my_lseek64(fd, 0, SEEK_END); + + if (fileLength < kFooterMinSize) { + if (fileLength < 0) { + LOGW("error seeking in ObbFile: %s\n", strerror(errno)); + } else { + LOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize); + } + return false; + } + + ssize_t actual; + size_t footerSize; + + { + my_lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); + + char *footer = new char[kFooterTagSize]; + actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); + if (actual != kFooterTagSize) { + LOGW("couldn't read footer signature: %s\n", strerror(errno)); + return false; + } + + unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t)); + if (fileSig != kSignature) { + LOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n", + kSignature, fileSig); + return false; + } + + footerSize = get4LE((unsigned char*)footer); + if (footerSize > (size_t)fileLength - kFooterTagSize + || footerSize > kMaxBufSize) { + LOGW("claimed footer size is too large (0x%08lx; file size is 0x%08llx)\n", + footerSize, fileLength); + return false; + } + } + + my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize; + if (my_lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { + LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); + return false; + } + + size_t readAmount = kMaxBufSize; + if (readAmount > footerSize) + readAmount = footerSize; + + char* scanBuf = (char*)malloc(readAmount); + if (scanBuf == NULL) { + LOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); + return false; + } + + actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount)); + // readAmount is guaranteed to be less than kMaxBufSize + if (actual != (ssize_t)readAmount) { + LOGI("couldn't read ObbFile footer: %s\n", strerror(errno)); + free(scanBuf); + return false; + } + +#ifdef DEBUG + for (int i = 0; i < readAmount; ++i) { + LOGI("char: 0x%02x", scanBuf[i]); + } +#endif + + uint32_t sigVersion = get4LE((unsigned char*)scanBuf); + if (sigVersion != kSigVersion) { + LOGW("Unsupported ObbFile version %d\n", sigVersion); + free(scanBuf); + return false; + } + + mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset); + + uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); + if (packageNameLen <= 0 + || packageNameLen > (footerSize - kPackageNameOffset)) { + LOGW("bad ObbFile package name length (0x%08x)\n", packageNameLen); + free(scanBuf); + return false; + } + + char* packageName = reinterpret_cast<char*>(scanBuf + kPackageNameOffset); + mPackageName = String8(const_cast<char*>(packageName), packageNameLen); + + free(scanBuf); + return true; +} + +bool ObbFile::writeTo(const char* filename) +{ + int fd; + bool success = false; + + fd = ::open(filename, O_WRONLY); + if (fd < 0) { + goto out; + } + success = writeTo(fd); + close(fd); + +out: + if (!success) { + LOGW("failed to write to %s: %s\n", filename, strerror(errno)); + } + return success; +} + +bool ObbFile::writeTo(int fd) +{ + if (fd < 0) { + return false; + } + + if (mPackageName.size() == 0 || mVersion == -1) { + LOGW("tried to write uninitialized ObbFile data"); + return false; + } + + unsigned char intBuf[sizeof(uint32_t)+1]; + memset(&intBuf, 0, sizeof(intBuf)); + + put4LE(intBuf, kSigVersion); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write signature version: %s", strerror(errno)); + return false; + } + + put4LE(intBuf, mVersion); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write package version"); + return false; + } + + size_t packageNameLen = mPackageName.size(); + put4LE(intBuf, packageNameLen); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write package name length: %s", strerror(errno)); + return false; + } + + if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) { + LOGW("couldn't write package name: %s", strerror(errno)); + return false; + } + + put4LE(intBuf, 3*sizeof(uint32_t) + packageNameLen); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write footer size: %s", strerror(errno)); + return false; + } + + put4LE(intBuf, kSignature); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write footer magic signature: %s", strerror(errno)); + return false; + } + + return true; +} + +} diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp index 58fe141..f740fa0 100644 --- a/libs/utils/PollLoop.cpp +++ b/libs/utils/PollLoop.cpp @@ -25,8 +25,9 @@ static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; static bool gHaveTLS = false; static pthread_key_t gTLS = 0; -PollLoop::PollLoop() : - mPolling(false), mWaiters(0) { +PollLoop::PollLoop(bool allowNonCallbacks) : + mAllowNonCallbacks(allowNonCallbacks), mPolling(false), + mWaiters(0), mPendingFdsPos(0) { openWakePipe(); } @@ -106,7 +107,18 @@ void PollLoop::closeWakePipe() { // method is currently only called by the destructor. } -bool PollLoop::pollOnce(int timeoutMillis) { +int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { + // If there are still pending fds from the last call, dispatch those + // first, to avoid an earlier fd from starving later ones. + const size_t pendingFdsCount = mPendingFds.size(); + if (mPendingFdsPos < pendingFdsCount) { + const PendingCallback& pending = mPendingFds.itemAt(mPendingFdsPos); + mPendingFdsPos++; + if (outEvents != NULL) *outEvents = pending.events; + if (outData != NULL) *outData = pending.data; + return pending.fd; + } + mLock.lock(); while (mWaiters != 0) { mResume.wait(mLock); @@ -114,7 +126,7 @@ bool PollLoop::pollOnce(int timeoutMillis) { mPolling = true; mLock.unlock(); - bool result; + int32_t result; size_t requestedCount = mRequestedFds.size(); #if DEBUG_POLL_AND_WAKE @@ -131,7 +143,7 @@ bool PollLoop::pollOnce(int timeoutMillis) { #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - timeout", this); #endif - result = false; + result = POLL_TIMEOUT; goto Done; } @@ -143,7 +155,7 @@ bool PollLoop::pollOnce(int timeoutMillis) { if (errno != EINTR) { LOGW("Poll failed with an unexpected error, errno=%d", errno); } - result = false; + result = POLL_ERROR; goto Done; } @@ -156,38 +168,44 @@ bool PollLoop::pollOnce(int timeoutMillis) { #endif mPendingCallbacks.clear(); + mPendingFds.clear(); + mPendingFdsPos = 0; + if (outEvents != NULL) *outEvents = 0; + if (outData != NULL) *outData = NULL; + + result = POLL_CALLBACK; for (size_t i = 0; i < requestedCount; i++) { const struct pollfd& requestedFd = mRequestedFds.itemAt(i); short revents = requestedFd.revents; if (revents) { const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i); - Callback callback = requestedCallback.callback; - ALooper_callbackFunc* looperCallback = requestedCallback.looperCallback; - - if (callback || looperCallback) { - PendingCallback pendingCallback; - pendingCallback.fd = requestedFd.fd; - pendingCallback.events = requestedFd.revents; - pendingCallback.callback = callback; - pendingCallback.looperCallback = looperCallback; - pendingCallback.data = requestedCallback.data; - mPendingCallbacks.push(pendingCallback); + PendingCallback pending; + pending.fd = requestedFd.fd; + pending.events = revents; + pending.callback = requestedCallback.callback; + pending.looperCallback = requestedCallback.looperCallback; + pending.data = requestedCallback.data; + + if (pending.callback || pending.looperCallback) { + mPendingCallbacks.push(pending); + } else if (pending.fd != mWakeReadPipeFd) { + if (result == POLL_CALLBACK) { + result = pending.fd; + if (outEvents != NULL) *outEvents = pending.events; + if (outData != NULL) *outData = pending.data; + } else { + mPendingFds.push(pending); + } } else { - if (requestedFd.fd == mWakeReadPipeFd) { #if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - awoken", this); -#endif - char buffer[16]; - ssize_t nRead; - do { - nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while (nRead == sizeof(buffer)); - } else { -#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - LOGD("%p ~ pollOnce - fd %d has no callback!", this, requestedFd.fd); + LOGD("%p ~ pollOnce - awoken", this); #endif - } + char buffer[16]; + ssize_t nRead; + do { + nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); + } while (nRead == sizeof(buffer)); } respondedCount -= 1; @@ -196,7 +214,6 @@ bool PollLoop::pollOnce(int timeoutMillis) { } } } - result = true; Done: mLock.lock(); @@ -206,7 +223,7 @@ Done: } mLock.unlock(); - if (result) { + if (result == POLL_CALLBACK || result >= 0) { size_t pendingCount = mPendingCallbacks.size(); for (size_t i = 0; i < pendingCount; i++) { const PendingCallback& pendingCallback = mPendingCallbacks.itemAt(i); @@ -247,6 +264,10 @@ void PollLoop::wake() { } } +bool PollLoop::getAllowNonCallbacks() const { + return mAllowNonCallbacks; +} + void PollLoop::setCallback(int fd, int events, Callback callback, void* data) { setCallbackCommon(fd, events, callback, NULL, data); } @@ -263,12 +284,18 @@ void PollLoop::setCallbackCommon(int fd, int events, Callback callback, LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events); #endif - if (! events || (! callback && ! looperCallback)) { - LOGE("Invalid attempt to set a callback with no selected poll events or no callback."); + if (! events) { + LOGE("Invalid attempt to set a callback with no selected poll events."); removeCallback(fd); return; } + if (! callback && ! looperCallback && ! mAllowNonCallbacks) { + LOGE("Invalid attempt to set NULL callback but not allowed."); + removeCallback(fd); + return; + } + wakeAndLock(); struct pollfd requestedFd; diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 92ebfd7c..f1b8cd5 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -3,6 +3,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) test_src_files := \ + ObbFile_test.cpp \ PollLoop_test.cpp shared_libraries := \ diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/utils/tests/ObbFile_test.cpp new file mode 100644 index 0000000..05aaf08 --- /dev/null +++ b/libs/utils/tests/ObbFile_test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 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. + */ + +#define LOG_TAG "ObbFile_test" +#include <utils/Log.h> +#include <utils/ObbFile.h> +#include <utils/RefBase.h> +#include <utils/String8.h> + +#include <gtest/gtest.h> + +namespace android { + +#define TEST_FILENAME "/test.obb" + +class ObbFileTest : public testing::Test { +protected: + sp<ObbFile> mObbFile; + char* mExternalStorage; + char* mFileName; + + virtual void SetUp() { + mObbFile = new ObbFile(); + mExternalStorage = getenv("EXTERNAL_STORAGE"); + + const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1; + mFileName = new char[totalLen]; + snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME); + } + + virtual void TearDown() { + } +}; + +TEST_F(ObbFileTest, ReadFailure) { + EXPECT_FALSE(mObbFile->readFrom(-1)) + << "No failure on invalid file descriptor"; +} + +TEST_F(ObbFileTest, WriteThenRead) { + const char* packageName = "com.example.obbfile"; + const int32_t versionNum = 1; + + mObbFile->setPackageName(String8(packageName)); + mObbFile->setVersion(versionNum); + + EXPECT_TRUE(mObbFile->writeTo(mFileName)) + << "couldn't write to fake .obb file"; + + mObbFile = new ObbFile(); + + EXPECT_TRUE(mObbFile->readFrom(mFileName)) + << "couldn't read from fake .obb file"; + + EXPECT_EQ(versionNum, mObbFile->getVersion()) + << "version didn't come out the same as it went in"; + const char* currentPackageName = mObbFile->getPackageName().string(); + EXPECT_STREQ(packageName, currentPackageName) + << "package name didn't come out the same as it went in"; +} + +} diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp index 4848c0f..02f1808 100644 --- a/libs/utils/tests/PollLoop_test.cpp +++ b/libs/utils/tests/PollLoop_test.cpp @@ -87,7 +87,7 @@ protected: sp<PollLoop> mPollLoop; virtual void SetUp() { - mPollLoop = new PollLoop(); + mPollLoop = new PollLoop(false); } virtual void TearDown() { @@ -98,26 +98,26 @@ protected: TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) { StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; } TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) { mPollLoop->wake(); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(1000); + int32_t result = mPollLoop->pollOnce(1000); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because wake() was called before waiting"; - EXPECT_TRUE(result) - << "pollOnce result should be true because loop was awoken"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because loop was awoken"; } TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) { @@ -125,24 +125,24 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyRe delayedWake->run(); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(1000); + int32_t result = mPollLoop->pollOnce(1000); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal wake delay"; - EXPECT_TRUE(result) - << "pollOnce result should be true because loop was awoken"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because loop was awoken"; } TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) { StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(0); + int32_t result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; } TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) { @@ -152,13 +152,13 @@ TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturn handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(0); + int32_t result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; EXPECT_EQ(0, handler.callbackCount) << "callback should not have been invoked because FD was not signalled"; } @@ -171,13 +171,13 @@ TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCa handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(0); + int32_t result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked exactly once"; EXPECT_EQ(pipe.receiveFd, handler.fd) @@ -193,13 +193,13 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeou handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; EXPECT_EQ(0, handler.callbackCount) << "callback should not have been invoked because FD was not signalled"; } @@ -212,15 +212,15 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_Imme handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked exactly once"; EXPECT_EQ(pipe.receiveFd, handler.fd) @@ -238,15 +238,15 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_Promp delayedWriteSignal->run(); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(1000); + int32_t result = mPollLoop->pollOnce(1000); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal signal delay"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked exactly once"; EXPECT_EQ(pipe.receiveFd, handler.fd) @@ -264,15 +264,15 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeIn mPollLoop->removeCallback(pipe.receiveFd); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout because FD was no longer registered"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; EXPECT_EQ(0, handler.callbackCount) << "callback should not be invoked"; } @@ -287,15 +287,15 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvoke pipe.writeSignal(); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(0); + int32_t result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal zero because FD was already signalled"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked"; @@ -310,8 +310,8 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvoke << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal zero because timeout was zero"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; EXPECT_EQ(1, handler.callbackCount) << "callback should not be invoked this time"; } @@ -351,15 +351,15 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeI pipe.writeSignal(); // would cause FD to be considered signalled StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because FD was already signalled"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(0, handler1.callbackCount) << "original handler callback should not be invoked because it was replaced"; EXPECT_EQ(1, handler2.callbackCount) diff --git a/media/java/android/media/BassBoost.java b/media/java/android/media/BassBoost.java new file mode 100644 index 0000000..ef4ce05 --- /dev/null +++ b/media/java/android/media/BassBoost.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2009 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.media; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +import android.media.AudioEffect; + +/** + * Bass boost is an audio effect to boost or amplify low frequencies of the sound. It is comparable + * to an simple equalizer but limited to one band amplification in the low frequency range. + * <p>An application creates a BassBoost object to instantiate and control a bass boost engine + * in the audio framework. + * <p>The methods, parameter types and units exposed by the BassBoost implementation are directly + * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/) + * for the SLBassBoostItf interface. Please refer to this specification for more details. + * <p>To attach the BassBoost to a particular AudioTrack or MediaPlayer, specify the audio session + * ID of this AudioTrack or MediaPlayer when constructing the BassBoost. If the audio session ID 0 + * is specified, the BassBoost applies to the main audio output mix. + // TODO when AudioEffect is unhidden + // <p> See {_at_link android.media.AudioEffect} class for more details on controlling audio effects. + * + * {@hide Pending API council review} + */ + +public class BassBoost extends AudioEffect { + + private final static String TAG = "BassBoost"; + + // These constants must be synchronized with those in + // frameworks/base/include/media/EffectBassBoostApi.h + /** + * Is strength parameter supported by bass boost engine. Parameter ID for getParameter(). + */ + public static final int PARAM_STRENGTH_SUPPORTED = 0; + /** + * Bass boost effect strength. Parameter ID for + * {@link android.media.BassBoost.OnParameterChangeListener} + */ + public static final int PARAM_STRENGTH = 1; + + /** + * Indicates if strength parameter is supported by the bass boost engine + */ + private boolean mStrengthSupported = false; + + /** + * Registered listener for parameter changes. + */ + private OnParameterChangeListener mParamListener = null; + + /** + * Listener used internally to to receive raw parameter change event from AudioEffect super class + */ + private BaseParameterListener mBaseParamListener = null; + + /** + * Lock for access to mParamListener + */ + private final Object mParamListenerLock = new Object(); + + /** + * Class constructor. + * @param priority the priority level requested by the application for controlling the BassBoost + * engine. As the same engine can be shared by several applications, this parameter indicates + * how much the requesting application needs control of effect parameters. The normal priority + * is 0, above normal is a positive number, below normal a negative number. + * @param audioSession System wide unique audio session identifier. If audioSession + * is not 0, the BassBoost will be attached to the MediaPlayer or AudioTrack in the + * same audio session. Otherwise, the BassBoost will apply to the output mix. + * + * @throws java.lang.IllegalStateException + * @throws java.lang.IllegalArgumentException + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + public BassBoost(int priority, int audioSession) + throws IllegalStateException, IllegalArgumentException, + UnsupportedOperationException, RuntimeException { + super(EFFECT_TYPE_BASS_BOOST, EFFECT_TYPE_NULL, priority, audioSession); + + short[] value = new short[1]; + checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value)); + mStrengthSupported = (value[0] != 0); + } + + /** + * Indicates whether setting strength is supported. If this method returns false, only one + * strength is supported and the setStrength() method always rounds to that value. + * @return true is strength parameter is supported, false otherwise + */ + public boolean getStrengthSupported() { + return mStrengthSupported; + } + + /** + * Sets the strength of the bass boost effect. If the implementation does not support per mille + * accuracy for setting the strength, it is allowed to round the given strength to the nearest + * supported value. You can use the {@link #getRoundedStrength()} method to query the + * (possibly rounded) value that was actually set. + * @param strength Strength of the effect. The valid range for strength strength is [0, 1000], + * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setStrength(short strength) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + checkStatus(setParameter(PARAM_STRENGTH, strength)); + } + + /** + * Gets the current strength of the effect. + * @return The strength of the effect. The valid range for strength is [0, 1000], where 0 per + * mille designates the mildest effect and 1000 per mille the strongest + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getRoundedStrength() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + short[] value = new short[1]; + checkStatus(getParameter(PARAM_STRENGTH, value)); + return value[0]; + } + + /** + * The OnParameterChangeListener interface defines a method called by the BassBoost when a + * parameter value has changed. + */ + public interface OnParameterChangeListener { + /** + * Method called when a parameter value has changed. The method is called only if the + * parameter was changed by another application having the control of the same + * BassBoost engine. + * @param effect the BassBoost on which the interface is registered. + * @param status status of the set parameter operation. + // TODO when AudioEffect is unhidden + // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}. + * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ... + * @param value the new parameter value. + */ + void onParameterChange(BassBoost effect, int status, int param, short value); + } + + /** + * Listener used internally to receive unformatted parameter change events from AudioEffect + * super class. + */ + private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { + private BaseParameterListener() { + + } + public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { + OnParameterChangeListener l = null; + + synchronized (mParamListenerLock) { + if (mParamListener != null) { + l = mParamListener; + } + } + if (l != null) { + int p = -1; + short v = -1; + + if (param.length == 4) { + p = byteArrayToInt(param, 0); + } + if (value.length == 2) { + v = byteArrayToShort(value, 0); + } + if (p != -1 && v != -1) { + l.onParameterChange(BassBoost.this, status, p, v); + } + } + } + } + + /** + * Registers an OnParameterChangeListener interface. + * @param listener OnParameterChangeListener interface registered + */ + public void setParameterListener(OnParameterChangeListener listener) { + synchronized (mParamListenerLock) { + if (mParamListener == null) { + mParamListener = listener; + mBaseParamListener = new BaseParameterListener(); + super.setParameterListener(mBaseParamListener); + } + } + } +} diff --git a/media/java/android/media/EnvironmentalReverb.java b/media/java/android/media/EnvironmentalReverb.java new file mode 100644 index 0000000..88230fc --- /dev/null +++ b/media/java/android/media/EnvironmentalReverb.java @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2009 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.media; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; + +import android.media.AudioEffect; + +/** + * A sound generated within a room travels in many directions. The listener first hears the + * direct sound from the source itself. Later, he or she hears discrete echoes caused by sound + * bouncing off nearby walls, the ceiling and the floor. As sound waves arrive after + * undergoing more and more reflections, individual reflections become indistinguishable and + * the listener hears continuous reverberation that decays over time. + * Reverb is vital for modeling a listener's environment. It can be used in music applications + * to simulate music being played back in various environments, or in games to immerse the + * listener within the game's environment. + * The EnvironmentalReverb class allows an application to control each reverb engine property in a + * global reverb environment and is more suitable for games. For basic control, more suitable for + * music applications, it is recommended to use the + // TODO when PresetReverb is unhidden + // {_at_link android.media.PresetReverb} class. + * <p>An application creates a EnvironmentalReverb object to instantiate and control a reverb engine + * in the audio framework. + * <p>The methods, parameter types and units exposed by the EnvironmentalReverb implementation are + * directly mapping those defined by the OpenSL ES 1.0.1 Specification + * (http://www.khronos.org/opensles/) for the SLEnvironmentalReverbItf interface. + * Please refer to this specification for more details. + * <p>The EnvironmentalReverb is an output mix auxiliary effect and should be created on + * Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect, + * they must be explicitely attached to it and a send level must be specified. Use the effect ID + * returned by getId() method to designate this particular effect when attaching it to the + * MediaPlayer or AudioTrack. + // TODO when AudioEffect is unhidden + // <p> See {_at_link android.media.AudioEffect} class for more details on controlling + * audio effects. + * + * {@hide Pending API council review} + */ + +public class EnvironmentalReverb extends AudioEffect { + + private final static String TAG = "EnvironmentalReverb"; + + // These constants must be synchronized with those in + // frameworks/base/include/media/EffectEnvironmentalReverbApi.h + + /** + * Room level. Parameter ID for + * {@link android.media.EnvironmentalReverb.OnParameterChangeListener} + */ + public static final int PARAM_ROOM_LEVEL = 0; + /** + * Room HF level. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_ROOM_HF_LEVEL = 1; + /** + * Decay time. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_DECAY_TIME = 2; + /** + * Decay HF ratio. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_DECAY_HF_RATIO = 3; + /** + * Early reflections level. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_REFLECTIONS_LEVEL = 4; + /** + * Early reflections delay. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_REFLECTIONS_DELAY = 5; + /** + * Reverb level. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_REVERB_LEVEL = 6; + /** + * Reverb delay. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_REVERB_DELAY = 7; + /** + * Diffusion. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_DIFFUSION = 8; + /** + * Density. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_DENSITY = 9; + + /** + * Registered listener for parameter changes + */ + private OnParameterChangeListener mParamListener = null; + + /** + * Listener used internally to to receive raw parameter change event from AudioEffect super + * class + */ + private BaseParameterListener mBaseParamListener = null; + + /** + * Lock for access to mParamListener + */ + private final Object mParamListenerLock = new Object(); + + /** + * Class constructor. + * @param priority the priority level requested by the application for controlling the + * EnvironmentalReverb engine. As the same engine can be shared by several applications, this + * parameter indicates how much the requesting application needs control of effect parameters. + * The normal priority is 0, above normal is a positive number, below normal a negative number. + * @param audioSession System wide unique audio session identifier. If audioSession + * is not 0, the EnvironmentalReverb will be attached to the MediaPlayer or AudioTrack in the + * same audio session. Otherwise, the EnvironmentalReverb will apply to the output mix. + * As the EnvironmentalReverb is an auxiliary effect it is recommended to instantiate it on + * audio session 0 and to attach it to the MediaPLayer auxiliary output. + * + * @throws java.lang.IllegalArgumentException + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + public EnvironmentalReverb(int priority, int audioSession) + throws IllegalArgumentException, UnsupportedOperationException, RuntimeException { + super(EFFECT_TYPE_ENV_REVERB, EFFECT_TYPE_NULL, priority, audioSession); + Log.e(TAG, "contructor"); + } + + /** + * Sets the master volume level of the environmental reverb effect. + * @param room Room level in millibels. The valid range is [-9000, 0]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setRoomLevel(short room) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(room); + checkStatus(setParameter(PARAM_ROOM_LEVEL, param)); + } + + /** + * Gets the master volume level of the environmental reverb effect. + * @return the room level in millibels. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getRoomLevel() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_ROOM_LEVEL, param)); + return byteArrayToShort(param); + } + + /** + * Sets the volume level at 5 kHz relative to the volume level at low frequencies of the + * overall reverb effect. + * <p>This controls a low-pass filter that will reduce the level of the high-frequency. + * @param roomHF High frequency attenuation level in millibels. The valid range is [-9000, 0]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setRoomHFLevel(short roomHF) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(roomHF); + checkStatus(setParameter(PARAM_ROOM_HF_LEVEL, param)); + } + + /** + * Gets the room HF level. + * @return the room HF level in millibels. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getRoomHFLevel() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_ROOM_HF_LEVEL, param)); + return byteArrayToShort(param); + } + + /** + * Sets the time taken for the level of reverberation to decay by 60 dB. + * @param decayTime Decay time in milliseconds. The valid range is [100, 20000]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setDecayTime(int decayTime) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = intToByteArray(decayTime); + checkStatus(setParameter(PARAM_DECAY_TIME, param)); + } + + /** + * Gets the decay time. + * @return the decay time in milliseconds. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public int getDecayTime() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[4]; + checkStatus(getParameter(PARAM_DECAY_TIME, param)); + return byteArrayToInt(param); + } + + /** + * Sets the ratio of high frequency decay time (at 5 kHz) relative to the decay time at low + * frequencies. + * @param decayHFRatio High frequency decay ratio using a permille scale. The valid range is + * [100, 2000]. A ratio of 1000 indicates that all frequencies decay at the same rate. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setDecayHFRatio(short decayHFRatio) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(decayHFRatio); + checkStatus(setParameter(PARAM_DECAY_HF_RATIO, param)); + } + + /** + * Gets the ratio of high frequency decay time (at 5 kHz) relative to low frequencies. + * @return the decay HF ration. See {@link #setDecayHFRatio(short)} for units. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getDecayHFRatio() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_DECAY_HF_RATIO, param)); + return byteArrayToShort(param); + } + + /** + * Sets the volume level of the early reflections. + * <p>This level is combined with the overall room level + * (set using {@link #setRoomLevel(short)}). + * @param reflectionsLevel Reflection level in millibels. The valid range is [-9000, 1000]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setReflectionsLevel(short reflectionsLevel) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(reflectionsLevel); + checkStatus(setParameter(PARAM_REFLECTIONS_LEVEL, param)); + } + + /** + * Gets the volume level of the early reflections. + * @return the early reflections level in millibels. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getReflectionsLevel() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_REFLECTIONS_LEVEL, param)); + return byteArrayToShort(param); + } + + /** + * Sets the delay time for the early reflections. + * <p>This method sets the time between when the direct path is heard and when the first + * reflection is heard. + * @param reflectionsDelay Reflections delay in milliseconds. The valid range is [0, 300]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setReflectionsDelay(int reflectionsDelay) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = intToByteArray(reflectionsDelay); + checkStatus(setParameter(PARAM_REFLECTIONS_DELAY, param)); + } + + /** + * Gets the reflections delay. + * @return the early reflections delay in milliseconds. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public int getReflectionsDelay() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[4]; + checkStatus(getParameter(PARAM_REFLECTIONS_DELAY, param)); + return byteArrayToInt(param); + } + + /** + * Sets the volume level of the late reverberation. + * <p>This level is combined with the overall room level (set using {@link #setRoomLevel(short)}). + * @param reverbLevel Reverb level in millibels. The valid range is [-9000, 2000]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setReverbLevel(short reverbLevel) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(reverbLevel); + checkStatus(setParameter(PARAM_REVERB_LEVEL, param)); + } + + /** + * Gets the reverb level. + * @return the reverb level in millibels. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getReverbLevel() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_REVERB_LEVEL, param)); + return byteArrayToShort(param); + } + + /** + * Sets the time between the first reflection and the reverberation. + * @param reverbDelay Reverb delay in milliseconds. The valid range is [0, 100]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setReverbDelay(int reverbDelay) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = intToByteArray(reverbDelay); + checkStatus(setParameter(PARAM_REVERB_DELAY, param)); + } + + /** + * Gets the reverb delay. + * @return the reverb delay in milliseconds. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public int getReverbDelay() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[4]; + checkStatus(getParameter(PARAM_REVERB_DELAY, param)); + return byteArrayToInt(param); + } + + /** + * Sets the echo density in the late reverberation decay. + * <p>The scale should approximately map linearly to the perceived change in reverberation. + * @param diffusion Diffusion specified using a permille scale. The diffusion valid range is + * [0, 1000]. A value of 1000 o/oo indicates a smooth reverberation decay. + * Values below this level give a more <i>grainy</i> character. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setDiffusion(short diffusion) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(diffusion); + checkStatus(setParameter(PARAM_DIFFUSION, param)); + } + + /** + * Gets diffusion level. + * @return the diffusion level. See {@link #setDiffusion(short)} for units. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getDiffusion() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_DIFFUSION, param)); + return byteArrayToShort(param); + } + + + /** + * Controls the modal density of the late reverberation decay. + * <p> The scale should approximately map linearly to the perceived change in reverberation. + * A lower density creates a hollow sound that is useful for simulating small reverberation + * spaces such as bathrooms. + * @param density Density specified using a permille scale. The valid range is [0, 1000]. + * A value of 1000 o/oo indicates a natural sounding reverberation. Values below this level + * produce a more colored effect. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setDensity(short density) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(density); + checkStatus(setParameter(PARAM_DENSITY, param)); + } + + /** + * Gets the density level. + * @return the density level. See {@link #setDiffusion(short)} for units. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getDensity() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_DENSITY, param)); + return byteArrayToShort(param); + } + + + /** + * The OnParameterChangeListener interface defines a method called by the EnvironmentalReverb + * when a parameter value has changed. + */ + public interface OnParameterChangeListener { + /** + * Method called when a parameter value has changed. The method is called only if the + * parameter was changed by another application having the control of the same + * EnvironmentalReverb engine. + * @param effect the EnvironmentalReverb on which the interface is registered. + * @param status status of the set parameter operation. + // TODO when AudioEffect is unhidden + // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}. + * @param param ID of the modified parameter. See {@link #PARAM_ROOM_LEVEL} ... + * @param value the new parameter value. + */ + void onParameterChange(EnvironmentalReverb effect, int status, int param, int value); + } + + /** + * Listener used internally to receive unformatted parameter change events from AudioEffect + * super class. + */ + private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { + private BaseParameterListener() { + + } + public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { + OnParameterChangeListener l = null; + + synchronized (mParamListenerLock) { + if (mParamListener != null) { + l = mParamListener; + } + } + if (l != null) { + int p = -1; + int v = -1; + + if (param.length == 4) { + p = byteArrayToInt(param, 0); + } + if (value.length == 2) { + v = (int)byteArrayToShort(value, 0); + } else if (value.length == 4) { + v = byteArrayToInt(value, 0); + } + if (p != -1 && v != -1) { + l.onParameterChange(EnvironmentalReverb.this, status, p, v); + } + } + } + } + + /** + * Registers an OnParameterChangeListener interface. + * @param listener OnParameterChangeListener interface registered + */ + public void setParameterListener(OnParameterChangeListener listener) { + synchronized (mParamListenerLock) { + if (mParamListener == null) { + mParamListener = listener; + mBaseParamListener = new BaseParameterListener(); + super.setParameterListener(mBaseParamListener); + } + } + } +} diff --git a/media/java/android/media/Equalizer.java b/media/java/android/media/Equalizer.java new file mode 100644 index 0000000..082f694 --- /dev/null +++ b/media/java/android/media/Equalizer.java @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2009 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.media; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +import android.media.AudioEffect; + +/** + * An Equalizer is used to alter the frequency response of a particular music source or of the main + * output mix. + * <p>An application creates an Equalizer object to instantiate and control an Equalizer engine + * in the audio framework. The application can either simply use predefined presets or have a more + * precise control of the gain in each frequency band controlled by the equalizer. + * <p>The methods, parameter types and units exposed by the Equalizer implementation are directly + * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/) + * for the SLEqualizerItf interface. Please refer to this specification for more details. + * <p>To attach the Equalizer to a particular AudioTrack or MediaPlayer, specify the audio session + * ID of this AudioTrack or MediaPlayer when constructing the Equalizer. If the audio session ID 0 + * is specified, the Equalizer applies to the main audio output mix. + // TODO when AudioEffect is unhidden + // <p> See {_at_link android.media.AudioEffect} class for more details on controlling audio effects. + * + * {@hide Pending API council review} + */ + +public class Equalizer extends AudioEffect { + + private final static String TAG = "Equalizer"; + + // These constants must be synchronized with those in + // frameworks/base/include/media/EffectEqualizerApi.h + /** + * Number of bands. Parameter ID for {@link android.media.Equalizer.OnParameterChangeListener} + */ + public static final int PARAM_NUM_BANDS = 0; + /** + * Band level range. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_LEVEL_RANGE = 1; + /** + * Band level. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_BAND_LEVEL = 2; + /** + * Band center frequency. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_CENTER_FREQ = 3; + /** + * Band frequency range. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_BAND_FREQ_RANGE = 4; + /** + * Band for a given frequency. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_GET_BAND = 5; + /** + * Current preset. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_CURRENT_PRESET = 6; + /** + * Request number of presets. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_GET_NUM_OF_PRESETS = 7; + /** + * Request preset name. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_GET_PRESET_NAME = 8; + /** + * maximum size for perset name + */ + public static final int PARAM_STRING_SIZE_MAX = 32; + + /** + * Number of presets implemented by Equalizer engine + */ + private int mNumPresets; + /** + * Names of presets implemented by Equalizer engine + */ + private String[] mPresetNames; + + /** + * Registered listener for parameter changes. + */ + private OnParameterChangeListener mParamListener = null; + + /** + * Listener used internally to to receive raw parameter change event from AudioEffect super class + */ + private BaseParameterListener mBaseParamListener = null; + + /** + * Lock for access to mParamListener + */ + private final Object mParamListenerLock = new Object(); + + /** + * Class constructor. + * @param priority the priority level requested by the application for controlling the Equalizer + * engine. As the same engine can be shared by several applications, this parameter indicates + * how much the requesting application needs control of effect parameters. The normal priority + * is 0, above normal is a positive number, below normal a negative number. + * @param audioSession System wide unique audio session identifier. If audioSession + * is not 0, the Equalizer will be attached to the MediaPlayer or AudioTrack in the + * same audio session. Otherwise, the Equalizer will apply to the output mix. + * + * @throws java.lang.IllegalStateException + * @throws java.lang.IllegalArgumentException + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + public Equalizer(int priority, int audioSession) + throws IllegalStateException, IllegalArgumentException, + UnsupportedOperationException, RuntimeException { + super(EFFECT_TYPE_EQUALIZER, EFFECT_TYPE_NULL, priority, audioSession); + + mNumPresets = (int)getNumberOfPresets(); + + if (mNumPresets != 0) { + mPresetNames = new String[mNumPresets]; + byte[] value = new byte[PARAM_STRING_SIZE_MAX]; + int[] param = new int[2]; + param[0] = PARAM_GET_PRESET_NAME; + for (int i = 0; i < mNumPresets; i++) { + param[1] = i; + checkStatus(getParameter(param, value)); + int length = 0; + while (value[length] != 0) length++; + try { + mPresetNames[i] = new String(value, 0, length, "ISO-8859-1"); + Log.e(TAG, "preset #: "+i+" name: "+mPresetNames[i]+" length: "+length); + } catch (java.io.UnsupportedEncodingException e) { + Log.e(TAG, "preset name decode error"); + } + } + } + } + + /** + * Gets the number of frequency bands supported by the Equalizer engine. + * @return the number of bands + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getNumberOfBands() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[1]; + param[0] = PARAM_NUM_BANDS; + short[] value = new short[1]; + checkStatus(getParameter(param, value)); + return value[0]; + } + + /** + * Gets the level range for use by {@link #setBandLevel(int,short)}. The level is expressed in + * milliBel. + * @return the band level range in an array of short integers. The first element is the lower + * limit of the range, the second element the upper limit. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short[] getBandLevelRange() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[1]; + int[] value = new int[2]; + param[0] = PARAM_LEVEL_RANGE; + checkStatus(getParameter(param, value)); + + short[] result = new short[2]; + + result[0] = (short)value[0]; + result[1] = (short)value[1]; + + return result; + } + + /** + * Sets the given equalizer band to the given gain value. + * @param band Frequency band that will have the new gain. The numbering of the bands starts + * from 0 and ends at (number of bands - 1). See @see #getNumberOfBands(). + * @param level New gain in millibels that will be set to the given band. getBandLevelRange() + * will define the maximum and minimum values. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setBandLevel(int band, short level) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[2]; + int[] value = new int[1]; + + param[0] = PARAM_BAND_LEVEL; + param[1] = band; + value[0] = (int)level; + checkStatus(setParameter(param, value)); + } + + /** + * Gets the gain set for the given equalizer band. + * @param band Frequency band whose gain is requested. The numbering of the bands starts + * from 0 and ends at (number of bands - 1). + * @return Gain in millibels of the given band. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getBandLevel(int band) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[2]; + int[] result = new int[1]; + + param[0] = PARAM_BAND_LEVEL; + param[1] = band; + checkStatus(getParameter(param, result)); + + return (short)result[0]; + } + + + /** + * Gets the center frequency of the given band. + * @param band Frequency band whose center frequency is requested. The numbering of the bands + * starts from 0 and ends at (number of bands - 1). + * @return The center frequency in milliHertz + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public int getCenterFreq(int band) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[2]; + int[] result = new int[1]; + + param[0] = PARAM_CENTER_FREQ; + param[1] = band; + checkStatus(getParameter(param, result)); + + return result[0]; + } + + /** + * Gets the frequency range of the given frequency band. + * @param band Frequency band whose frequency range is requested. The numbering of the bands + * starts from 0 and ends at (number of bands - 1). + * @return The frequency range in millHertz in an array of integers. The first element is the + * lower limit of the range, the second element the upper limit. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public int[] getBandFreqRange(int band) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[2]; + int[] result = new int[2]; + param[0] = PARAM_BAND_FREQ_RANGE; + param[1] = band; + checkStatus(getParameter(param, result)); + + return result; + } + + /** + * Gets the band that has the most effect on the given frequency. + * @param frequency Frequency in milliHertz which is to be equalized via the returned band. + * @return Frequency band that has most effect on the given frequency. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public int getBand(int frequency) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[2]; + int[] result = new int[1]; + + param[0] = PARAM_GET_BAND; + param[1] = frequency; + checkStatus(getParameter(param, result)); + + return result[0]; + } + + /** + * Gets current preset. + * @return Preset that is set at the moment. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getCurrentPreset() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[1]; + param[0] = PARAM_CURRENT_PRESET; + short[] value = new short[1]; + checkStatus(getParameter(param, value)); + return value[0]; + } + + /** + * Sets the equalizer according to the given preset. + * @param preset New preset that will be taken into use. The valid range is [0, + * number of presets-1]. See {@see #getNumberOfPresets()}. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void usePreset(short preset) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + checkStatus(setParameter(PARAM_CURRENT_PRESET, preset)); + } + + /** + * Gets the total number of presets the equalizer supports. The presets will have indices + * [0, number of presets-1]. + * @return The number of presets the equalizer supports. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getNumberOfPresets() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[1]; + param[0] = PARAM_GET_NUM_OF_PRESETS; + short[] value = new short[1]; + checkStatus(getParameter(param, value)); + return value[0]; + } + + /** + * Gets the preset name based on the index. + * @param preset Index of the preset. The valid range is [0, number of presets-1]. + * @return A string containing the name of the given preset. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public String getPresetName(short preset) + { + if (preset >= 0 && preset < mNumPresets) { + return mPresetNames[preset]; + } else { + return ""; + } + } + + /** + * The OnParameterChangeListener interface defines a method called by the Equalizer when a + * parameter value has changed. + */ + public interface OnParameterChangeListener { + /** + * Method called when a parameter value has changed. The method is called only if the + * parameter was changed by another application having the control of the same + * Equalizer engine. + * @param effect the Equalizer on which the interface is registered. + * @param status status of the set parameter operation. + // TODO when AudioEffect is unhidden + // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}. + * @param param1 ID of the modified parameter. See {@link #PARAM_BAND_LEVEL} ... + * @param param2 additional parameter qualifier (e.g the band for band level parameter). + * @param value the new parameter value. + */ + void onParameterChange(Equalizer effect, int status, int param1, int param2, int value); + } + + /** + * Listener used internally to receive unformatted parameter change events from AudioEffect + * super class. + */ + private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { + private BaseParameterListener() { + + } + public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { + OnParameterChangeListener l = null; + + synchronized (mParamListenerLock) { + if (mParamListener != null) { + l = mParamListener; + } + } + if (l != null) { + int p1 = -1; + int p2 = -1; + int v = -1; + + if (param.length >= 4) { + p1 = byteArrayToInt(param, 0); + if (param.length >= 8) { + p2 = byteArrayToInt(param, 4); + } + } + if (value.length == 2) { + v = (int)byteArrayToShort(value, 0);; + } else if (value.length == 4) { + v = byteArrayToInt(value, 0); + } + + if (p1 != -1 && v != -1) { + l.onParameterChange(Equalizer.this, status, p1, p2, v); + } + } + } + } + + /** + * Registers an OnParameterChangeListener interface. + * @param listener OnParameterChangeListener interface registered + */ + public void setParameterListener(OnParameterChangeListener listener) { + synchronized (mParamListenerLock) { + if (mParamListener == null) { + mParamListener = listener; + mBaseParamListener = new BaseParameterListener(); + super.setParameterListener(mBaseParamListener); + } + } + } + +} diff --git a/media/java/android/media/PresetReverb.java b/media/java/android/media/PresetReverb.java new file mode 100644 index 0000000..83a01a4 --- /dev/null +++ b/media/java/android/media/PresetReverb.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2009 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.media; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; + +import android.media.AudioEffect; + +/** + * A sound generated within a room travels in many directions. The listener first hears the + * direct sound from the source itself. Later, he or she hears discrete echoes caused by sound + * bouncing off nearby walls, the ceiling and the floor. As sound waves arrive after + * undergoing more and more reflections, individual reflections become indistinguishable and + * the listener hears continuous reverberation that decays over time. + * Reverb is vital for modeling a listener's environment. It can be used in music applications + * to simulate music being played back in various environments, or in games to immerse the + * listener within the game's environment. + * The PresetReverb class allows an application to configure the global reverb using a reverb preset. + * This is primarily used for adding some reverb in a music playback context. Applications + * requiring control over a more advanced environmental reverb are advised to use the + // TODO when EnvironmentalReverb is unhidden + // {_at_link android.media.EnvironmentalReverb} class. + * <p>An application creates a PresetReverb object to instantiate and control a reverb engine in the + * audio framework. + * <p>The methods, parameter types and units exposed by the PresetReverb implementation are + * directly mapping those defined by the OpenSL ES 1.0.1 Specification + * (http://www.khronos.org/opensles/) for the SLPresetReverbItf interface. + * Please refer to this specification for more details. + * <p>The PresetReverb is an output mix auxiliary effect and should be created on + * Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect, + * they must be explicitely attached to it and a send level must be specified. Use the effect ID + * returned by getId() method to designate this particular effect when attaching it to the + * MediaPlayer or AudioTrack. + // TODO when AudioEffect is unhidden + // <p> See {_at_link android.media.AudioEffect} class for more details on controlling audio effects. + * + * {@hide Pending API council review} + */ + +public class PresetReverb extends AudioEffect { + + private final static String TAG = "PresetReverb"; + + // These constants must be synchronized with those in + // frameworks/base/include/media/EffectPresetReverbApi.h + + /** + * Preset. Parameter ID for + * {@link android.media.PresetReverb.OnParameterChangeListener} + */ + public static final int PARAM_PRESET = 0; + + /** + * Room level. Parameter ID for + * {@link android.media.PresetReverb.OnParameterChangeListener} + */ + public static final int PRESET_NONE = 0; + public static final int PRESET_SMALLROOM = 1; + public static final int PRESET_MEDIUMROOM = 2; + public static final int PRESET_LARGEROOM = 3; + public static final int PRESET_MEDIUMHALL = 4; + public static final int PRESET_LARGEHALL = 5; + public static final int PRESET_PLATE = 6; + + /** + * Registered listener for parameter changes. + */ + private OnParameterChangeListener mParamListener = null; + + /** + * Listener used internally to to receive raw parameter change event from AudioEffect super class + */ + private BaseParameterListener mBaseParamListener = null; + + /** + * Lock for access to mParamListener + */ + private final Object mParamListenerLock = new Object(); + + /** + * Class constructor. + * @param priority the priority level requested by the application for controlling the + * PresetReverb engine. As the same engine can be shared by several applications, this + * parameter indicates how much the requesting application needs control of effect parameters. + * The normal priority is 0, above normal is a positive number, below normal a negative number. + * @param audioSession System wide unique audio session identifier. If audioSession + * is not 0, the PresetReverb will be attached to the MediaPlayer or AudioTrack in the + * same audio session. Otherwise, the PresetReverb will apply to the output mix. + * As the PresetReverb is an auxiliary effect it is recommended to instantiate it on + * audio session 0 and to attach it to the MediaPLayer auxiliary output. + * + * @throws java.lang.IllegalArgumentException + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + public PresetReverb(int priority, int audioSession) + throws IllegalArgumentException, UnsupportedOperationException, RuntimeException { + super(EFFECT_TYPE_PRESET_REVERB, EFFECT_TYPE_NULL, priority, audioSession); + Log.e(TAG, "contructor"); + } + + /** + * Enables a preset on the reverb. + * <p>The reverb PRESET_NONE disables any reverb from the current output but does not free the + * resources associated with the reverb. For an application to signal to the implementation + * to free the resources, it must call the release() method. + * @param preset This must be one of the the preset constants defined in this class. + * e.g. {@link #PRESET_SMALLROOM} + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setPreset(short preset) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + checkStatus(setParameter(PARAM_PRESET, preset)); + } + + /** + * Gets current reverb preset. + * @return Preset that is set at the moment. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getPreset() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[1]; + param[0] = PARAM_PRESET; + short[] value = new short[1]; + checkStatus(getParameter(param, value)); + return value[0]; + } + + /** + * The OnParameterChangeListener interface defines a method called by the PresetReverb + * when a parameter value has changed. + */ + public interface OnParameterChangeListener { + /** + * Method called when a parameter value has changed. The method is called only if the + * parameter was changed by another application having the control of the same + * PresetReverb engine. + * @param effect the PresetReverb on which the interface is registered. + * @param status status of the set parameter operation. + // TODO when AudioEffect is unhidden + // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}. + * @param param ID of the modified parameter. See {@link #PARAM_PRESET} ... + * @param value the new parameter value. + */ + void onParameterChange(PresetReverb effect, int status, int param, short value); + } + + /** + * Listener used internally to receive unformatted parameter change events from AudioEffect + * super class. + */ + private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { + private BaseParameterListener() { + + } + public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { + OnParameterChangeListener l = null; + + synchronized (mParamListenerLock) { + if (mParamListener != null) { + l = mParamListener; + } + } + if (l != null) { + int p = -1; + short v = -1; + + if (param.length == 4) { + p = byteArrayToInt(param, 0); + } + if (value.length == 2) { + v = byteArrayToShort(value, 0); + } + if (p != -1 && v != -1) { + l.onParameterChange(PresetReverb.this, status, p, v); + } + } + } + } + + /** + * Registers an OnParameterChangeListener interface. + * @param listener OnParameterChangeListener interface registered + */ + public void setParameterListener(OnParameterChangeListener listener) { + synchronized (mParamListenerLock) { + if (mParamListener == null) { + mParamListener = listener; + mBaseParamListener = new BaseParameterListener(); + super.setParameterListener(mBaseParamListener); + } + } + } +} diff --git a/media/java/android/media/Virtualizer.java b/media/java/android/media/Virtualizer.java new file mode 100644 index 0000000..9f71297 --- /dev/null +++ b/media/java/android/media/Virtualizer.java @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2009 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.media; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +import android.media.AudioEffect; + +/** + * An audio virtualizer is a general name for an effect to spatialize audio channels. The exact + * behavior of this effect is dependent on the number of audio input channels and the types and + * number of audio output channels of the device. For example, in the case of a stereo input and + * stereo headphone output, a stereo widening effect is used when this effect is turned on. + * <p>An application creates a Virtualizer object to instantiate and control a virtualizer engine + * in the audio framework. + * <p>The methods, parameter types and units exposed by the Virtualizer implementation are directly + * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/) + * for the SLVirtualizerItf interface. Please refer to this specification for more details. + * <p>To attach the Virtualizer to a particular AudioTrack or MediaPlayer, specify the audio session + * ID of this AudioTrack or MediaPlayer when constructing the Virtualizer. If the audio session ID 0 + * is specified, the Virtualizer applies to the main audio output mix. + // TODO when AudioEffect is unhidden + // <p> See {_at_link android.media.AudioEffect} class for more details on controlling audio effects. + * + * {@hide Pending API council review} + */ + +public class Virtualizer extends AudioEffect { + + private final static String TAG = "Virtualizer"; + + // These constants must be synchronized with those in frameworks/base/include/media/EffectVirtualizerApi.h + /** + * Is strength parameter supported by virtualizer engine. Parameter ID for getParameter(). + */ + public static final int PARAM_STRENGTH_SUPPORTED = 0; + /** + * Virtualizer effect strength. Parameter ID for + * {@link android.media.Virtualizer.OnParameterChangeListener} + */ + public static final int PARAM_STRENGTH = 1; + + /** + * Indicates if strength parameter is supported by the virtualizer engine + */ + private boolean mStrengthSupported = false; + + /** + * Registered listener for parameter changes. + */ + private OnParameterChangeListener mParamListener = null; + + /** + * Listener used internally to to receive raw parameter change event from AudioEffect super class + */ + private BaseParameterListener mBaseParamListener = null; + + /** + * Lock for access to mParamListener + */ + private final Object mParamListenerLock = new Object(); + + /** + * Class constructor. + * @param priority the priority level requested by the application for controlling the Virtualizer + * engine. As the same engine can be shared by several applications, this parameter indicates + * how much the requesting application needs control of effect parameters. The normal priority + * is 0, above normal is a positive number, below normal a negative number. + * @param audioSession System wide unique audio session identifier. If audioSession + * is not 0, the Virtualizer will be attached to the MediaPlayer or AudioTrack in the + * same audio session. Otherwise, the Virtualizer will apply to the output mix. + * + * @throws java.lang.IllegalStateException + * @throws java.lang.IllegalArgumentException + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + public Virtualizer(int priority, int audioSession) + throws IllegalStateException, IllegalArgumentException, + UnsupportedOperationException, RuntimeException { + super(EFFECT_TYPE_VIRTUALIZER, EFFECT_TYPE_NULL, priority, audioSession); + + short[] value = new short[1]; + checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value)); + mStrengthSupported = (value[0] != 0); + } + + /** + * Indicates whether setting strength is supported. If this method returns false, only one + * strength is supported and the setStrength() method always rounds to that value. + * @return true is strength parameter is supported, false otherwise + */ + public boolean getStrengthSupported() { + return mStrengthSupported; + } + + /** + * Sets the strength of the virtualizer effect. If the implementation does not support per mille + * accuracy for setting the strength, it is allowed to round the given strength to the nearest + * supported value. You can use the {@link #getRoundedStrength()} method to query the + * (possibly rounded) value that was actually set. + * @param strength Strength of the effect. The valid range for strength strength is [0, 1000], + * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setStrength(short strength) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + checkStatus(setParameter(PARAM_STRENGTH, strength)); + } + + /** + * Gets the current strength of the effect. + * @return The strength of the effect. The valid range for strength is [0, 1000], where 0 per + * mille designates the mildest effect and 1000 per mille the strongest + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getRoundedStrength() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + short[] value = new short[1]; + checkStatus(getParameter(PARAM_STRENGTH, value)); + return value[0]; + } + + /** + * The OnParameterChangeListener interface defines a method called by the Virtualizer when a + * parameter value has changed. + */ + public interface OnParameterChangeListener { + /** + * Method called when a parameter value has changed. The method is called only if the + * parameter was changed by another application having the control of the same + * Virtualizer engine. + * @param effect the Virtualizer on which the interface is registered. + * @param status status of the set parameter operation. + // TODO when AudioEffect is unhidden + // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}. + * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ... + * @param value the new parameter value. + */ + void onParameterChange(Virtualizer effect, int status, int param, short value); + } + + /** + * Listener used internally to receive unformatted parameter change events from AudioEffect + * super class. + */ + private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { + private BaseParameterListener() { + + } + public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { + OnParameterChangeListener l = null; + + synchronized (mParamListenerLock) { + if (mParamListener != null) { + l = mParamListener; + } + } + if (l != null) { + int p = -1; + short v = -1; + + if (param.length == 4) { + p = byteArrayToInt(param, 0); + } + if (value.length == 2) { + v = byteArrayToShort(value, 0); + } + if (p != -1 && v != -1) { + l.onParameterChange(Virtualizer.this, status, p, v); + } + } + } + } + + /** + * Registers an OnParameterChangeListener interface. + * @param listener OnParameterChangeListener interface registered + */ + public void setParameterListener(OnParameterChangeListener listener) { + synchronized (mParamListenerLock) { + if (mParamListener == null) { + mParamListener = listener; + mBaseParamListener = new BaseParameterListener(); + super.setParameterListener(mBaseParamListener); + } + } + } +} diff --git a/media/libeffects/EffectReverb.c b/media/libeffects/EffectReverb.c index ada252c..5c87f23 100644 --- a/media/libeffects/EffectReverb.c +++ b/media/libeffects/EffectReverb.c @@ -57,7 +57,7 @@ static const effect_descriptor_t gInsertEnvReverbDescriptor = { // Google auxiliary preset reverb UUID: 63909320-53a6-11df-bdbd-0002a5d5c51b static const effect_descriptor_t gAuxPresetReverbDescriptor = { - {0x47382d60, 0xddd8, 0x4763, 0x11db, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, {0x63909320, 0x53a6, 0x11df, 0xbdbd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, EFFECT_API_VERSION, EFFECT_FLAG_TYPE_AUXILIARY, @@ -69,7 +69,7 @@ static const effect_descriptor_t gAuxPresetReverbDescriptor = { // Google insert preset reverb UUID: d93dc6a0-6342-11df-b128-0002a5d5c51b static const effect_descriptor_t gInsertPresetReverbDescriptor = { - {0x47382d60, 0xddd8, 0x4763, 0x11db, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, {0xd93dc6a0, 0x6342, 0x11df, 0xb128, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, EFFECT_API_VERSION, EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST, @@ -196,7 +196,7 @@ static int Reverb_Process(effect_interface_t self, audio_buffer_t *inBuffer, aud pReverb = (reverb_object_t*) &pRvbModule->context; //if bypassed or the preset forces the signal to be completely dry - if (pReverb->m_bBypass) { + if (pReverb->m_bBypass != 0) { if (inBuffer->raw != outBuffer->raw) { int16_t smp; pSrc = inBuffer->s16; @@ -520,7 +520,7 @@ int Reverb_Configure(reverb_module_t *pRvbModule, effect_config_t *pConfig, pReverb->m_bUseNoise = true; // for debugging purposes, allow bypass - pReverb->m_bBypass = false; + pReverb->m_bBypass = 0; pReverb->m_nNextRoom = 1; @@ -662,248 +662,254 @@ int Reverb_getParameter(reverb_object_t *pReverb, int32_t param, size_t *pSize, int32_t temp2; size_t size; - if (pReverb->m_Preset && param != REVERB_PARAM_PRESET) { - return -EINVAL; - } - if (!pReverb->m_Preset && param == REVERB_PARAM_PRESET) { - return -EINVAL; - } - - switch (param) { - case REVERB_PARAM_ROOM_LEVEL: - case REVERB_PARAM_ROOM_HF_LEVEL: - case REVERB_PARAM_DECAY_HF_RATIO: - case REVERB_PARAM_REFLECTIONS_LEVEL: - case REVERB_PARAM_REVERB_LEVEL: - case REVERB_PARAM_DIFFUSION: - case REVERB_PARAM_DENSITY: + if (pReverb->m_Preset) { + if (param != REVERB_PARAM_PRESET || *pSize < sizeof(int16_t)) { + return -EINVAL; + } size = sizeof(int16_t); - break; - - case REVERB_PARAM_BYPASS: - case REVERB_PARAM_PRESET: - case REVERB_PARAM_DECAY_TIME: - case REVERB_PARAM_REFLECTIONS_DELAY: - case REVERB_PARAM_REVERB_DELAY: - size = sizeof(int32_t); - break; - - case REVERB_PARAM_PROPERTIES: - size = sizeof(t_reverb_properties); - break; - - default: - return -EINVAL; - } + pValue16 = (int16_t *)pValue; + // REVERB_PRESET_NONE is mapped to bypass + if (pReverb->m_bBypass != 0) { + *pValue16 = (int16_t)REVERB_PRESET_NONE; + } else { + *pValue16 = (int16_t)(pReverb->m_nNextRoom + 1); + } + LOGV("get REVERB_PARAM_PRESET, preset %d", *pValue16); + } else { + switch (param) { + case REVERB_PARAM_ROOM_LEVEL: + case REVERB_PARAM_ROOM_HF_LEVEL: + case REVERB_PARAM_DECAY_HF_RATIO: + case REVERB_PARAM_REFLECTIONS_LEVEL: + case REVERB_PARAM_REVERB_LEVEL: + case REVERB_PARAM_DIFFUSION: + case REVERB_PARAM_DENSITY: + size = sizeof(int16_t); + break; - if (*pSize < size) { - return -EINVAL; - } - *pSize = size; - pValue32 = (int32_t *) pValue; - pValue16 = (int16_t *) pValue; - pProperties = (t_reverb_properties *) pValue; + case REVERB_PARAM_BYPASS: + case REVERB_PARAM_DECAY_TIME: + case REVERB_PARAM_REFLECTIONS_DELAY: + case REVERB_PARAM_REVERB_DELAY: + size = sizeof(int32_t); + break; - switch (param) { - case REVERB_PARAM_BYPASS: - *(int32_t *) pValue = (int32_t) pReverb->m_bBypass; - break; - case REVERB_PARAM_PRESET: - *(int32_t *) pValue = (int8_t) pReverb->m_nCurrentRoom; - break; + case REVERB_PARAM_PROPERTIES: + size = sizeof(t_reverb_properties); + break; - case REVERB_PARAM_PROPERTIES: - pValue16 = &pProperties->roomLevel; - /* FALL THROUGH */ + default: + return -EINVAL; + } - case REVERB_PARAM_ROOM_LEVEL: - // Convert m_nRoomLpfFwd to millibels - temp = (pReverb->m_nRoomLpfFwd << 15) - / (32767 - pReverb->m_nRoomLpfFbk); - *pValue16 = Effects_Linear16ToMillibels(temp); + if (*pSize < size) { + return -EINVAL; + } - LOGV("get REVERB_PARAM_ROOM_LEVEL %d, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", *pValue16, temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); + pValue32 = (int32_t *) pValue; + pValue16 = (int16_t *) pValue; + pProperties = (t_reverb_properties *) pValue; - if (param == REVERB_PARAM_ROOM_LEVEL) { - break; - } - pValue16 = &pProperties->roomHFLevel; - /* FALL THROUGH */ - - case REVERB_PARAM_ROOM_HF_LEVEL: - // The ratio between linear gain at 0Hz and at 5000Hz for the room low pass is: - // (1 + a1) / sqrt(a1^2 + 2*C*a1 + 1) where: - // - a1 is minus the LP feedback gain: -pReverb->m_nRoomLpfFbk - // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz - - temp = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFbk); - LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 %d", temp); - temp2 = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nCosWT_5KHz) - << 1; - LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, 2 Cos a1 %d", temp2); - temp = 32767 + temp - temp2; - LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 + 2 Cos a1 + 1 %d", temp); - temp = Effects_Sqrt(temp) * 181; - LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, SQRT(a1^2 + 2 Cos a1 + 1) %d", temp); - temp = ((32767 - pReverb->m_nRoomLpfFbk) << 15) / temp; - - LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); - - *pValue16 = Effects_Linear16ToMillibels(temp); - - if (param == REVERB_PARAM_ROOM_HF_LEVEL) { + switch (param) { + case REVERB_PARAM_BYPASS: + *pValue32 = (int32_t) pReverb->m_bBypass; break; - } - pValue32 = &pProperties->decayTime; - /* FALL THROUGH */ - case REVERB_PARAM_DECAY_TIME: - // Calculate reverb feedback path gain - temp = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk); - temp = Effects_Linear16ToMillibels(temp); + case REVERB_PARAM_PROPERTIES: + pValue16 = &pProperties->roomLevel; + /* FALL THROUGH */ - // Calculate decay time: g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time - temp = (-6000 * pReverb->m_nLateDelay) / temp; + case REVERB_PARAM_ROOM_LEVEL: + // Convert m_nRoomLpfFwd to millibels + temp = (pReverb->m_nRoomLpfFwd << 15) + / (32767 - pReverb->m_nRoomLpfFbk); + *pValue16 = Effects_Linear16ToMillibels(temp); - // Convert samples to ms - *pValue32 = (temp * 1000) / pReverb->m_nSamplingRate; + LOGV("get REVERB_PARAM_ROOM_LEVEL %d, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", *pValue16, temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); - LOGV("get REVERB_PARAM_DECAY_TIME, samples %d, ms %d", temp, *pValue32); - - if (param == REVERB_PARAM_DECAY_TIME) { - break; - } - pValue16 = &pProperties->decayHFRatio; - /* FALL THROUGH */ - - case REVERB_PARAM_DECAY_HF_RATIO: - // If r is the decay HF ratio (r = REVERB_PARAM_DECAY_HF_RATIO/1000) we have: - // DT_5000Hz = DT_0Hz * r - // and G_5000Hz = -6000 * d / DT_5000Hz and G_0Hz = -6000 * d / DT_0Hz in millibels so : - // r = G_0Hz/G_5000Hz in millibels - // The linear gain at 5000Hz is b0 / sqrt(a1^2 + 2*C*a1 + 1) where: - // - a1 is minus the LP feedback gain: -pReverb->m_nRvbLpfFbk - // - b0 is the LP forward gain: pReverb->m_nRvbLpfFwd - // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz - if (pReverb->m_nRvbLpfFbk == 0) { - *pValue16 = 1000; - LOGV("get REVERB_PARAM_DECAY_HF_RATIO, pReverb->m_nRvbLpfFbk == 0, ratio %d", *pValue16); - } else { - temp = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFbk); - temp2 = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nCosWT_5KHz) + if (param == REVERB_PARAM_ROOM_LEVEL) { + break; + } + pValue16 = &pProperties->roomHFLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_ROOM_HF_LEVEL: + // The ratio between linear gain at 0Hz and at 5000Hz for the room low pass is: + // (1 + a1) / sqrt(a1^2 + 2*C*a1 + 1) where: + // - a1 is minus the LP feedback gain: -pReverb->m_nRoomLpfFbk + // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz + + temp = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFbk); + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 %d", temp); + temp2 = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nCosWT_5KHz) << 1; + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, 2 Cos a1 %d", temp2); temp = 32767 + temp - temp2; + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 + 2 Cos a1 + 1 %d", temp); temp = Effects_Sqrt(temp) * 181; - temp = (pReverb->m_nRvbLpfFwd << 15) / temp; - // The linear gain at 0Hz is b0 / (a1 + 1) - temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767 - - pReverb->m_nRvbLpfFbk); + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, SQRT(a1^2 + 2 Cos a1 + 1) %d", temp); + temp = ((32767 - pReverb->m_nRoomLpfFbk) << 15) / temp; + + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); + + *pValue16 = Effects_Linear16ToMillibels(temp); + + if (param == REVERB_PARAM_ROOM_HF_LEVEL) { + break; + } + pValue32 = &pProperties->decayTime; + /* FALL THROUGH */ + case REVERB_PARAM_DECAY_TIME: + // Calculate reverb feedback path gain + temp = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk); temp = Effects_Linear16ToMillibels(temp); - temp2 = Effects_Linear16ToMillibels(temp2); - LOGV("get REVERB_PARAM_DECAY_HF_RATIO, gain 5KHz %d mB, gain DC %d mB", temp, temp2); - if (temp == 0) - temp = 1; - temp = (int16_t) ((1000 * temp2) / temp); - if (temp > 1000) - temp = 1000; + // Calculate decay time: g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time + temp = (-6000 * pReverb->m_nLateDelay) / temp; - *pValue16 = temp; - LOGV("get REVERB_PARAM_DECAY_HF_RATIO, ratio %d", *pValue16); - } + // Convert samples to ms + *pValue32 = (temp * 1000) / pReverb->m_nSamplingRate; - if (param == REVERB_PARAM_DECAY_HF_RATIO) { - break; - } - pValue16 = &pProperties->reflectionsLevel; - /* FALL THROUGH */ + LOGV("get REVERB_PARAM_DECAY_TIME, samples %d, ms %d", temp, *pValue32); - case REVERB_PARAM_REFLECTIONS_LEVEL: - *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nEarlyGain); + if (param == REVERB_PARAM_DECAY_TIME) { + break; + } + pValue16 = &pProperties->decayHFRatio; + /* FALL THROUGH */ + + case REVERB_PARAM_DECAY_HF_RATIO: + // If r is the decay HF ratio (r = REVERB_PARAM_DECAY_HF_RATIO/1000) we have: + // DT_5000Hz = DT_0Hz * r + // and G_5000Hz = -6000 * d / DT_5000Hz and G_0Hz = -6000 * d / DT_0Hz in millibels so : + // r = G_0Hz/G_5000Hz in millibels + // The linear gain at 5000Hz is b0 / sqrt(a1^2 + 2*C*a1 + 1) where: + // - a1 is minus the LP feedback gain: -pReverb->m_nRvbLpfFbk + // - b0 is the LP forward gain: pReverb->m_nRvbLpfFwd + // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz + if (pReverb->m_nRvbLpfFbk == 0) { + *pValue16 = 1000; + LOGV("get REVERB_PARAM_DECAY_HF_RATIO, pReverb->m_nRvbLpfFbk == 0, ratio %d", *pValue16); + } else { + temp = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFbk); + temp2 = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nCosWT_5KHz) + << 1; + temp = 32767 + temp - temp2; + temp = Effects_Sqrt(temp) * 181; + temp = (pReverb->m_nRvbLpfFwd << 15) / temp; + // The linear gain at 0Hz is b0 / (a1 + 1) + temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767 + - pReverb->m_nRvbLpfFbk); + + temp = Effects_Linear16ToMillibels(temp); + temp2 = Effects_Linear16ToMillibels(temp2); + LOGV("get REVERB_PARAM_DECAY_HF_RATIO, gain 5KHz %d mB, gain DC %d mB", temp, temp2); + + if (temp == 0) + temp = 1; + temp = (int16_t) ((1000 * temp2) / temp); + if (temp > 1000) + temp = 1000; + + *pValue16 = temp; + LOGV("get REVERB_PARAM_DECAY_HF_RATIO, ratio %d", *pValue16); + } - LOGV("get REVERB_PARAM_REFLECTIONS_LEVEL, %d", *pValue16); - if (param == REVERB_PARAM_REFLECTIONS_LEVEL) { - break; - } - pValue32 = &pProperties->reflectionsDelay; - /* FALL THROUGH */ + if (param == REVERB_PARAM_DECAY_HF_RATIO) { + break; + } + pValue16 = &pProperties->reflectionsLevel; + /* FALL THROUGH */ - case REVERB_PARAM_REFLECTIONS_DELAY: - // convert samples to ms - *pValue32 = (pReverb->m_nEarlyDelay * 1000) / pReverb->m_nSamplingRate; + case REVERB_PARAM_REFLECTIONS_LEVEL: + *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nEarlyGain); - LOGV("get REVERB_PARAM_REFLECTIONS_DELAY, samples %d, ms %d", pReverb->m_nEarlyDelay, *pValue32); + LOGV("get REVERB_PARAM_REFLECTIONS_LEVEL, %d", *pValue16); + if (param == REVERB_PARAM_REFLECTIONS_LEVEL) { + break; + } + pValue32 = &pProperties->reflectionsDelay; + /* FALL THROUGH */ - if (param == REVERB_PARAM_REFLECTIONS_DELAY) { - break; - } - pValue16 = &pProperties->reverbLevel; - /* FALL THROUGH */ + case REVERB_PARAM_REFLECTIONS_DELAY: + // convert samples to ms + *pValue32 = (pReverb->m_nEarlyDelay * 1000) / pReverb->m_nSamplingRate; - case REVERB_PARAM_REVERB_LEVEL: - // Convert linear gain to millibels - *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nLateGain << 2); + LOGV("get REVERB_PARAM_REFLECTIONS_DELAY, samples %d, ms %d", pReverb->m_nEarlyDelay, *pValue32); - LOGV("get REVERB_PARAM_REVERB_LEVEL %d", *pValue16); + if (param == REVERB_PARAM_REFLECTIONS_DELAY) { + break; + } + pValue16 = &pProperties->reverbLevel; + /* FALL THROUGH */ - if (param == REVERB_PARAM_REVERB_LEVEL) { - break; - } - pValue32 = &pProperties->reverbDelay; - /* FALL THROUGH */ + case REVERB_PARAM_REVERB_LEVEL: + // Convert linear gain to millibels + *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nLateGain << 2); - case REVERB_PARAM_REVERB_DELAY: - // convert samples to ms - *pValue32 = (pReverb->m_nLateDelay * 1000) / pReverb->m_nSamplingRate; + LOGV("get REVERB_PARAM_REVERB_LEVEL %d", *pValue16); - LOGV("get REVERB_PARAM_REVERB_DELAY, samples %d, ms %d", pReverb->m_nLateDelay, *pValue32); + if (param == REVERB_PARAM_REVERB_LEVEL) { + break; + } + pValue32 = &pProperties->reverbDelay; + /* FALL THROUGH */ - if (param == REVERB_PARAM_REVERB_DELAY) { - break; - } - pValue16 = &pProperties->diffusion; - /* FALL THROUGH */ + case REVERB_PARAM_REVERB_DELAY: + // convert samples to ms + *pValue32 = (pReverb->m_nLateDelay * 1000) / pReverb->m_nSamplingRate; - case REVERB_PARAM_DIFFUSION: - temp = (int16_t) ((1000 * (pReverb->m_sAp0.m_nApGain - AP0_GAIN_BASE)) - / AP0_GAIN_RANGE); + LOGV("get REVERB_PARAM_REVERB_DELAY, samples %d, ms %d", pReverb->m_nLateDelay, *pValue32); - if (temp < 0) - temp = 0; - if (temp > 1000) - temp = 1000; + if (param == REVERB_PARAM_REVERB_DELAY) { + break; + } + pValue16 = &pProperties->diffusion; + /* FALL THROUGH */ - *pValue16 = temp; - LOGV("get REVERB_PARAM_DIFFUSION, %d, AP0 gain %d", *pValue16, pReverb->m_sAp0.m_nApGain); + case REVERB_PARAM_DIFFUSION: + temp = (int16_t) ((1000 * (pReverb->m_sAp0.m_nApGain - AP0_GAIN_BASE)) + / AP0_GAIN_RANGE); - if (param == REVERB_PARAM_DIFFUSION) { - break; - } - pValue16 = &pProperties->density; - /* FALL THROUGH */ + if (temp < 0) + temp = 0; + if (temp > 1000) + temp = 1000; - case REVERB_PARAM_DENSITY: - // Calculate AP delay in time units - temp = ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn) << 16) - / pReverb->m_nSamplingRate; + *pValue16 = temp; + LOGV("get REVERB_PARAM_DIFFUSION, %d, AP0 gain %d", *pValue16, pReverb->m_sAp0.m_nApGain); - temp = (int16_t) ((1000 * (temp - AP0_TIME_BASE)) / AP0_TIME_RANGE); + if (param == REVERB_PARAM_DIFFUSION) { + break; + } + pValue16 = &pProperties->density; + /* FALL THROUGH */ - if (temp < 0) - temp = 0; - if (temp > 1000) - temp = 1000; + case REVERB_PARAM_DENSITY: + // Calculate AP delay in time units + temp = ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn) << 16) + / pReverb->m_nSamplingRate; - *pValue16 = temp; + temp = (int16_t) ((1000 * (temp - AP0_TIME_BASE)) / AP0_TIME_RANGE); - LOGV("get REVERB_PARAM_DENSITY, %d, AP0 delay smps %d", *pValue16, pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn); - break; + if (temp < 0) + temp = 0; + if (temp > 1000) + temp = 1000; - default: - break; + *pValue16 = temp; + + LOGV("get REVERB_PARAM_DENSITY, %d, AP0 delay smps %d", *pValue16, pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn); + break; + + default: + break; + } } + *pSize = size; + LOGV("Reverb_getParameter, context %p, param %d, value %d", pReverb, param, *(int *)pValue); @@ -945,382 +951,386 @@ int Reverb_setParameter(reverb_object_t *pReverb, int32_t param, size_t size, LOGV("Reverb_setParameter, context %p, param %d, value16 %d, value32 %d", pReverb, param, *(int16_t *)pValue, *(int32_t *)pValue); - if (pReverb->m_Preset && param != REVERB_PARAM_PRESET) { - return -EINVAL; - } - if (!pReverb->m_Preset && param == REVERB_PARAM_PRESET) { - return -EINVAL; - } - - switch (param) { - case REVERB_PARAM_ROOM_LEVEL: - case REVERB_PARAM_ROOM_HF_LEVEL: - case REVERB_PARAM_DECAY_HF_RATIO: - case REVERB_PARAM_REFLECTIONS_LEVEL: - case REVERB_PARAM_REVERB_LEVEL: - case REVERB_PARAM_DIFFUSION: - case REVERB_PARAM_DENSITY: - paramSize = sizeof(int16_t); - break; - - case REVERB_PARAM_BYPASS: - case REVERB_PARAM_PRESET: - case REVERB_PARAM_DECAY_TIME: - case REVERB_PARAM_REFLECTIONS_DELAY: - case REVERB_PARAM_REVERB_DELAY: - paramSize = sizeof(int32_t); - break; - - case REVERB_PARAM_PROPERTIES: - paramSize = sizeof(t_reverb_properties); - break; - - default: - return -EINVAL; - } - - if (size != paramSize) { - return -EINVAL; - } - - if (paramSize == sizeof(int16_t)) { - value16 = *(int16_t *) pValue; - } else if (paramSize == sizeof(int32_t)) { - value32 = *(int32_t *) pValue; - } else { - pProperties = (t_reverb_properties *) pValue; - } - - pPreset = &pReverb->m_sPreset.m_sPreset[pReverb->m_nCurrentRoom]; - - switch (param) { - case REVERB_PARAM_BYPASS: - pReverb->m_bBypass = (uint16_t)value32; - break; - case REVERB_PARAM_PRESET: - if (value32 != REVERB_PRESET_LARGE_HALL && value32 - != REVERB_PRESET_HALL && value32 != REVERB_PRESET_CHAMBER - && value32 != REVERB_PRESET_ROOM) + if (pReverb->m_Preset) { + if (param != REVERB_PARAM_PRESET || size != sizeof(int16_t)) { return -EINVAL; - pReverb->m_nNextRoom = (int16_t) value32; - break; - - case REVERB_PARAM_PROPERTIES: - value16 = pProperties->roomLevel; - /* FALL THROUGH */ - - case REVERB_PARAM_ROOM_LEVEL: - // Convert millibels to linear 16 bit signed => m_nRoomLpfFwd - if (value16 > 0) + } + value16 = *(int16_t *)pValue; + LOGV("set REVERB_PARAM_PRESET, preset %d", value16); + if (value16 < REVERB_PRESET_NONE || value16 > REVERB_PRESET_PLATE) { return -EINVAL; + } + // REVERB_PRESET_NONE is mapped to bypass + if (value16 == REVERB_PRESET_NONE) { + pReverb->m_bBypass = 1; + } else { + pReverb->m_bBypass = 0; + pReverb->m_nNextRoom = value16 - 1; + } + } else { + switch (param) { + case REVERB_PARAM_ROOM_LEVEL: + case REVERB_PARAM_ROOM_HF_LEVEL: + case REVERB_PARAM_DECAY_HF_RATIO: + case REVERB_PARAM_REFLECTIONS_LEVEL: + case REVERB_PARAM_REVERB_LEVEL: + case REVERB_PARAM_DIFFUSION: + case REVERB_PARAM_DENSITY: + paramSize = sizeof(int16_t); + break; - temp = Effects_MillibelsToLinear16(value16); - - pReverb->m_nRoomLpfFwd - = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRoomLpfFbk)); - - LOGV("REVERB_PARAM_ROOM_LEVEL, gain %d, new m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); - if (param == REVERB_PARAM_ROOM_LEVEL) + case REVERB_PARAM_BYPASS: + case REVERB_PARAM_DECAY_TIME: + case REVERB_PARAM_REFLECTIONS_DELAY: + case REVERB_PARAM_REVERB_DELAY: + paramSize = sizeof(int32_t); break; - value16 = pProperties->roomHFLevel; - /* FALL THROUGH */ - case REVERB_PARAM_ROOM_HF_LEVEL: + case REVERB_PARAM_PROPERTIES: + paramSize = sizeof(t_reverb_properties); + break; - // Limit to 0 , -40dB range because of low pass implementation - if (value16 > 0 || value16 < -4000) + default: return -EINVAL; - // Convert attenuation @ 5000H expressed in millibels to => m_nRoomLpfFbk - // m_nRoomLpfFbk is -a1 where a1 is the solution of: - // a1^2 + 2*(C-dG^2)/(1-dG^2)*a1 + 1 = 0 where: - // - C is cos(2*pi*5000/Fs) (pReverb->m_nCosWT_5KHz) - // - dG is G0/Gf (G0 is the linear gain at DC and Gf is the wanted gain at 5000Hz) - - // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged - // while changing HF level - temp2 = (pReverb->m_nRoomLpfFwd << 15) / (32767 - - pReverb->m_nRoomLpfFbk); - if (value16 == 0) { - pReverb->m_nRoomLpfFbk = 0; - } else { - int32_t dG2, b, delta; - - // dG^2 - temp = Effects_MillibelsToLinear16(value16); - LOGV("REVERB_PARAM_ROOM_HF_LEVEL, HF gain %d", temp); - temp = (1 << 30) / temp; - LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain %d", temp); - dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15); - LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain ^ 2 %d", dG2); - // b = 2*(C-dG^2)/(1-dG^2) - b = (int32_t) ((((int64_t) 1 << (15 + 1)) - * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2)) - / ((int64_t) 32767 - (int64_t) dG2)); - - // delta = b^2 - 4 - delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15 - + 2))); - - LOGV_IF(delta > (1<<30), " delta overflow %d", delta); - - LOGV("REVERB_PARAM_ROOM_HF_LEVEL, dG2 %d, b %d, delta %d, m_nCosWT_5KHz %d", dG2, b, delta, pReverb->m_nCosWT_5KHz); - // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2 - pReverb->m_nRoomLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1; } - LOGV("REVERB_PARAM_ROOM_HF_LEVEL, olg DC gain %d new m_nRoomLpfFbk %d, old m_nRoomLpfFwd %d", - temp2, pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFwd); - pReverb->m_nRoomLpfFwd - = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRoomLpfFbk)); - LOGV("REVERB_PARAM_ROOM_HF_LEVEL, new m_nRoomLpfFwd %d", pReverb->m_nRoomLpfFwd); - - if (param == REVERB_PARAM_ROOM_HF_LEVEL) - break; - value32 = pProperties->decayTime; - /* FALL THROUGH */ - - case REVERB_PARAM_DECAY_TIME: - - // Convert milliseconds to => m_nRvbLpfFwd (function of m_nRvbLpfFbk) - // convert ms to samples - value32 = (value32 * pReverb->m_nSamplingRate) / 1000; - - // calculate valid decay time range as a function of current reverb delay and - // max feed back gain. Min value <=> -40dB in one pass, Max value <=> feedback gain = -1 dB - // Calculate attenuation for each round in late reverb given a total attenuation of -6000 millibels. - // g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time - averageDelay = pReverb->m_nLateDelay - pReverb->m_nMaxExcursion; - averageDelay += ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn) - + (pReverb->m_sAp1.m_zApOut - pReverb->m_sAp1.m_zApIn)) >> 1; - - temp = (-6000 * averageDelay) / value32; - LOGV("REVERB_PARAM_DECAY_TIME, delay smps %d, DT smps %d, gain mB %d",averageDelay, value32, temp); - if (temp < -4000 || temp > -100) + if (size != paramSize) { return -EINVAL; + } - // calculate low pass gain by adding reverb input attenuation (pReverb->m_nLateGain) and substrating output - // xfade and sum gain (max +9dB) - temp -= Effects_Linear16ToMillibels(pReverb->m_nLateGain) + 900; - temp = Effects_MillibelsToLinear16(temp); - - // DC gain (temp) = b0 / (1 + a1) = pReverb->m_nRvbLpfFwd / (32767 - pReverb->m_nRvbLpfFbk) - pReverb->m_nRvbLpfFwd - = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRvbLpfFbk)); + if (paramSize == sizeof(int16_t)) { + value16 = *(int16_t *) pValue; + } else if (paramSize == sizeof(int32_t)) { + value32 = *(int32_t *) pValue; + } else { + pProperties = (t_reverb_properties *) pValue; + } - LOGV("REVERB_PARAM_DECAY_TIME, gain %d, new m_nRvbLpfFwd %d, old m_nRvbLpfFbk %d, reverb gain %d", temp, pReverb->m_nRvbLpfFwd, pReverb->m_nRvbLpfFbk, Effects_Linear16ToMillibels(pReverb->m_nLateGain)); + pPreset = &pReverb->m_sPreset.m_sPreset[pReverb->m_nNextRoom]; - if (param == REVERB_PARAM_DECAY_TIME) + switch (param) { + case REVERB_PARAM_BYPASS: + pReverb->m_bBypass = (uint16_t)value32; break; - value16 = pProperties->decayHFRatio; - /* FALL THROUGH */ - case REVERB_PARAM_DECAY_HF_RATIO: + case REVERB_PARAM_PROPERTIES: + value16 = pProperties->roomLevel; + /* FALL THROUGH */ - // We limit max value to 1000 because reverb filter is lowpass only - if (value16 < 100 || value16 > 1000) - return -EINVAL; - // Convert per mille to => m_nLpfFwd, m_nLpfFbk - - // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged - // while changing HF level - temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk); + case REVERB_PARAM_ROOM_LEVEL: + // Convert millibels to linear 16 bit signed => m_nRoomLpfFwd + if (value16 > 0) + return -EINVAL; - if (value16 == 1000) { - pReverb->m_nRvbLpfFbk = 0; - } else { - int32_t dG2, b, delta; + temp = Effects_MillibelsToLinear16(value16); - temp = Effects_Linear16ToMillibels(temp2); - // G_5000Hz = G_DC * (1000/REVERB_PARAM_DECAY_HF_RATIO) in millibels + pReverb->m_nRoomLpfFwd + = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRoomLpfFbk)); + + LOGV("REVERB_PARAM_ROOM_LEVEL, gain %d, new m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); + if (param == REVERB_PARAM_ROOM_LEVEL) + break; + value16 = pProperties->roomHFLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_ROOM_HF_LEVEL: + + // Limit to 0 , -40dB range because of low pass implementation + if (value16 > 0 || value16 < -4000) + return -EINVAL; + // Convert attenuation @ 5000H expressed in millibels to => m_nRoomLpfFbk + // m_nRoomLpfFbk is -a1 where a1 is the solution of: + // a1^2 + 2*(C-dG^2)/(1-dG^2)*a1 + 1 = 0 where: + // - C is cos(2*pi*5000/Fs) (pReverb->m_nCosWT_5KHz) + // - dG is G0/Gf (G0 is the linear gain at DC and Gf is the wanted gain at 5000Hz) + + // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged + // while changing HF level + temp2 = (pReverb->m_nRoomLpfFwd << 15) / (32767 + - pReverb->m_nRoomLpfFbk); + if (value16 == 0) { + pReverb->m_nRoomLpfFbk = 0; + } else { + int32_t dG2, b, delta; + + // dG^2 + temp = Effects_MillibelsToLinear16(value16); + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, HF gain %d", temp); + temp = (1 << 30) / temp; + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain %d", temp); + dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15); + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain ^ 2 %d", dG2); + // b = 2*(C-dG^2)/(1-dG^2) + b = (int32_t) ((((int64_t) 1 << (15 + 1)) + * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2)) + / ((int64_t) 32767 - (int64_t) dG2)); + + // delta = b^2 - 4 + delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15 + + 2))); + + LOGV_IF(delta > (1<<30), " delta overflow %d", delta); + + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, dG2 %d, b %d, delta %d, m_nCosWT_5KHz %d", dG2, b, delta, pReverb->m_nCosWT_5KHz); + // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2 + pReverb->m_nRoomLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1; + } + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, olg DC gain %d new m_nRoomLpfFbk %d, old m_nRoomLpfFwd %d", + temp2, pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFwd); + + pReverb->m_nRoomLpfFwd + = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRoomLpfFbk)); + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, new m_nRoomLpfFwd %d", pReverb->m_nRoomLpfFwd); + + if (param == REVERB_PARAM_ROOM_HF_LEVEL) + break; + value32 = pProperties->decayTime; + /* FALL THROUGH */ + + case REVERB_PARAM_DECAY_TIME: + + // Convert milliseconds to => m_nRvbLpfFwd (function of m_nRvbLpfFbk) + // convert ms to samples + value32 = (value32 * pReverb->m_nSamplingRate) / 1000; + + // calculate valid decay time range as a function of current reverb delay and + // max feed back gain. Min value <=> -40dB in one pass, Max value <=> feedback gain = -1 dB + // Calculate attenuation for each round in late reverb given a total attenuation of -6000 millibels. + // g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time + averageDelay = pReverb->m_nLateDelay - pReverb->m_nMaxExcursion; + averageDelay += ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn) + + (pReverb->m_sAp1.m_zApOut - pReverb->m_sAp1.m_zApIn)) >> 1; + + temp = (-6000 * averageDelay) / value32; + LOGV("REVERB_PARAM_DECAY_TIME, delay smps %d, DT smps %d, gain mB %d",averageDelay, value32, temp); + if (temp < -4000 || temp > -100) + return -EINVAL; + + // calculate low pass gain by adding reverb input attenuation (pReverb->m_nLateGain) and substrating output + // xfade and sum gain (max +9dB) + temp -= Effects_Linear16ToMillibels(pReverb->m_nLateGain) + 900; + temp = Effects_MillibelsToLinear16(temp); - value32 = ((int32_t) 1000 << 15) / (int32_t) value16; - LOGV("REVERB_PARAM_DECAY_HF_RATIO, DC gain %d, DC gain mB %d, 1000/R %d", temp2, temp, value32); + // DC gain (temp) = b0 / (1 + a1) = pReverb->m_nRvbLpfFwd / (32767 - pReverb->m_nRvbLpfFbk) + pReverb->m_nRvbLpfFwd + = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRvbLpfFbk)); - temp = (int32_t) (((int64_t) temp * (int64_t) value32) >> 15); + LOGV("REVERB_PARAM_DECAY_TIME, gain %d, new m_nRvbLpfFwd %d, old m_nRvbLpfFbk %d, reverb gain %d", temp, pReverb->m_nRvbLpfFwd, pReverb->m_nRvbLpfFbk, Effects_Linear16ToMillibels(pReverb->m_nLateGain)); - if (temp < -4000) { - LOGV("REVERB_PARAM_DECAY_HF_RATIO HF gain overflow %d mB", temp); - temp = -4000; - } + if (param == REVERB_PARAM_DECAY_TIME) + break; + value16 = pProperties->decayHFRatio; + /* FALL THROUGH */ - temp = Effects_MillibelsToLinear16(temp); - LOGV("REVERB_PARAM_DECAY_HF_RATIO, HF gain %d", temp); - // dG^2 - temp = (temp2 << 15) / temp; - dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15); + case REVERB_PARAM_DECAY_HF_RATIO: - // b = 2*(C-dG^2)/(1-dG^2) - b = (int32_t) ((((int64_t) 1 << (15 + 1)) - * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2)) - / ((int64_t) 32767 - (int64_t) dG2)); + // We limit max value to 1000 because reverb filter is lowpass only + if (value16 < 100 || value16 > 1000) + return -EINVAL; + // Convert per mille to => m_nLpfFwd, m_nLpfFbk - // delta = b^2 - 4 - delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15 - + 2))); + // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged + // while changing HF level + temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk); - // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2 - pReverb->m_nRvbLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1; + if (value16 == 1000) { + pReverb->m_nRvbLpfFbk = 0; + } else { + int32_t dG2, b, delta; - LOGV("REVERB_PARAM_DECAY_HF_RATIO, dG2 %d, b %d, delta %d", dG2, b, delta); + temp = Effects_Linear16ToMillibels(temp2); + // G_5000Hz = G_DC * (1000/REVERB_PARAM_DECAY_HF_RATIO) in millibels - } + value32 = ((int32_t) 1000 << 15) / (int32_t) value16; + LOGV("REVERB_PARAM_DECAY_HF_RATIO, DC gain %d, DC gain mB %d, 1000/R %d", temp2, temp, value32); - LOGV("REVERB_PARAM_DECAY_HF_RATIO, gain %d, m_nRvbLpfFbk %d, m_nRvbLpfFwd %d", temp2, pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFwd); + temp = (int32_t) (((int64_t) temp * (int64_t) value32) >> 15); - pReverb->m_nRvbLpfFwd - = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRvbLpfFbk)); + if (temp < -4000) { + LOGV("REVERB_PARAM_DECAY_HF_RATIO HF gain overflow %d mB", temp); + temp = -4000; + } - if (param == REVERB_PARAM_DECAY_HF_RATIO) - break; - value16 = pProperties->reflectionsLevel; - /* FALL THROUGH */ + temp = Effects_MillibelsToLinear16(temp); + LOGV("REVERB_PARAM_DECAY_HF_RATIO, HF gain %d", temp); + // dG^2 + temp = (temp2 << 15) / temp; + dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15); - case REVERB_PARAM_REFLECTIONS_LEVEL: - // We limit max value to 0 because gain is limited to 0dB - if (value16 > 0 || value16 < -6000) - return -EINVAL; + // b = 2*(C-dG^2)/(1-dG^2) + b = (int32_t) ((((int64_t) 1 << (15 + 1)) + * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2)) + / ((int64_t) 32767 - (int64_t) dG2)); - // Convert millibels to linear 16 bit signed and recompute m_sEarlyL.m_nGain[i] and m_sEarlyR.m_nGain[i]. - value16 = Effects_MillibelsToLinear16(value16); - for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { - pReverb->m_sEarlyL.m_nGain[i] - = MULT_EG1_EG1(pPreset->m_sEarlyL.m_nGain[i],value16); - pReverb->m_sEarlyR.m_nGain[i] - = MULT_EG1_EG1(pPreset->m_sEarlyR.m_nGain[i],value16); - } - pReverb->m_nEarlyGain = value16; - LOGV("REVERB_PARAM_REFLECTIONS_LEVEL, m_nEarlyGain %d", pReverb->m_nEarlyGain); + // delta = b^2 - 4 + delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15 + + 2))); - if (param == REVERB_PARAM_REFLECTIONS_LEVEL) - break; - value32 = pProperties->reflectionsDelay; - /* FALL THROUGH */ - - case REVERB_PARAM_REFLECTIONS_DELAY: - // We limit max value MAX_EARLY_TIME - // convert ms to time units - temp = (value32 * 65536) / 1000; - if (temp < 0 || temp > MAX_EARLY_TIME) - return -EINVAL; + // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2 + pReverb->m_nRvbLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1; - maxSamples = (int32_t) (MAX_EARLY_TIME * pReverb->m_nSamplingRate) - >> 16; - temp = (temp * pReverb->m_nSamplingRate) >> 16; - for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { - temp2 = temp + (((int32_t) pPreset->m_sEarlyL.m_zDelay[i] - * pReverb->m_nSamplingRate) >> 16); - if (temp2 > maxSamples) - temp2 = maxSamples; - pReverb->m_sEarlyL.m_zDelay[i] = pReverb->m_nEarly0in + temp2; - temp2 = temp + (((int32_t) pPreset->m_sEarlyR.m_zDelay[i] - * pReverb->m_nSamplingRate) >> 16); - if (temp2 > maxSamples) - temp2 = maxSamples; - pReverb->m_sEarlyR.m_zDelay[i] = pReverb->m_nEarly1in + temp2; - } - pReverb->m_nEarlyDelay = temp; + LOGV("REVERB_PARAM_DECAY_HF_RATIO, dG2 %d, b %d, delta %d", dG2, b, delta); - LOGV("REVERB_PARAM_REFLECTIONS_DELAY, m_nEarlyDelay smps %d max smp delay %d", pReverb->m_nEarlyDelay, maxSamples); + } - // Convert milliseconds to sample count => m_nEarlyDelay - if (param == REVERB_PARAM_REFLECTIONS_DELAY) - break; - value16 = pProperties->reverbLevel; - /* FALL THROUGH */ + LOGV("REVERB_PARAM_DECAY_HF_RATIO, gain %d, m_nRvbLpfFbk %d, m_nRvbLpfFwd %d", temp2, pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFwd); - case REVERB_PARAM_REVERB_LEVEL: - // We limit max value to 0 because gain is limited to 0dB - if (value16 > 0 || value16 < -6000) - return -EINVAL; - // Convert millibels to linear 16 bits (gange 0 - 8191) => m_nLateGain. - pReverb->m_nLateGain = Effects_MillibelsToLinear16(value16) >> 2; + pReverb->m_nRvbLpfFwd + = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRvbLpfFbk)); - LOGV("REVERB_PARAM_REVERB_LEVEL, m_nLateGain %d", pReverb->m_nLateGain); + if (param == REVERB_PARAM_DECAY_HF_RATIO) + break; + value16 = pProperties->reflectionsLevel; + /* FALL THROUGH */ - if (param == REVERB_PARAM_REVERB_LEVEL) - break; - value32 = pProperties->reverbDelay; - /* FALL THROUGH */ - - case REVERB_PARAM_REVERB_DELAY: - // We limit max value to MAX_DELAY_TIME - // convert ms to time units - temp = (value32 * 65536) / 1000; - if (temp < 0 || temp > MAX_DELAY_TIME) - return -EINVAL; + case REVERB_PARAM_REFLECTIONS_LEVEL: + // We limit max value to 0 because gain is limited to 0dB + if (value16 > 0 || value16 < -6000) + return -EINVAL; - maxSamples = (int32_t) (MAX_DELAY_TIME * pReverb->m_nSamplingRate) - >> 16; - temp = (temp * pReverb->m_nSamplingRate) >> 16; - if ((temp + pReverb->m_nMaxExcursion) > maxSamples) { - temp = maxSamples - pReverb->m_nMaxExcursion; - } - if (temp < pReverb->m_nMaxExcursion) { - temp = pReverb->m_nMaxExcursion; - } + // Convert millibels to linear 16 bit signed and recompute m_sEarlyL.m_nGain[i] and m_sEarlyR.m_nGain[i]. + value16 = Effects_MillibelsToLinear16(value16); + for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { + pReverb->m_sEarlyL.m_nGain[i] + = MULT_EG1_EG1(pPreset->m_sEarlyL.m_nGain[i],value16); + pReverb->m_sEarlyR.m_nGain[i] + = MULT_EG1_EG1(pPreset->m_sEarlyR.m_nGain[i],value16); + } + pReverb->m_nEarlyGain = value16; + LOGV("REVERB_PARAM_REFLECTIONS_LEVEL, m_nEarlyGain %d", pReverb->m_nEarlyGain); + + if (param == REVERB_PARAM_REFLECTIONS_LEVEL) + break; + value32 = pProperties->reflectionsDelay; + /* FALL THROUGH */ + + case REVERB_PARAM_REFLECTIONS_DELAY: + // We limit max value MAX_EARLY_TIME + // convert ms to time units + temp = (value32 * 65536) / 1000; + if (temp < 0 || temp > MAX_EARLY_TIME) + return -EINVAL; + + maxSamples = (int32_t) (MAX_EARLY_TIME * pReverb->m_nSamplingRate) + >> 16; + temp = (temp * pReverb->m_nSamplingRate) >> 16; + for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { + temp2 = temp + (((int32_t) pPreset->m_sEarlyL.m_zDelay[i] + * pReverb->m_nSamplingRate) >> 16); + if (temp2 > maxSamples) + temp2 = maxSamples; + pReverb->m_sEarlyL.m_zDelay[i] = pReverb->m_nEarly0in + temp2; + temp2 = temp + (((int32_t) pPreset->m_sEarlyR.m_zDelay[i] + * pReverb->m_nSamplingRate) >> 16); + if (temp2 > maxSamples) + temp2 = maxSamples; + pReverb->m_sEarlyR.m_zDelay[i] = pReverb->m_nEarly1in + temp2; + } + pReverb->m_nEarlyDelay = temp; + + LOGV("REVERB_PARAM_REFLECTIONS_DELAY, m_nEarlyDelay smps %d max smp delay %d", pReverb->m_nEarlyDelay, maxSamples); + + // Convert milliseconds to sample count => m_nEarlyDelay + if (param == REVERB_PARAM_REFLECTIONS_DELAY) + break; + value16 = pProperties->reverbLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_REVERB_LEVEL: + // We limit max value to 0 because gain is limited to 0dB + if (value16 > 0 || value16 < -6000) + return -EINVAL; + // Convert millibels to linear 16 bits (gange 0 - 8191) => m_nLateGain. + pReverb->m_nLateGain = Effects_MillibelsToLinear16(value16) >> 2; + + LOGV("REVERB_PARAM_REVERB_LEVEL, m_nLateGain %d", pReverb->m_nLateGain); + + if (param == REVERB_PARAM_REVERB_LEVEL) + break; + value32 = pProperties->reverbDelay; + /* FALL THROUGH */ + + case REVERB_PARAM_REVERB_DELAY: + // We limit max value to MAX_DELAY_TIME + // convert ms to time units + temp = (value32 * 65536) / 1000; + if (temp < 0 || temp > MAX_DELAY_TIME) + return -EINVAL; + + maxSamples = (int32_t) (MAX_DELAY_TIME * pReverb->m_nSamplingRate) + >> 16; + temp = (temp * pReverb->m_nSamplingRate) >> 16; + if ((temp + pReverb->m_nMaxExcursion) > maxSamples) { + temp = maxSamples - pReverb->m_nMaxExcursion; + } + if (temp < pReverb->m_nMaxExcursion) { + temp = pReverb->m_nMaxExcursion; + } - temp -= pReverb->m_nLateDelay; - pReverb->m_nDelay0Out += temp; - pReverb->m_nDelay1Out += temp; - pReverb->m_nLateDelay += temp; + temp -= pReverb->m_nLateDelay; + pReverb->m_nDelay0Out += temp; + pReverb->m_nDelay1Out += temp; + pReverb->m_nLateDelay += temp; - LOGV("REVERB_PARAM_REVERB_DELAY, m_nLateDelay smps %d max smp delay %d", pReverb->m_nLateDelay, maxSamples); + LOGV("REVERB_PARAM_REVERB_DELAY, m_nLateDelay smps %d max smp delay %d", pReverb->m_nLateDelay, maxSamples); - // Convert milliseconds to sample count => m_nDelay1Out + m_nMaxExcursion - if (param == REVERB_PARAM_REVERB_DELAY) - break; + // Convert milliseconds to sample count => m_nDelay1Out + m_nMaxExcursion + if (param == REVERB_PARAM_REVERB_DELAY) + break; - value16 = pProperties->diffusion; - /* FALL THROUGH */ + value16 = pProperties->diffusion; + /* FALL THROUGH */ - case REVERB_PARAM_DIFFUSION: - if (value16 < 0 || value16 > 1000) - return -EINVAL; + case REVERB_PARAM_DIFFUSION: + if (value16 < 0 || value16 > 1000) + return -EINVAL; - // Convert per mille to m_sAp0.m_nApGain, m_sAp1.m_nApGain - pReverb->m_sAp0.m_nApGain = AP0_GAIN_BASE + ((int32_t) value16 - * AP0_GAIN_RANGE) / 1000; - pReverb->m_sAp1.m_nApGain = AP1_GAIN_BASE + ((int32_t) value16 - * AP1_GAIN_RANGE) / 1000; + // Convert per mille to m_sAp0.m_nApGain, m_sAp1.m_nApGain + pReverb->m_sAp0.m_nApGain = AP0_GAIN_BASE + ((int32_t) value16 + * AP0_GAIN_RANGE) / 1000; + pReverb->m_sAp1.m_nApGain = AP1_GAIN_BASE + ((int32_t) value16 + * AP1_GAIN_RANGE) / 1000; - LOGV("REVERB_PARAM_DIFFUSION, m_sAp0.m_nApGain %d m_sAp1.m_nApGain %d", pReverb->m_sAp0.m_nApGain, pReverb->m_sAp1.m_nApGain); + LOGV("REVERB_PARAM_DIFFUSION, m_sAp0.m_nApGain %d m_sAp1.m_nApGain %d", pReverb->m_sAp0.m_nApGain, pReverb->m_sAp1.m_nApGain); - if (param == REVERB_PARAM_DIFFUSION) - break; + if (param == REVERB_PARAM_DIFFUSION) + break; - value16 = pProperties->density; - /* FALL THROUGH */ + value16 = pProperties->density; + /* FALL THROUGH */ - case REVERB_PARAM_DENSITY: - if (value16 < 0 || value16 > 1000) - return -EINVAL; + case REVERB_PARAM_DENSITY: + if (value16 < 0 || value16 > 1000) + return -EINVAL; - // Convert per mille to m_sAp0.m_zApOut, m_sAp1.m_zApOut - maxSamples = (int32_t) (MAX_AP_TIME * pReverb->m_nSamplingRate) >> 16; + // Convert per mille to m_sAp0.m_zApOut, m_sAp1.m_zApOut + maxSamples = (int32_t) (MAX_AP_TIME * pReverb->m_nSamplingRate) >> 16; - temp = AP0_TIME_BASE + ((int32_t) value16 * AP0_TIME_RANGE) / 1000; - /*lint -e{702} shift for performance */ - temp = (temp * pReverb->m_nSamplingRate) >> 16; - if (temp > maxSamples) - temp = maxSamples; - pReverb->m_sAp0.m_zApOut = (uint16_t) (pReverb->m_sAp0.m_zApIn + temp); + temp = AP0_TIME_BASE + ((int32_t) value16 * AP0_TIME_RANGE) / 1000; + /*lint -e{702} shift for performance */ + temp = (temp * pReverb->m_nSamplingRate) >> 16; + if (temp > maxSamples) + temp = maxSamples; + pReverb->m_sAp0.m_zApOut = (uint16_t) (pReverb->m_sAp0.m_zApIn + temp); - LOGV("REVERB_PARAM_DENSITY, Ap0 delay smps %d", temp); + LOGV("REVERB_PARAM_DENSITY, Ap0 delay smps %d", temp); - temp = AP1_TIME_BASE + ((int32_t) value16 * AP1_TIME_RANGE) / 1000; - /*lint -e{702} shift for performance */ - temp = (temp * pReverb->m_nSamplingRate) >> 16; - if (temp > maxSamples) - temp = maxSamples; - pReverb->m_sAp1.m_zApOut = (uint16_t) (pReverb->m_sAp1.m_zApIn + temp); + temp = AP1_TIME_BASE + ((int32_t) value16 * AP1_TIME_RANGE) / 1000; + /*lint -e{702} shift for performance */ + temp = (temp * pReverb->m_nSamplingRate) >> 16; + if (temp > maxSamples) + temp = maxSamples; + pReverb->m_sAp1.m_zApOut = (uint16_t) (pReverb->m_sAp1.m_zApIn + temp); - LOGV("Ap1 delay smps %d", temp); + LOGV("Ap1 delay smps %d", temp); - break; + break; - default: - break; + default: + break; + } } + return 0; } /* end Reverb_setParameter */ @@ -1905,24 +1915,26 @@ static int ReverbUpdateRoom(reverb_object_t *pReverb, bool fullUpdate) { */ static int ReverbReadInPresets(reverb_object_t *pReverb) { - int preset = 0; - int defaultPreset = 0; + int preset; - //now init any remaining presets to defaults - for (defaultPreset = preset; defaultPreset < REVERB_MAX_ROOM_TYPE; defaultPreset++) { - reverb_preset_t *pPreset = &pReverb->m_sPreset.m_sPreset[defaultPreset]; - if (defaultPreset == 0 || defaultPreset > REVERB_MAX_ROOM_TYPE - 1) { - pPreset->m_nRvbLpfFbk = 8307; - pPreset->m_nRvbLpfFwd = 14768; + // this is for test only. OpenSL ES presets are mapped to 4 presets. + // REVERB_PRESET_NONE is mapped to bypass + for (preset = 0; preset < REVERB_NUM_PRESETS; preset++) { + reverb_preset_t *pPreset = &pReverb->m_sPreset.m_sPreset[preset]; + switch (preset + 1) { + case REVERB_PRESET_PLATE: + case REVERB_PRESET_SMALLROOM: + pPreset->m_nRvbLpfFbk = 5077; + pPreset->m_nRvbLpfFwd = 11076; pPreset->m_nEarlyGain = 27690; pPreset->m_nEarlyDelay = 1311; pPreset->m_nLateGain = 8191; pPreset->m_nLateDelay = 3932; pPreset->m_nRoomLpfFbk = 3692; - pPreset->m_nRoomLpfFwd = 24569; + pPreset->m_nRoomLpfFwd = 20474; pPreset->m_sEarlyL.m_zDelay[0] = 1376; pPreset->m_sEarlyL.m_nGain[0] = 22152; - pPreset->m_sEarlyL.m_zDelay[1] = 2163; + pPreset->m_sEarlyL.m_zDelay[1] = 1462; pPreset->m_sEarlyL.m_nGain[1] = 17537; pPreset->m_sEarlyL.m_zDelay[2] = 0; pPreset->m_sEarlyL.m_nGain[2] = 14768; @@ -1941,11 +1953,11 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_sEarlyR.m_zDelay[4] = 0; pPreset->m_sEarlyR.m_nGain[4] = 13384; pPreset->m_nMaxExcursion = 127; - pPreset->m_nXfadeInterval = 6388; - pPreset->m_nAp0_ApGain = 15691; - pPreset->m_nAp0_ApOut = 711; - pPreset->m_nAp1_ApGain = 16317; - pPreset->m_nAp1_ApOut = 1029; + pPreset->m_nXfadeInterval = 6470; //6483; + pPreset->m_nAp0_ApGain = 14768; + pPreset->m_nAp0_ApOut = 792; + pPreset->m_nAp1_ApGain = 14777; + pPreset->m_nAp1_ApOut = 1191; pPreset->m_rfu4 = 0; pPreset->m_rfu5 = 0; pPreset->m_rfu6 = 0; @@ -1953,15 +1965,17 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_rfu8 = 0; pPreset->m_rfu9 = 0; pPreset->m_rfu10 = 0; - } else if (defaultPreset == 1) { - pPreset->m_nRvbLpfFbk = 6461; - pPreset->m_nRvbLpfFwd = 14307; + break; + case REVERB_PRESET_MEDIUMROOM: + case REVERB_PRESET_LARGEROOM: + pPreset->m_nRvbLpfFbk = 5077; + pPreset->m_nRvbLpfFwd = 12922; pPreset->m_nEarlyGain = 27690; pPreset->m_nEarlyDelay = 1311; pPreset->m_nLateGain = 8191; pPreset->m_nLateDelay = 3932; pPreset->m_nRoomLpfFbk = 3692; - pPreset->m_nRoomLpfFwd = 24569; + pPreset->m_nRoomLpfFwd = 21703; pPreset->m_sEarlyL.m_zDelay[0] = 1376; pPreset->m_sEarlyL.m_nGain[0] = 22152; pPreset->m_sEarlyL.m_zDelay[1] = 1462; @@ -1983,11 +1997,11 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_sEarlyR.m_zDelay[4] = 0; pPreset->m_sEarlyR.m_nGain[4] = 13384; pPreset->m_nMaxExcursion = 127; - pPreset->m_nXfadeInterval = 6391; - pPreset->m_nAp0_ApGain = 15230; - pPreset->m_nAp0_ApOut = 708; - pPreset->m_nAp1_ApGain = 15547; - pPreset->m_nAp1_ApOut = 1023; + pPreset->m_nXfadeInterval = 6449; + pPreset->m_nAp0_ApGain = 15691; + pPreset->m_nAp0_ApOut = 774; + pPreset->m_nAp1_ApGain = 16317; + pPreset->m_nAp1_ApOut = 1155; pPreset->m_rfu4 = 0; pPreset->m_rfu5 = 0; pPreset->m_rfu6 = 0; @@ -1995,15 +2009,16 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_rfu8 = 0; pPreset->m_rfu9 = 0; pPreset->m_rfu10 = 0; - } else if (defaultPreset == 2) { - pPreset->m_nRvbLpfFbk = 5077; - pPreset->m_nRvbLpfFwd = 12922; + break; + case REVERB_PRESET_MEDIUMHALL: + pPreset->m_nRvbLpfFbk = 6461; + pPreset->m_nRvbLpfFwd = 14307; pPreset->m_nEarlyGain = 27690; pPreset->m_nEarlyDelay = 1311; pPreset->m_nLateGain = 8191; pPreset->m_nLateDelay = 3932; pPreset->m_nRoomLpfFbk = 3692; - pPreset->m_nRoomLpfFwd = 21703; + pPreset->m_nRoomLpfFwd = 24569; pPreset->m_sEarlyL.m_zDelay[0] = 1376; pPreset->m_sEarlyL.m_nGain[0] = 22152; pPreset->m_sEarlyL.m_zDelay[1] = 1462; @@ -2025,11 +2040,11 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_sEarlyR.m_zDelay[4] = 0; pPreset->m_sEarlyR.m_nGain[4] = 13384; pPreset->m_nMaxExcursion = 127; - pPreset->m_nXfadeInterval = 6449; - pPreset->m_nAp0_ApGain = 15691; - pPreset->m_nAp0_ApOut = 774; - pPreset->m_nAp1_ApGain = 16317; - pPreset->m_nAp1_ApOut = 1155; + pPreset->m_nXfadeInterval = 6391; + pPreset->m_nAp0_ApGain = 15230; + pPreset->m_nAp0_ApOut = 708; + pPreset->m_nAp1_ApGain = 15547; + pPreset->m_nAp1_ApOut = 1023; pPreset->m_rfu4 = 0; pPreset->m_rfu5 = 0; pPreset->m_rfu6 = 0; @@ -2037,18 +2052,19 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_rfu8 = 0; pPreset->m_rfu9 = 0; pPreset->m_rfu10 = 0; - } else if (defaultPreset == 3) { - pPreset->m_nRvbLpfFbk = 5077; - pPreset->m_nRvbLpfFwd = 11076; + break; + case REVERB_PRESET_LARGEHALL: + pPreset->m_nRvbLpfFbk = 8307; + pPreset->m_nRvbLpfFwd = 14768; pPreset->m_nEarlyGain = 27690; pPreset->m_nEarlyDelay = 1311; pPreset->m_nLateGain = 8191; pPreset->m_nLateDelay = 3932; pPreset->m_nRoomLpfFbk = 3692; - pPreset->m_nRoomLpfFwd = 20474; + pPreset->m_nRoomLpfFwd = 24569; pPreset->m_sEarlyL.m_zDelay[0] = 1376; pPreset->m_sEarlyL.m_nGain[0] = 22152; - pPreset->m_sEarlyL.m_zDelay[1] = 1462; + pPreset->m_sEarlyL.m_zDelay[1] = 2163; pPreset->m_sEarlyL.m_nGain[1] = 17537; pPreset->m_sEarlyL.m_zDelay[2] = 0; pPreset->m_sEarlyL.m_nGain[2] = 14768; @@ -2067,11 +2083,11 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_sEarlyR.m_zDelay[4] = 0; pPreset->m_sEarlyR.m_nGain[4] = 13384; pPreset->m_nMaxExcursion = 127; - pPreset->m_nXfadeInterval = 6470; //6483; - pPreset->m_nAp0_ApGain = 14768; - pPreset->m_nAp0_ApOut = 792; - pPreset->m_nAp1_ApGain = 14777; - pPreset->m_nAp1_ApOut = 1191; + pPreset->m_nXfadeInterval = 6388; + pPreset->m_nAp0_ApGain = 15691; + pPreset->m_nAp0_ApOut = 711; + pPreset->m_nAp1_ApGain = 16317; + pPreset->m_nAp1_ApOut = 1029; pPreset->m_rfu4 = 0; pPreset->m_rfu5 = 0; pPreset->m_rfu6 = 0; @@ -2079,6 +2095,7 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_rfu8 = 0; pPreset->m_rfu9 = 0; pPreset->m_rfu10 = 0; + break; } } diff --git a/media/libeffects/EffectReverb.h b/media/libeffects/EffectReverb.h index f5aadfa..5af316d 100644 --- a/media/libeffects/EffectReverb.h +++ b/media/libeffects/EffectReverb.h @@ -17,7 +17,8 @@ #ifndef ANDROID_EFFECTREVERB_H_ #define ANDROID_EFFECTREVERB_H_ -#include <media/EffectReverbApi.h> +#include <media/EffectEnvironmentalReverbApi.h> +#include <media/EffectPresetReverbApi.h> /*------------------------------------ @@ -43,7 +44,7 @@ if the buffer size is a power of two. #define REVERB_BUFFER_SIZE_IN_SAMPLES_MAX 16384 -#define REVERB_MAX_ROOM_TYPE 4 // any room numbers larger than this are invalid +#define REVERB_NUM_PRESETS REVERB_PRESET_PLATE // REVERB_PRESET_NONE is not included #define REVERB_MAX_NUM_REFLECTIONS 5 // max num reflections per channel @@ -171,7 +172,7 @@ typedef struct typedef struct { - reverb_preset_t m_sPreset[REVERB_MAX_ROOM_TYPE]; //array of presets + reverb_preset_t m_sPreset[REVERB_NUM_PRESETS]; // array of presets(does not include REVERB_PRESET_NONE) } reverb_preset_bank_t; diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 4872047..5401ec0 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -511,11 +511,17 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) sp<Client> c = mClients[i].promote(); if (c != 0) c->dump(fd, args); } - for (int i = 0, n = mMediaRecorderClients.size(); i < n; ++i) { - result.append(" MediaRecorderClient\n"); - sp<MediaRecorderClient> c = mMediaRecorderClients[i].promote(); - snprintf(buffer, 255, " pid(%d)\n\n", c->mPid); - result.append(buffer); + if (mMediaRecorderClients.size() == 0) { + result.append(" No media recorder client\n\n"); + } else { + for (int i = 0, n = mMediaRecorderClients.size(); i < n; ++i) { + sp<MediaRecorderClient> c = mMediaRecorderClients[i].promote(); + snprintf(buffer, 255, " MediaRecorderClient pid(%d)\n", c->mPid); + result.append(buffer); + write(fd, result.string(), result.size()); + result = "\n"; + c->dump(fd, args); + } } result.append(" Files opened and/or mapped:\n"); diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index 80b1cfd..fef3e6e 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -329,5 +329,12 @@ status_t MediaRecorderClient::setListener(const sp<IMediaRecorderClient>& listen return mRecorder->setListener(listener); } +status_t MediaRecorderClient::dump(int fd, const Vector<String16>& args) const { + if (mRecorder != NULL) { + return mRecorder->dump(fd, args); + } + return OK; +} + }; // namespace android diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index b53d950..d12e558 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -28,7 +28,7 @@ class MediaPlayerService; class MediaRecorderClient : public BnMediaRecorder { public: - virtual status_t setCamera(const sp<ICamera>& camera); + virtual status_t setCamera(const sp<ICamera>& camera); virtual status_t setPreviewSurface(const sp<ISurface>& surface); virtual status_t setVideoSource(int vs); virtual status_t setAudioSource(int as); @@ -45,21 +45,22 @@ public: virtual status_t getMaxAmplitude(int* max); virtual status_t start(); virtual status_t stop(); - virtual status_t reset(); + virtual status_t reset(); virtual status_t init(); virtual status_t close(); virtual status_t release(); + virtual status_t dump(int fd, const Vector<String16>& args) const; private: - friend class MediaPlayerService; // for accessing private constructor + friend class MediaPlayerService; // for accessing private constructor - MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid); - virtual ~MediaRecorderClient(); + MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid); + virtual ~MediaRecorderClient(); - pid_t mPid; - Mutex mLock; - MediaRecorderBase *mRecorder; - sp<MediaPlayerService> mMediaPlayerService; + pid_t mPid; + Mutex mLock; + MediaRecorderBase *mRecorder; + sp<MediaPlayerService> mMediaPlayerService; }; }; // namespace android diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 50f74f2..72061ad 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -1057,4 +1057,64 @@ status_t StagefrightRecorder::getMaxAmplitude(int *max) { return OK; } +status_t StagefrightRecorder::dump(int fd, const Vector<String16>& args) const { + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, " Recorder: %p", this); + snprintf(buffer, SIZE, " Output file (fd %d):\n", mOutputFd); + result.append(buffer); + snprintf(buffer, SIZE, " File format: %d\n", mOutputFormat); + result.append(buffer); + snprintf(buffer, SIZE, " Max file size (bytes): %lld\n", mMaxFileSizeBytes); + result.append(buffer); + snprintf(buffer, SIZE, " Max file duration (us): %lld\n", mMaxFileDurationUs); + result.append(buffer); + snprintf(buffer, SIZE, " File offset length (bits): %d\n", mUse64BitFileOffset? 64: 32); + result.append(buffer); + snprintf(buffer, SIZE, " Interleave duration (us): %d\n", mInterleaveDurationUs); + result.append(buffer); + snprintf(buffer, SIZE, " Progress notification: %d frames\n", mTrackEveryNumberOfFrames); + result.append(buffer); + snprintf(buffer, SIZE, " Progress notification: %lld us\n", mTrackEveryTimeDurationUs); + result.append(buffer); + snprintf(buffer, SIZE, " Audio\n"); + result.append(buffer); + snprintf(buffer, SIZE, " Source: %d\n", mAudioSource); + result.append(buffer); + snprintf(buffer, SIZE, " Encoder: %d\n", mAudioEncoder); + result.append(buffer); + snprintf(buffer, SIZE, " Bit rate (bps): %d\n", mAudioBitRate); + result.append(buffer); + snprintf(buffer, SIZE, " Sampling rate (hz): %d\n", mSampleRate); + result.append(buffer); + snprintf(buffer, SIZE, " Number of channels: %d\n", mAudioChannels); + result.append(buffer); + snprintf(buffer, SIZE, " Max amplitude: %d\n", mAudioSourceNode == 0? 0: mAudioSourceNode->getMaxAmplitude()); + result.append(buffer); + snprintf(buffer, SIZE, " Video\n"); + result.append(buffer); + snprintf(buffer, SIZE, " Source: %d\n", mVideoSource); + result.append(buffer); + snprintf(buffer, SIZE, " Camera Id: %d\n", mCameraId); + result.append(buffer); + snprintf(buffer, SIZE, " Camera flags: %d\n", mFlags); + result.append(buffer); + snprintf(buffer, SIZE, " Encoder: %d\n", mVideoEncoder); + result.append(buffer); + snprintf(buffer, SIZE, " Encoder profile: %d\n", mVideoEncoderProfile); + result.append(buffer); + snprintf(buffer, SIZE, " Encoder level: %d\n", mVideoEncoderLevel); + result.append(buffer); + snprintf(buffer, SIZE, " I frames interval (s): %d\n", mIFramesInterval); + result.append(buffer); + snprintf(buffer, SIZE, " Frame size (pixels): %dx%d\n", mVideoWidth, mVideoHeight); + result.append(buffer); + snprintf(buffer, SIZE, " Frame rate (fps): %d\n", mFrameRate); + result.append(buffer); + snprintf(buffer, SIZE, " Bit rate (bps): %d\n", mVideoBitRate); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return OK; +} } // namespace android diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 85d2557..704523f 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -54,6 +54,7 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual status_t close(); virtual status_t reset(); virtual status_t getMaxAmplitude(int *max); + virtual status_t dump(int fd, const Vector<String16>& args) const; private: enum CameraFlags { diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 83f7040..efaab5b 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -948,7 +948,7 @@ status_t OMXCodec::getVideoProfileLevel( int32_t supportedProfile = static_cast<int32_t>(param.eProfile); int32_t supportedLevel = static_cast<int32_t>(param.eLevel); - CODEC_LOGV("Supported profile: %ld, level %ld", + CODEC_LOGV("Supported profile: %d, level %d", supportedProfile, supportedLevel); if (profile == supportedProfile && diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp index 2bc4448..f3b281f 100644 --- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp +++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp @@ -15,6 +15,7 @@ */ #include "AACDecoder.h" +#define LOG_TAG "AACDecoder" #include "../../include/ESDS.h" @@ -36,26 +37,33 @@ AACDecoder::AACDecoder(const sp<MediaSource> &source) mAnchorTimeUs(0), mNumSamplesOutput(0), mInputBuffer(NULL) { -} -AACDecoder::~AACDecoder() { - if (mStarted) { - stop(); - } + sp<MetaData> srcFormat = mSource->getFormat(); - delete mConfig; - mConfig = NULL; -} + int32_t sampleRate; + CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); -status_t AACDecoder::start(MetaData *params) { - CHECK(!mStarted); + mMeta = new MetaData; + mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - mBufferGroup = new MediaBufferGroup; - mBufferGroup->add_buffer(new MediaBuffer(2048 * 2)); + // We'll always output stereo, regardless of how many channels are + // present in the input due to decoder limitations. + mMeta->setInt32(kKeyChannelCount, 2); + mMeta->setInt32(kKeySampleRate, sampleRate); + + int64_t durationUs; + if (srcFormat->findInt64(kKeyDuration, &durationUs)) { + mMeta->setInt64(kKeyDuration, durationUs); + } + mMeta->setCString(kKeyDecoderComponent, "AACDecoder"); + + mInitCheck = initCheck(); +} +status_t AACDecoder::initCheck() { + memset(mConfig, 0, sizeof(tPVMP4AudioDecoderExternal)); mConfig->outputFormat = OUTPUTFORMAT_16PCM_INTERLEAVED; - mConfig->aacPlusUpsamplingFactor = 0; - mConfig->aacPlusEnabled = false; + mConfig->aacPlusEnabled = 1; // The software decoder doesn't properly support mono output on // AACplus files. Always output stereo. @@ -64,8 +72,11 @@ status_t AACDecoder::start(MetaData *params) { UInt32 memRequirements = PVMP4AudioDecoderGetMemRequirements(); mDecoderBuf = malloc(memRequirements); - CHECK_EQ(PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf), - MP4AUDEC_SUCCESS); + status_t err = PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf); + if (err != MP4AUDEC_SUCCESS) { + LOGE("Failed to initialize MP4 audio decoder"); + return UNKNOWN_ERROR; + } uint32_t type; const void *data; @@ -83,18 +94,38 @@ status_t AACDecoder::start(MetaData *params) { mConfig->pInputBuffer = (UChar *)codec_specific_data; mConfig->inputBufferCurrentLength = codec_specific_data_size; mConfig->inputBufferMaxLength = 0; - mConfig->inputBufferUsedLength = 0; - mConfig->remainderBits = 0; - - mConfig->pOutputBuffer = NULL; - mConfig->pOutputBuffer_plus = NULL; - mConfig->repositionFlag = false; if (PVMP4AudioDecoderConfig(mConfig, mDecoderBuf) != MP4AUDEC_SUCCESS) { return ERROR_UNSUPPORTED; } + + // Check on the sampling rate to see whether it is changed. + int32_t sampleRate; + CHECK(mMeta->findInt32(kKeySampleRate, &sampleRate)); + if (mConfig->samplingRate != sampleRate) { + mMeta->setInt32(kKeySampleRate, mConfig->samplingRate); + LOGW("Sample rate was %d, but now is %d", + sampleRate, mConfig->samplingRate); + } } + return OK; +} + +AACDecoder::~AACDecoder() { + if (mStarted) { + stop(); + } + + delete mConfig; + mConfig = NULL; +} + +status_t AACDecoder::start(MetaData *params) { + CHECK(!mStarted); + + mBufferGroup = new MediaBufferGroup; + mBufferGroup->add_buffer(new MediaBuffer(4096 * 2)); mSource->start(); @@ -127,28 +158,7 @@ status_t AACDecoder::stop() { } sp<MetaData> AACDecoder::getFormat() { - sp<MetaData> srcFormat = mSource->getFormat(); - - int32_t sampleRate; - CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); - - sp<MetaData> meta = new MetaData; - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - - // We'll always output stereo, regardless of how many channels are - // present in the input due to decoder limitations. - meta->setInt32(kKeyChannelCount, 2); - - meta->setInt32(kKeySampleRate, sampleRate); - - int64_t durationUs; - if (srcFormat->findInt64(kKeyDuration, &durationUs)) { - meta->setInt64(kKeyDuration, durationUs); - } - - meta->setCString(kKeyDecoderComponent, "AACDecoder"); - - return meta; + return mMeta; } status_t AACDecoder::read( @@ -200,13 +210,19 @@ status_t AACDecoder::read( mConfig->remainderBits = 0; mConfig->pOutputBuffer = static_cast<Int16 *>(buffer->data()); - mConfig->pOutputBuffer_plus = NULL; + mConfig->pOutputBuffer_plus = &mConfig->pOutputBuffer[2048]; mConfig->repositionFlag = false; Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf); size_t numOutBytes = mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels; + if (mConfig->aacPlusUpsamplingFactor == 2) { + if (mConfig->desiredChannels == 1) { + memcpy(&mConfig->pOutputBuffer[1024], &mConfig->pOutputBuffer[2048], numOutBytes * 2); + } + numOutBytes *= 2; + } if (decoderErr != MP4AUDEC_SUCCESS) { LOGW("AAC decoder returned error %d, substituting silence", decoderErr); diff --git a/media/libstagefright/include/AACDecoder.h b/media/libstagefright/include/AACDecoder.h index f09addd..200f93c 100644 --- a/media/libstagefright/include/AACDecoder.h +++ b/media/libstagefright/include/AACDecoder.h @@ -25,6 +25,7 @@ struct tPVMP4AudioDecoderExternal; namespace android { struct MediaBufferGroup; +struct MetaData; struct AACDecoder : public MediaSource { AACDecoder(const sp<MediaSource> &source); @@ -41,6 +42,7 @@ protected: virtual ~AACDecoder(); private: + sp<MetaData> mMeta; sp<MediaSource> mSource; bool mStarted; @@ -50,9 +52,11 @@ private: void *mDecoderBuf; int64_t mAnchorTimeUs; int64_t mNumSamplesOutput; + status_t mInitCheck; MediaBuffer *mInputBuffer; + status_t initCheck(); AACDecoder(const AACDecoder &); AACDecoder &operator=(const AACDecoder &); }; diff --git a/native/android/input.cpp b/native/android/input.cpp index 015a1ce..89d53e2 100644 --- a/native/android/input.cpp +++ b/native/android/input.cpp @@ -186,9 +186,9 @@ float AMotionEvent_getHistoricalSize(AInputEvent* motion_event, size_t pointer_i } void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, - ALooper_callbackFunc callback, void* data) { + ALooper_callbackFunc* callback, void* data) { queue->setPollLoop(static_cast<android::PollLoop*>(looper)); - ALooper_setCallback(looper, queue->getConsumer().getChannel()->getReceivePipeFd(), + ALooper_addFd(looper, queue->getConsumer().getChannel()->getReceivePipeFd(), POLLIN, callback, data); } diff --git a/native/android/looper.cpp b/native/android/looper.cpp index 6e78bbd..1564c47 100644 --- a/native/android/looper.cpp +++ b/native/android/looper.cpp @@ -27,22 +27,41 @@ ALooper* ALooper_forThread() { return PollLoop::getForThread().get(); } -ALooper* ALooper_prepare() { +ALooper* ALooper_prepare(int32_t opts) { + bool allowFds = (opts&ALOOPER_PREPARE_ALLOW_NON_CALLBACKS) != 0; sp<PollLoop> loop = PollLoop::getForThread(); if (loop == NULL) { - loop = new PollLoop(); + loop = new PollLoop(allowFds); PollLoop::setForThread(loop); } + if (loop->getAllowNonCallbacks() != allowFds) { + LOGW("ALooper_prepare again with different ALOOPER_PREPARE_ALLOW_NON_CALLBACKS"); + } return loop.get(); } -int32_t ALooper_pollOnce(int timeoutMillis) { +int32_t ALooper_pollOnce(int timeoutMillis, int* outEvents, void** outData) { sp<PollLoop> loop = PollLoop::getForThread(); if (loop == NULL) { LOGW("ALooper_pollOnce: No looper for this thread!"); return -1; } - return loop->pollOnce(timeoutMillis) ? 1 : 0; + return loop->pollOnce(timeoutMillis, outEvents, outData); +} + +int32_t ALooper_pollAll(int timeoutMillis, int* outEvents, void** outData) { + sp<PollLoop> loop = PollLoop::getForThread(); + if (loop == NULL) { + LOGW("ALooper_pollOnce: No looper for this thread!"); + return -1; + } + + int32_t result; + while ((result = loop->pollOnce(timeoutMillis, outEvents, outData)) == ALOOPER_POLL_CALLBACK) { + ; + } + + return result; } void ALooper_acquire(ALooper* looper) { @@ -53,11 +72,11 @@ void ALooper_release(ALooper* looper) { static_cast<PollLoop*>(looper)->decStrong((void*)ALooper_acquire); } -void ALooper_setCallback(ALooper* looper, int fd, int events, +void ALooper_addFd(ALooper* looper, int fd, int events, ALooper_callbackFunc* callback, void* data) { static_cast<PollLoop*>(looper)->setLooperCallback(fd, events, callback, data); } -int32_t ALooper_removeCallback(ALooper* looper, int fd) { +int32_t ALooper_removeFd(ALooper* looper, int fd) { return static_cast<PollLoop*>(looper)->removeCallback(fd) ? 1 : 0; } diff --git a/native/glue/threaded_app/Android.mk b/native/glue/threaded_app/Android.mk new file mode 100644 index 0000000..cfc9b2a --- /dev/null +++ b/native/glue/threaded_app/Android.mk @@ -0,0 +1,18 @@ +BASE_PATH := $(call my-dir) +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +# our source files +# +LOCAL_SRC_FILES:= \ + threaded_app.c + +LOCAL_C_INCLUDES += \ + frameworks/base/native/include \ + frameworks/base/core/jni/android \ + dalvik/libnativehelper/include/nativehelper + +LOCAL_MODULE:= libthreaded_app + +include $(BUILD_STATIC_LIBRARY) diff --git a/native/glue/threaded_app/threaded_app.c b/native/glue/threaded_app/threaded_app.c new file mode 100644 index 0000000..c9cae8b --- /dev/null +++ b/native/glue/threaded_app/threaded_app.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2010 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. + * + */ + +#include <jni.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/resource.h> + +#include <android_glue/threaded_app.h> + +#include <android/log.h> + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) +#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "threaded_app", __VA_ARGS__)) + +void android_app_destroy(struct android_app* android_app) { + LOGI("android_app_destroy!"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->destroyed = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + // Can't touch android_app object after this. +} + +int8_t android_app_read_cmd(struct android_app* android_app) { + int8_t cmd; + if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) { + return cmd; + } else { + LOGW("No data on command pipe!"); + } + return -1; +} + +void android_app_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_INPUT_CHANGED: + LOGI("APP_CMD_INPUT_CHANGED\n"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->inputQueue = android_app->pendingInputQueue; + if (android_app->inputQueue != NULL) { + LOGI("Attaching input queue to looper"); + AInputQueue_attachLooper(android_app->inputQueue, + android_app->looper, NULL, (void*)LOOPER_ID_EVENT); + } + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_WINDOW_CHANGED: + LOGI("APP_CMD_WINDOW_CHANGED\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = android_app->pendingWindow; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_START: + case APP_CMD_RESUME: + case APP_CMD_PAUSE: + case APP_CMD_STOP: + LOGI("activityState=%d\n", cmd); + pthread_mutex_lock(&android_app->mutex); + android_app->activityState = cmd; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_DESTROY: + LOGI("APP_CMD_DESTROY\n"); + android_app->destroyRequested = 1; + break; + } +} + +static void* android_app_entry(void* param) { + struct android_app* android_app = (struct android_app*)param; + + ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + ALooper_addFd(looper, android_app->msgread, POLLIN, NULL, (void*)LOOPER_ID_MAIN); + android_app->looper = looper; + + pthread_mutex_lock(&android_app->mutex); + android_app->running = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + + android_main(android_app); + return NULL; +} + +// -------------------------------------------------------------------- +// Native activity interaction (called from main thread) +// -------------------------------------------------------------------- + +static struct android_app* android_app_create(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app)); + memset(android_app, 0, sizeof(struct android_app)); + android_app->activity = activity; + + pthread_mutex_init(&android_app->mutex, NULL); + pthread_cond_init(&android_app->cond, NULL); + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGI("could not create pipe: %s", strerror(errno)); + } + android_app->msgread = msgpipe[0]; + android_app->msgwrite = msgpipe[1]; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&android_app->thread, &attr, android_app_entry, android_app); + + // Wait for thread to start. + pthread_mutex_lock(&android_app->mutex); + while (!android_app->running) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + return android_app; +} + +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { + if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { + LOGI("Failure writing android_app cmd: %s\n", strerror(errno)); + } +} + +static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingInputQueue = inputQueue; + android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); + while (android_app->inputQueue != android_app->pendingInputQueue) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingWindow = window; + android_app_write_cmd(android_app, APP_CMD_WINDOW_CHANGED); + while (android_app->window != android_app->pendingWindow) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, cmd); + while (android_app->activityState != cmd) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_free(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, APP_CMD_DESTROY); + while (!android_app->destroyed) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + close(android_app->msgread); + close(android_app->msgwrite); + pthread_cond_destroy(&android_app->cond); + pthread_mutex_destroy(&android_app->mutex); + free(android_app); +} + +static void onDestroy(ANativeActivity* activity) { + LOGI("Destroy: %p\n", activity); + android_app_free((struct android_app*)activity->instance); +} + +static void onStart(ANativeActivity* activity) { + LOGI("Start: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START); +} + +static void onResume(ANativeActivity* activity) { + LOGI("Resume: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME); +} + +static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { + LOGI("SaveInstanceState: %p\n", activity); + return NULL; +} + +static void onPause(ANativeActivity* activity) { + LOGI("Pause: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE); +} + +static void onStop(ANativeActivity* activity) { + LOGI("Stop: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP); +} + +static void onLowMemory(ANativeActivity* activity) { + LOGI("LowMemory: %p\n", activity); +} + +static void onWindowFocusChanged(ANativeActivity* activity, int focused) { + LOGI("WindowFocusChanged: %p -- %d\n", activity, focused); + android_app_write_cmd((struct android_app*)activity->instance, + focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); +} + +static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { + LOGI("NativeWindowCreated: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, window); +} + +static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { + LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, NULL); +} + +static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { + LOGI("InputQueueCreated: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, queue); +} + +static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { + LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, NULL); +} + +void ANativeActivity_onCreate(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + LOGI("Creating: %p\n", activity); + activity->callbacks->onDestroy = onDestroy; + activity->callbacks->onStart = onStart; + activity->callbacks->onResume = onResume; + activity->callbacks->onSaveInstanceState = onSaveInstanceState; + activity->callbacks->onPause = onPause; + activity->callbacks->onStop = onStop; + activity->callbacks->onLowMemory = onLowMemory; + activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; + activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; + activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; + activity->callbacks->onInputQueueCreated = onInputQueueCreated; + activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; + + activity->instance = android_app_create(activity); +} diff --git a/native/include/android/input.h b/native/include/android/input.h index 75be85a..014b6a3 100644 --- a/native/include/android/input.h +++ b/native/include/android/input.h @@ -534,10 +534,11 @@ struct AInputQueue; typedef struct AInputQueue AInputQueue; /* - * Add this input queue to a looper for processing. + * Add this input queue to a looper for processing. See + * ALooper_addFd() for information on the callback and data params. */ void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, - ALooper_callbackFunc callback, void* data); + ALooper_callbackFunc* callback, void* data); /* * Remove the input queue from the looper it is currently attached to. diff --git a/native/include/android/looper.h b/native/include/android/looper.h index 90a8983..2917216 100644 --- a/native/include/android/looper.h +++ b/native/include/android/looper.h @@ -24,25 +24,151 @@ extern "C" { #endif +/** + * ALooper + * + * A looper is the state tracking an event loop for a thread. + * Loopers do not define event structures or other such things; rather + * they are a lower-level facility to attach one or more discrete objects + * listening for an event. An "event" here is simply data available on + * a file descriptor: each attached object has an associated file descriptor, + * and waiting for "events" means (internally) polling on all of these file + * descriptors until one or more of them have data available. + * + * A thread can have only one ALooper associated with it. + */ struct ALooper; typedef struct ALooper ALooper; +/** + * For callback-based event loops, this is the prototype of the function + * that is called. It is given the file descriptor it is associated with, + * a bitmask of the poll events that were triggered (typically POLLIN), and + * the data pointer that was originally supplied. + * + * Implementations should return 1 to continue receiving callbacks, or 0 + * to have this file descriptor and callback unregistered from the looper. + */ typedef int ALooper_callbackFunc(int fd, int events, void* data); +/** + * Return the ALooper associated with the calling thread, or NULL if + * there is not one. + */ ALooper* ALooper_forThread(); -ALooper* ALooper_prepare(); +enum { + /** + * Option for ALooper_prepare: this ALooper will accept calls to + * ALooper_addFd() that do not have a callback (that is provide NULL + * for the callback). In this case the caller of ALooper_pollOnce() + * or ALooper_pollAll() MUST check the return from these functions to + * discover when data is available on such fds and process it. + */ + ALOOPER_PREPARE_ALLOW_NON_CALLBACKS = 1<<0 +}; -int32_t ALooper_pollOnce(int timeoutMillis); +/** + * Prepare an ALooper associated with the calling thread, and return it. + * If the thread already has an ALooper, it is returned. Otherwise, a new + * one is created, associated with the thread, and returned. + * + * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0. + */ +ALooper* ALooper_prepare(int32_t opts); + +enum { + /** + * Result from ALooper_pollOnce() and ALooper_pollAll(): one or + * more callbacks were executed. + */ + ALOOPER_POLL_CALLBACK = -1, + + /** + * Result from ALooper_pollOnce() and ALooper_pollAll(): the + * timeout expired. + */ + ALOOPER_POLL_TIMEOUT = -2, + + /** + * Result from ALooper_pollOnce() and ALooper_pollAll(): an error + * occurred. + */ + ALOOPER_POLL_ERROR = -3, +}; +/** + * Wait for events to be available, with optional timeout in milliseconds. + * Invokes callbacks for all file descriptors on which an event occurred. + * + * If the timeout is zero, returns immediately without blocking. + * If the timeout is negative, waits indefinitely until an event appears. + * + * Returns ALOOPER_POLL_CALLBACK if a callback was invoked. + * + * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given + * timeout expired. + * + * Returns ALOPER_POLL_ERROR if an error occurred. + * + * Returns a value >= 0 containing a file descriptor if it has data + * and it has no callback function (requiring the caller here to handle it). + * In this (and only this) case outEvents and outData will contain the poll + * events and data associated with the fd. + * + * This method does not return until it has finished invoking the appropriate callbacks + * for all file descriptors that were signalled. + */ +int32_t ALooper_pollOnce(int timeoutMillis, int* outEvents, void** outData); + +/** + * Like ALooper_pollOnce(), but performs all pending callbacks until all + * data has been consumed or a file descriptor is available with no callback. + * This function will never return ALOOPER_POLL_CALLBACK. + */ +int32_t ALooper_pollAll(int timeoutMillis, int* outEvents, void** outData); + +/** + * Acquire a reference on the given ALooper object. This prevents the object + * from being deleted until the reference is removed. This is only needed + * to safely hand an ALooper from one thread to another. + */ void ALooper_acquire(ALooper* looper); +/** + * Remove a reference that was previously acquired with ALooper_acquire(). + */ void ALooper_release(ALooper* looper); -void ALooper_setCallback(ALooper* looper, int fd, int events, +/** + * Add a new file descriptor to be polled by the looper. If the same file + * descriptor was previously added, it is replaced. + * + * "fd" is the file descriptor to be added. + * "events" are the poll events to wake up on. Typically this is POLLIN. + * "callback" is the function to call when there is an event on the file + * descriptor. + * "id" is an identifier to associated with this file descriptor, or 0. + * "data" is a private data pointer to supply to the callback. + * + * There are two main uses of this function: + * + * (1) If "callback" is non-NULL, then + * this function will be called when there is data on the file descriptor. It + * should execute any events it has pending, appropriately reading from the + * file descriptor. + * + * (2) If "callback" is NULL, the fd will be returned by ALooper_pollOnce + * when it has data available, requiring the caller to take care of processing + * it. + */ +void ALooper_addFd(ALooper* looper, int fd, int events, ALooper_callbackFunc* callback, void* data); -int32_t ALooper_removeCallback(ALooper* looper, int fd); +/** + * Remove a previously added file descriptor from the looper. + */ +int32_t ALooper_removeFd(ALooper* looper, int fd); #ifdef __cplusplus }; diff --git a/native/include/android_glue/threaded_app.h b/native/include/android_glue/threaded_app.h new file mode 100644 index 0000000..80de3bf --- /dev/null +++ b/native/include/android_glue/threaded_app.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2010 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. + * + */ + +#include <poll.h> +#include <pthread.h> +#include <sched.h> + +#include <android/native_activity.h> +#include <android/looper.h> + +/** + * This is the interface for the standard glue code of a threaded + * application. In this model, the application's code is running + * in its own thread separate from the main thread of the process. + * It is not required that this thread be associated with the Java + * VM, although it will need to be in order to make JNI calls any + * Java objects. + */ +struct android_app { + // The application can place a pointer to its own state object + // here if it likes. + void* userData; + + // The ANativeActivity object instance that this app is running in. + ANativeActivity* activity; + + // The ALooper associated with the app's thread. + ALooper* looper; + + // When non-NULL, this is the input queue from which the app will + // receive user input events. + AInputQueue* inputQueue; + + // When non-NULL, this is the window surface that the app can draw in. + ANativeWindow* window; + + // Current state of the app's activity. May be either APP_CMD_START, + // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. + int activityState; + + // This is non-zero when the application's NativeActivity is being + // destroyed and waiting for the app thread to complete. + int destroyRequested; + + // ------------------------------------------------- + // Below are "private" implementation of the glue code. + + pthread_mutex_t mutex; + pthread_cond_t cond; + + int msgread; + int msgwrite; + + pthread_t thread; + + int running; + int destroyed; + AInputQueue* pendingInputQueue; + ANativeWindow* pendingWindow; +}; + +enum { + /** + * Looper data ID of commands coming from the app's main thread. + * These can be retrieved and processed with android_app_read_cmd() + * and android_app_exec_cmd(). + */ + LOOPER_ID_MAIN = 1, + + /** + * Looper data ID of events coming from the AInputQueue of the + * application's window. These can be read via the inputQueue + * object of android_app. + */ + LOOPER_ID_EVENT = 2 +}; + +enum { + /** + * Command from main thread: the AInputQueue has changed. Upon processing + * this command, android_app->inputQueue will be updated to the new queue + * (or NULL). + */ + APP_CMD_INPUT_CHANGED, + + /** + * Command from main thread: the ANativeWindow has changed. Upon processing + * this command, android_app->window will be updated to the new window surface + * (or NULL). + */ + APP_CMD_WINDOW_CHANGED, + + /** + * Command from main thread: the app's activity window has gained + * input focus. + */ + APP_CMD_GAINED_FOCUS, + + /** + * Command from main thread: the app's activity window has lost + * input focus. + */ + APP_CMD_LOST_FOCUS, + + /** + * Command from main thread: the app's activity has been started. + */ + APP_CMD_START, + + /** + * Command from main thread: the app's activity has been resumed. + */ + APP_CMD_RESUME, + + /** + * Command from main thread: the app's activity has been paused. + */ + APP_CMD_PAUSE, + + /** + * Command from main thread: the app's activity has been stopped. + */ + APP_CMD_STOP, + + /** + * Command from main thread: the app's activity is being destroyed, + * and waiting for the app thread to clean up and exit before proceeding. + */ + APP_CMD_DESTROY, +}; + +/** + * Call if android_app->destroyRequested is non-zero. Upon return, the + * android_app structure is no longer valid and must not be touched. + */ +void android_app_destroy(struct android_app* android_app); + +/** + * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next + * app command message. + */ +int8_t android_app_read_cmd(struct android_app* android_app); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * default processing of the given command. + */ +void android_app_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * This is the function that application code must implement, representing + * the main entry to the app. + */ +extern void android_main(struct android_app* app); diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index f8abc5a..b9e915a4 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -244,27 +244,12 @@ public class StorageNotification extends StorageEventListener { intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - final boolean adbOn = 1 == Settings.Secure.getInt( - mContext.getContentResolver(), - Settings.Secure.ADB_ENABLED, - 0); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); setUsbStorageNotification( com.android.internal.R.string.usb_storage_notification_title, com.android.internal.R.string.usb_storage_notification_message, com.android.internal.R.drawable.stat_sys_data_usb, false, true, pi); - - if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) { - // We assume that developers don't want to enable UMS every - // time they attach a device to a USB host. The average user, - // however, is looking to charge the phone (in which case this - // is harmless) or transfer files (in which case this coaches - // the user about how to complete that task and saves several - // steps). - mContext.startActivity(intent); - } } else { setUsbStorageNotification(0, 0, 0, false, false, null); } @@ -313,6 +298,23 @@ public class StorageNotification extends StorageEventListener { } mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi); + final boolean adbOn = 1 == Settings.Secure.getInt( + mContext.getContentResolver(), + Settings.Secure.ADB_ENABLED, + 0); + + if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) { + // Pop up a full-screen alert to coach the user through enabling UMS. The average + // user has attached the device to USB either to charge the phone (in which case + // this is harmless) or transfer files, and in the latter case this alert saves + // several steps (as well as subtly indicates that you shouldn't mix UMS with other + // activities on the device). + // + // If ADB is enabled, however, we suppress this dialog (under the assumption that a + // developer (a) knows how to enable UMS, and (b) is probably using USB to install + // builds or use adb commands. + mUsbStorageNotification.fullScreenIntent = pi; + } } final int notificationId = mUsbStorageNotification.icon; diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index a01e25b..83d9c47 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -60,7 +60,6 @@ import android.view.IWindowManager; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.WindowOrientationListener; -import android.view.RawInputEvent; import android.view.Surface; import android.view.View; import android.view.ViewConfiguration; @@ -153,8 +152,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1; static final int APPLICATION_PANEL_SUBLAYER = 1; static final int APPLICATION_SUB_PANEL_SUBLAYER = 2; - - static final float SLIDE_TOUCH_EVENT_SIZE_LIMIT = 0.6f; // Debugging: set this to have the system act like there is no hard keyboard. static final boolean KEYBOARD_ALWAYS_HIDDEN = false; @@ -164,6 +161,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; + // Useful scan codes. + private static final int SW_LID = 0x00; + private static final int BTN_MOUSE = 0x110; + final Object mLock = new Object(); Context mContext; @@ -690,7 +691,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { void readLidState() { try { - int sw = mWindowManager.getSwitchState(RawInputEvent.SW_LID); + int sw = mWindowManager.getSwitchState(SW_LID); if (sw >= 0) { mLidOpen = sw == 0; } @@ -727,19 +728,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { : Configuration.KEYBOARDHIDDEN_YES; } - public boolean isCheekPressedAgainstScreen(MotionEvent ev) { - if(ev.getSize() > SLIDE_TOUCH_EVENT_SIZE_LIMIT) { - return true; - } - int size = ev.getHistorySize(); - for(int i = 0; i < size; i++) { - if(ev.getHistoricalSize(i) > SLIDE_TOUCH_EVENT_SIZE_LIMIT) { - return true; - } - } - return false; - } - public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY) { if (mPointerLocationView == null) { return; @@ -1034,19 +1022,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { }; /** {@inheritDoc} */ - public boolean interceptKeyTi(WindowState win, int code, int metaKeys, boolean down, - int repeatCount, int flags) { - boolean keyguardOn = keyguardOn(); + @Override + public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags, + int keyCode, int metaState, int repeatCount, int policyFlags) { + final boolean keyguardOn = keyguardOn(); + final boolean down = (action == KeyEvent.ACTION_DOWN); + final boolean canceled = ((flags & KeyEvent.FLAG_CANCELED) != 0); if (false) { - Log.d(TAG, "interceptKeyTi code=" + code + " down=" + down + " repeatCount=" + Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount=" + repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed); } // Clear a pending HOME longpress if the user releases Home // TODO: This could probably be inside the next bit of logic, but that code // turned out to be a bit fragile so I'm doing it here explicitly, for now. - if ((code == KeyEvent.KEYCODE_HOME) && !down) { + if ((keyCode == KeyEvent.KEYCODE_HOME) && !down) { mHandler.removeCallbacks(mHomeLongPress); } @@ -1056,11 +1047,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // If we have released the home key, and didn't do anything else // while it was pressed, then it is time to go home! - if (code == KeyEvent.KEYCODE_HOME) { + if (keyCode == KeyEvent.KEYCODE_HOME) { if (!down) { mHomePressed = false; - if ((flags&KeyEvent.FLAG_CANCELED) == 0) { + if (! canceled) { // If an incoming call is ringing, HOME is totally disabled. // (The user is already on the InCallScreen at this point, // and his ONLY options are to answer or reject the call.) @@ -1094,7 +1085,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // can never break it, although if keyguard is on, we do let // it handle it, because that gives us the correct 5 second // timeout. - if (code == KeyEvent.KEYCODE_HOME) { + if (keyCode == KeyEvent.KEYCODE_HOME) { // If a system window has focus, then it doesn't make sense // right now to interact with applications. @@ -1122,17 +1113,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHomePressed = true; } return true; - } else if (code == KeyEvent.KEYCODE_MENU) { + } else if (keyCode == KeyEvent.KEYCODE_MENU) { // Hijack modified menu keys for debugging features final int chordBug = KeyEvent.META_SHIFT_ON; if (down && repeatCount == 0) { - if (mEnableShiftMenuBugReports && (metaKeys & chordBug) == chordBug) { + if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) { Intent intent = new Intent(Intent.ACTION_BUG_REPORT); mContext.sendOrderedBroadcast(intent, null); return true; } else if (SHOW_PROCESSES_ON_ALT_MENU && - (metaKeys & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) { + (metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) { Intent service = new Intent(); service.setClassName(mContext, "com.android.server.LoadAverageService"); ContentResolver res = mContext.getContentResolver(); @@ -1148,7 +1139,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } } - } else if (code == KeyEvent.KEYCODE_SEARCH) { + } else if (keyCode == KeyEvent.KEYCODE_SEARCH) { if (down) { if (repeatCount == 0) { mSearchKeyPressed = true; @@ -1167,7 +1158,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Shortcuts are invoked through Search+key, so intercept those here if (mSearchKeyPressed) { if (down && repeatCount == 0 && !keyguardOn) { - Intent shortcutIntent = mShortcutManager.getIntent(code, metaKeys); + Intent shortcutIntent = mShortcutManager.getIntent(keyCode, metaState); if (shortcutIntent != null) { shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(shortcutIntent); @@ -1606,42 +1597,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** {@inheritDoc} */ - public boolean preprocessInputEventTq(RawInputEvent event) { - switch (event.type) { - case RawInputEvent.EV_SW: - if (event.keycode == RawInputEvent.SW_LID) { - // lid changed state - mLidOpen = event.value == 0; - boolean awakeNow = mKeyguardMediator.doLidChangeTq(mLidOpen); - updateRotation(Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE); - if (awakeNow) { - // If the lid opening and we don't have to keep the - // keyguard up, then we can turn on the screen - // immediately. - mKeyguardMediator.pokeWakelock(); - } else if (keyguardIsShowingTq()) { - if (mLidOpen) { - // If we are opening the lid and not hiding the - // keyguard, then we need to have it turn on the - // screen once it is shown. - mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq( - KeyEvent.KEYCODE_POWER); - } - } else { - // Light up the keyboard if we are sliding up. - if (mLidOpen) { - mPowerManager.userActivity(SystemClock.uptimeMillis(), false, - LocalPowerManager.BUTTON_EVENT); - } else { - mPowerManager.userActivity(SystemClock.uptimeMillis(), false, - LocalPowerManager.OTHER_EVENT); - } - } - } - } - return false; - } - public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { // lid changed state mLidOpen = lidOpen; @@ -1672,26 +1627,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - - /** {@inheritDoc} */ - public boolean isAppSwitchKeyTqTiLwLi(int keycode) { - return keycode == KeyEvent.KEYCODE_HOME - || keycode == KeyEvent.KEYCODE_ENDCALL; - } - - /** {@inheritDoc} */ - public boolean isMovementKeyTi(int keycode) { - switch (keycode) { - case KeyEvent.KEYCODE_DPAD_UP: - case KeyEvent.KEYCODE_DPAD_DOWN: - case KeyEvent.KEYCODE_DPAD_LEFT: - case KeyEvent.KEYCODE_DPAD_RIGHT: - return true; - } - return false; - } - - /** * @return Whether a telephone call is in progress right now. */ @@ -1762,60 +1697,63 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** {@inheritDoc} */ - public int interceptKeyTq(RawInputEvent event, boolean screenIsOn) { + @Override + public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, + int policyFlags, boolean isScreenOn) { int result = ACTION_PASS_TO_USER; - final boolean isWakeKey = isWakeKeyTq(event); + + final boolean isWakeKey = (policyFlags + & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; + // If screen is off then we treat the case where the keyguard is open but hidden // the same as if it were open and in front. // This will prevent any keys other than the power button from waking the screen // when the keyguard is hidden by another activity. - final boolean keyguardActive = (screenIsOn ? + final boolean keyguardActive = (isScreenOn ? mKeyguardMediator.isShowingAndNotHidden() : mKeyguardMediator.isShowing()); if (false) { - Log.d(TAG, "interceptKeyTq event=" + event + " keycode=" + event.keycode - + " screenIsOn=" + screenIsOn + " keyguardActive=" + keyguardActive); + Log.d(TAG, "interceptKeyTq keycode=" + keyCode + + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive); } if (keyguardActive) { - if (screenIsOn) { + if (isScreenOn) { // when the screen is on, always give the event to the keyguard result |= ACTION_PASS_TO_USER; } else { // otherwise, don't pass it to the user result &= ~ACTION_PASS_TO_USER; - final boolean isKeyDown = - (event.type == RawInputEvent.EV_KEY) && (event.value != 0); - if (isWakeKey && isKeyDown) { + if (isWakeKey && down) { // tell the mediator about a wake key, it may decide to // turn on the screen depending on whether the key is // appropriate. - if (!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(event.keycode) - && (event.keycode == KeyEvent.KEYCODE_VOLUME_DOWN - || event.keycode == KeyEvent.KEYCODE_VOLUME_UP)) { + if (!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode) + && (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { // when keyguard is showing and screen off, we need // to handle the volume key for calls and music here if (isInCall()) { - handleVolumeKey(AudioManager.STREAM_VOICE_CALL, event.keycode); + handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); } else if (isMusicActive()) { - handleVolumeKey(AudioManager.STREAM_MUSIC, event.keycode); + handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode); } } } } - } else if (!screenIsOn) { + } else if (!isScreenOn) { // If we are in-call with screen off and keyguard is not showing, // then handle the volume key ourselves. // This is necessary because the phone app will disable the keyguard // when the proximity sensor is in use. - if (isInCall() && event.type == RawInputEvent.EV_KEY && - (event.keycode == KeyEvent.KEYCODE_VOLUME_DOWN - || event.keycode == KeyEvent.KEYCODE_VOLUME_UP)) { + if (isInCall() && + (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { result &= ~ACTION_PASS_TO_USER; - handleVolumeKey(AudioManager.STREAM_VOICE_CALL, event.keycode); + handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); } if (isWakeKey) { // a wake key has a sole purpose of waking the device; don't pass @@ -1825,156 +1763,151 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - int type = event.type; - int code = event.keycode; - boolean down = event.value != 0; - - if (type == RawInputEvent.EV_KEY) { - if (code == KeyEvent.KEYCODE_ENDCALL - || code == KeyEvent.KEYCODE_POWER) { - if (down) { - boolean handled = false; - boolean hungUp = false; - // key repeats are generated by the window manager, and we don't see them - // here, so unless the driver is doing something it shouldn't be, we know - // this is the real press event. - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - try { - if (code == KeyEvent.KEYCODE_ENDCALL) { + if (keyCode == KeyEvent.KEYCODE_ENDCALL + || keyCode == KeyEvent.KEYCODE_POWER) { + if (down) { + boolean handled = false; + boolean hungUp = false; + // key repeats are generated by the window manager, and we don't see them + // here, so unless the driver is doing something it shouldn't be, we know + // this is the real press event. + ITelephony phoneServ = getPhoneInterface(); + if (phoneServ != null) { + try { + if (keyCode == KeyEvent.KEYCODE_ENDCALL) { + handled = hungUp = phoneServ.endCall(); + } else if (keyCode == KeyEvent.KEYCODE_POWER) { + if (phoneServ.isRinging()) { + // Pressing Power while there's a ringing incoming + // call should silence the ringer. + phoneServ.silenceRinger(); + handled = true; + } else if (phoneServ.isOffhook() && + ((mIncallPowerBehavior + & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) + != 0)) { + // Otherwise, if "Power button ends call" is enabled, + // the Power button will hang up any current active call. handled = hungUp = phoneServ.endCall(); - } else if (code == KeyEvent.KEYCODE_POWER) { - if (phoneServ.isRinging()) { - // Pressing Power while there's a ringing incoming - // call should silence the ringer. - phoneServ.silenceRinger(); - handled = true; - } else if (phoneServ.isOffhook() && - ((mIncallPowerBehavior - & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) - != 0)) { - // Otherwise, if "Power button ends call" is enabled, - // the Power button will hang up any current active call. - handled = hungUp = phoneServ.endCall(); - } } - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException" + ex); } - } else { - Log.w(TAG, "!!! Unable to find ITelephony interface !!!"); + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException" + ex); } + } else { + Log.w(TAG, "!!! Unable to find ITelephony interface !!!"); + } - if (!screenIsOn - || (handled && code != KeyEvent.KEYCODE_POWER) - || (handled && hungUp && code == KeyEvent.KEYCODE_POWER)) { - mShouldTurnOffOnKeyUp = false; + if (!isScreenOn + || (handled && keyCode != KeyEvent.KEYCODE_POWER) + || (handled && hungUp && keyCode == KeyEvent.KEYCODE_POWER)) { + mShouldTurnOffOnKeyUp = false; + } else { + // only try to turn off the screen if we didn't already hang up + mShouldTurnOffOnKeyUp = true; + mHandler.postDelayed(mPowerLongPress, + ViewConfiguration.getGlobalActionKeyTimeout()); + result &= ~ACTION_PASS_TO_USER; + } + } else { + mHandler.removeCallbacks(mPowerLongPress); + if (mShouldTurnOffOnKeyUp) { + mShouldTurnOffOnKeyUp = false; + boolean gohome, sleeps; + if (keyCode == KeyEvent.KEYCODE_ENDCALL) { + gohome = (mEndcallBehavior + & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0; + sleeps = (mEndcallBehavior + & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0; } else { - // only try to turn off the screen if we didn't already hang up - mShouldTurnOffOnKeyUp = true; - mHandler.postDelayed(mPowerLongPress, - ViewConfiguration.getGlobalActionKeyTimeout()); - result &= ~ACTION_PASS_TO_USER; + gohome = false; + sleeps = true; } - } else { - mHandler.removeCallbacks(mPowerLongPress); - if (mShouldTurnOffOnKeyUp) { - mShouldTurnOffOnKeyUp = false; - boolean gohome, sleeps; - if (code == KeyEvent.KEYCODE_ENDCALL) { - gohome = (mEndcallBehavior - & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0; - sleeps = (mEndcallBehavior - & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0; - } else { - gohome = false; - sleeps = true; - } - if (keyguardActive - || (sleeps && !gohome) - || (gohome && !goHome() && sleeps)) { - // they must already be on the keyguad or home screen, - // go to sleep instead - Log.d(TAG, "I'm tired mEndcallBehavior=0x" - + Integer.toHexString(mEndcallBehavior)); - result &= ~ACTION_POKE_USER_ACTIVITY; - result |= ACTION_GO_TO_SLEEP; - } - result &= ~ACTION_PASS_TO_USER; + if (keyguardActive + || (sleeps && !gohome) + || (gohome && !goHome() && sleeps)) { + // they must already be on the keyguad or home screen, + // go to sleep instead + Log.d(TAG, "I'm tired mEndcallBehavior=0x" + + Integer.toHexString(mEndcallBehavior)); + result &= ~ACTION_POKE_USER_ACTIVITY; + result |= ACTION_GO_TO_SLEEP; } + result &= ~ACTION_PASS_TO_USER; } - } else if (isMediaKey(code)) { - // This key needs to be handled even if the screen is off. - // If others need to be handled while it's off, this is a reasonable - // pattern to follow. - if ((result & ACTION_PASS_TO_USER) == 0) { - // Only do this if we would otherwise not pass it to the user. In that - // case, the PhoneWindow class will do the same thing, except it will - // only do it if the showing app doesn't process the key on its own. - KeyEvent keyEvent = new KeyEvent(event.when, event.when, - down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, - code, 0); - mBroadcastWakeLock.acquire(); - mHandler.post(new PassHeadsetKey(keyEvent)); - } - } else if (code == KeyEvent.KEYCODE_CALL) { - // If an incoming call is ringing, answer it! - // (We handle this key here, rather than in the InCallScreen, to make - // sure we'll respond to the key even if the InCallScreen hasn't come to - // the foreground yet.) - - // We answer the call on the DOWN event, to agree with - // the "fallback" behavior in the InCallScreen. - if (down) { - try { - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - if (phoneServ.isRinging()) { - Log.i(TAG, "interceptKeyTq:" - + " CALL key-down while ringing: Answer the call!"); - phoneServ.answerRingingCall(); - - // And *don't* pass this key thru to the current activity - // (which is presumably the InCallScreen.) - result &= ~ACTION_PASS_TO_USER; - } - } else { - Log.w(TAG, "CALL button: Unable to find ITelephony interface"); + } + } else if (isMediaKey(keyCode)) { + // This key needs to be handled even if the screen is off. + // If others need to be handled while it's off, this is a reasonable + // pattern to follow. + if ((result & ACTION_PASS_TO_USER) == 0) { + // Only do this if we would otherwise not pass it to the user. In that + // case, the PhoneWindow class will do the same thing, except it will + // only do it if the showing app doesn't process the key on its own. + long when = whenNanos / 1000000; + KeyEvent keyEvent = new KeyEvent(when, when, + down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, + keyCode, 0); + mBroadcastWakeLock.acquire(); + mHandler.post(new PassHeadsetKey(keyEvent)); + } + } else if (keyCode == KeyEvent.KEYCODE_CALL) { + // If an incoming call is ringing, answer it! + // (We handle this key here, rather than in the InCallScreen, to make + // sure we'll respond to the key even if the InCallScreen hasn't come to + // the foreground yet.) + + // We answer the call on the DOWN event, to agree with + // the "fallback" behavior in the InCallScreen. + if (down) { + try { + ITelephony phoneServ = getPhoneInterface(); + if (phoneServ != null) { + if (phoneServ.isRinging()) { + Log.i(TAG, "interceptKeyTq:" + + " CALL key-down while ringing: Answer the call!"); + phoneServ.answerRingingCall(); + + // And *don't* pass this key thru to the current activity + // (which is presumably the InCallScreen.) + result &= ~ACTION_PASS_TO_USER; } - } catch (RemoteException ex) { - Log.w(TAG, "CALL button: RemoteException from getPhoneInterface()", ex); + } else { + Log.w(TAG, "CALL button: Unable to find ITelephony interface"); } + } catch (RemoteException ex) { + Log.w(TAG, "CALL button: RemoteException from getPhoneInterface()", ex); } - } else if ((code == KeyEvent.KEYCODE_VOLUME_UP) - || (code == KeyEvent.KEYCODE_VOLUME_DOWN)) { - // If an incoming call is ringing, either VOLUME key means - // "silence ringer". We handle these keys here, rather than - // in the InCallScreen, to make sure we'll respond to them - // even if the InCallScreen hasn't come to the foreground yet. - - // Look for the DOWN event here, to agree with the "fallback" - // behavior in the InCallScreen. - if (down) { - try { - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - if (phoneServ.isRinging()) { - Log.i(TAG, "interceptKeyTq:" - + " VOLUME key-down while ringing: Silence ringer!"); - // Silence the ringer. (It's safe to call this - // even if the ringer has already been silenced.) - phoneServ.silenceRinger(); - - // And *don't* pass this key thru to the current activity - // (which is probably the InCallScreen.) - result &= ~ACTION_PASS_TO_USER; - } - } else { - Log.w(TAG, "VOLUME button: Unable to find ITelephony interface"); + } + } else if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) + || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) { + // If an incoming call is ringing, either VOLUME key means + // "silence ringer". We handle these keys here, rather than + // in the InCallScreen, to make sure we'll respond to them + // even if the InCallScreen hasn't come to the foreground yet. + + // Look for the DOWN event here, to agree with the "fallback" + // behavior in the InCallScreen. + if (down) { + try { + ITelephony phoneServ = getPhoneInterface(); + if (phoneServ != null) { + if (phoneServ.isRinging()) { + Log.i(TAG, "interceptKeyTq:" + + " VOLUME key-down while ringing: Silence ringer!"); + // Silence the ringer. (It's safe to call this + // even if the ringer has already been silenced.) + phoneServ.silenceRinger(); + + // And *don't* pass this key thru to the current activity + // (which is probably the InCallScreen.) + result &= ~ACTION_PASS_TO_USER; } - } catch (RemoteException ex) { - Log.w(TAG, "VOLUME button: RemoteException from getPhoneInterface()", ex); + } else { + Log.w(TAG, "VOLUME button: Unable to find ITelephony interface"); } + } catch (RemoteException ex) { + Log.w(TAG, "VOLUME button: RemoteException from getPhoneInterface()", ex); } } } @@ -2024,35 +1957,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { }; /** {@inheritDoc} */ - public boolean isWakeRelMovementTq(int device, int classes, - RawInputEvent event) { - // if it's tagged with one of the wake bits, it wakes up the device - return ((event.flags & (FLAG_WAKE | FLAG_WAKE_DROPPED)) != 0); - } - - /** {@inheritDoc} */ - public boolean isWakeAbsMovementTq(int device, int classes, - RawInputEvent event) { - // if it's tagged with one of the wake bits, it wakes up the device - return ((event.flags & (FLAG_WAKE | FLAG_WAKE_DROPPED)) != 0); - } - - /** - * Given the current state of the world, should this key wake up the device? - */ - protected boolean isWakeKeyTq(RawInputEvent event) { - // There are not key maps for trackball devices, but we'd still - // like to have pressing it wake the device up, so force it here. - int keycode = event.keycode; - int flags = event.flags; - if (keycode == RawInputEvent.BTN_MOUSE) { - flags |= WindowManagerPolicy.FLAG_WAKE; - } - return (flags - & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; - } - - /** {@inheritDoc} */ public void screenTurnedOff(int why) { EventLog.writeEvent(70000, 0); mKeyguardMediator.onScreenTurnedOff(why); @@ -2165,7 +2069,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int menuState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_MENU); int sState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_S); int dpadState = mWindowManager.getDPadKeycodeState(KeyEvent.KEYCODE_DPAD_CENTER); - int trackballState = mWindowManager.getTrackballScancodeState(RawInputEvent.BTN_MOUSE); + int trackballState = mWindowManager.getTrackballScancodeState(BTN_MOUSE); mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0; performHapticFeedbackLw(null, mSafeMode ? HapticFeedbackConstants.SAFE_MODE_ENABLED @@ -2413,13 +2317,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } - public void keyFeedbackFromInput(KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_DOWN - && (event.getFlags()&KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0) { - performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); - } - } - public void screenOnStoppedLw() { if (!mKeyguardMediator.isShowingAndNotHidden() && mPowerManager.isScreenOn()) { long curTime = SystemClock.uptimeMillis(); diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java deleted file mode 100644 index 414b69f..0000000 --- a/services/java/com/android/server/InputDevice.java +++ /dev/null @@ -1,1025 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.server; - -import android.util.Slog; -import android.view.Display; -import android.view.MotionEvent; -import android.view.Surface; -import android.view.WindowManagerPolicy; - -import java.io.PrintWriter; - -public class InputDevice { - static final boolean DEBUG_POINTERS = false; - static final boolean DEBUG_HACKS = false; - - /** Amount that trackball needs to move in order to generate a key event. */ - static final int TRACKBALL_MOVEMENT_THRESHOLD = 6; - - /** Maximum number of pointers we will track and report. */ - static final int MAX_POINTERS = 10; - - /** - * Slop distance for jumpy pointer detection. - * The vertical range of the screen divided by this is our epsilon value. - */ - private static final int JUMPY_EPSILON_DIVISOR = 212; - - /** Number of jumpy points to drop for touchscreens that need it. */ - private static final int JUMPY_TRANSITION_DROPS = 3; - private static final int JUMPY_DROP_LIMIT = 3; - - final int id; - final int classes; - final String name; - final AbsoluteInfo absX; - final AbsoluteInfo absY; - final AbsoluteInfo absPressure; - final AbsoluteInfo absSize; - - long mKeyDownTime = 0; - int mMetaKeysState = 0; - - // For use by KeyInputQueue for keeping track of the current touch - // data in the old non-multi-touch protocol. - final int[] curTouchVals = new int[MotionEvent.NUM_SAMPLE_DATA * 2]; - - final MotionState mAbs = new MotionState(0, 0); - final MotionState mRel = new MotionState(TRACKBALL_MOVEMENT_THRESHOLD, - TRACKBALL_MOVEMENT_THRESHOLD); - - static class MotionState { - int xPrecision; - int yPrecision; - float xMoveScale; - float yMoveScale; - MotionEvent currentMove = null; - boolean changed = false; - boolean everChanged = false; - long mDownTime = 0; - - // The currently assigned pointer IDs, corresponding to the last data. - int[] mPointerIds = new int[MAX_POINTERS]; - - // This is the last generated pointer data, ordered to match - // mPointerIds. - boolean mSkipLastPointers; - int mLastNumPointers = 0; - final int[] mLastData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS]; - - // This is the next set of pointer data being generated. It is not - // in any known order, and will be propagated in to mLastData - // as part of mapping it to the appropriate pointer IDs. - // Note that we have one extra sample of data here, to help clients - // avoid doing bounds checking. - int mNextNumPointers = 0; - final int[] mNextData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS) - + MotionEvent.NUM_SAMPLE_DATA]; - - // Used to determine whether we dropped bad data, to avoid doing - // it repeatedly. - final boolean[] mDroppedBadPoint = new boolean[MAX_POINTERS]; - - // Used to count the number of jumpy points dropped. - private int mJumpyPointsDropped = 0; - - // Used to perform averaging of reported coordinates, to smooth - // the data and filter out transients during a release. - static final int HISTORY_SIZE = 5; - int[] mHistoryDataStart = new int[MAX_POINTERS]; - int[] mHistoryDataEnd = new int[MAX_POINTERS]; - final int[] mHistoryData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS) - * HISTORY_SIZE]; - final int[] mAveragedData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS]; - - // Temporary data structures for doing the pointer ID mapping. - final int[] mLast2Next = new int[MAX_POINTERS]; - final int[] mNext2Last = new int[MAX_POINTERS]; - final long[] mNext2LastDistance = new long[MAX_POINTERS]; - - // Temporary data structure for generating the final motion data. - final float[] mReportData = new float[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS]; - - // This is not used here, but can be used by callers for state tracking. - int mAddingPointerOffset = 0; - final boolean[] mDown = new boolean[MAX_POINTERS]; - - void dumpIntArray(PrintWriter pw, int[] array) { - pw.print("["); - for (int i=0; i<array.length; i++) { - if (i > 0) pw.print(", "); - pw.print(array[i]); - } - pw.print("]"); - } - - void dumpBooleanArray(PrintWriter pw, boolean[] array) { - pw.print("["); - for (int i=0; i<array.length; i++) { - if (i > 0) pw.print(", "); - pw.print(array[i] ? "true" : "false"); - } - pw.print("]"); - } - - void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("xPrecision="); pw.print(xPrecision); - pw.print(" yPrecision="); pw.println(yPrecision); - pw.print(prefix); pw.print("xMoveScale="); pw.print(xMoveScale); - pw.print(" yMoveScale="); pw.println(yMoveScale); - if (currentMove != null) { - pw.print(prefix); pw.print("currentMove="); pw.println(currentMove); - } - if (changed || mDownTime != 0) { - pw.print(prefix); pw.print("changed="); pw.print(changed); - pw.print(" mDownTime="); pw.println(mDownTime); - } - pw.print(prefix); pw.print("mPointerIds="); dumpIntArray(pw, mPointerIds); - pw.println(""); - if (mSkipLastPointers || mLastNumPointers != 0) { - pw.print(prefix); pw.print("mSkipLastPointers="); pw.print(mSkipLastPointers); - pw.print(" mLastNumPointers="); pw.println(mLastNumPointers); - pw.print(prefix); pw.print("mLastData="); dumpIntArray(pw, mLastData); - pw.println(""); - } - if (mNextNumPointers != 0) { - pw.print(prefix); pw.print("mNextNumPointers="); pw.println(mNextNumPointers); - pw.print(prefix); pw.print("mNextData="); dumpIntArray(pw, mNextData); - pw.println(""); - } - pw.print(prefix); pw.print("mDroppedBadPoint="); - dumpBooleanArray(pw, mDroppedBadPoint); pw.println(""); - pw.print(prefix); pw.print("mAddingPointerOffset="); pw.println(mAddingPointerOffset); - pw.print(prefix); pw.print("mDown="); - dumpBooleanArray(pw, mDown); pw.println(""); - } - - MotionState(int mx, int my) { - xPrecision = mx; - yPrecision = my; - xMoveScale = mx != 0 ? (1.0f/mx) : 1.0f; - yMoveScale = my != 0 ? (1.0f/my) : 1.0f; - for (int i=0; i<MAX_POINTERS; i++) { - mPointerIds[i] = i; - } - } - - /** - * Special hack for devices that have bad screen data: if one of the - * points has moved more than a screen height from the last position, - * then drop it. - */ - void dropBadPoint(InputDevice dev) { - // We should always have absY, but let's be paranoid. - if (dev.absY == null) { - return; - } - // Don't do anything if a finger is going down or up. We run - // here before assigning pointer IDs, so there isn't a good - // way to do per-finger matching. - if (mNextNumPointers != mLastNumPointers) { - return; - } - - // We consider a single movement across more than a 7/16 of - // the long size of the screen to be bad. This was a magic value - // determined by looking at the maximum distance it is feasible - // to actually move in one sample. - final int maxDy = ((dev.absY.maxValue-dev.absY.minValue)*7)/16; - - // Look through all new points and see if any are farther than - // acceptable from all previous points. - for (int i=mNextNumPointers-1; i>=0; i--) { - final int ioff = i * MotionEvent.NUM_SAMPLE_DATA; - //final int x = mNextData[ioff + MotionEvent.SAMPLE_X]; - final int y = mNextData[ioff + MotionEvent.SAMPLE_Y]; - if (DEBUG_HACKS) Slog.v("InputDevice", "Looking at next point #" + i + ": y=" + y); - boolean dropped = false; - if (!mDroppedBadPoint[i] && mLastNumPointers > 0) { - dropped = true; - int closestDy = -1; - int closestY = -1; - // We will drop this new point if it is sufficiently - // far away from -all- last points. - for (int j=mLastNumPointers-1; j>=0; j--) { - final int joff = j * MotionEvent.NUM_SAMPLE_DATA; - //int dx = x - mLastData[joff + MotionEvent.SAMPLE_X]; - int dy = y - mLastData[joff + MotionEvent.SAMPLE_Y]; - //if (dx < 0) dx = -dx; - if (dy < 0) dy = -dy; - if (DEBUG_HACKS) Slog.v("InputDevice", "Comparing with last point #" + j - + ": y=" + mLastData[joff] + " dy=" + dy); - if (dy < maxDy) { - dropped = false; - break; - } else if (closestDy < 0 || dy < closestDy) { - closestDy = dy; - closestY = mLastData[joff + MotionEvent.SAMPLE_Y]; - } - } - if (dropped) { - dropped = true; - Slog.i("InputDevice", "Dropping bad point #" + i - + ": newY=" + y + " closestDy=" + closestDy - + " maxDy=" + maxDy); - mNextData[ioff + MotionEvent.SAMPLE_Y] = closestY; - break; - } - } - mDroppedBadPoint[i] = dropped; - } - } - - void dropJumpyPoint(InputDevice dev) { - // We should always have absY, but let's be paranoid. - if (dev.absY == null) { - return; - } - final int jumpyEpsilon = dev.absY.range / JUMPY_EPSILON_DIVISOR; - - final int nextNumPointers = mNextNumPointers; - final int lastNumPointers = mLastNumPointers; - final int[] nextData = mNextData; - final int[] lastData = mLastData; - - if (nextNumPointers != mLastNumPointers) { - if (DEBUG_HACKS) { - Slog.d("InputDevice", "Different pointer count " + lastNumPointers + - " -> " + nextNumPointers); - for (int i = 0; i < nextNumPointers; i++) { - int ioff = i * MotionEvent.NUM_SAMPLE_DATA; - Slog.d("InputDevice", "Pointer " + i + " (" + - mNextData[ioff + MotionEvent.SAMPLE_X] + ", " + - mNextData[ioff + MotionEvent.SAMPLE_Y] + ")"); - } - } - - // Just drop the first few events going from 1 to 2 pointers. - // They're bad often enough that they're not worth considering. - if (lastNumPointers == 1 && nextNumPointers == 2 - && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) { - mNextNumPointers = 1; - mJumpyPointsDropped++; - } else if (lastNumPointers == 2 && nextNumPointers == 1 - && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) { - // The event when we go from 2 -> 1 tends to be messed up too - System.arraycopy(lastData, 0, nextData, 0, - lastNumPointers * MotionEvent.NUM_SAMPLE_DATA); - mNextNumPointers = lastNumPointers; - mJumpyPointsDropped++; - - if (DEBUG_HACKS) { - for (int i = 0; i < mNextNumPointers; i++) { - int ioff = i * MotionEvent.NUM_SAMPLE_DATA; - Slog.d("InputDevice", "Pointer " + i + " replaced (" + - mNextData[ioff + MotionEvent.SAMPLE_X] + ", " + - mNextData[ioff + MotionEvent.SAMPLE_Y] + ")"); - } - } - } else { - mJumpyPointsDropped = 0; - - if (DEBUG_HACKS) { - Slog.d("InputDevice", "Transition - drop limit reset"); - } - } - return; - } - - // A 'jumpy' point is one where the coordinate value for one axis - // has jumped to the other pointer's location. No need to do anything - // else if we only have one pointer. - if (nextNumPointers < 2) { - return; - } - - int badPointerIndex = -1; - int badPointerReplaceXWith = 0; - int badPointerReplaceYWith = 0; - int badPointerDistance = Integer.MIN_VALUE; - for (int i = nextNumPointers - 1; i >= 0; i--) { - boolean dropx = false; - boolean dropy = false; - - // Limit how many times a jumpy point can get dropped. - if (mJumpyPointsDropped < JUMPY_DROP_LIMIT) { - final int ioff = i * MotionEvent.NUM_SAMPLE_DATA; - final int x = nextData[ioff + MotionEvent.SAMPLE_X]; - final int y = nextData[ioff + MotionEvent.SAMPLE_Y]; - - if (DEBUG_HACKS) { - Slog.d("InputDevice", "Point " + i + " (" + x + ", " + y + ")"); - } - - // Check if a touch point is too close to another's coordinates - for (int j = 0; j < nextNumPointers && !dropx && !dropy; j++) { - if (j == i) { - continue; - } - - final int joff = j * MotionEvent.NUM_SAMPLE_DATA; - final int xOther = nextData[joff + MotionEvent.SAMPLE_X]; - final int yOther = nextData[joff + MotionEvent.SAMPLE_Y]; - - dropx = Math.abs(x - xOther) <= jumpyEpsilon; - dropy = Math.abs(y - yOther) <= jumpyEpsilon; - } - - if (dropx) { - int xreplace = lastData[MotionEvent.SAMPLE_X]; - int yreplace = lastData[MotionEvent.SAMPLE_Y]; - int distance = Math.abs(yreplace - y); - for (int j = 1; j < lastNumPointers; j++) { - final int joff = j * MotionEvent.NUM_SAMPLE_DATA; - int lasty = lastData[joff + MotionEvent.SAMPLE_Y]; - int currDist = Math.abs(lasty - y); - if (currDist < distance) { - xreplace = lastData[joff + MotionEvent.SAMPLE_X]; - yreplace = lasty; - distance = currDist; - } - } - - int badXDelta = Math.abs(xreplace - x); - if (badXDelta > badPointerDistance) { - badPointerDistance = badXDelta; - badPointerIndex = i; - badPointerReplaceXWith = xreplace; - badPointerReplaceYWith = yreplace; - } - } else if (dropy) { - int xreplace = lastData[MotionEvent.SAMPLE_X]; - int yreplace = lastData[MotionEvent.SAMPLE_Y]; - int distance = Math.abs(xreplace - x); - for (int j = 1; j < lastNumPointers; j++) { - final int joff = j * MotionEvent.NUM_SAMPLE_DATA; - int lastx = lastData[joff + MotionEvent.SAMPLE_X]; - int currDist = Math.abs(lastx - x); - if (currDist < distance) { - xreplace = lastx; - yreplace = lastData[joff + MotionEvent.SAMPLE_Y]; - distance = currDist; - } - } - - int badYDelta = Math.abs(yreplace - y); - if (badYDelta > badPointerDistance) { - badPointerDistance = badYDelta; - badPointerIndex = i; - badPointerReplaceXWith = xreplace; - badPointerReplaceYWith = yreplace; - } - } - } - } - if (badPointerIndex >= 0) { - if (DEBUG_HACKS) { - Slog.d("InputDevice", "Replacing bad pointer " + badPointerIndex + - " with (" + badPointerReplaceXWith + ", " + badPointerReplaceYWith + - ")"); - } - - final int offset = badPointerIndex * MotionEvent.NUM_SAMPLE_DATA; - nextData[offset + MotionEvent.SAMPLE_X] = badPointerReplaceXWith; - nextData[offset + MotionEvent.SAMPLE_Y] = badPointerReplaceYWith; - mJumpyPointsDropped++; - } else { - mJumpyPointsDropped = 0; - } - } - - /** - * Special hack for devices that have bad screen data: aggregate and - * compute averages of the coordinate data, to reduce the amount of - * jitter seen by applications. - */ - int[] generateAveragedData(int upOrDownPointer, int lastNumPointers, - int nextNumPointers) { - final int numPointers = mLastNumPointers; - final int[] rawData = mLastData; - if (DEBUG_HACKS) Slog.v("InputDevice", "lastNumPointers=" + lastNumPointers - + " nextNumPointers=" + nextNumPointers - + " numPointers=" + numPointers); - for (int i=0; i<numPointers; i++) { - final int ioff = i * MotionEvent.NUM_SAMPLE_DATA; - // We keep the average data in offsets based on the pointer - // ID, so we don't need to move it around as fingers are - // pressed and released. - final int p = mPointerIds[i]; - final int poff = p * MotionEvent.NUM_SAMPLE_DATA * HISTORY_SIZE; - if (i == upOrDownPointer && lastNumPointers != nextNumPointers) { - if (lastNumPointers < nextNumPointers) { - // This pointer is going down. Clear its history - // and start fresh. - if (DEBUG_HACKS) Slog.v("InputDevice", "Pointer down @ index " - + upOrDownPointer + " id " + mPointerIds[i]); - mHistoryDataStart[i] = 0; - mHistoryDataEnd[i] = 0; - System.arraycopy(rawData, ioff, mHistoryData, poff, - MotionEvent.NUM_SAMPLE_DATA); - System.arraycopy(rawData, ioff, mAveragedData, ioff, - MotionEvent.NUM_SAMPLE_DATA); - continue; - } else { - // The pointer is going up. Just fall through to - // recompute the last averaged point (and don't add - // it as a new point to include in the average). - if (DEBUG_HACKS) Slog.v("InputDevice", "Pointer up @ index " - + upOrDownPointer + " id " + mPointerIds[i]); - } - } else { - int end = mHistoryDataEnd[i]; - int eoff = poff + (end*MotionEvent.NUM_SAMPLE_DATA); - int oldX = mHistoryData[eoff + MotionEvent.SAMPLE_X]; - int oldY = mHistoryData[eoff + MotionEvent.SAMPLE_Y]; - int newX = rawData[ioff + MotionEvent.SAMPLE_X]; - int newY = rawData[ioff + MotionEvent.SAMPLE_Y]; - int dx = newX-oldX; - int dy = newY-oldY; - int delta = dx*dx + dy*dy; - if (DEBUG_HACKS) Slog.v("InputDevice", "Delta from last: " + delta); - if (delta >= (75*75)) { - // Magic number, if moving farther than this, turn - // off filtering to avoid lag in response. - mHistoryDataStart[i] = 0; - mHistoryDataEnd[i] = 0; - System.arraycopy(rawData, ioff, mHistoryData, poff, - MotionEvent.NUM_SAMPLE_DATA); - System.arraycopy(rawData, ioff, mAveragedData, ioff, - MotionEvent.NUM_SAMPLE_DATA); - continue; - } else { - end++; - if (end >= HISTORY_SIZE) { - end -= HISTORY_SIZE; - } - mHistoryDataEnd[i] = end; - int noff = poff + (end*MotionEvent.NUM_SAMPLE_DATA); - mHistoryData[noff + MotionEvent.SAMPLE_X] = newX; - mHistoryData[noff + MotionEvent.SAMPLE_Y] = newY; - mHistoryData[noff + MotionEvent.SAMPLE_PRESSURE] - = rawData[ioff + MotionEvent.SAMPLE_PRESSURE]; - int start = mHistoryDataStart[i]; - if (end == start) { - start++; - if (start >= HISTORY_SIZE) { - start -= HISTORY_SIZE; - } - mHistoryDataStart[i] = start; - } - } - } - - // Now compute the average. - int start = mHistoryDataStart[i]; - int end = mHistoryDataEnd[i]; - int x=0, y=0; - int totalPressure = 0; - while (start != end) { - int soff = poff + (start*MotionEvent.NUM_SAMPLE_DATA); - int pressure = mHistoryData[soff + MotionEvent.SAMPLE_PRESSURE]; - if (pressure <= 0) pressure = 1; - x += mHistoryData[soff + MotionEvent.SAMPLE_X] * pressure; - y += mHistoryData[soff + MotionEvent.SAMPLE_Y] * pressure; - totalPressure += pressure; - start++; - if (start >= HISTORY_SIZE) start = 0; - } - int eoff = poff + (end*MotionEvent.NUM_SAMPLE_DATA); - int pressure = mHistoryData[eoff + MotionEvent.SAMPLE_PRESSURE]; - if (pressure <= 0) pressure = 1; - x += mHistoryData[eoff + MotionEvent.SAMPLE_X] * pressure; - y += mHistoryData[eoff + MotionEvent.SAMPLE_Y] * pressure; - totalPressure += pressure; - x /= totalPressure; - y /= totalPressure; - if (DEBUG_HACKS) Slog.v("InputDevice", "Averaging " + totalPressure - + " weight: (" + x + "," + y + ")"); - mAveragedData[ioff + MotionEvent.SAMPLE_X] = x; - mAveragedData[ioff + MotionEvent.SAMPLE_Y] = y; - mAveragedData[ioff + MotionEvent.SAMPLE_PRESSURE] = - rawData[ioff + MotionEvent.SAMPLE_PRESSURE]; - mAveragedData[ioff + MotionEvent.SAMPLE_SIZE] = - rawData[ioff + MotionEvent.SAMPLE_SIZE]; - } - return mAveragedData; - } - - private boolean assignPointer(int nextIndex, boolean allowOverlap) { - final int lastNumPointers = mLastNumPointers; - final int[] next2Last = mNext2Last; - final long[] next2LastDistance = mNext2LastDistance; - final int[] last2Next = mLast2Next; - final int[] lastData = mLastData; - final int[] nextData = mNextData; - final int id = nextIndex * MotionEvent.NUM_SAMPLE_DATA; - - if (DEBUG_POINTERS) Slog.v("InputDevice", "assignPointer: nextIndex=" - + nextIndex + " dataOff=" + id); - final int x1 = nextData[id + MotionEvent.SAMPLE_X]; - final int y1 = nextData[id + MotionEvent.SAMPLE_Y]; - - long bestDistance = -1; - int bestIndex = -1; - for (int j=0; j<lastNumPointers; j++) { - // If we are not allowing multiple new points to be assigned - // to the same old pointer, then skip this one if it is already - // detected as a conflict (-2). - if (!allowOverlap && last2Next[j] < -1) { - continue; - } - final int jd = j * MotionEvent.NUM_SAMPLE_DATA; - final int xd = lastData[jd + MotionEvent.SAMPLE_X] - x1; - final int yd = lastData[jd + MotionEvent.SAMPLE_Y] - y1; - final long distance = xd*(long)xd + yd*(long)yd; - if (bestDistance == -1 || distance < bestDistance) { - bestDistance = distance; - bestIndex = j; - } - } - - if (DEBUG_POINTERS) Slog.v("InputDevice", "New index " + nextIndex - + " best old index=" + bestIndex + " (distance=" - + bestDistance + ")"); - next2Last[nextIndex] = bestIndex; - next2LastDistance[nextIndex] = bestDistance; - - if (bestIndex < 0) { - return true; - } - - if (last2Next[bestIndex] == -1) { - last2Next[bestIndex] = nextIndex; - return false; - } - - if (DEBUG_POINTERS) Slog.v("InputDevice", "Old index " + bestIndex - + " has multiple best new pointers!"); - - last2Next[bestIndex] = -2; - return true; - } - - private int updatePointerIdentifiers() { - final int[] lastData = mLastData; - final int[] nextData = mNextData; - final int nextNumPointers = mNextNumPointers; - final int lastNumPointers = mLastNumPointers; - - if (nextNumPointers == 1 && lastNumPointers == 1) { - System.arraycopy(nextData, 0, lastData, 0, - MotionEvent.NUM_SAMPLE_DATA); - return -1; - } - - // Clear our old state. - final int[] last2Next = mLast2Next; - for (int i=0; i<lastNumPointers; i++) { - last2Next[i] = -1; - } - - if (DEBUG_POINTERS) Slog.v("InputDevice", - "Update pointers: lastNumPointers=" + lastNumPointers - + " nextNumPointers=" + nextNumPointers); - - // Figure out the closes new points to the previous points. - final int[] next2Last = mNext2Last; - final long[] next2LastDistance = mNext2LastDistance; - boolean conflicts = false; - for (int i=0; i<nextNumPointers; i++) { - conflicts |= assignPointer(i, true); - } - - // Resolve ambiguities in pointer mappings, when two or more - // new pointer locations find their best previous location is - // the same. - if (conflicts) { - if (DEBUG_POINTERS) Slog.v("InputDevice", "Resolving conflicts"); - - for (int i=0; i<lastNumPointers; i++) { - if (last2Next[i] != -2) { - continue; - } - - // Note that this algorithm is far from perfect. Ideally - // we should do something like the one described at - // http://portal.acm.org/citation.cfm?id=997856 - - if (DEBUG_POINTERS) Slog.v("InputDevice", - "Resolving last index #" + i); - - int numFound; - do { - numFound = 0; - long worstDistance = 0; - int worstJ = -1; - for (int j=0; j<nextNumPointers; j++) { - if (next2Last[j] != i) { - continue; - } - numFound++; - if (worstDistance < next2LastDistance[j]) { - worstDistance = next2LastDistance[j]; - worstJ = j; - } - } - - if (worstJ >= 0) { - if (DEBUG_POINTERS) Slog.v("InputDevice", - "Worst new pointer: " + worstJ - + " (distance=" + worstDistance + ")"); - if (assignPointer(worstJ, false)) { - // In this case there is no last pointer - // remaining for this new one! - next2Last[worstJ] = -1; - } - } - } while (numFound > 2); - } - } - - int retIndex = -1; - - if (lastNumPointers < nextNumPointers) { - // We have one or more new pointers that are down. Create a - // new pointer identifier for one of them. - if (DEBUG_POINTERS) Slog.v("InputDevice", "Adding new pointer"); - int nextId = 0; - int i=0; - while (i < lastNumPointers) { - if (mPointerIds[i] > nextId) { - // Found a hole, insert the pointer here. - if (DEBUG_POINTERS) Slog.v("InputDevice", - "Inserting new pointer at hole " + i); - System.arraycopy(mPointerIds, i, mPointerIds, - i+1, lastNumPointers-i); - System.arraycopy(lastData, i*MotionEvent.NUM_SAMPLE_DATA, - lastData, (i+1)*MotionEvent.NUM_SAMPLE_DATA, - (lastNumPointers-i)*MotionEvent.NUM_SAMPLE_DATA); - System.arraycopy(next2Last, i, next2Last, - i+1, lastNumPointers-i); - break; - } - i++; - nextId++; - } - - if (DEBUG_POINTERS) Slog.v("InputDevice", - "New pointer id " + nextId + " at index " + i); - - mLastNumPointers++; - retIndex = i; - mPointerIds[i] = nextId; - - // And assign this identifier to the first new pointer. - for (int j=0; j<nextNumPointers; j++) { - if (next2Last[j] < 0) { - if (DEBUG_POINTERS) Slog.v("InputDevice", - "Assigning new id to new pointer index " + j); - next2Last[j] = i; - break; - } - } - } - - // Propagate all of the current data into the appropriate - // location in the old data to match the pointer ID that was - // assigned to it. - for (int i=0; i<nextNumPointers; i++) { - int lastIndex = next2Last[i]; - if (lastIndex >= 0) { - if (DEBUG_POINTERS) Slog.v("InputDevice", - "Copying next pointer index " + i - + " to last index " + lastIndex); - System.arraycopy(nextData, i*MotionEvent.NUM_SAMPLE_DATA, - lastData, lastIndex*MotionEvent.NUM_SAMPLE_DATA, - MotionEvent.NUM_SAMPLE_DATA); - } - } - - if (lastNumPointers > nextNumPointers) { - // One or more pointers has gone up. Find the first one, - // and adjust accordingly. - if (DEBUG_POINTERS) Slog.v("InputDevice", "Removing old pointer"); - for (int i=0; i<lastNumPointers; i++) { - if (last2Next[i] == -1) { - if (DEBUG_POINTERS) Slog.v("InputDevice", - "Removing old pointer at index " + i); - retIndex = i; - break; - } - } - } - - return retIndex; - } - - void removeOldPointer(int index) { - final int lastNumPointers = mLastNumPointers; - if (index >= 0 && index < lastNumPointers) { - System.arraycopy(mPointerIds, index+1, mPointerIds, - index, lastNumPointers-index-1); - System.arraycopy(mLastData, (index+1)*MotionEvent.NUM_SAMPLE_DATA, - mLastData, (index)*MotionEvent.NUM_SAMPLE_DATA, - (lastNumPointers-index-1)*MotionEvent.NUM_SAMPLE_DATA); - mLastNumPointers--; - } - } - - MotionEvent generateAbsMotion(InputDevice device, long curTime, - long curTimeNano, Display display, int orientation, - int metaState) { - - if (mSkipLastPointers) { - mSkipLastPointers = false; - mLastNumPointers = 0; - } - - if (mNextNumPointers <= 0 && mLastNumPointers <= 0) { - return null; - } - - final int lastNumPointers = mLastNumPointers; - final int nextNumPointers = mNextNumPointers; - if (mNextNumPointers > MAX_POINTERS) { - Slog.w("InputDevice", "Number of pointers " + mNextNumPointers - + " exceeded maximum of " + MAX_POINTERS); - mNextNumPointers = MAX_POINTERS; - } - - int upOrDownPointer = updatePointerIdentifiers(); - - final float[] reportData = mReportData; - final int[] rawData; - if (KeyInputQueue.BAD_TOUCH_HACK) { - rawData = generateAveragedData(upOrDownPointer, lastNumPointers, - nextNumPointers); - } else { - rawData = mLastData; - } - - final int numPointers = mLastNumPointers; - - if (DEBUG_POINTERS) Slog.v("InputDevice", "Processing " - + numPointers + " pointers (going from " + lastNumPointers - + " to " + nextNumPointers + ")"); - - for (int i=0; i<numPointers; i++) { - final int pos = i * MotionEvent.NUM_SAMPLE_DATA; - reportData[pos + MotionEvent.SAMPLE_X] = rawData[pos + MotionEvent.SAMPLE_X]; - reportData[pos + MotionEvent.SAMPLE_Y] = rawData[pos + MotionEvent.SAMPLE_Y]; - reportData[pos + MotionEvent.SAMPLE_PRESSURE] = rawData[pos + MotionEvent.SAMPLE_PRESSURE]; - reportData[pos + MotionEvent.SAMPLE_SIZE] = rawData[pos + MotionEvent.SAMPLE_SIZE]; - } - - int action; - int edgeFlags = 0; - if (nextNumPointers != lastNumPointers) { - if (nextNumPointers > lastNumPointers) { - if (lastNumPointers == 0) { - action = MotionEvent.ACTION_DOWN; - mDownTime = curTime; - } else { - action = MotionEvent.ACTION_POINTER_DOWN - | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT); - } - } else { - if (numPointers == 1) { - action = MotionEvent.ACTION_UP; - } else { - action = MotionEvent.ACTION_POINTER_UP - | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT); - } - } - currentMove = null; - } else { - action = MotionEvent.ACTION_MOVE; - } - - final int dispW = display.getWidth()-1; - final int dispH = display.getHeight()-1; - int w = dispW; - int h = dispH; - if (orientation == Surface.ROTATION_90 - || orientation == Surface.ROTATION_270) { - int tmp = w; - w = h; - h = tmp; - } - - final AbsoluteInfo absX = device.absX; - final AbsoluteInfo absY = device.absY; - final AbsoluteInfo absPressure = device.absPressure; - final AbsoluteInfo absSize = device.absSize; - for (int i=0; i<numPointers; i++) { - final int j = i * MotionEvent.NUM_SAMPLE_DATA; - - if (absX != null) { - reportData[j + MotionEvent.SAMPLE_X] = - ((reportData[j + MotionEvent.SAMPLE_X]-absX.minValue) - / absX.range) * w; - } - if (absY != null) { - reportData[j + MotionEvent.SAMPLE_Y] = - ((reportData[j + MotionEvent.SAMPLE_Y]-absY.minValue) - / absY.range) * h; - } - if (absPressure != null) { - reportData[j + MotionEvent.SAMPLE_PRESSURE] = - ((reportData[j + MotionEvent.SAMPLE_PRESSURE]-absPressure.minValue) - / (float)absPressure.range); - } - if (absSize != null) { - reportData[j + MotionEvent.SAMPLE_SIZE] = - ((reportData[j + MotionEvent.SAMPLE_SIZE]-absSize.minValue) - / (float)absSize.range); - } - - switch (orientation) { - case Surface.ROTATION_90: { - final float temp = reportData[j + MotionEvent.SAMPLE_X]; - reportData[j + MotionEvent.SAMPLE_X] = reportData[j + MotionEvent.SAMPLE_Y]; - reportData[j + MotionEvent.SAMPLE_Y] = w-temp; - break; - } - case Surface.ROTATION_180: { - reportData[j + MotionEvent.SAMPLE_X] = w-reportData[j + MotionEvent.SAMPLE_X]; - reportData[j + MotionEvent.SAMPLE_Y] = h-reportData[j + MotionEvent.SAMPLE_Y]; - break; - } - case Surface.ROTATION_270: { - final float temp = reportData[j + MotionEvent.SAMPLE_X]; - reportData[j + MotionEvent.SAMPLE_X] = h-reportData[j + MotionEvent.SAMPLE_Y]; - reportData[j + MotionEvent.SAMPLE_Y] = temp; - break; - } - } - } - - // We only consider the first pointer when computing the edge - // flags, since they are global to the event. - if (action == MotionEvent.ACTION_DOWN) { - if (reportData[MotionEvent.SAMPLE_X] <= 0) { - edgeFlags |= MotionEvent.EDGE_LEFT; - } else if (reportData[MotionEvent.SAMPLE_X] >= dispW) { - edgeFlags |= MotionEvent.EDGE_RIGHT; - } - if (reportData[MotionEvent.SAMPLE_Y] <= 0) { - edgeFlags |= MotionEvent.EDGE_TOP; - } else if (reportData[MotionEvent.SAMPLE_Y] >= dispH) { - edgeFlags |= MotionEvent.EDGE_BOTTOM; - } - } - - if (currentMove != null) { - if (false) Slog.i("InputDevice", "Adding batch x=" - + reportData[MotionEvent.SAMPLE_X] - + " y=" + reportData[MotionEvent.SAMPLE_Y] - + " to " + currentMove); - currentMove.addBatch(curTime, reportData, metaState); - if (WindowManagerPolicy.WATCH_POINTER) { - Slog.i("KeyInputQueue", "Updating: " + currentMove); - } - return null; - } - - MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime, - curTimeNano, action, numPointers, mPointerIds, reportData, - metaState, xPrecision, yPrecision, device.id, edgeFlags); - if (action == MotionEvent.ACTION_MOVE) { - currentMove = me; - } - - if (nextNumPointers < lastNumPointers) { - removeOldPointer(upOrDownPointer); - } - - return me; - } - - boolean hasMore() { - return mLastNumPointers != mNextNumPointers; - } - - void finish() { - mNextNumPointers = mAddingPointerOffset = 0; - mNextData[MotionEvent.SAMPLE_PRESSURE] = 0; - } - - MotionEvent generateRelMotion(InputDevice device, long curTime, - long curTimeNano, int orientation, int metaState) { - - final float[] scaled = mReportData; - - // For now we only support 1 pointer with relative motions. - scaled[MotionEvent.SAMPLE_X] = mNextData[MotionEvent.SAMPLE_X]; - scaled[MotionEvent.SAMPLE_Y] = mNextData[MotionEvent.SAMPLE_Y]; - scaled[MotionEvent.SAMPLE_PRESSURE] = 1.0f; - scaled[MotionEvent.SAMPLE_SIZE] = 0; - int edgeFlags = 0; - - int action; - if (mNextNumPointers != mLastNumPointers) { - mNextData[MotionEvent.SAMPLE_X] = - mNextData[MotionEvent.SAMPLE_Y] = 0; - if (mNextNumPointers > 0 && mLastNumPointers == 0) { - action = MotionEvent.ACTION_DOWN; - mDownTime = curTime; - } else if (mNextNumPointers == 0) { - action = MotionEvent.ACTION_UP; - } else { - action = MotionEvent.ACTION_MOVE; - } - mLastNumPointers = mNextNumPointers; - currentMove = null; - } else { - action = MotionEvent.ACTION_MOVE; - } - - scaled[MotionEvent.SAMPLE_X] *= xMoveScale; - scaled[MotionEvent.SAMPLE_Y] *= yMoveScale; - switch (orientation) { - case Surface.ROTATION_90: { - final float temp = scaled[MotionEvent.SAMPLE_X]; - scaled[MotionEvent.SAMPLE_X] = scaled[MotionEvent.SAMPLE_Y]; - scaled[MotionEvent.SAMPLE_Y] = -temp; - break; - } - case Surface.ROTATION_180: { - scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_X]; - scaled[MotionEvent.SAMPLE_Y] = -scaled[MotionEvent.SAMPLE_Y]; - break; - } - case Surface.ROTATION_270: { - final float temp = scaled[MotionEvent.SAMPLE_X]; - scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_Y]; - scaled[MotionEvent.SAMPLE_Y] = temp; - break; - } - } - - if (currentMove != null) { - if (false) Slog.i("InputDevice", "Adding batch x=" - + scaled[MotionEvent.SAMPLE_X] - + " y=" + scaled[MotionEvent.SAMPLE_Y] - + " to " + currentMove); - currentMove.addBatch(curTime, scaled, metaState); - if (WindowManagerPolicy.WATCH_POINTER) { - Slog.i("KeyInputQueue", "Updating: " + currentMove); - } - return null; - } - - MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime, - curTimeNano, action, 1, mPointerIds, scaled, metaState, - xPrecision, yPrecision, device.id, edgeFlags); - if (action == MotionEvent.ACTION_MOVE) { - currentMove = me; - } - return me; - } - } - - static class AbsoluteInfo { - int minValue; - int maxValue; - int range; - int flat; - int fuzz; - - final void dump(PrintWriter pw) { - pw.print("minValue="); pw.print(minValue); - pw.print(" maxValue="); pw.print(maxValue); - pw.print(" range="); pw.print(range); - pw.print(" flat="); pw.print(flat); - pw.print(" fuzz="); pw.print(fuzz); - } - }; - - InputDevice(int _id, int _classes, String _name, - AbsoluteInfo _absX, AbsoluteInfo _absY, - AbsoluteInfo _absPressure, AbsoluteInfo _absSize) { - id = _id; - classes = _classes; - name = _name; - absX = _absX; - absY = _absY; - absPressure = _absPressure; - absSize = _absSize; - } -}; diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index 2ba2914..cdae27c 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -56,7 +56,6 @@ public class InputManager { private final Callbacks mCallbacks; private final Context mContext; private final WindowManagerService mWindowManagerService; - private final WindowManagerPolicy mWindowManagerPolicy; private final PowerManager mPowerManager; private final PowerManagerService mPowerManagerService; @@ -103,12 +102,10 @@ public class InputManager { public InputManager(Context context, WindowManagerService windowManagerService, - WindowManagerPolicy windowManagerPolicy, PowerManager powerManager, PowerManagerService powerManagerService) { this.mContext = context; this.mWindowManagerService = windowManagerService; - this.mWindowManagerPolicy = windowManagerPolicy; this.mPowerManager = powerManager; this.mPowerManagerService = powerManagerService; @@ -325,23 +322,8 @@ public class InputManager { private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; @SuppressWarnings("unused") - public boolean isScreenOn() { - return mPowerManagerService.isScreenOn(); - } - - @SuppressWarnings("unused") - public boolean isScreenBright() { - return mPowerManagerService.isScreenBright(); - } - - @SuppressWarnings("unused") - public void virtualKeyFeedback(long whenNanos, int deviceId, int action, int flags, - int keyCode, int scanCode, int metaState, long downTimeNanos) { - KeyEvent keyEvent = new KeyEvent(downTimeNanos / 1000000, - whenNanos / 1000000, action, keyCode, 0, metaState, scanCode, deviceId, - flags); - - mWindowManagerService.virtualKeyFeedback(keyEvent); + public void virtualKeyDownFeedback() { + mWindowManagerService.mInputMonitor.virtualKeyDownFeedback(); } @SuppressWarnings("unused") @@ -356,7 +338,7 @@ public class InputManager { @SuppressWarnings("unused") public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { - mWindowManagerPolicy.notifyLidSwitchChanged(whenNanos, lidOpen); + mWindowManagerService.mInputMonitor.notifyLidSwitchChanged(whenNanos, lidOpen); } @SuppressWarnings("unused") @@ -380,17 +362,17 @@ public class InputManager { } @SuppressWarnings("unused") - public int interceptKeyBeforeQueueing(int deviceId, int type, int scanCode, - int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) { - return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(deviceId, type, - scanCode, keyCode, policyFlags, value, whenNanos, isScreenOn); + public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, + int policyFlags, boolean isScreenOn) { + return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing( + whenNanos, keyCode, down, policyFlags, isScreenOn); } @SuppressWarnings("unused") - public boolean interceptKeyBeforeDispatching(InputChannel focus, int keyCode, - int metaState, boolean down, int repeatCount, int policyFlags) { + public boolean interceptKeyBeforeDispatching(InputChannel focus, int action, + int flags, int keyCode, int metaState, int repeatCount, int policyFlags) { return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus, - keyCode, metaState, down, repeatCount, policyFlags); + action, flags, keyCode, metaState, repeatCount, policyFlags); } @SuppressWarnings("unused") @@ -401,18 +383,6 @@ public class InputManager { } @SuppressWarnings("unused") - public void goToSleep(long whenNanos) { - long when = whenNanos / 1000000; - mPowerManager.goToSleep(when); - } - - @SuppressWarnings("unused") - public void pokeUserActivity(long eventTimeNanos, int eventType) { - long eventTime = eventTimeNanos / 1000000; - mPowerManagerService.userActivity(eventTime, false, eventType, false); - } - - @SuppressWarnings("unused") public void notifyAppSwitchComing() { mWindowManagerService.mInputMonitor.notifyAppSwitchComing(); } diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java deleted file mode 100644 index f62c7ee..0000000 --- a/services/java/com/android/server/KeyInputQueue.java +++ /dev/null @@ -1,1388 +0,0 @@ -/* - * Copyright (C) 2007 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 com.android.server; - -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.os.Environment; -import android.os.LatencyTimer; -import android.os.PowerManager; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.util.Slog; -import android.util.SparseArray; -import android.util.Xml; -import android.view.Display; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.RawInputEvent; -import android.view.Surface; -import android.view.WindowManagerPolicy; - -import com.android.internal.util.XmlUtils; - -import org.xmlpull.v1.XmlPullParser; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.util.ArrayList; - -public abstract class KeyInputQueue { - static final String TAG = "KeyInputQueue"; - - static final boolean DEBUG = false; - static final boolean DEBUG_VIRTUAL_KEYS = false; - static final boolean DEBUG_POINTERS = false; - - /** - * Turn on some hacks we have to improve the touch interaction with a - * certain device whose screen currently is not all that good. - */ - static boolean BAD_TOUCH_HACK = false; - - /** - * Turn on some hacks to improve touch interaction with another device - * where touch coordinate data can get corrupted. - */ - static boolean JUMPY_TOUCH_HACK = false; - - private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; - - final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>(); - final SparseArray<InputDevice> mIgnoredDevices = new SparseArray<InputDevice>(); - final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>(); - final HapticFeedbackCallback mHapticFeedbackCallback; - - int mGlobalMetaState = 0; - boolean mHaveGlobalMetaState = false; - - final QueuedEvent mFirst; - final QueuedEvent mLast; - QueuedEvent mCache; - int mCacheCount; - - Display mDisplay = null; - int mDisplayWidth; - int mDisplayHeight; - - int mOrientation = Surface.ROTATION_0; - int[] mKeyRotationMap = null; - - VirtualKey mPressedVirtualKey = null; - - PowerManager.WakeLock mWakeLock; - - static final int[] KEY_90_MAP = new int[] { - KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT, - KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP, - KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT, - KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN, - }; - - static final int[] KEY_180_MAP = new int[] { - KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP, - KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT, - KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN, - KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT, - }; - - static final int[] KEY_270_MAP = new int[] { - KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT, - KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP, - KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT, - KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN, - }; - - public static final int FILTER_REMOVE = 0; - public static final int FILTER_KEEP = 1; - public static final int FILTER_ABORT = -1; - - private static final boolean MEASURE_LATENCY = false; - private LatencyTimer lt; - - public interface FilterCallback { - int filterEvent(QueuedEvent ev); - } - - public interface HapticFeedbackCallback { - void virtualKeyFeedback(KeyEvent event); - } - - static class QueuedEvent { - InputDevice inputDevice; - long whenNano; - int flags; // From the raw event - int classType; // One of the class constants in InputEvent - Object event; - boolean inQueue; - - void copyFrom(QueuedEvent that) { - this.inputDevice = that.inputDevice; - this.whenNano = that.whenNano; - this.flags = that.flags; - this.classType = that.classType; - this.event = that.event; - } - - @Override - public String toString() { - return "QueuedEvent{" - + Integer.toHexString(System.identityHashCode(this)) - + " " + event + "}"; - } - - // not copied - QueuedEvent prev; - QueuedEvent next; - } - - /** - * A key that exists as a part of the touch-screen, outside of the normal - * display area of the screen. - */ - static class VirtualKey { - int scancode; - int centerx; - int centery; - int width; - int height; - - int hitLeft; - int hitTop; - int hitRight; - int hitBottom; - - InputDevice lastDevice; - int lastKeycode; - - boolean checkHit(int x, int y) { - return (x >= hitLeft && x <= hitRight - && y >= hitTop && y <= hitBottom); - } - - void computeHitRect(InputDevice dev, int dw, int dh) { - if (dev == lastDevice) { - return; - } - - if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "computeHitRect for " + scancode - + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY); - - lastDevice = dev; - - int minx = dev.absX.minValue; - int maxx = dev.absX.maxValue; - - int halfw = width/2; - int left = centerx - halfw; - int right = centerx + halfw; - hitLeft = minx + ((left*maxx-minx)/dw); - hitRight = minx + ((right*maxx-minx)/dw); - - int miny = dev.absY.minValue; - int maxy = dev.absY.maxValue; - - int halfh = height/2; - int top = centery - halfh; - int bottom = centery + halfh; - hitTop = miny + ((top*maxy-miny)/dh); - hitBottom = miny + ((bottom*maxy-miny)/dh); - } - } - - private void readVirtualKeys(String deviceName) { - try { - FileInputStream fis = new FileInputStream( - "/sys/board_properties/virtualkeys." + deviceName); - InputStreamReader isr = new InputStreamReader(fis); - BufferedReader br = new BufferedReader(isr, 2048); - String str = br.readLine(); - if (str != null) { - String[] it = str.split(":"); - if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it); - final int N = it.length-6; - for (int i=0; i<=N; i+=6) { - if (!"0x01".equals(it[i])) { - Slog.w(TAG, "Unknown virtual key type at elem #" + i - + ": " + it[i]); - continue; - } - try { - VirtualKey sb = new VirtualKey(); - sb.scancode = Integer.parseInt(it[i+1]); - sb.centerx = Integer.parseInt(it[i+2]); - sb.centery = Integer.parseInt(it[i+3]); - sb.width = Integer.parseInt(it[i+4]); - sb.height = Integer.parseInt(it[i+5]); - if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key " - + sb.scancode + ": center=" + sb.centerx + "," - + sb.centery + " size=" + sb.width + "x" - + sb.height); - mVirtualKeys.add(sb); - } catch (NumberFormatException e) { - Slog.w(TAG, "Bad number at region " + i + " in: " - + str, e); - } - } - } - br.close(); - } catch (FileNotFoundException e) { - Slog.i(TAG, "No virtual keys found"); - } catch (IOException e) { - Slog.w(TAG, "Error reading virtual keys", e); - } - } - - private void readExcludedDevices() { - // Read partner-provided list of excluded input devices - XmlPullParser parser = null; - // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system". - File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH); - FileReader confreader = null; - try { - confreader = new FileReader(confFile); - parser = Xml.newPullParser(); - parser.setInput(confreader); - XmlUtils.beginDocument(parser, "devices"); - - while (true) { - XmlUtils.nextElement(parser); - if (!"device".equals(parser.getName())) { - break; - } - String name = parser.getAttributeValue(null, "name"); - if (name != null) { - if (DEBUG) Slog.v(TAG, "addExcludedDevice " + name); - addExcludedDevice(name); - } - } - } catch (FileNotFoundException e) { - // It's ok if the file does not exist. - } catch (Exception e) { - Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e); - } finally { - try { if (confreader != null) confreader.close(); } catch (IOException e) { } - } - } - - KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) { - if (MEASURE_LATENCY) { - lt = new LatencyTimer(100, 1000); - } - - Resources r = context.getResources(); - BAD_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterTouchEvents); - - JUMPY_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterJumpyTouchEvents); - - mHapticFeedbackCallback = hapticFeedbackCallback; - - if (! WindowManagerService.ENABLE_NATIVE_INPUT_DISPATCH) { - readExcludedDevices(); - } - - PowerManager pm = (PowerManager)context.getSystemService( - Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - "KeyInputQueue"); - mWakeLock.setReferenceCounted(false); - - mFirst = new QueuedEvent(); - mLast = new QueuedEvent(); - mFirst.next = mLast; - mLast.prev = mFirst; - - if (! WindowManagerService.ENABLE_NATIVE_INPUT_DISPATCH) { - mThread.start(); - } - } - - public void setDisplay(Display display) { - mDisplay = display; - - // We assume at this point that the display dimensions reflect the - // natural, unrotated display. We will perform hit tests for soft - // buttons based on that display. - mDisplayWidth = display.getWidth(); - mDisplayHeight = display.getHeight(); - } - - public void getInputConfiguration(Configuration config) { - synchronized (mFirst) { - config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH; - config.keyboard = Configuration.KEYBOARD_NOKEYS; - config.navigation = Configuration.NAVIGATION_NONAV; - - final int N = mDevices.size(); - for (int i=0; i<N; i++) { - InputDevice d = mDevices.valueAt(i); - if (d != null) { - if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) { - config.touchscreen - = Configuration.TOUCHSCREEN_FINGER; - //Slog.i("foo", "***** HAVE TOUCHSCREEN!"); - } - if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) { - config.keyboard - = Configuration.KEYBOARD_QWERTY; - //Slog.i("foo", "***** HAVE QWERTY!"); - } - if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) { - config.navigation - = Configuration.NAVIGATION_TRACKBALL; - //Slog.i("foo", "***** HAVE TRACKBALL!"); - } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) { - config.navigation - = Configuration.NAVIGATION_DPAD; - //Slog.i("foo", "***** HAVE DPAD!"); - } - } - } - } - } - - public int getScancodeState(int code) { - synchronized (mFirst) { - VirtualKey vk = mPressedVirtualKey; - if (vk != null) { - if (vk.scancode == code) { - return 2; - } - } - return nativeGetScancodeState(code); - } - } - - public int getScancodeState(int deviceId, int code) { - synchronized (mFirst) { - VirtualKey vk = mPressedVirtualKey; - if (vk != null) { - if (vk.scancode == code) { - return 2; - } - } - return nativeGetScancodeState(deviceId, code); - } - } - - public int getTrackballScancodeState(int code) { - synchronized (mFirst) { - final int N = mDevices.size(); - for (int i=0; i<N; i++) { - InputDevice dev = mDevices.valueAt(i); - if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) { - int res = nativeGetScancodeState(dev.id, code); - if (res > 0) { - return res; - } - } - } - } - - return 0; - } - - public int getDPadScancodeState(int code) { - synchronized (mFirst) { - final int N = mDevices.size(); - for (int i=0; i<N; i++) { - InputDevice dev = mDevices.valueAt(i); - if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) { - int res = nativeGetScancodeState(dev.id, code); - if (res > 0) { - return res; - } - } - } - } - - return 0; - } - - public int getKeycodeState(int code) { - synchronized (mFirst) { - VirtualKey vk = mPressedVirtualKey; - if (vk != null) { - if (vk.lastKeycode == code) { - return 2; - } - } - return nativeGetKeycodeState(code); - } - } - - public int getKeycodeState(int deviceId, int code) { - synchronized (mFirst) { - VirtualKey vk = mPressedVirtualKey; - if (vk != null) { - if (vk.lastKeycode == code) { - return 2; - } - } - return nativeGetKeycodeState(deviceId, code); - } - } - - public int getTrackballKeycodeState(int code) { - synchronized (mFirst) { - final int N = mDevices.size(); - for (int i=0; i<N; i++) { - InputDevice dev = mDevices.valueAt(i); - if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) { - int res = nativeGetKeycodeState(dev.id, code); - if (res > 0) { - return res; - } - } - } - } - - return 0; - } - - public int getDPadKeycodeState(int code) { - synchronized (mFirst) { - final int N = mDevices.size(); - for (int i=0; i<N; i++) { - InputDevice dev = mDevices.valueAt(i); - if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) { - int res = nativeGetKeycodeState(dev.id, code); - if (res > 0) { - return res; - } - } - } - } - - return 0; - } - - public static native String getDeviceName(int deviceId); - public static native int getDeviceClasses(int deviceId); - public static native void addExcludedDevice(String deviceName); - public static native boolean getAbsoluteInfo(int deviceId, int axis, - InputDevice.AbsoluteInfo outInfo); - public static native int getSwitchState(int sw); - public static native int getSwitchState(int deviceId, int sw); - public static native int nativeGetScancodeState(int code); - public static native int nativeGetScancodeState(int deviceId, int code); - public static native int nativeGetKeycodeState(int code); - public static native int nativeGetKeycodeState(int deviceId, int code); - public static native int scancodeToKeycode(int deviceId, int scancode); - public static native boolean hasKeys(int[] keycodes, boolean[] keyExists); - - public static KeyEvent newKeyEvent(InputDevice device, long downTime, - long eventTime, boolean down, int keycode, int repeatCount, - int scancode, int flags) { - return new KeyEvent( - downTime, eventTime, - down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, - keycode, repeatCount, - device != null ? device.mMetaKeysState : 0, - device != null ? device.id : -1, scancode, - flags | KeyEvent.FLAG_FROM_SYSTEM); - } - - Thread mThread = new Thread("InputDeviceReader") { - public void run() { - if (DEBUG) Slog.v(TAG, "InputDeviceReader.run()"); - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY); - - RawInputEvent ev = new RawInputEvent(); - while (true) { - try { - InputDevice di; - - // block, doesn't release the monitor - readEvent(ev); - - boolean send = false; - boolean configChanged = false; - - if (false) { - Slog.i(TAG, "Input event: dev=0x" - + Integer.toHexString(ev.deviceId) - + " type=0x" + Integer.toHexString(ev.type) - + " scancode=" + ev.scancode - + " keycode=" + ev.keycode - + " value=" + ev.value); - } - - if (ev.type == RawInputEvent.EV_DEVICE_ADDED) { - synchronized (mFirst) { - di = newInputDevice(ev.deviceId); - if (di.classes != 0) { - // If this device is some kind of input class, - // we care about it. - mDevices.put(ev.deviceId, di); - if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) { - readVirtualKeys(di.name); - } - // The configuration may have changed because - // of this device. - configChanged = true; - } else { - // We won't do anything with this device. - mIgnoredDevices.put(ev.deviceId, di); - Slog.i(TAG, "Ignoring non-input device: id=0x" - + Integer.toHexString(di.id) - + ", name=" + di.name); - } - } - } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) { - synchronized (mFirst) { - if (false) { - Slog.i(TAG, "Device removed: id=0x" - + Integer.toHexString(ev.deviceId)); - } - di = mDevices.get(ev.deviceId); - if (di != null) { - mDevices.delete(ev.deviceId); - // The configuration may have changed because - // of this device. - configChanged = true; - } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) { - mIgnoredDevices.remove(ev.deviceId); - } else { - Slog.w(TAG, "Removing bad device id: " - + Integer.toHexString(ev.deviceId)); - continue; - } - } - } else { - di = getInputDevice(ev.deviceId); - if (di == null) { - // This may be some junk from an ignored device. - continue; - } - - // first crack at it - send = preprocessEvent(di, ev); - - if (ev.type == RawInputEvent.EV_KEY) { - di.mMetaKeysState = makeMetaState(ev.keycode, - ev.value != 0, di.mMetaKeysState); - mHaveGlobalMetaState = false; - } - } - - if (configChanged) { - synchronized (mFirst) { - addLocked(di, System.nanoTime(), 0, - RawInputEvent.CLASS_CONFIGURATION_CHANGED, - null); - } - } - - if (!send) { - continue; - } - - synchronized (mFirst) { - // NOTE: The event timebase absolutely must be the same - // timebase as SystemClock.uptimeMillis(). - //curTime = gotOne ? ev.when : SystemClock.uptimeMillis(); - final long curTime = SystemClock.uptimeMillis(); - final long curTimeNano = System.nanoTime(); - //Slog.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis()); - - final int classes = di.classes; - final int type = ev.type; - final int scancode = ev.scancode; - send = false; - - // Is it a key event? - if (type == RawInputEvent.EV_KEY && - (classes&RawInputEvent.CLASS_KEYBOARD) != 0 && - (scancode < RawInputEvent.BTN_FIRST || - scancode > RawInputEvent.BTN_LAST)) { - boolean down; - if (ev.value != 0) { - down = true; - di.mKeyDownTime = curTime; - } else { - down = false; - } - int keycode = rotateKeyCodeLocked(ev.keycode); - addLocked(di, curTimeNano, ev.flags, - RawInputEvent.CLASS_KEYBOARD, - newKeyEvent(di, di.mKeyDownTime, curTime, down, - keycode, 0, scancode, - ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0) - ? KeyEvent.FLAG_WOKE_HERE : 0)); - - } else if (ev.type == RawInputEvent.EV_KEY) { - // Single touch protocol: touch going down or up. - if (ev.scancode == RawInputEvent.BTN_TOUCH && - (classes&(RawInputEvent.CLASS_TOUCHSCREEN - |RawInputEvent.CLASS_TOUCHSCREEN_MT)) - == RawInputEvent.CLASS_TOUCHSCREEN) { - di.mAbs.changed = true; - di.mAbs.mDown[0] = ev.value != 0; - - // Trackball (mouse) protocol: press down or up. - } else if (ev.scancode == RawInputEvent.BTN_MOUSE && - (classes&RawInputEvent.CLASS_TRACKBALL) != 0) { - di.mRel.changed = true; - di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0; - send = true; - } - - // Process position events from multitouch protocol. - } else if (ev.type == RawInputEvent.EV_ABS && - (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) { - if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) { - di.mAbs.changed = true; - di.mAbs.mNextData[di.mAbs.mAddingPointerOffset - + MotionEvent.SAMPLE_PRESSURE] = ev.value; - } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) { - di.mAbs.changed = true; - di.mAbs.mNextData[di.mAbs.mAddingPointerOffset - + MotionEvent.SAMPLE_X] = ev.value; - if (DEBUG_POINTERS) Slog.v(TAG, "MT @" - + di.mAbs.mAddingPointerOffset - + " X:" + ev.value); - } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) { - di.mAbs.changed = true; - di.mAbs.mNextData[di.mAbs.mAddingPointerOffset - + MotionEvent.SAMPLE_Y] = ev.value; - if (DEBUG_POINTERS) Slog.v(TAG, "MT @" - + di.mAbs.mAddingPointerOffset - + " Y:" + ev.value); - } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) { - di.mAbs.changed = true; - di.mAbs.mNextData[di.mAbs.mAddingPointerOffset - + MotionEvent.SAMPLE_SIZE] = ev.value; - } - - // Process position events from single touch protocol. - } else if (ev.type == RawInputEvent.EV_ABS && - (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) { - if (ev.scancode == RawInputEvent.ABS_X) { - di.mAbs.changed = true; - di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value; - } else if (ev.scancode == RawInputEvent.ABS_Y) { - di.mAbs.changed = true; - di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value; - } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) { - di.mAbs.changed = true; - di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value; - di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA - + MotionEvent.SAMPLE_PRESSURE] = ev.value; - } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) { - di.mAbs.changed = true; - di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value; - di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA - + MotionEvent.SAMPLE_SIZE] = ev.value; - } - - // Process movement events from trackball (mouse) protocol. - } else if (ev.type == RawInputEvent.EV_REL && - (classes&RawInputEvent.CLASS_TRACKBALL) != 0) { - // Add this relative movement into our totals. - if (ev.scancode == RawInputEvent.REL_X) { - di.mRel.changed = true; - di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value; - } else if (ev.scancode == RawInputEvent.REL_Y) { - di.mRel.changed = true; - di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value; - } - } - - // Handle multitouch protocol sync: tells us that the - // driver has returned all data for -one- of the pointers - // that is currently down. - if (ev.type == RawInputEvent.EV_SYN - && ev.scancode == RawInputEvent.SYN_MT_REPORT - && di.mAbs != null) { - di.mAbs.changed = true; - if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) { - // If the value is <= 0, the pointer is not - // down, so keep it in the count. - - if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset - + MotionEvent.SAMPLE_PRESSURE] != 0) { - final int num = di.mAbs.mNextNumPointers+1; - di.mAbs.mNextNumPointers = num; - if (DEBUG_POINTERS) Slog.v(TAG, - "MT_REPORT: now have " + num + " pointers"); - final int newOffset = (num <= InputDevice.MAX_POINTERS) - ? (num * MotionEvent.NUM_SAMPLE_DATA) - : (InputDevice.MAX_POINTERS * - MotionEvent.NUM_SAMPLE_DATA); - di.mAbs.mAddingPointerOffset = newOffset; - di.mAbs.mNextData[newOffset - + MotionEvent.SAMPLE_PRESSURE] = 0; - } else { - if (DEBUG_POINTERS) Slog.v(TAG, "MT_REPORT: no pointer"); - } - } - - // Handle general event sync: all data for the current - // event update has been delivered. - } else if (send || (ev.type == RawInputEvent.EV_SYN - && ev.scancode == RawInputEvent.SYN_REPORT)) { - if (mDisplay != null) { - if (!mHaveGlobalMetaState) { - computeGlobalMetaStateLocked(); - } - - MotionEvent me; - - InputDevice.MotionState ms = di.mAbs; - if (ms.changed) { - ms.everChanged = true; - ms.changed = false; - - if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN - |RawInputEvent.CLASS_TOUCHSCREEN_MT)) - == RawInputEvent.CLASS_TOUCHSCREEN) { - ms.mNextNumPointers = 0; - if (ms.mDown[0]) { - System.arraycopy(di.curTouchVals, 0, - ms.mNextData, 0, - MotionEvent.NUM_SAMPLE_DATA); - ms.mNextNumPointers++; - } - } - - if (BAD_TOUCH_HACK) { - ms.dropBadPoint(di); - } - if (JUMPY_TOUCH_HACK) { - ms.dropJumpyPoint(di); - } - - boolean doMotion = !monitorVirtualKey(di, - ev, curTime, curTimeNano); - - if (doMotion && ms.mNextNumPointers > 0 - && (ms.mLastNumPointers == 0 - || ms.mSkipLastPointers)) { - doMotion = !generateVirtualKeyDown(di, - ev, curTime, curTimeNano); - } - - if (doMotion) { - // XXX Need to be able to generate - // multiple events here, for example - // if two fingers change up/down state - // at the same time. - do { - me = ms.generateAbsMotion(di, curTime, - curTimeNano, mDisplay, - mOrientation, mGlobalMetaState); - if (DEBUG_POINTERS) Slog.v(TAG, "Absolute: x=" - + di.mAbs.mNextData[MotionEvent.SAMPLE_X] - + " y=" - + di.mAbs.mNextData[MotionEvent.SAMPLE_Y] - + " ev=" + me); - if (me != null) { - if (WindowManagerPolicy.WATCH_POINTER) { - Slog.i(TAG, "Enqueueing: " + me); - } - addLocked(di, curTimeNano, ev.flags, - RawInputEvent.CLASS_TOUCHSCREEN, me); - } - } while (ms.hasMore()); - } else { - // We are consuming movement in the - // virtual key area... but still - // propagate this to the previous - // data for comparisons. - int num = ms.mNextNumPointers; - if (num > InputDevice.MAX_POINTERS) { - num = InputDevice.MAX_POINTERS; - } - System.arraycopy(ms.mNextData, 0, - ms.mLastData, 0, - num * MotionEvent.NUM_SAMPLE_DATA); - ms.mLastNumPointers = num; - ms.mSkipLastPointers = true; - } - - ms.finish(); - } - - ms = di.mRel; - if (ms.changed) { - ms.everChanged = true; - ms.changed = false; - - me = ms.generateRelMotion(di, curTime, - curTimeNano, - mOrientation, mGlobalMetaState); - if (false) Slog.v(TAG, "Relative: x=" - + di.mRel.mNextData[MotionEvent.SAMPLE_X] - + " y=" - + di.mRel.mNextData[MotionEvent.SAMPLE_Y] - + " ev=" + me); - if (me != null) { - addLocked(di, curTimeNano, ev.flags, - RawInputEvent.CLASS_TRACKBALL, me); - } - } - } - } - } - - } catch (RuntimeException exc) { - Slog.e(TAG, "InputReaderThread uncaught exception", exc); - } - } - } - }; - - private boolean isInsideDisplay(InputDevice dev) { - final InputDevice.AbsoluteInfo absx = dev.absX; - final InputDevice.AbsoluteInfo absy = dev.absY; - final InputDevice.MotionState absm = dev.mAbs; - if (absx == null || absy == null || absm == null) { - return true; - } - - if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue - && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue - && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue - && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) { - if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Input (" - + absm.mNextData[MotionEvent.SAMPLE_X] - + "," + absm.mNextData[MotionEvent.SAMPLE_Y] - + ") inside of display"); - return true; - } - - return false; - } - - private VirtualKey findVirtualKey(InputDevice dev) { - final int N = mVirtualKeys.size(); - if (N <= 0) { - return null; - } - - final InputDevice.MotionState absm = dev.mAbs; - for (int i=0; i<N; i++) { - VirtualKey sb = mVirtualKeys.get(i); - sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight); - if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit test (" - + absm.mNextData[MotionEvent.SAMPLE_X] + "," - + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code " - + sb.scancode + " - (" + sb.hitLeft - + "," + sb.hitTop + ")-(" + sb.hitRight + "," - + sb.hitBottom + ")"); - if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X], - absm.mNextData[MotionEvent.SAMPLE_Y])) { - if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit!"); - return sb; - } - } - - return null; - } - - private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev, - long curTime, long curTimeNano) { - if (isInsideDisplay(di)) { - // Didn't consume event. - return false; - } - - - VirtualKey vk = findVirtualKey(di); - if (vk != null) { - final InputDevice.MotionState ms = di.mAbs; - mPressedVirtualKey = vk; - vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode); - ms.mLastNumPointers = ms.mNextNumPointers; - di.mKeyDownTime = curTime; - if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, - "Generate key down for: " + vk.scancode - + " (keycode=" + vk.lastKeycode + ")"); - KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true, - vk.lastKeycode, 0, vk.scancode, - KeyEvent.FLAG_VIRTUAL_HARD_KEY); - mHapticFeedbackCallback.virtualKeyFeedback(event); - addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD, - event); - } - - // We always consume the event, even if we didn't - // generate a key event. There are two reasons for - // this: to avoid spurious touches when holding - // the edges of the device near the touchscreen, - // and to avoid reporting events if there are virtual - // keys on the touchscreen outside of the display - // area. - // Note that for all of this we are only looking at the - // first pointer, since what we are handling here is the - // first pointer going down, and this is the coordinate - // that will be used to dispatch the event. - if (false) { - final InputDevice.AbsoluteInfo absx = di.absX; - final InputDevice.AbsoluteInfo absy = di.absY; - final InputDevice.MotionState absm = di.mAbs; - Slog.v(TAG, "Rejecting (" - + absm.mNextData[MotionEvent.SAMPLE_X] + "," - + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of (" - + absx.minValue + "," + absy.minValue - + ")-(" + absx.maxValue + "," - + absx.maxValue + ")"); - } - return true; - } - - private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev, - long curTime, long curTimeNano) { - VirtualKey vk = mPressedVirtualKey; - if (vk == null) { - return false; - } - - final InputDevice.MotionState ms = di.mAbs; - if (ms.mNextNumPointers <= 0) { - mPressedVirtualKey = null; - ms.mLastNumPointers = 0; - if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Generate key up for: " + vk.scancode); - KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false, - vk.lastKeycode, 0, vk.scancode, - KeyEvent.FLAG_VIRTUAL_HARD_KEY); - mHapticFeedbackCallback.virtualKeyFeedback(event); - addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD, - event); - return true; - - } else if (isInsideDisplay(di)) { - // Whoops the pointer has moved into - // the display area! Cancel the - // virtual key and start a pointer - // motion. - mPressedVirtualKey = null; - if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Cancel key up for: " + vk.scancode); - KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false, - vk.lastKeycode, 0, vk.scancode, - KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY); - mHapticFeedbackCallback.virtualKeyFeedback(event); - addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD, - event); - ms.mLastNumPointers = 0; - return false; - } - - return true; - } - - /** - * Returns a new meta state for the given keys and old state. - */ - private static final int makeMetaState(int keycode, boolean down, int old) { - int mask; - switch (keycode) { - case KeyEvent.KEYCODE_ALT_LEFT: - mask = KeyEvent.META_ALT_LEFT_ON; - break; - case KeyEvent.KEYCODE_ALT_RIGHT: - mask = KeyEvent.META_ALT_RIGHT_ON; - break; - case KeyEvent.KEYCODE_SHIFT_LEFT: - mask = KeyEvent.META_SHIFT_LEFT_ON; - break; - case KeyEvent.KEYCODE_SHIFT_RIGHT: - mask = KeyEvent.META_SHIFT_RIGHT_ON; - break; - case KeyEvent.KEYCODE_SYM: - mask = KeyEvent.META_SYM_ON; - break; - default: - return old; - } - int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON) - & (down ? (old | mask) : (old & ~mask)); - if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) { - result |= KeyEvent.META_ALT_ON; - } - if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) { - result |= KeyEvent.META_SHIFT_ON; - } - return result; - } - - private void computeGlobalMetaStateLocked() { - int i = mDevices.size(); - mGlobalMetaState = 0; - while ((--i) >= 0) { - mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState; - } - mHaveGlobalMetaState = true; - } - - /* - * Return true if you want the event to get passed on to the - * rest of the system, and false if you've handled it and want - * it dropped. - */ - abstract boolean preprocessEvent(InputDevice device, RawInputEvent event); - - InputDevice getInputDevice(int deviceId) { - synchronized (mFirst) { - return getInputDeviceLocked(deviceId); - } - } - - private InputDevice getInputDeviceLocked(int deviceId) { - return mDevices.get(deviceId); - } - - public void setOrientation(int orientation) { - synchronized(mFirst) { - mOrientation = orientation; - switch (orientation) { - case Surface.ROTATION_90: - mKeyRotationMap = KEY_90_MAP; - break; - case Surface.ROTATION_180: - mKeyRotationMap = KEY_180_MAP; - break; - case Surface.ROTATION_270: - mKeyRotationMap = KEY_270_MAP; - break; - default: - mKeyRotationMap = null; - break; - } - } - } - - public int rotateKeyCode(int keyCode) { - synchronized(mFirst) { - return rotateKeyCodeLocked(keyCode); - } - } - - private int rotateKeyCodeLocked(int keyCode) { - int[] map = mKeyRotationMap; - if (map != null) { - final int N = map.length; - for (int i=0; i<N; i+=2) { - if (map[i] == keyCode) { - return map[i+1]; - } - } - } - return keyCode; - } - - boolean hasEvents() { - synchronized (mFirst) { - return mFirst.next != mLast; - } - } - - /* - * returns true if we returned an event, and false if we timed out - */ - QueuedEvent getEvent(long timeoutMS) { - long begin = SystemClock.uptimeMillis(); - final long end = begin+timeoutMS; - long now = begin; - synchronized (mFirst) { - while (mFirst.next == mLast && end > now) { - try { - mWakeLock.release(); - mFirst.wait(end-now); - } - catch (InterruptedException e) { - } - now = SystemClock.uptimeMillis(); - if (begin > now) { - begin = now; - } - } - if (mFirst.next == mLast) { - return null; - } - QueuedEvent p = mFirst.next; - mFirst.next = p.next; - mFirst.next.prev = mFirst; - p.inQueue = false; - return p; - } - } - - /** - * Return true if the queue has an up event pending that corresponds - * to the same key as the given key event. - */ - boolean hasKeyUpEvent(KeyEvent origEvent) { - synchronized (mFirst) { - final int keyCode = origEvent.getKeyCode(); - QueuedEvent cur = mLast.prev; - while (cur.prev != null) { - if (cur.classType == RawInputEvent.CLASS_KEYBOARD) { - KeyEvent ke = (KeyEvent)cur.event; - if (ke.getAction() == KeyEvent.ACTION_UP - && ke.getKeyCode() == keyCode) { - return true; - } - } - cur = cur.prev; - } - } - - return false; - } - - void recycleEvent(QueuedEvent ev) { - synchronized (mFirst) { - //Slog.i(TAG, "Recycle event: " + ev); - if (ev.event == ev.inputDevice.mAbs.currentMove) { - ev.inputDevice.mAbs.currentMove = null; - } - if (ev.event == ev.inputDevice.mRel.currentMove) { - if (false) Slog.i(TAG, "Detach rel " + ev.event); - ev.inputDevice.mRel.currentMove = null; - ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0; - ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0; - } - recycleLocked(ev); - } - } - - void filterQueue(FilterCallback cb) { - synchronized (mFirst) { - QueuedEvent cur = mLast.prev; - while (cur.prev != null) { - switch (cb.filterEvent(cur)) { - case FILTER_REMOVE: - cur.prev.next = cur.next; - cur.next.prev = cur.prev; - break; - case FILTER_ABORT: - return; - } - cur = cur.prev; - } - } - } - - private QueuedEvent obtainLocked(InputDevice device, long whenNano, - int flags, int classType, Object event) { - QueuedEvent ev; - if (mCacheCount == 0) { - ev = new QueuedEvent(); - } else { - ev = mCache; - ev.inQueue = false; - mCache = ev.next; - mCacheCount--; - } - ev.inputDevice = device; - ev.whenNano = whenNano; - ev.flags = flags; - ev.classType = classType; - ev.event = event; - return ev; - } - - private void recycleLocked(QueuedEvent ev) { - if (ev.inQueue) { - throw new RuntimeException("Event already in queue!"); - } - if (mCacheCount < 10) { - mCacheCount++; - ev.next = mCache; - mCache = ev; - ev.inQueue = true; - } - } - - private void addLocked(InputDevice device, long whenNano, int flags, - int classType, Object event) { - boolean poke = mFirst.next == mLast; - - QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event); - QueuedEvent p = mLast.prev; - while (p != mFirst && ev.whenNano < p.whenNano) { - p = p.prev; - } - - ev.next = p.next; - ev.prev = p; - p.next = ev; - ev.next.prev = ev; - ev.inQueue = true; - - if (poke) { - long time; - if (MEASURE_LATENCY) { - time = System.nanoTime(); - } - mFirst.notify(); - mWakeLock.acquire(); - if (MEASURE_LATENCY) { - lt.sample("1 addLocked-queued event ", System.nanoTime() - time); - } - } - } - - private InputDevice newInputDevice(int deviceId) { - int classes = getDeviceClasses(deviceId); - String name = getDeviceName(deviceId); - InputDevice.AbsoluteInfo absX = null; - InputDevice.AbsoluteInfo absY = null; - InputDevice.AbsoluteInfo absPressure = null; - InputDevice.AbsoluteInfo absSize = null; - if (classes != 0) { - Slog.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId) - + ", name=" + name - + ", classes=" + Integer.toHexString(classes)); - if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) { - absX = loadAbsoluteInfo(deviceId, - RawInputEvent.ABS_MT_POSITION_X, "X"); - absY = loadAbsoluteInfo(deviceId, - RawInputEvent.ABS_MT_POSITION_Y, "Y"); - absPressure = loadAbsoluteInfo(deviceId, - RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure"); - absSize = loadAbsoluteInfo(deviceId, - RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size"); - } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) { - absX = loadAbsoluteInfo(deviceId, - RawInputEvent.ABS_X, "X"); - absY = loadAbsoluteInfo(deviceId, - RawInputEvent.ABS_Y, "Y"); - absPressure = loadAbsoluteInfo(deviceId, - RawInputEvent.ABS_PRESSURE, "Pressure"); - absSize = loadAbsoluteInfo(deviceId, - RawInputEvent.ABS_TOOL_WIDTH, "Size"); - } - } - - return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize); - } - - private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel, - String name) { - InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo(); - if (getAbsoluteInfo(id, channel, info) - && info.minValue != info.maxValue) { - Slog.i(TAG, " " + name + ": min=" + info.minValue - + " max=" + info.maxValue - + " flat=" + info.flat - + " fuzz=" + info.fuzz); - info.range = info.maxValue-info.minValue; - return info; - } - Slog.i(TAG, " " + name + ": unknown values"); - return null; - } - private static native boolean readEvent(RawInputEvent outEvent); - - void dump(PrintWriter pw, String prefix) { - synchronized (mFirst) { - for (int i=0; i<mDevices.size(); i++) { - InputDevice dev = mDevices.valueAt(i); - pw.print(prefix); pw.print("Device #"); - pw.print(mDevices.keyAt(i)); pw.print(" "); - pw.print(dev.name); pw.print(" (classes=0x"); - pw.print(Integer.toHexString(dev.classes)); - pw.println("):"); - pw.print(prefix); pw.print(" mKeyDownTime="); - pw.print(dev.mKeyDownTime); pw.print(" mMetaKeysState="); - pw.println(dev.mMetaKeysState); - if (dev.absX != null) { - pw.print(prefix); pw.print(" absX: "); dev.absX.dump(pw); - pw.println(""); - } - if (dev.absY != null) { - pw.print(prefix); pw.print(" absY: "); dev.absY.dump(pw); - pw.println(""); - } - if (dev.absPressure != null) { - pw.print(prefix); pw.print(" absPressure: "); - dev.absPressure.dump(pw); pw.println(""); - } - if (dev.absSize != null) { - pw.print(prefix); pw.print(" absSize: "); - dev.absSize.dump(pw); pw.println(""); - } - if (dev.mAbs.everChanged) { - pw.print(prefix); pw.println(" mAbs:"); - dev.mAbs.dump(pw, prefix + " "); - } - if (dev.mRel.everChanged) { - pw.print(prefix); pw.println(" mRel:"); - dev.mRel.dump(pw, prefix + " "); - } - } - pw.println(" "); - for (int i=0; i<mIgnoredDevices.size(); i++) { - InputDevice dev = mIgnoredDevices.valueAt(i); - pw.print(prefix); pw.print("Ignored Device #"); - pw.print(mIgnoredDevices.keyAt(i)); pw.print(" "); - pw.print(dev.name); pw.print(" (classes=0x"); - pw.print(Integer.toHexString(dev.classes)); - pw.println(")"); - } - pw.println(" "); - for (int i=0; i<mVirtualKeys.size(); i++) { - VirtualKey vk = mVirtualKeys.get(i); - pw.print(prefix); pw.print("Virtual Key #"); - pw.print(i); pw.println(":"); - pw.print(prefix); pw.print(" scancode="); pw.println(vk.scancode); - pw.print(prefix); pw.print(" centerx="); pw.print(vk.centerx); - pw.print(" centery="); pw.print(vk.centery); - pw.print(" width="); pw.print(vk.width); - pw.print(" height="); pw.println(vk.height); - pw.print(prefix); pw.print(" hitLeft="); pw.print(vk.hitLeft); - pw.print(" hitTop="); pw.print(vk.hitTop); - pw.print(" hitRight="); pw.print(vk.hitRight); - pw.print(" hitBottom="); pw.println(vk.hitBottom); - if (vk.lastDevice != null) { - pw.print(prefix); pw.print(" lastDevice=#"); - pw.println(vk.lastDevice.id); - } - if (vk.lastKeycode != 0) { - pw.print(prefix); pw.print(" lastKeycode="); - pw.println(vk.lastKeycode); - } - } - pw.println(" "); - pw.print(prefix); pw.print(" Default keyboard: "); - pw.println(SystemProperties.get("hw.keyboards.0.devname")); - pw.print(prefix); pw.print(" mGlobalMetaState="); - pw.print(mGlobalMetaState); pw.print(" mHaveGlobalMetaState="); - pw.println(mHaveGlobalMetaState); - pw.print(prefix); pw.print(" mDisplayWidth="); - pw.print(mDisplayWidth); pw.print(" mDisplayHeight="); - pw.println(mDisplayHeight); - pw.print(prefix); pw.print(" mOrientation="); - pw.println(mOrientation); - if (mPressedVirtualKey != null) { - pw.print(prefix); pw.print(" mPressedVirtualKey.scancode="); - pw.println(mPressedVirtualKey.scancode); - } - } - } -} diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index b4fc15a..d97f30c 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -4583,6 +4583,8 @@ class PackageManagerService extends IPackageManager.Stub { } }; + private static final boolean DEBUG_OBB = false; + private static final void sendPackageBroadcast(String action, String pkg, Bundle extras, IIntentReceiver finishedReceiver) { IActivityManager am = ActivityManagerNative.getDefault(); @@ -4757,6 +4759,27 @@ class PackageManagerService extends IPackageManager.Stub { mHandler.sendMessage(msg); } + public void setPackageObbPath(String packageName, String path) { + if (DEBUG_OBB) + Log.v(TAG, "Setting .obb path for " + packageName + " to: " + path); + PackageSetting pkgSetting; + final int uid = Binder.getCallingUid(); + boolean allowedByPermission = false; + synchronized (mPackages) { + pkgSetting = mSettings.mPackages.get(packageName); + if (pkgSetting == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (!allowedByPermission && (uid != pkgSetting.userId)) { + throw new SecurityException("Permission denial: attempt to set .obb file from pid=" + + Binder.getCallingPid() + ", uid=" + uid + ", package uid=" + + pkgSetting.userId); + } + pkgSetting.obbPathString = path; + mSettings.writeLP(); + } + } + private void processPendingInstall(final InstallArgs args, final int currentStatus) { // Queue up an async operation since the package installation may take a little while. mHandler.post(new Runnable() { @@ -7118,6 +7141,7 @@ class PackageManagerService extends IPackageManager.Stub { pw.print(" pkg="); pw.println(ps.pkg); pw.print(" codePath="); pw.println(ps.codePathString); pw.print(" resourcePath="); pw.println(ps.resourcePathString); + pw.print(" obbPath="); pw.println(ps.obbPathString); if (ps.pkg != null) { pw.print(" dataDir="); pw.println(ps.pkg.applicationInfo.dataDir); pw.print(" targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion); @@ -7684,6 +7708,7 @@ class PackageManagerService extends IPackageManager.Stub { String codePathString; File resourcePath; String resourcePathString; + String obbPathString; private long timeStamp; private String timeStampString = "0"; int versionCode; @@ -8684,6 +8709,9 @@ class PackageManagerService extends IPackageManager.Stub { if (pkg.installerPackageName != null) { serializer.attribute(null, "installer", pkg.installerPackageName); } + if (pkg.obbPathString != null) { + serializer.attribute(null, "obbPath", pkg.obbPathString); + } pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); if ((pkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { serializer.startTag(null, "perms"); @@ -9060,6 +9088,7 @@ class PackageManagerService extends IPackageManager.Stub { String sharedIdStr = null; String codePathStr = null; String resourcePathStr = null; + String obbPathStr = null; String systemStr = null; String installerPackageName = null; String uidError = null; @@ -9077,6 +9106,7 @@ class PackageManagerService extends IPackageManager.Stub { sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); codePathStr = parser.getAttributeValue(null, "codePath"); resourcePathStr = parser.getAttributeValue(null, "resourcePath"); + obbPathStr = parser.getAttributeValue(null, "obbPath"); version = parser.getAttributeValue(null, "version"); if (version != null) { try { @@ -9174,6 +9204,7 @@ class PackageManagerService extends IPackageManager.Stub { if (packageSetting != null) { packageSetting.uidError = "true".equals(uidError); packageSetting.installerPackageName = installerPackageName; + packageSetting.obbPathString = obbPathStr; final String enabledStr = parser.getAttributeValue(null, "enabled"); if (enabledStr != null) { if (enabledStr.equalsIgnoreCase("true")) { diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 493a348..e9d5efc 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -247,6 +247,9 @@ class PowerManagerService extends IPowerManager.Stub private static final boolean mSpew = false; private static final boolean mDebugProximitySensor = (true || mSpew); private static final boolean mDebugLightSensor = (false || mSpew); + + private native void nativeInit(); + private native void nativeSetPowerState(boolean screenOn, boolean screenBright); /* static PrintStream mLog; @@ -481,6 +484,11 @@ class PowerManagerService extends IPowerManager.Stub } } } + + nativeInit(); + synchronized (mLocks) { + updateNativePowerStateLocked(); + } } void initInThread() { @@ -1557,8 +1565,16 @@ class PowerManagerService extends IPowerManager.Stub } } } + + updateNativePowerStateLocked(); } } + + private void updateNativePowerStateLocked() { + nativeSetPowerState( + (mPowerState & SCREEN_ON_BIT) != 0, + (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT); + } private int screenOffFinishedAnimatingLocked(int reason) { // I don't think we need to check the current state here because all of these diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 483f9eb..38f1e1f 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -48,7 +48,6 @@ import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; import com.android.internal.view.IInputMethodManager; import com.android.internal.view.WindowManagerPolicyThread; -import com.android.server.KeyInputQueue.QueuedEvent; import com.android.server.am.BatteryStatsService; import android.Manifest; @@ -95,6 +94,7 @@ import android.util.Slog; import android.util.SparseIntArray; import android.view.Display; import android.view.Gravity; +import android.view.HapticFeedbackConstants; import android.view.IApplicationToken; import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; @@ -105,7 +105,6 @@ import android.view.InputChannel; import android.view.InputQueue; import android.view.KeyEvent; import android.view.MotionEvent; -import android.view.RawInputEvent; import android.view.Surface; import android.view.SurfaceSession; import android.view.View; @@ -137,7 +136,7 @@ import java.util.List; /** {@hide} */ public class WindowManagerService extends IWindowManager.Stub - implements Watchdog.Monitor, KeyInputQueue.HapticFeedbackCallback { + implements Watchdog.Monitor { static final String TAG = "WindowManager"; static final boolean DEBUG = false; static final boolean DEBUG_FOCUS = false; @@ -159,17 +158,12 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean SHOW_TRANSACTIONS = false; static final boolean HIDE_STACK_CRAWLS = true; static final boolean MEASURE_LATENCY = false; - static final boolean ENABLE_NATIVE_INPUT_DISPATCH = - WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH; static private LatencyTimer lt; static final boolean PROFILE_ORIENTATION = false; static final boolean BLUR = true; static final boolean localLOGV = DEBUG; - /** How long to wait for subsequent key repeats, in milliseconds */ - static final int KEY_REPEAT_DELAY = 50; - /** How much to multiply the policy's type layer, to reserve room * for multiple windows of the same type and Z-ordering adjustment * with TYPE_LAYER_OFFSET. */ @@ -210,34 +204,11 @@ public class WindowManagerService extends IWindowManager.Stub // Default input dispatching timeout in nanoseconds. private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L; - static final int INJECT_FAILED = 0; - static final int INJECT_SUCCEEDED = 1; - static final int INJECT_NO_PERMISSION = -1; - static final int UPDATE_FOCUS_NORMAL = 0; static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; static final int UPDATE_FOCUS_PLACING_SURFACES = 2; static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3; - /** The minimum time between dispatching touch events. */ - int mMinWaitTimeBetweenTouchEvents = 1000 / 35; - - // Last touch event time - long mLastTouchEventTime = 0; - - // Last touch event type - int mLastTouchEventType = OTHER_EVENT; - - // Time to wait before calling useractivity again. This saves CPU usage - // when we get a flood of touch events. - static final int MIN_TIME_BETWEEN_USERACTIVITIES = 1000; - - // Last time we call user activity - long mLastUserActivityCallTime = 0; - - // Last time we updated battery stats - long mLastBatteryStatsCallTime = 0; - private static final String SYSTEM_SECURE = "ro.secure"; private static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; @@ -486,7 +457,6 @@ public class WindowManagerService extends IWindowManager.Stub float mLastWallpaperY = -1; float mLastWallpaperXStep = -1; float mLastWallpaperYStep = -1; - boolean mSendingPointersToWallpaper = false; // This is set when we are waiting for a wallpaper to tell us it is done // changing its scroll position. WindowState mWaitingOnWallpaper; @@ -504,10 +474,7 @@ public class WindowManagerService extends IWindowManager.Stub float mWindowAnimationScale = 1.0f; float mTransitionAnimationScale = 1.0f; - final KeyWaiter mKeyWaiter = new KeyWaiter(); - final KeyQ mQueue; final InputManager mInputManager; - final InputDispatcherThread mInputThread; // Who is holding the screen on. Session mHoldingScreenOn; @@ -523,8 +490,6 @@ public class WindowManagerService extends IWindowManager.Stub private ViewServer mViewServer; - final Rect mTempRect = new Rect(); - final Configuration mTempConfiguration = new Configuration(); int mScreenLayout = Configuration.SCREENLAYOUT_SIZE_UNDEFINED; @@ -652,28 +617,11 @@ public class WindowManagerService extends IWindowManager.Stub filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter); - int max_events_per_sec = 35; - try { - max_events_per_sec = Integer.parseInt(SystemProperties - .get("windowsmgr.max_events_per_sec")); - if (max_events_per_sec < 1) { - max_events_per_sec = 35; - } - } catch (NumberFormatException e) { - } - mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec; - mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "KEEP_SCREEN_ON_FLAG"); mHoldingScreenWakeLock.setReferenceCounted(false); - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputManager = new InputManager(context, this, mPolicy, pmc, mPowerManager); - } else { - mInputManager = null; - } - mQueue = new KeyQ(); - mInputThread = new InputDispatcherThread(); + mInputManager = new InputManager(context, this, pmc, mPowerManager); PolicyThread thr = new PolicyThread(mPolicy, this, context, pm); thr.start(); @@ -687,11 +635,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputManager.start(); - } else { - mInputThread.start(); - } + mInputManager.start(); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); @@ -1817,70 +1761,6 @@ public class WindowManagerService extends IWindowManager.Stub } } } - - void sendPointerToWallpaperLocked(WindowState srcWin, - MotionEvent pointer, long eventTime) { - int curTokenIndex = mWallpaperTokens.size(); - while (curTokenIndex > 0) { - curTokenIndex--; - WindowToken token = mWallpaperTokens.get(curTokenIndex); - int curWallpaperIndex = token.windows.size(); - while (curWallpaperIndex > 0) { - curWallpaperIndex--; - WindowState wallpaper = token.windows.get(curWallpaperIndex); - if ((wallpaper.mAttrs.flags & - WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { - continue; - } - try { - MotionEvent ev = MotionEvent.obtainNoHistory(pointer); - if (srcWin != null) { - ev.offsetLocation(srcWin.mFrame.left-wallpaper.mFrame.left, - srcWin.mFrame.top-wallpaper.mFrame.top); - } else { - ev.offsetLocation(-wallpaper.mFrame.left, -wallpaper.mFrame.top); - } - switch (pointer.getAction()) { - case MotionEvent.ACTION_DOWN: - mSendingPointersToWallpaper = true; - break; - case MotionEvent.ACTION_UP: - mSendingPointersToWallpaper = false; - break; - } - wallpaper.mClient.dispatchPointer(ev, eventTime, false); - } catch (RemoteException e) { - Slog.w(TAG, "Failure sending pointer to wallpaper", e); - } - } - } - } - - void dispatchPointerElsewhereLocked(WindowState srcWin, WindowState relWin, - MotionEvent pointer, long eventTime, boolean skipped) { - if (relWin != null) { - mPolicy.dispatchedPointerEventLw(pointer, relWin.mFrame.left, relWin.mFrame.top); - } else { - mPolicy.dispatchedPointerEventLw(pointer, 0, 0); - } - - // If we sent an initial down to the wallpaper, then continue - // sending events until the final up. - if (mSendingPointersToWallpaper) { - if (skipped) { - Slog.i(TAG, "Sending skipped pointer to wallpaper!"); - } - sendPointerToWallpaperLocked(relWin, pointer, eventTime); - - // If we are on top of the wallpaper, then the wallpaper also - // gets to see this movement. - } else if (srcWin != null - && pointer.getAction() == MotionEvent.ACTION_DOWN - && mWallpaperTarget == srcWin - && srcWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) { - sendPointerToWallpaperLocked(relWin, pointer, eventTime); - } - } public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility, @@ -1903,12 +1783,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplay = wm.getDefaultDisplay(); mInitialDisplayWidth = mDisplay.getWidth(); mInitialDisplayHeight = mDisplay.getHeight(); - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputManager.setDisplaySize(0, - mInitialDisplayWidth, mInitialDisplayHeight); - } else { - mQueue.setDisplay(mDisplay); - } + mInputManager.setDisplaySize(0, mInitialDisplayWidth, mInitialDisplayHeight); reportNewConfig = true; } @@ -2002,15 +1877,13 @@ public class WindowManagerService extends IWindowManager.Stub return res; } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - if (outInputChannel != null) { - String name = win.makeInputChannelName(); - InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); - win.mInputChannel = inputChannels[0]; - inputChannels[1].transferToBinderOutParameter(outInputChannel); - - mInputManager.registerInputChannel(win.mInputChannel); - } + if (outInputChannel != null) { + String name = win.makeInputChannelName(); + InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); + win.mInputChannel = inputChannels[0]; + inputChannels[1].transferToBinderOutParameter(outInputChannel); + + mInputManager.registerInputChannel(win.mInputChannel); } // From now on, no exceptions or errors allowed! @@ -2186,14 +2059,7 @@ public class WindowManagerService extends IWindowManager.Stub } private void removeWindowInnerLocked(Session session, WindowState win) { - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.windowIsBeingRemovedLw(win); - } else { - mKeyWaiter.finishedKey(session, win.mClient, true, - KeyWaiter.RETURN_NOTHING); - mKeyWaiter.releasePendingPointerLocked(win.mSession); - mKeyWaiter.releasePendingTrackballLocked(win.mSession); - } + mInputMonitor.windowIsBeingRemovedLw(win); win.mRemoved = true; @@ -2561,12 +2427,7 @@ public class WindowManagerService extends IWindowManager.Stub applyAnimationLocked(win, transit, false)) { focusMayChange = true; win.mExiting = true; - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.windowIsBecomingInvisibleLw(win); - } else { - mKeyWaiter.finishedKey(session, client, true, - KeyWaiter.RETURN_NOTHING); - } + mInputMonitor.windowIsBecomingInvisibleLw(win); } else if (win.isAnimating()) { // Currently in a hide animation... turn this into // an exit. @@ -3027,12 +2888,7 @@ public class WindowManagerService extends IWindowManager.Stub if (win.isVisibleNow()) { applyAnimationLocked(win, WindowManagerPolicy.TRANSIT_EXIT, false); - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.windowIsBeingRemovedLw(win); - } else { - mKeyWaiter.finishedKey(win.mSession, win.mClient, true, - KeyWaiter.RETURN_NOTHING); - } + mInputMonitor.windowIsBeingRemovedLw(win); changed = true; } } @@ -3349,12 +3205,8 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_FOCUS) Slog.v(TAG, "Clearing focused app, was " + mFocusedApp); changed = mFocusedApp != null; mFocusedApp = null; - if (ENABLE_NATIVE_INPUT_DISPATCH) { - if (changed) { - mInputMonitor.setFocusedAppLw(null); - } - } else { - mKeyWaiter.tickle(); + if (changed) { + mInputMonitor.setFocusedAppLw(null); } } else { AppWindowToken newFocus = findAppWindowToken(token); @@ -3365,12 +3217,8 @@ public class WindowManagerService extends IWindowManager.Stub changed = mFocusedApp != newFocus; mFocusedApp = newFocus; if (DEBUG_FOCUS) Slog.v(TAG, "Set focused app to: " + mFocusedApp); - if (ENABLE_NATIVE_INPUT_DISPATCH) { - if (changed) { - mInputMonitor.setFocusedAppLw(newFocus); - } - } else { - mKeyWaiter.tickle(); + if (changed) { + mInputMonitor.setFocusedAppLw(newFocus); } } @@ -3682,12 +3530,7 @@ public class WindowManagerService extends IWindowManager.Stub applyAnimationLocked(win, WindowManagerPolicy.TRANSIT_EXIT, false); } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.windowIsBecomingInvisibleLw(win); - } else { - mKeyWaiter.finishedKey(win.mSession, win.mClient, true, - KeyWaiter.RETURN_NOTHING); - } + mInputMonitor.windowIsBecomingInvisibleLw(win); changed = true; } } @@ -3972,11 +3815,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_FOCUS) Slog.v(TAG, "Removing focused app token:" + wtoken); mFocusedApp = null; updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL); - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.setFocusedAppLw(null); - } else { - mKeyWaiter.tickle(); - } + mInputMonitor.setFocusedAppLw(null); } } else { Slog.w(TAG, "Attempted to remove non-existing app token: " + token); @@ -4441,11 +4280,7 @@ public class WindowManagerService extends IWindowManager.Stub "getSwitchState()")) { throw new SecurityException("Requires READ_INPUT_STATE permission"); } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - return mInputManager.getSwitchState(sw); - } else { - return KeyInputQueue.getSwitchState(sw); - } + return mInputManager.getSwitchState(sw); } public int getSwitchStateForDevice(int devid, int sw) { @@ -4453,11 +4288,7 @@ public class WindowManagerService extends IWindowManager.Stub "getSwitchStateForDevice()")) { throw new SecurityException("Requires READ_INPUT_STATE permission"); } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - return mInputManager.getSwitchState(devid, sw); - } else { - return KeyInputQueue.getSwitchState(devid, sw); - } + return mInputManager.getSwitchState(devid, sw); } public int getScancodeState(int sw) { @@ -4465,11 +4296,7 @@ public class WindowManagerService extends IWindowManager.Stub "getScancodeState()")) { throw new SecurityException("Requires READ_INPUT_STATE permission"); } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - return mInputManager.getScancodeState(sw); - } else { - return mQueue.getScancodeState(sw); - } + return mInputManager.getScancodeState(sw); } public int getScancodeStateForDevice(int devid, int sw) { @@ -4477,11 +4304,7 @@ public class WindowManagerService extends IWindowManager.Stub "getScancodeStateForDevice()")) { throw new SecurityException("Requires READ_INPUT_STATE permission"); } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - return mInputManager.getScancodeState(devid, sw); - } else { - return mQueue.getScancodeState(devid, sw); - } + return mInputManager.getScancodeState(devid, sw); } public int getTrackballScancodeState(int sw) { @@ -4489,11 +4312,7 @@ public class WindowManagerService extends IWindowManager.Stub "getTrackballScancodeState()")) { throw new SecurityException("Requires READ_INPUT_STATE permission"); } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - return mInputManager.getTrackballScancodeState(sw); - } else { - return mQueue.getTrackballScancodeState(sw); - } + return mInputManager.getTrackballScancodeState(sw); } public int getDPadScancodeState(int sw) { @@ -4501,11 +4320,7 @@ public class WindowManagerService extends IWindowManager.Stub "getDPadScancodeState()")) { throw new SecurityException("Requires READ_INPUT_STATE permission"); } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - return mInputManager.getDPadScancodeState(sw); - } else { - return mQueue.getDPadScancodeState(sw); - } + return mInputManager.getDPadScancodeState(sw); } public int getKeycodeState(int sw) { @@ -4513,11 +4328,7 @@ public class WindowManagerService extends IWindowManager.Stub "getKeycodeState()")) { throw new SecurityException("Requires READ_INPUT_STATE permission"); } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - return mInputManager.getKeycodeState(sw); - } else { - return mQueue.getKeycodeState(sw); - } + return mInputManager.getKeycodeState(sw); } public int getKeycodeStateForDevice(int devid, int sw) { @@ -4525,11 +4336,7 @@ public class WindowManagerService extends IWindowManager.Stub "getKeycodeStateForDevice()")) { throw new SecurityException("Requires READ_INPUT_STATE permission"); } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - return mInputManager.getKeycodeState(devid, sw); - } else { - return mQueue.getKeycodeState(devid, sw); - } + return mInputManager.getKeycodeState(devid, sw); } public int getTrackballKeycodeState(int sw) { @@ -4537,11 +4344,7 @@ public class WindowManagerService extends IWindowManager.Stub "getTrackballKeycodeState()")) { throw new SecurityException("Requires READ_INPUT_STATE permission"); } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - return mInputManager.getTrackballKeycodeState(sw); - } else { - return mQueue.getTrackballKeycodeState(sw); - } + return mInputManager.getTrackballKeycodeState(sw); } public int getDPadKeycodeState(int sw) { @@ -4549,19 +4352,11 @@ public class WindowManagerService extends IWindowManager.Stub "getDPadKeycodeState()")) { throw new SecurityException("Requires READ_INPUT_STATE permission"); } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - return mInputManager.getDPadKeycodeState(sw); - } else { - return mQueue.getDPadKeycodeState(sw); - } + return mInputManager.getDPadKeycodeState(sw); } public boolean hasKeys(int[] keycodes, boolean[] keyExists) { - if (ENABLE_NATIVE_INPUT_DISPATCH) { - return mInputManager.hasKeys(keycodes, keyExists); - } else { - return KeyInputQueue.hasKeys(keycodes, keyExists); - } + return mInputManager.hasKeys(keycodes, keyExists); } public void enableScreenAfterBoot() { @@ -4706,11 +4501,7 @@ public class WindowManagerService extends IWindowManager.Stub mLayoutNeeded = true; startFreezingDisplayLocked(); Slog.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags); - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputManager.setDisplayOrientation(0, rotation); - } else { - mQueue.setOrientation(rotation); - } + mInputManager.setDisplayOrientation(0, rotation); if (mDisplayEnabled) { Surface.setOrientation(0, rotation, animFlags); } @@ -5041,11 +4832,8 @@ public class WindowManagerService extends IWindowManager.Stub if (mDisplay == null) { return false; } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputManager.getInputConfiguration(config); - } else { - mQueue.getInputConfiguration(config); - } + + mInputManager.getInputConfiguration(config); // Use the effective "visual" dimensions based on current rotation final boolean rotated = (mRotation == Surface.ROTATION_90 @@ -5326,6 +5114,13 @@ public class WindowManagerService extends IWindowManager.Stub mTempInputWindows.clear(); } + /* Provides feedback for a virtual key down. */ + public void virtualKeyDownFeedback() { + synchronized (mWindowMap) { + mPolicy.performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); + } + } + /* Notifies that an app switch key (BACK / HOME) has just been pressed. * This essentially starts a .5 second timeout for the application to process * subsequent input events while waiting for the app switch to occur. If it takes longer @@ -5334,30 +5129,28 @@ public class WindowManagerService extends IWindowManager.Stub public void notifyAppSwitchComing() { // TODO Not implemented yet. Should go in the native side. } - + + /* Notifies that the lid switch changed state. */ + public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { + mPolicy.notifyLidSwitchChanged(whenNanos, lidOpen); + } + /* Provides an opportunity for the window manager policy to intercept early key * processing as soon as the key has been read from the device. */ - public int interceptKeyBeforeQueueing(int deviceId, int type, int scanCode, - int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) { - RawInputEvent event = new RawInputEvent(); - event.deviceId = deviceId; - event.type = type; - event.scancode = scanCode; - event.keycode = keyCode; - event.flags = policyFlags; - event.value = value; - event.when = whenNanos / 1000000; - - return mPolicy.interceptKeyTq(event, isScreenOn); + public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, + int policyFlags, boolean isScreenOn) { + return mPolicy.interceptKeyBeforeQueueing(whenNanos, + keyCode, down, policyFlags, isScreenOn); } /* Provides an opportunity for the window manager policy to process a key before * ordinary dispatch. */ - public boolean interceptKeyBeforeDispatching(InputChannel focus, int keyCode, - int metaState, boolean down, int repeatCount, int policyFlags) { + public boolean interceptKeyBeforeDispatching(InputChannel focus, + int action, int flags, int keyCode, int metaState, int repeatCount, + int policyFlags) { WindowState windowState = getWindowStateForInputChannel(focus); - return mPolicy.interceptKeyTi(windowState, keyCode, metaState, down, repeatCount, - policyFlags); + return mPolicy.interceptKeyBeforeDispatching(windowState, action, flags, + keyCode, metaState, repeatCount, policyFlags); } /* Called when the current input focus changes. @@ -5496,450 +5289,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - private final void wakeupIfNeeded(WindowState targetWin, int eventType) { - long curTime = SystemClock.uptimeMillis(); - - if (eventType == TOUCH_EVENT || eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT) { - if (mLastTouchEventType == eventType && - (curTime - mLastUserActivityCallTime) < MIN_TIME_BETWEEN_USERACTIVITIES) { - return; - } - mLastUserActivityCallTime = curTime; - mLastTouchEventType = eventType; - } - - if (targetWin == null - || targetWin.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) { - mPowerManager.userActivity(curTime, false, eventType, false); - } - } - - // tells if it's a cheek event or not -- this function is stateful - private static final int EVENT_NONE = 0; - private static final int EVENT_UNKNOWN = 0; - private static final int EVENT_CHEEK = 0; - private static final int EVENT_IGNORE_DURATION = 300; // ms - private static final float CHEEK_THRESHOLD = 0.6f; - private int mEventState = EVENT_NONE; - private float mEventSize; - - private int eventType(MotionEvent ev) { - float size = ev.getSize(); - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - mEventSize = size; - return (mEventSize > CHEEK_THRESHOLD) ? CHEEK_EVENT : TOUCH_EVENT; - case MotionEvent.ACTION_UP: - if (size > mEventSize) mEventSize = size; - return (mEventSize > CHEEK_THRESHOLD) ? CHEEK_EVENT : TOUCH_UP_EVENT; - case MotionEvent.ACTION_MOVE: - final int N = ev.getHistorySize(); - if (size > mEventSize) mEventSize = size; - if (mEventSize > CHEEK_THRESHOLD) return CHEEK_EVENT; - for (int i=0; i<N; i++) { - size = ev.getHistoricalSize(i); - if (size > mEventSize) mEventSize = size; - if (mEventSize > CHEEK_THRESHOLD) return CHEEK_EVENT; - } - if (ev.getEventTime() < ev.getDownTime() + EVENT_IGNORE_DURATION) { - return TOUCH_EVENT; - } else { - return LONG_TOUCH_EVENT; - } - default: - // not good - return OTHER_EVENT; - } - } - - private boolean mFatTouch; // remove me together with dispatchPointer - - /** - * @return Returns true if event was dispatched, false if it was dropped for any reason - */ - private int dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) { - if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Slog.v(TAG, - "dispatchPointer " + ev); - - if (MEASURE_LATENCY) { - lt.sample("3 Wait for last dispatch ", System.nanoTime() - qev.whenNano); - } - - Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev, - ev, true, false, pid, uid); - - if (MEASURE_LATENCY) { - lt.sample("3 Last dispatch finished ", System.nanoTime() - qev.whenNano); - } - - int action = ev.getAction(); - - if (action == MotionEvent.ACTION_UP) { - // let go of our target - mKeyWaiter.mMotionTarget = null; - mPowerManager.logPointerUpEvent(); - } else if (action == MotionEvent.ACTION_DOWN) { - mPowerManager.logPointerDownEvent(); - } - - if (targetObj == null) { - // In this case we are either dropping the event, or have received - // a move or up without a down. It is common to receive move - // events in such a way, since this means the user is moving the - // pointer without actually pressing down. All other cases should - // be atypical, so let's log them. - if (action != MotionEvent.ACTION_MOVE) { - Slog.w(TAG, "No window to dispatch pointer action " + ev.getAction()); - } - synchronized (mWindowMap) { - dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true); - } - if (qev != null) { - mQueue.recycleEvent(qev); - } - ev.recycle(); - return INJECT_FAILED; - } - if (targetObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { - synchronized (mWindowMap) { - dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), true); - } - if (qev != null) { - mQueue.recycleEvent(qev); - } - ev.recycle(); - return INJECT_SUCCEEDED; - } - - WindowState target = (WindowState)targetObj; - - final long eventTime = ev.getEventTime(); - final long eventTimeNano = ev.getEventTimeNano(); - - //Slog.i(TAG, "Sending " + ev + " to " + target); - - if (uid != 0 && uid != target.mSession.mUid) { - if (mContext.checkPermission( - android.Manifest.permission.INJECT_EVENTS, pid, uid) - != PackageManager.PERMISSION_GRANTED) { - Slog.w(TAG, "Permission denied: injecting pointer event from pid " - + pid + " uid " + uid + " to window " + target - + " owned by uid " + target.mSession.mUid); - if (qev != null) { - mQueue.recycleEvent(qev); - } - ev.recycle(); - return INJECT_NO_PERMISSION; - } - } - - if (MEASURE_LATENCY) { - lt.sample("4 in dispatchPointer ", System.nanoTime() - eventTimeNano); - } - - if ((target.mAttrs.flags & - WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) { - //target wants to ignore fat touch events - boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(ev); - //explicit flag to return without processing event further - boolean returnFlag = false; - if((action == MotionEvent.ACTION_DOWN)) { - mFatTouch = false; - if(cheekPress) { - mFatTouch = true; - returnFlag = true; - } - } else { - if(action == MotionEvent.ACTION_UP) { - if(mFatTouch) { - //earlier even was invalid doesnt matter if current up is cheekpress or not - mFatTouch = false; - returnFlag = true; - } else if(cheekPress) { - //cancel the earlier event - ev.setAction(MotionEvent.ACTION_CANCEL); - action = MotionEvent.ACTION_CANCEL; - } - } else if(action == MotionEvent.ACTION_MOVE) { - if(mFatTouch) { - //two cases here - //an invalid down followed by 0 or moves(valid or invalid) - //a valid down, invalid move, more moves. want to ignore till up - returnFlag = true; - } else if(cheekPress) { - //valid down followed by invalid moves - //an invalid move have to cancel earlier action - ev.setAction(MotionEvent.ACTION_CANCEL); - action = MotionEvent.ACTION_CANCEL; - if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE"); - //note that the subsequent invalid moves will not get here - mFatTouch = true; - } - } - } //else if action - if(returnFlag) { - //recycle que, ev - if (qev != null) { - mQueue.recycleEvent(qev); - } - ev.recycle(); - return INJECT_FAILED; - } - } //end if target - - // Enable this for testing the "right" value - if (false && action == MotionEvent.ACTION_DOWN) { - int max_events_per_sec = 35; - try { - max_events_per_sec = Integer.parseInt(SystemProperties - .get("windowsmgr.max_events_per_sec")); - if (max_events_per_sec < 1) { - max_events_per_sec = 35; - } - } catch (NumberFormatException e) { - } - mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec; - } - - /* - * Throttle events to minimize CPU usage when there's a flood of events - * e.g. constant contact with the screen - */ - if (action == MotionEvent.ACTION_MOVE) { - long nextEventTime = mLastTouchEventTime + mMinWaitTimeBetweenTouchEvents; - long now = SystemClock.uptimeMillis(); - if (now < nextEventTime) { - try { - Thread.sleep(nextEventTime - now); - } catch (InterruptedException e) { - } - mLastTouchEventTime = nextEventTime; - } else { - mLastTouchEventTime = now; - } - } - - if (MEASURE_LATENCY) { - lt.sample("5 in dispatchPointer ", System.nanoTime() - eventTimeNano); - } - - synchronized(mWindowMap) { - if (!target.isVisibleLw()) { - // During this motion dispatch, the target window has become - // invisible. - dispatchPointerElsewhereLocked(null, null, ev, ev.getEventTime(), false); - if (qev != null) { - mQueue.recycleEvent(qev); - } - ev.recycle(); - return INJECT_SUCCEEDED; - } - - if (qev != null && action == MotionEvent.ACTION_MOVE) { - mKeyWaiter.bindTargetWindowLocked(target, - KeyWaiter.RETURN_PENDING_POINTER, qev); - ev = null; - } else { - if (action == MotionEvent.ACTION_DOWN) { - WindowState out = mKeyWaiter.mOutsideTouchTargets; - if (out != null) { - MotionEvent oev = MotionEvent.obtain(ev); - oev.setAction(MotionEvent.ACTION_OUTSIDE); - do { - final Rect frame = out.mFrame; - oev.offsetLocation(-(float)frame.left, -(float)frame.top); - try { - out.mClient.dispatchPointer(oev, eventTime, false); - } catch (android.os.RemoteException e) { - Slog.i(TAG, "WINDOW DIED during outside motion dispatch: " + out); - } - oev.offsetLocation((float)frame.left, (float)frame.top); - out = out.mNextOutsideTouch; - } while (out != null); - mKeyWaiter.mOutsideTouchTargets = null; - } - } - - dispatchPointerElsewhereLocked(target, null, ev, ev.getEventTime(), false); - - final Rect frame = target.mFrame; - ev.offsetLocation(-(float)frame.left, -(float)frame.top); - mKeyWaiter.bindTargetWindowLocked(target); - } - } - - // finally offset the event to the target's coordinate system and - // dispatch the event. - try { - if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) { - Slog.v(TAG, "Delivering pointer " + qev + " to " + target); - } - - if (MEASURE_LATENCY) { - lt.sample("6 before svr->client ipc ", System.nanoTime() - eventTimeNano); - } - - target.mClient.dispatchPointer(ev, eventTime, true); - - if (MEASURE_LATENCY) { - lt.sample("7 after svr->client ipc ", System.nanoTime() - eventTimeNano); - } - return INJECT_SUCCEEDED; - } catch (android.os.RemoteException e) { - Slog.i(TAG, "WINDOW DIED during motion dispatch: " + target); - mKeyWaiter.mMotionTarget = null; - try { - removeWindow(target.mSession, target.mClient); - } catch (java.util.NoSuchElementException ex) { - // This will happen if the window has already been - // removed. - } - } - return INJECT_FAILED; - } - - /** - * @return Returns true if event was dispatched, false if it was dropped for any reason - */ - private int dispatchTrackball(QueuedEvent qev, MotionEvent ev, int pid, int uid) { - if (DEBUG_INPUT) Slog.v( - TAG, "dispatchTrackball [" + ev.getAction() +"] <" + ev.getX() + ", " + ev.getY() + ">"); - - Object focusObj = mKeyWaiter.waitForNextEventTarget(null, qev, - ev, false, false, pid, uid); - if (focusObj == null) { - Slog.w(TAG, "No focus window, dropping trackball: " + ev); - if (qev != null) { - mQueue.recycleEvent(qev); - } - ev.recycle(); - return INJECT_FAILED; - } - if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { - if (qev != null) { - mQueue.recycleEvent(qev); - } - ev.recycle(); - return INJECT_SUCCEEDED; - } - - WindowState focus = (WindowState)focusObj; - - if (uid != 0 && uid != focus.mSession.mUid) { - if (mContext.checkPermission( - android.Manifest.permission.INJECT_EVENTS, pid, uid) - != PackageManager.PERMISSION_GRANTED) { - Slog.w(TAG, "Permission denied: injecting key event from pid " - + pid + " uid " + uid + " to window " + focus - + " owned by uid " + focus.mSession.mUid); - if (qev != null) { - mQueue.recycleEvent(qev); - } - ev.recycle(); - return INJECT_NO_PERMISSION; - } - } - - final long eventTime = ev.getEventTime(); - - synchronized(mWindowMap) { - if (qev != null && ev.getAction() == MotionEvent.ACTION_MOVE) { - mKeyWaiter.bindTargetWindowLocked(focus, - KeyWaiter.RETURN_PENDING_TRACKBALL, qev); - // We don't deliver movement events to the client, we hold - // them and wait for them to call back. - ev = null; - } else { - mKeyWaiter.bindTargetWindowLocked(focus); - } - } - - try { - focus.mClient.dispatchTrackball(ev, eventTime, true); - return INJECT_SUCCEEDED; - } catch (android.os.RemoteException e) { - Slog.i(TAG, "WINDOW DIED during key dispatch: " + focus); - try { - removeWindow(focus.mSession, focus.mClient); - } catch (java.util.NoSuchElementException ex) { - // This will happen if the window has already been - // removed. - } - } - - return INJECT_FAILED; - } - - /** - * @return Returns true if event was dispatched, false if it was dropped for any reason - */ - private int dispatchKey(KeyEvent event, int pid, int uid) { - if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event); - - Object focusObj = mKeyWaiter.waitForNextEventTarget(event, null, - null, false, false, pid, uid); - if (focusObj == null) { - Slog.w(TAG, "No focus window, dropping: " + event); - return INJECT_FAILED; - } - if (focusObj == mKeyWaiter.CONSUMED_EVENT_TOKEN) { - return INJECT_SUCCEEDED; - } - - // Okay we have finished waiting for the last event to be processed. - // First off, if this is a repeat event, check to see if there is - // a corresponding up event in the queue. If there is, we will - // just drop the repeat, because it makes no sense to repeat after - // the user has released a key. (This is especially important for - // long presses.) - if (event.getRepeatCount() > 0 && mQueue.hasKeyUpEvent(event)) { - return INJECT_SUCCEEDED; - } - - WindowState focus = (WindowState)focusObj; - - if (DEBUG_INPUT) Slog.v( - TAG, "Dispatching to " + focus + ": " + event); - - if (uid != 0 && uid != focus.mSession.mUid) { - if (mContext.checkPermission( - android.Manifest.permission.INJECT_EVENTS, pid, uid) - != PackageManager.PERMISSION_GRANTED) { - Slog.w(TAG, "Permission denied: injecting key event from pid " - + pid + " uid " + uid + " to window " + focus - + " owned by uid " + focus.mSession.mUid); - return INJECT_NO_PERMISSION; - } - } - - synchronized(mWindowMap) { - mKeyWaiter.bindTargetWindowLocked(focus); - } - - // NOSHIP extra state logging - mKeyWaiter.recordDispatchState(event, focus); - // END NOSHIP - - try { - if (DEBUG_INPUT || DEBUG_FOCUS) { - Slog.v(TAG, "Delivering key " + event.getKeyCode() - + " to " + focus); - } - focus.mClient.dispatchKey(event); - return INJECT_SUCCEEDED; - } catch (android.os.RemoteException e) { - Slog.i(TAG, "WINDOW DIED during key dispatch: " + focus); - try { - removeWindow(focus.mSession, focus.mClient); - } catch (java.util.NoSuchElementException ex) { - // This will happen if the window has already been - // removed. - } - } - - return INJECT_FAILED; - } - public void pauseKeyDispatching(IBinder _token) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "pauseKeyDispatching()")) { @@ -5949,11 +5298,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { WindowToken token = mTokenMap.get(_token); if (token != null) { - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.pauseDispatchingLw(token); - } else { - mKeyWaiter.pauseDispatchingLocked(token); - } + mInputMonitor.pauseDispatchingLw(token); } } } @@ -5967,11 +5312,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { WindowToken token = mTokenMap.get(_token); if (token != null) { - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.resumeDispatchingLw(token); - } else { - mKeyWaiter.resumeDispatchingLocked(token); - } + mInputMonitor.resumeDispatchingLw(token); } } } @@ -5983,11 +5324,7 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized (mWindowMap) { - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.setEventDispatchingLw(enabled); - } else { - mKeyWaiter.setEventDispatchingLocked(enabled); - } + mInputMonitor.setEventDispatchingLw(enabled); } } @@ -6020,16 +5357,8 @@ public class WindowManagerService extends IWindowManager.Stub final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); - final int result; - if (ENABLE_NATIVE_INPUT_DISPATCH) { - result = mInputManager.injectKeyEvent(newEvent, InputQueue.INPUT_EVENT_NATURE_KEY, - pid, uid, sync, INJECTION_TIMEOUT_MILLIS); - } else { - result = dispatchKey(newEvent, pid, uid); - if (sync) { - mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid); - } - } + final int result = mInputManager.injectKeyEvent(newEvent, + InputQueue.INPUT_EVENT_NATURE_KEY, pid, uid, sync, INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); return reportInjectionResult(result); @@ -6049,16 +5378,8 @@ public class WindowManagerService extends IWindowManager.Stub final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); - final int result; - if (ENABLE_NATIVE_INPUT_DISPATCH) { - result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TOUCH, - pid, uid, sync, INJECTION_TIMEOUT_MILLIS); - } else { - result = dispatchPointer(null, ev, pid, uid); - if (sync) { - mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid); - } - } + final int result = mInputManager.injectMotionEvent(ev, + InputQueue.INPUT_EVENT_NATURE_TOUCH, pid, uid, sync, INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); return reportInjectionResult(result); @@ -6078,48 +5399,29 @@ public class WindowManagerService extends IWindowManager.Stub final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); - final int result; - if (ENABLE_NATIVE_INPUT_DISPATCH) { - result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TRACKBALL, - pid, uid, sync, INJECTION_TIMEOUT_MILLIS); - } else { - result = dispatchTrackball(null, ev, pid, uid); - if (sync) { - mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid); - } - } + final int result = mInputManager.injectMotionEvent(ev, + InputQueue.INPUT_EVENT_NATURE_TRACKBALL, pid, uid, sync, INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); return reportInjectionResult(result); } private boolean reportInjectionResult(int result) { - if (ENABLE_NATIVE_INPUT_DISPATCH) { - switch (result) { - case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED: - Slog.w(TAG, "Input event injection permission denied."); - throw new SecurityException( - "Injecting to another application requires INJECT_EVENTS permission"); - case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED: - Slog.v(TAG, "Input event injection succeeded."); - return true; - case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT: - Slog.w(TAG, "Input event injection timed out."); - return false; - case InputManager.INPUT_EVENT_INJECTION_FAILED: - default: - Slog.w(TAG, "Input event injection failed."); - return false; - } - } else { - switch (result) { - case INJECT_NO_PERMISSION: - throw new SecurityException( - "Injecting to another application requires INJECT_EVENTS permission"); - case INJECT_SUCCEEDED: - return true; - } - return false; + switch (result) { + case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED: + Slog.w(TAG, "Input event injection permission denied."); + throw new SecurityException( + "Injecting to another application requires INJECT_EVENTS permission"); + case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED: + Slog.v(TAG, "Input event injection succeeded."); + return true; + case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT: + Slog.w(TAG, "Input event injection timed out."); + return false; + case InputManager.INPUT_EVENT_INJECTION_FAILED: + default: + Slog.w(TAG, "Input event injection failed."); + return false; } } @@ -6133,867 +5435,6 @@ public class WindowManagerService extends IWindowManager.Stub return mCurrentFocus; } - /** - * This class holds the state for dispatching key events. This state - * is protected by the KeyWaiter instance, NOT by the window lock. You - * can be holding the main window lock while acquire the KeyWaiter lock, - * but not the other way around. - */ - final class KeyWaiter { - // NOSHIP debugging - public class DispatchState { - private KeyEvent event; - private WindowState focus; - private long time; - private WindowState lastWin; - private IBinder lastBinder; - private boolean finished; - private boolean gotFirstWindow; - private boolean eventDispatching; - private long timeToSwitch; - private boolean wasFrozen; - private boolean focusPaused; - private WindowState curFocus; - - DispatchState(KeyEvent theEvent, WindowState theFocus) { - focus = theFocus; - event = theEvent; - time = System.currentTimeMillis(); - // snapshot KeyWaiter state - lastWin = mLastWin; - lastBinder = mLastBinder; - finished = mFinished; - gotFirstWindow = mGotFirstWindow; - eventDispatching = mEventDispatching; - timeToSwitch = mTimeToSwitch; - wasFrozen = mWasFrozen; - curFocus = mCurrentFocus; - // cache the paused state at ctor time as well - if (theFocus == null || theFocus.mToken == null) { - focusPaused = false; - } else { - focusPaused = theFocus.mToken.paused; - } - } - - public String toString() { - return "{{" + event + " to " + focus + " @ " + time - + " lw=" + lastWin + " lb=" + lastBinder - + " fin=" + finished + " gfw=" + gotFirstWindow - + " ed=" + eventDispatching + " tts=" + timeToSwitch - + " wf=" + wasFrozen + " fp=" + focusPaused - + " mcf=" + curFocus + "}}"; - } - }; - private DispatchState mDispatchState = null; - public void recordDispatchState(KeyEvent theEvent, WindowState theFocus) { - mDispatchState = new DispatchState(theEvent, theFocus); - } - // END NOSHIP - - public static final int RETURN_NOTHING = 0; - public static final int RETURN_PENDING_POINTER = 1; - public static final int RETURN_PENDING_TRACKBALL = 2; - - final Object SKIP_TARGET_TOKEN = new Object(); - final Object CONSUMED_EVENT_TOKEN = new Object(); - - private WindowState mLastWin = null; - private IBinder mLastBinder = null; - private boolean mFinished = true; - private boolean mGotFirstWindow = false; - private boolean mEventDispatching = true; - private long mTimeToSwitch = 0; - /* package */ boolean mWasFrozen = false; - - // Target of Motion events - WindowState mMotionTarget; - - // Windows above the target who would like to receive an "outside" - // touch event for any down events outside of them. - WindowState mOutsideTouchTargets; - - /** - * Wait for the last event dispatch to complete, then find the next - * target that should receive the given event and wait for that one - * to be ready to receive it. - */ - Object waitForNextEventTarget(KeyEvent nextKey, QueuedEvent qev, - MotionEvent nextMotion, boolean isPointerEvent, - boolean failIfTimeout, int callingPid, int callingUid) { - long startTime = SystemClock.uptimeMillis(); - long keyDispatchingTimeout = 5 * 1000; - long waitedFor = 0; - - while (true) { - // Figure out which window we care about. It is either the - // last window we are waiting to have process the event or, - // if none, then the next window we think the event should go - // to. Note: we retrieve mLastWin outside of the lock, so - // it may change before we lock. Thus we must check it again. - WindowState targetWin = mLastWin; - boolean targetIsNew = targetWin == null; - if (DEBUG_INPUT) Slog.v( - TAG, "waitForLastKey: mFinished=" + mFinished + - ", mLastWin=" + mLastWin); - if (targetIsNew) { - Object target = findTargetWindow(nextKey, qev, nextMotion, - isPointerEvent, callingPid, callingUid); - if (target == SKIP_TARGET_TOKEN) { - // The user has pressed a special key, and we are - // dropping all pending events before it. - if (DEBUG_INPUT) Slog.v(TAG, "Skipping: " + nextKey - + " " + nextMotion); - return null; - } - if (target == CONSUMED_EVENT_TOKEN) { - if (DEBUG_INPUT) Slog.v(TAG, "Consumed: " + nextKey - + " " + nextMotion); - return target; - } - targetWin = (WindowState)target; - } - - AppWindowToken targetApp = null; - - // Now: is it okay to send the next event to this window? - synchronized (this) { - // First: did we come here based on the last window not - // being null, but it changed by the time we got here? - // If so, try again. - if (!targetIsNew && mLastWin == null) { - continue; - } - - // We never dispatch events if not finished with the - // last one, or the display is frozen. - if (mFinished && !mDisplayFrozen) { - // If event dispatching is disabled, then we - // just consume the events. - if (!mEventDispatching) { - if (DEBUG_INPUT) Slog.v(TAG, - "Skipping event; dispatching disabled: " - + nextKey + " " + nextMotion); - return null; - } - if (targetWin != null) { - // If this is a new target, and that target is not - // paused or unresponsive, then all looks good to - // handle the event. - if (targetIsNew && !targetWin.mToken.paused) { - return targetWin; - } - - // If we didn't find a target window, and there is no - // focused app window, then just eat the events. - } else if (mFocusedApp == null) { - if (DEBUG_INPUT) Slog.v(TAG, - "Skipping event; no focused app: " - + nextKey + " " + nextMotion); - return null; - } - } - - if (DEBUG_INPUT) Slog.v( - TAG, "Waiting for last key in " + mLastBinder - + " target=" + targetWin - + " mFinished=" + mFinished - + " mDisplayFrozen=" + mDisplayFrozen - + " targetIsNew=" + targetIsNew - + " paused=" - + (targetWin != null ? targetWin.mToken.paused : false) - + " mFocusedApp=" + mFocusedApp - + " mCurrentFocus=" + mCurrentFocus); - - targetApp = targetWin != null - ? targetWin.mAppToken : mFocusedApp; - - long curTimeout = keyDispatchingTimeout; - if (mTimeToSwitch != 0) { - long now = SystemClock.uptimeMillis(); - if (mTimeToSwitch <= now) { - // If an app switch key has been pressed, and we have - // waited too long for the current app to finish - // processing keys, then wait no more! - doFinishedKeyLocked(false); - continue; - } - long switchTimeout = mTimeToSwitch - now; - if (curTimeout > switchTimeout) { - curTimeout = switchTimeout; - } - } - - try { - // after that continue - // processing keys, so we don't get stuck. - if (DEBUG_INPUT) Slog.v( - TAG, "Waiting for key dispatch: " + curTimeout); - wait(curTimeout); - if (DEBUG_INPUT) Slog.v(TAG, "Finished waiting @" - + SystemClock.uptimeMillis() + " startTime=" - + startTime + " switchTime=" + mTimeToSwitch - + " target=" + targetWin + " mLW=" + mLastWin - + " mLB=" + mLastBinder + " fin=" + mFinished - + " mCurrentFocus=" + mCurrentFocus); - } catch (InterruptedException e) { - } - } - - // If we were frozen during configuration change, restart the - // timeout checks from now; otherwise look at whether we timed - // out before awakening. - if (mWasFrozen) { - waitedFor = 0; - mWasFrozen = false; - } else { - waitedFor = SystemClock.uptimeMillis() - startTime; - } - - if (waitedFor >= keyDispatchingTimeout && mTimeToSwitch == 0) { - IApplicationToken at = null; - synchronized (this) { - Slog.w(TAG, "Key dispatching timed out sending to " + - (targetWin != null ? targetWin.mAttrs.getTitle() - : "<null>: no window ready for key dispatch")); - // NOSHIP debugging - Slog.w(TAG, "Previous dispatch state: " + mDispatchState); - Slog.w(TAG, "Current dispatch state: " + - new DispatchState(nextKey, targetWin)); - // END NOSHIP - //dump(); - if (targetWin != null) { - at = targetWin.getAppToken(); - } else if (targetApp != null) { - at = targetApp.appToken; - } - } - - boolean abort = true; - if (at != null) { - try { - long timeout = at.getKeyDispatchingTimeout(); - if (timeout > waitedFor) { - // we did not wait the proper amount of time for this application. - // set the timeout to be the real timeout and wait again. - keyDispatchingTimeout = timeout - waitedFor; - continue; - } else { - abort = at.keyDispatchingTimedOut(); - } - } catch (RemoteException ex) { - } - } - - synchronized (this) { - if (abort && (mLastWin == targetWin || targetWin == null)) { - mFinished = true; - if (mLastWin != null) { - if (DEBUG_INPUT) Slog.v(TAG, - "Window " + mLastWin + - " timed out on key input"); - if (mLastWin.mToken.paused) { - Slog.w(TAG, "Un-pausing dispatching to this window"); - mLastWin.mToken.paused = false; - } - } - if (mMotionTarget == targetWin) { - mMotionTarget = null; - } - mLastWin = null; - mLastBinder = null; - if (failIfTimeout || targetWin == null) { - return null; - } - } else { - Slog.w(TAG, "Continuing to wait for key to be dispatched"); - startTime = SystemClock.uptimeMillis(); - } - } - } - } - } - - Object findTargetWindow(KeyEvent nextKey, QueuedEvent qev, - MotionEvent nextMotion, boolean isPointerEvent, - int callingPid, int callingUid) { - mOutsideTouchTargets = null; - - if (nextKey != null) { - // Find the target window for a normal key event. - final int keycode = nextKey.getKeyCode(); - final int repeatCount = nextKey.getRepeatCount(); - final boolean down = nextKey.getAction() != KeyEvent.ACTION_UP; - boolean dispatch = mKeyWaiter.checkShouldDispatchKey(keycode); - - if (!dispatch) { - if (callingUid == 0 || - mContext.checkPermission( - android.Manifest.permission.INJECT_EVENTS, - callingPid, callingUid) - == PackageManager.PERMISSION_GRANTED) { - mPolicy.interceptKeyTi(null, keycode, - nextKey.getMetaState(), down, repeatCount, - nextKey.getFlags()); - } - Slog.w(TAG, "Event timeout during app switch: dropping " - + nextKey); - return SKIP_TARGET_TOKEN; - } - - // System.out.println("##### [" + SystemClock.uptimeMillis() + "] WindowManagerService.dispatchKey(" + keycode + ", " + down + ", " + repeatCount + ")"); - - WindowState focus = null; - synchronized(mWindowMap) { - focus = getFocusedWindowLocked(); - } - - wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT); - - if (callingUid == 0 || - (focus != null && callingUid == focus.mSession.mUid) || - mContext.checkPermission( - android.Manifest.permission.INJECT_EVENTS, - callingPid, callingUid) - == PackageManager.PERMISSION_GRANTED) { - if (mPolicy.interceptKeyTi(focus, - keycode, nextKey.getMetaState(), down, repeatCount, - nextKey.getFlags())) { - return CONSUMED_EVENT_TOKEN; - } - } - - return focus; - - } else if (!isPointerEvent) { - boolean dispatch = mKeyWaiter.checkShouldDispatchKey(-1); - if (!dispatch) { - Slog.w(TAG, "Event timeout during app switch: dropping trackball " - + nextMotion); - return SKIP_TARGET_TOKEN; - } - - WindowState focus = null; - synchronized(mWindowMap) { - focus = getFocusedWindowLocked(); - } - - wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT); - return focus; - } - - if (nextMotion == null) { - return SKIP_TARGET_TOKEN; - } - - boolean dispatch = mKeyWaiter.checkShouldDispatchKey( - KeyEvent.KEYCODE_UNKNOWN); - if (!dispatch) { - Slog.w(TAG, "Event timeout during app switch: dropping pointer " - + nextMotion); - return SKIP_TARGET_TOKEN; - } - - // Find the target window for a pointer event. - int action = nextMotion.getAction(); - final float xf = nextMotion.getX(); - final float yf = nextMotion.getY(); - final long eventTime = nextMotion.getEventTime(); - - final boolean screenWasOff = qev != null - && (qev.flags&WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0; - - WindowState target = null; - - synchronized(mWindowMap) { - synchronized (this) { - if (action == MotionEvent.ACTION_DOWN) { - if (mMotionTarget != null) { - // this is weird, we got a pen down, but we thought it was - // already down! - // XXX: We should probably send an ACTION_UP to the current - // target. - Slog.w(TAG, "Pointer down received while already down in: " - + mMotionTarget); - mMotionTarget = null; - } - - // ACTION_DOWN is special, because we need to lock next events to - // the window we'll land onto. - final int x = (int)xf; - final int y = (int)yf; - - final ArrayList windows = mWindows; - final int N = windows.size(); - WindowState topErrWindow = null; - final Rect tmpRect = mTempRect; - for (int i=N-1; i>=0; i--) { - WindowState child = (WindowState)windows.get(i); - //Slog.i(TAG, "Checking dispatch to: " + child); - final int flags = child.mAttrs.flags; - if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) { - if (topErrWindow == null) { - topErrWindow = child; - } - } - if (!child.isVisibleLw()) { - //Slog.i(TAG, "Not visible!"); - continue; - } - if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { - //Slog.i(TAG, "Not touchable!"); - if ((flags & WindowManager.LayoutParams - .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { - child.mNextOutsideTouch = mOutsideTouchTargets; - mOutsideTouchTargets = child; - } - continue; - } - tmpRect.set(child.mFrame); - if (child.mTouchableInsets == ViewTreeObserver - .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) { - // The touch is inside of the window if it is - // inside the frame, AND the content part of that - // frame that was given by the application. - tmpRect.left += child.mGivenContentInsets.left; - tmpRect.top += child.mGivenContentInsets.top; - tmpRect.right -= child.mGivenContentInsets.right; - tmpRect.bottom -= child.mGivenContentInsets.bottom; - } else if (child.mTouchableInsets == ViewTreeObserver - .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) { - // The touch is inside of the window if it is - // inside the frame, AND the visible part of that - // frame that was given by the application. - tmpRect.left += child.mGivenVisibleInsets.left; - tmpRect.top += child.mGivenVisibleInsets.top; - tmpRect.right -= child.mGivenVisibleInsets.right; - tmpRect.bottom -= child.mGivenVisibleInsets.bottom; - } - final int touchFlags = flags & - (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); - if (tmpRect.contains(x, y) || touchFlags == 0) { - //Slog.i(TAG, "Using this target!"); - if (!screenWasOff || (flags & - WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) { - mMotionTarget = child; - } else { - //Slog.i(TAG, "Waking, skip!"); - mMotionTarget = null; - } - break; - } - - if ((flags & WindowManager.LayoutParams - .FLAG_WATCH_OUTSIDE_TOUCH) != 0) { - child.mNextOutsideTouch = mOutsideTouchTargets; - mOutsideTouchTargets = child; - //Slog.i(TAG, "Adding to outside target list: " + child); - } - } - - // if there's an error window but it's not accepting - // focus (typically because it is not yet visible) just - // wait for it -- any other focused window may in fact - // be in ANR state. - if (topErrWindow != null && mMotionTarget != topErrWindow) { - mMotionTarget = null; - } - } - - target = mMotionTarget; - } - } - - wakeupIfNeeded(target, eventType(nextMotion)); - - // Pointer events are a little different -- if there isn't a - // target found for any event, then just drop it. - return target != null ? target : SKIP_TARGET_TOKEN; - } - - boolean checkShouldDispatchKey(int keycode) { - synchronized (this) { - if (mPolicy.isAppSwitchKeyTqTiLwLi(keycode)) { - mTimeToSwitch = 0; - return true; - } - if (mTimeToSwitch != 0 - && mTimeToSwitch < SystemClock.uptimeMillis()) { - return false; - } - return true; - } - } - - void bindTargetWindowLocked(WindowState win, - int pendingWhat, QueuedEvent pendingMotion) { - synchronized (this) { - bindTargetWindowLockedLocked(win, pendingWhat, pendingMotion); - } - } - - void bindTargetWindowLocked(WindowState win) { - synchronized (this) { - bindTargetWindowLockedLocked(win, RETURN_NOTHING, null); - } - } - - void bindTargetWindowLockedLocked(WindowState win, - int pendingWhat, QueuedEvent pendingMotion) { - mLastWin = win; - mLastBinder = win.mClient.asBinder(); - mFinished = false; - if (pendingMotion != null) { - final Session s = win.mSession; - if (pendingWhat == RETURN_PENDING_POINTER) { - releasePendingPointerLocked(s); - s.mPendingPointerMove = pendingMotion; - s.mPendingPointerWindow = win; - if (DEBUG_INPUT) Slog.v(TAG, - "bindTargetToWindow " + s.mPendingPointerMove); - } else if (pendingWhat == RETURN_PENDING_TRACKBALL) { - releasePendingTrackballLocked(s); - s.mPendingTrackballMove = pendingMotion; - s.mPendingTrackballWindow = win; - } - } - } - - void releasePendingPointerLocked(Session s) { - if (DEBUG_INPUT) Slog.v(TAG, - "releasePendingPointer " + s.mPendingPointerMove); - if (s.mPendingPointerMove != null) { - mQueue.recycleEvent(s.mPendingPointerMove); - s.mPendingPointerMove = null; - } - } - - void releasePendingTrackballLocked(Session s) { - if (s.mPendingTrackballMove != null) { - mQueue.recycleEvent(s.mPendingTrackballMove); - s.mPendingTrackballMove = null; - } - } - - MotionEvent finishedKey(Session session, IWindow client, boolean force, - int returnWhat) { - if (DEBUG_INPUT) Slog.v( - TAG, "finishedKey: client=" + client + ", force=" + force); - - if (client == null) { - return null; - } - - MotionEvent res = null; - QueuedEvent qev = null; - WindowState win = null; - - synchronized (this) { - if (DEBUG_INPUT) Slog.v( - TAG, "finishedKey: client=" + client.asBinder() - + ", force=" + force + ", last=" + mLastBinder - + " (token=" + (mLastWin != null ? mLastWin.mToken : null) + ")"); - - if (returnWhat == RETURN_PENDING_POINTER) { - qev = session.mPendingPointerMove; - win = session.mPendingPointerWindow; - session.mPendingPointerMove = null; - session.mPendingPointerWindow = null; - } else if (returnWhat == RETURN_PENDING_TRACKBALL) { - qev = session.mPendingTrackballMove; - win = session.mPendingTrackballWindow; - session.mPendingTrackballMove = null; - session.mPendingTrackballWindow = null; - } - - if (mLastBinder == client.asBinder()) { - if (DEBUG_INPUT) Slog.v( - TAG, "finishedKey: last paused=" - + ((mLastWin != null) ? mLastWin.mToken.paused : "null")); - if (mLastWin != null && (!mLastWin.mToken.paused || force - || !mEventDispatching)) { - doFinishedKeyLocked(true); - } else { - // Make sure to wake up anyone currently waiting to - // dispatch a key, so they can re-evaluate their - // current situation. - mFinished = true; - notifyAll(); - } - } - - if (qev != null) { - res = (MotionEvent)qev.event; - if (DEBUG_INPUT) Slog.v(TAG, - "Returning pending motion: " + res); - mQueue.recycleEvent(qev); - if (win != null && returnWhat == RETURN_PENDING_POINTER) { - res.offsetLocation(-win.mFrame.left, -win.mFrame.top); - } - } - } - - if (res != null && returnWhat == RETURN_PENDING_POINTER) { - synchronized (mWindowMap) { - dispatchPointerElsewhereLocked(win, win, res, res.getEventTime(), false); - } - } - - return res; - } - - void tickle() { - synchronized (this) { - notifyAll(); - } - } - - void handleNewWindowLocked(WindowState newWindow) { - if (!newWindow.canReceiveKeys()) { - return; - } - synchronized (this) { - if (DEBUG_INPUT) Slog.v( - TAG, "New key dispatch window: win=" - + newWindow.mClient.asBinder() - + ", last=" + mLastBinder - + " (token=" + (mLastWin != null ? mLastWin.mToken : null) - + "), finished=" + mFinished + ", paused=" - + newWindow.mToken.paused); - - // Displaying a window implicitly causes dispatching to - // be unpaused. (This is to protect against bugs if someone - // pauses dispatching but forgets to resume.) - newWindow.mToken.paused = false; - - mGotFirstWindow = true; - - if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) { - if (DEBUG_INPUT) Slog.v(TAG, - "New SYSTEM_ERROR window; resetting state"); - mLastWin = null; - mLastBinder = null; - mMotionTarget = null; - mFinished = true; - } else if (mLastWin != null) { - // If the new window is above the window we are - // waiting on, then stop waiting and let key dispatching - // start on the new guy. - if (DEBUG_INPUT) Slog.v( - TAG, "Last win layer=" + mLastWin.mLayer - + ", new win layer=" + newWindow.mLayer); - if (newWindow.mLayer >= mLastWin.mLayer) { - // The new window is above the old; finish pending input to the last - // window and start directing it to the new one. - mLastWin.mToken.paused = false; - doFinishedKeyLocked(false); // does a notifyAll() - return; - } - } - - // Now that we've put a new window state in place, make the event waiter - // take notice and retarget its attentions. - notifyAll(); - } - } - - void pauseDispatchingLocked(WindowToken token) { - synchronized (this) - { - if (DEBUG_INPUT) Slog.v(TAG, "Pausing WindowToken " + token); - token.paused = true; - - /* - if (mLastWin != null && !mFinished && mLastWin.mBaseLayer <= layer) { - mPaused = true; - } else { - if (mLastWin == null) { - Slog.i(TAG, "Key dispatching not paused: no last window."); - } else if (mFinished) { - Slog.i(TAG, "Key dispatching not paused: finished last key."); - } else { - Slog.i(TAG, "Key dispatching not paused: window in higher layer."); - } - } - */ - } - } - - void resumeDispatchingLocked(WindowToken token) { - synchronized (this) { - if (token.paused) { - if (DEBUG_INPUT) Slog.v( - TAG, "Resuming WindowToken " + token - + ", last=" + mLastBinder - + " (token=" + (mLastWin != null ? mLastWin.mToken : null) - + "), finished=" + mFinished + ", paused=" - + token.paused); - token.paused = false; - if (mLastWin != null && mLastWin.mToken == token && mFinished) { - doFinishedKeyLocked(false); - } else { - notifyAll(); - } - } - } - } - - void setEventDispatchingLocked(boolean enabled) { - synchronized (this) { - mEventDispatching = enabled; - notifyAll(); - } - } - - void appSwitchComing() { - synchronized (this) { - // Don't wait for more than .5 seconds for app to finish - // processing the pending events. - long now = SystemClock.uptimeMillis() + 500; - if (DEBUG_INPUT) Slog.v(TAG, "appSwitchComing: " + now); - if (mTimeToSwitch == 0 || now < mTimeToSwitch) { - mTimeToSwitch = now; - } - notifyAll(); - } - } - - private final void doFinishedKeyLocked(boolean force) { - if (mLastWin != null) { - releasePendingPointerLocked(mLastWin.mSession); - releasePendingTrackballLocked(mLastWin.mSession); - } - - if (force || mLastWin == null || !mLastWin.mToken.paused - || !mLastWin.isVisibleLw()) { - // If the current window has been paused, we aren't -really- - // finished... so let the waiters still wait. - mLastWin = null; - mLastBinder = null; - } - mFinished = true; - notifyAll(); - } - } - - private class KeyQ extends KeyInputQueue - implements KeyInputQueue.FilterCallback { - KeyQ() { - super(mContext, WindowManagerService.this); - } - - @Override - boolean preprocessEvent(InputDevice device, RawInputEvent event) { - if (mPolicy.preprocessInputEventTq(event)) { - return true; - } - - switch (event.type) { - case RawInputEvent.EV_KEY: { - // XXX begin hack - if (DEBUG) { - if (event.keycode == KeyEvent.KEYCODE_G) { - if (event.value != 0) { - // G down - mPolicy.screenTurnedOff(WindowManagerPolicy.OFF_BECAUSE_OF_USER); - } - return false; - } - if (event.keycode == KeyEvent.KEYCODE_D) { - if (event.value != 0) { - //dump(); - } - return false; - } - } - // XXX end hack - - boolean screenIsOff = !mPowerManager.isScreenOn(); - boolean screenIsDim = !mPowerManager.isScreenBright(); - int actions = mPolicy.interceptKeyTq(event, !screenIsOff); - - if ((actions & WindowManagerPolicy.ACTION_GO_TO_SLEEP) != 0) { - mPowerManager.goToSleep(event.when); - } - - if (screenIsOff) { - event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE; - } - if (screenIsDim) { - event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE; - } - if ((actions & WindowManagerPolicy.ACTION_POKE_USER_ACTIVITY) != 0) { - mPowerManager.userActivity(event.when, false, - LocalPowerManager.BUTTON_EVENT, false); - } - - if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) { - if (event.value != 0 && mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)) { - filterQueue(this); - mKeyWaiter.appSwitchComing(); - } - return true; - } else { - return false; - } - } - - case RawInputEvent.EV_REL: { - boolean screenIsOff = !mPowerManager.isScreenOn(); - boolean screenIsDim = !mPowerManager.isScreenBright(); - if (screenIsOff) { - if (!mPolicy.isWakeRelMovementTq(event.deviceId, - device.classes, event)) { - //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey"); - return false; - } - event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE; - } - if (screenIsDim) { - event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE; - } - return true; - } - - case RawInputEvent.EV_ABS: { - boolean screenIsOff = !mPowerManager.isScreenOn(); - boolean screenIsDim = !mPowerManager.isScreenBright(); - if (screenIsOff) { - if (!mPolicy.isWakeAbsMovementTq(event.deviceId, - device.classes, event)) { - //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey"); - return false; - } - event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE; - } - if (screenIsDim) { - event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE; - } - return true; - } - - default: - return true; - } - } - - public int filterEvent(QueuedEvent ev) { - switch (ev.classType) { - case RawInputEvent.CLASS_KEYBOARD: - KeyEvent ke = (KeyEvent)ev.event; - if (mPolicy.isMovementKeyTi(ke.getKeyCode())) { - Slog.w(TAG, "Dropping movement key during app switch: " - + ke.getKeyCode() + ", action=" + ke.getAction()); - return FILTER_REMOVE; - } - return FILTER_ABORT; - default: - return FILTER_KEEP; - } - } - } - public boolean detectSafeMode() { mSafeMode = mPolicy.detectSafeMode(); return mSafeMode; @@ -7003,219 +5444,6 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.systemReady(); } - private final class InputDispatcherThread extends Thread { - // Time to wait when there is nothing to do: 9999 seconds. - static final int LONG_WAIT=9999*1000; - - public InputDispatcherThread() { - super("InputDispatcher"); - } - - @Override - public void run() { - while (true) { - try { - process(); - } catch (Exception e) { - Slog.e(TAG, "Exception in input dispatcher", e); - } - } - } - - private void process() { - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY); - - // The last key event we saw - KeyEvent lastKey = null; - - // Last keydown time for auto-repeating keys - long lastKeyTime = SystemClock.uptimeMillis(); - long nextKeyTime = lastKeyTime+LONG_WAIT; - long downTime = 0; - - // How many successive repeats we generated - int keyRepeatCount = 0; - - // Need to report that configuration has changed? - boolean configChanged = false; - - while (true) { - long curTime = SystemClock.uptimeMillis(); - - if (DEBUG_INPUT) Slog.v( - TAG, "Waiting for next key: now=" + curTime - + ", repeat @ " + nextKeyTime); - - // Retrieve next event, waiting only as long as the next - // repeat timeout. If the configuration has changed, then - // don't wait at all -- we'll report the change as soon as - // we have processed all events. - QueuedEvent ev = mQueue.getEvent( - (int)((!configChanged && curTime < nextKeyTime) - ? (nextKeyTime-curTime) : 0)); - - if (DEBUG_INPUT && ev != null) Slog.v( - TAG, "Event: type=" + ev.classType + " data=" + ev.event); - - if (MEASURE_LATENCY) { - lt.sample("2 got event ", System.nanoTime() - ev.whenNano); - } - - if (lastKey != null && !mPolicy.allowKeyRepeat()) { - // cancel key repeat at the request of the policy. - lastKey = null; - downTime = 0; - lastKeyTime = curTime; - nextKeyTime = curTime + LONG_WAIT; - } - try { - if (ev != null) { - curTime = SystemClock.uptimeMillis(); - int eventType; - if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) { - eventType = eventType((MotionEvent)ev.event); - } else if (ev.classType == RawInputEvent.CLASS_KEYBOARD || - ev.classType == RawInputEvent.CLASS_TRACKBALL) { - eventType = LocalPowerManager.BUTTON_EVENT; - } else { - eventType = LocalPowerManager.OTHER_EVENT; - } - try { - if ((curTime - mLastBatteryStatsCallTime) - >= MIN_TIME_BETWEEN_USERACTIVITIES) { - mLastBatteryStatsCallTime = curTime; - mBatteryStats.noteInputEvent(); - } - } catch (RemoteException e) { - // Ignore - } - - if (ev.classType == RawInputEvent.CLASS_CONFIGURATION_CHANGED) { - // do not wake screen in this case - } else if (eventType != TOUCH_EVENT - && eventType != LONG_TOUCH_EVENT - && eventType != CHEEK_EVENT) { - mPowerManager.userActivity(curTime, false, - eventType, false); - } else if (mLastTouchEventType != eventType - || (curTime - mLastUserActivityCallTime) - >= MIN_TIME_BETWEEN_USERACTIVITIES) { - mLastUserActivityCallTime = curTime; - mLastTouchEventType = eventType; - mPowerManager.userActivity(curTime, false, - eventType, false); - } - - switch (ev.classType) { - case RawInputEvent.CLASS_KEYBOARD: - KeyEvent ke = (KeyEvent)ev.event; - if (ke.isDown()) { - lastKeyTime = curTime; - if (lastKey != null && - ke.getKeyCode() == lastKey.getKeyCode()) { - keyRepeatCount++; - // Arbitrary long timeout to block - // repeating here since we know that - // the device driver takes care of it. - nextKeyTime = lastKeyTime + LONG_WAIT; - if (DEBUG_INPUT) Slog.v( - TAG, "Received repeated key down"); - } else { - downTime = curTime; - keyRepeatCount = 0; - nextKeyTime = lastKeyTime - + ViewConfiguration.getLongPressTimeout(); - if (DEBUG_INPUT) Slog.v( - TAG, "Received key down: first repeat @ " - + nextKeyTime); - } - lastKey = ke; - } else { - lastKey = null; - downTime = 0; - keyRepeatCount = 0; - // Arbitrary long timeout. - lastKeyTime = curTime; - nextKeyTime = curTime + LONG_WAIT; - if (DEBUG_INPUT) Slog.v( - TAG, "Received key up: ignore repeat @ " - + nextKeyTime); - } - if (keyRepeatCount > 0) { - dispatchKey(KeyEvent.changeTimeRepeat(ke, - ke.getEventTime(), keyRepeatCount), 0, 0); - } else { - dispatchKey(ke, 0, 0); - } - mQueue.recycleEvent(ev); - break; - case RawInputEvent.CLASS_TOUCHSCREEN: - //Slog.i(TAG, "Read next event " + ev); - dispatchPointer(ev, (MotionEvent)ev.event, 0, 0); - break; - case RawInputEvent.CLASS_TRACKBALL: - dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0); - break; - case RawInputEvent.CLASS_CONFIGURATION_CHANGED: - configChanged = true; - break; - default: - mQueue.recycleEvent(ev); - break; - } - - } else if (configChanged) { - configChanged = false; - sendNewConfiguration(); - - } else if (lastKey != null) { - curTime = SystemClock.uptimeMillis(); - - // Timeout occurred while key was down. If it is at or - // past the key repeat time, dispatch the repeat. - if (DEBUG_INPUT) Slog.v( - TAG, "Key timeout: repeat=" + nextKeyTime - + ", now=" + curTime); - if (curTime < nextKeyTime) { - continue; - } - - lastKeyTime = nextKeyTime; - nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY; - keyRepeatCount++; - if (DEBUG_INPUT) Slog.v( - TAG, "Key repeat: count=" + keyRepeatCount - + ", next @ " + nextKeyTime); - KeyEvent newEvent; - if (downTime != 0 && (downTime - + ViewConfiguration.getLongPressTimeout()) - <= curTime) { - newEvent = KeyEvent.changeTimeRepeat(lastKey, - curTime, keyRepeatCount, - lastKey.getFlags() | KeyEvent.FLAG_LONG_PRESS); - downTime = 0; - } else { - newEvent = KeyEvent.changeTimeRepeat(lastKey, - curTime, keyRepeatCount); - } - dispatchKey(newEvent, 0, 0); - - } else { - curTime = SystemClock.uptimeMillis(); - - lastKeyTime = curTime; - nextKeyTime = curTime + LONG_WAIT; - } - - } catch (Exception e) { - Slog.e(TAG, - "Input thread received uncaught exception: " + e, e); - } - } - } - } - // ------------------------------------------------------------- // Client Session State // ------------------------------------------------------------- @@ -7231,20 +5459,6 @@ public class WindowManagerService extends IWindowManager.Stub int mNumWindow = 0; boolean mClientDead = false; - /** - * Current pointer move event being dispatched to client window... must - * hold key lock to access. - */ - QueuedEvent mPendingPointerMove; - WindowState mPendingPointerWindow; - - /** - * Current trackball move event being dispatched to client window... must - * hold key lock to access. - */ - QueuedEvent mPendingTrackballMove; - WindowState mPendingTrackballWindow; - public Session(IInputMethodClient client, IInputContext inputContext) { mClient = client; mInputContext = inputContext; @@ -7363,36 +5577,6 @@ public class WindowManagerService extends IWindowManager.Stub finishDrawingWindow(this, window); } - public void finishKey(IWindow window) { - if (localLOGV) Slog.v( - TAG, "IWindow finishKey called for " + window); - if (ENABLE_NATIVE_INPUT_DISPATCH) { - throw new IllegalStateException("Should not be called anymore."); - } - mKeyWaiter.finishedKey(this, window, false, - KeyWaiter.RETURN_NOTHING); - } - - public MotionEvent getPendingPointerMove(IWindow window) { - if (localLOGV) Slog.v( - TAG, "IWindow getPendingMotionEvent called for " + window); - if (ENABLE_NATIVE_INPUT_DISPATCH) { - throw new IllegalStateException("Should not be called anymore."); - } - return mKeyWaiter.finishedKey(this, window, false, - KeyWaiter.RETURN_PENDING_POINTER); - } - - public MotionEvent getPendingTrackballMove(IWindow window) { - if (localLOGV) Slog.v( - TAG, "IWindow getPendingMotionEvent called for " + window); - if (ENABLE_NATIVE_INPUT_DISPATCH) { - throw new IllegalStateException("Should not be called anymore."); - } - return mKeyWaiter.finishedKey(this, window, false, - KeyWaiter.RETURN_PENDING_TRACKBALL); - } - public void setInTouchMode(boolean mode) { synchronized(mWindowMap) { mInTouchMode = mode; @@ -7496,16 +5680,6 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow); pw.print(" mClientDead="); pw.print(mClientDead); pw.print(" mSurfaceSession="); pw.println(mSurfaceSession); - if (mPendingPointerWindow != null || mPendingPointerMove != null) { - pw.print(prefix); - pw.print("mPendingPointerWindow="); pw.print(mPendingPointerWindow); - pw.print(" mPendingPointerMove="); pw.println(mPendingPointerMove); - } - if (mPendingTrackballWindow != null || mPendingTrackballMove != null) { - pw.print(prefix); - pw.print("mPendingTrackballWindow="); pw.print(mPendingTrackballWindow); - pw.print(" mPendingTrackballMove="); pw.println(mPendingTrackballMove); - } } @Override @@ -7556,8 +5730,6 @@ public class WindowManagerService extends IWindowManager.Stub boolean mObscured; boolean mTurnOnScreen; - WindowState mNextOutsideTouch; - int mLayoutSeq = -1; Configuration mConfiguration = null; @@ -8074,16 +6246,6 @@ public class WindowManagerService extends IWindowManager.Stub } void destroySurfaceLocked() { - // Window is no longer on-screen, so can no longer receive - // key events... if we were waiting for it to finish - // handling a key event, the wait is over! - if (! ENABLE_NATIVE_INPUT_DISPATCH) { - mKeyWaiter.finishedKey(mSession, mClient, true, - KeyWaiter.RETURN_NOTHING); - mKeyWaiter.releasePendingPointerLocked(mSession); - mKeyWaiter.releasePendingTrackballLocked(mSession); - } - if (mAppToken != null && this == mAppToken.startingWindow) { mAppToken.startingDisplayed = false; } @@ -8099,9 +6261,7 @@ public class WindowManagerService extends IWindowManager.Stub WindowState c = (WindowState)mChildWindows.get(i); c.mAttachedHidden = true; - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.windowIsBecomingInvisibleLw(c); - } + mInputMonitor.windowIsBecomingInvisibleLw(c); } if (mReportDestroySurface) { @@ -8410,12 +6570,8 @@ public class WindowManagerService extends IWindowManager.Stub } mLastHidden = true; - if (ENABLE_NATIVE_INPUT_DISPATCH) { - for (int i=0; i<N; i++) { - mInputMonitor.windowIsBecomingInvisibleLw((WindowState)mChildWindows.get(i)); - } - } else { - mKeyWaiter.releasePendingPointerLocked(mSession); + for (int i=0; i<N; i++) { + mInputMonitor.windowIsBecomingInvisibleLw((WindowState)mChildWindows.get(i)); } } mExiting = false; @@ -8752,13 +6908,11 @@ public class WindowManagerService extends IWindowManager.Stub // we are doing this as part of processing a death note.) } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - if (mInputChannel != null) { - mInputManager.unregisterInputChannel(mInputChannel); - - mInputChannel.dispose(); - mInputChannel = null; - } + if (mInputChannel != null) { + mInputManager.unregisterInputChannel(mInputChannel); + + mInputChannel.dispose(); + mInputChannel = null; } } @@ -10174,9 +8328,7 @@ public class WindowManagerService extends IWindowManager.Stub } // Window frames may have changed. Tell the input dispatcher about it. - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.updateInputWindowsLw(); - } + mInputMonitor.updateInputWindowsLw(); return mPolicy.finishLayoutLw(); } @@ -10977,11 +9129,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.w(TAG, "Exception hiding surface in " + w); } } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.windowIsBecomingInvisibleLw(w); - } else { - mKeyWaiter.releasePendingPointerLocked(w.mSession); - } + mInputMonitor.windowIsBecomingInvisibleLw(w); } // If we are waiting for this window to handle an // orientation change, well, it is hidden, so @@ -11583,13 +9731,7 @@ public class WindowManagerService extends IWindowManager.Stub } private void finishUpdateFocusedWindowAfterAssignLayersLocked() { - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.setInputFocusLw(mCurrentFocus); - } else { - if (mCurrentFocus != null) { - mKeyWaiter.handleNewWindowLocked(mCurrentFocus); - } - } + mInputMonitor.setInputFocusLw(mCurrentFocus); } private WindowState computeFocusedWindowLocked() { @@ -11663,17 +9805,6 @@ public class WindowManagerService extends IWindowManager.Stub private void startFreezingDisplayLocked() { if (mDisplayFrozen) { - // Freezing the display also suspends key event delivery, to - // keep events from going astray while the display is reconfigured. - // If someone has changed orientation again while the screen is - // still frozen, the events will continue to be blocked while the - // successive orientation change is processed. To prevent spurious - // ANRs, we reset the event dispatch timeout in this case. - if (! ENABLE_NATIVE_INPUT_DISPATCH) { - synchronized (mKeyWaiter) { - mKeyWaiter.mWasFrozen = true; - } - } return; } @@ -11696,9 +9827,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayFrozen = true; - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.freezeInputDispatchingLw(); - } + mInputMonitor.freezeInputDispatchingLw(); if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET; @@ -11731,16 +9860,7 @@ public class WindowManagerService extends IWindowManager.Stub } Surface.unfreezeDisplay(0); - // Reset the key delivery timeout on unfreeze, too. We force a wakeup here - // too because regular key delivery processing should resume immediately. - if (ENABLE_NATIVE_INPUT_DISPATCH) { - mInputMonitor.thawInputDispatchingLw(); - } else { - synchronized (mKeyWaiter) { - mKeyWaiter.mWasFrozen = true; - mKeyWaiter.notifyAll(); - } - } + mInputMonitor.thawInputDispatchingLw(); // While the display is frozen we don't re-compute the orientation // to avoid inconsistent states. However, something interesting @@ -11772,13 +9892,8 @@ public class WindowManagerService extends IWindowManager.Stub return; } - if (ENABLE_NATIVE_INPUT_DISPATCH) { - pw.println("Input Dispatcher State:"); - mInputManager.dump(pw); - } else { - pw.println("Input State:"); - mQueue.dump(pw, " "); - } + pw.println("Input Dispatcher State:"); + mInputManager.dump(pw); pw.println(" "); synchronized(mWindowMap) { @@ -11995,16 +10110,6 @@ public class WindowManagerService extends IWindowManager.Stub } pw.print(" DisplayWidth="); pw.print(mDisplay.getWidth()); pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight()); - - if (! ENABLE_NATIVE_INPUT_DISPATCH) { - pw.println(" KeyWaiter state:"); - pw.print(" mLastWin="); pw.print(mKeyWaiter.mLastWin); - pw.print(" mLastBinder="); pw.println(mKeyWaiter.mLastBinder); - pw.print(" mFinished="); pw.print(mKeyWaiter.mFinished); - pw.print(" mGotFirstWindow="); pw.print(mKeyWaiter.mGotFirstWindow); - pw.print(" mEventDispatching="); pw.print(mKeyWaiter.mEventDispatching); - pw.print(" mTimeToSwitch="); pw.println(mKeyWaiter.mTimeToSwitch); - } } } @@ -12012,12 +10117,6 @@ public class WindowManagerService extends IWindowManager.Stub public void monitor() { synchronized (mWindowMap) { } synchronized (mKeyguardTokenWatcher) { } - synchronized (mKeyWaiter) { } - synchronized (mInputMonitor) { } - } - - public void virtualKeyFeedback(KeyEvent event) { - mPolicy.keyFeedbackFromInput(event); } /** diff --git a/services/jni/Android.mk b/services/jni/Android.mk index 499ca86..0cf36b3 100644 --- a/services/jni/Android.mk +++ b/services/jni/Android.mk @@ -4,9 +4,9 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ com_android_server_AlarmManagerService.cpp \ com_android_server_BatteryService.cpp \ - com_android_server_KeyInputQueue.cpp \ com_android_server_InputManager.cpp \ com_android_server_LightsService.cpp \ + com_android_server_PowerManagerService.cpp \ com_android_server_SensorService.cpp \ com_android_server_SystemServer.cpp \ com_android_server_VibratorService.cpp \ diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index d0f856b..fc901f4 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -40,6 +40,7 @@ #include "../../core/jni/android_view_KeyEvent.h" #include "../../core/jni/android_view_MotionEvent.h" #include "../../core/jni/android_view_InputChannel.h" +#include "com_android_server_PowerManagerService.h" namespace android { @@ -107,16 +108,6 @@ enum { LAST_SYSTEM_WINDOW = 2999, }; -enum { - POWER_MANAGER_OTHER_EVENT = 0, - POWER_MANAGER_CHEEK_EVENT = 1, - POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either - // up events or LONG_TOUCH events. - POWER_MANAGER_LONG_TOUCH_EVENT = 3, - POWER_MANAGER_TOUCH_UP_EVENT = 4, - POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events. -}; - // Delay between reporting long touch events to the power manager. const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms @@ -133,20 +124,16 @@ const nsecs_t MIN_INPUT_DISPATCHING_TIMEOUT = 1000 * 1000000LL; // 1 sec static struct { jclass clazz; - jmethodID isScreenOn; - jmethodID isScreenBright; jmethodID notifyConfigurationChanged; jmethodID notifyLidSwitchChanged; jmethodID notifyInputChannelBroken; jmethodID notifyInputChannelANR; jmethodID notifyInputChannelRecoveredFromANR; jmethodID notifyANR; - jmethodID virtualKeyFeedback; + jmethodID virtualKeyDownFeedback; jmethodID interceptKeyBeforeQueueing; jmethodID interceptKeyBeforeDispatching; jmethodID checkInjectEventsPermission; - jmethodID goToSleep; - jmethodID pokeUserActivity; jmethodID notifyAppSwitchComing; jmethodID filterTouchEvents; jmethodID filterJumpyTouchEvents; @@ -228,9 +215,7 @@ public: virtual bool getDisplayInfo(int32_t displayId, int32_t* width, int32_t* height, int32_t* orientation); - virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId, - int32_t action, int32_t flags, int32_t keyCode, - int32_t scanCode, int32_t metaState, nsecs_t downTime); + virtual void virtualKeyDownFeedback(); virtual int32_t interceptKey(nsecs_t when, int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags); virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown, @@ -321,7 +306,7 @@ private: int32_t mDisplayWidth, mDisplayHeight; int32_t mDisplayOrientation; - // Callbacks. + // Power manager interactions. bool isScreenOn(); bool isScreenBright(); @@ -369,9 +354,9 @@ private: void releaseTouchedWindowLd(); - int32_t identifyTrackballEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + int32_t waitForTrackballEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); - int32_t identifyTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + int32_t waitForTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); bool interceptKeyBeforeDispatching(const InputTarget& target, @@ -391,6 +376,7 @@ private: } static bool isAppSwitchKey(int32_t keyCode); + static bool isPolicyKey(int32_t keyCode, bool isScreenOn); static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); }; @@ -422,6 +408,36 @@ bool NativeInputManager::isAppSwitchKey(int32_t keyCode) { return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL; } +bool NativeInputManager::isPolicyKey(int32_t keyCode, bool isScreenOn) { + // Special keys that the WindowManagerPolicy might care about. + switch (keyCode) { + case KEYCODE_VOLUME_UP: + case KEYCODE_VOLUME_DOWN: + case KEYCODE_ENDCALL: + case KEYCODE_POWER: + case KEYCODE_CALL: + case KEYCODE_HOME: + case KEYCODE_MENU: + case KEYCODE_SEARCH: + // media keys + case KEYCODE_HEADSETHOOK: + case KEYCODE_MEDIA_PLAY_PAUSE: + case KEYCODE_MEDIA_STOP: + case KEYCODE_MEDIA_NEXT: + case KEYCODE_MEDIA_PREVIOUS: + case KEYCODE_MEDIA_REWIND: + case KEYCODE_MEDIA_FAST_FORWARD: + return true; + default: + // We need to pass all keys to the policy in the following cases: + // - screen is off + // - keyguard is visible + // - policy is performing key chording + //return ! isScreenOn || keyguardVisible || chording; + return true; // XXX stubbed out for now + } +} + bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { if (env->ExceptionCheck()) { LOGE("An exception was thrown by callback '%s'.", methodName); @@ -546,39 +562,22 @@ bool NativeInputManager::getDisplayInfo(int32_t displayId, } bool NativeInputManager::isScreenOn() { - JNIEnv* env = jniEnv(); - - jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenOn); - if (checkAndClearExceptionFromCallback(env, "isScreenOn")) { - return true; - } - return result; + return android_server_PowerManagerService_isScreenOn(); } bool NativeInputManager::isScreenBright() { - JNIEnv* env = jniEnv(); - - jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenBright); - if (checkAndClearExceptionFromCallback(env, "isScreenBright")) { - return true; - } - return result; + return android_server_PowerManagerService_isScreenBright(); } -void NativeInputManager::virtualKeyFeedback(nsecs_t when, int32_t deviceId, - int32_t action, int32_t flags, int32_t keyCode, - int32_t scanCode, int32_t metaState, nsecs_t downTime) { +void NativeInputManager::virtualKeyDownFeedback() { #if DEBUG_INPUT_READER_POLICY - LOGD("virtualKeyFeedback - when=%lld, deviceId=%d, action=%d, flags=%d, keyCode=%d, " - "scanCode=%d, metaState=%d, downTime=%lld", - when, deviceId, action, flags, keyCode, scanCode, metaState, downTime); + LOGD("virtualKeyDownFeedback"); #endif JNIEnv* env = jniEnv(); - env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyFeedback, - when, deviceId, action, flags, keyCode, scanCode, metaState, downTime); - checkAndClearExceptionFromCallback(env, "virtualKeyFeedback"); + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyDownFeedback); + checkAndClearExceptionFromCallback(env, "virtualKeyDownFeedback"); } int32_t NativeInputManager::interceptKey(nsecs_t when, @@ -593,16 +592,21 @@ int32_t NativeInputManager::interceptKey(nsecs_t when, const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2; const int32_t WM_ACTION_GO_TO_SLEEP = 4; - JNIEnv* env = jniEnv(); - bool isScreenOn = this->isScreenOn(); bool isScreenBright = this->isScreenBright(); - jint wmActions = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.interceptKeyBeforeQueueing, - deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn); - if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) { - wmActions = 0; + jint wmActions = 0; + if (isPolicyKey(keyCode, isScreenOn)) { + JNIEnv* env = jniEnv(); + + wmActions = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.interceptKeyBeforeQueueing, + when, keyCode, down, policyFlags, isScreenOn); + if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) { + wmActions = 0; + } + } else { + wmActions = WM_ACTION_PASS_TO_USER; } int32_t actions = InputReaderPolicyInterface::ACTION_NONE; @@ -617,8 +621,7 @@ int32_t NativeInputManager::interceptKey(nsecs_t when, } if (wmActions & WM_ACTION_GO_TO_SLEEP) { - env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.goToSleep, when); - checkAndClearExceptionFromCallback(env, "goToSleep"); + android_server_PowerManagerService_goToSleep(when); } if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) { @@ -629,6 +632,8 @@ int32_t NativeInputManager::interceptKey(nsecs_t when, actions |= InputReaderPolicyInterface::ACTION_DISPATCH; if (down && isAppSwitchKey(keyCode)) { + JNIEnv* env = jniEnv(); + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing); checkAndClearExceptionFromCallback(env, "notifyAppSwitchComing"); @@ -1531,11 +1536,13 @@ int32_t NativeInputManager::waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t windowType = focusedWindow->layoutParamsType; } // release lock - const InputTarget& target = outTargets.top(); - bool consumed = interceptKeyBeforeDispatching(target, keyEvent, policyFlags); - if (consumed) { - outTargets.clear(); - return INPUT_EVENT_INJECTION_SUCCEEDED; + if (isPolicyKey(keyEvent->getKeyCode(), isScreenOn())) { + const InputTarget& target = outTargets.top(); + bool consumed = interceptKeyBeforeDispatching(target, keyEvent, policyFlags); + if (consumed) { + outTargets.clear(); + return INPUT_EVENT_INJECTION_SUCCEEDED; + } } pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT); @@ -1552,11 +1559,11 @@ int32_t NativeInputManager::waitForMotionEventTargets(MotionEvent* motionEvent, switch (motionEvent->getNature()) { case INPUT_EVENT_NATURE_TRACKBALL: - return identifyTrackballEventTargets(motionEvent, policyFlags, injectorPid, injectorUid, + return waitForTrackballEventTargets(motionEvent, policyFlags, injectorPid, injectorUid, outTargets); case INPUT_EVENT_NATURE_TOUCH: - return identifyTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid, + return waitForTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid, outTargets); default: @@ -1565,11 +1572,11 @@ int32_t NativeInputManager::waitForMotionEventTargets(MotionEvent* motionEvent, } } -int32_t NativeInputManager::identifyTrackballEventTargets(MotionEvent* motionEvent, +int32_t NativeInputManager::waitForTrackballEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) { #if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("identifyTrackballEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", + LOGD("waitForTrackballEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", policyFlags, injectorPid, injectorUid); #endif @@ -1591,11 +1598,11 @@ int32_t NativeInputManager::identifyTrackballEventTargets(MotionEvent* motionEve return INPUT_EVENT_INJECTION_SUCCEEDED; } -int32_t NativeInputManager::identifyTouchEventTargets(MotionEvent* motionEvent, +int32_t NativeInputManager::waitForTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) { #if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("identifyTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", + LOGD("waitForTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", policyFlags, injectorPid, injectorUid); #endif @@ -1642,8 +1649,8 @@ bool NativeInputManager::interceptKeyBeforeDispatching(const InputTarget& target if (inputChannelObj) { jboolean consumed = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.interceptKeyBeforeDispatching, - inputChannelObj, keyEvent->getKeyCode(), keyEvent->getMetaState(), - keyEvent->getAction() == KEY_EVENT_ACTION_DOWN, + inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(), + keyEvent->getKeyCode(), keyEvent->getMetaState(), keyEvent->getRepeatCount(), policyFlags); bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching"); @@ -1665,10 +1672,7 @@ void NativeInputManager::pokeUserActivityIfNeeded(int32_t windowType, int32_t ev } void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) { - JNIEnv* env = jniEnv(); - env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivity, - eventTime, eventType); - checkAndClearExceptionFromCallback(env, "pokeUserActivity"); + android_server_PowerManagerService_userActivity(eventTime, eventType); } void NativeInputManager::dumpDispatchStateLd() { @@ -2082,12 +2086,6 @@ int register_android_server_InputManager(JNIEnv* env) { FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks"); - GET_METHOD_ID(gCallbacksClassInfo.isScreenOn, gCallbacksClassInfo.clazz, - "isScreenOn", "()Z"); - - GET_METHOD_ID(gCallbacksClassInfo.isScreenBright, gCallbacksClassInfo.clazz, - "isScreenBright", "()Z"); - GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz, "notifyConfigurationChanged", "(JIII)V"); @@ -2106,24 +2104,18 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz, "notifyANR", "(Ljava/lang/Object;)J"); - GET_METHOD_ID(gCallbacksClassInfo.virtualKeyFeedback, gCallbacksClassInfo.clazz, - "virtualKeyFeedback", "(JIIIIIIJ)V"); + GET_METHOD_ID(gCallbacksClassInfo.virtualKeyDownFeedback, gCallbacksClassInfo.clazz, + "virtualKeyDownFeedback", "()V"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz, - "interceptKeyBeforeQueueing", "(IIIIIIJZ)I"); + "interceptKeyBeforeQueueing", "(JIZIZ)I"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz, - "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIZII)Z"); + "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIII)Z"); GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz, "checkInjectEventsPermission", "(II)Z"); - GET_METHOD_ID(gCallbacksClassInfo.goToSleep, gCallbacksClassInfo.clazz, - "goToSleep", "(J)V"); - - GET_METHOD_ID(gCallbacksClassInfo.pokeUserActivity, gCallbacksClassInfo.clazz, - "pokeUserActivity", "(JI)V"); - GET_METHOD_ID(gCallbacksClassInfo.notifyAppSwitchComing, gCallbacksClassInfo.clazz, "notifyAppSwitchComing", "()V"); diff --git a/services/jni/com_android_server_KeyInputQueue.cpp b/services/jni/com_android_server_KeyInputQueue.cpp deleted file mode 100644 index f9e3585..0000000 --- a/services/jni/com_android_server_KeyInputQueue.cpp +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (C) 2007 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. - */ - -#define LOG_TAG "Input" - -#include "jni.h" -#include "JNIHelp.h" -#include <utils/misc.h> -#include <utils/Log.h> - -#include <ui/EventHub.h> -#include <utils/threads.h> - -#include <stdio.h> - -namespace android { - -// ---------------------------------------------------------------------------- - -static struct input_offsets_t -{ - jfieldID mMinValue; - jfieldID mMaxValue; - jfieldID mFlat; - jfieldID mFuzz; - - jfieldID mDeviceId; - jfieldID mType; - jfieldID mScancode; - jfieldID mKeycode; - jfieldID mFlags; - jfieldID mValue; - jfieldID mWhen; -} gInputOffsets; - -// ---------------------------------------------------------------------------- - -static Mutex gLock; -static sp<EventHub> gHub; - -static jboolean -android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, - jobject event) -{ - gLock.lock(); - sp<EventHub> hub = gHub; - if (hub == NULL) { - hub = new EventHub; - gHub = hub; - } - gLock.unlock(); - - int32_t deviceId; - int32_t type; - int32_t scancode, keycode; - uint32_t flags; - int32_t value; - nsecs_t when; - bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode, - &flags, &value, &when); - - env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId); - env->SetIntField(event, gInputOffsets.mType, (jint)type); - env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode); - env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode); - env->SetIntField(event, gInputOffsets.mFlags, (jint)flags); - env->SetIntField(event, gInputOffsets.mValue, value); - env->SetLongField(event, gInputOffsets.mWhen, - (jlong)(nanoseconds_to_milliseconds(when))); - - return res; -} - -static jint -android_server_KeyInputQueue_getDeviceClasses(JNIEnv* env, jobject clazz, - jint deviceId) -{ - jint classes = 0; - gLock.lock(); - if (gHub != NULL) classes = gHub->getDeviceClasses(deviceId); - gLock.unlock(); - return classes; -} - -static jstring -android_server_KeyInputQueue_getDeviceName(JNIEnv* env, jobject clazz, - jint deviceId) -{ - String8 name; - gLock.lock(); - if (gHub != NULL) name = gHub->getDeviceName(deviceId); - gLock.unlock(); - - if (name.size() > 0) { - return env->NewStringUTF(name.string()); - } - return NULL; -} - -static void -android_server_KeyInputQueue_addExcludedDevice(JNIEnv* env, jobject clazz, - jstring deviceName) -{ - gLock.lock(); - sp<EventHub> hub = gHub; - if (hub == NULL) { - hub = new EventHub; - gHub = hub; - } - gLock.unlock(); - - const char* nameStr = env->GetStringUTFChars(deviceName, NULL); - gHub->addExcludedDevice(nameStr); - env->ReleaseStringUTFChars(deviceName, nameStr); -} - -static jboolean -android_server_KeyInputQueue_getAbsoluteInfo(JNIEnv* env, jobject clazz, - jint deviceId, jint axis, - jobject info) -{ - int32_t minValue, maxValue, flat, fuzz; - int res = -1; - gLock.lock(); - if (gHub != NULL) { - res = gHub->getAbsoluteInfo(deviceId, axis, - &minValue, &maxValue, &flat, &fuzz); - } - gLock.unlock(); - - if (res < 0) return JNI_FALSE; - - env->SetIntField(info, gInputOffsets.mMinValue, (jint)minValue); - env->SetIntField(info, gInputOffsets.mMaxValue, (jint)maxValue); - env->SetIntField(info, gInputOffsets.mFlat, (jint)flat); - env->SetIntField(info, gInputOffsets.mFuzz, (jint)fuzz); - return JNI_TRUE; -} - -static jint -android_server_KeyInputQueue_getSwitchState(JNIEnv* env, jobject clazz, - jint sw) -{ - jint st = -1; - gLock.lock(); - if (gHub != NULL) st = gHub->getSwitchState(-1, -1, sw); - gLock.unlock(); - - return st; -} - -static jint -android_server_KeyInputQueue_getSwitchStateDevice(JNIEnv* env, jobject clazz, - jint deviceId, jint sw) -{ - jint st = -1; - gLock.lock(); - if (gHub != NULL) st = gHub->getSwitchState(deviceId, -1, sw); - gLock.unlock(); - - return st; -} - -static jint -android_server_KeyInputQueue_getScancodeState(JNIEnv* env, jobject clazz, - jint sw) -{ - jint st = -1; - gLock.lock(); - if (gHub != NULL) st = gHub->getScanCodeState(0, -1, sw); - gLock.unlock(); - - return st; -} - -static jint -android_server_KeyInputQueue_getScancodeStateDevice(JNIEnv* env, jobject clazz, - jint deviceId, jint sw) -{ - jint st = -1; - gLock.lock(); - if (gHub != NULL) st = gHub->getScanCodeState(deviceId, -1, sw); - gLock.unlock(); - - return st; -} - -static jint -android_server_KeyInputQueue_getKeycodeState(JNIEnv* env, jobject clazz, - jint sw) -{ - jint st = -1; - gLock.lock(); - if (gHub != NULL) st = gHub->getKeyCodeState(0, -1, sw); - gLock.unlock(); - - return st; -} - -static jint -android_server_KeyInputQueue_getKeycodeStateDevice(JNIEnv* env, jobject clazz, - jint deviceId, jint sw) -{ - jint st = -1; - gLock.lock(); - if (gHub != NULL) st = gHub->getKeyCodeState(deviceId,-1, sw); - gLock.unlock(); - - return st; -} - -static jint -android_server_KeyInputQueue_scancodeToKeycode(JNIEnv* env, jobject clazz, - jint deviceId, jint scancode) -{ - jint res = 0; - gLock.lock(); - if (gHub != NULL) { - int32_t keycode; - uint32_t flags; - gHub->scancodeToKeycode(deviceId, scancode, &keycode, &flags); - res = keycode; - } - gLock.unlock(); - - return res; -} - -static jboolean -android_server_KeyInputQueue_hasKeys(JNIEnv* env, jobject clazz, - jintArray keyCodes, jbooleanArray outFlags) -{ - jboolean ret = JNI_FALSE; - - int32_t* codes = env->GetIntArrayElements(keyCodes, NULL); - uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL); - jsize numCodes = env->GetArrayLength(keyCodes); - if (numCodes == env->GetArrayLength(outFlags)) { - gLock.lock(); - if (gHub != NULL) ret = gHub->hasKeys(numCodes, codes, flags); - gLock.unlock(); - } - - env->ReleaseBooleanArrayElements(outFlags, flags, 0); - env->ReleaseIntArrayElements(keyCodes, codes, 0); - return ret; -} - -// ---------------------------------------------------------------------------- - -/* - * JNI registration. - */ -static JNINativeMethod gInputMethods[] = { - /* name, signature, funcPtr */ - { "readEvent", "(Landroid/view/RawInputEvent;)Z", - (void*) android_server_KeyInputQueue_readEvent }, - { "getDeviceClasses", "(I)I", - (void*) android_server_KeyInputQueue_getDeviceClasses }, - { "getDeviceName", "(I)Ljava/lang/String;", - (void*) android_server_KeyInputQueue_getDeviceName }, - { "addExcludedDevice", "(Ljava/lang/String;)V", - (void*) android_server_KeyInputQueue_addExcludedDevice }, - { "getAbsoluteInfo", "(IILcom/android/server/InputDevice$AbsoluteInfo;)Z", - (void*) android_server_KeyInputQueue_getAbsoluteInfo }, - { "getSwitchState", "(I)I", - (void*) android_server_KeyInputQueue_getSwitchState }, - { "getSwitchState", "(II)I", - (void*) android_server_KeyInputQueue_getSwitchStateDevice }, - { "nativeGetScancodeState", "(I)I", - (void*) android_server_KeyInputQueue_getScancodeState }, - { "nativeGetScancodeState", "(II)I", - (void*) android_server_KeyInputQueue_getScancodeStateDevice }, - { "nativeGetKeycodeState", "(I)I", - (void*) android_server_KeyInputQueue_getKeycodeState }, - { "nativeGetKeycodeState", "(II)I", - (void*) android_server_KeyInputQueue_getKeycodeStateDevice }, - { "hasKeys", "([I[Z)Z", - (void*) android_server_KeyInputQueue_hasKeys }, - { "scancodeToKeycode", "(II)I", - (void*) android_server_KeyInputQueue_scancodeToKeycode }, -}; - -int register_android_server_KeyInputQueue(JNIEnv* env) -{ - jclass input = env->FindClass("com/android/server/KeyInputQueue"); - LOG_FATAL_IF(input == NULL, "Unable to find class com/android/server/KeyInputQueue"); - int res = jniRegisterNativeMethods(env, "com/android/server/KeyInputQueue", - gInputMethods, NELEM(gInputMethods)); - - jclass absoluteInfo = env->FindClass("com/android/server/InputDevice$AbsoluteInfo"); - LOG_FATAL_IF(absoluteInfo == NULL, "Unable to find class com/android/server/InputDevice$AbsoluteInfo"); - - gInputOffsets.mMinValue - = env->GetFieldID(absoluteInfo, "minValue", "I"); - LOG_FATAL_IF(gInputOffsets.mMinValue == NULL, "Unable to find InputDevice.AbsoluteInfo.minValue"); - - gInputOffsets.mMaxValue - = env->GetFieldID(absoluteInfo, "maxValue", "I"); - LOG_FATAL_IF(gInputOffsets.mMaxValue == NULL, "Unable to find InputDevice.AbsoluteInfo.maxValue"); - - gInputOffsets.mFlat - = env->GetFieldID(absoluteInfo, "flat", "I"); - LOG_FATAL_IF(gInputOffsets.mFlat == NULL, "Unable to find InputDevice.AbsoluteInfo.flat"); - - gInputOffsets.mFuzz - = env->GetFieldID(absoluteInfo, "fuzz", "I"); - LOG_FATAL_IF(gInputOffsets.mFuzz == NULL, "Unable to find InputDevice.AbsoluteInfo.fuzz"); - - jclass inputEvent = env->FindClass("android/view/RawInputEvent"); - LOG_FATAL_IF(inputEvent == NULL, "Unable to find class android/view/RawInputEvent"); - - gInputOffsets.mDeviceId - = env->GetFieldID(inputEvent, "deviceId", "I"); - LOG_FATAL_IF(gInputOffsets.mDeviceId == NULL, "Unable to find RawInputEvent.deviceId"); - - gInputOffsets.mType - = env->GetFieldID(inputEvent, "type", "I"); - LOG_FATAL_IF(gInputOffsets.mType == NULL, "Unable to find RawInputEvent.type"); - - gInputOffsets.mScancode - = env->GetFieldID(inputEvent, "scancode", "I"); - LOG_FATAL_IF(gInputOffsets.mScancode == NULL, "Unable to find RawInputEvent.scancode"); - - gInputOffsets.mKeycode - = env->GetFieldID(inputEvent, "keycode", "I"); - LOG_FATAL_IF(gInputOffsets.mKeycode == NULL, "Unable to find RawInputEvent.keycode"); - - gInputOffsets.mFlags - = env->GetFieldID(inputEvent, "flags", "I"); - LOG_FATAL_IF(gInputOffsets.mFlags == NULL, "Unable to find RawInputEvent.flags"); - - gInputOffsets.mValue - = env->GetFieldID(inputEvent, "value", "I"); - LOG_FATAL_IF(gInputOffsets.mValue == NULL, "Unable to find RawInputEvent.value"); - - gInputOffsets.mWhen - = env->GetFieldID(inputEvent, "when", "J"); - LOG_FATAL_IF(gInputOffsets.mWhen == NULL, "Unable to find RawInputEvent.when"); - - return res; -} - -}; // namespace android - diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp new file mode 100644 index 0000000..b80dbc5 --- /dev/null +++ b/services/jni/com_android_server_PowerManagerService.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2010 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. + */ + +#define LOG_TAG "PowerManagerService-JNI" + +//#define LOG_NDEBUG 0 + +#include "JNIHelp.h" +#include "jni.h" +#include <limits.h> +#include <android_runtime/AndroidRuntime.h> +#include "com_android_server_PowerManagerService.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static struct { + jclass clazz; + + jmethodID goToSleep; + jmethodID userActivity; +} gPowerManagerServiceClassInfo; + +// ---------------------------------------------------------------------------- + +static jobject gPowerManagerServiceObj; + +static Mutex gPowerManagerLock; +static bool gScreenOn; +static bool gScreenBright; + +// ---------------------------------------------------------------------------- + +static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + LOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + return true; + } + return false; +} + +bool android_server_PowerManagerService_isScreenOn() { + AutoMutex _l(gPowerManagerLock); + return gScreenOn; +} + +bool android_server_PowerManagerService_isScreenBright() { + AutoMutex _l(gPowerManagerLock); + return gScreenBright; +} + +void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) { + if (gPowerManagerServiceObj) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.userActivity, + nanoseconds_to_milliseconds(eventTime), false, eventType, false); + checkAndClearExceptionFromCallback(env, "userActivity"); + } +} + +void android_server_PowerManagerService_goToSleep(nsecs_t eventTime) { + if (gPowerManagerServiceObj) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.goToSleep, + nanoseconds_to_milliseconds(eventTime)); + checkAndClearExceptionFromCallback(env, "goToSleep"); + } +} + +// ---------------------------------------------------------------------------- + +static void android_server_PowerManagerService_nativeInit(JNIEnv* env, jobject obj) { + gPowerManagerServiceObj = env->NewGlobalRef(obj); +} + +static void android_server_PowerManagerService_nativeSetPowerState(JNIEnv* env, + jobject serviceObj, jboolean screenOn, jboolean screenBright) { + AutoMutex _l(gPowerManagerLock); + gScreenOn = screenOn; + gScreenBright = screenBright; +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gPowerManagerServiceMethods[] = { + /* name, signature, funcPtr */ + { "nativeInit", "()V", + (void*) android_server_PowerManagerService_nativeInit }, + { "nativeSetPowerState", "(ZZ)V", + (void*) android_server_PowerManagerService_nativeSetPowerState }, +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + +#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method " methodName); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +int register_android_server_PowerManagerService(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "com/android/server/PowerManagerService", + gPowerManagerServiceMethods, NELEM(gPowerManagerServiceMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + + // Callbacks + + FIND_CLASS(gPowerManagerServiceClassInfo.clazz, "com/android/server/PowerManagerService"); + + GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleep, gPowerManagerServiceClassInfo.clazz, + "goToSleep", "(J)V"); + + GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, gPowerManagerServiceClassInfo.clazz, + "userActivity", "(JZIZ)V"); + + return 0; +} + +} /* namespace android */ diff --git a/services/jni/com_android_server_PowerManagerService.h b/services/jni/com_android_server_PowerManagerService.h new file mode 100644 index 0000000..9b05f38 --- /dev/null +++ b/services/jni/com_android_server_PowerManagerService.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef _ANDROID_SERVER_POWER_MANAGER_SERVICE_H +#define _ANDROID_SERVER_POWER_MANAGER_SERVICE_H + +#include "JNIHelp.h" +#include "jni.h" + +namespace android { + +enum { + POWER_MANAGER_OTHER_EVENT = 0, + POWER_MANAGER_CHEEK_EVENT = 1, + POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either + // up events or LONG_TOUCH events. + POWER_MANAGER_LONG_TOUCH_EVENT = 3, + POWER_MANAGER_TOUCH_UP_EVENT = 4, + POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events. +}; + +extern bool android_server_PowerManagerService_isScreenOn(); +extern bool android_server_PowerManagerService_isScreenBright(); +extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType); +extern void android_server_PowerManagerService_goToSleep(nsecs_t eventTime); + +} // namespace android + +#endif // _ANDROID_SERVER_POWER_MANAGER_SERVICE_H diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp index a1a6838..1a2d8b6 100644 --- a/services/jni/onload.cpp +++ b/services/jni/onload.cpp @@ -6,9 +6,9 @@ namespace android { int register_android_server_AlarmManagerService(JNIEnv* env); int register_android_server_BatteryService(JNIEnv* env); -int register_android_server_KeyInputQueue(JNIEnv* env); int register_android_server_InputManager(JNIEnv* env); int register_android_server_LightsService(JNIEnv* env); +int register_android_server_PowerManagerService(JNIEnv* env); int register_android_server_SensorService(JNIEnv* env); int register_android_server_VibratorService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); @@ -28,7 +28,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) } LOG_ASSERT(env, "Could not retrieve the env!"); - register_android_server_KeyInputQueue(env); + register_android_server_PowerManagerService(env); register_android_server_InputManager(env); register_android_server_LightsService(env); register_android_server_AlarmManagerService(env); diff --git a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java index a74c5c2..de59b81 100644 --- a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java +++ b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java @@ -496,9 +496,11 @@ public class PhoneNumberUtilsTest extends AndroidTestCase { assertFalse(PhoneNumberUtils.isVoiceMailNumber("+18001234567")); assertFalse(PhoneNumberUtils.isVoiceMailNumber("")); assertFalse(PhoneNumberUtils.isVoiceMailNumber(null)); - TelephonyManager mTelephonyManager = + // This test fails on a device without a sim card + /*TelephonyManager mTelephonyManager = (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE); String mVoiceMailNumber = mTelephonyManager.getDefault().getVoiceMailNumber(); assertTrue(PhoneNumberUtils.isVoiceMailNumber(mVoiceMailNumber)); + */ } } diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java index 63d50c7..70d1643 100644 --- a/test-runner/src/android/test/InstrumentationTestRunner.java +++ b/test-runner/src/android/test/InstrumentationTestRunner.java @@ -496,9 +496,18 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu return null; } + /** + * Initialize the current thread as a looper. + * <p/> + * Exposed for unit testing. + */ + void prepareLooper() { + Looper.prepare(); + } + @Override public void onStart() { - Looper.prepare(); + prepareLooper(); if (mJustCount) { mResults.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID); @@ -521,6 +530,11 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu long runTime = System.currentTimeMillis() - startTime; resultPrinter.print(mTestRunner.getTestResult(), runTime); + } catch (Throwable t) { + // catch all exceptions so a more verbose error message can be outputted + writer.println(String.format("Test run aborted due to unexpected exception: %s", + t.getMessage())); + t.printStackTrace(writer); } finally { mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, String.format("\nTest results for %s=%s", @@ -762,9 +776,11 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu TimedTest.class).includeDetailedStats(); } } catch (SecurityException e) { - throw new IllegalStateException(e); + // ignore - the test with given name cannot be accessed. Will be handled during + // test execution } catch (NoSuchMethodException e) { - throw new IllegalStateException(e); + // ignore- the test with given name does not exist. Will be handled during test + // execution } if (mIsTimedTest && mIncludeDetailedStats) { diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 3e77b9b..e96173b 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -483,4 +483,9 @@ public class MockPackageManager extends PackageManager { public boolean isSafeMode() { throw new UnsupportedOperationException(); } + + @Override + public void setPackageObbPath(String packageName, String path) { + throw new UnsupportedOperationException(); + } } diff --git a/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java b/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java index 6db72ad..d98b217 100644 --- a/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java +++ b/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java @@ -16,6 +16,7 @@ package android.test; +import android.app.Instrumentation; import android.content.Context; import android.os.Bundle; import android.test.mock.MockContext; @@ -89,6 +90,42 @@ public class InstrumentationTestRunnerTest extends TestCase { } + /** + * Test that runtime exceptions during runTest are handled gracefully + */ + public void testUnhandledException() throws Exception { + StubAndroidTestRunner stubAndroidTestRunner = new StubAndroidTestRunner() { + @Override + public void runTest() { + throw new RuntimeException(); + } + }; + StubInstrumentationTestRunner instrumentationTestRunner = new StubInstrumentationTestRunner( + new StubContext("com.google.foo.tests"), + new StubContext(mTargetContextPackageName), stubAndroidTestRunner); + instrumentationTestRunner.onCreate(new Bundle()); + instrumentationTestRunner.onStart(); + assertTrue("Instrumentation did not finish", instrumentationTestRunner.isFinished()); + // ensure a meaningful error message placed in results + String resultsData = instrumentationTestRunner.mResults.getString( + Instrumentation.REPORT_KEY_STREAMRESULT); + assertTrue("Instrumentation results is missing RuntimeException", + resultsData.contains("RuntimeException")); + } + + /** + * Test that specifying a method which does not exist is handled gracefully + */ + public void testBadMethodArgument() throws Exception { + String testClassName = PlaceHolderTest.class.getName(); + String invalidMethodName = "testNoExist"; + String classAndMethod = testClassName + "#" + invalidMethodName; + mInstrumentationTestRunner.onCreate(createBundle( + InstrumentationTestRunner.ARGUMENT_TEST_CLASS, classAndMethod)); + assertTestRunnerCalledWithExpectedParameters(testClassName, + invalidMethodName); + } + public void testDelayParameter() throws Exception { int delayMsec = 1000; Bundle args = new Bundle(); @@ -170,6 +207,7 @@ public class InstrumentationTestRunnerTest extends TestCase { private TestSuite mTestSuite; private TestSuite mDefaultTestSuite; private String mPackageNameForDefaultTests; + private Bundle mResults; public StubInstrumentationTestRunner(Context context, Context targetContext, AndroidTestRunner androidTestRunner) { @@ -200,6 +238,7 @@ public class InstrumentationTestRunnerTest extends TestCase { public void finish(int resultCode, Bundle results) { mFinished = true; + mResults = results; } public boolean isStarted() { @@ -221,6 +260,11 @@ public class InstrumentationTestRunnerTest extends TestCase { public String getPackageNameForDefaultTests() { return mPackageNameForDefaultTests; } + + @Override + void prepareLooper() { + // ignore + } } private static class StubContext extends MockContext { |