summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.xml47
-rw-r--r--core/java/android/app/ContextImpl.java9
-rw-r--r--core/java/android/app/NativeActivity.java11
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageManager.java13
-rw-r--r--core/java/android/hardware/Camera.java2
-rw-r--r--core/java/android/hardware/Sensor.java20
-rw-r--r--core/java/android/hardware/SensorEvent.java203
-rw-r--r--core/java/android/hardware/SensorManager.java246
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java77
-rw-r--r--core/java/android/view/IWindow.aidl3
-rw-r--r--core/java/android/view/IWindowSession.aidl4
-rw-r--r--core/java/android/view/RawInputEvent.java201
-rw-r--r--core/java/android/view/SurfaceView.java36
-rw-r--r--core/java/android/view/ViewRoot.java603
-rw-r--r--core/java/android/view/WindowManagerPolicy.java131
-rw-r--r--core/java/android/widget/CursorTreeAdapter.java8
-rw-r--r--core/java/android/widget/SimpleCursorTreeAdapter.java55
-rw-r--r--core/java/com/android/internal/view/BaseIWindow.java53
-rw-r--r--core/jni/android_app_NativeActivity.cpp187
-rw-r--r--core/jni/android_os_MessageQueue.cpp8
-rw-r--r--core/jni/android_view_Surface.cpp2
-rw-r--r--core/jni/com_google_android_gles_jni_EGLImpl.cpp3
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.pngbin1263 -> 0 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.pngbin1239 -> 0 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.pngbin1263 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.pngbin815 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.pngbin773 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.pngbin815 -> 0 bytes
-rw-r--r--core/res/res/values/public.xml6
-rw-r--r--docs/html/images/axis_device.pngbin0 -> 17625 bytes
-rw-r--r--docs/html/images/axis_globe.pngbin0 -> 8306 bytes
-rw-r--r--docs/html/sdk/android-1.5.jd2
-rw-r--r--docs/html/sdk/android-1.6.jd2
-rw-r--r--docs/html/sdk/android-2.1.jd2
-rw-r--r--docs/html/sdk/android-2.2.jd28
-rw-r--r--docs/html/sdk/download.jd4
-rw-r--r--docs/html/sdk/eclipse-adt.jd5
-rw-r--r--docs/html/sdk/ndk/index.jd29
-rw-r--r--docs/html/sdk/requirements.jd3
-rw-r--r--docs/html/sdk/sdk_toc.cs6
-rw-r--r--include/android_runtime/android_app_NativeActivity.h35
-rw-r--r--include/android_runtime/android_view_Surface.h (renamed from core/jni/android_view_Surface.h)0
-rw-r--r--include/media/AudioEffect.h70
-rw-r--r--include/media/EffectBassBoostApi.h42
-rw-r--r--include/media/EffectEnvironmentalReverbApi.h (renamed from include/media/EffectReverbApi.h)27
-rw-r--r--include/media/EffectPresetReverbApi.h54
-rw-r--r--include/media/EffectVirtualizerApi.h42
-rw-r--r--include/media/EffectVisualizerApi.h55
-rw-r--r--include/media/IMediaPlayerService.h4
-rw-r--r--include/media/MediaRecorderBase.h1
-rw-r--r--include/media/PVMediaRecorder.h1
-rw-r--r--include/media/Visualizer.h160
-rw-r--r--include/media/mediaplayer.h1
-rw-r--r--include/media/stagefright/MetaData.h4
-rw-r--r--include/media/stagefright/OMXCodec.h7
-rw-r--r--include/media/stagefright/foundation/AHandler.h2
-rw-r--r--include/media/stagefright/foundation/ALooperRoster.h6
-rw-r--r--include/ui/InputReader.h15
-rw-r--r--include/ui/InputTransport.h5
-rw-r--r--include/ui/Rect.h33
-rw-r--r--include/utils/ObbFile.h87
-rw-r--r--include/utils/PollLoop.h74
-rw-r--r--libs/audioflinger/AudioFlinger.cpp40
-rw-r--r--libs/audioflinger/AudioFlinger.h6
-rw-r--r--libs/ui/InputDispatcher.cpp9
-rw-r--r--libs/ui/InputReader.cpp128
-rw-r--r--libs/ui/Rect.cpp8
-rw-r--r--libs/utils/Android.mk6
-rw-r--r--libs/utils/ObbFile.cpp296
-rw-r--r--libs/utils/PollLoop.cpp153
-rw-r--r--libs/utils/tests/Android.mk1
-rw-r--r--libs/utils/tests/ObbFile_test.cpp82
-rw-r--r--libs/utils/tests/PollLoop_test.cpp78
-rw-r--r--media/java/android/media/AudioEffect.java713
-rw-r--r--media/java/android/media/BassBoost.java213
-rw-r--r--media/java/android/media/EnvironmentalReverb.java504
-rw-r--r--media/java/android/media/Equalizer.java443
-rw-r--r--media/java/android/media/PresetReverb.java219
-rw-r--r--media/java/android/media/Virtualizer.java214
-rwxr-xr-xmedia/java/android/media/Visualizer.java510
-rw-r--r--media/jni/android_media_MediaPlayer.cpp13
-rw-r--r--media/jni/audioeffect/Android.mk3
-rw-r--r--media/jni/audioeffect/android_media_AudioEffect.cpp45
-rw-r--r--media/jni/audioeffect/android_media_Visualizer.cpp507
-rw-r--r--media/libeffects/Android.mk30
-rw-r--r--media/libeffects/EffectReverb.c1145
-rw-r--r--media/libeffects/EffectReverb.h7
-rw-r--r--media/libeffects/EffectVisualizer.cpp401
-rw-r--r--media/libmedia/Android.mk3
-rw-r--r--media/libmedia/AudioEffect.cpp40
-rw-r--r--media/libmedia/IMediaPlayerService.cpp17
-rw-r--r--media/libmedia/Visualizer.cpp330
-rw-r--r--media/libmedia/mediaplayer.cpp57
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp137
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.h4
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.cpp7
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.h19
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.cpp98
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.h5
-rw-r--r--media/libstagefright/AwesomePlayer.cpp52
-rw-r--r--media/libstagefright/OMXCodec.cpp105
-rw-r--r--media/libstagefright/codecs/aacdec/AACDecoder.cpp106
-rw-r--r--media/libstagefright/foundation/AHandler.cpp33
-rw-r--r--media/libstagefright/foundation/ALooperRoster.cpp54
-rw-r--r--media/libstagefright/foundation/Android.mk1
-rw-r--r--media/libstagefright/include/AACDecoder.h4
-rw-r--r--media/libstagefright/include/AwesomePlayer.h5
-rw-r--r--media/libstagefright/omx/OMX.cpp9
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java30
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java4
-rw-r--r--native/android/Android.mk8
-rw-r--r--native/android/activity.cpp0
-rw-r--r--native/android/input.cpp13
-rw-r--r--native/android/looper.cpp82
-rw-r--r--native/android/native_activity.cpp31
-rw-r--r--native/android/native_window.cpp58
-rw-r--r--native/glue/threaded_app/Android.mk18
-rw-r--r--native/glue/threaded_app/threaded_app.c279
-rw-r--r--native/include/android/input.h15
-rw-r--r--native/include/android/looper.h177
-rw-r--r--native/include/android/native_activity.h20
-rw-r--r--native/include/android/native_window.h43
-rw-r--r--native/include/android/native_window_jni.h40
-rw-r--r--native/include/android/rect.h36
-rw-r--r--native/include/android/window.h58
-rw-r--r--native/include/android_glue/threaded_app.h165
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java19
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java9
-rw-r--r--packages/SystemUI/res/layout/intruder_alert.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java32
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java453
-rw-r--r--services/java/com/android/server/InputDevice.java1025
-rw-r--r--services/java/com/android/server/InputManager.java50
-rw-r--r--services/java/com/android/server/KeyInputQueue.java1388
-rw-r--r--services/java/com/android/server/PackageManagerService.java33
-rw-r--r--services/java/com/android/server/PowerManagerService.java16
-rw-r--r--services/java/com/android/server/WindowManagerService.java2093
-rw-r--r--services/jni/Android.mk2
-rw-r--r--services/jni/com_android_server_InputManager.cpp164
-rw-r--r--services/jni/com_android_server_KeyInputQueue.cpp358
-rw-r--r--services/jni/com_android_server_PowerManagerService.cpp142
-rw-r--r--services/jni/com_android_server_PowerManagerService.h42
-rw-r--r--services/jni/onload.cpp4
-rw-r--r--telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java4
-rw-r--r--test-runner/src/android/test/InstrumentationTestRunner.java22
-rw-r--r--test-runner/src/android/test/mock/MockPackageManager.java5
-rw-r--r--test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java44
-rw-r--r--tests/StatusBar/res/drawable-hdpi/stat_sys_phone.pngbin0 -> 1184 bytes
-rw-r--r--tests/StatusBar/res/drawable-mdpi/stat_sys_phone.pngbin0 -> 746 bytes
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java9
151 files changed, 8713 insertions, 7876 deletions
diff --git a/api/current.xml b/api/current.xml
index 5d71cad..e66b2e7 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"
@@ -13625,7 +13625,7 @@
value="17301636"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -13636,7 +13636,7 @@
value="17301637"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -13647,7 +13647,7 @@
value="17301638"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -13691,7 +13691,7 @@
value="17301671"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -13702,7 +13702,7 @@
value="17301672"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -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/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 161161c..d72dda7 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -6,7 +6,9 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.PixelFormat;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
import android.os.Looper;
import android.os.MessageQueue;
import android.view.InputChannel;
@@ -33,7 +35,8 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback,
private boolean mDestroyed;
- private native int loadNativeCode(String path, MessageQueue queue);
+ private native int loadNativeCode(String path, MessageQueue queue,
+ String internalDataPath, String externalDataPath, int sdkVersion);
private native void unloadNativeCode(int handle);
private native void onStartNative(int handle);
@@ -90,7 +93,11 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback,
throw new IllegalArgumentException("Unable to find native library: " + libname);
}
- mNativeHandle = loadNativeCode(path, Looper.myQueue());
+ mNativeHandle = loadNativeCode(path, Looper.myQueue(),
+ getFilesDir().toString(),
+ Environment.getExternalStorageAppFilesDirectory(ai.packageName).toString(),
+ Build.VERSION.SDK_INT);
+
if (mNativeHandle == 0) {
throw new IllegalArgumentException("Unable to load native library: " + path);
}
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/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index e2f5ada..3490ac0 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -20,24 +20,32 @@ package android.hardware;
/**
* Class representing a sensor. Use {@link SensorManager#getSensorList} to get
* the list of available Sensors.
+ *
+ * @see SensorManager
+ * @see SensorEventListener
+ * @see SensorEvent
+ *
*/
public class Sensor {
/**
* A constant describing an accelerometer sensor type. See
- * {@link android.hardware.SensorEvent SensorEvent} for more details.
+ * {@link android.hardware.SensorEvent#values SensorEvent.values} for more
+ * details.
*/
public static final int TYPE_ACCELEROMETER = 1;
/**
* A constant describing a magnetic field sensor type. See
- * {@link android.hardware.SensorEvent SensorEvent} for more details.
+ * {@link android.hardware.SensorEvent#values SensorEvent.values} for more
+ * details.
*/
public static final int TYPE_MAGNETIC_FIELD = 2;
/**
* A constant describing an orientation sensor type. See
- * {@link android.hardware.SensorEvent SensorEvent} for more details.
+ * {@link android.hardware.SensorEvent#values SensorEvent.values} for more
+ * details.
*
* @deprecated use {@link android.hardware.SensorManager#getOrientation
* SensorManager.getOrientation()} instead.
@@ -50,7 +58,8 @@ public class Sensor {
/**
* A constant describing an light sensor type. See
- * {@link android.hardware.SensorEvent SensorEvent} for more details.
+ * {@link android.hardware.SensorEvent#values SensorEvent.values} for more
+ * details.
*/
public static final int TYPE_LIGHT = 5;
@@ -62,7 +71,8 @@ public class Sensor {
/**
* A constant describing an proximity sensor type. See
- * {@link android.hardware.SensorEvent SensorEvent} for more details.
+ * {@link android.hardware.SensorEvent#values SensorEvent.values} for more
+ * details.
*/
public static final int TYPE_PROXIMITY = 8;
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index dfefe7e..70519ff 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -28,17 +28,20 @@ package android.hardware;
* </p>
*
* <p>
- * The coordinate space is defined relative to the screen of the phone in its
+ * The coordinate-system is defined relative to the screen of the phone in its
* default orientation. The axes are not swapped when the device's screen
* orientation changes.
* </p>
*
* <p>
- * The OpenGL ES coordinate system is used. The origin is in the lower-left
- * corner with respect to the screen, with the X axis horizontal and pointing
- * right, the Y axis vertical and pointing up and the Z axis pointing outside
- * the front face of the screen. In this system, coordinates behind the screen
- * have negative Z values.
+ * The X axis is horizontal and points to the right, the Y axis is vertical and
+ * points up and the Z axis points towards the outside of the front face of the
+ * screen. In this system, coordinates behind the screen have negative Z values.
+ * </p>
+ *
+ * <p>
+ * <center><img src="../../../images/axis_device.png"
+ * alt="Sensors coordinate-system diagram." border="0" /></center>
* </p>
*
* <p>
@@ -46,127 +49,157 @@ package android.hardware;
* Android 2D APIs where the origin is in the top-left corner.
* </p>
*
- * <pre>
- * x<0 x>0
- * ^
- * |
- * +-----------+--> y>0
- * | |
- * | |
- * | |
- * | | / z<0
- * | | /
- * | | /
- * O-----------+/
- * |[] [ ] []/
- * +----------/+ y<0
- * /
- * /
- * |/ z>0 (toward the sky)
+ * @see SensorManager
+ * @see SensorEvent
+ * @see Sensor
*
- * O: Origin (x=0,y=0,z=0)
- * </pre>
*/
public class SensorEvent {
/**
* <p>
- * The length and contents of the values array vary depending on which
- * {@link android.hardware.Sensor sensor} type is being monitored (see also
- * {@link SensorEvent} for a definition of the coordinate system used):
+ * The length and contents of the {@link #values values} array depends on
+ * which {@link android.hardware.Sensor sensor} type is being monitored (see
+ * also {@link SensorEvent} for a definition of the coordinate system used).
* </p>
*
- * <h3>{@link android.hardware.Sensor#TYPE_ORIENTATION
- * Sensor.TYPE_ORIENTATION}:</h3> All values are angles in degrees.
- *
+ * <h4>{@link android.hardware.Sensor#TYPE_ACCELEROMETER
+ * Sensor.TYPE_ACCELEROMETER}:</h4> All values are in SI units (m/s^2)
+ *
* <ul>
* <p>
- * values[0]: Azimuth, angle between the magnetic north direction and the Y
- * axis, around the Z axis (0 to 359). 0=North, 90=East, 180=South, 270=West
- *
+ * values[0]: Acceleration minus Gx on the x-axis
+ * </p>
* <p>
- * values[1]: Pitch, rotation around X axis (-180 to 180), with positive
- * values when the z-axis moves <b>toward</b> the y-axis.
- *
+ * values[1]: Acceleration minus Gy on the y-axis
+ * </p>
* <p>
- * values[2]: Roll, rotation around Y axis (-90 to 90), with positive values
- * when the x-axis moves <b>toward</b> the z-axis.
+ * values[2]: Acceleration minus Gz on the z-axis
+ * </p>
* </ul>
- *
- * <p>
- * <b>Important note:</b> For historical reasons the roll angle is positive
- * in the clockwise direction (mathematically speaking, it should be
- * positive in the counter-clockwise direction).
- *
+ *
* <p>
- * <b>Note:</b> This definition is different from <b>yaw, pitch and roll</b>
- * used in aviation where the X axis is along the long side of the plane
- * (tail to nose).
- *
+ * A sensor of this type measures the acceleration applied to the device
+ * (<b>Ad</b>). Conceptually, it does so by measuring forces applied to the
+ * sensor itself (<b>Fs</b>) using the relation:
+ * </p>
+ *
+ * <b><center>Ad = - ·Fs / mass</center></b>
+ *
* <p>
- * <b>Note:</b> This sensor type exists for legacy reasons, please use
- * {@link android.hardware.SensorManager#getRotationMatrix
- * getRotationMatrix()} in conjunction with
- * {@link android.hardware.SensorManager#remapCoordinateSystem
- * remapCoordinateSystem()} and
- * {@link android.hardware.SensorManager#getOrientation getOrientation()} to
- * compute these values instead.
- *
- * <h3>{@link android.hardware.Sensor#TYPE_ACCELEROMETER
- * Sensor.TYPE_ACCELEROMETER}:</h3>
- * All values are in SI units (m/s^2) and measure the acceleration applied
- * to the phone minus the force of gravity.
- *
- * <ul>
+ * In particular, the force of gravity is always influencing the measured
+ * acceleration:
+ * </p>
+ *
+ * <b><center>Ad = -g - ·F / mass</center></b>
+ *
* <p>
- * values[0]: Acceleration minus Gx on the x-axis
+ * For this reason, when the device is sitting on a table (and obviously not
+ * accelerating), the accelerometer reads a magnitude of <b>g</b> = 9.81
+ * m/s^2
+ * </p>
+ *
* <p>
- * values[1]: Acceleration minus Gy on the y-axis
+ * Similarly, when the device is in free-fall and therefore dangerously
+ * accelerating towards to ground at 9.81 m/s^2, its accelerometer reads a
+ * magnitude of 0 m/s^2.
+ * </p>
+ *
* <p>
- * values[2]: Acceleration minus Gz on the z-axis
- * </ul>
- *
+ * It should be apparent that in order to measure the real acceleration of
+ * the device, the contribution of the force of gravity must be eliminated.
+ * This can be achieved by applying a <i>high-pass</i> filter. Conversely, a
+ * <i>low-pass</i> filter can be used to isolate the force of gravity.
+ * </p>
* <p>
* <u>Examples</u>:
* <ul>
* <li>When the device lies flat on a table and is pushed on its left side
* toward the right, the x acceleration value is positive.</li>
- *
+ *
* <li>When the device lies flat on a table, the acceleration value is
* +9.81, which correspond to the acceleration of the device (0 m/s^2) minus
* the force of gravity (-9.81 m/s^2).</li>
- *
+ *
* <li>When the device lies flat on a table and is pushed toward the sky
* with an acceleration of A m/s^2, the acceleration value is equal to
* A+9.81 which correspond to the acceleration of the device (+A m/s^2)
* minus the force of gravity (-9.81 m/s^2).</li>
* </ul>
- *
- *
- * <h3>{@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD
- * Sensor.TYPE_MAGNETIC_FIELD}:</h3>
+ *
+ *
+ * <h4>{@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD
+ * Sensor.TYPE_MAGNETIC_FIELD}:</h4>
* All values are in micro-Tesla (uT) and measure the ambient magnetic field
* in the X, Y and Z axis.
- *
- * <h3>{@link android.hardware.Sensor#TYPE_LIGHT Sensor.TYPE_LIGHT}:</h3>
- *
+ *
+ * <h4>{@link android.hardware.Sensor#TYPE_LIGHT Sensor.TYPE_LIGHT}:</h4>
+ *
* <ul>
* <p>
* values[0]: Ambient light level in SI lux units
* </ul>
- *
- * <h3>{@link android.hardware.Sensor#TYPE_PROXIMITY Sensor.TYPE_PROXIMITY}:
- * </h3>
- *
+ *
+ * <h4>{@link android.hardware.Sensor#TYPE_PROXIMITY Sensor.TYPE_PROXIMITY}:
+ * </h4>
+ *
* <ul>
* <p>
* values[0]: Proximity sensor distance measured in centimeters
* </ul>
- *
+ *
* <p>
- * Note that some proximity sensors only support a binary "close" or "far"
- * measurement. In this case, the sensor should report its maxRange value in
- * the "far" state and a value less than maxRange in the "near" state.
+ * <b>Note:</b> Some proximity sensors only support a binary <i>near</i> or
+ * <i>far</i> measurement. In this case, the sensor should report its
+ * {@link android.hardware.Sensor#getMaximumRange() maximum range} value in
+ * the <i>far</i> state and a lesser value in the <i>near</i> state.
+ * </p>
+ *
+ * <h4>{@link android.hardware.Sensor#TYPE_ORIENTATION
+ * Sensor.TYPE_ORIENTATION}:</h4> All values are angles in degrees.
+ *
+ * <ul>
+ * <p>
+ * values[0]: Azimuth, angle between the magnetic north direction and the
+ * y-axis, around the z-axis (0 to 359). 0=North, 90=East, 180=South,
+ * 270=West
+ * </p>
+ *
+ * <p>
+ * values[1]: Pitch, rotation around x-axis (-180 to 180), with positive
+ * values when the z-axis moves <b>toward</b> the y-axis.
+ * </p>
+ *
+ * <p>
+ * values[2]: Roll, rotation around y-axis (-90 to 90), with positive values
+ * when the x-axis moves <b>toward</b> the z-axis.
+ * </p>
+ * </ul>
+ *
+ * <p>
+ * <b>Note:</b> This definition is different from <b>yaw, pitch and roll</b>
+ * used in aviation where the X axis is along the long side of the plane
+ * (tail to nose).
+ * </p>
+ *
+ * <p>
+ * <b>Note:</b> This sensor type exists for legacy reasons, please use
+ * {@link android.hardware.SensorManager#getRotationMatrix
+ * getRotationMatrix()} in conjunction with
+ * {@link android.hardware.SensorManager#remapCoordinateSystem
+ * remapCoordinateSystem()} and
+ * {@link android.hardware.SensorManager#getOrientation getOrientation()} to
+ * compute these values instead.
+ * </p>
+ *
+ * <p>
+ * <b>Important note:</b> For historical reasons the roll angle is positive
+ * in the clockwise direction (mathematically speaking, it should be
+ * positive in the counter-clockwise direction).
+ * </p>
+ *
+ * @see SensorEvent
+ * @see GeomagneticField
*/
public final float[] values;
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index f60e2d7..492f8cc 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -46,6 +46,30 @@ import java.util.List;
* {@link android.content.Context#getSystemService(java.lang.String)
* Context.getSystemService()} with the argument
* {@link android.content.Context#SENSOR_SERVICE}.
+ *
+ * <pre class="prettyprint">
+ * public class SensorActivity extends Activity, implements SensorEventListener {
+ * private final SensorManager mSensorManager;
+ * private final Sensor mAccelerometer;
+ *
+ * public SensorActivity() {
+ * mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
+ * mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ * mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
+ * }
+ *
+ * public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ * }
+ *
+ * public abstract void onSensorChanged(SensorEvent event) {
+ * }
+ * }
+ * </pre>
+ *
+ * @see SensorEventListener
+ * @see SensorEvent
+ * @see Sensor
+ *
*/
public class SensorManager
{
@@ -57,7 +81,7 @@ public class SensorManager
/**
* A constant describing an orientation sensor. See
* {@link android.hardware.SensorListener SensorListener} for more details.
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -66,7 +90,7 @@ public class SensorManager
/**
* A constant describing an accelerometer. See
* {@link android.hardware.SensorListener SensorListener} for more details.
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -75,7 +99,7 @@ public class SensorManager
/**
* A constant describing a temperature sensor See
* {@link android.hardware.SensorListener SensorListener} for more details.
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -84,7 +108,7 @@ public class SensorManager
/**
* A constant describing a magnetic sensor See
* {@link android.hardware.SensorListener SensorListener} for more details.
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -93,7 +117,7 @@ public class SensorManager
/**
* A constant describing an ambient light sensor See
* {@link android.hardware.SensorListener SensorListener} for more details.
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -102,7 +126,7 @@ public class SensorManager
/**
* A constant describing a proximity sensor See
* {@link android.hardware.SensorListener SensorListener} for more details.
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -111,7 +135,7 @@ public class SensorManager
/**
* A constant describing a Tricorder See
* {@link android.hardware.SensorListener SensorListener} for more details.
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -120,7 +144,7 @@ public class SensorManager
/**
* A constant describing an orientation sensor. See
* {@link android.hardware.SensorListener SensorListener} for more details.
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -128,7 +152,7 @@ public class SensorManager
/**
* A constant that includes all sensors
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -136,7 +160,7 @@ public class SensorManager
/**
* Smallest sensor ID
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -144,7 +168,7 @@ public class SensorManager
/**
* Largest sensor ID
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -154,7 +178,7 @@ public class SensorManager
/**
* Index of the X value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -163,7 +187,7 @@ public class SensorManager
/**
* Index of the Y value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -172,7 +196,7 @@ public class SensorManager
/**
* Index of the Z value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -181,7 +205,7 @@ public class SensorManager
/**
* Offset to the untransformed values in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -190,7 +214,7 @@ public class SensorManager
/**
* Index of the untransformed X value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -199,7 +223,7 @@ public class SensorManager
/**
* Index of the untransformed Y value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -208,7 +232,7 @@ public class SensorManager
/**
* Index of the untransformed Z value in the array returned by
* {@link android.hardware.SensorListener#onSensorChanged}
- *
+ *
* @deprecated use {@link android.hardware.Sensor Sensor} instead.
*/
@Deprecated
@@ -250,7 +274,7 @@ public class SensorManager
/** Minimum magnetic field on Earth's surface */
public static final float MAGNETIC_FIELD_EARTH_MIN = 30.0f;
-
+
/** Maximum luminance of sunlight in lux */
public static final float LIGHT_SUNLIGHT_MAX = 120000.0f;
/** luminance of sunlight in lux */
@@ -268,7 +292,7 @@ public class SensorManager
/** luminance at night with no moon in lux*/
public static final float LIGHT_NO_MOON = 0.001f;
-
+
/** get sensor data as fast as possible */
public static final int SENSOR_DELAY_FASTEST = 0;
/** rate suitable for games */
@@ -673,11 +697,14 @@ public class SensorManager
* Make multiple calls to get sensors of different types or use
* {@link android.hardware.Sensor#TYPE_ALL Sensor.TYPE_ALL} to get all the
* sensors.
- *
+ *
* @param type
* of sensors requested
- *
+ *
* @return a list of sensors matching the asked type.
+ *
+ * @see #getDefaultSensor(int)
+ * @see Sensor
*/
public List<Sensor> getSensorList(int type) {
// cache the returned lists the first time
@@ -707,11 +734,14 @@ public class SensorManager
* returned sensor could be a composite sensor, and its data could be
* averaged or filtered. If you need to access the raw sensors use
* {@link SensorManager#getSensorList(int) getSensorList}.
- *
+ *
* @param type
* of sensors requested
- *
+ *
* @return the default sensors matching the asked type.
+ *
+ * @see #getSensorList(int)
+ * @see Sensor
*/
public Sensor getDefaultSensor(int type) {
// TODO: need to be smarter, for now, just return the 1st sensor
@@ -721,17 +751,17 @@ public class SensorManager
/**
* Registers a listener for given sensors.
- *
+ *
* @deprecated This method is deprecated, use
* {@link SensorManager#registerListener(SensorEventListener, Sensor, int)}
* instead.
- *
+ *
* @param listener
* sensor listener object
- *
+ *
* @param sensors
* a bit masks of the sensors to register to
- *
+ *
* @return <code>true</code> if the sensor is supported and successfully
* enabled
*/
@@ -742,24 +772,24 @@ public class SensorManager
/**
* Registers a SensorListener for given sensors.
- *
+ *
* @deprecated This method is deprecated, use
* {@link SensorManager#registerListener(SensorEventListener, Sensor, int)}
* instead.
- *
+ *
* @param listener
* sensor listener object
- *
+ *
* @param sensors
* a bit masks of the sensors to register to
- *
+ *
* @param rate
* rate of events. This is only a hint to the system. events may be
* received faster or slower than the specified rate. Usually events
* are received faster. The value must be one of
* {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
* {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}.
- *
+ *
* @return <code>true</code> if the sensor is supported and successfully
* enabled
*/
@@ -819,14 +849,14 @@ public class SensorManager
/**
* Unregisters a listener for the sensors with which it is registered.
- *
+ *
* @deprecated This method is deprecated, use
* {@link SensorManager#unregisterListener(SensorEventListener, Sensor)}
* instead.
- *
+ *
* @param listener
* a SensorListener object
- *
+ *
* @param sensors
* a bit masks of the sensors to unregister from
*/
@@ -891,11 +921,11 @@ public class SensorManager
/**
* Unregisters a listener for all sensors.
- *
+ *
* @deprecated This method is deprecated, use
* {@link SensorManager#unregisterListener(SensorEventListener)}
* instead.
- *
+ *
* @param listener
* a SensorListener object
*/
@@ -906,12 +936,16 @@ public class SensorManager
/**
* Unregisters a listener for the sensors with which it is registered.
- *
+ *
* @param listener
* a SensorEventListener object
+ *
* @param sensor
* the sensor to unregister from
- *
+ *
+ * @see #unregisterListener(SensorEventListener)
+ * @see #registerListener(SensorEventListener, Sensor, int)
+ *
*/
public void unregisterListener(SensorEventListener listener, Sensor sensor) {
unregisterListener((Object)listener, sensor);
@@ -919,10 +953,13 @@ public class SensorManager
/**
* Unregisters a listener for all sensors.
- *
+ *
* @param listener
* a SensorListener object
- *
+ *
+ * @see #unregisterListener(SensorEventListener, Sensor)
+ * @see #registerListener(SensorEventListener, Sensor, int)
+ *
*/
public void unregisterListener(SensorEventListener listener) {
unregisterListener((Object)listener);
@@ -931,14 +968,14 @@ public class SensorManager
/**
* Registers a {@link android.hardware.SensorEventListener
* SensorEventListener} for the given sensor.
- *
+ *
* @param listener
* A {@link android.hardware.SensorEventListener SensorEventListener}
* object.
- *
+ *
* @param sensor
* The {@link android.hardware.Sensor Sensor} to register to.
- *
+ *
* @param rate
* The rate {@link android.hardware.SensorEvent sensor events} are
* delivered at. This is only a hint to the system. Events may be
@@ -946,10 +983,14 @@ public class SensorManager
* are received faster. The value must be one of
* {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
* {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}.
- *
+ *
* @return <code>true</code> if the sensor is supported and successfully
* enabled.
- *
+ *
+ * @see #registerListener(SensorEventListener, Sensor, int, Handler)
+ * @see #unregisterListener(SensorEventListener)
+ * @see #unregisterListener(SensorEventListener, Sensor)
+ *
*/
public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) {
return registerListener(listener, sensor, rate, null);
@@ -958,14 +999,14 @@ public class SensorManager
/**
* Registers a {@link android.hardware.SensorEventListener
* SensorEventListener} for the given sensor.
- *
+ *
* @param listener
* A {@link android.hardware.SensorEventListener SensorEventListener}
* object.
- *
+ *
* @param sensor
* The {@link android.hardware.Sensor Sensor} to register to.
- *
+ *
* @param rate
* The rate {@link android.hardware.SensorEvent sensor events} are
* delivered at. This is only a hint to the system. Events may be
@@ -973,14 +1014,18 @@ public class SensorManager
* are received faster. The value must be one of
* {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
* {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}.
- *
+ *
* @param handler
* The {@link android.os.Handler Handler} the
* {@link android.hardware.SensorEvent sensor events} will be
* delivered to.
- *
+ *
* @return true if the sensor is supported and successfully enabled.
- *
+ *
+ * @see #registerListener(SensorEventListener, Sensor, int)
+ * @see #unregisterListener(SensorEventListener)
+ * @see #unregisterListener(SensorEventListener, Sensor)
+ *
*/
public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate,
Handler handler) {
@@ -1107,7 +1152,7 @@ public class SensorManager
* world's coordinate system which is defined as a direct orthonormal basis,
* where:
* </p>
- *
+ *
* <ul>
* <li>X is defined as the vector product <b>Y.Z</b> (It is tangential to
* the ground at the device's current location and roughly points East).</li>
@@ -1115,6 +1160,12 @@ public class SensorManager
* points towards the magnetic North Pole.</li>
* <li>Z points towards the sky and is perpendicular to the ground.</li>
* </ul>
+ *
+ * <p>
+ * <center><img src="../../../images/axis_globe.png"
+ * alt="Sensors coordinate-system diagram." border="0" /></center>
+ * </p>
+ *
* <p>
* <hr>
* <p>
@@ -1129,27 +1180,27 @@ public class SensorManager
* world's coordinate system, that is, when the device's X axis points
* toward East, the Y axis points to the North Pole and the device is facing
* the sky.
- *
+ *
* <p>
* <b>I</b> is a rotation matrix transforming the geomagnetic vector into
* the same coordinate space as gravity (the world's coordinate space).
* <b>I</b> is a simple rotation around the X axis. The inclination angle in
* radians can be computed with {@link #getInclination}.
* <hr>
- *
+ *
* <p>
* Each matrix is returned either as a 3x3 or 4x4 row-major matrix depending
* on the length of the passed array:
* <p>
* <u>If the array length is 16:</u>
- *
+ *
* <pre>
* / M[ 0] M[ 1] M[ 2] M[ 3] \
* | M[ 4] M[ 5] M[ 6] M[ 7] |
* | M[ 8] M[ 9] M[10] M[11] |
* \ M[12] M[13] M[14] M[15] /
*</pre>
- *
+ *
* This matrix is ready to be used by OpenGL ES's
* {@link javax.microedition.khronos.opengles.GL10#glLoadMatrixf(float[], int)
* glLoadMatrixf(float[], int)}.
@@ -1161,44 +1212,44 @@ public class SensorManager
* therefore be used with OpenGL ES directly.
* <p>
* Also note that the returned matrices always have this form:
- *
+ *
* <pre>
* / M[ 0] M[ 1] M[ 2] 0 \
* | M[ 4] M[ 5] M[ 6] 0 |
* | M[ 8] M[ 9] M[10] 0 |
* \ 0 0 0 1 /
*</pre>
- *
+ *
* <p>
* <u>If the array length is 9:</u>
- *
+ *
* <pre>
* / M[ 0] M[ 1] M[ 2] \
* | M[ 3] M[ 4] M[ 5] |
* \ M[ 6] M[ 7] M[ 8] /
*</pre>
- *
+ *
* <hr>
* <p>
* The inverse of each matrix can be computed easily by taking its
* transpose.
- *
+ *
* <p>
* The matrices returned by this function are meaningful only when the
* device is not free-falling and it is not close to the magnetic north. If
* the device is accelerating, or placed into a strong magnetic field, the
* returned matrices may be inaccurate.
- *
+ *
* @param R
* is an array of 9 floats holding the rotation matrix <b>R</b> when
* this function returns. R can be null.
* <p>
- *
+ *
* @param I
* is an array of 9 floats holding the rotation matrix <b>I</b> when
* this function returns. I can be null.
* <p>
- *
+ *
* @param gravity
* is an array of 3 floats containing the gravity vector expressed in
* the device's coordinate. You can simply use the
@@ -1208,7 +1259,7 @@ public class SensorManager
* {@link android.hardware.Sensor#TYPE_ACCELEROMETER
* TYPE_ACCELEROMETER}.
* <p>
- *
+ *
* @param geomagnetic
* is an array of 3 floats containing the geomagnetic vector
* expressed in the device's coordinate. You can simply use the
@@ -1217,10 +1268,14 @@ public class SensorManager
* {@link android.hardware.Sensor Sensor} of type
* {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD
* TYPE_MAGNETIC_FIELD}.
- *
+ *
* @return <code>true</code> on success, <code>false</code> on failure (for
* instance, if the device is in free fall). On failure the output
* matrices are not modified.
+ *
+ * @see #getInclination(float[])
+ * @see #getOrientation(float[], float[])
+ * @see #remapCoordinateSystem(float[], int, int, float[])
*/
public static boolean getRotationMatrix(float[] R, float[] I,
@@ -1289,16 +1344,22 @@ public class SensorManager
/**
* Computes the geomagnetic inclination angle in radians from the
* inclination matrix <b>I</b> returned by {@link #getRotationMatrix}.
- *
+ *
* @param I
* inclination matrix see {@link #getRotationMatrix}.
+ *
* @return The geomagnetic inclination angle in radians.
+ *
+ * @see #getRotationMatrix(float[], float[], float[], float[])
+ * @see #getOrientation(float[], float[])
+ * @see GeomagneticField
+ *
*/
public static float getInclination(float[] I) {
if (I.length == 9) {
return (float)Math.atan2(I[5], I[4]);
} else {
- return (float)Math.atan2(I[6], I[5]);
+ return (float)Math.atan2(I[6], I[5]);
}
}
@@ -1309,7 +1370,7 @@ public class SensorManager
* compute the three orientation angles of the device (see
* {@link #getOrientation}) in a different coordinate system.
* </p>
- *
+ *
* <p>
* When the rotation matrix is used for drawing (for instance with OpenGL
* ES), it usually <b>doesn't need</b> to be transformed by this function,
@@ -1319,60 +1380,62 @@ public class SensorManager
* is generally free to rotate their screen, you often should consider the
* rotation in deciding the parameters to use here.
* </p>
- *
+ *
* <p>
* <u>Examples:</u>
* <p>
- *
+ *
* <ul>
* <li>Using the camera (Y axis along the camera's axis) for an augmented
* reality application where the rotation angles are needed:</li>
- *
+ *
* <p>
* <ul>
* <code>remapCoordinateSystem(inR, AXIS_X, AXIS_Z, outR);</code>
* </ul>
* </p>
- *
+ *
* <li>Using the device as a mechanical compass when rotation is
* {@link android.view.Surface#ROTATION_90 Surface.ROTATION_90}:</li>
- *
+ *
* <p>
* <ul>
* <code>remapCoordinateSystem(inR, AXIS_Y, AXIS_MINUS_X, outR);</code>
* </ul>
* </p>
- *
+ *
* Beware of the above example. This call is needed only to account for a
* rotation from its natural orientation when calculating the rotation
* angles (see {@link #getOrientation}). If the rotation matrix is also used
* for rendering, it may not need to be transformed, for instance if your
* {@link android.app.Activity Activity} is running in landscape mode.
* </ul>
- *
+ *
* <p>
* Since the resulting coordinate system is orthonormal, only two axes need
* to be specified.
- *
+ *
* @param inR
* the rotation matrix to be transformed. Usually it is the matrix
* returned by {@link #getRotationMatrix}.
- *
+ *
* @param X
* defines on which world axis and direction the X axis of the device
* is mapped.
- *
+ *
* @param Y
* defines on which world axis and direction the Y axis of the device
* is mapped.
- *
+ *
* @param outR
* the transformed rotation matrix. inR and outR can be the same
* array, but it is not recommended for performance reason.
- *
+ *
* @return <code>true</code> on success. <code>false</code> if the input
* parameters are incorrect, for instance if X and Y define the same
* axis. Or if inR and outR don't have the same length.
+ *
+ * @see #getRotationMatrix(float[], float[], float[], float[])
*/
public static boolean remapCoordinateSystem(float[] inR, int X, int Y,
@@ -1464,14 +1527,23 @@ public class SensorManager
* <li>values[2]: <i>roll</i>, rotation around the Y axis.</li>
* </ul>
* <p>
+ * <center><img src="../../../images/axis_device.png"
+ * alt="Sensors coordinate-system diagram." border="0" /></center>
+ * </p>
+ * <p>
* All three angles above are in <b>radians</b> and <b>positive</b> in the
* <b>counter-clockwise</b> direction.
*
* @param R
* rotation matrix see {@link #getRotationMatrix}.
+ *
* @param values
* an array of 3 floats to hold the result.
+ *
* @return The array values passed as argument.
+ *
+ * @see #getRotationMatrix(float[], float[], float[], float[])
+ * @see GeomagneticField
*/
public static float[] getOrientation(float[] R, float values[]) {
/*
@@ -1480,12 +1552,12 @@ public class SensorManager
* | R[ 4] R[ 5] R[ 6] 0 |
* | R[ 8] R[ 9] R[10] 0 |
* \ 0 0 0 1 /
- *
+ *
* 3x3 (length=9) case:
* / R[ 0] R[ 1] R[ 2] \
* | R[ 3] R[ 4] R[ 5] |
* \ R[ 6] R[ 7] R[ 8] /
- *
+ *
*/
if (R.length == 9) {
values[0] = (float)Math.atan2(R[1], R[4]);
@@ -1647,7 +1719,7 @@ public class SensorManager
}
}
}
-
+
class LmsFilter {
private static final int SENSORS_RATE_MS = 20;
private static final int COUNT = 12;
@@ -1715,7 +1787,7 @@ public class SensorManager
}
}
-
+
private static native void nativeClassInit();
private static native int sensors_module_init();
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..fb45971 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -452,6 +452,7 @@ public final class ViewRoot extends Handler implements ViewParent,
((RootViewSurfaceTaker)view).willYouTakeTheSurface();
if (mSurfaceHolderCallback != null) {
mSurfaceHolder = new TakenSurfaceHolder();
+ mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
}
}
Resources resources = mView.getContext().getResources();
@@ -556,18 +557,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);
@@ -756,7 +755,7 @@ public final class ViewRoot extends Handler implements ViewParent,
// object is not initialized to its backing store, but soon it
// will be (assuming the window is visible).
attachInfo.mSurface = mSurface;
- attachInfo.mTranslucentWindow = lp.format != PixelFormat.OPAQUE;
+ attachInfo.mTranslucentWindow = PixelFormat.formatHasAlpha(lp.format);
attachInfo.mHasWindowFocus = false;
attachInfo.mWindowVisibility = viewVisibility;
attachInfo.mRecomputeGlobalAttributes = false;
@@ -928,8 +927,6 @@ public final class ViewRoot extends Handler implements ViewParent,
if (mSurfaceHolder != null) {
mSurfaceHolder.mSurfaceLock.lock();
mDrawingAllowed = true;
- lp.format = mSurfaceHolder.getRequestedFormat();
- lp.type = mSurfaceHolder.getRequestedType();
}
boolean initialized = false;
@@ -1745,16 +1742,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 +1756,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 +1860,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 +2009,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 +2136,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 +2239,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 +2765,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 +2805,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 +2947,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 +2963,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_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index dab1dba..54a9c2a 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -21,7 +21,8 @@
#include <dlfcn.h>
#include <android_runtime/AndroidRuntime.h>
-#include <android/native_activity.h>
+#include <android_runtime/android_view_Surface.h>
+#include <android_runtime/android_app_NativeActivity.h>
#include <surfaceflinger/Surface.h>
#include <ui/egl/android_natives.h>
#include <ui/InputTransport.h>
@@ -31,7 +32,6 @@
#include "android_os_MessageQueue.h"
#include "android_view_InputChannel.h"
#include "android_view_KeyEvent.h"
-#include "android_view_Surface.h"
namespace android
{
@@ -46,6 +46,48 @@ static struct {
// ------------------------------------------------------------------------
+struct ActivityWork {
+ int32_t cmd;
+ int32_t arg1;
+ int32_t arg2;
+};
+
+enum {
+ CMD_DEF_KEY = 1,
+ CMD_SET_WINDOW_FORMAT,
+ CMD_SET_WINDOW_FLAGS,
+};
+
+static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) {
+ ActivityWork work;
+ work.cmd = cmd;
+ work.arg1 = arg1;
+ work.arg2 = arg2;
+
+restart:
+ int res = write(fd, &work, sizeof(work));
+ if (res < 0 && errno == EINTR) {
+ goto restart;
+ }
+
+ if (res == sizeof(work)) return;
+
+ if (res < 0) LOGW("Failed writing to work fd: %s", strerror(errno));
+ else LOGW("Truncated writing to work fd: %d", res);
+}
+
+static bool read_work(int fd, ActivityWork* outWork) {
+ int res = read(fd, outWork, sizeof(ActivityWork));
+ // no need to worry about EINTR, poll loop will just come back again.
+ if (res == sizeof(ActivityWork)) return true;
+
+ if (res < 0) LOGW("Failed reading work fd: %s", strerror(errno));
+ else LOGW("Truncated reading work fd: %d", res);
+ return false;
+}
+
+// ------------------------------------------------------------------------
+
/*
* Specialized input queue that allows unhandled key events to be dispatched
* back to the native activity's Java framework code.
@@ -59,8 +101,7 @@ struct MyInputQueue : AInputQueue {
mLock.lock();
LOGI("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite);
if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) {
- int8_t cmd = 1;
- write(mWorkWrite, &cmd, sizeof(cmd));
+ write_work(mWorkWrite, CMD_DEF_KEY);
}
mPendingKeys.add(keyEvent);
mLock.unlock();
@@ -90,9 +131,9 @@ struct MyInputQueue : AInputQueue {
/*
* Native state for interacting with the NativeActivity class.
*/
-struct NativeCode {
+struct NativeCode : public ANativeActivity {
NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) {
- memset(&activity, sizeof(activity), 0);
+ memset((ANativeActivity*)this, sizeof(ANativeActivity), 0);
memset(&callbacks, sizeof(callbacks), 0);
dlhandle = _dlhandle;
createActivityFunc = _createFunc;
@@ -103,8 +144,11 @@ struct NativeCode {
}
~NativeCode() {
- if (activity.env != NULL && activity.clazz != NULL) {
- activity.env->DeleteGlobalRef(activity.clazz);
+ if (callbacks.onDestroy != NULL) {
+ callbacks.onDestroy(this);
+ }
+ if (env != NULL && clazz != NULL) {
+ env->DeleteGlobalRef(clazz);
}
if (pollLoop != NULL && mainWorkRead >= 0) {
pollLoop->removeCallback(mainWorkRead);
@@ -114,9 +158,6 @@ struct NativeCode {
}
setSurface(NULL);
setInputChannel(NULL);
- if (callbacks.onDestroy != NULL) {
- callbacks.onDestroy(&activity);
- }
if (mainWorkRead >= 0) close(mainWorkRead);
if (mainWorkWrite >= 0) close(mainWorkWrite);
if (dlhandle != NULL) {
@@ -129,7 +170,7 @@ struct NativeCode {
void setSurface(jobject _surface) {
if (_surface != NULL) {
- nativeWindow = android_Surface_getNativeWindow(activity.env, _surface);
+ nativeWindow = android_Surface_getNativeWindow(env, _surface);
} else {
nativeWindow = NULL;
}
@@ -138,14 +179,14 @@ struct NativeCode {
status_t setInputChannel(jobject _channel) {
if (inputChannel != NULL) {
delete nativeInputQueue;
- activity.env->DeleteGlobalRef(inputChannel);
+ env->DeleteGlobalRef(inputChannel);
}
inputChannel = NULL;
nativeInputQueue = NULL;
if (_channel != NULL) {
- inputChannel = activity.env->NewGlobalRef(_channel);
+ inputChannel = env->NewGlobalRef(_channel);
sp<InputChannel> ic =
- android_view_InputChannel_getInputChannel(activity.env, _channel);
+ android_view_InputChannel_getInputChannel(env, _channel);
if (ic != NULL) {
nativeInputQueue = new MyInputQueue(ic, mainWorkWrite);
if (nativeInputQueue->getConsumer().initialize() != android::OK) {
@@ -160,12 +201,14 @@ struct NativeCode {
return OK;
}
- ANativeActivity activity;
ANativeActivityCallbacks callbacks;
void* dlhandle;
ANativeActivity_createFunc* createActivityFunc;
+ String8 internalDataPath;
+ String8 externalDataPath;
+
sp<ANativeWindow> nativeWindow;
jobject inputChannel;
struct MyInputQueue* nativeInputQueue;
@@ -176,6 +219,18 @@ struct NativeCode {
sp<PollLoop> pollLoop;
};
+void android_NativeActivity_setWindowFormat(
+ ANativeActivity* activity, int32_t format) {
+ NativeCode* code = static_cast<NativeCode*>(activity);
+ write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format);
+}
+
+void android_NativeActivity_setWindowFlags(
+ ANativeActivity* activity, int32_t values, int32_t mask) {
+ NativeCode* code = static_cast<NativeCode*>(activity);
+ write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask);
+}
+
// ------------------------------------------------------------------------
/*
@@ -183,19 +238,40 @@ struct NativeCode {
*/
static bool mainWorkCallback(int fd, int events, void* data) {
NativeCode* code = (NativeCode*)data;
- if ((events & POLLIN) != 0) {
- KeyEvent* keyEvent;
- while ((keyEvent=code->nativeInputQueue->getNextEvent()) != NULL) {
- jobject inputEventObj = android_view_KeyEvent_fromNative(
- code->activity.env, keyEvent);
- code->activity.env->CallVoidMethod(code->activity.clazz,
- gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
- int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal();
- if (res != OK) {
- LOGW("Failed to send finished signal on channel '%s'. status=%d",
- code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res);
+ if ((events & POLLIN) == 0) {
+ return true;
+ }
+
+ ActivityWork work;
+ if (!read_work(code->mainWorkRead, &work)) {
+ return true;
+ }
+ switch (work.cmd) {
+ case CMD_DEF_KEY: {
+ KeyEvent* keyEvent;
+ while ((keyEvent=code->nativeInputQueue->getNextEvent()) != NULL) {
+ jobject inputEventObj = android_view_KeyEvent_fromNative(
+ code->env, keyEvent);
+ code->env->CallVoidMethod(code->clazz,
+ gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
+ int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal();
+ if (res != OK) {
+ LOGW("Failed to send finished signal on channel '%s'. status=%d",
+ code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res);
+ }
}
- }
+ } break;
+ case CMD_SET_WINDOW_FORMAT: {
+ code->env->CallVoidMethod(code->clazz,
+ gNativeActivityClassInfo.setWindowFormat, work.arg1);
+ } break;
+ case CMD_SET_WINDOW_FLAGS: {
+ code->env->CallVoidMethod(code->clazz,
+ gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2);
+ } break;
+ default:
+ LOGW("Unknown work command: %d", work.cmd);
+ break;
}
return true;
@@ -204,7 +280,8 @@ static bool mainWorkCallback(int fd, int events, void* data) {
// ------------------------------------------------------------------------
static jint
-loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue)
+loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue,
+ jstring internalDataDir, jstring externalDataDir, int sdkVersion)
{
const char* pathStr = env->GetStringUTFChars(path, NULL);
NativeCode* code = NULL;
@@ -239,15 +316,28 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQ
code->mainWorkWrite = msgpipe[1];
code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code);
- code->activity.callbacks = &code->callbacks;
- if (env->GetJavaVM(&code->activity.vm) < 0) {
+ code->ANativeActivity::callbacks = &code->callbacks;
+ if (env->GetJavaVM(&code->vm) < 0) {
LOGW("NativeActivity GetJavaVM failed");
delete code;
return 0;
}
- code->activity.env = env;
- code->activity.clazz = env->NewGlobalRef(clazz);
- code->createActivityFunc(&code->activity, NULL, 0);
+ code->env = env;
+ code->clazz = env->NewGlobalRef(clazz);
+
+ const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
+ code->internalDataPath = dirStr;
+ code->internalDataPath = code->internalDataPath.string();
+ env->ReleaseStringUTFChars(path, dirStr);
+
+ dirStr = env->GetStringUTFChars(externalDataDir, NULL);
+ code->externalDataPath = dirStr;
+ code->externalDataPath = code->externalDataPath.string();
+ env->ReleaseStringUTFChars(path, dirStr);
+
+ code->sdkVersion = sdkVersion;
+
+ code->createActivityFunc(code, NULL, 0);
}
return (jint)code;
@@ -268,7 +358,7 @@ onStart_native(JNIEnv* env, jobject clazz, jint handle)
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onStart != NULL) {
- code->callbacks.onStart(&code->activity);
+ code->callbacks.onStart(code);
}
}
}
@@ -279,7 +369,7 @@ onResume_native(JNIEnv* env, jobject clazz, jint handle)
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onResume != NULL) {
- code->callbacks.onResume(&code->activity);
+ code->callbacks.onResume(code);
}
}
}
@@ -291,7 +381,7 @@ onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onSaveInstanceState != NULL) {
size_t len = 0;
- code->callbacks.onSaveInstanceState(&code->activity, &len);
+ code->callbacks.onSaveInstanceState(code, &len);
}
}
}
@@ -302,7 +392,7 @@ onPause_native(JNIEnv* env, jobject clazz, jint handle)
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onPause != NULL) {
- code->callbacks.onPause(&code->activity);
+ code->callbacks.onPause(code);
}
}
}
@@ -313,7 +403,7 @@ onStop_native(JNIEnv* env, jobject clazz, jint handle)
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onStop != NULL) {
- code->callbacks.onStop(&code->activity);
+ code->callbacks.onStop(code);
}
}
}
@@ -324,7 +414,7 @@ onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onLowMemory != NULL) {
- code->callbacks.onLowMemory(&code->activity);
+ code->callbacks.onLowMemory(code);
}
}
}
@@ -335,7 +425,7 @@ onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean fo
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onWindowFocusChanged != NULL) {
- code->callbacks.onWindowFocusChanged(&code->activity, focused ? 1 : 0);
+ code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0);
}
}
}
@@ -347,7 +437,7 @@ onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface
NativeCode* code = (NativeCode*)handle;
code->setSurface(surface);
if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
- code->callbacks.onNativeWindowCreated(&code->activity,
+ code->callbacks.onNativeWindowCreated(code,
code->nativeWindow.get());
}
}
@@ -363,11 +453,11 @@ onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface
code->setSurface(surface);
if (oldNativeWindow != code->nativeWindow) {
if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
- code->callbacks.onNativeWindowDestroyed(&code->activity,
+ code->callbacks.onNativeWindowDestroyed(code,
oldNativeWindow.get());
}
if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
- code->callbacks.onNativeWindowCreated(&code->activity,
+ code->callbacks.onNativeWindowCreated(code,
code->nativeWindow.get());
}
}
@@ -380,7 +470,7 @@ onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surfa
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
- code->callbacks.onNativeWindowDestroyed(&code->activity,
+ code->callbacks.onNativeWindowDestroyed(code,
code->nativeWindow.get());
}
code->setSurface(NULL);
@@ -399,7 +489,7 @@ onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject ch
return;
}
if (code->callbacks.onInputQueueCreated != NULL) {
- code->callbacks.onInputQueueCreated(&code->activity,
+ code->callbacks.onInputQueueCreated(code,
code->nativeInputQueue);
}
}
@@ -412,7 +502,7 @@ onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject
NativeCode* code = (NativeCode*)handle;
if (code->nativeInputQueue != NULL
&& code->callbacks.onInputQueueDestroyed != NULL) {
- code->callbacks.onInputQueueDestroyed(&code->activity,
+ code->callbacks.onInputQueueDestroyed(code,
code->nativeInputQueue);
}
code->setInputChannel(NULL);
@@ -420,7 +510,8 @@ onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject
}
static const JNINativeMethod g_methods[] = {
- { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;)I", (void*)loadNativeCode_native },
+ { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;I)I",
+ (void*)loadNativeCode_native },
{ "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
{ "onStartNative", "(I)V", (void*)onStart_native },
{ "onResumeNative", "(I)V", (void*)onResume_native },
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index 030d6c7..847b5a5 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -51,14 +51,18 @@ private:
// ----------------------------------------------------------------------------
NativeMessageQueue::NativeMessageQueue() {
- mPollLoop = new PollLoop();
+ mPollLoop = PollLoop::getForThread();
+ if (mPollLoop == NULL) {
+ mPollLoop = new PollLoop(false);
+ PollLoop::setForThread(mPollLoop);
+ }
}
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/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index a82abc9..7305032 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -33,7 +33,7 @@
#include "jni.h"
#include <android_runtime/AndroidRuntime.h>
-#include "android_view_Surface.h"
+#include <android_runtime/android_view_Surface.h>
#include <utils/misc.h>
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 866c038..941ed63 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -16,6 +16,7 @@
*/
#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_view_Surface.h>
#include <utils/misc.h>
#include <EGL/egl.h>
@@ -25,8 +26,6 @@
#include <SkBitmap.h>
#include <SkPixelRef.h>
-#include "android_view_Surface.h"
-
namespace android {
static jclass gDisplay_class;
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png
deleted file mode 100644
index 2d13237..0000000
--- a/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png
deleted file mode 100644
index 950713b..0000000
--- a/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png
deleted file mode 100644
index 2d13237..0000000
--- a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png
deleted file mode 100644
index 7abfd19..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png
deleted file mode 100644
index c44d062..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png
deleted file mode 100644
index 7abfd19..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 27cb763..bd65fee 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -869,8 +869,11 @@
<public type="drawable" name="stat_sys_download" id="0x01080081" />
<public type="drawable" name="stat_sys_download_done" id="0x01080082" />
<public type="drawable" name="stat_sys_headset" id="0x01080083" />
+ <!-- @deprecated Replaced by a private asset in the phone app. -->
<public type="drawable" name="stat_sys_phone_call" id="0x01080084" />
+ <!-- @deprecated Replaced by a private asset in the phone app. -->
<public type="drawable" name="stat_sys_phone_call_forward" id="0x01080085" />
+ <!-- @deprecated Replaced by a private asset in the phone app. -->
<public type="drawable" name="stat_sys_phone_call_on_hold" id="0x01080086" />
<public type="drawable" name="stat_sys_speakerphone" id="0x01080087" />
<public type="drawable" name="stat_sys_upload" id="0x01080088" />
@@ -1132,7 +1135,9 @@
<public type="style" name="Widget.ProgressBar.Large.Inverse" id="0x0103005c" />
<public type="style" name="Widget.ProgressBar.Small.Inverse" id="0x0103005d" />
+ <!-- @deprecated Replaced by a private asset in the phone app. -->
<public type="drawable" name="stat_sys_vp_phone_call" id="0x010800a7" />
+ <!-- @deprecated Replaced by a private asset in the phone app. -->
<public type="drawable" name="stat_sys_vp_phone_call_on_hold" id="0x010800a8" />
<public type="anim" name="anticipate_interpolator" id="0x010a0007" />
@@ -1246,6 +1251,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/images/axis_device.png b/docs/html/images/axis_device.png
new file mode 100644
index 0000000..f1f666a
--- /dev/null
+++ b/docs/html/images/axis_device.png
Binary files differ
diff --git a/docs/html/images/axis_globe.png b/docs/html/images/axis_globe.png
new file mode 100644
index 0000000..dccb58b
--- /dev/null
+++ b/docs/html/images/axis_globe.png
Binary files differ
diff --git a/docs/html/sdk/android-1.5.jd b/docs/html/sdk/android-1.5.jd
index 1d6e0ad..0c16b60 100644
--- a/docs/html/sdk/android-1.5.jd
+++ b/docs/html/sdk/android-1.5.jd
@@ -139,7 +139,7 @@ function toggleDiv(link) {
<div class="toggleable closed">
<a href="#" onclick="return toggleDiv(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android 1.5, Revision 3</a> <em>(July 2009)</em></a>
<div class="toggleme">
<dl>
diff --git a/docs/html/sdk/android-1.6.jd b/docs/html/sdk/android-1.6.jd
index c2651b6..c4e08ff 100644
--- a/docs/html/sdk/android-1.6.jd
+++ b/docs/html/sdk/android-1.6.jd
@@ -138,7 +138,7 @@ function toggleDiv(link) {
<div class="toggleable closed">
<a href="#" onclick="return toggleDiv(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android 1.6, Revision 2</a> <em>(December 2009)</em></a>
<div class="toggleme">
<dl>
diff --git a/docs/html/sdk/android-2.1.jd b/docs/html/sdk/android-2.1.jd
index 7490bae..cd48a72 100644
--- a/docs/html/sdk/android-2.1.jd
+++ b/docs/html/sdk/android-2.1.jd
@@ -139,7 +139,7 @@ function toggleDiv(link) {
<div class="toggleable closed">
<a href="#" onclick="return toggleDiv(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android 2.1, Revision 1</a> <em>(January 2010)</em></a>
<div class="toggleme">
<dl>
diff --git a/docs/html/sdk/android-2.2.jd b/docs/html/sdk/android-2.2.jd
index f82edf9..495fd80 100644
--- a/docs/html/sdk/android-2.2.jd
+++ b/docs/html/sdk/android-2.2.jd
@@ -2,7 +2,6 @@ page.title=Android 2.2 Platform
sdk.platform.version=2.2
sdk.platform.apiLevel=8
sdk.platform.majorMinor=minor
-sdk.platform.deployableDate=May 2010
@jd:body
@@ -118,6 +117,30 @@ function toggleDiv(link) {
<div class="toggleable opened">
<a href="#" onclick="return toggleDiv(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ Android {@sdkPlatformVersion}, Revision 2</a> <em>(July 2010)</em></a>
+ <div class="toggleme">
+<dl>
+<dt>Dependencies:</dt>
+<dd>
+<p>Requires SDK Tools r6 or higher.</p>
+</dd>
+
+<dt>System Image:</dt>
+<dd>
+<ul>
+<li>Adds default Search Widget.</li>
+<li>Includes proper provisioning for the platform's Backup Manager. For more information about how to use the Backup Manager, see <a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a>.</li>
+<li>Updates the Android 2.2 system image to FRF91.</li>
+</ul>
+</dd>
+
+</dl>
+ </div>
+</div>
+
+<div class="toggleable closed">
+ <a href="#" onclick="return toggleDiv(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android {@sdkPlatformVersion}, Revision 1</a> <em>(May 2010)</em></a>
<div class="toggleme">
<dl>
@@ -135,7 +158,6 @@ function toggleDiv(link) {
</div>
</div>
-
<h2 id="api-level">API Level</h2>
<p>The Android {@sdkPlatformVersion} platform delivers an updated version of
@@ -444,4 +466,4 @@ emulator skins are:</p>
<p>For more information about how to develop an application that displays
and functions properly on all Android-powered devices, see <a
href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
-Screens</a>.</p> \ No newline at end of file
+Screens</a>.</p>
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..bd7eeed 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
@@ -295,7 +298,7 @@ location:
<p>Additionally, before you can configure or use ADT, you must install the
Android SDK starter package, as described in <a
-href="installing.html#Installing">Downloading the SDK Starter Pacskage</a>.
+href="installing.html#Installing">Downloading the SDK Starter Package</a>.
Specifically, you need to install a compatible version of the Android SDK Tools
and at least one development platform. To simplify ADT setup, we recommend
installing the Android SDK prior to installing ADT. </p>
diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd
index 69cc73d..9e88d94 100644
--- a/docs/html/sdk/ndk/index.jd
+++ b/docs/html/sdk/ndk/index.jd
@@ -1,16 +1,16 @@
ndk=true
-ndk.win_download=android-ndk-r4-windows.zip
-ndk.win_bytes=45778965
-ndk.win_checksum=1eded98a7f5cd5e71f8ac74565f73f11
+ndk.win_download=android-ndk-r4b-windows.zip
+ndk.win_bytes=45792835
+ndk.win_checksum=e397145e155a639be53ee4b6db8ad511
-ndk.mac_download=android-ndk-r4-darwin-x86.zip
-ndk.mac_bytes=50572163
-ndk.mac_checksum=b7d5f149fecf951c05a79b045f00419f
+ndk.mac_download=android-ndk-r4b-darwin-x86.zip
+ndk.mac_bytes=50586041
+ndk.mac_checksum=41dbd54335fb828ee408eab17103a1b0
-ndk.linux_download=android-ndk-r4-linux-x86.zip
-ndk.linux_bytes=49450682
-ndk.linux_checksum=0892b0637d45d145e045cc68e163dee3
+ndk.linux_download=android-ndk-r4b-linux-x86.zip
+ndk.linux_bytes=49464776
+ndk.linux_checksum=2deabcb125c219b34140975b710f00ec
page.title=Android NDK
@jd:body
@@ -62,8 +62,15 @@ padding: .25em 1em;
<div class="toggleable open">
<a href="#" onclick="return toggleDiv(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
-Android NDK, Revision 4</a> <em>(May 2010)</em>
+Android NDK, Revision 4b</a> <em>(June 2010)</em>
<div class="toggleme">
+<dl>
+<dt>NDK r4b notes:</dt>
+<dd><p>Includes fixes for several issues in the NDK build and debugging scripts
+&mdash; if you are using NDK r4, we recommend downloading the NDK r4b build. For
+detailed information the changes in this release, read the CHANGES.TXT document
+included in the downloaded NDK package.</p></dd>
+</dl>
<dl>
<dt>General notes:</dt>
@@ -114,7 +121,7 @@ code.</li>
<div class="toggleable closed">
<a href="#" onclick="return toggleDiv(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android NDK, Revision 3</a> <em>(March 2010)</em>
<div class="toggleme">
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/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index a80981c..404e938 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -75,7 +75,8 @@
</li>
</ul>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r6</a> <span class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r6</a>
+ </li>
<li><a href="<?cs var:toroot ?>sdk/win-usb.html">USB Driver for
Windows, r3</a>
</li>
@@ -101,7 +102,6 @@
<span style="display:none" class="ja"></span>
<span style="display:none" class="zh-CN"></span>
<span style="display:none" class="zh-TW"></span></a>
- <span class="new">new!</span>
</li>
</ul>
</li>
@@ -116,7 +116,7 @@
<span style="display:none" class="zh-TW"></span>
</h2>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r4</a>
+ <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r4b</a>
<span class="new">new!</span></li>
</ul>
</li>
diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h
new file mode 100644
index 0000000..f808328
--- /dev/null
+++ b/include/android_runtime/android_app_NativeActivity.h
@@ -0,0 +1,35 @@
+/*
+ * 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_APP_NATIVEACTIVITY_H
+#define _ANDROID_APP_NATIVEACTIVITY_H
+
+#include <android/native_activity.h>
+
+#include "jni.h"
+
+namespace android {
+
+extern void android_NativeActivity_setWindowFormat(
+ ANativeActivity* activity, int32_t format);
+
+extern void android_NativeActivity_setWindowFlags(
+ ANativeActivity* activity, int32_t values, int32_t mask);
+
+
+} // namespace android
+
+#endif // _ANDROID_APP_NATIVEACTIVITY_H
diff --git a/core/jni/android_view_Surface.h b/include/android_runtime/android_view_Surface.h
index c37932e..c37932e 100644
--- a/core/jni/android_view_Surface.h
+++ b/include/android_runtime/android_view_Surface.h
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index 66670f3..e9ff8a3 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -307,29 +307,18 @@ public:
int32_t priority() const { return mPriority; }
- /* Enables the effect engine.
+ /* Enables or disables the effect engine.
*
* Parameters:
- * None.
+ * enabled: requested enable state.
*
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful operation
- * - INVALID_OPERATION: the application does not have control of the effect engine
+ * - INVALID_OPERATION: the application does not have control of the effect engine or the
+ * effect is already in the requested state.
*/
- status_t enable();
-
- /* Disables the effect engine.
- *
- * Parameters:
- * None.
- *
- * Returned status (from utils/Errors.h) can be:
- * - NO_ERROR: successful operation
- * - INVALID_OPERATION: the application does not have control of the effect engine
- */
- status_t disable();
-
- bool isEnabled() const;
+ virtual status_t setEnabled(bool enabled);
+ bool getEnabled() const;
/* Sets a parameter value.
*
@@ -342,7 +331,7 @@ public:
* - BAD_VALUE: invalid parameter identifier or value.
* - DEAD_OBJECT: the effect engine has been deleted.
*/
- status_t setParameter(effect_param_t *param);
+ virtual status_t setParameter(effect_param_t *param);
/* Prepare a new parameter value that will be set by next call to
* setParameterCommit(). This method can be used to set multiple parameters
@@ -359,7 +348,7 @@ public:
* - NO_MEMORY: no more space available in shared memory used for deferred parameter
* setting.
*/
- status_t setParameterDeferred(effect_param_t *param);
+ virtual status_t setParameterDeferred(effect_param_t *param);
/* Commit all parameter values previously prepared by setParameterDeferred().
*
@@ -373,7 +362,7 @@ public:
* as to which of the parameters caused this error.
* - DEAD_OBJECT: the effect engine has been deleted.
*/
- status_t setParameterCommit();
+ virtual status_t setParameterCommit();
/* Gets a parameter value.
*
@@ -387,13 +376,17 @@ public:
* - BAD_VALUE: invalid parameter identifier.
* - DEAD_OBJECT: the effect engine has been deleted.
*/
- status_t getParameter(effect_param_t *param);
+ virtual status_t getParameter(effect_param_t *param);
/* Sends a command and receives a response to/from effect engine.
* See EffectApi.h for details on effect command() function, valid command codes
* and formats.
*/
- status_t command(int32_t cmdCode, int32_t cmdSize, void *cmdData, int32_t *replySize, void *replyData);
+ virtual status_t command(int32_t cmdCode,
+ int32_t cmdSize,
+ void *cmdData,
+ int32_t *replySize,
+ void *replyData);
/*
@@ -409,6 +402,17 @@ public:
*/
static status_t guidToString(const effect_uuid_t *guid, char *str, size_t maxLen);
+protected:
+ volatile int32_t mEnabled; // enable state
+ int32_t mSessionId; // audio session ID
+ int32_t mPriority; // priority for effect control
+ status_t mStatus; // effect status
+ effect_callback_t mCbf; // callback function for status, control and
+ // parameter changes notifications
+ void* mUserData; // client context for callback function
+ effect_descriptor_t mDescriptor; // effect descriptor
+ int32_t mId; // system wide unique effect engine instance ID
+
private:
// Implements the IEffectClient interface
@@ -419,9 +423,17 @@ private:
EffectClient(AudioEffect *effect) : mEffect(effect){}
// IEffectClient
- virtual void controlStatusChanged(bool controlGranted) {mEffect->controlStatusChanged(controlGranted);}
- virtual void enableStatusChanged(bool enabled) {mEffect->enableStatusChanged(enabled);}
- virtual void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData) {
+ virtual void controlStatusChanged(bool controlGranted) {
+ mEffect->controlStatusChanged(controlGranted);
+ }
+ virtual void enableStatusChanged(bool enabled) {
+ mEffect->enableStatusChanged(enabled);
+ }
+ virtual void commandExecuted(int cmdCode,
+ int cmdSize,
+ void *pCmdData,
+ int replySize,
+ void *pReplyData) {
mEffect->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
}
@@ -446,14 +458,6 @@ private:
sp<EffectClient> mIEffectClient; // IEffectClient implementation
sp<IMemory> mCblkMemory; // shared memory for deferred parameter setting
effect_param_cblk_t* mCblk; // control block for deferred parameter setting
- int32_t mPriority; // priority for effect control
- status_t mStatus; // effect status
- volatile int32_t mEnabled; // enable state
- effect_callback_t mCbf; // callback function for status, control, parameter changes notifications
- void* mUserData; // client context for callback function
- effect_descriptor_t mDescriptor; // effect descriptor
- int32_t mId; // system wide unique effect engine instance identifier
- int32_t mSessionId; // audio session ID
};
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/EffectVisualizerApi.h b/include/media/EffectVisualizerApi.h
new file mode 100644
index 0000000..1155db8
--- /dev/null
+++ b/include/media/EffectVisualizerApi.h
@@ -0,0 +1,55 @@
+/*
+ * 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_EFFECTVISUALIZERAPI_H_
+#define ANDROID_EFFECTVISUALIZERAPI_H_
+
+#include <media/EffectApi.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+//TODO replace by openSL ES include when available
+static const effect_uuid_t SL_IID_VISUALIZATION_ =
+ { 0xe46b26a0, 0xdddd, 0x11db, 0x8afd, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
+const effect_uuid_t * const SL_IID_VISUALIZATION = &SL_IID_VISUALIZATION_;
+
+#define VISUALIZER_CAPTURE_SIZE_MAX 1024 // maximum capture size in samples
+#define VISUALIZER_CAPTURE_SIZE_MIN 128 // minimum capture size in samples
+
+/* enumerated parameters for Visualizer effect */
+typedef enum
+{
+ VISU_PARAM_CAPTURE_SIZE, // Sets the number PCM samples in the capture.
+} t_visualizer_params;
+
+/* commands */
+typedef enum
+{
+ VISU_CMD_CAPTURE = EFFECT_CMD_FIRST_PROPRIETARY, // Gets the latest PCM capture.
+}t_visualizer_cmds;
+
+// VISU_CMD_CAPTURE retrieves the latest PCM snapshot captured by the visualizer engine.
+// It returns the number of samples specified by VISU_PARAM_CAPTURE_SIZE
+// in 8 bit unsigned format (0 = 0x80)
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+
+#endif /*ANDROID_EFFECTVISUALIZERAPI_H_*/
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index e892875..9416ca1 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -48,10 +48,6 @@ public:
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
virtual sp<IOMX> getOMX() = 0;
-
- // Take a peek at currently playing audio, for visualization purposes.
- // This returns a buffer of 16 bit mono PCM data, or NULL if no visualization buffer is currently available.
- virtual sp<IMemory> snoop() = 0;
};
// ----------------------------------------------------------------------------
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/media/Visualizer.h b/include/media/Visualizer.h
new file mode 100644
index 0000000..5d51de8
--- /dev/null
+++ b/include/media/Visualizer.h
@@ -0,0 +1,160 @@
+/*
+ * 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_MEDIA_VISUALIZER_H
+#define ANDROID_MEDIA_VISUALIZER_H
+
+#include <media/AudioEffect.h>
+#include <media/EffectVisualizerApi.h>
+#include <string.h>
+
+/**
+ * The Visualizer class enables application to retrieve part of the currently playing audio for
+ * visualization purpose. It is not an audio recording interface and only returns partial and low
+ * quality audio content. However, to protect privacy of certain audio data (e.g voice mail) the use
+ * of the visualizer requires the permission android.permission.RECORD_AUDIO.
+ * The audio session ID passed to the constructor indicates which audio content should be
+ * visualized:
+ * - If the session is 0, the audio output mix is visualized
+ * - If the session is not 0, the audio from a particular MediaPlayer or AudioTrack
+ * using this audio session is visualized
+ * Two types of representation of audio content can be captured:
+ * - Waveform data: consecutive 8-bit (unsigned) mono samples by using the getWaveForm() method
+ * - Frequency data: 8-bit magnitude FFT by using the getFft() method
+ *
+ * The length of the capture can be retrieved or specified by calling respectively
+ * getCaptureSize() and setCaptureSize() methods. Note that the size of the FFT
+ * is half of the specified capture size but both sides of the spectrum are returned yielding in a
+ * number of bytes equal to the capture size. The capture size must be a power of 2 in the range
+ * returned by getMinCaptureSize() and getMaxCaptureSize().
+ * In addition to the polling capture mode, a callback mode is also available by installing a
+ * callback function by use of the setCaptureCallBack() method. The rate at which the callback
+ * is called as well as the type of data returned is specified.
+ * Before capturing data, the Visualizer must be enabled by calling the setEnabled() method.
+ * When data capture is not needed any more, the Visualizer should be disabled.
+ */
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class Visualizer: public AudioEffect {
+public:
+
+ enum callback_flags {
+ CAPTURE_WAVEFORM = 0x00000001, // capture callback returns a PCM wave form
+ CAPTURE_FFT = 0x00000002, // apture callback returns a frequency representation
+ CAPTURE_CALL_JAVA = 0x00000004 // the callback thread can call java
+ };
+
+
+ /* Constructor.
+ * See AudioEffect constructor for details on parameters.
+ */
+ Visualizer(int32_t priority = 0,
+ effect_callback_t cbf = 0,
+ void* user = 0,
+ int sessionId = 0);
+
+ ~Visualizer();
+
+ virtual status_t setEnabled(bool enabled);
+
+ // maximum capture size in samples
+ static uint32_t getMaxCaptureSize() { return VISUALIZER_CAPTURE_SIZE_MAX; }
+ // minimum capture size in samples
+ static uint32_t getMinCaptureSize() { return VISUALIZER_CAPTURE_SIZE_MIN; }
+ // maximum capture rate in millihertz
+ static uint32_t getMaxCaptureRate() { return CAPTURE_RATE_MAX; }
+
+ // callback used to return periodic PCM or FFT captures to the application. Either one or both
+ // types of data are returned (PCM and FFT) according to flags indicated when installing the
+ // callback. When a type of data is not present, the corresponding size (waveformSize or
+ // fftSize) is 0.
+ typedef void (*capture_cbk_t)(void* user,
+ uint32_t waveformSize,
+ uint8_t *waveform,
+ uint32_t fftSize,
+ uint8_t *fft,
+ uint32_t samplingrate);
+
+ // install a callback to receive periodic captures. The capture rate is specified in milliHertz
+ // and the capture format is according to flags (see callback_flags).
+ status_t setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate);
+
+ // set the capture size capture size must be a power of two in the range
+ // [VISUALIZER_CAPTURE_SIZE_MAX. VISUALIZER_CAPTURE_SIZE_MIN]
+ // must be called when the visualizer is not enabled
+ status_t setCaptureSize(uint32_t size);
+ uint32_t getCaptureSize() { return mCaptureSize; }
+
+ // returns the capture rate indicated when installing the callback
+ uint32_t getCaptureRate() { return mCaptureRate; }
+
+ // returns the sampling rate of the audio being captured
+ uint32_t getSamplingRate() { return mSampleRate; }
+
+ // return a capture in PCM 8 bit unsigned format. The size of the capture is equal to
+ // getCaptureSize()
+ status_t getWaveForm(uint8_t *waveform);
+
+ // return a capture in FFT 8 bit signed format. The size of the capture is equal to
+ // getCaptureSize() but the length of the FFT is half of the size (both parts of the spectrum
+ // are returned
+ status_t getFft(uint8_t *fft);
+
+private:
+
+ static const uint32_t CAPTURE_RATE_MAX = 20000;
+ static const uint32_t CAPTURE_RATE_DEF = 10000;
+ static const uint32_t CAPTURE_SIZE_DEF = VISUALIZER_CAPTURE_SIZE_MAX;
+
+ /* internal class to handle the callback */
+ class CaptureThread : public Thread
+ {
+ public:
+ CaptureThread(Visualizer& receiver, uint32_t captureRate, bool bCanCallJava = false);
+
+ private:
+ friend class Visualizer;
+ virtual bool threadLoop();
+ virtual status_t readyToRun();
+ virtual void onFirstRef();
+ Visualizer& mReceiver;
+ Mutex mLock;
+ uint32_t mSleepTimeUs;
+ };
+
+ status_t doFft(uint8_t *fft, uint8_t *waveform);
+ void periodicCapture();
+ uint32_t initCaptureSize();
+
+ Mutex mLock;
+ uint32_t mCaptureRate;
+ uint32_t mCaptureSize;
+ uint32_t mSampleRate;
+ capture_cbk_t mCaptureCallBack;
+ void *mCaptureCbkUser;
+ sp<CaptureThread> mCaptureThread;
+ uint32_t mCaptureFlags;
+ void *mFftTable;
+};
+
+
+}; // namespace android
+
+#endif // ANDROID_MEDIA_VISUALIZER_H
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 62a4e50..4963f73 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -166,7 +166,6 @@ public:
void notify(int msg, int ext1, int ext2);
static sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
- static int snoop(short *data, int len, int kind);
status_t invoke(const Parcel& request, Parcel *reply);
status_t setMetadataFilter(const Parcel& filter);
status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata);
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 95fe6f6..73f5547 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -69,6 +69,10 @@ enum {
kKeyDate = 'date', // cstring
kKeyWriter = 'writ', // cstring
+ // video profile and level
+ kKeyVideoProfile = 'vprf', // int32_t
+ kKeyVideoLevel = 'vlev', // int32_t
+
// Set this key to enable authoring files in 64-bit offset
kKey64BitFileOffset = 'fobt', // int32_t (bool)
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 99ec8e6..214f43a 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -27,6 +27,7 @@ namespace android {
class MemoryDealer;
struct OMXCodecObserver;
+struct CodecProfileLevel;
struct OMXCodec : public MediaSource,
public MediaBufferObserver {
@@ -178,6 +179,12 @@ private:
status_t setupMPEG4EncoderParameters(const sp<MetaData>& meta);
status_t setupAVCEncoderParameters(const sp<MetaData>& meta);
+ // If profile/level is set in the meta data, its value in the meta
+ // data will be used; otherwise, the default value will be used.
+ status_t getVideoProfileLevel(const sp<MetaData>& meta,
+ const CodecProfileLevel& defaultProfileLevel,
+ CodecProfileLevel& profileLevel);
+
status_t setVideoOutputFormat(
const char *mime, OMX_U32 width, OMX_U32 height);
diff --git a/include/media/stagefright/foundation/AHandler.h b/include/media/stagefright/foundation/AHandler.h
index 9fccead..b008b54 100644
--- a/include/media/stagefright/foundation/AHandler.h
+++ b/include/media/stagefright/foundation/AHandler.h
@@ -34,6 +34,8 @@ struct AHandler : public RefBase {
return mID;
}
+ sp<ALooper> looper();
+
protected:
virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
diff --git a/include/media/stagefright/foundation/ALooperRoster.h b/include/media/stagefright/foundation/ALooperRoster.h
index 1c6869c..c1bd4ed 100644
--- a/include/media/stagefright/foundation/ALooperRoster.h
+++ b/include/media/stagefright/foundation/ALooperRoster.h
@@ -34,10 +34,12 @@ struct ALooperRoster {
void postMessage(const sp<AMessage> &msg, int64_t delayUs = 0);
void deliverMessage(const sp<AMessage> &msg);
+ sp<ALooper> findLooper(ALooper::handler_id handlerID);
+
private:
struct HandlerInfo {
- sp<ALooper> mLooper;
- sp<AHandler> mHandler;
+ wp<ALooper> mLooper;
+ wp<AHandler> mHandler;
};
Mutex mLock;
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/ui/InputTransport.h b/include/ui/InputTransport.h
index 2dfe2a8..11714d5 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -33,6 +33,7 @@
#include <semaphore.h>
#include <ui/Input.h>
#include <utils/Errors.h>
+#include <utils/PollLoop.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
@@ -345,11 +346,15 @@ public:
android::status_t consume(android::InputEvent** event);
+ void setPollLoop(const android::sp<android::PollLoop>& pollLoop) { mPollLoop = pollLoop; }
+ const android::sp<android::PollLoop> getPollLoop() const { return mPollLoop; }
+
virtual void doDefaultKey(android::KeyEvent* keyEvent) = 0;
private:
android::InputConsumer mConsumer;
android::PreallocatedInputEventFactory mInputEventFactory;
+ android::sp<android::PollLoop> mPollLoop;
};
#endif // _UI_INPUT_TRANSPORT_H
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index a213c09..4e65a2d 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -20,31 +20,28 @@
#include <utils/TypeHelpers.h>
#include <ui/Point.h>
+#include <android/rect.h>
+
namespace android {
-class Rect
+class Rect : public ARect
{
public:
- int left;
- int top;
- int right;
- int bottom;
-
- typedef int value_type;
+ typedef int32_t value_type;
// we don't provide copy-ctor and operator= on purpose
// because we want the compiler generated versions
inline Rect() {
}
- inline Rect(int w, int h)
- : left(0), top(0), right(w), bottom(h) {
+ inline Rect(int32_t w, int32_t h) {
+ left = top = 0; right = w; bottom = h;
}
- inline Rect(int l, int t, int r, int b)
- : left(l), top(t), right(r), bottom(b) {
+ inline Rect(int32_t l, int32_t t, int32_t r, int32_t b) {
+ left = l; top = t; right = r; bottom = b;
}
- inline Rect(const Point& lt, const Point& rb)
- : left(lt.x), top(lt.y), right(rb.x), bottom(rb.y) {
+ inline Rect(const Point& lt, const Point& rb) {
+ left = lt.x; top = lt.y; right = rb.x; bottom = rb.y;
}
void makeInvalid();
@@ -68,12 +65,12 @@ public:
}
// rectangle's width
- inline int width() const {
+ inline int32_t width() const {
return right-left;
}
// rectangle's height
- inline int height() const {
+ inline int32_t height() const {
return bottom-top;
}
@@ -136,12 +133,12 @@ public:
const Rect operator + (const Point& rhs) const;
const Rect operator - (const Point& rhs) const;
- void translate(int dx, int dy) { // legacy, don't use.
+ void translate(int32_t dx, int32_t dy) { // legacy, don't use.
offsetBy(dx, dy);
}
- Rect& offsetTo(int x, int y);
- Rect& offsetBy(int x, int y);
+ Rect& offsetTo(int32_t x, int32_t y);
+ Rect& offsetBy(int32_t x, int32_t y);
bool intersect(const Rect& with, Rect* result) const;
};
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 a95fb17..81230e8 100644
--- a/include/utils/PollLoop.h
+++ b/include/utils/PollLoop.h
@@ -22,17 +22,27 @@
#include <sys/poll.h>
+#include <android/looper.h>
+
+struct ALooper : public android::RefBase {
+protected:
+ virtual ~ALooper() { }
+
+public:
+ ALooper() { }
+};
+
namespace android {
/**
* A basic file descriptor polling loop based on poll() with callbacks.
*/
-class PollLoop : public RefBase {
+class PollLoop : public ALooper {
protected:
virtual ~PollLoop();
public:
- PollLoop();
+ PollLoop(bool allowNonCallbacks);
/**
* A callback that it to be invoked when an event occurs on a file descriptor.
@@ -44,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.
@@ -51,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.
+ *
+ * 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 main thread.
+ * 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.
@@ -71,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.
*
@@ -83,6 +114,12 @@ public:
void setCallback(int fd, int events, Callback callback, void* data = NULL);
/**
+ * Like setCallback(), but for the NDK callback function.
+ */
+ void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback,
+ void* data);
+
+ /**
* Removes the callback for a file descriptor, if one exists.
*
* When this method returns, it is safe to close the file descriptor since the poll loop
@@ -100,9 +137,22 @@ public:
*/
bool removeCallback(int fd);
+ /**
+ * Set the given PollLoop to be associated with the
+ * calling thread. There must be a 1:1 relationship between
+ * PollLoop and thread.
+ */
+ static void setForThread(const sp<PollLoop>& pollLoop);
+
+ /**
+ * Return the PollLoop associated with the calling thread.
+ */
+ static sp<PollLoop> getForThread();
+
private:
struct RequestedCallback {
Callback callback;
+ ALooper_callbackFunc* looperCallback;
void* data;
};
@@ -110,9 +160,12 @@ private:
int fd;
int events;
Callback callback;
+ ALooper_callbackFunc* looperCallback;
void* data;
};
-
+
+ const bool mAllowNonCallbacks;
+
Mutex mLock;
bool mPolling;
uint32_t mWaiters;
@@ -126,12 +179,17 @@ 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();
+ void setCallbackCommon(int fd, int events, Callback callback,
+ ALooper_callbackFunc* looperCallback, void* data);
ssize_t getRequestIndexLocked(int fd);
void wakeAndLock();
+ static void threadDestructor(void *st);
};
} // namespace android
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 48c04a6..e6f46ce 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -17,7 +17,8 @@
#define LOG_TAG "AudioFlinger"
-//#define LOG_NDEBUG 0
+//
+#define LOG_NDEBUG 0
#include <math.h>
#include <signal.h>
@@ -52,6 +53,7 @@
#endif
#include <media/EffectsFactoryApi.h>
+#include <media/EffectVisualizerApi.h>
// ----------------------------------------------------------------------------
// the sim build doesn't have gettid
@@ -4498,6 +4500,11 @@ status_t AudioFlinger::getEffectDescriptor(effect_uuid_t *pUuid, effect_descript
return EffectGetDescriptor(pUuid, descriptor);
}
+
+// this UUID must match the one defined in media/libeffects/EffectVisualizer.cpp
+static const effect_uuid_t VISUALIZATION_UUID_ =
+ {0xd069d9e0, 0x8329, 0x11df, 0x9168, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}};
+
sp<IEffect> AudioFlinger::createEffect(pid_t pid,
effect_descriptor_t *pDesc,
const sp<IEffectClient>& effectClient,
@@ -4525,6 +4532,15 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
{
Mutex::Autolock _l(mLock);
+ // check recording permission for visualizer
+ if (memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 ||
+ memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) {
+ if (!recordingAllowed()) {
+ lStatus = PERMISSION_DENIED;
+ goto Exit;
+ }
+ }
+
if (!EffectIsNullUuid(&pDesc->uuid)) {
// if uuid is specified, request effect descriptor
lStatus = EffectGetDescriptor(&pDesc->uuid, &desc);
@@ -5089,7 +5105,7 @@ void AudioFlinger::EffectModule::process()
if (mState != ACTIVE) {
switch (mState) {
case RESET:
- reset();
+ reset_l();
mState = STARTING;
// clear auxiliary effect input buffer for next accumulation
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
@@ -5097,14 +5113,14 @@ void AudioFlinger::EffectModule::process()
}
return;
case STARTING:
- start();
+ start_l();
mState = ACTIVE;
break;
case STOPPING:
mState = STOPPED;
break;
case STOPPED:
- stop();
+ stop_l();
mState = IDLE;
return;
}
@@ -5132,7 +5148,7 @@ void AudioFlinger::EffectModule::process()
}
}
-void AudioFlinger::EffectModule::reset()
+void AudioFlinger::EffectModule::reset_l()
{
if (mEffectInterface == NULL) {
return;
@@ -5205,6 +5221,7 @@ status_t AudioFlinger::EffectModule::configure()
status_t AudioFlinger::EffectModule::init()
{
+ Mutex::Autolock _l(mLock);
if (mEffectInterface == NULL) {
return NO_INIT;
}
@@ -5217,7 +5234,7 @@ status_t AudioFlinger::EffectModule::init()
return status;
}
-status_t AudioFlinger::EffectModule::start()
+status_t AudioFlinger::EffectModule::start_l()
{
if (mEffectInterface == NULL) {
return NO_INIT;
@@ -5231,7 +5248,7 @@ status_t AudioFlinger::EffectModule::start()
return status;
}
-status_t AudioFlinger::EffectModule::stop()
+status_t AudioFlinger::EffectModule::stop_l()
{
if (mEffectInterface == NULL) {
return NO_INIT;
@@ -5247,7 +5264,8 @@ status_t AudioFlinger::EffectModule::stop()
status_t AudioFlinger::EffectModule::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData)
{
- LOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface);
+ Mutex::Autolock _l(mLock);
+// LOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface);
if (mEffectInterface == NULL) {
return NO_INIT;
@@ -5255,7 +5273,6 @@ status_t AudioFlinger::EffectModule::command(int cmdCode, int cmdSize, void *pCm
status_t status = (*mEffectInterface)->command(mEffectInterface, cmdCode, cmdSize, pCmdData, replySize, pReplyData);
if (cmdCode != EFFECT_CMD_GET_PARAM && status == NO_ERROR) {
int size = (replySize == NULL) ? 0 : *replySize;
- Mutex::Autolock _l(mLock);
for (size_t i = 1; i < mHandles.size(); i++) {
sp<EffectHandle> h = mHandles[i].promote();
if (h != 0) {
@@ -5322,6 +5339,7 @@ bool AudioFlinger::EffectModule::isEnabled()
status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller)
{
+ Mutex::Autolock _l(mLock);
status_t status = NO_ERROR;
// Send volume indication if EFFECT_FLAG_VOLUME_IND is set and read back altered volume
@@ -5347,6 +5365,7 @@ status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right,
status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
{
+ Mutex::Autolock _l(mLock);
status_t status = NO_ERROR;
if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) {
// convert device bit field from AudioSystem to EffectApi format.
@@ -5366,6 +5385,7 @@ status_t AudioFlinger::EffectModule::setDevice(uint32_t device)
status_t AudioFlinger::EffectModule::setMode(uint32_t mode)
{
+ Mutex::Autolock _l(mLock);
status_t status = NO_ERROR;
if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) {
// convert audio mode from AudioSystem to EffectApi format.
@@ -5586,7 +5606,7 @@ void AudioFlinger::EffectHandle::disconnect()
status_t AudioFlinger::EffectHandle::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData)
{
- LOGV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p", cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get());
+// LOGV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p", cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get());
// only get parameter command is permitted for applications not controlling the effect
if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) {
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index 42dca4c..ec3d7f1 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -916,7 +916,7 @@ private:
void process();
status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData);
- void reset();
+ void reset_l();
status_t configure();
status_t init();
uint32_t state() {
@@ -951,8 +951,8 @@ private:
EffectModule(const EffectModule&);
EffectModule& operator = (const EffectModule&);
- status_t start();
- status_t stop();
+ status_t start_l();
+ status_t stop_l();
// update this table when AudioSystem::audio_devices or audio_device_e (in EffectApi.h) are modified
static const uint32_t sDeviceConvTable[];
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/ui/Rect.cpp b/libs/ui/Rect.cpp
index 66b9576..5694e00 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -18,11 +18,11 @@
namespace android {
-static inline int min(int a, int b) {
+static inline int32_t min(int32_t a, int32_t b) {
return (a<b) ? a : b;
}
-static inline int max(int a, int b) {
+static inline int32_t max(int32_t a, int32_t b) {
return (a>b) ? a : b;
}
@@ -53,7 +53,7 @@ bool Rect::operator < (const Rect& rhs) const
return false;
}
-Rect& Rect::offsetTo(int x, int y)
+Rect& Rect::offsetTo(int32_t x, int32_t y)
{
right -= left - x;
bottom -= top - y;
@@ -62,7 +62,7 @@ Rect& Rect::offsetTo(int x, int y)
return *this;
}
-Rect& Rect::offsetBy(int x, int y)
+Rect& Rect::offsetBy(int32_t x, int32_t y)
{
left += x;
top += y;
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..fe49300
--- /dev/null
+++ b/libs/utils/ObbFile.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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) {
+ LOGW("couldn't open file %s: %s", filename, strerror(errno));
+ goto out;
+ }
+ success = readFrom(fd);
+ close(fd);
+
+ if (!success) {
+ LOGW("failed to read from %s (fd=%d)\n", filename, fd);
+ }
+
+out:
+ return success;
+}
+
+bool ObbFile::readFrom(int fd)
+{
+ if (fd < 0) {
+ LOGW("attempt to read from invalid fd\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%08zx; file size is 0x%08llx)\n",
+ footerSize, fileLength);
+ return false;
+ }
+
+ if (footerSize < kFooterMinSize) {
+ LOGW("claimed footer size is too small (%08zx; minimum size is 0x%x)\n",
+ footerSize, kFooterMinSize);
+ 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;
+ }
+
+ char* scanBuf = (char*)malloc(footerSize);
+ if (scanBuf == NULL) {
+ LOGW("couldn't allocate scanBuf: %s\n", strerror(errno));
+ return false;
+ }
+
+ actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, footerSize));
+ // readAmount is guaranteed to be less than kMaxBufSize
+ if (actual != (ssize_t)footerSize) {
+ LOGI("couldn't read ObbFile footer: %s\n", strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+#ifdef DEBUG
+ for (int i = 0; i < footerSize; ++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%04x; 0x%04x possible)\n",
+ packageNameLen, footerSize - kPackageNameOffset);
+ free(scanBuf);
+ return false;
+ }
+
+ char* packageName = reinterpret_cast<char*>(scanBuf + kPackageNameOffset);
+ mPackageName = String8(const_cast<char*>(packageName), packageNameLen);
+
+ free(scanBuf);
+
+#ifdef DEBUG
+ LOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion);
+#endif
+
+ 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;
+ }
+
+ my_lseek64(fd, 0, SEEK_END);
+
+ 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 20a4d13..f740fa0 100644
--- a/libs/utils/PollLoop.cpp
+++ b/libs/utils/PollLoop.cpp
@@ -21,8 +21,13 @@
namespace android {
-PollLoop::PollLoop() :
- mPolling(false), mWaiters(0) {
+static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
+static bool gHaveTLS = false;
+static pthread_key_t gTLS = 0;
+
+PollLoop::PollLoop(bool allowNonCallbacks) :
+ mAllowNonCallbacks(allowNonCallbacks), mPolling(false),
+ mWaiters(0), mPendingFdsPos(0) {
openWakePipe();
}
@@ -30,6 +35,41 @@ PollLoop::~PollLoop() {
closeWakePipe();
}
+void PollLoop::threadDestructor(void *st) {
+ PollLoop* const self = static_cast<PollLoop*>(st);
+ if (self != NULL) {
+ self->decStrong((void*)threadDestructor);
+ }
+}
+
+void PollLoop::setForThread(const sp<PollLoop>& pollLoop) {
+ sp<PollLoop> old = getForThread();
+
+ if (pollLoop != NULL) {
+ pollLoop->incStrong((void*)threadDestructor);
+ }
+
+ pthread_setspecific(gTLS, pollLoop.get());
+
+ if (old != NULL) {
+ old->decStrong((void*)threadDestructor);
+ }
+}
+
+sp<PollLoop> PollLoop::getForThread() {
+ if (!gHaveTLS) {
+ pthread_mutex_lock(&gTLSMutex);
+ if (pthread_key_create(&gTLS, threadDestructor) != 0) {
+ pthread_mutex_unlock(&gTLSMutex);
+ return NULL;
+ }
+ gHaveTLS = true;
+ pthread_mutex_unlock(&gTLSMutex);
+ }
+
+ return (PollLoop*)pthread_getspecific(gTLS);
+}
+
void PollLoop::openWakePipe() {
int wakeFds[2];
int result = pipe(wakeFds);
@@ -54,6 +94,7 @@ void PollLoop::openWakePipe() {
RequestedCallback requestedCallback;
requestedCallback.callback = NULL;
+ requestedCallback.looperCallback = NULL;
requestedCallback.data = NULL;
mRequestedCallbacks.insertAt(requestedCallback, 0);
}
@@ -66,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);
@@ -74,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
@@ -91,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;
}
@@ -103,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;
}
@@ -116,36 +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;
-
- if (callback) {
- PendingCallback pendingCallback;
- pendingCallback.fd = requestedFd.fd;
- pendingCallback.events = requestedFd.revents;
- pendingCallback.callback = callback;
- 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;
@@ -154,7 +214,6 @@ bool PollLoop::pollOnce(int timeoutMillis) {
}
}
}
- result = true;
Done:
mLock.lock();
@@ -164,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);
@@ -172,8 +231,14 @@ Done:
LOGD("%p ~ pollOnce - invoking callback for fd %d", this, pendingCallback.fd);
#endif
- bool keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events,
- pendingCallback.data);
+ bool keep = true;
+ if (pendingCallback.callback != NULL) {
+ keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events,
+ pendingCallback.data);
+ } else {
+ keep = pendingCallback.looperCallback(pendingCallback.fd, pendingCallback.events,
+ pendingCallback.data) != 0;
+ }
if (! keep) {
removeCallback(pendingCallback.fd);
}
@@ -199,17 +264,38 @@ 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);
+}
+
+void PollLoop::setLooperCallback(int fd, int events, ALooper_callbackFunc* callback,
+ void* data) {
+ setCallbackCommon(fd, events, NULL, callback, data);
+}
+
+void PollLoop::setCallbackCommon(int fd, int events, Callback callback,
+ ALooper_callbackFunc* looperCallback, void* data) {
+
#if DEBUG_CALLBACKS
LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events);
#endif
- if (! events || ! callback) {
- 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;
@@ -218,6 +304,7 @@ void PollLoop::setCallback(int fd, int events, Callback callback, void* data) {
RequestedCallback requestedCallback;
requestedCallback.callback = callback;
+ requestedCallback.looperCallback = looperCallback;
requestedCallback.data = data;
ssize_t index = getRequestIndexLocked(fd);
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..29bb70a
--- /dev/null
+++ b/libs/utils/tests/ObbFile_test.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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>
+
+#include <fcntl.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);
+
+ int fd = ::open(mFileName, O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ FAIL() << "Couldn't create " << mFileName << " for tests";
+ }
+ }
+
+ 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/AudioEffect.java b/media/java/android/media/AudioEffect.java
index b1b7fed..053cc22 100644
--- a/media/java/android/media/AudioEffect.java
+++ b/media/java/android/media/AudioEffect.java
@@ -27,22 +27,25 @@ import java.nio.ByteBuffer;
import java.util.UUID;
/**
- * AudioEffect is the base class for implementing audio effect control in Java applications.
- * Creating an AudioEffect object will create the effect engine in audio framework if no
- * instance of the same effect type exists in the specified audio session.
- * If one exists, this instance will be used. The application creating the AudioEffect object
- * (or a derived class) will either receive control of the effect engine or not depending
- * on the priority parameter. If priority is higher than the priority used by the current
- * effect engine owner, the control will be transfered to the new object. Otherwise
- * control will remain with the previous object. In this case, the new application will be
- * notified of changes in effect engine state or control ownership by the appropiate listener.
- * If the effect is to be applied to a specific AudioTrack or MediaPlayer instance,
- * the application must specify the audio session ID of that instance.
+ * AudioEffect is the base class for implementing audio effect control in Java
+ * applications.
+ * <p>Creating an AudioEffect object will create the effect engine in
+ * audio framework if no instance of the same effect type exists in the
+ * specified audio session. If one exists, this instance will be used.
+ * <p>The application creating the AudioEffect object (or a derived class) will either
+ * receive control of the effect engine or not depending on the priority
+ * parameter. If priority is higher than the priority used by the current effect
+ * engine owner, the control will be transfered to the new object. Otherwise
+ * control will remain with the previous object. In this case, the new
+ * application will be notified of changes in effect engine state or control
+ * ownership by the appropiate listener.
+ * <p>If the effect is to be applied to a specific AudioTrack or MediaPlayer instance,
+ * the application must specify the audio session ID of that instance when calling the AudioEffect
+ * constructor.
*
- * {@hide Pending API council review}
+ * { @hide Pending API council review }
*/
-public class AudioEffect
-{
+public class AudioEffect {
static {
System.loadLibrary("audioeffect_jni");
native_init();
@@ -51,31 +54,60 @@ public class AudioEffect
private final static String TAG = "AudioEffect-JAVA";
/**
- * The following UUIDs define effect types corresponding to standard audio effects
- * whose implementation and interface conform to the OpenSL ES specification.
- * The definitions match the corresponding interface IDs in OpenSLES_IID.h
+ * The following UUIDs define effect types corresponding to standard audio
+ * effects whose implementation and interface conform to the OpenSL ES
+ * specification. The definitions match the corresponding interface IDs in
+ * OpenSLES_IID.h
*/
- public static final UUID EFFECT_TYPE_ENV_REVERB = UUID.fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e");
- public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID.fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b");
- public static final UUID EFFECT_TYPE_EQUALIZER = UUID.fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b");
- public static final UUID EFFECT_TYPE_BASS_BOOST = UUID.fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b");
- public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID.fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b");
- public static final UUID EFFECT_TYPE_INVALID = UUID.fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
+ /**
+ * UUID for environmental reverb effect
+ */
+ public static final UUID EFFECT_TYPE_ENV_REVERB = UUID
+ .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e");
+ /**
+ * UUID for preset reverb effect
+ */
+ public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID
+ .fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b");
+ /**
+ * UUID for equalizer effect
+ */
+ public static final UUID EFFECT_TYPE_EQUALIZER = UUID
+ .fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b");
+ /**
+ * UUID for bass boost effect
+ */
+ public static final UUID EFFECT_TYPE_BASS_BOOST = UUID
+ .fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b");
+ /**
+ * UUID for virtualizer effect
+ */
+ public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID
+ .fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b");
/**
- * State of an AudioEffect object that was not successfully initialized upon creation
+ * Null effect UUID. Used when the UUID for effect type of
+ */
+ public static final UUID EFFECT_TYPE_NULL = UUID
+ .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
+
+ /**
+ * State of an AudioEffect object that was not successfully initialized upon
+ * creation
*/
public static final int STATE_UNINITIALIZED = 0;
/**
* State of an AudioEffect object that is ready to be used.
*/
- public static final int STATE_INITIALIZED = 1;
+ public static final int STATE_INITIALIZED = 1;
+ // to keep in sync with
+ // frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp
/**
* Event id for engine state change notification.
*/
- protected static final int NATIVE_EVENT_ENABLED_STATUS = 0;
+ protected static final int NATIVE_EVENT_ENABLED_STATUS = 0;
/**
* Event id for engine control ownership change notification.
*/
@@ -85,56 +117,89 @@ public class AudioEffect
*/
protected static final int NATIVE_EVENT_PARAMETER_CHANGED = 2;
+ /**
+ * Successful operation.
+ */
+ public static final int SUCCESS = 0;
+ /**
+ * Unspecified error.
+ */
+ public static final int ERROR = -1;
+ /**
+ * Internal opreation status. Not returned by any method.
+ */
+ public static final int ALREADY_EXISTS = -2;
+ /**
+ * Operation failed due to bad object initialization.
+ */
+ public static final int ERROR_NO_INIT = -3;
+ /**
+ * Operation failed due to bad parameter value.
+ */
+ public static final int ERROR_BAD_VALUE = -4;
+ /**
+ * Operation failed because it was requested in wrong state.
+ */
+ public static final int ERROR_INVALID_OPERATION = -5;
+ /**
+ * Operation failed due to lack of memory.
+ */
+ public static final int ERROR_NO_MEMORY = -6;
+ /**
+ * Operation failed due to dead remote object.
+ */
+ public static final int ERROR_DEAD_OBJECT = -7;
+
+ /**
+ * The effect descriptor contains necessary information to facilitate
+ * effects enumeration:<br>
+ * <ul>
+ * <li>mType: UUID corresponding to the OpenSL ES interface implemented by this effect</li>
+ * <li>mUuid: UUID for this particular implementation</li>
+ * <li>mConnectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li>
+ * <li>mName: human readable effect name</li>
+ * <li>mImplementor: human readable effect implementor name</li>
+ * </ul>
+ */
+ public static class Descriptor {
+
+ public Descriptor() {
+ }
+
+ public Descriptor(String type, String uuid, String connectMode,
+ String name, String implementor) {
+ mType = UUID.fromString(type);
+ mUuid = UUID.fromString(uuid);
+ mConnectMode = connectMode;
+ mName = name;
+ mImplementor = implementor;
+ }
+
+ public UUID mType;
+ public UUID mUuid;
+ public String mConnectMode;
+ public String mName;
+ public String mImplementor;
+ };
+
+ /**
+ * Effect connection mode is insert. Specifying an audio session ID when creating the effect
+ * will insert this effect after all players in the same audio session.
+ */
+ public static final String EFFECT_INSERT = "Insert";
+ /**
+ * Effect connection mode is auxiliary.
+ * <p>Auxiliary effects must be created on session 0 (global output mix). In order for a
+ * MediaPlayer or AudioTrack to be fed into this effect, they must be explicitely attached to
+ * this effect and a send level must be specified.
+ * <p>Use the effect ID returned by {@link #getId()} to designate this particular effect when
+ * attaching it to the MediaPlayer or AudioTrack.
+ */
+ public static final String EFFECT_AUXILIARY = "Auxiliary";
- // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp
- public static final int SUCCESS = 0;
- public static final int ERROR = -1;
- public static final int ALREADY_EXISTS = -2;
- public static final int NO_INIT = -3;
- public static final int BAD_VALUE = -4;
- public static final int INVALID_OPERATION = -5;
- public static final int NO_MEMORY = -6;
- public static final int DEAD_OBJECT = -7;
-
-
- /**
- * The effect descriptor contains necessary information to facilitate
- * effects enumeration:
- * mType: UUID corresponding to the OpenSL ES interface implemented by this effect
- * mUuid: UUID for this particular implementation
- * mConnectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}
- * mName: human readable effect name
- * mImplementor: human readable effect implementor name
- */
- public static class Descriptor {
-
- public Descriptor() {
- }
- public Descriptor(String type,
- String uuid,
- String connectMode,
- String name,
- String implementor) {
- mType = UUID.fromString(type);
- mUuid = UUID.fromString(uuid);
- mConnectMode = connectMode;
- mName = name;
- mImplementor = implementor;
- }
-
- public UUID mType;
- public UUID mUuid;
- public String mConnectMode;
- public String mName;
- public String mImplementor;
- };
-
- public static final String EFFECT_INSERT = "Insert";
- public static final String EFFECT_AUXILIARY = "Auxiliary";
-
- //--------------------------------------------------------------------------
+ // --------------------------------------------------------------------------
// Member variables
- //--------------------
+ // --------------------
/**
* Indicates the state of the AudioEffect instance
*/
@@ -159,17 +224,20 @@ public class AudioEffect
/**
* Listener for effect engine state change notifications.
- * @see #setEnableStatusListener(OnEnableStatusChangeListener)
+ *
+ * @see #setEnableStatusListener(OnEnableStatusChangeListener)
*/
protected OnEnableStatusChangeListener mEnableStatusChangeListener = null;
/**
* Listener for effect engine control ownership change notifications.
- * @see #setControlStatusListener(OnControlStatusChangeListener)
+ *
+ * @see #setControlStatusListener(OnControlStatusChangeListener)
*/
protected OnControlStatusChangeListener mControlChangeStatusListener = null;
/**
* Listener for effect engine control ownership change notifications.
- * @see #setParameterListener(OnParameterChangeListener)
+ *
+ * @see #setParameterListener(OnParameterChangeListener)
*/
protected OnParameterChangeListener mParameterChangeListener = null;
/**
@@ -181,32 +249,36 @@ public class AudioEffect
*/
protected NativeEventHandler mNativeEventHandler = null;
-
-
- //--------------------------------------------------------------------------
+ // --------------------------------------------------------------------------
// Constructor, Finalize
- //--------------------
+ // --------------------
/**
* Class constructor.
- * @param type: type of effect engine created. See
- * {@link #EFFECT_TYPE_ENV_REVERB}, {@link #EFFECT_TYPE_EQUALIZER} ...
- * Types corresponding to built-in effects are defined by AudioEffect class.
- * Other types can be specified provided they correspond an existing OpenSL ES
- * interface ID and the corresponsing effect is available on the platform.
- * If an unspecified effect type is requested, the constructor with throw the
- * IllegalArgumentException.
- * @param uuid: unique identifier of a particular effect implementation. Must be
- * specified if the caller wants to use a particular implementation of an effect type.
- * This parameter can be set to null in which case only the type will be used to select
- * the effect.
- * @param priority: the priority level requested by the application for controlling
- * the effect engine. As the same effect 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 effect will be attached to the MediaPlayer or AudioTrack in the
- * same audio session. Otherwise, the effect will apply to the output mix.
+ *
+ * @param type type of effect engine created. See {@link #EFFECT_TYPE_ENV_REVERB},
+ * {@link #EFFECT_TYPE_EQUALIZER} ... Types corresponding to
+ * built-in effects are defined by AudioEffect class. Other types
+ * can be specified provided they correspond an existing OpenSL
+ * ES interface ID and the corresponsing effect is available on
+ * the platform. If an unspecified effect type is requested, the
+ * constructor with throw the IllegalArgumentException. This
+ * parameter can be set to {@link #EFFECT_TYPE_NULL} in which
+ * case only the uuid will be used to select the effect.
+ * @param uuid unique identifier of a particular effect implementation.
+ * Must be specified if the caller wants to use a particular
+ * implementation of an effect type. This parameter can be set to
+ * {@link #EFFECT_TYPE_NULL} in which case only the type will
+ * be used to select the effect.
+ * @param priority the priority level requested by the application for
+ * controlling the effect engine. As the same effect 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 effect will be attached to the MediaPlayer or
+ * AudioTrack in the same audio session. Otherwise, the effect
+ * will apply to the output mix.
*
* @throws java.lang.IllegalArgumentException
* @throws java.lang.UnsupportedOperationException
@@ -214,22 +286,28 @@ public class AudioEffect
*/
public AudioEffect(UUID type, UUID uuid, int priority, int audioSession)
- throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
+ throws IllegalArgumentException, UnsupportedOperationException,
+ RuntimeException {
int[] id = new int[1];
Descriptor[] desc = new Descriptor[1];
// native initialization
int initResult = native_setup(new WeakReference<AudioEffect>(this),
- type.toString(), uuid.toString(), priority, audioSession, id, desc);
+ type.toString(), uuid.toString(), priority, audioSession, id,
+ desc);
if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
- Log.e(TAG, "Error code "+initResult+" when initializing AudioEffect.");
+ Log.e(TAG, "Error code " + initResult
+ + " when initializing AudioEffect.");
switch (initResult) {
- case BAD_VALUE:
- throw (new IllegalArgumentException("Effect type: "+type+ " not supported."));
- case INVALID_OPERATION:
- throw (new UnsupportedOperationException("Effect library not loaded"));
+ case ERROR_BAD_VALUE:
+ throw (new IllegalArgumentException("Effect type: " + type
+ + " not supported."));
+ case ERROR_INVALID_OPERATION:
+ throw (new UnsupportedOperationException(
+ "Effect library not loaded"));
default:
- throw (new RuntimeException("Cannot initialize effect engine for type: "+type+
- "Error: "+ initResult));
+ throw (new RuntimeException(
+ "Cannot initialize effect engine for type: " + type
+ + "Error: " + initResult));
}
}
mId = id[0];
@@ -240,9 +318,9 @@ public class AudioEffect
}
/**
- * Releases the native AudioEffect resources. It is a good practice to release the
- * effect engine when not in use as control can be returned to other applications
- * or the native resources released.
+ * Releases the native AudioEffect resources. It is a good practice to
+ * release the effect engine when not in use as control can be returned to
+ * other applications or the native resources released.
*/
public void release() {
synchronized (mStateLock) {
@@ -258,119 +336,115 @@ public class AudioEffect
/**
* Get the effect descriptor.
- * {@see #Descriptor}.
+ *
+ //TODO when AudioEffect class is unhidden @ see android.media.AudioEffect.Descriptor
* @throws IllegalStateException
*/
- public Descriptor getDescriptor()
- throws IllegalStateException {
+ public Descriptor getDescriptor() throws IllegalStateException {
checkState("getDescriptor()");
return mDescriptor;
}
- //--------------------------------------------------------------------------
+ // --------------------------------------------------------------------------
// Effects Enumeration
- //--------------------
+ // --------------------
/**
* Query all effects available on the platform. Returns an array of
- * {@link #Descriptor} objects
+ //TODO when AudioEffect class is unhidden: {@ link android.media.AudioEffect.Descriptor} objects
*
* @throws IllegalStateException
*/
static public Descriptor[] queryEffects() {
- return (Descriptor[])native_query_effects();
+ return (Descriptor[]) native_query_effects();
}
- //--------------------------------------------------------------------------
+ // --------------------------------------------------------------------------
// Control methods
- //--------------------
-
- /**
- * Enable effect engine.
- * @return {@link #NO_ERROR} in case of success,
- * {@link #INVALID_OPERATION} or {@link #DEAD_OBJECT} in case of failure.
- * @throws IllegalStateException
- */
- public int enable()
- throws IllegalStateException {
- checkState("enable()");
- return native_enable();
- }
+ // --------------------
/**
- * Disable effect engine.
- * @return NO_ERROR in case of success,
- * INVALID_OPERATION or DEAD_OBJECT in case of failure.
+ * Enable or disable effect engine.
+ *
+ * @param enabled the requested enable state
+ * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION}
+ * or {@link #ERROR_DEAD_OBJECT} in case of failure.
* @throws IllegalStateException
*/
- public int disable()
- throws IllegalStateException {
- checkState("disable()");
- return native_disable();
+ public int setEnabled(boolean enabled) throws IllegalStateException {
+ checkState("setEnabled()");
+ return native_setEnabled(enabled);
}
/**
* Set effect parameter. The setParameter method is provided in several
- * forms addressing most common parameter formats. This form is the
- * most generic one where the parameter and its value are both specified
- * as an array of bytes. The parameter and value type and length are therefore
- * totally free. For standard effect defined by OpenSL ES, the parameter format
- * and values must match the definitions in the corresponding OpenSL ES interface.
+ * forms addressing most common parameter formats. This form is the most
+ * generic one where the parameter and its value are both specified as an
+ * array of bytes. The parameter and value type and length are therefore
+ * totally free. For standard effect defined by OpenSL ES, the parameter
+ * format and values must match the definitions in the corresponding OpenSL
+ * ES interface.
*
- * @param param: the identifier of the parameter to set
- * @param value: the new value for the specified parameter
- * @return NO_ERROR in case of success,
- * {@link #BAD_VALUE}, {@link #NO_MEMORY}, {@link #INVALID_OPERATION} or {@link DEAD_OBJECT} in case of failure
+ * @param param the identifier of the parameter to set
+ * @param value the new value for the specified parameter
+ * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
+ * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or
+ * {@link #ERROR_DEAD_OBJECT} in case of failure
* @throws IllegalStateException
*/
public int setParameter(byte[] param, byte[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
checkState("setParameter()");
return native_setParameter(param.length, param, value.length, value);
}
/**
* Set effect parameter. The parameter and its value are integers.
- * @see #setParameter(byte[], byte[])
+ *
+ * @see #setParameter(byte[], byte[])
*/
- public int setParameter(int param, int value)
- throws IllegalStateException {
+ public int setParameter(int param, int value) throws IllegalStateException {
byte[] p = intToByteArray(param);
byte[] v = intToByteArray(value);
return setParameter(p, v);
}
/**
- * Set effect parameter. The parameter is an integer and the value is a short integer.
- * @see #setParameter(byte[], byte[])
+ * Set effect parameter. The parameter is an integer and the value is a
+ * short integer.
+ *
+ * @see #setParameter(byte[], byte[])
*/
public int setParameter(int param, short value)
- throws IllegalStateException {
+ throws IllegalStateException {
byte[] p = intToByteArray(param);
byte[] v = shortToByteArray(value);
return setParameter(p, v);
}
/**
- * Set effect parameter. The parameter is an integer and the value is an array of bytes.
- * @see #setParameter(byte[], byte[])
+ * Set effect parameter. The parameter is an integer and the value is an
+ * array of bytes.
+ *
+ * @see #setParameter(byte[], byte[])
*/
public int setParameter(int param, byte[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
byte[] p = intToByteArray(param);
return setParameter(p, value);
}
/**
- * Set effect parameter. The parameter is an array of 1 or 2 integers and the value
- * is also an array of 1 or 2 integers
- * @see #setParameter(byte[], byte[])
+ * Set effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is also an array of 1 or 2 integers
+ *
+ * @see #setParameter(byte[], byte[])
*/
public int setParameter(int[] param, int[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (param.length > 2 || value.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param[0]);
if (param.length > 1) {
@@ -386,14 +460,15 @@ public class AudioEffect
}
/**
- * Set effect parameter. The parameter is an array of 1 or 2 integers and the value
- * is an array of 1 or 2 short integers
- * @see #setParameter(byte[], byte[])
+ * Set effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is an array of 1 or 2 short integers
+ *
+ * @see #setParameter(byte[], byte[])
*/
public int setParameter(int[] param, short[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (param.length > 2 || value.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param[0]);
if (param.length > 1) {
@@ -410,14 +485,15 @@ public class AudioEffect
}
/**
- * Set effect parameter. The parameter is an array of 1 or 2 integers and the value
- * is an array of bytes
- * @see #setParameter(byte[], byte[])
+ * Set effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is an array of bytes
+ *
+ * @see #setParameter(byte[], byte[])
*/
public int setParameter(int[] param, byte[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (param.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param[0]);
if (param.length > 1) {
@@ -429,20 +505,23 @@ public class AudioEffect
/**
* Get effect parameter. The getParameter method is provided in several
- * forms addressing most common parameter formats. This form is the
- * most generic one where the parameter and its value are both specified
- * as an array of bytes. The parameter and value type and length are therefore
+ * forms addressing most common parameter formats. This form is the most
+ * generic one where the parameter and its value are both specified as an
+ * array of bytes. The parameter and value type and length are therefore
* totally free.
- * @param param: the identifier of the parameter to set
- * @param value: the new value for the specified parameter
- * @return NO_ERROR in case of success,
- * {@link #BAD_VALUE}, {@link #NO_MEMORY}, {@link #INVALID_OPERATION} or {@link DEAD_OBJECT} in case of failure
- * When called, value.length indicates the maximum size of the returned parameters value.
- * When returning, value.length is updated with the actual size of the returned value.
+ *
+ * @param param the identifier of the parameter to set
+ * @param value the new value for the specified parameter
+ * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
+ * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or
+ * {@link #ERROR_DEAD_OBJECT} in case of failure When called, value.length
+ * indicates the maximum size of the returned parameters value. When
+ * returning, value.length is updated with the actual size of the
+ * returned value.
* @throws IllegalStateException
*/
public int getParameter(byte[] param, byte[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
checkState("getParameter()");
int[] vSize = new int[1];
vSize[0] = value.length;
@@ -456,25 +535,28 @@ public class AudioEffect
}
/**
- * Get effect parameter. The parameter is an integer and the value is an array of bytes.
- * @see #getParameter(byte[], byte[])
+ * Get effect parameter. The parameter is an integer and the value is an
+ * array of bytes.
+ *
+ * @see #getParameter(byte[], byte[])
*/
public int getParameter(int param, byte[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
byte[] p = intToByteArray(param);
return getParameter(p, value);
}
/**
- * Get effect parameter. The parameter is an integer and the value
- * is an array of 1 or 2 integers
- * @see #getParameter(byte[], byte[])
+ * Get effect parameter. The parameter is an integer and the value is an
+ * array of 1 or 2 integers
+ *
+ * @see #getParameter(byte[], byte[])
*/
public int getParameter(int param, int[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (value.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param);
@@ -490,14 +572,15 @@ public class AudioEffect
}
/**
- * Get effect parameter. The parameter is an integer and the value
- * is an array of 1 or 2 short integers
- * @see #getParameter(byte[], byte[])
+ * Get effect parameter. The parameter is an integer and the value is an
+ * array of 1 or 2 short integers
+ *
+ * @see #getParameter(byte[], byte[])
*/
public int getParameter(int param, short[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (value.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param);
@@ -513,14 +596,15 @@ public class AudioEffect
}
/**
- * Get effect parameter. The parameter is an array of 1 or 2 integers and the value
- * is also an array of 1 or 2 integers
- * @see #getParameter(byte[], byte[])
+ * Get effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is also an array of 1 or 2 integers
+ *
+ * @see #getParameter(byte[], byte[])
*/
public int getParameter(int[] param, int[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (param.length > 2 || value.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param[0]);
if (param.length > 1) {
@@ -539,14 +623,15 @@ public class AudioEffect
}
/**
- * Get effect parameter. The parameter is an array of 1 or 2 integers and the value
- * is an array of 1 or 2 short integers
- * @see #getParameter(byte[], byte[])
+ * Get effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is an array of 1 or 2 short integers
+ *
+ * @see #getParameter(byte[], byte[])
*/
public int getParameter(int[] param, short[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (param.length > 2 || value.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param[0]);
if (param.length > 1) {
@@ -565,14 +650,15 @@ public class AudioEffect
}
/**
- * Get effect parameter. The parameter is an array of 1 or 2 integers and the value
- * is an array of bytes
- * @see #getParameter(byte[], byte[])
+ * Get effect parameter. The parameter is an array of 1 or 2 integers and
+ * the value is an array of bytes
+ *
+ * @see #getParameter(byte[], byte[])
*/
public int getParameter(int[] param, byte[] value)
- throws IllegalStateException {
+ throws IllegalStateException {
if (param.length > 2) {
- return BAD_VALUE;
+ return ERROR_BAD_VALUE;
}
byte[] p = intToByteArray(param[0]);
if (param.length > 1) {
@@ -583,19 +669,19 @@ public class AudioEffect
return getParameter(p, value);
}
-
/**
- * Send a command to the effect engine. This method is intended to send proprietary
- * commands to a particular effect implementation.
+ * Send a command to the effect engine. This method is intended to send
+ * proprietary commands to a particular effect implementation.
*
*/
public int command(int cmdCode, byte[] command, byte[] reply)
- throws IllegalStateException {
+ throws IllegalStateException {
checkState("command()");
int[] replySize = new int[1];
replySize[0] = reply.length;
- int status = native_command(cmdCode, command.length, command, replySize, reply);
+ int status = native_command(cmdCode, command.length, command,
+ replySize, reply);
if (reply.length > replySize[0]) {
byte[] resizedReply = new byte[replySize[0]];
@@ -605,51 +691,53 @@ public class AudioEffect
return status;
}
- //--------------------------------------------------------------------------
+ // --------------------------------------------------------------------------
// Getters
- //--------------------
+ // --------------------
/**
- * Returns effect unique identifier. This system wide unique identifier
- * can be used to attach this effect to a MediaPlayer or an AudioTrack
- * when the effect is an auxiliary effect (Reverb)
+ * Returns effect unique identifier. This system wide unique identifier can
+ * be used to attach this effect to a MediaPlayer or an AudioTrack when the
+ * effect is an auxiliary effect (Reverb)
+ *
* @return the effect identifier.
* @throws IllegalStateException
*/
- public int getId()
- throws IllegalStateException {
+ public int getId() throws IllegalStateException {
checkState("getId()");
return mId;
}
/**
* Returns effect engine enable state
+ *
* @return true if the effect is enabled, false otherwise.
* @throws IllegalStateException
*/
- public boolean getEnable()
- throws IllegalStateException {
- checkState("getEnable()");
- return native_getEnable();
+ public boolean getEnabled() throws IllegalStateException {
+ checkState("getEnabled()");
+ return native_getEnabled();
}
/**
* Checks if this AudioEffect object is controlling the effect engine.
- * @return true if this instance has control of effect engine, false otherwise.
+ *
+ * @return true if this instance has control of effect engine, false
+ * otherwise.
* @throws IllegalStateException
*/
- public boolean hasControl()
- throws IllegalStateException {
+ public boolean hasControl() throws IllegalStateException {
checkState("hasControl()");
return native_hasControl();
}
- //--------------------------------------------------------------------------
+ // --------------------------------------------------------------------------
// Initialization / configuration
- //--------------------
+ // --------------------
/**
* Sets the listener AudioEffect notifies when the effect engine is enabled
* or disabled.
+ *
* @param listener
*/
public void setEnableStatusListener(OnEnableStatusChangeListener listener) {
@@ -662,8 +750,9 @@ public class AudioEffect
}
/**
- * Sets the listener AudioEffect notifies when the effect engine control
- * is taken or returned.
+ * Sets the listener AudioEffect notifies when the effect engine control is
+ * taken or returned.
+ *
* @param listener
*/
public void setControlStatusListener(OnControlStatusChangeListener listener) {
@@ -677,6 +766,7 @@ public class AudioEffect
/**
* Sets the listener AudioEffect notifies when a parameter is changed.
+ *
* @param listener
*/
public void setParameterListener(OnParameterChangeListener listener) {
@@ -691,7 +781,7 @@ public class AudioEffect
// Convenience method for the creation of the native event handler
// It is called only when a non-null event listener is set.
// precondition:
- // mNativeEventHandler is null
+ // mNativeEventHandler is null
private void createNativeEventHandler() {
Looper looper;
if ((looper = Looper.myLooper()) != null) {
@@ -703,52 +793,62 @@ public class AudioEffect
}
}
- //---------------------------------------------------------
+ // ---------------------------------------------------------
// Interface definitions
- //--------------------
+ // --------------------
/**
- * Interface definition for a callback to be invoked when the
- * effect engine is enabled or disabled.
+ * The OnParameterChangeListener interface defines a method called by the AudioEffect
+ * when a the enabled state of the effect engine was changed by the controlling application.
*/
- public interface OnEnableStatusChangeListener {
+ public interface OnEnableStatusChangeListener {
/**
- * Called on the listener to notify it that the effect engine
- * has been enabled or disabled.
+ * Called on the listener to notify it that the effect engine has been
+ * enabled or disabled.
+ * @param effect the effect on which the interface is registered.
+ * @param enabled new effect state.
*/
void onEnableStatusChange(AudioEffect effect, boolean enabled);
}
/**
- * Interface definition for a callback to be invoked when the
- * effect engine control is taken or returned.
+ * The OnControlStatusChangeListener interface defines a method called by the AudioEffect
+ * when a the control of the effect engine is gained or lost by the application
*/
- public interface OnControlStatusChangeListener {
+ public interface OnControlStatusChangeListener {
/**
- * Called on the listener to notify it that the effect engine
- * control has been taken or returned.
+ * Called on the listener to notify it that the effect engine control
+ * has been taken or returned.
+ * @param effect the effect on which the interface is registered.
+ * @param controlGranted true if the application has been granted control of the effect
+ * engine, false otherwise.
*/
void onControlStatusChange(AudioEffect effect, boolean controlGranted);
}
/**
- * Interface definition for a callback to be invoked when a
- * parameter value has changed.
+ * The OnParameterChangeListener interface defines a method called by the AudioEffect
+ * when a parameter is changed in the effect engine by the controlling application.
*/
- public interface OnParameterChangeListener {
+ public interface OnParameterChangeListener {
/**
* Called on the listener to notify it that a parameter value has changed.
+ * @param effect the effect on which the interface is registered.
+ * @param status status of the set parameter operation.
+ * @param param ID of the modified parameter.
+ * @param value the new parameter value.
*/
- void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value);
+ void onParameterChange(AudioEffect effect, int status, byte[] param,
+ byte[] value);
}
- //---------------------------------------------------------
+ // ---------------------------------------------------------
// Inner classes
- //--------------------
+ // --------------------
/**
- * Helper class to handle the forwarding of native events to the appropriate listeners
+ * Helper class to handle the forwarding of native events to the appropriate
+ * listeners
*/
- private class NativeEventHandler extends Handler
- {
+ private class NativeEventHandler extends Handler {
private AudioEffect mAudioEffect;
public NativeEventHandler(AudioEffect ae, Looper looper) {
@@ -761,14 +861,15 @@ public class AudioEffect
if (mAudioEffect == null) {
return;
}
- switch(msg.what) {
+ switch (msg.what) {
case NATIVE_EVENT_ENABLED_STATUS:
OnEnableStatusChangeListener enableStatusChangeListener = null;
synchronized (mListenerLock) {
enableStatusChangeListener = mAudioEffect.mEnableStatusChangeListener;
}
if (enableStatusChangeListener != null) {
- enableStatusChangeListener.onEnableStatusChange(mAudioEffect, (boolean)(msg.arg1 != 0));
+ enableStatusChangeListener.onEnableStatusChange(
+ mAudioEffect, (boolean) (msg.arg1 != 0));
}
break;
case NATIVE_EVENT_CONTROL_STATUS:
@@ -777,7 +878,8 @@ public class AudioEffect
controlStatusChangeListener = mAudioEffect.mControlChangeStatusListener;
}
if (controlStatusChangeListener != null) {
- controlStatusChangeListener.onControlStatusChange(mAudioEffect, (boolean)(msg.arg1 != 0));
+ controlStatusChangeListener.onControlStatusChange(
+ mAudioEffect, (boolean) (msg.arg1 != 0));
}
break;
case NATIVE_EVENT_PARAMETER_CHANGED:
@@ -786,10 +888,12 @@ public class AudioEffect
parameterChangeListener = mAudioEffect.mParameterChangeListener;
}
if (parameterChangeListener != null) {
- // arg1 contains offset of parameter value from start of byte array
+ // arg1 contains offset of parameter value from start of
+ // byte array
int vOffset = msg.arg1;
- byte[] p = (byte[])msg.obj;
- // See effect_param_t in EffectApi.h for psize and vsize fields offsets
+ byte[] p = (byte[]) msg.obj;
+ // See effect_param_t in EffectApi.h for psize and vsize
+ // fields offsets
int status = byteArrayToInt(p, 0);
int psize = byteArrayToInt(p, 4);
int vsize = byteArrayToInt(p, 8);
@@ -798,90 +902,76 @@ public class AudioEffect
System.arraycopy(p, 12, param, 0, psize);
System.arraycopy(p, vOffset, value, 0, vsize);
- parameterChangeListener.onParameterChange(mAudioEffect, status, param, value);
+ parameterChangeListener.onParameterChange(mAudioEffect,
+ status, param, value);
}
break;
- default:
+ default:
Log.e(TAG, "handleMessage() Unknown event type: " + msg.what);
break;
}
}
}
-
- //---------------------------------------------------------
+ // ---------------------------------------------------------
// Java methods called from the native side
- //--------------------
+ // --------------------
@SuppressWarnings("unused")
- private static void postEventFromNative(Object effect_ref,
- int what, int arg1, int arg2, Object obj) {
- AudioEffect effect = (AudioEffect)((WeakReference)effect_ref).get();
+ private static void postEventFromNative(Object effect_ref, int what,
+ int arg1, int arg2, Object obj) {
+ AudioEffect effect = (AudioEffect) ((WeakReference) effect_ref).get();
if (effect == null) {
return;
}
if (effect.mNativeEventHandler != null) {
- Message m = effect.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj);
+ Message m = effect.mNativeEventHandler.obtainMessage(what, arg1,
+ arg2, obj);
effect.mNativeEventHandler.sendMessage(m);
}
}
-
- //---------------------------------------------------------
+ // ---------------------------------------------------------
// Native methods called from the Java side
- //--------------------
+ // --------------------
private static native final void native_init();
- private native final int native_setup(Object audioeffect_this,
- String type,
- String uuid,
- int priority,
- int audioSession,
- int[] id,
- Object[] desc);
+ private native final int native_setup(Object audioeffect_this, String type,
+ String uuid, int priority, int audioSession, int[] id, Object[] desc);
private native final void native_finalize();
private native final void native_release();
- private native final int native_enable();
+ private native final int native_setEnabled(boolean enabled);
- private native final int native_disable();
-
- private native final boolean native_getEnable();
+ private native final boolean native_getEnabled();
private native final boolean native_hasControl();
- private native final int native_setParameter(int psize,
- byte[] param,
- int vsize,
- byte[] value);
+ private native final int native_setParameter(int psize, byte[] param,
+ int vsize, byte[] value);
- private native final int native_getParameter(int psize,
- byte[] param,
- int[] vsize,
- byte[] value);
+ private native final int native_getParameter(int psize, byte[] param,
+ int[] vsize, byte[] value);
- private native final int native_command(int cmdCode,
- int cmdSize,
- byte[] cmdData,
- int[] repSize,
- byte[] repData);
+ private native final int native_command(int cmdCode, int cmdSize,
+ byte[] cmdData, int[] repSize, byte[] repData);
private static native Object[] native_query_effects();
- //---------------------------------------------------------
+ // ---------------------------------------------------------
// Utility methods
- //------------------
+ // ------------------
- protected void checkState(String methodName)
- throws IllegalStateException {
+ protected void checkState(String methodName) throws IllegalStateException {
synchronized (mStateLock) {
if (mState != STATE_INITIALIZED) {
- throw(new IllegalStateException(methodName+" called on uninitialized AudioEffect."));
+ throw (new IllegalStateException(methodName
+ + " called on uninitialized AudioEffect."));
}
}
}
@@ -890,10 +980,12 @@ public class AudioEffect
switch (status) {
case AudioEffect.SUCCESS:
break;
- case AudioEffect.BAD_VALUE:
- throw (new IllegalArgumentException("AudioEffect: bad parameter value"));
- case AudioEffect.INVALID_OPERATION:
- throw (new UnsupportedOperationException("AudioEffect: invalid parameter operation"));
+ case AudioEffect.ERROR_BAD_VALUE:
+ throw (new IllegalArgumentException(
+ "AudioEffect: bad parameter value"));
+ case AudioEffect.ERROR_INVALID_OPERATION:
+ throw (new UnsupportedOperationException(
+ "AudioEffect: invalid parameter operation"));
default:
throw (new RuntimeException("AudioEffect: set/get parameter error"));
}
@@ -903,6 +995,7 @@ public class AudioEffect
return byteArrayToInt(valueBuf, 0);
}
+
protected int byteArrayToInt(byte[] valueBuf, int offset) {
ByteBuffer converter = ByteBuffer.wrap(valueBuf);
converter.order(ByteOrder.nativeOrder());
@@ -931,12 +1024,12 @@ public class AudioEffect
protected byte[] shortToByteArray(short value) {
ByteBuffer converter = ByteBuffer.allocate(2);
converter.order(ByteOrder.nativeOrder());
- short sValue = (short)value;
+ short sValue = (short) value;
converter.putShort(sValue);
return converter.array();
}
- protected byte[] concatArrays(byte[] ...arrays) {
+ protected byte[] concatArrays(byte[]... arrays) {
int len = 0;
for (byte[] a : arrays) {
len += a.length;
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/java/android/media/Visualizer.java b/media/java/android/media/Visualizer.java
new file mode 100755
index 0000000..cdd3cdf
--- /dev/null
+++ b/media/java/android/media/Visualizer.java
@@ -0,0 +1,510 @@
+/*
+ * 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.
+ */
+
+package android.media;
+
+import android.util.Log;
+import java.lang.ref.WeakReference;
+import java.io.IOException;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * The Visualizer class enables application to retrieve part of the currently playing audio for
+ * visualization purpose. It is not an audio recording interface and only returns partial and low
+ * quality audio content. However, to protect privacy of certain audio data (e.g voice mail) the use
+ * of the visualizer requires the permission android.permission.RECORD_AUDIO.
+ * <p>The audio session ID passed to the constructor indicates which audio content should be
+ * visualized:<br>
+ * <ul>
+ * <li>If the session is 0, the audio output mix is visualized</li>
+ * <li>If the session is not 0, the audio from a particular {@link MediaPlayer} or
+ * {@link AudioTrack}
+ * using this audio session is visualized </li>
+ * </ul>
+ * <p>Two types of representation of audio content can be captured: <br>
+ * <ul>
+ * <li>Waveform data: consecutive 8-bit (unsigned) mono samples by using the
+ * {@link #getWaveForm(byte[])} method</li>
+ * <li>Frequency data: 8-bit magnitude FFT by using the {@link #getFft(byte[])} method</li>
+ * </ul>
+ * <p>The length of the capture can be retrieved or specified by calling respectively
+ * {@link #getCaptureSize()} and {@link #setCaptureSize(int)} methods. Note that the size of the FFT
+ * is half of the specified capture size but both sides of the spectrum are returned yielding in a
+ * number of bytes equal to the capture size. The capture size must be a power of 2 in the range
+ * returned by {@link #getCaptureSizeRange()}.
+ * <p>In addition to the polling capture mode described above with {@link #getWaveForm(byte[])} and
+ * {@link #getFft(byte[])} methods, a callback mode is also available by installing a listener by
+ * use of the {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
+ * The rate at which the listener capture method is called as well as the type of data returned is
+ * specified.
+ * <p>Before capturing data, the Visualizer must be enabled by calling the
+ * {@link #setEnabled(boolean)} method.
+ * When data capture is not needed any more, the Visualizer should be disabled.
+ * <p>It is good practice to call the {@link #release()} method when the Visualizer is not used
+ * anymore to free up native resources associated to the Visualizer instance.
+ *
+ * {@hide Pending API council review}
+ */
+
+public class Visualizer {
+
+ static {
+ System.loadLibrary("audioeffect_jni");
+ native_init();
+ }
+
+ private final static String TAG = "Visualizer-JAVA";
+
+ /**
+ * State of a Visualizer object that was not successfully initialized upon creation
+ */
+ public static final int STATE_UNINITIALIZED = 0;
+ /**
+ * State of a Visualizer object that is ready to be used.
+ */
+ public static final int STATE_INITIALIZED = 1;
+ /**
+ * State of a Visualizer object that is active.
+ */
+ public static final int STATE_ENABLED = 2;
+
+ // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_Visualizer.cpp
+ protected static final int NATIVE_EVENT_PCM_CAPTURE = 0;
+ protected static final int NATIVE_EVENT_FFT_CAPTURE = 1;
+
+ // Error codes:
+ /**
+ * Successful operation.
+ */
+ public static final int SUCCESS = 0;
+ /**
+ * Unspecified error.
+ */
+ public static final int ERROR = -1;
+ /**
+ * Internal opreation status. Not returned by any method.
+ */
+ public static final int ALREADY_EXISTS = -2;
+ /**
+ * Operation failed due to bad object initialization.
+ */
+ public static final int ERROR_NO_INIT = -3;
+ /**
+ * Operation failed due to bad parameter value.
+ */
+ public static final int ERROR_BAD_VALUE = -4;
+ /**
+ * Operation failed because it was requested in wrong state.
+ */
+ public static final int ERROR_INVALID_OPERATION = -5;
+ /**
+ * Operation failed due to lack of memory.
+ */
+ public static final int ERROR_NO_MEMORY = -6;
+ /**
+ * Operation failed due to dead remote object.
+ */
+ public static final int ERROR_DEAD_OBJECT = -7;
+
+ //--------------------------------------------------------------------------
+ // Member variables
+ //--------------------
+ /**
+ * Indicates the state of the Visualizer instance
+ */
+ protected int mState = STATE_UNINITIALIZED;
+ /**
+ * Lock to synchronize access to mState
+ */
+ protected final Object mStateLock = new Object();
+ /**
+ * System wide unique Identifier of the visualizer engine used by this Visualizer instance
+ */
+ protected int mId;
+
+ /**
+ * Lock to protect listeners updates against event notifications
+ */
+ protected final Object mListenerLock = new Object();
+ /**
+ * Handler for events coming from the native code
+ */
+ protected NativeEventHandler mNativeEventHandler = null;
+ /**
+ * PCM and FFT capture listener registered by client
+ */
+ protected OnDataCaptureListener mCaptureListener = null;
+
+ // accessed by native methods
+ private int mNativeVisualizer;
+ private int mJniData;
+
+ //--------------------------------------------------------------------------
+ // Constructor, Finalize
+ //--------------------
+ /**
+ * Class constructor.
+ * @param audioSession System wide unique audio session identifier. If audioSession
+ * is not 0, the visualizer will be attached to the MediaPlayer or AudioTrack in the
+ * same audio session. Otherwise, the Visualizer will apply to the output mix.
+ *
+ * @throws java.lang.UnsupportedOperationException
+ * @throws java.lang.RuntimeException
+ */
+
+ public Visualizer(int audioSession)
+ throws UnsupportedOperationException, RuntimeException {
+ int[] id = new int[1];
+
+ synchronized (mStateLock) {
+ mState = STATE_UNINITIALIZED;
+ // native initialization
+ int result = native_setup(new WeakReference<Visualizer>(this), audioSession, id);
+ if (result != SUCCESS && result != ALREADY_EXISTS) {
+ Log.e(TAG, "Error code "+result+" when initializing Visualizer.");
+ switch (result) {
+ case ERROR_INVALID_OPERATION:
+ throw (new UnsupportedOperationException("Effect library not loaded"));
+ default:
+ throw (new RuntimeException("Cannot initialize Visualizer engine, error: "
+ +result));
+ }
+ }
+ mId = id[0];
+ if (native_getEnabled()) {
+ mState = STATE_ENABLED;
+ } else {
+ mState = STATE_INITIALIZED;
+ }
+ }
+ }
+
+ /**
+ * Releases the native Visualizer resources. It is a good practice to release the
+ * visualization engine when not in use.
+ */
+ public void release() {
+ synchronized (mStateLock) {
+ native_release();
+ mState = STATE_UNINITIALIZED;
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ native_finalize();
+ }
+
+ /**
+ * Enable or disable the visualization engine.
+ * @param enabled requested enable state
+ * @return {@link #SUCCESS} in case of success,
+ * {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} in case of failure.
+ * @throws IllegalStateException
+ */
+ public int setEnabled(boolean enabled)
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if ((enabled && mState != STATE_INITIALIZED) ||
+ (!enabled && mState != STATE_ENABLED)) {
+ throw(new IllegalStateException("setEnabled() called in wrong state: "+mState));
+ }
+ int status = native_setEnabled(enabled);
+ if (status == SUCCESS) {
+ mState = enabled ? STATE_ENABLED : STATE_INITIALIZED;
+ }
+ return status;
+ }
+ }
+
+ /**
+ * Get current activation state of the visualizer.
+ * @return true if the visualizer is active, false otherwise
+ */
+ public boolean getEnabled()
+ {
+ synchronized (mStateLock) {
+ if (mState == STATE_UNINITIALIZED) {
+ throw(new IllegalStateException("getEnabled() called in wrong state: "+mState));
+ }
+ return native_getEnabled();
+ }
+ }
+
+ /**
+ * Returns the capture size range.
+ * @return the mininum capture size is returned in first array element and the maximum in second
+ * array element.
+ */
+ public static native int[] getCaptureSizeRange();
+
+ /**
+ * Returns the maximum capture rate for the callback capture method. This is the maximum value
+ * for the rate parameter of the
+ * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
+ * @return the maximum capture rate expressed in milliHertz
+ */
+ public static native int getMaxCaptureRate();
+
+ /**
+ * Sets the capture size, i.e. the number of bytes returned by {@link #getWaveForm(byte[])} and
+ * {@link #getFft(byte[])} methods. The capture size must be a power of 2 in the range returned
+ * by {@link #getCaptureSizeRange()}.
+ * This method must not be called when the Visualizer is enabled.
+ * @param size requested capture size
+ * @return {@link #SUCCESS} in case of success,
+ * {@link #ERROR_BAD_VALUE} in case of failure.
+ * @throws IllegalStateException
+ */
+ public int setCaptureSize(int size)
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState != STATE_INITIALIZED) {
+ throw(new IllegalStateException("setCaptureSize() called in wrong state: "+mState));
+ }
+ return native_setCaptureSize(size);
+ }
+ }
+
+ /**
+ * Returns current capture size.
+ * @return the capture size in bytes.
+ */
+ public int getCaptureSize()
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState == STATE_UNINITIALIZED) {
+ throw(new IllegalStateException("getCaptureSize() called in wrong state: "+mState));
+ }
+ return native_getCaptureSize();
+ }
+ }
+
+ /**
+ * Returns the sampling rate of the captured audio.
+ * @return the sampling rate in milliHertz.
+ */
+ public int getSamplingRate()
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState == STATE_UNINITIALIZED) {
+ throw(new IllegalStateException("getSamplingRate() called in wrong state: "+mState));
+ }
+ return native_getSamplingRate();
+ }
+ }
+
+ /**
+ * Returns a waveform capture of currently playing audio content. The capture consists in
+ * a number of consecutive 8-bit (unsigned) mono PCM samples equal to the capture size returned
+ * by {@link #getCaptureSize()}.
+ * <p>This method must be called when the Visualizer is enabled.
+ * @param waveform array of bytes where the waveform should be returned
+ * @return {@link #SUCCESS} in case of success,
+ * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
+ * in case of failure.
+ * @throws IllegalStateException
+ */
+ public int getWaveForm(byte[] waveform)
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState != STATE_ENABLED) {
+ throw(new IllegalStateException("getWaveForm() called in wrong state: "+mState));
+ }
+ return native_getWaveForm(waveform);
+ }
+ }
+ /**
+ * Returns a frequency capture of currently playing audio content. The capture is a 8-bit
+ * magnitude FFT. Note that the size of the FFT is half of the specified capture size but both
+ * sides of the spectrum are returned yielding in a number of bytes equal to the capture size.
+ * {@see #getCaptureSize()}.
+ * <p>This method must be called when the Visualizer is enabled.
+ * @param fft array of bytes where the FFT should be returned
+ * @return {@link #SUCCESS} in case of success,
+ * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
+ * in case of failure.
+ * @throws IllegalStateException
+ */
+ public int getFft(byte[] fft)
+ throws IllegalStateException {
+ synchronized (mStateLock) {
+ if (mState != STATE_ENABLED) {
+ throw(new IllegalStateException("getFft() called in wrong state: "+mState));
+ }
+ return native_getFft(fft);
+ }
+ }
+
+ //---------------------------------------------------------
+ // Interface definitions
+ //--------------------
+ /**
+ * The OnDataCaptureListener interface defines methods called by the Visualizer to periodically
+ * update the audio visualization capture.
+ * The client application can implement this interface and register the listener with the
+ * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
+ */
+ public interface OnDataCaptureListener {
+ /**
+ * Method called when a new waveform capture is available.
+ * @param visualizer Visualizer object on which the listener is registered.
+ * @param waveform array of bytes containing the waveform representation.
+ * @param samplingRate sampling rate of the audio visualized.
+ */
+ void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate);
+
+ /**
+ * Method called when a new frequency capture is available.
+ * @param visualizer Visualizer object on which the listener is registered.
+ * @param fft array of bytes containing the frequency representation.
+ * @param samplingRate sampling rate of the audio visualized.
+ */
+ void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
+ }
+
+ /**
+ * Registers an OnDataCaptureListener interface and specifies the rate at which the capture
+ * should be updated as well as the type of capture requested.
+ * <p>Call this method with a null listener to stop receiving the capture updates.
+ * @param listener OnDataCaptureListener registered
+ * @param rate rate in milliHertz at which the capture should be updated
+ * @param waveform true if a waveform capture is requested: the onWaveFormDataCapture()
+ * method will be called on the OnDataCaptureListener interface.
+ * @param fft true if a frequency capture is requested: the onFftDataCapture() method will be
+ * called on the OnDataCaptureListener interface.
+ * @return {@link #SUCCESS} in case of success,
+ * {@link #ERROR_NO_INIT} or {@link #ERROR_BAD_VALUE} in case of failure.
+ */
+ public int setDataCaptureListener(OnDataCaptureListener listener,
+ int rate, boolean waveform, boolean fft) {
+ synchronized (mListenerLock) {
+ mCaptureListener = listener;
+ }
+ if (listener == null) {
+ // make sure capture callback is stopped in native code
+ waveform = false;
+ fft = false;
+ }
+ int status = native_setPeriodicCapture(rate, waveform, fft);
+ if (status == SUCCESS) {
+ if ((listener != null) && (mNativeEventHandler == null)) {
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mNativeEventHandler = new NativeEventHandler(this, looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mNativeEventHandler = new NativeEventHandler(this, looper);
+ } else {
+ mNativeEventHandler = null;
+ status = ERROR_NO_INIT;
+ }
+ }
+ }
+ return status;
+ }
+
+ /**
+ * Helper class to handle the forwarding of native events to the appropriate listeners
+ */
+ private class NativeEventHandler extends Handler
+ {
+ private Visualizer mVisualizer;
+
+ public NativeEventHandler(Visualizer v, Looper looper) {
+ super(looper);
+ mVisualizer = v;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (mVisualizer == null) {
+ return;
+ }
+ OnDataCaptureListener l = null;
+ synchronized (mListenerLock) {
+ l = mVisualizer.mCaptureListener;
+ }
+
+ if (l != null) {
+ byte[] data = (byte[])msg.obj;
+ int samplingRate = msg.arg1;
+ switch(msg.what) {
+ case NATIVE_EVENT_PCM_CAPTURE:
+ l.onWaveFormDataCapture(mVisualizer, data, samplingRate);
+ break;
+ case NATIVE_EVENT_FFT_CAPTURE:
+ l.onFftDataCapture(mVisualizer, data, samplingRate);
+ break;
+ default:
+ Log.e(TAG,"Unknown native event: "+msg.what);
+ break;
+ }
+ }
+ }
+ }
+
+ //---------------------------------------------------------
+ // Interface definitions
+ //--------------------
+
+ private static native final void native_init();
+
+ private native final int native_setup(Object audioeffect_this,
+ int audioSession,
+ int[] id);
+
+ private native final void native_finalize();
+
+ private native final void native_release();
+
+ private native final int native_setEnabled(boolean enabled);
+
+ private native final boolean native_getEnabled();
+
+ private native final int native_setCaptureSize(int size);
+
+ private native final int native_getCaptureSize();
+
+ private native final int native_getSamplingRate();
+
+ private native final int native_getWaveForm(byte[] waveform);
+
+ private native final int native_getFft(byte[] fft);
+
+ private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft);
+
+ //---------------------------------------------------------
+ // Java methods called from the native side
+ //--------------------
+ @SuppressWarnings("unused")
+ private static void postEventFromNative(Object effect_ref,
+ int what, int arg1, int arg2, Object obj) {
+ Visualizer visu = (Visualizer)((WeakReference)effect_ref).get();
+ if (visu == null) {
+ return;
+ }
+
+ if (visu.mNativeEventHandler != null) {
+ Message m = visu.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj);
+ visu.mNativeEventHandler.sendMessage(m);
+ }
+
+ }
+
+}
+
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index c5250d7..aedb54a 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -681,18 +681,6 @@ android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz)
}
static jint
-android_media_MediaPlayer_snoop(JNIEnv* env, jobject thiz, jobject data, jint kind) {
- jshort* ar = (jshort*)env->GetPrimitiveArrayCritical((jarray)data, 0);
- jsize len = env->GetArrayLength((jarray)data);
- int ret = 0;
- if (ar) {
- ret = MediaPlayer::snoop(ar, len, kind);
- env->ReleasePrimitiveArrayCritical((jarray)data, ar, 0);
- }
- return ret;
-}
-
-static jint
android_media_MediaPlayer_native_suspend_resume(
JNIEnv *env, jobject thiz, jboolean isSuspend) {
LOGV("suspend_resume(%d)", isSuspend);
@@ -757,7 +745,6 @@ static JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_MediaPlayer_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
- {"snoop", "([SI)I", (void *)android_media_MediaPlayer_snoop},
{"native_suspend_resume", "(Z)I", (void *)android_media_MediaPlayer_native_suspend_resume},
{"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id},
{"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
diff --git a/media/jni/audioeffect/Android.mk b/media/jni/audioeffect/Android.mk
index d03b63b..4c5cf71 100644
--- a/media/jni/audioeffect/Android.mk
+++ b/media/jni/audioeffect/Android.mk
@@ -2,7 +2,8 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- android_media_AudioEffect.cpp
+ android_media_AudioEffect.cpp \
+ android_media_Visualizer.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 17f2d8f..beb3dfc 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -323,8 +323,8 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t
priority,
effectCallback,
&lpJniStorage->mCallbackData,
- 0,
- sessionId);
+ sessionId,
+ 0);
if (lpAudioEffect == NULL) {
LOGE("Error creating AudioEffect");
goto setup_failure;
@@ -455,9 +455,8 @@ static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz)
env->SetIntField(thiz, fields.fidJniData, 0);
}
-
static jint
-android_media_AudioEffect_native_enable(JNIEnv *env, jobject thiz)
+android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
{
// retrieve the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
@@ -469,29 +468,11 @@ android_media_AudioEffect_native_enable(JNIEnv *env, jobject thiz)
return AUDIOEFFECT_ERROR_NO_INIT;
}
- return translateError(lpAudioEffect->enable());
+ return translateError(lpAudioEffect->setEnabled(enabled));
}
-
-static jint
-android_media_AudioEffect_native_disable(JNIEnv *env, jobject thiz)
-{
- // retrieve the AudioEffect object
- AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
- thiz, fields.fidNativeAudioEffect);
-
- if (lpAudioEffect == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve AudioEffect pointer for disable()");
- return AUDIOEFFECT_ERROR_NO_INIT;
- }
-
- return translateError(lpAudioEffect->disable());
-}
-
-
static jboolean
-android_media_AudioEffect_native_getEnable(JNIEnv *env, jobject thiz)
+android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz)
{
// retrieve the AudioEffect object
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
@@ -503,7 +484,7 @@ android_media_AudioEffect_native_getEnable(JNIEnv *env, jobject thiz)
return false;
}
- return (jboolean)lpAudioEffect->isEnabled();
+ return (jboolean)lpAudioEffect->getEnabled();
}
@@ -516,7 +497,7 @@ android_media_AudioEffect_native_hasControl(JNIEnv *env, jobject thiz)
if (lpAudioEffect == NULL) {
jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve AudioEffect pointer for getEnabled()");
+ "Unable to retrieve AudioEffect pointer for hasControl()");
return false;
}
@@ -817,9 +798,8 @@ static JNINativeMethod gMethods[] = {
(void *)android_media_AudioEffect_native_setup},
{"native_finalize", "()V", (void *)android_media_AudioEffect_native_finalize},
{"native_release", "()V", (void *)android_media_AudioEffect_native_release},
- {"native_enable", "()I", (void *)android_media_AudioEffect_native_enable},
- {"native_disable", "()I", (void *)android_media_AudioEffect_native_disable},
- {"native_getEnable", "()Z", (void *)android_media_AudioEffect_native_getEnable},
+ {"native_setEnabled", "(Z)I", (void *)android_media_AudioEffect_native_setEnabled},
+ {"native_getEnabled", "()Z", (void *)android_media_AudioEffect_native_getEnabled},
{"native_hasControl", "()Z", (void *)android_media_AudioEffect_native_hasControl},
{"native_setParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_setParameter},
{"native_getParameter", "(I[B[I[B)I", (void *)android_media_AudioEffect_native_getParameter},
@@ -830,6 +810,8 @@ static JNINativeMethod gMethods[] = {
// ----------------------------------------------------------------------------
+extern int register_android_media_visualizer(JNIEnv *env);
+
int register_android_media_AudioEffect(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
@@ -852,6 +834,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
goto bail;
}
+ if (register_android_media_visualizer(env) < 0) {
+ LOGE("ERROR: Visualizer native registration failed\n");
+ goto bail;
+ }
+
/* success -- return valid version number */
result = JNI_VERSION_1_4;
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
new file mode 100644
index 0000000..31119f8
--- /dev/null
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -0,0 +1,507 @@
+/*
+ * 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 <stdio.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "visualizers-JNI"
+
+#include <utils/Log.h>
+#include <nativehelper/jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include "media/Visualizer.h"
+
+using namespace android;
+
+#define VISUALIZER_SUCCESS 0
+#define VISUALIZER_ERROR -1
+#define VISUALIZER_ERROR_ALREADY_EXISTS -2
+#define VISUALIZER_ERROR_NO_INIT -3
+#define VISUALIZER_ERROR_BAD_VALUE -4
+#define VISUALIZER_ERROR_INVALID_OPERATION -5
+#define VISUALIZER_ERROR_NO_MEMORY -6
+#define VISUALIZER_ERROR_DEAD_OBJECT -7
+
+#define NATIVE_EVENT_PCM_CAPTURE 0
+#define NATIVE_EVENT_FFT_CAPTURE 1
+
+// ----------------------------------------------------------------------------
+static const char* const kClassPathName = "android/media/Visualizer";
+
+struct fields_t {
+ // these fields provide access from C++ to the...
+ jclass clazzEffect; // Visualizer class
+ jmethodID midPostNativeEvent; // event post callback method
+ jfieldID fidNativeVisualizer; // stores in Java the native Visualizer object
+ jfieldID fidJniData; // stores in Java additional resources used by the native Visualizer
+};
+static fields_t fields;
+
+struct visualizer_callback_cookie {
+ jclass visualizer_class; // Visualizer class
+ jobject visualizer_ref; // Visualizer object instance
+ };
+
+// ----------------------------------------------------------------------------
+class visualizerJniStorage {
+ public:
+ visualizer_callback_cookie mCallbackData;
+
+ visualizerJniStorage() {
+ }
+
+ ~visualizerJniStorage() {
+ }
+
+};
+
+
+static jint translateError(int code) {
+ switch(code) {
+ case NO_ERROR:
+ return VISUALIZER_SUCCESS;
+ case ALREADY_EXISTS:
+ return VISUALIZER_ERROR_ALREADY_EXISTS;
+ case NO_INIT:
+ return VISUALIZER_ERROR_NO_INIT;
+ case BAD_VALUE:
+ return VISUALIZER_ERROR_BAD_VALUE;
+ case INVALID_OPERATION:
+ return VISUALIZER_ERROR_INVALID_OPERATION;
+ case NO_MEMORY:
+ return VISUALIZER_ERROR_NO_MEMORY;
+ case DEAD_OBJECT:
+ return VISUALIZER_ERROR_DEAD_OBJECT;
+ default:
+ return VISUALIZER_ERROR;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static void captureCallback(void* user,
+ uint32_t waveformSize,
+ uint8_t *waveform,
+ uint32_t fftSize,
+ uint8_t *fft,
+ uint32_t samplingrate) {
+
+ int arg1 = 0;
+ int arg2 = 0;
+ size_t size;
+
+ visualizer_callback_cookie *callbackInfo = (visualizer_callback_cookie *)user;
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ LOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
+ callbackInfo,
+ callbackInfo->visualizer_ref,
+ callbackInfo->visualizer_class);
+
+ if (!user || !env) {
+ LOGW("captureCallback error user %p, env %p", user, env);
+ return;
+ }
+
+ if (waveformSize != 0 && waveform != NULL) {
+ jbyteArray jArray = env->NewByteArray(waveformSize);
+ if (jArray != NULL) {
+ jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
+ memcpy(nArray, waveform, waveformSize);
+ env->ReleaseByteArrayElements(jArray, nArray, 0);
+ env->CallStaticVoidMethod(
+ callbackInfo->visualizer_class,
+ fields.midPostNativeEvent,
+ callbackInfo->visualizer_ref,
+ NATIVE_EVENT_PCM_CAPTURE,
+ samplingrate,
+ 0,
+ jArray);
+ }
+ }
+
+ if (fftSize != 0 && fft != NULL) {
+ jbyteArray jArray = env->NewByteArray(fftSize);
+ if (jArray != NULL) {
+ jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
+ memcpy(nArray, fft, fftSize);
+ env->ReleaseByteArrayElements(jArray, nArray, 0);
+ env->CallStaticVoidMethod(
+ callbackInfo->visualizer_class,
+ fields.midPostNativeEvent,
+ callbackInfo->visualizer_ref,
+ NATIVE_EVENT_FFT_CAPTURE,
+ samplingrate,
+ 0,
+ jArray);
+ env->DeleteLocalRef(jArray);
+ }
+ }
+
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+}
+
+static Visualizer *getVisualizer(JNIEnv* env, jobject thiz)
+{
+ Visualizer *v = (Visualizer *)env->GetIntField(
+ thiz, fields.fidNativeVisualizer);
+ if (v == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve Visualizer pointer");
+ }
+ return v;
+}
+
+// ----------------------------------------------------------------------------
+// This function gets some field IDs, which in turn causes class initialization.
+// It is called from a static block in Visualizer, which won't run until the
+// first time an instance of this class is used.
+static void
+android_media_visualizer_native_init(JNIEnv *env)
+{
+
+ LOGV("android_media_visualizer_native_init");
+
+ fields.clazzEffect = NULL;
+
+ // Get the Visualizer class
+ jclass clazz = env->FindClass(kClassPathName);
+ if (clazz == NULL) {
+ LOGE("Can't find %s", kClassPathName);
+ return;
+ }
+
+ fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
+
+ // Get the postEvent method
+ fields.midPostNativeEvent = env->GetStaticMethodID(
+ fields.clazzEffect,
+ "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+ if (fields.midPostNativeEvent == NULL) {
+ LOGE("Can't find Visualizer.%s", "postEventFromNative");
+ return;
+ }
+
+ // Get the variables fields
+ // nativeTrackInJavaObj
+ fields.fidNativeVisualizer = env->GetFieldID(
+ fields.clazzEffect,
+ "mNativeVisualizer", "I");
+ if (fields.fidNativeVisualizer == NULL) {
+ LOGE("Can't find Visualizer.%s", "mNativeVisualizer");
+ return;
+ }
+ // fidJniData;
+ fields.fidJniData = env->GetFieldID(
+ fields.clazzEffect,
+ "mJniData", "I");
+ if (fields.fidJniData == NULL) {
+ LOGE("Can't find Visualizer.%s", "mJniData");
+ return;
+ }
+
+}
+
+
+static jint
+android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
+ jint sessionId, jintArray jId)
+{
+ LOGV("android_media_visualizer_native_setup");
+ visualizerJniStorage* lpJniStorage = NULL;
+ int lStatus = VISUALIZER_ERROR_NO_MEMORY;
+ Visualizer* lpVisualizer = NULL;
+ jint* nId = NULL;
+
+ lpJniStorage = new visualizerJniStorage();
+ if (lpJniStorage == NULL) {
+ LOGE("setup: Error creating JNI Storage");
+ goto setup_failure;
+ }
+
+ lpJniStorage->mCallbackData.visualizer_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
+ // we use a weak reference so the Visualizer object can be garbage collected.
+ lpJniStorage->mCallbackData.visualizer_ref = env->NewGlobalRef(weak_this);
+
+ LOGV("setup: lpJniStorage: %p visualizer_ref %p visualizer_class %p, &mCallbackData %p",
+ lpJniStorage,
+ lpJniStorage->mCallbackData.visualizer_ref,
+ lpJniStorage->mCallbackData.visualizer_class,
+ &lpJniStorage->mCallbackData);
+
+ if (jId) {
+ nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
+ if (nId == NULL) {
+ LOGE("setup: Error retrieving id pointer");
+ lStatus = VISUALIZER_ERROR_BAD_VALUE;
+ goto setup_failure;
+ }
+ } else {
+ LOGE("setup: NULL java array for id pointer");
+ lStatus = VISUALIZER_ERROR_BAD_VALUE;
+ goto setup_failure;
+ }
+
+ // create the native Visualizer object
+ lpVisualizer = new Visualizer(0,
+ NULL,
+ NULL,
+ sessionId);
+ if (lpVisualizer == NULL) {
+ LOGE("Error creating Visualizer");
+ goto setup_failure;
+ }
+
+ lStatus = translateError(lpVisualizer->initCheck());
+ if (lStatus != VISUALIZER_SUCCESS && lStatus != VISUALIZER_ERROR_ALREADY_EXISTS) {
+ LOGE("Visualizer initCheck failed %d", lStatus);
+ goto setup_failure;
+ }
+
+ nId[0] = lpVisualizer->id();
+
+ env->ReleasePrimitiveArrayCritical(jId, nId, 0);
+ nId = NULL;
+
+ env->SetIntField(thiz, fields.fidNativeVisualizer, (int)lpVisualizer);
+
+ env->SetIntField(thiz, fields.fidJniData, (int)lpJniStorage);
+
+ return VISUALIZER_SUCCESS;
+
+ // failures:
+setup_failure:
+
+ if (nId != NULL) {
+ env->ReleasePrimitiveArrayCritical(jId, nId, 0);
+ }
+
+ if (lpVisualizer) {
+ delete lpVisualizer;
+ }
+ env->SetIntField(thiz, fields.fidNativeVisualizer, 0);
+
+ if (lpJniStorage) {
+ delete lpJniStorage;
+ }
+ env->SetIntField(thiz, fields.fidJniData, 0);
+
+ return lStatus;
+}
+
+// ----------------------------------------------------------------------------
+static void android_media_visualizer_native_finalize(JNIEnv *env, jobject thiz) {
+ LOGV("android_media_visualizer_native_finalize jobject: %x\n", (int)thiz);
+
+ // delete the Visualizer object
+ Visualizer* lpVisualizer = (Visualizer *)env->GetIntField(
+ thiz, fields.fidNativeVisualizer);
+ if (lpVisualizer) {
+ LOGV("deleting Visualizer: %x\n", (int)lpVisualizer);
+ delete lpVisualizer;
+ }
+
+ // delete the JNI data
+ visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(
+ thiz, fields.fidJniData);
+ if (lpJniStorage) {
+ LOGV("deleting pJniStorage: %x\n", (int)lpJniStorage);
+ delete lpJniStorage;
+ }
+}
+
+// ----------------------------------------------------------------------------
+static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) {
+
+ // do everything a call to finalize would
+ android_media_visualizer_native_finalize(env, thiz);
+ // + reset the native resources in the Java object so any attempt to access
+ // them after a call to release fails.
+ env->SetIntField(thiz, fields.fidNativeVisualizer, 0);
+ env->SetIntField(thiz, fields.fidJniData, 0);
+}
+
+static jint
+android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return VISUALIZER_ERROR_NO_INIT;
+ }
+
+ return translateError(lpVisualizer->setEnabled(enabled));
+}
+
+static jboolean
+android_media_visualizer_native_getEnabled(JNIEnv *env, jobject thiz)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return false;
+ }
+
+ return (jboolean)lpVisualizer->getEnabled();
+}
+
+static jintArray
+android_media_visualizer_native_getCaptureSizeRange(JNIEnv *env, jobject thiz)
+{
+ jintArray jRange = env->NewIntArray(2);
+ jint *nRange = env->GetIntArrayElements(jRange, NULL);
+ nRange[0] = Visualizer::getMinCaptureSize();
+ nRange[1] = Visualizer::getMaxCaptureSize();
+ LOGV("getCaptureSizeRange() min %d max %d", nRange[0], nRange[1]);
+ env->ReleaseIntArrayElements(jRange, nRange, 0);
+ return jRange;
+}
+
+static jint
+android_media_visualizer_native_getMaxCaptureRate(JNIEnv *env, jobject thiz)
+{
+ return Visualizer::getMaxCaptureRate();
+}
+
+static jint
+android_media_visualizer_native_setCaptureSize(JNIEnv *env, jobject thiz, jint size)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return VISUALIZER_ERROR_NO_INIT;
+ }
+
+ return translateError(lpVisualizer->setCaptureSize(size));
+}
+
+static jint
+android_media_visualizer_native_getCaptureSize(JNIEnv *env, jobject thiz)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return -1;
+ }
+ return lpVisualizer->getCaptureSize();
+}
+
+static jint
+android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return -1;
+ }
+ return lpVisualizer->getSamplingRate();
+}
+
+static jint
+android_media_visualizer_native_getWaveForm(JNIEnv *env, jobject thiz, jbyteArray jWaveform)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return VISUALIZER_ERROR_NO_INIT;
+ }
+
+ jbyte* nWaveform = (jbyte *) env->GetPrimitiveArrayCritical(jWaveform, NULL);
+ if (nWaveform == NULL) {
+ return VISUALIZER_ERROR_NO_MEMORY;
+ }
+ jint status = translateError(lpVisualizer->getWaveForm((uint8_t *)nWaveform));
+
+ env->ReleasePrimitiveArrayCritical(jWaveform, nWaveform, 0);
+
+ return status;
+}
+
+static jint
+android_media_visualizer_native_getFft(JNIEnv *env, jobject thiz, jbyteArray jFft)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return VISUALIZER_ERROR_NO_INIT;
+ }
+
+ jbyte* nFft = (jbyte *) env->GetPrimitiveArrayCritical(jFft, NULL);
+ if (nFft == NULL) {
+ return VISUALIZER_ERROR_NO_MEMORY;
+ }
+ jint status = translateError(lpVisualizer->getFft((uint8_t *)nFft));
+
+ env->ReleasePrimitiveArrayCritical(jFft, nFft, 0);
+
+ return status;
+}
+
+static jint
+android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean jWaveform, jboolean jFft)
+{
+ Visualizer* lpVisualizer = getVisualizer(env, thiz);
+ if (lpVisualizer == NULL) {
+ return VISUALIZER_ERROR_NO_INIT;
+ }
+ visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(thiz,
+ fields.fidJniData);
+ if (lpJniStorage == NULL) {
+ return VISUALIZER_ERROR_NO_INIT;
+ }
+
+ LOGV("setPeriodicCapture: rate %d, jWaveform %d jFft %d",
+ rate,
+ jWaveform,
+ jFft);
+
+ uint32_t flags = Visualizer::CAPTURE_CALL_JAVA;
+ if (jWaveform) flags |= Visualizer::CAPTURE_WAVEFORM;
+ if (jFft) flags |= Visualizer::CAPTURE_FFT;
+ Visualizer::capture_cbk_t cbk = captureCallback;
+ if (!jWaveform && !jFft) cbk = NULL;
+
+ return translateError(lpVisualizer->setCaptureCallBack(cbk,
+ &lpJniStorage->mCallbackData,
+ flags,
+ rate));
+}
+
+// ----------------------------------------------------------------------------
+
+// Dalvik VM type signatures
+static JNINativeMethod gMethods[] = {
+ {"native_init", "()V", (void *)android_media_visualizer_native_init},
+ {"native_setup", "(Ljava/lang/Object;I[I)I",
+ (void *)android_media_visualizer_native_setup},
+ {"native_finalize", "()V", (void *)android_media_visualizer_native_finalize},
+ {"native_release", "()V", (void *)android_media_visualizer_native_release},
+ {"native_setEnabled", "(Z)I", (void *)android_media_visualizer_native_setEnabled},
+ {"native_getEnabled", "()Z", (void *)android_media_visualizer_native_getEnabled},
+ {"getCaptureSizeRange", "()[I", (void *)android_media_visualizer_native_getCaptureSizeRange},
+ {"getMaxCaptureRate", "()I", (void *)android_media_visualizer_native_getMaxCaptureRate},
+ {"native_setCaptureSize", "(I)I", (void *)android_media_visualizer_native_setCaptureSize},
+ {"native_getCaptureSize", "()I", (void *)android_media_visualizer_native_getCaptureSize},
+ {"native_getSamplingRate", "()I", (void *)android_media_visualizer_native_getSamplingRate},
+ {"native_getWaveForm", "([B)I", (void *)android_media_visualizer_native_getWaveForm},
+ {"native_getFft", "([B)I", (void *)android_media_visualizer_native_getFft},
+ {"native_setPeriodicCapture","(IZZ)I",(void *)android_media_setPeriodicCapture},
+};
+
+// ----------------------------------------------------------------------------
+
+int register_android_media_visualizer(JNIEnv *env)
+{
+ return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
diff --git a/media/libeffects/Android.mk b/media/libeffects/Android.mk
index b5f1d42..54e87f3 100644
--- a/media/libeffects/Android.mk
+++ b/media/libeffects/Android.mk
@@ -94,3 +94,33 @@ LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
endif
+
+
+# Visualizer library
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ EffectVisualizer.cpp
+
+LOCAL_CFLAGS+= -O2
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE:= libvisualizer
+
+ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
+LOCAL_LDLIBS += -ldl
+endif
+
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, graphics corecg)
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
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/libeffects/EffectVisualizer.cpp b/media/libeffects/EffectVisualizer.cpp
new file mode 100644
index 0000000..f27e296
--- /dev/null
+++ b/media/libeffects/EffectVisualizer.cpp
@@ -0,0 +1,401 @@
+/*
+ * 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 "Visualizer"
+//#define LOG_NDEBUG 0
+#include <cutils/log.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <new>
+#include <media/EffectVisualizerApi.h>
+
+namespace android {
+
+// effect_interface_t interface implementation for visualizer effect
+extern "C" const struct effect_interface_s gVisualizerInterface;
+
+// Google Visualizer UUID: d069d9e0-8329-11df-9168-0002a5d5c51b
+const effect_descriptor_t gVisualizerDescriptor = {
+ {0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type
+ {0xd069d9e0, 0x8329, 0x11df, 0x9168, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
+ EFFECT_API_VERSION,
+ (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
+ 0, // TODO
+ 1,
+ "Visualizer",
+ "Google Inc.",
+};
+
+enum visualizer_state_e {
+ VISUALIZER_STATE_UNINITIALIZED,
+ VISUALIZER_STATE_INITIALIZED,
+ VISUALIZER_STATE_ACTIVE,
+};
+
+struct VisualizerContext {
+ const struct effect_interface_s *mItfe;
+ effect_config_t mConfig;
+ uint32_t mState;
+ uint32_t mCaptureIdx;
+ uint32_t mCaptureSize;
+ uint32_t mCurrentBuf;
+ uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX];
+};
+
+
+//
+//--- Local functions
+//
+
+void Visualizer_reset(VisualizerContext *pContext)
+{
+ pContext->mCaptureIdx = 0;
+ pContext->mCurrentBuf = 0;
+ memset(pContext->mCaptureBuf[0], 0, VISUALIZER_CAPTURE_SIZE_MAX);
+ memset(pContext->mCaptureBuf[1], 0, VISUALIZER_CAPTURE_SIZE_MAX);
+}
+
+//----------------------------------------------------------------------------
+// Visualizer_configure()
+//----------------------------------------------------------------------------
+// Purpose: Set input and output audio configuration.
+//
+// Inputs:
+// pContext: effect engine context
+// pConfig: pointer to effect_config_t structure holding input and output
+// configuration parameters
+//
+// Outputs:
+//
+//----------------------------------------------------------------------------
+
+int Visualizer_configure(VisualizerContext *pContext, effect_config_t *pConfig)
+{
+ LOGV("Visualizer_configure start");
+
+ if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL;
+ if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL;
+ if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL;
+ if (pConfig->inputCfg.channels != CHANNEL_STEREO) return -EINVAL;
+ if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
+ pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
+ if (pConfig->inputCfg.format != SAMPLE_FORMAT_PCM_S15) return -EINVAL;
+
+ memcpy(&pContext->mConfig, pConfig, sizeof(effect_config_t));
+
+ Visualizer_reset(pContext);
+
+ return 0;
+}
+
+
+//----------------------------------------------------------------------------
+// Visualizer_init()
+//----------------------------------------------------------------------------
+// Purpose: Initialize engine with default configuration.
+//
+// Inputs:
+// pContext: effect engine context
+//
+// Outputs:
+//
+//----------------------------------------------------------------------------
+
+int Visualizer_init(VisualizerContext *pContext)
+{
+ pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+ pContext->mConfig.inputCfg.channels = CHANNEL_STEREO;
+ pContext->mConfig.inputCfg.format = SAMPLE_FORMAT_PCM_S15;
+ pContext->mConfig.inputCfg.samplingRate = 44100;
+ pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL;
+ pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
+ pContext->mConfig.inputCfg.bufferProvider.cookie = NULL;
+ pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
+ pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+ pContext->mConfig.outputCfg.channels = CHANNEL_STEREO;
+ pContext->mConfig.outputCfg.format = SAMPLE_FORMAT_PCM_S15;
+ pContext->mConfig.outputCfg.samplingRate = 44100;
+ pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL;
+ pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
+ pContext->mConfig.outputCfg.bufferProvider.cookie = NULL;
+ pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
+
+ pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX;
+
+ Visualizer_configure(pContext, &pContext->mConfig);
+
+ return 0;
+}
+
+//
+//--- Effect Library Interface Implementation
+//
+
+extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects) {
+ *pNumEffects = 1;
+ return 0;
+}
+
+extern "C" int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) {
+ if (pDescriptor == NULL) {
+ return -EINVAL;
+ }
+ if (index > 0) {
+ return -EINVAL;
+ }
+ memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t));
+ return 0;
+}
+
+extern "C" int EffectCreate(effect_uuid_t *uuid,
+ int32_t sessionId,
+ int32_t ioId,
+ effect_interface_t *pInterface) {
+ int ret;
+ int i;
+
+ if (pInterface == NULL || uuid == NULL) {
+ return -EINVAL;
+ }
+
+ if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) {
+ return -EINVAL;
+ }
+
+ VisualizerContext *pContext = new VisualizerContext;
+
+ pContext->mItfe = &gVisualizerInterface;
+ pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
+
+ ret = Visualizer_init(pContext);
+ if (ret < 0) {
+ LOGW("EffectCreate() init failed");
+ delete pContext;
+ return ret;
+ }
+
+ *pInterface = (effect_interface_t)pContext;
+
+ pContext->mState = VISUALIZER_STATE_INITIALIZED;
+
+ LOGV("EffectCreate %p", pContext);
+
+ return 0;
+
+}
+
+extern "C" int EffectRelease(effect_interface_t interface) {
+ VisualizerContext * pContext = (VisualizerContext *)interface;
+
+ LOGV("EffectRelease %p", interface);
+ if (pContext == NULL) {
+ return -EINVAL;
+ }
+ pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
+ delete pContext;
+
+ return 0;
+}
+
+//
+//--- Effect Control Interface Implementation
+//
+
+static inline int16_t clamp16(int32_t sample)
+{
+ if ((sample>>15) ^ (sample>>31))
+ sample = 0x7FFF ^ (sample>>31);
+ return sample;
+}
+
+extern "C" int Visualizer_process(
+ effect_interface_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
+{
+ android::VisualizerContext * pContext = (android::VisualizerContext *)self;
+
+ if (pContext == NULL) {
+ return -EINVAL;
+ }
+ if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
+ return -ENOSYS;
+ }
+
+ if (inBuffer == NULL || inBuffer->raw == NULL ||
+ outBuffer == NULL || outBuffer->raw == NULL ||
+ inBuffer->frameCount != outBuffer->frameCount ||
+ inBuffer->frameCount == 0) {
+ return -EINVAL;
+ }
+
+ // all code below assumes stereo 16 bit PCM output and input
+ uint32_t captIdx;
+ uint32_t inIdx;
+ uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf];
+ for (inIdx = 0, captIdx = pContext->mCaptureIdx;
+ inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize;
+ inIdx++, captIdx++) {
+ int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
+ smp = (smp + (1 << 8)) >> 9;
+ buf[captIdx] = ((uint8_t)smp)^0x80;
+ }
+ pContext->mCaptureIdx = captIdx;
+
+ // go to next buffer when buffer full
+ if (pContext->mCaptureIdx == pContext->mCaptureSize) {
+ pContext->mCurrentBuf ^= 1;
+ pContext->mCaptureIdx = 0;
+ }
+
+ if (inBuffer->raw != outBuffer->raw) {
+ if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+ for (size_t i = 0; i < outBuffer->frameCount*2; i++) {
+ outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]);
+ }
+ } else {
+ memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t));
+ }
+ }
+ return 0;
+} // end Visualizer_process
+
+extern "C" int Visualizer_command(effect_interface_t self, int cmdCode, int cmdSize,
+ void *pCmdData, int *replySize, void *pReplyData) {
+
+ android::VisualizerContext * pContext = (android::VisualizerContext *)self;
+ int retsize;
+
+ if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) {
+ return -EINVAL;
+ }
+
+// LOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize);
+
+ switch (cmdCode) {
+ case EFFECT_CMD_INIT:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ *(int *) pReplyData = Visualizer_init(pContext);
+ break;
+ case EFFECT_CMD_CONFIGURE:
+ if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
+ || pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ *(int *) pReplyData = Visualizer_configure(pContext,
+ (effect_config_t *) pCmdData);
+ break;
+ case EFFECT_CMD_RESET:
+ Visualizer_reset(pContext);
+ break;
+ case EFFECT_CMD_ENABLE:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ if (pContext->mState != VISUALIZER_STATE_INITIALIZED) {
+ return -ENOSYS;
+ }
+ pContext->mState = VISUALIZER_STATE_ACTIVE;
+ LOGV("EFFECT_CMD_ENABLE() OK");
+ *(int *)pReplyData = 0;
+ break;
+ case EFFECT_CMD_DISABLE:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
+ return -ENOSYS;
+ }
+ pContext->mState = VISUALIZER_STATE_INITIALIZED;
+ LOGV("EFFECT_CMD_DISABLE() OK");
+ *(int *)pReplyData = 0;
+ break;
+ case EFFECT_CMD_GET_PARAM: {
+ if (pCmdData == NULL ||
+ cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
+ pReplyData == NULL ||
+ *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
+ return -EINVAL;
+ }
+ memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
+ effect_param_t *p = (effect_param_t *)pReplyData;
+ p->status = 0;
+ *replySize = sizeof(effect_param_t) + sizeof(uint32_t);
+ if (p->psize != sizeof(uint32_t) ||
+ *(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) {
+ p->status = -EINVAL;
+ break;
+ }
+ LOGV("get mCaptureSize = %d", pContext->mCaptureSize);
+ *((uint32_t *)p->data + 1) = pContext->mCaptureSize;
+ p->vsize = sizeof(uint32_t);
+ *replySize += sizeof(uint32_t);
+ } break;
+ case EFFECT_CMD_SET_PARAM: {
+ if (pCmdData == NULL ||
+ cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
+ pReplyData == NULL || *replySize != sizeof(int32_t)) {
+ return -EINVAL;
+ }
+ *(int32_t *)pReplyData = 0;
+ effect_param_t *p = (effect_param_t *)pCmdData;
+ if (p->psize != sizeof(uint32_t) ||
+ p->vsize != sizeof(uint32_t) ||
+ *(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) {
+ *(int32_t *)pReplyData = -EINVAL;
+ break;;
+ }
+ pContext->mCaptureSize = *((uint32_t *)p->data + 1);
+ LOGV("set mCaptureSize = %d", pContext->mCaptureSize);
+ } break;
+ case EFFECT_CMD_SET_DEVICE:
+ case EFFECT_CMD_SET_VOLUME:
+ case EFFECT_CMD_SET_AUDIO_MODE:
+ break;
+
+
+ case VISU_CMD_CAPTURE:
+ if (pReplyData == NULL || *replySize != (int)pContext->mCaptureSize) {
+ LOGV("VISU_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d",
+ *replySize, pContext->mCaptureSize);
+ return -EINVAL;
+ }
+ if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
+ memcpy(pReplyData,
+ pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
+ pContext->mCaptureSize);
+ } else {
+ memset(pReplyData, 0x80, pContext->mCaptureSize);
+ }
+ break;
+
+ default:
+ LOGW("Visualizer_command invalid command %d",cmdCode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+// effect_interface_t interface implementation for visualizer effect
+const struct effect_interface_s gVisualizerInterface = {
+ Visualizer_process,
+ Visualizer_command
+};
+
+} // namespace
+
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index de9e51d..977e6be 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -30,7 +30,8 @@ LOCAL_SRC_FILES:= \
MediaProfiles.cpp \
IEffect.cpp \
IEffectClient.cpp \
- AudioEffect.cpp
+ AudioEffect.cpp \
+ Visualizer.cpp
LOCAL_SHARED_LIBRARIES := \
libui libcutils libutils libbinder libsonivox libicuuc libexpat libsurfaceflinger_client libcamera_client
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 4afa2dc..df0f73b 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -52,7 +52,7 @@ AudioEffect::AudioEffect(const effect_uuid_t *type,
)
: mStatus(NO_INIT)
{
- mStatus = set(type, uuid, priority, cbf, user, output, sessionId);
+ mStatus = set(type, uuid, priority, cbf, user, sessionId, output);
}
AudioEffect::AudioEffect(const char *typeStr,
@@ -84,7 +84,7 @@ AudioEffect::AudioEffect(const char *typeStr,
}
}
- mStatus = set(pType, pUuid, priority, cbf, user, output, sessionId);
+ mStatus = set(pType, pUuid, priority, cbf, user, sessionId, output);
}
status_t AudioEffect::set(const effect_uuid_t *type,
@@ -171,7 +171,7 @@ AudioEffect::~AudioEffect()
LOGV("Destructor %p", this);
if (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS) {
- disable();
+ setEnabled(false);
if (mIEffect != NULL) {
mIEffect->disconnect();
mIEffect->asBinder()->unlinkToDeath(mIEffectClient);
@@ -196,36 +196,28 @@ effect_descriptor_t AudioEffect::descriptor() const
return mDescriptor;
}
-bool AudioEffect::isEnabled() const
+bool AudioEffect::getEnabled() const
{
return (mEnabled != 0);
}
-status_t AudioEffect::enable()
+status_t AudioEffect::setEnabled(bool enabled)
{
if (mStatus != NO_ERROR) {
return INVALID_OPERATION;
}
- LOGV("enable %p", this);
- if (android_atomic_or(1, &mEnabled) == 0) {
- return mIEffect->enable();
- }
-
- return INVALID_OPERATION;
-}
-
-status_t AudioEffect::disable()
-{
- if (mStatus != NO_ERROR) {
- return INVALID_OPERATION;
- }
- LOGV("disable %p", this);
-
- if (android_atomic_and(~1, &mEnabled) == 1) {
- return mIEffect->disable();
+ if (enabled) {
+ LOGV("enable %p", this);
+ if (android_atomic_or(1, &mEnabled) == 0) {
+ return mIEffect->enable();
+ }
+ } else {
+ LOGV("disable %p", this);
+ if (android_atomic_and(~1, &mEnabled) == 1) {
+ return mIEffect->disable();
+ }
}
-
return INVALID_OPERATION;
}
@@ -349,7 +341,7 @@ void AudioEffect::controlStatusChanged(bool controlGranted)
void AudioEffect::enableStatusChanged(bool enabled)
{
- LOGV("enableStatusChanged %p enabled %d", this, enabled);
+ LOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf);
if (mStatus == ALREADY_EXISTS) {
mEnabled = enabled;
if (mCbf) {
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index 1ae222e..4abfa75 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -35,8 +35,7 @@ enum {
DECODE_FD,
CREATE_MEDIA_RECORDER,
CREATE_METADATA_RETRIEVER,
- GET_OMX,
- SNOOP
+ GET_OMX
};
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
@@ -134,14 +133,6 @@ public:
return interface_cast<IMemory>(reply.readStrongBinder());
}
- virtual sp<IMemory> snoop()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
- remote()->transact(SNOOP, data, &reply);
- return interface_cast<IMemory>(reply.readStrongBinder());
- }
-
virtual sp<IOMX> getOMX() {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
@@ -221,12 +212,6 @@ status_t BnMediaPlayerService::onTransact(
reply->writeStrongBinder(player->asBinder());
return NO_ERROR;
} break;
- case SNOOP: {
- CHECK_INTERFACE(IMediaPlayerService, data, reply);
- sp<IMemory> snooped_audio = snoop();
- reply->writeStrongBinder(snooped_audio->asBinder());
- return NO_ERROR;
- } break;
case CREATE_MEDIA_RECORDER: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
pid_t pid = data.readInt32();
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
new file mode 100644
index 0000000..47e96e5
--- /dev/null
+++ b/media/libmedia/Visualizer.cpp
@@ -0,0 +1,330 @@
+/*
+**
+** Copyright 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_NDEBUG 0
+#define LOG_TAG "Visualizer"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <limits.h>
+
+#include <media/Visualizer.h>
+
+extern "C" {
+#define FLOATING_POINT 1
+#include "fftwrap.h"
+}
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+Visualizer::Visualizer (int32_t priority,
+ effect_callback_t cbf,
+ void* user,
+ int sessionId)
+ : AudioEffect(SL_IID_VISUALIZATION, NULL, priority, cbf, user, sessionId),
+ mCaptureRate(CAPTURE_RATE_DEF),
+ mCaptureSize(CAPTURE_SIZE_DEF),
+ mSampleRate(44100000),
+ mCaptureCallBack(NULL),
+ mCaptureCbkUser(NULL)
+{
+ initCaptureSize();
+ if (mCaptureSize != 0) {
+ mFftTable = spx_fft_init(mCaptureSize);
+ } else {
+ mFftTable = NULL;
+ }
+}
+
+Visualizer::~Visualizer()
+{
+ if (mFftTable != NULL) {
+ spx_fft_destroy(mFftTable);
+ }
+}
+
+status_t Visualizer::setEnabled(bool enabled)
+{
+ Mutex::Autolock _l(mLock);
+
+ sp<CaptureThread> t = mCaptureThread;
+ if (t != 0) {
+ if (enabled) {
+ if (t->exitPending()) {
+ if (t->requestExitAndWait() == WOULD_BLOCK) {
+ LOGE("Visualizer::enable() called from thread");
+ return INVALID_OPERATION;
+ }
+ }
+ }
+ t->mLock.lock();
+ }
+
+ status_t status = AudioEffect::setEnabled(enabled);
+
+ if (status == NO_ERROR) {
+ if (t != 0) {
+ if (enabled) {
+ t->run("AudioTrackThread");
+ } else {
+ t->requestExit();
+ }
+ }
+ }
+
+ if (t != 0) {
+ t->mLock.unlock();
+ }
+
+ return status;
+}
+
+status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate)
+{
+ if (rate > CAPTURE_RATE_MAX) {
+ return BAD_VALUE;
+ }
+ Mutex::Autolock _l(mLock);
+
+ if (mEnabled) {
+ return INVALID_OPERATION;
+ }
+
+ sp<CaptureThread> t = mCaptureThread;
+ if (t != 0) {
+ t->mLock.lock();
+ }
+ mCaptureThread.clear();
+ mCaptureCallBack = cbk;
+ mCaptureCbkUser = user;
+ mCaptureFlags = flags;
+ mCaptureRate = rate;
+
+ if (t != 0) {
+ t->mLock.unlock();
+ }
+
+ if (cbk != NULL) {
+ mCaptureThread = new CaptureThread(*this, rate, ((flags & CAPTURE_CALL_JAVA) != 0));
+ if (mCaptureThread == 0) {
+ LOGE("Could not create callback thread");
+ return NO_INIT;
+ }
+ }
+ LOGV("setCaptureCallBack() rate: %d thread %p flags 0x%08x",
+ rate, mCaptureThread.get(), mCaptureFlags);
+ return NO_ERROR;
+}
+
+status_t Visualizer::setCaptureSize(uint32_t size)
+{
+ if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
+ size < VISUALIZER_CAPTURE_SIZE_MIN ||
+ AudioSystem::popCount(size) != 1) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock _l(mLock);
+ if (mEnabled) {
+ return INVALID_OPERATION;
+ }
+
+ uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+ effect_param_t *p = (effect_param_t *)buf32;
+
+ p->psize = sizeof(uint32_t);
+ p->vsize = sizeof(uint32_t);
+ *(int32_t *)p->data = VISU_PARAM_CAPTURE_SIZE;
+ *((int32_t *)p->data + 1)= size;
+ status_t status = setParameter(p);
+
+ LOGV("setCaptureSize size %d status %d p->status %d", size, status, p->status);
+
+ if (status == NO_ERROR) {
+ status = p->status;
+ }
+ if (status == NO_ERROR) {
+ mCaptureSize = size;
+ if (mFftTable != NULL) {
+ spx_fft_destroy(mFftTable);
+ }
+ mFftTable = spx_fft_init(mCaptureSize);
+ LOGV("setCaptureSize size %d mFftTable %p", mCaptureSize, mFftTable);
+ }
+
+ return status;
+}
+
+status_t Visualizer::getWaveForm(uint8_t *waveform)
+{
+ if (waveform == NULL) {
+ return BAD_VALUE;
+ }
+ if (mCaptureSize == 0) {
+ return NO_INIT;
+ }
+
+ status_t status = NO_ERROR;
+ if (mEnabled) {
+ int32_t replySize = mCaptureSize;
+ status_t status = command(VISU_CMD_CAPTURE, 0, NULL, &replySize, waveform);
+ if (replySize == 0) {
+ status = NOT_ENOUGH_DATA;
+ }
+ } else {
+ memset(waveform, 0x80, mCaptureSize);
+ }
+ return status;
+}
+
+status_t Visualizer::getFft(uint8_t *fft)
+{
+ if (fft == NULL) {
+ return BAD_VALUE;
+ }
+ if (mCaptureSize == 0) {
+ return NO_INIT;
+ }
+
+ status_t status = NO_ERROR;
+ if (mEnabled) {
+ uint8_t buf[mCaptureSize];
+ status_t status = getWaveForm(buf);
+ if (status == NO_ERROR) {
+ status = doFft(fft, buf);
+ }
+ } else {
+ memset(fft, 0, mCaptureSize);
+ }
+ return status;
+}
+
+status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
+{
+ if (mFftTable == NULL) {
+ return NO_INIT;
+ }
+
+ float fsrc[mCaptureSize];
+ for (uint32_t i = 0; i < mCaptureSize; i++) {
+ fsrc[i] = (int16_t)(waveform[i] ^ 0x80) << 8;
+ }
+ float fdst[mCaptureSize];
+ spx_fft_float(mFftTable, fsrc, fdst);
+ for (uint32_t i = 0; i < mCaptureSize; i++) {
+ fft[i] = (uint8_t)((int32_t)fdst[i] >> 8);
+ }
+ return NO_ERROR;
+}
+
+void Visualizer::periodicCapture()
+{
+ Mutex::Autolock _l(mLock);
+ LOGV("periodicCapture() %p mCaptureCallBack %p mCaptureFlags 0x%08x",
+ this, mCaptureCallBack, mCaptureFlags);
+ if (mCaptureCallBack != NULL &&
+ (mCaptureFlags & (CAPTURE_WAVEFORM|CAPTURE_FFT)) &&
+ mCaptureSize != 0) {
+ uint8_t waveform[mCaptureSize];
+ status_t status = getWaveForm(waveform);
+ if (status != NO_ERROR) {
+ return;
+ }
+ uint8_t fft[mCaptureSize];
+ if (mCaptureFlags & CAPTURE_FFT) {
+ status = doFft(fft, waveform);
+ }
+ if (status != NO_ERROR) {
+ return;
+ }
+ uint8_t *wavePtr = NULL;
+ uint8_t *fftPtr = NULL;
+ uint32_t waveSize = 0;
+ uint32_t fftSize = 0;
+ if (mCaptureFlags & CAPTURE_WAVEFORM) {
+ wavePtr = waveform;
+ waveSize = mCaptureSize;
+ }
+ if (mCaptureFlags & CAPTURE_FFT) {
+ fftPtr = fft;
+ fftSize = mCaptureSize;
+ }
+ mCaptureCallBack(mCaptureCbkUser, waveSize, wavePtr, fftSize, fftPtr, mSampleRate);
+ }
+}
+
+uint32_t Visualizer::initCaptureSize()
+{
+ uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+ effect_param_t *p = (effect_param_t *)buf32;
+
+ p->psize = sizeof(uint32_t);
+ p->vsize = sizeof(uint32_t);
+ *(int32_t *)p->data = VISU_PARAM_CAPTURE_SIZE;
+ status_t status = getParameter(p);
+
+ if (status == NO_ERROR) {
+ status = p->status;
+ }
+
+ uint32_t size = 0;
+ if (status == NO_ERROR) {
+ size = *((int32_t *)p->data + 1);
+ }
+ mCaptureSize = size;
+
+ LOGV("initCaptureSize size %d status %d", mCaptureSize, status);
+
+ return size;
+}
+
+//-------------------------------------------------------------------------
+
+Visualizer::CaptureThread::CaptureThread(Visualizer& receiver, uint32_t captureRate, bool bCanCallJava)
+ : Thread(bCanCallJava), mReceiver(receiver)
+{
+ mSleepTimeUs = 1000000000 / captureRate;
+ LOGV("CaptureThread cstor %p captureRate %d mSleepTimeUs %d", this, captureRate, mSleepTimeUs);
+}
+
+bool Visualizer::CaptureThread::threadLoop()
+{
+ LOGV("CaptureThread %p enter", this);
+ while (!exitPending())
+ {
+ usleep(mSleepTimeUs);
+ mReceiver.periodicCapture();
+ }
+ LOGV("CaptureThread %p exiting", this);
+ return false;
+}
+
+status_t Visualizer::CaptureThread::readyToRun()
+{
+ return NO_ERROR;
+}
+
+void Visualizer::CaptureThread::onFirstRef()
+{
+}
+
+}; // namespace android
+
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index d5a3c13..b43f75f 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -658,61 +658,4 @@ void MediaPlayer::died()
}
-extern "C" {
-#define FLOATING_POINT 1
-#include "fftwrap.h"
-}
-
-static void *ffttable = NULL;
-
-// peeks at the audio data and fills 'data' with the requested kind
-// (currently kind=0 returns mono 16 bit PCM data, and kind=1 returns
-// 256 point FFT data). Return value is number of samples returned,
-// which may be 0.
-/*static*/ int MediaPlayer::snoop(short* data, int len, int kind) {
-
- sp<IMemory> p;
- const sp<IMediaPlayerService>& service = getMediaPlayerService();
- if (service != 0) {
- // Take a peek at the waveform. The returned data consists of 16 bit mono PCM data.
- p = service->snoop();
-
- if (p == NULL) {
- return 0;
- }
-
- if (kind == 0) { // return waveform data
- int plen = p->size();
- len *= 2; // number of shorts -> number of bytes
- short *src = (short*) p->pointer();
- if (plen > len) {
- plen = len;
- }
- memcpy(data, src, plen);
- return plen / sizeof(short); // return number of samples
- } else if (kind == 1) {
- // TODO: use a more efficient FFT
- // Right now this uses the speex library, which is compiled to do a float FFT
- if (!ffttable) ffttable = spx_fft_init(512);
- short *usrc = (short*) p->pointer();
- float fsrc[512];
- for (int i=0;i<512;i++)
- fsrc[i] = usrc[i];
- float fdst[512];
- spx_fft_float(ffttable, fsrc, fdst);
- if (len > 512) {
- len = 512;
- }
- len /= 2; // only half the output data is valid
- for (int i=0; i < len; i++)
- data[i] = fdst[i];
- return len;
- }
-
- } else {
- LOGE("Unable to locate media service");
- }
- return 0;
-}
-
}; // namespace android
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 82d5c14..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");
@@ -1265,98 +1271,6 @@ Exit:
return mem;
}
-/*
- * Avert your eyes, ugly hack ahead.
- * The following is to support music visualizations.
- */
-
-static const int NUMVIZBUF = 32;
-static const int VIZBUFFRAMES = 1024;
-static const int BUFTIMEMSEC = NUMVIZBUF * VIZBUFFRAMES * 1000 / 44100;
-static const int TOTALBUFTIMEMSEC = NUMVIZBUF * BUFTIMEMSEC;
-
-static bool gotMem = false;
-static sp<MemoryHeapBase> heap;
-static sp<MemoryBase> mem[NUMVIZBUF];
-static uint64_t endTime;
-static uint64_t lastReadTime;
-static uint64_t lastWriteTime;
-static int writeIdx = 0;
-
-static void allocVizBufs() {
- if (!gotMem) {
- heap = new MemoryHeapBase(NUMVIZBUF * VIZBUFFRAMES * 2, 0, "snooper");
- for (int i=0;i<NUMVIZBUF;i++) {
- mem[i] = new MemoryBase(heap, VIZBUFFRAMES * 2 * i, VIZBUFFRAMES * 2);
- }
- endTime = 0;
- gotMem = true;
- }
-}
-
-
-/*
- * Get a buffer of audio data that is about to be played.
- * We don't synchronize this because in practice the writer
- * is ahead of the reader, and even if we did happen to catch
- * a buffer while it's being written, it's just a visualization,
- * so no harm done.
- */
-static sp<MemoryBase> getVizBuffer() {
-
- allocVizBufs();
-
- lastReadTime = uptimeMillis();
-
- // if there is no recent buffer (yet), just return empty handed
- if (lastWriteTime + TOTALBUFTIMEMSEC < lastReadTime) {
- //LOGI("@@@@ no audio data to look at yet: %d + %d < %d", (int)lastWriteTime, TOTALBUFTIMEMSEC, (int)lastReadTime);
- return NULL;
- }
-
- int timedelta = endTime - lastReadTime;
- if (timedelta < 0) timedelta = 0;
- int framedelta = timedelta * 44100 / 1000;
- int headIdx = (writeIdx - framedelta) / VIZBUFFRAMES - 1;
- while (headIdx < 0) {
- headIdx += NUMVIZBUF;
- }
- return mem[headIdx];
-}
-
-// Append the data to the vizualization buffer
-static void makeVizBuffers(const char *data, int len, uint64_t time) {
-
- allocVizBufs();
-
- uint64_t startTime = time;
- const int frameSize = 4; // 16 bit stereo sample is 4 bytes
- int offset = writeIdx;
- int maxoff = heap->getSize() / 2; // in shorts
- short *base = (short*)heap->getBase();
- short *src = (short*)data;
- while (len > 0) {
-
- // Degrade quality by mixing to mono and clearing the lowest 3 bits.
- // This should still be good enough for a visualization
- base[offset++] = ((int(src[0]) + int(src[1])) >> 1) & ~0x7;
- src += 2;
- len -= frameSize;
- if (offset >= maxoff) {
- offset = 0;
- }
- }
- writeIdx = offset;
- endTime = time + (len / frameSize) / 44;
- //LOGI("@@@ stored buffers from %d to %d", uint32_t(startTime), uint32_t(time));
-}
-
-sp<IMemory> MediaPlayerService::snoop()
-{
- sp<MemoryBase> mem = getVizBuffer();
- return mem;
-}
-
#undef LOG_TAG
#define LOG_TAG "AudioSink"
@@ -1371,7 +1285,6 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId)
mRightVolume = 1.0;
mLatency = 0;
mMsecsPerFrame = 0;
- mNumFramesWritten = 0;
setMinBufferCount();
}
@@ -1516,30 +1429,9 @@ void MediaPlayerService::AudioOutput::start()
if (mTrack) {
mTrack->setVolume(mLeftVolume, mRightVolume);
mTrack->start();
- mTrack->getPosition(&mNumFramesWritten);
}
}
-void MediaPlayerService::AudioOutput::snoopWrite(const void* buffer, size_t size) {
- // Only make visualization buffers if anyone recently requested visualization data
- uint64_t now = uptimeMillis();
- if (lastReadTime + TOTALBUFTIMEMSEC >= now) {
- // Based on the current play counter, the number of frames written and
- // the current real time we can calculate the approximate real start
- // time of the buffer we're about to write.
- uint32_t pos;
- mTrack->getPosition(&pos);
-
- // we're writing ahead by this many frames:
- int ahead = mNumFramesWritten - pos;
- //LOGI("@@@ written: %d, playpos: %d, latency: %d", mNumFramesWritten, pos, mTrack->latency());
- // which is this many milliseconds, assuming 44100 Hz:
- ahead /= 44;
-
- makeVizBuffers((const char*)buffer, size, now + ahead + mTrack->latency());
- lastWriteTime = now;
- }
-}
ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
@@ -1548,9 +1440,7 @@ ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
//LOGV("write(%p, %u)", buffer, size);
if (mTrack) {
- snoopWrite(buffer, size);
ssize_t ret = mTrack->write(buffer, size);
- mNumFramesWritten += ret / 4; // assume 16 bit stereo
return ret;
}
return NO_INIT;
@@ -1560,7 +1450,6 @@ void MediaPlayerService::AudioOutput::stop()
{
LOGV("stop");
if (mTrack) mTrack->stop();
- lastWriteTime = 0;
}
void MediaPlayerService::AudioOutput::flush()
@@ -1573,7 +1462,6 @@ void MediaPlayerService::AudioOutput::pause()
{
LOGV("pause");
if (mTrack) mTrack->pause();
- lastWriteTime = 0;
}
void MediaPlayerService::AudioOutput::close()
@@ -1609,9 +1497,6 @@ void MediaPlayerService::AudioOutput::CallbackWrapper(
buffer->size = actualSize;
- if (actualSize > 0) {
- me->snoopWrite(buffer->raw, actualSize);
- }
}
#undef LOG_TAG
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 60b91c6..39f525e 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -113,9 +113,6 @@ class MediaPlayerService : public BnMediaPlayerService
static bool mIsOnEmulator;
static int mMinBufferCount; // 12 for emulator; otherwise 4
- public: // visualization hack support
- uint32_t mNumFramesWritten;
- void snoopWrite(const void*, size_t);
};
class AudioCache : public MediaPlayerBase::AudioSink
@@ -191,7 +188,6 @@ public:
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length, int audioSessionId);
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
- virtual sp<IMemory> snoop();
virtual sp<IOMX> getOMX();
virtual status_t dump(int fd, const Vector<String16>& args);
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 91c5b92..72061ad 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -426,6 +426,24 @@ status_t StagefrightRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) {
return OK;
}
+status_t StagefrightRecorder::setParamVideoEncoderProfile(int32_t profile) {
+ LOGV("setParamVideoEncoderProfile: %d", profile);
+
+ // Additional check will be done later when we load the encoder.
+ // For now, we are accepting values defined in OpenMAX IL.
+ mVideoEncoderProfile = profile;
+ return OK;
+}
+
+status_t StagefrightRecorder::setParamVideoEncoderLevel(int32_t level) {
+ LOGV("setParamVideoEncoderLevel: %d", level);
+
+ // Additional check will be done later when we load the encoder.
+ // For now, we are accepting values defined in OpenMAX IL.
+ mVideoEncoderLevel = level;
+ return OK;
+}
+
status_t StagefrightRecorder::setParameter(
const String8 &key, const String8 &value) {
LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
@@ -484,6 +502,16 @@ status_t StagefrightRecorder::setParameter(
if (safe_strtoi32(value.string(), &interval)) {
return setParamVideoIFramesInterval(interval);
}
+ } else if (key == "video-param-encoder-profile") {
+ int32_t profile;
+ if (safe_strtoi32(value.string(), &profile)) {
+ return setParamVideoEncoderProfile(profile);
+ }
+ } else if (key == "video-param-encoder-level") {
+ int32_t level;
+ if (safe_strtoi32(value.string(), &level)) {
+ return setParamVideoEncoderLevel(level);
+ }
} else if (key == "video-param-camera-id") {
int32_t cameraId;
if (safe_strtoi32(value.string(), &cameraId)) {
@@ -851,6 +879,12 @@ status_t StagefrightRecorder::setupVideoEncoder(const sp<MediaWriter>& writer) {
enc_meta->setInt32(kKeyIFramesInterval, mIFramesInterval);
enc_meta->setInt32(kKeyStride, stride);
enc_meta->setInt32(kKeySliceHeight, sliceHeight);
+ if (mVideoEncoderProfile != -1) {
+ enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile);
+ }
+ if (mVideoEncoderLevel != -1) {
+ enc_meta->setInt32(kKeyVideoLevel, mVideoEncoderLevel);
+ }
OMXClient client;
CHECK_EQ(client.connect(), OK);
@@ -992,6 +1026,10 @@ status_t StagefrightRecorder::reset() {
mAudioSourceNode = 0;
mUse64BitFileOffset = false;
mCameraId = 0;
+ mVideoEncoderProfile = -1;
+ mVideoEncoderLevel = -1;
+ mMaxFileDurationUs = 0;
+ mMaxFileSizeBytes = 0;
mTrackEveryNumberOfFrames = 0;
mTrackEveryTimeDurationUs = 0;
mEncoderProfiles = MediaProfiles::getInstance();
@@ -1019,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 cb05571..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 {
@@ -82,6 +83,8 @@ private:
int32_t mInterleaveDurationUs;
int32_t mIFramesInterval;
int32_t mCameraId;
+ int32_t mVideoEncoderProfile;
+ int32_t mVideoEncoderLevel;
int64_t mMaxFileSizeBytes;
int64_t mMaxFileDurationUs;
int32_t mTrackEveryNumberOfFrames;
@@ -108,6 +111,8 @@ private:
status_t setParamAudioSamplingRate(int32_t sampleRate);
status_t setParamVideoEncodingBitRate(int32_t bitRate);
status_t setParamVideoIFramesInterval(int32_t interval);
+ status_t setParamVideoEncoderProfile(int32_t profile);
+ status_t setParamVideoEncoderLevel(int32_t level);
status_t setParamVideoCameraId(int32_t cameraId);
status_t setParamTrackTimeStatus(int64_t timeDurationUs);
status_t setParamTrackFrameStatus(int32_t nFrames);
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 4a1580f..ffed74f 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -371,9 +371,6 @@ void AwesomePlayer::reset_l() {
}
mAudioSource.clear();
- if (mTimeSource != mAudioPlayer) {
- delete mTimeSource;
- }
mTimeSource = NULL;
delete mAudioPlayer;
@@ -494,22 +491,35 @@ void AwesomePlayer::onStreamDone() {
}
mStreamDoneEventPending = false;
- if (mStreamDoneStatus == ERROR_END_OF_STREAM && (mFlags & LOOPING)) {
+ if (mStreamDoneStatus != ERROR_END_OF_STREAM) {
+ LOGV("MEDIA_ERROR %d", mStreamDoneStatus);
+
+ notifyListener_l(
+ MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, mStreamDoneStatus);
+
+ pause_l();
+
+ mFlags |= AT_EOS;
+ return;
+ }
+
+ const bool allDone =
+ (mVideoSource == NULL || (mFlags & VIDEO_AT_EOS))
+ && (mAudioSource == NULL || (mFlags & AUDIO_AT_EOS));
+
+ if (!allDone) {
+ return;
+ }
+
+ if (mFlags & LOOPING) {
seekTo_l(0);
if (mVideoSource != NULL) {
postVideoEvent_l();
}
} else {
- if (mStreamDoneStatus == ERROR_END_OF_STREAM) {
- LOGV("MEDIA_PLAYBACK_COMPLETE");
- notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
- } else {
- LOGV("MEDIA_ERROR %d", mStreamDoneStatus);
-
- notifyListener_l(
- MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, mStreamDoneStatus);
- }
+ LOGV("MEDIA_PLAYBACK_COMPLETE");
+ notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
pause_l();
@@ -563,7 +573,6 @@ status_t AwesomePlayer::play_l() {
return err;
}
- delete mTimeSource;
mTimeSource = mAudioPlayer;
deferredAudioSeek = true;
@@ -579,7 +588,7 @@ status_t AwesomePlayer::play_l() {
}
if (mTimeSource == NULL && mAudioPlayer == NULL) {
- mTimeSource = new SystemTimeSource;
+ mTimeSource = &mSystemTimeSource;
}
if (mVideoSource != NULL) {
@@ -744,7 +753,7 @@ status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
mSeeking = true;
mSeekNotificationSent = false;
mSeekTimeUs = timeUs;
- mFlags &= ~AT_EOS;
+ mFlags &= ~(AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS);
seekAudioIfNecessary_l();
@@ -924,6 +933,7 @@ void AwesomePlayer::onVideoEvent() {
continue;
}
+ mFlags |= VIDEO_AT_EOS;
postStreamDoneEvent_l(err);
return;
}
@@ -968,19 +978,21 @@ void AwesomePlayer::onVideoEvent() {
mSeekNotificationSent = false;
}
+ TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource;
+
if (mFlags & FIRST_FRAME) {
mFlags &= ~FIRST_FRAME;
- mTimeSourceDeltaUs = mTimeSource->getRealTimeUs() - timeUs;
+ mTimeSourceDeltaUs = ts->getRealTimeUs() - timeUs;
}
int64_t realTimeUs, mediaTimeUs;
- if (mAudioPlayer != NULL
+ if (!(mFlags & AUDIO_AT_EOS) && mAudioPlayer != NULL
&& mAudioPlayer->getMediaTimeMapping(&realTimeUs, &mediaTimeUs)) {
mTimeSourceDeltaUs = realTimeUs - mediaTimeUs;
}
- int64_t nowUs = mTimeSource->getRealTimeUs() - mTimeSourceDeltaUs;
+ int64_t nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs;
int64_t latenessUs = nowUs - timeUs;
@@ -1081,6 +1093,8 @@ void AwesomePlayer::onCheckAudioStatus() {
status_t finalStatus;
if (mWatchForAudioEOS && mAudioPlayer->reachedEOS(&finalStatus)) {
mWatchForAudioEOS = false;
+ mFlags |= AUDIO_AT_EOS;
+ mFlags |= FIRST_FRAME;
postStreamDoneEvent_l(finalStatus);
}
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index dacb8d3..efaab5b 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -831,7 +831,7 @@ void OMXCodec::setVideoInputFormat(
video_def->nFrameWidth = width;
video_def->nFrameHeight = height;
- video_def->xFramerate = (frameRate << 16); // Q16 format
+ video_def->xFramerate = 0; // No need for output port
video_def->nBitrate = bitRate; // Q16 format
video_def->eCompressionFormat = compressionFormat;
video_def->eColorFormat = OMX_COLOR_FormatUnused;
@@ -918,6 +918,52 @@ status_t OMXCodec::setupBitRate(int32_t bitRate) {
return OK;
}
+status_t OMXCodec::getVideoProfileLevel(
+ const sp<MetaData>& meta,
+ const CodecProfileLevel& defaultProfileLevel,
+ CodecProfileLevel &profileLevel) {
+ CODEC_LOGV("Default profile: %ld, level %ld",
+ defaultProfileLevel.mProfile, defaultProfileLevel.mLevel);
+
+ // Are the default profile and level overwriten?
+ int32_t profile, level;
+ if (!meta->findInt32(kKeyVideoProfile, &profile)) {
+ profile = defaultProfileLevel.mProfile;
+ }
+ if (!meta->findInt32(kKeyVideoLevel, &level)) {
+ level = defaultProfileLevel.mLevel;
+ }
+ CODEC_LOGV("Target profile: %d, level: %d", profile, level);
+
+ // Are the target profile and level supported by the encoder?
+ OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
+ InitOMXParams(&param);
+ param.nPortIndex = kPortIndexOutput;
+ for (param.nProfileIndex = 0;; ++param.nProfileIndex) {
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoProfileLevelQuerySupported,
+ &param, sizeof(param));
+
+ if (err != OK) return err;
+
+ int32_t supportedProfile = static_cast<int32_t>(param.eProfile);
+ int32_t supportedLevel = static_cast<int32_t>(param.eLevel);
+ CODEC_LOGV("Supported profile: %d, level %d",
+ supportedProfile, supportedLevel);
+
+ if (profile == supportedProfile &&
+ level == supportedLevel) {
+ profileLevel.mProfile = profile;
+ profileLevel.mLevel = level;
+ return OK;
+ }
+ }
+
+ CODEC_LOGE("Target profile (%d) and level (%d) is not supported",
+ profile, level);
+ return BAD_VALUE;
+}
+
status_t OMXCodec::setupH263EncoderParameters(const sp<MetaData>& meta) {
int32_t iFramesInterval, frameRate, bitRate;
bool success = meta->findInt32(kKeyBitRate, &bitRate);
@@ -941,8 +987,14 @@ status_t OMXCodec::setupH263EncoderParameters(const sp<MetaData>& meta) {
}
h263type.nBFrames = 0;
- h263type.eProfile = OMX_VIDEO_H263ProfileBaseline;
- h263type.eLevel = OMX_VIDEO_H263Level45;
+ // Check profile and level parameters
+ CodecProfileLevel defaultProfileLevel, profileLevel;
+ defaultProfileLevel.mProfile = OMX_VIDEO_H263ProfileBaseline;
+ defaultProfileLevel.mLevel = OMX_VIDEO_H263Level45;
+ err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel);
+ if (err != OK) return err;
+ h263type.eProfile = static_cast<OMX_VIDEO_H263PROFILETYPE>(profileLevel.mProfile);
+ h263type.eLevel = static_cast<OMX_VIDEO_H263LEVELTYPE>(profileLevel.mLevel);
h263type.bPLUSPTYPEAllowed = OMX_FALSE;
h263type.bForceRoundingTypeToZero = OMX_FALSE;
@@ -992,8 +1044,14 @@ status_t OMXCodec::setupMPEG4EncoderParameters(const sp<MetaData>& meta) {
mpeg4type.nHeaderExtension = 0;
mpeg4type.bReversibleVLC = OMX_FALSE;
- mpeg4type.eProfile = OMX_VIDEO_MPEG4ProfileSimple;
- mpeg4type.eLevel = OMX_VIDEO_MPEG4Level2;
+ // Check profile and level parameters
+ CodecProfileLevel defaultProfileLevel, profileLevel;
+ defaultProfileLevel.mProfile = OMX_VIDEO_MPEG4ProfileSimple;
+ defaultProfileLevel.mLevel = OMX_VIDEO_MPEG4Level2;
+ err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel);
+ if (err != OK) return err;
+ mpeg4type.eProfile = static_cast<OMX_VIDEO_MPEG4PROFILETYPE>(profileLevel.mProfile);
+ mpeg4type.eLevel = static_cast<OMX_VIDEO_MPEG4LEVELTYPE>(profileLevel.mLevel);
err = mOMX->setParameter(
mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
@@ -1029,22 +1087,39 @@ status_t OMXCodec::setupAVCEncoderParameters(const sp<MetaData>& meta) {
if (h264type.nPFrames == 0) {
h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
}
- h264type.bUseHadamard = OMX_TRUE;
- h264type.nRefFrames = 1;
- h264type.nRefIdx10ActiveMinus1 = 0;
- h264type.nRefIdx11ActiveMinus1 = 0;
+
+ // Check profile and level parameters
+ CodecProfileLevel defaultProfileLevel, profileLevel;
+ defaultProfileLevel.mProfile = h264type.eProfile;
+ defaultProfileLevel.mLevel = h264type.eLevel;
+ err = getVideoProfileLevel(meta, defaultProfileLevel, profileLevel);
+ if (err != OK) return err;
+ h264type.eProfile = static_cast<OMX_VIDEO_AVCPROFILETYPE>(profileLevel.mProfile);
+ h264type.eLevel = static_cast<OMX_VIDEO_AVCLEVELTYPE>(profileLevel.mLevel);
+
+ if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) {
+ h264type.bUseHadamard = OMX_TRUE;
+ h264type.nRefFrames = 1;
+ h264type.nRefIdx10ActiveMinus1 = 0;
+ h264type.nRefIdx11ActiveMinus1 = 0;
+ h264type.bEntropyCodingCABAC = OMX_FALSE;
+ h264type.bWeightedPPrediction = OMX_FALSE;
+ h264type.bconstIpred = OMX_FALSE;
+ h264type.bDirect8x8Inference = OMX_FALSE;
+ h264type.bDirectSpatialTemporal = OMX_FALSE;
+ h264type.nCabacInitIdc = 0;
+ }
+
+ if (h264type.nBFrames != 0) {
+ h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB;
+ }
+
h264type.bEnableUEP = OMX_FALSE;
h264type.bEnableFMO = OMX_FALSE;
h264type.bEnableASO = OMX_FALSE;
h264type.bEnableRS = OMX_FALSE;
h264type.bFrameMBsOnly = OMX_TRUE;
h264type.bMBAFF = OMX_FALSE;
- h264type.bEntropyCodingCABAC = OMX_FALSE;
- h264type.bWeightedPPrediction = OMX_FALSE;
- h264type.bconstIpred = OMX_FALSE;
- h264type.bDirect8x8Inference = OMX_FALSE;
- h264type.bDirectSpatialTemporal = OMX_FALSE;
- h264type.nCabacInitIdc = 0;
h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;
err = mOMX->setParameter(
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/foundation/AHandler.cpp b/media/libstagefright/foundation/AHandler.cpp
new file mode 100644
index 0000000..bd5f7e94
--- /dev/null
+++ b/media/libstagefright/foundation/AHandler.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "AHandler"
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/AHandler.h>
+
+#include <media/stagefright/foundation/ALooperRoster.h>
+
+namespace android {
+
+sp<ALooper> AHandler::looper() {
+ extern ALooperRoster gLooperRoster;
+
+ return gLooperRoster.findLooper(id());
+}
+
+} // namespace android
diff --git a/media/libstagefright/foundation/ALooperRoster.cpp b/media/libstagefright/foundation/ALooperRoster.cpp
index 5bb1cf9..65f7593 100644
--- a/media/libstagefright/foundation/ALooperRoster.cpp
+++ b/media/libstagefright/foundation/ALooperRoster.cpp
@@ -54,10 +54,15 @@ void ALooperRoster::unregisterHandler(ALooper::handler_id handlerID) {
Mutex::Autolock autoLock(mLock);
ssize_t index = mHandlers.indexOfKey(handlerID);
- CHECK(index >= 0);
+ CHECK_GE(index, 0);
const HandlerInfo &info = mHandlers.valueAt(index);
- info.mHandler->setID(0);
+
+ sp<AHandler> handler = info.mHandler.promote();
+
+ if (handler != NULL) {
+ handler->setID(0);
+ }
mHandlers.removeItemsAt(index);
}
@@ -74,7 +79,18 @@ void ALooperRoster::postMessage(
}
const HandlerInfo &info = mHandlers.valueAt(index);
- info.mLooper->post(msg, delayUs);
+
+ sp<ALooper> looper = info.mLooper.promote();
+
+ if (looper == NULL) {
+ LOG(WARNING) << "failed to post message. "
+ "Target handler still registered, but object gone.";
+
+ mHandlers.removeItemsAt(index);
+ return;
+ }
+
+ looper->post(msg, delayUs);
}
void ALooperRoster::deliverMessage(const sp<AMessage> &msg) {
@@ -86,15 +102,43 @@ void ALooperRoster::deliverMessage(const sp<AMessage> &msg) {
ssize_t index = mHandlers.indexOfKey(msg->target());
if (index < 0) {
- LOG(WARNING) << "failed to deliver message. Target handler not registered.";
+ LOG(WARNING) << "failed to deliver message. "
+ << "Target handler not registered.";
return;
}
const HandlerInfo &info = mHandlers.valueAt(index);
- handler = info.mHandler;
+ handler = info.mHandler.promote();
+
+ if (handler == NULL) {
+ LOG(WARNING) << "failed to deliver message. "
+ "Target handler registered, but object gone.";
+
+ mHandlers.removeItemsAt(index);
+ return;
+ }
}
handler->onMessageReceived(msg);
}
+sp<ALooper> ALooperRoster::findLooper(ALooper::handler_id handlerID) {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mHandlers.indexOfKey(handlerID);
+
+ if (index < 0) {
+ return NULL;
+ }
+
+ sp<ALooper> looper = mHandlers.valueAt(index).mLooper.promote();
+
+ if (looper == NULL) {
+ mHandlers.removeItemsAt(index);
+ return NULL;
+ }
+
+ return looper;
+}
+
} // namespace android
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index 73047e7..35eea7e 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \
AAtomizer.cpp \
ABuffer.cpp \
ADebug.cpp \
+ AHandler.cpp \
ALooper.cpp \
ALooperRoster.cpp \
AMessage.cpp \
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/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 2a9f21b..8d0877c 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -24,6 +24,7 @@
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/TimeSource.h>
#include <utils/threads.h>
namespace android {
@@ -33,7 +34,6 @@ struct DataSource;
struct MediaBuffer;
struct MediaExtractor;
struct MediaSource;
-struct TimeSource;
struct NuCachedSource2;
struct ALooper;
@@ -102,6 +102,8 @@ private:
AT_EOS = 32,
PREPARE_CANCELLED = 64,
CACHE_UNDERRUN = 128,
+ AUDIO_AT_EOS = 256,
+ VIDEO_AT_EOS = 512,
};
mutable Mutex mLock;
@@ -115,6 +117,7 @@ private:
sp<ISurface> mISurface;
sp<MediaPlayerBase::AudioSink> mAudioSink;
+ SystemTimeSource mSystemTimeSource;
TimeSource *mTimeSource;
String8 mUri;
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index ad5b0f9..6de761f 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -164,6 +164,10 @@ void OMX::binderDied(const wp<IBinder> &the_late_who) {
instance = mLiveNodes.editValueAt(index);
mLiveNodes.removeItemsAt(index);
+ index = mDispatchers.indexOfKey(instance->nodeID());
+ CHECK(index >= 0);
+ mDispatchers.removeItemsAt(index);
+
invalidateNodeID_l(instance->nodeID());
}
@@ -240,6 +244,11 @@ status_t OMX::freeNode(node_id node) {
ssize_t index = mLiveNodes.indexOfKey(instance->observer()->asBinder());
CHECK(index >= 0);
mLiveNodes.removeItemsAt(index);
+
+ index = mDispatchers.indexOfKey(node);
+ CHECK(index >= 0);
+ mDispatchers.removeItemsAt(index);
+
instance->observer()->asBinder()->unlinkToDeath(this);
return instance->freeNode(mMaster);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java
index 056537d..b1ad315 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java
@@ -34,10 +34,12 @@ public class MediaProfileReader
private static final List<AudioDecoder> audioDecoders = DecoderCapabilities.getAudioDecoders();
private static final List<VideoEncoderCap> videoEncoders = EncoderCapabilities.getVideoEncoders();
private static final List<AudioEncoderCap> audioEncoders = EncoderCapabilities.getAudioEncoders();
- private static final HashMap<Integer, String> encoderMap = new HashMap<Integer, String>();
+ private static final HashMap<Integer, String> videoEncoderMap = new HashMap<Integer, String>();
+ private static final HashMap<Integer, String> audioEncoderMap = new HashMap<Integer, String>();
static {
- initEncoderMap();
+ initAudioEncoderMap();
+ initVideoEncoderMap();
};
public static List<VideoEncoderCap> getVideoEncoders() {
@@ -79,7 +81,7 @@ public class MediaProfileReader
videoEncoder != MediaRecorder.VideoEncoder.MPEG_4_SP) {
throw new IllegalArgumentException("Unsupported video encoder " + videoEncoder);
}
- return encoderMap.get(videoEncoder);
+ return videoEncoderMap.get(videoEncoder);
}
public static String getAudioCodecName(int audioEncoder) {
@@ -90,22 +92,24 @@ public class MediaProfileReader
audioEncoder != MediaRecorder.AudioEncoder.EAAC_PLUS) {
throw new IllegalArgumentException("Unsupported audio encodeer " + audioEncoder);
}
- return encoderMap.get(audioEncoder);
+ return audioEncoderMap.get(audioEncoder);
}
private MediaProfileReader() {} // Don't call me
- private static void initEncoderMap() {
+ private static void initVideoEncoderMap() {
// video encoders
- encoderMap.put(MediaRecorder.VideoEncoder.H263, "h263");
- encoderMap.put(MediaRecorder.VideoEncoder.H264, "h264");
- encoderMap.put(MediaRecorder.VideoEncoder.MPEG_4_SP, "m4v");
+ videoEncoderMap.put(MediaRecorder.VideoEncoder.H263, "h263");
+ videoEncoderMap.put(MediaRecorder.VideoEncoder.H264, "h264");
+ videoEncoderMap.put(MediaRecorder.VideoEncoder.MPEG_4_SP, "m4v");
+ }
+ private static void initAudioEncoderMap() {
// audio encoders
- encoderMap.put(MediaRecorder.AudioEncoder.AMR_NB, "amrnb");
- encoderMap.put(MediaRecorder.AudioEncoder.AMR_WB, "amrwb");
- encoderMap.put(MediaRecorder.AudioEncoder.AAC, "aac");
- encoderMap.put(MediaRecorder.AudioEncoder.AAC_PLUS, "aacplus");
- encoderMap.put(MediaRecorder.AudioEncoder.EAAC_PLUS, "eaacplus");
+ audioEncoderMap.put(MediaRecorder.AudioEncoder.AMR_NB, "amrnb");
+ audioEncoderMap.put(MediaRecorder.AudioEncoder.AMR_WB, "amrwb");
+ audioEncoderMap.put(MediaRecorder.AudioEncoder.AAC, "aac");
+ audioEncoderMap.put(MediaRecorder.AudioEncoder.AAC_PLUS, "aacplus");
+ audioEncoderMap.put(MediaRecorder.AudioEncoder.EAAC_PLUS, "eaacplus");
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
index a52fd76..2332657 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
@@ -121,8 +121,8 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase<MediaFram
mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
String filename = ("/sdcard/" + videoEncoder + "_" + audioEncoder + "_" + highQuality + ".3gp");
try {
- Log.v(TAG, "video encoder :" + videoEncoder);
- Log.v(TAG, "audio encoder :" + audioEncoder);
+ Log.v(TAG, "video encoder : " + videoEncoder);
+ Log.v(TAG, "audio encoder : " + audioEncoder);
Log.v(TAG, "quality : " + (highQuality?"high": "low"));
Log.v(TAG, "encoder : " + MediaProfileReader.getVideoCodecName(videoEncoder));
Log.v(TAG, "audio : " + MediaProfileReader.getAudioCodecName(audioEncoder));
diff --git a/native/android/Android.mk b/native/android/Android.mk
index fe8ed00..509a379 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -6,16 +6,18 @@ include $(CLEAR_VARS)
# our source files
#
LOCAL_SRC_FILES:= \
- activity.cpp \
input.cpp \
+ looper.cpp \
+ native_activity.cpp \
native_window.cpp
LOCAL_SHARED_LIBRARIES := \
- libandroid_runtime \
libcutils \
libutils \
libbinder \
- libui
+ libui \
+ libsurfaceflinger_client \
+ libandroid_runtime
LOCAL_C_INCLUDES += \
frameworks/base/native/include \
diff --git a/native/android/activity.cpp b/native/android/activity.cpp
deleted file mode 100644
index e69de29..0000000
--- a/native/android/activity.cpp
+++ /dev/null
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 8498840..89d53e2 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -20,6 +20,7 @@
#include <android/input.h>
#include <ui/Input.h>
#include <ui/InputTransport.h>
+#include <utils/PollLoop.h>
#include <poll.h>
@@ -184,8 +185,16 @@ float AMotionEvent_getHistoricalSize(AInputEvent* motion_event, size_t pointer_i
pointer_index, history_index);
}
-int AInputQueue_getFd(AInputQueue* queue) {
- return queue->getConsumer().getChannel()->getReceivePipeFd();
+void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
+ ALooper_callbackFunc* callback, void* data) {
+ queue->setPollLoop(static_cast<android::PollLoop*>(looper));
+ ALooper_addFd(looper, queue->getConsumer().getChannel()->getReceivePipeFd(),
+ POLLIN, callback, data);
+}
+
+void AInputQueue_detachLooper(AInputQueue* queue) {
+ queue->getPollLoop()->removeCallback(
+ queue->getConsumer().getChannel()->getReceivePipeFd());
}
int AInputQueue_hasEvents(AInputQueue* queue) {
diff --git a/native/android/looper.cpp b/native/android/looper.cpp
new file mode 100644
index 0000000..1564c47
--- /dev/null
+++ b/native/android/looper.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 "ALooper"
+#include <utils/Log.h>
+
+#include <android/looper.h>
+#include <utils/PollLoop.h>
+
+using android::PollLoop;
+using android::sp;
+
+ALooper* ALooper_forThread() {
+ return PollLoop::getForThread().get();
+}
+
+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(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, 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, 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) {
+ static_cast<PollLoop*>(looper)->incStrong((void*)ALooper_acquire);
+}
+
+void ALooper_release(ALooper* looper) {
+ static_cast<PollLoop*>(looper)->decStrong((void*)ALooper_acquire);
+}
+
+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_removeFd(ALooper* looper, int fd) {
+ return static_cast<PollLoop*>(looper)->removeCallback(fd) ? 1 : 0;
+}
diff --git a/native/android/native_activity.cpp b/native/android/native_activity.cpp
new file mode 100644
index 0000000..509cc33
--- /dev/null
+++ b/native/android/native_activity.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "native_activity"
+#include <utils/Log.h>
+
+#include <android_runtime/android_app_NativeActivity.h>
+
+using namespace android;
+
+void ANativeActivity_setWindowFormat(ANativeActivity* activity, int32_t format) {
+ android_NativeActivity_setWindowFormat(activity, format);
+}
+
+void ANativeActivity_setWindowFlags(ANativeActivity* activity,
+ uint32_t addFlags, uint32_t removeFlags) {
+ android_NativeActivity_setWindowFlags(activity, addFlags, addFlags|removeFlags);
+}
diff --git a/native/android/native_window.cpp b/native/android/native_window.cpp
index 448cbfc..bada078 100644
--- a/native/android/native_window.cpp
+++ b/native/android/native_window.cpp
@@ -17,10 +17,27 @@
#define LOG_TAG "Surface"
#include <utils/Log.h>
-#include <android/native_window.h>
+#include <android/native_window_jni.h>
#include <surfaceflinger/Surface.h>
+#include <android_runtime/android_view_Surface.h>
-using android::Surface;
+using namespace android;
+
+ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface) {
+ sp<ANativeWindow> win = android_Surface_getNativeWindow(env, surface);
+ if (win != NULL) {
+ win->incStrong((void*)ANativeWindow_acquire);
+ }
+ return win.get();
+}
+
+void ANativeWindow_acquire(ANativeWindow* window) {
+ window->incStrong((void*)ANativeWindow_acquire);
+}
+
+void ANativeWindow_release(ANativeWindow* window) {
+ window->decStrong((void*)ANativeWindow_acquire);
+}
static int32_t getWindowProp(ANativeWindow* window, int what) {
int value;
@@ -41,7 +58,40 @@ int32_t ANativeWindow_getFormat(ANativeWindow* window) {
}
int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width,
- int32_t height, int32_t format) {
- native_window_set_buffers_geometry(window, width, height, format);
+ int32_t height) {
+ native_window_set_buffers_geometry(window, width, height, 0);
return 0;
}
+
+int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
+ ARect* inOutDirtyBounds) {
+ Region dirtyRegion;
+ Region* dirtyParam = NULL;
+ if (inOutDirtyBounds != NULL) {
+ dirtyRegion.set(*(Rect*)inOutDirtyBounds);
+ dirtyParam = &dirtyRegion;
+ }
+
+ Surface::SurfaceInfo info;
+ status_t res = static_cast<Surface*>(window)->lock(&info, dirtyParam);
+ if (res != OK) {
+ return -1;
+ }
+
+ outBuffer->width = (int32_t)info.w;
+ outBuffer->height = (int32_t)info.h;
+ outBuffer->stride = (int32_t)info.s;
+ outBuffer->format = (int32_t)info.format;
+ outBuffer->bits = info.bits;
+
+ if (inOutDirtyBounds != NULL) {
+ *inOutDirtyBounds = dirtyRegion.getBounds();
+ }
+
+ return 0;
+}
+
+int32_t ANativeWindow_unlockAndPost(ANativeWindow* window) {
+ status_t res = static_cast<Surface*>(window)->unlockAndPost();
+ return res == android::OK ? 0 : -1;
+}
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..2411e93
--- /dev/null
+++ b/native/glue/threaded_app/threaded_app.c
@@ -0,0 +1,279 @@
+/*
+ * 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__))
+
+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;
+}
+
+int32_t 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;
+ }
+
+ return android_app->destroyRequested ? 0 : 1;
+}
+
+static 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.
+}
+
+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);
+
+ android_app_destroy(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 7617662..014b6a3 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -42,6 +42,7 @@
#include <sys/types.h>
#include <android/keycodes.h>
+#include <android/looper.h>
#ifdef __cplusplus
extern "C" {
@@ -533,12 +534,16 @@ struct AInputQueue;
typedef struct AInputQueue AInputQueue;
/*
- * Return a file descriptor for the queue, which you
- * can use to determine if there are events available. This
- * is typically used with select() or poll() to multiplex
- * with other kinds of events.
+ * Add this input queue to a looper for processing. See
+ * ALooper_addFd() for information on the callback and data params.
*/
-int AInputQueue_getFd(AInputQueue* queue);
+void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper,
+ ALooper_callbackFunc* callback, void* data);
+
+/*
+ * Remove the input queue from the looper it is currently attached to.
+ */
+void AInputQueue_detachLooper(AInputQueue* queue);
/*
* Returns true if there are one or more events available in the
diff --git a/native/include/android/looper.h b/native/include/android/looper.h
new file mode 100644
index 0000000..2917216
--- /dev/null
+++ b/native/include/android/looper.h
@@ -0,0 +1,177 @@
+/*
+ * 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_LOOPER_H
+#define ANDROID_LOOPER_H
+
+#include <poll.h>
+
+#ifdef __cplusplus
+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();
+
+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
+};
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+/**
+ * Remove a previously added file descriptor from the looper.
+ */
+int32_t ALooper_removeFd(ALooper* looper, int fd);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_NATIVE_WINDOW_H
diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h
index bf5c641..d0ff052 100644
--- a/native/include/android/native_activity.h
+++ b/native/include/android/native_activity.h
@@ -64,6 +64,21 @@ typedef struct ANativeActivity {
jobject clazz;
/**
+ * Path to this application's internal data directory.
+ */
+ const char* internalDataPath;
+
+ /**
+ * Path to this application's external (removable/mountable) data directory.
+ */
+ const char* externalDataPath;
+
+ /**
+ * The platform's SDK version code.
+ */
+ int32_t sdkVersion;
+
+ /**
* This is the native instance of the application. It is not used by
* the framework, but can be set by the application to its own instance
* state.
@@ -177,6 +192,11 @@ typedef void ANativeActivity_createFunc(ANativeActivity* activity,
*/
extern ANativeActivity_createFunc ANativeActivity_onCreate;
+void ANativeActivity_setWindowFormat(ANativeActivity* activity, int32_t format);
+
+void ANativeActivity_setWindowFlags(ANativeActivity* activity,
+ uint32_t addFlags, uint32_t removeFlags);
+
#ifdef __cplusplus
};
#endif
diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h
index 678ba3d..7599d7e 100644
--- a/native/include/android/native_window.h
+++ b/native/include/android/native_window.h
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-
#ifndef ANDROID_NATIVE_WINDOW_H
#define ANDROID_NATIVE_WINDOW_H
+#include <android/rect.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -34,6 +35,27 @@ enum {
struct ANativeWindow;
typedef struct ANativeWindow ANativeWindow;
+typedef struct ANativeWindow_Buffer {
+ int32_t width;
+ int32_t height;
+ int32_t stride;
+ int32_t format;
+ void* bits;
+
+ uint32_t reserved[6];
+} ANativeWindow_Buffer;
+
+/**
+ * Acquire a reference on the given ANativeWindow object. This prevents the object
+ * from being deleted until the reference is removed.
+ */
+void ANativeWindow_acquire(ANativeWindow* window);
+
+/**
+ * Remove a reference that was previously acquired with ANativeWindow_acquire().
+ */
+void ANativeWindow_release(ANativeWindow* window);
+
/*
* Return the current width in pixels of the window surface. Returns a
* negative value on error.
@@ -60,13 +82,22 @@ int32_t ANativeWindow_getFormat(ANativeWindow* window);
* window's physical size, then it buffer will be scaled to match that size
* when compositing it to the screen.
*
- * The format may be one of the window format constants above.
- *
- * For all of these parameters, if 0 is supplied than the window's base
+ * For all of these parameters, if 0 is supplied then the window's base
* value will come back in force.
*/
-int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width,
- int32_t height, int32_t format);
+int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, int32_t height);
+
+/**
+ * Lock the window's next drawing surface for writing.
+ */
+int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
+ ARect* inOutDirtyBounds);
+
+/**
+ * Unlock the window's drawing surface after previously locking it,
+ * posting the new buffer to the display.
+ */
+int32_t ANativeWindow_unlockAndPost(ANativeWindow* window);
#ifdef __cplusplus
};
diff --git a/native/include/android/native_window_jni.h b/native/include/android/native_window_jni.h
new file mode 100644
index 0000000..b9e72ef
--- /dev/null
+++ b/native/include/android/native_window_jni.h
@@ -0,0 +1,40 @@
+/*
+ * 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_NATIVE_WINDOW_JNI_H
+#define ANDROID_NATIVE_WINDOW_JNI_H
+
+#include <android/native_window.h>
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Return the ANativeWindow associated with a Java Surface object,
+ * for interacting with it through native code. This acquires a reference
+ * on the ANativeWindow that is returned; be sure to use ANativeWindow_release()
+ * when done with it so that it doesn't leak.
+ */
+ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_NATIVE_WINDOW_H
diff --git a/native/include/android/rect.h b/native/include/android/rect.h
new file mode 100644
index 0000000..3e81f53
--- /dev/null
+++ b/native/include/android/rect.h
@@ -0,0 +1,36 @@
+/*
+ * 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_RECT_H
+#define ANDROID_RECT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ARect {
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+} ARect;
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_RECT_H
diff --git a/native/include/android/window.h b/native/include/android/window.h
new file mode 100644
index 0000000..2ab192b
--- /dev/null
+++ b/native/include/android/window.h
@@ -0,0 +1,58 @@
+/*
+ * 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_WINDOW_H
+#define ANDROID_WINDOW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Window flags, as per the Java API at android.view.WindowManager.LayoutParams.
+ */
+enum {
+ AWINDOW_FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
+ AWINDOW_FLAG_DIM_BEHIND = 0x00000002,
+ AWINDOW_FLAG_BLUR_BEHIND = 0x00000004,
+ AWINDOW_FLAG_NOT_FOCUSABLE = 0x00000008,
+ AWINDOW_FLAG_NOT_TOUCHABLE = 0x00000010,
+ AWINDOW_FLAG_NOT_TOUCH_MODAL = 0x00000020,
+ AWINDOW_FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
+ AWINDOW_FLAG_KEEP_SCREEN_ON = 0x00000080,
+ AWINDOW_FLAG_LAYOUT_IN_SCREEN = 0x00000100,
+ AWINDOW_FLAG_LAYOUT_NO_LIMITS = 0x00000200,
+ AWINDOW_FLAG_FULLSCREEN = 0x00000400,
+ AWINDOW_FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
+ AWINDOW_FLAG_DITHER = 0x00001000,
+ AWINDOW_FLAG_SECURE = 0x00002000,
+ AWINDOW_FLAG_SCALED = 0x00004000,
+ AWINDOW_FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
+ AWINDOW_FLAG_LAYOUT_INSET_DECOR = 0x00010000,
+ AWINDOW_FLAG_ALT_FOCUSABLE_IM = 0x00020000,
+ AWINDOW_FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
+ AWINDOW_FLAG_SHOW_WHEN_LOCKED = 0x00080000,
+ AWINDOW_FLAG_SHOW_WALLPAPER = 0x00100000,
+ AWINDOW_FLAG_TURN_SCREEN_ON = 0x00200000,
+ AWINDOW_FLAG_DISMISS_KEYGUARD = 0x00400000,
+};
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_WINDOW_H
diff --git a/native/include/android_glue/threaded_app.h b/native/include/android_glue/threaded_app.h
new file mode 100644
index 0000000..adfdbea
--- /dev/null
+++ b/native/include/android_glue/threaded_app.h
@@ -0,0 +1,165 @@
+/*
+ * 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;
+
+ // -------------------------------------------------
+ // Below are "private" implementation of the glue code.
+
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+
+ int msgread;
+ int msgwrite;
+
+ pthread_t thread;
+
+ // This is non-zero when the application's NativeActivity is being
+ // destroyed and waiting for the app thread to complete.
+ int destroyRequested;
+
+ 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 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.
+ *
+ * Important: returns 0 if the app should exit. You must ALWAYS check for
+ * a zero return and, if found, exit your android_main() function.
+ */
+int32_t 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/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index cd4f96d..7395233 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -49,6 +49,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.HashSet;
import java.util.List;
/**
@@ -67,11 +68,29 @@ public class DatabaseHelper extends SQLiteOpenHelper {
private Context mContext;
+ private static final HashSet<String> mValidTables = new HashSet<String>();
+
+ static {
+ mValidTables.add("system");
+ mValidTables.add("secure");
+ mValidTables.add("bluetooth_devices");
+ mValidTables.add("bookmarks");
+
+ // These are old.
+ mValidTables.add("favorites");
+ mValidTables.add("gservices");
+ mValidTables.add("old_favorites");
+ }
+
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = context;
}
+ public static boolean isValidTable(String name) {
+ return mValidTables.contains(name);
+ }
+
private void createSecureTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE secure (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1019fa8..6a5290e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -83,6 +83,9 @@ public class SettingsProvider extends ContentProvider {
SqlArguments(Uri url, String where, String[] args) {
if (url.getPathSegments().size() == 1) {
this.table = url.getPathSegments().get(0);
+ if (!DatabaseHelper.isValidTable(this.table)) {
+ throw new IllegalArgumentException("Bad root path: " + this.table);
+ }
this.where = where;
this.args = args;
} else if (url.getPathSegments().size() != 2) {
@@ -91,6 +94,9 @@ public class SettingsProvider extends ContentProvider {
throw new UnsupportedOperationException("WHERE clause not supported: " + url);
} else {
this.table = url.getPathSegments().get(0);
+ if (!DatabaseHelper.isValidTable(this.table)) {
+ throw new IllegalArgumentException("Bad root path: " + this.table);
+ }
if ("system".equals(this.table) || "secure".equals(this.table)) {
this.where = Settings.NameValueTable.NAME + "=?";
this.args = new String[] { url.getPathSegments().get(1) };
@@ -105,6 +111,9 @@ public class SettingsProvider extends ContentProvider {
SqlArguments(Uri url) {
if (url.getPathSegments().size() == 1) {
this.table = url.getPathSegments().get(0);
+ if (!DatabaseHelper.isValidTable(this.table)) {
+ throw new IllegalArgumentException("Bad root path: " + this.table);
+ }
this.where = null;
this.args = null;
} else {
diff --git a/packages/SystemUI/res/layout/intruder_alert.xml b/packages/SystemUI/res/layout/intruder_alert.xml
index 24eb960..ba4a774 100644
--- a/packages/SystemUI/res/layout/intruder_alert.xml
+++ b/packages/SystemUI/res/layout/intruder_alert.xml
@@ -44,7 +44,7 @@
android:id="@+id/alertIcon"
android:layout_width="25dip"
android:layout_height="25dip"
- android:paddingLeft="6dip"
+ android:layout_marginLeft="6dip"
android:layout_marginRight="8dip"
/>
<TextView
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..11d0b7a 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,29 @@ 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();
+ final int permission = mContext.checkCallingPermission(
+ android.Manifest.permission.INSTALL_PACKAGES);
+ final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ 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 +7143,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 +7710,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 +8711,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 +9090,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 +9108,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 +9206,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 {
diff --git a/tests/StatusBar/res/drawable-hdpi/stat_sys_phone.png b/tests/StatusBar/res/drawable-hdpi/stat_sys_phone.png
new file mode 100644
index 0000000..9815553
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/stat_sys_phone.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/stat_sys_phone.png b/tests/StatusBar/res/drawable-mdpi/stat_sys_phone.png
new file mode 100644
index 0000000..402abc7
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/stat_sys_phone.png
Binary files differ
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index c6a4134..b665d2f 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -89,11 +89,12 @@ public class StatusBarTest extends TestActivity
new Test("Priority notification") {
public void run() {
Notification not = new Notification(StatusBarTest.this,
- R.drawable.ic_statusbar_missedcall,
- "tick tick tick",
+ R.drawable.stat_sys_phone,
+ "Incoming call from: Imperious Leader",
System.currentTimeMillis()-(1000*60*60*24),
- "(453) 123-2328",
- "", null
+ "Imperious Leader",
+ "(888) 555-5038",
+ null
);
not.flags |= Notification.FLAG_HIGH_PRIORITY;
Intent fullScreenIntent = new Intent(StatusBarTest.this, TestAlertActivity.class);