diff options
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 Binary files differdeleted file mode 100644 index 2d13237..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 950713b..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 2d13237..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 7abfd19..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index c44d062..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 7abfd19..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png +++ /dev/null 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 Binary files differnew file mode 100644 index 0000000..f1f666a --- /dev/null +++ b/docs/html/images/axis_device.png diff --git a/docs/html/images/axis_globe.png b/docs/html/images/axis_globe.png Binary files differnew file mode 100644 index 0000000..dccb58b --- /dev/null +++ b/docs/html/images/axis_globe.png 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 +— 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(¶m); + param.nPortIndex = kPortIndexOutput; + for (param.nProfileIndex = 0;; ++param.nProfileIndex) { + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamVideoProfileLevelQuerySupported, + ¶m, 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 Binary files differnew file mode 100644 index 0000000..9815553 --- /dev/null +++ b/tests/StatusBar/res/drawable-hdpi/stat_sys_phone.png diff --git a/tests/StatusBar/res/drawable-mdpi/stat_sys_phone.png b/tests/StatusBar/res/drawable-mdpi/stat_sys_phone.png Binary files differnew file mode 100644 index 0000000..402abc7 --- /dev/null +++ b/tests/StatusBar/res/drawable-mdpi/stat_sys_phone.png 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); |