diff options
151 files changed, 5638 insertions, 3914 deletions
diff --git a/api/current.xml b/api/current.xml index 9145462f7add..5d71cad53102 100644 --- a/api/current.xml +++ b/api/current.xml @@ -87294,6 +87294,21 @@ <parameter name="quality" type="int"> </parameter> </method> +<method name="get" + return="android.media.CamcorderProfile" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="cameraId" type="int"> +</parameter> +<parameter name="quality" type="int"> +</parameter> +</method> <field name="QUALITY_HIGH" type="int" transient="false" @@ -87466,6 +87481,21 @@ <parameter name="quality" type="int"> </parameter> </method> +<method name="getJpegEncodingQualityParameter" + return="int" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="cameraId" type="int"> +</parameter> +<parameter name="quality" type="int"> +</parameter> +</method> <field name="QUALITY_HIGH" type="int" transient="false" diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index 75948a5d532c..10668a496fb5 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -1031,6 +1031,7 @@ void CameraService::Client::handleShutter(image_rect_type *size) { mHardware->getRawHeap()); mSurface->registerBuffers(buffers); + IPCThreadState::self()->flushCommands(); } mLock.unlock(); diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk index 33696f4eae5c..9a972841460a 100644 --- a/cmds/stagefright/Android.mk +++ b/cmds/stagefright/Android.mk @@ -7,7 +7,7 @@ LOCAL_SRC_FILES:= \ SineSource.cpp LOCAL_SHARED_LIBRARIES := \ - libstagefright libmedia libutils libbinder + libstagefright libmedia libutils libbinder libstagefright_foundation LOCAL_C_INCLUDES:= \ $(JNI_H_INCLUDE) \ diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 877b90878d68..b7a3f99fd833 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -38,6 +38,9 @@ #include <media/stagefright/OMXCodec.h> #include <media/mediametadataretriever.h> +#include <media/stagefright/foundation/hexdump.h> +#include <media/stagefright/MPEG4Writer.h> + using namespace android; static long gNumRepetitions; @@ -45,6 +48,8 @@ static long gMaxNumFrames; // 0 means decode all available. static long gReproduceBug; // if not -1. static bool gPreferSoftwareCodec; static bool gPlaybackAudio; +static bool gWriteMP4; +static String8 gWriteMP4Filename; static int64_t getNowUs() { struct timeval tv; @@ -258,6 +263,21 @@ static void playSource(OMXClient *client, const sp<MediaSource> &source) { } } +static void writeSourceToMP4(const sp<MediaSource> &source) { + sp<MPEG4Writer> writer = + new MPEG4Writer(gWriteMP4Filename.string()); + + CHECK_EQ(writer->addSource(source), OK); + + sp<MetaData> params = new MetaData; + CHECK_EQ(writer->start(), OK); + + while (!writer->reachedEOS()) { + usleep(100000); + } + writer->stop(); +} + static void usage(const char *me) { fprintf(stderr, "usage: %s\n", me); fprintf(stderr, " -h(elp)\n"); @@ -270,6 +290,7 @@ static void usage(const char *me) { fprintf(stderr, " -t(humbnail) extract video thumbnail or album art\n"); fprintf(stderr, " -s(oftware) prefer software codec\n"); fprintf(stderr, " -o playback audio\n"); + fprintf(stderr, " -w(rite) filename (write to .mp4 file)\n"); } int main(int argc, char **argv) { @@ -284,9 +305,10 @@ int main(int argc, char **argv) { gReproduceBug = -1; gPreferSoftwareCodec = false; gPlaybackAudio = false; + gWriteMP4 = false; int res; - while ((res = getopt(argc, argv, "han:lm:b:ptso")) >= 0) { + while ((res = getopt(argc, argv, "han:lm:b:ptsow:")) >= 0) { switch (res) { case 'a': { @@ -322,6 +344,13 @@ int main(int argc, char **argv) { break; } + case 'w': + { + gWriteMP4 = true; + gWriteMP4Filename.setTo(optarg); + break; + } + case 'p': { dumpProfiles = true; @@ -554,7 +583,11 @@ int main(int argc, char **argv) { mediaSource = extractor->getTrack(i); } - playSource(&client, mediaSource); + if (gWriteMP4) { + writeSourceToMP4(mediaSource); + } else { + playSource(&client, mediaSource); + } } client.disconnect(); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 9b9ae5274365..985f5913ae93 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -39,7 +39,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.RemoteException; import android.text.Selection; import android.text.SpannableStringBuilder; diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index e56fee99cb10..1fe85e6c1756 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1021,16 +1021,6 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } - case REPORT_PSS_TRANSACTION: { - data.enforceInterface(IActivityManager.descriptor); - IBinder b = data.readStrongBinder(); - IApplicationThread app = ApplicationThreadNative.asInterface(b); - int pss = data.readInt(); - reportPss(app, pss); - reply.writeNoException(); - return true; - } - case START_RUNNING_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); String pkg = data.readString(); @@ -2529,14 +2519,6 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return res; } - public void reportPss(IApplicationThread caller, int pss) throws RemoteException { - Parcel data = Parcel.obtain(); - data.writeInterfaceToken(IActivityManager.descriptor); - data.writeStrongBinder(caller.asBinder()); - data.writeInt(pss); - mRemote.transact(REPORT_PSS_TRANSACTION, data, null, 0); - data.recycle(); - } public void startRunning(String pkg, String cls, String action, String indata) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 03bb858376e6..883366ba361d 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -616,14 +616,6 @@ public final class ActivityThread { queueOrSendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token); } - public void requestPss() { - try { - ActivityManagerNative.getDefault().reportPss(this, - (int)Process.getPss(Process.myPid())); - } catch (RemoteException e) { - } - } - public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) { ProfilerControlData pcd = new ProfilerControlData(); pcd.path = path; diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 360959d65517..1c20062f3f84 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -341,13 +341,6 @@ public abstract class ApplicationThreadNative extends Binder return true; } - case REQUEST_PSS_TRANSACTION: - { - data.enforceInterface(IApplicationThread.descriptor); - requestPss(); - return true; - } - case PROFILER_CONTROL_TRANSACTION: { data.enforceInterface(IApplicationThread.descriptor); @@ -779,14 +772,6 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } - public final void requestPss() throws RemoteException { - Parcel data = Parcel.obtain(); - data.writeInterfaceToken(IApplicationThread.descriptor); - mRemote.transact(REQUEST_PSS_TRANSACTION, data, null, - IBinder.FLAG_ONEWAY); - data.recycle(); - } - public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index bf02d5a14c68..20c9a80c430a 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -247,8 +247,6 @@ public interface IActivityManager extends IInterface { public boolean killPids(int[] pids, String reason) throws RemoteException; - public void reportPss(IApplicationThread caller, int pss) throws RemoteException; - // Special low-level communication with activity manager. public void startRunning(String pkg, String cls, String action, String data) throws RemoteException; @@ -502,7 +500,7 @@ public interface IActivityManager extends IInterface { int FORCE_STOP_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+78; int KILL_PIDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79; int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80; - int REPORT_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81; + int GET_RUNNING_APP_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+82; int GET_DEVICE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+83; int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84; diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index ffb86515bdd3..c8ef17f1b7a9 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -95,7 +95,6 @@ public interface IApplicationThread extends IInterface { throws RemoteException; void scheduleLowMemory() throws RemoteException; void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException; - void requestPss() throws RemoteException; void profilerControl(boolean start, String path, ParcelFileDescriptor fd) throws RemoteException; void setSchedulingGroup(int group) throws RemoteException; @@ -132,7 +131,7 @@ public interface IApplicationThread extends IInterface { int SCHEDULE_LOW_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+23; int SCHEDULE_ACTIVITY_CONFIGURATION_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+24; int SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+25; - int REQUEST_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26; + int PROFILER_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27; int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28; int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29; diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java index 429d16498aa8..d72dda7842fe 100644 --- a/core/java/android/app/NativeActivity.java +++ b/core/java/android/app/NativeActivity.java @@ -5,10 +5,18 @@ import dalvik.system.PathClassLoader; 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; import android.view.InputQueue; +import android.view.KeyEvent; +import android.view.Surface; import android.view.SurfaceHolder; +import android.view.View; import java.io.File; @@ -22,7 +30,13 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, private int mNativeHandle; - private native int loadNativeCode(String path); + private InputQueue mCurInputQueue; + private SurfaceHolder mCurSurfaceHolder; + + private boolean mDestroyed; + + 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); @@ -32,10 +46,10 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, private native void onStopNative(int handle); private native void onLowMemoryNative(int handle); private native void onWindowFocusChangedNative(int handle, boolean focused); - private native void onSurfaceCreatedNative(int handle, SurfaceHolder holder); - private native void onSurfaceChangedNative(int handle, SurfaceHolder holder, + private native void onSurfaceCreatedNative(int handle, Surface surface); + private native void onSurfaceChangedNative(int handle, Surface surface, int format, int width, int height); - private native void onSurfaceDestroyedNative(int handle, SurfaceHolder holder); + private native void onSurfaceDestroyedNative(int handle); private native void onInputChannelCreatedNative(int handle, InputChannel channel); private native void onInputChannelDestroyedNative(int handle, InputChannel channel); @@ -46,6 +60,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, getWindow().takeSurface(this); getWindow().takeInputQueue(this); + getWindow().setFormat(PixelFormat.RGB_565); try { ai = getPackageManager().getActivityInfo( @@ -78,7 +93,11 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, throw new IllegalArgumentException("Unable to find native library: " + libname); } - mNativeHandle = loadNativeCode(path); + 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); } @@ -87,6 +106,15 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, @Override protected void onDestroy() { + mDestroyed = true; + if (mCurSurfaceHolder != null) { + onSurfaceDestroyedNative(mNativeHandle); + mCurSurfaceHolder = null; + } + if (mCurInputQueue != null) { + onInputChannelDestroyedNative(mNativeHandle, mCurInputQueue.getInputChannel()); + mCurInputQueue = null; + } unloadNativeCode(mNativeHandle); super.onDestroy(); } @@ -124,32 +152,66 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback, @Override public void onLowMemory() { super.onLowMemory(); - onLowMemoryNative(mNativeHandle); + if (!mDestroyed) { + onLowMemoryNative(mNativeHandle); + } } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); - onWindowFocusChangedNative(mNativeHandle, hasFocus); + if (!mDestroyed) { + onWindowFocusChangedNative(mNativeHandle, hasFocus); + } } public void surfaceCreated(SurfaceHolder holder) { - onSurfaceCreatedNative(mNativeHandle, holder); + if (!mDestroyed) { + mCurSurfaceHolder = holder; + onSurfaceCreatedNative(mNativeHandle, holder.getSurface()); + } } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - onSurfaceChangedNative(mNativeHandle, holder, format, width, height); + if (!mDestroyed) { + mCurSurfaceHolder = holder; + onSurfaceChangedNative(mNativeHandle, holder.getSurface(), format, width, height); + } } public void surfaceDestroyed(SurfaceHolder holder) { - onSurfaceDestroyedNative(mNativeHandle, holder); + mCurSurfaceHolder = null; + if (!mDestroyed) { + onSurfaceDestroyedNative(mNativeHandle); + } } public void onInputQueueCreated(InputQueue queue) { - onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel()); + if (!mDestroyed) { + mCurInputQueue = queue; + onInputChannelCreatedNative(mNativeHandle, queue.getInputChannel()); + } } public void onInputQueueDestroyed(InputQueue queue) { - onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel()); + mCurInputQueue = null; + if (!mDestroyed) { + onInputChannelDestroyedNative(mNativeHandle, queue.getInputChannel()); + } + } + + void dispatchUnhandledKeyEvent(KeyEvent event) { + View decor = getWindow().getDecorView(); + if (decor != null) { + decor.dispatchKeyEvent(event); + } + } + + void setWindowFlags(int flags, int mask) { + getWindow().setFlags(flags, mask); + } + + void setWindowFormat(int format) { + getWindow().setFormat(format); } } diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index e2f5ada99e73..3490ac0e7d77 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 dfefe7e3dbd3..70519ff26fd6 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 f60e2d766f14..492f8cc4d508 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/hardware/Usb.java b/core/java/android/hardware/Usb.java index e9c2cf7b61dd..57271d4b72f8 100644 --- a/core/java/android/hardware/Usb.java +++ b/core/java/android/hardware/Usb.java @@ -39,6 +39,27 @@ public class Usb { public static final String ACTION_USB_DISCONNECTED = "android.hardware.action.USB_DISCONNECTED"; + /** + * Broadcast Action: A sticky broadcast for USB state change events. + * + * This is a sticky broadcast for clients that are interested in both USB connect and + * disconnect events. If you are only concerned with one or the other, you can use + * {@link #ACTION_USB_CONNECTED} or {@link #ACTION_USB_DISCONNECTED} to avoid receiving + * unnecessary broadcasts. The boolean {@link #USB_CONNECTED} extra indicates whether + * USB is connected or disconnected. + * The extras bundle will also contain name/value pairs with the name of the function + * and a value of either {@link #USB_FUNCTION_ENABLED} or {@link #USB_FUNCTION_DISABLED}. + * Possible USB function names include {@link #USB_FUNCTION_MASS_STORAGE}, + * {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS} and {@link #USB_FUNCTION_MTP}. + */ + public static final String ACTION_USB_STATE = + "android.hardware.action.USB_STATE"; + + /** + * Boolean extra indicating whether USB is connected or disconnected. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast. + */ + public static final String USB_CONNECTED = "connected"; /** * Name of the USB mass storage USB function. diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index a8c6f9b59990..9ad125b312e1 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -35,6 +35,11 @@ import java.security.cert.Certificate; import java.security.cert.X509Certificate; import javax.net.SocketFactory; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; @@ -48,17 +53,33 @@ import org.apache.harmony.xnet.provider.jsse.SSLParameters; /** * SSLSocketFactory implementation with several extra features: + * * <ul> * <li>Timeout specification for SSL handshake operations + * <li>Hostname verification in most cases (see WARNINGs below) * <li>Optional SSL session caching with {@link SSLSessionCache} * <li>Optionally bypass all SSL certificate checks * </ul> - * Note that the handshake timeout does not apply to actual connection. - * If you want a connection timeout as well, use {@link #createSocket()} and - * {@link Socket#connect(SocketAddress, int)}. - * <p> - * On development devices, "setprop socket.relaxsslcheck yes" bypasses all - * SSL certificate checks, for testing with development servers. + * + * The handshake timeout does not apply to actual TCP socket connection. + * If you want a connection timeout as well, use {@link #createSocket()} + * and {@link Socket#connect(SocketAddress, int)}, after which you + * must verify the identity of the server you are connected to. + * + * <p class="caution"><b>Most {@link SSLSocketFactory} implementations do not + * verify the server's identity, allowing man-in-the-middle attacks.</b> + * This implementation does check the server's certificate hostname, but only + * for createSocket variants that specify a hostname. When using methods that + * use {@link InetAddress} or which return an unconnected socket, you MUST + * verify the server's identity yourself to ensure a secure connection.</p> + * + * <p>One way to verify the server's identity is to use + * {@link HttpsURLConnection#getDefaultHostnameVerifier()} to get a + * {@link HostnameVerifier} to verify the certificate hostname. + * + * <p>On development devices, "setprop socket.relaxsslcheck yes" bypasses all + * SSL certificate and hostname checks for testing purposes. This setting + * requires root access. */ public class SSLCertificateSocketFactory extends SSLSocketFactory { private static final String TAG = "SSLCertificateSocketFactory"; @@ -71,6 +92,9 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } }; + private static final HostnameVerifier HOSTNAME_VERIFIER = + HttpsURLConnection.getDefaultHostnameVerifier(); + private SSLSocketFactory mInsecureFactory = null; private SSLSocketFactory mSecureFactory = null; @@ -95,7 +119,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { * * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 * for none. The socket timeout is reset to 0 after the handshake. - * @return a new SocketFactory with the specified parameters + * @return a new SSLSocketFactory with the specified parameters */ public static SocketFactory getDefault(int handshakeTimeoutMillis) { return new SSLCertificateSocketFactory(handshakeTimeoutMillis, null, true); @@ -108,7 +132,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 * for none. The socket timeout is reset to 0 after the handshake. * @param cache The {@link SSLClientSessionCache} to use, or null for no cache. - * @return a new SocketFactory with the specified parameters + * @return a new SSLSocketFactory with the specified parameters */ public static SSLSocketFactory getDefault(int handshakeTimeoutMillis, SSLSessionCache cache) { return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true); @@ -117,13 +141,14 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { /** * Returns a new instance of a socket factory with all SSL security checks * disabled, using an optional handshake timeout and SSL session cache. - * Sockets created using this factory are vulnerable to man-in-the-middle - * attacks! + * + * <p class="caution"><b>Warning:</b> Sockets created using this factory + * are vulnerable to man-in-the-middle attacks!</p> * * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 * for none. The socket timeout is reset to 0 after the handshake. * @param cache The {@link SSLClientSessionCache} to use, or null for no cache. - * @return an insecure SocketFactory with the specified parameters + * @return an insecure SSLSocketFactory with the specified parameters */ public static SSLSocketFactory getInsecure(int handshakeTimeoutMillis, SSLSessionCache cache) { return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, false); @@ -145,6 +170,44 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true)); } + /** + * Verify the hostname of the certificate used by the other end of a + * connected socket. You MUST call this if you did not supply a hostname + * to {@link #createSocket()}. It is harmless to call this method + * redundantly if the hostname has already been verified. + * + * <p>Wildcard certificates are allowed to verify any matching hostname, + * so "foo.bar.example.com" is verified if the peer has a certificate + * for "*.example.com". + * + * @param socket An SSL socket which has been connected to a server + * @param hostname The expected hostname of the remote server + * @throws IOException if something goes wrong handshaking with the server + * @throws SSLPeerUnverifiedException if the server cannot prove its identity + * + * @hide + */ + public static void verifyHostname(Socket socket, String hostname) throws IOException { + if (!(socket instanceof SSLSocket)) { + throw new IllegalArgumentException("Attempt to verify non-SSL socket"); + } + + if (!isSslCheckRelaxed()) { + // The code at the start of OpenSSLSocketImpl.startHandshake() + // ensures that the call is idempotent, so we can safely call it. + SSLSocket ssl = (SSLSocket) socket; + ssl.startHandshake(); + + SSLSession session = ssl.getSession(); + if (session == null) { + throw new SSLException("Cannot verify SSL socket without session"); + } + if (!HOSTNAME_VERIFIER.verify(hostname, session)) { + throw new SSLPeerUnverifiedException("Cannot verify hostname: " + hostname); + } + } + } + private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) { try { SSLContextImpl sslContext = new SSLContextImpl(); @@ -156,10 +219,14 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } } + private static boolean isSslCheckRelaxed() { + return "1".equals(SystemProperties.get("ro.debuggable")) && + "yes".equals(SystemProperties.get("socket.relaxsslcheck")); + } + private synchronized SSLSocketFactory getDelegate() { // Relax the SSL check if instructed (for this factory, or systemwide) - if (!mSecure || ("1".equals(SystemProperties.get("ro.debuggable")) && - "yes".equals(SystemProperties.get("socket.relaxsslcheck")))) { + if (!mSecure || isSslCheckRelaxed()) { if (mInsecureFactory == null) { if (mSecure) { Log.w(TAG, "*** BYPASSING SSL SECURITY CHECKS (socket.relaxsslcheck=yes) ***"); @@ -177,13 +244,27 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } } + /** + * {@inheritDoc} + * + * <p>This method verifies the peer's certificate hostname after connecting. + */ @Override public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close); s.setHandshakeTimeout(mHandshakeTimeoutMillis); + verifyHostname(s, host); return s; } + /** + * Creates a new socket which is not connected to any remote host. + * You must use {@link Socket#connect} to connect the socket. + * + * <p class="caution"><b>Warning:</b> Hostname verification is not performed + * with this method. You MUST verify the server's identity after connecting + * the socket to avoid man-in-the-middle attacks.</p> + */ @Override public Socket createSocket() throws IOException { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(); @@ -191,6 +272,13 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { return s; } + /** + * {@inheritDoc} + * + * <p class="caution"><b>Warning:</b> Hostname verification is not performed + * with this method. You MUST verify the server's identity after connecting + * the socket to avoid man-in-the-middle attacks.</p> + */ @Override public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort) throws IOException { @@ -200,6 +288,13 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { return s; } + /** + * {@inheritDoc} + * + * <p class="caution"><b>Warning:</b> Hostname verification is not performed + * with this method. You MUST verify the server's identity after connecting + * the socket to avoid man-in-the-middle attacks.</p> + */ @Override public Socket createSocket(InetAddress addr, int port) throws IOException { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port); @@ -207,19 +302,31 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { return s; } + /** + * {@inheritDoc} + * + * <p>This method verifies the peer's certificate hostname after connecting. + */ @Override public Socket createSocket(String host, int port, InetAddress localAddr, int localPort) throws IOException { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket( host, port, localAddr, localPort); s.setHandshakeTimeout(mHandshakeTimeoutMillis); + verifyHostname(s, host); return s; } + /** + * {@inheritDoc} + * + * <p>This method verifies the peer's certificate hostname after connecting. + */ @Override public Socket createSocket(String host, int port) throws IOException { OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port); s.setHandshakeTimeout(mHandshakeTimeoutMillis); + verifyHostname(s, host); return s; } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 5640a0627721..f695dbb4cdd4 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -626,6 +626,15 @@ public class Process { throws IllegalArgumentException, SecurityException; /** + * Call with 'false' to cause future calls to {@link #setThreadPriority(int)} to + * throw an exception if passed a background-level thread priority. This is only + * effective if the JNI layer is built with GUARD_THREAD_PRIORITY defined to 1. + * + * @hide + */ + public static final native void setCanSelfBackground(boolean backgroundOk); + + /** * Sets the scheduling group for a thread. * @hide * @param tid The indentifier of the thread/process to change. diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index ae9746e96373..0bfb6d69c951 100755 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -676,33 +676,12 @@ public class KeyEvent implements Parcelable { * TODO: should the dpad keys be here? arguably, because they also shouldn't be menu shortcuts */ public final boolean isSystem() { - switch (mKeyCode) { - case KEYCODE_MENU: - case KEYCODE_SOFT_RIGHT: - case KEYCODE_HOME: - case KEYCODE_BACK: - case KEYCODE_CALL: - case KEYCODE_ENDCALL: - case KEYCODE_VOLUME_UP: - case KEYCODE_VOLUME_DOWN: - case KEYCODE_MUTE: - case KEYCODE_POWER: - 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: - case KEYCODE_CAMERA: - case KEYCODE_FOCUS: - case KEYCODE_SEARCH: - case KEYCODE_PICTSYMBOLS: - case KEYCODE_SWITCH_CHARSET: - return true; - default: - return false; - } + return native_isSystemKey(mKeyCode); + } + + /** @hide */ + public final boolean hasDefaultAction() { + return native_hasDefaultAction(mKeyCode); } @@ -1226,4 +1205,7 @@ public class KeyEvent implements Parcelable { mDownTime = in.readLong(); mEventTime = in.readLong(); } + + private native boolean native_isSystemKey(int keyCode); + private native boolean native_hasDefaultAction(int keyCode); } diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 2369d25bd472..127ed68dff98 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -126,6 +126,11 @@ public class PowerProfile { public static final String POWER_CPU_SPEEDS = "cpu.speeds"; + /** + * Battery capacity in milliAmpHour (mAh). + */ + public static final String POWER_BATTERY_CAPACITY = "battery.capacity"; + static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>(); private static final String TAG_DEVICE = "device"; @@ -243,6 +248,19 @@ public class PowerProfile { } } + /** + * Returns the battery capacity, if available, in milli Amp Hours. If not available, + * it returns zero. + * @return the battery capacity in mAh + */ + public double getBatteryCapacity() { + return getAveragePower(POWER_BATTERY_CAPACITY); + } + + /** + * Returns the number of speeds that the CPU can be run at. + * @return + */ public int getNumSpeedSteps() { Object value = sPowerMap.get(POWER_CPU_SPEEDS); if (value != null && value instanceof Double[]) { diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 5e5e47e0ac57..af61b805b794 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -17,47 +17,121 @@ #define LOG_TAG "NativeActivity" #include <utils/Log.h> -#include "JNIHelp.h" -#include "android_view_InputChannel.h" +#include <poll.h> +#include <dlfcn.h> + #include <android_runtime/AndroidRuntime.h> #include <android/native_activity.h> +#include <surfaceflinger/Surface.h> +#include <ui/egl/android_natives.h> #include <ui/InputTransport.h> +#include <utils/PollLoop.h> -#include <dlfcn.h> +#include "JNIHelp.h" +#include "android_os_MessageQueue.h" +#include "android_view_InputChannel.h" +#include "android_view_KeyEvent.h" +#include "android_view_Surface.h" namespace android { +static struct { + jclass clazz; + + jmethodID dispatchUnhandledKeyEvent; + jmethodID setWindowFlags; + jmethodID setWindowFormat; +} gNativeActivityClassInfo; + +// ------------------------------------------------------------------------ + +/* + * Specialized input queue that allows unhandled key events to be dispatched + * back to the native activity's Java framework code. + */ +struct MyInputQueue : AInputQueue { + explicit MyInputQueue(const android::sp<android::InputChannel>& channel, int workWrite) + : AInputQueue(channel), mWorkWrite(workWrite) { + } + + virtual void doDefaultKey(android::KeyEvent* keyEvent) { + 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)); + } + mPendingKeys.add(keyEvent); + mLock.unlock(); + } + + KeyEvent* getNextEvent() { + KeyEvent* event = NULL; + + mLock.lock(); + if (mPendingKeys.size() > 0) { + event = mPendingKeys[0]; + mPendingKeys.removeAt(0); + } + mLock.unlock(); + + return event; + } + + int mWorkWrite; + + Mutex mLock; + Vector<KeyEvent*> mPendingKeys; +}; + +// ------------------------------------------------------------------------ + +/* + * Native state for interacting with the NativeActivity class. + */ struct NativeCode { - NativeCode(void* _dlhandle, android_activity_create_t* _createFunc) { + NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) { memset(&activity, sizeof(activity), 0); memset(&callbacks, sizeof(callbacks), 0); dlhandle = _dlhandle; createActivityFunc = _createFunc; - surface = NULL; + nativeWindow = NULL; inputChannel = NULL; nativeInputQueue = NULL; + mainWorkRead = mainWorkWrite = -1; } ~NativeCode() { + if (activity.env != NULL && activity.clazz != NULL) { + activity.env->DeleteGlobalRef(activity.clazz); + } + if (pollLoop != NULL && mainWorkRead >= 0) { + pollLoop->removeCallback(mainWorkRead); + } + if (nativeInputQueue != NULL) { + nativeInputQueue->mWorkWrite = -1; + } setSurface(NULL); setInputChannel(NULL); if (callbacks.onDestroy != NULL) { callbacks.onDestroy(&activity); } + if (mainWorkRead >= 0) close(mainWorkRead); + if (mainWorkWrite >= 0) close(mainWorkWrite); if (dlhandle != NULL) { - dlclose(dlhandle); + // for now don't unload... we probably should clean this + // up and only keep one open dlhandle per proc, since there + // is really no benefit to unloading the code. + //dlclose(dlhandle); } } void setSurface(jobject _surface) { - if (surface != NULL) { - activity.env->DeleteGlobalRef(surface); - } if (_surface != NULL) { - surface = activity.env->NewGlobalRef(_surface); + nativeWindow = android_Surface_getNativeWindow(activity.env, _surface); } else { - surface = NULL; + nativeWindow = NULL; } } @@ -73,7 +147,7 @@ struct NativeCode { sp<InputChannel> ic = android_view_InputChannel_getInputChannel(activity.env, _channel); if (ic != NULL) { - nativeInputQueue = new input_queue_t(ic); + nativeInputQueue = new MyInputQueue(ic, mainWorkWrite); if (nativeInputQueue->getConsumer().initialize() != android::OK) { delete nativeInputQueue; nativeInputQueue = NULL; @@ -86,19 +160,55 @@ struct NativeCode { return OK; } - android_activity_t activity; - android_activity_callbacks_t callbacks; + ANativeActivity activity; + ANativeActivityCallbacks callbacks; void* dlhandle; - android_activity_create_t* createActivityFunc; + ANativeActivity_createFunc* createActivityFunc; + + String8 internalDataPath; + String8 externalDataPath; - jobject surface; + sp<ANativeWindow> nativeWindow; jobject inputChannel; - struct input_queue_t* nativeInputQueue; + struct MyInputQueue* nativeInputQueue; + + // These are used to wake up the main thread to process work. + int mainWorkRead; + int mainWorkWrite; + sp<PollLoop> pollLoop; }; +// ------------------------------------------------------------------------ + +/* + * Callback for handling native events on the application's main thread. + */ +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); + } + } + } + + return true; +} + +// ------------------------------------------------------------------------ + static jint -loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path) +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; @@ -108,16 +218,52 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path) env->ReleaseStringUTFChars(path, pathStr); if (handle != NULL) { - code = new NativeCode(handle, (android_activity_create_t*) - dlsym(handle, "android_onCreateActivity")); + code = new NativeCode(handle, (ANativeActivity_createFunc*) + dlsym(handle, "ANativeActivity_onCreate")); if (code->createActivityFunc == NULL) { - LOGW("android_onCreateActivity not found"); + LOGW("ANativeActivity_onCreate not found"); + delete code; + return 0; + } + + code->pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueue); + if (code->pollLoop == NULL) { + LOGW("Unable to retrieve MessageQueue's PollLoop"); + delete code; + return 0; + } + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGW("could not create pipe: %s", strerror(errno)); delete code; return 0; } + code->mainWorkRead = msgpipe[0]; + code->mainWorkWrite = msgpipe[1]; + code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code); + code->activity.callbacks = &code->callbacks; + if (env->GetJavaVM(&code->activity.vm) < 0) { + LOGW("NativeActivity GetJavaVM failed"); + delete code; + return 0; + } code->activity.env = env; - code->activity.clazz = clazz; + code->activity.clazz = env->NewGlobalRef(clazz); + + const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL); + code->internalDataPath = dirStr; + code->activity.internalDataPath = code->internalDataPath.string(); + env->ReleaseStringUTFChars(path, dirStr); + + dirStr = env->GetStringUTFChars(externalDataDir, NULL); + code->externalDataPath = dirStr; + code->activity.externalDataPath = code->externalDataPath.string(); + env->ReleaseStringUTFChars(path, dirStr); + + code->activity.sdkVersion = sdkVersion; + code->createActivityFunc(&code->activity, NULL, 0); } @@ -217,9 +363,9 @@ onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface if (handle != 0) { NativeCode* code = (NativeCode*)handle; code->setSurface(surface); - if (code->callbacks.onSurfaceCreated != NULL) { - code->callbacks.onSurfaceCreated(&code->activity, - (android_surface_t*)code->surface); + if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) { + code->callbacks.onNativeWindowCreated(&code->activity, + code->nativeWindow.get()); } } } @@ -230,9 +376,17 @@ onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface { if (handle != 0) { NativeCode* code = (NativeCode*)handle; - if (code->surface != NULL && code->callbacks.onSurfaceChanged != NULL) { - code->callbacks.onSurfaceChanged(&code->activity, - (android_surface_t*)code->surface, format, width, height); + sp<ANativeWindow> oldNativeWindow = code->nativeWindow; + code->setSurface(surface); + if (oldNativeWindow != code->nativeWindow) { + if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { + code->callbacks.onNativeWindowDestroyed(&code->activity, + oldNativeWindow.get()); + } + if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) { + code->callbacks.onNativeWindowCreated(&code->activity, + code->nativeWindow.get()); + } } } } @@ -242,9 +396,9 @@ onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surfa { if (handle != 0) { NativeCode* code = (NativeCode*)handle; - if (code->surface != NULL && code->callbacks.onSurfaceDestroyed != NULL) { - code->callbacks.onSurfaceDestroyed(&code->activity, - (android_surface_t*)code->surface); + if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { + code->callbacks.onNativeWindowDestroyed(&code->activity, + code->nativeWindow.get()); } code->setSurface(NULL); } @@ -283,7 +437,8 @@ onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject } static const JNINativeMethod g_methods[] = { - { "loadNativeCode", "(Ljava/lang/String;)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 }, @@ -292,23 +447,40 @@ static const JNINativeMethod g_methods[] = { { "onStopNative", "(I)V", (void*)onStop_native }, { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native }, { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native }, - { "onSurfaceCreatedNative", "(ILandroid/view/SurfaceHolder;)V", (void*)onSurfaceCreated_native }, - { "onSurfaceChangedNative", "(ILandroid/view/SurfaceHolder;III)V", (void*)onSurfaceChanged_native }, - { "onSurfaceDestroyedNative", "(ILandroid/view/SurfaceHolder;)V", (void*)onSurfaceDestroyed_native }, + { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native }, + { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native }, + { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native }, { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native }, { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native }, }; static const char* const kNativeActivityPathName = "android/app/NativeActivity"; +#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, fieldDescriptor) \ + var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method" methodName); + int register_android_app_NativeActivity(JNIEnv* env) { //LOGD("register_android_app_NativeActivity"); - jclass clazz; + FIND_CLASS(gNativeActivityClassInfo.clazz, kNativeActivityPathName); + + GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent, + gNativeActivityClassInfo.clazz, + "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V"); - clazz = env->FindClass(kNativeActivityPathName); - LOG_FATAL_IF(clazz == NULL, "Unable to find class android.app.NativeActivity"); + GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags, + gNativeActivityClassInfo.clazz, + "setWindowFlags", "(II)V"); + GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat, + gNativeActivityClassInfo.clazz, + "setWindowFormat", "(I)V"); return AndroidRuntime::registerNativeMethods( env, kNativeActivityPathName, diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp index 030d6c755143..961f806ea3f5 100644 --- a/core/jni/android_os_MessageQueue.cpp +++ b/core/jni/android_os_MessageQueue.cpp @@ -51,7 +51,11 @@ private: // ---------------------------------------------------------------------------- NativeMessageQueue::NativeMessageQueue() { - mPollLoop = new PollLoop(); + mPollLoop = PollLoop::getForThread(); + if (mPollLoop == NULL) { + mPollLoop = new PollLoop(); + PollLoop::setForThread(mPollLoop); + } } NativeMessageQueue::~NativeMessageQueue() { diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 68be741b8372..7c9927117fe3 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -52,9 +52,15 @@ pid_t gettid() { return syscall(__NR_gettid);} #endif #define POLICY_DEBUG 0 +#define GUARD_THREAD_PRIORITY 0 using namespace android; +#if GUARD_THREAD_PRIORITY +Mutex gKeyCreateMutex; +static pthread_key_t gBgKey = -1; +#endif + static void signalExceptionForPriorityError(JNIEnv* env, jobject obj, int err) { switch (err) { @@ -264,9 +270,41 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin closedir(d); } +static void android_os_Process_setCanSelfBackground(JNIEnv* env, jobject clazz, jboolean bgOk) { + // Establishes the calling thread as illegal to put into the background. + // Typically used only for the system process's main looper. +#if GUARD_THREAD_PRIORITY + LOGV("Process.setCanSelfBackground(%d) : tid=%d", bgOk, androidGetTid()); + { + Mutex::Autolock _l(gKeyCreateMutex); + if (gBgKey == -1) { + pthread_key_create(&gBgKey, NULL); + } + } + + // inverted: not-okay, we set a sentinel value + pthread_setspecific(gBgKey, (void*)(bgOk ? 0 : 0xbaad)); +#endif +} + void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz, jint pid, jint pri) { +#if GUARD_THREAD_PRIORITY + // if we're putting the current thread into the background, check the TLS + // to make sure this thread isn't guarded. If it is, raise an exception. + if (pri >= ANDROID_PRIORITY_BACKGROUND) { + if (pid == androidGetTid()) { + void* bgOk = pthread_getspecific(gBgKey); + if (bgOk == ((void*)0xbaad)) { + LOGE("Thread marked fg-only put self in background!"); + jniThrowException(env, "java/lang/SecurityException", "May not put this thread into background"); + return; + } + } + } +#endif + int rc = androidSetThreadPriority(pid, pri); if (rc != 0) { if (rc == INVALID_OPERATION) { @@ -852,6 +890,7 @@ static const JNINativeMethod methods[] = { {"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName}, {"getGidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName}, {"setThreadPriority", "(II)V", (void*)android_os_Process_setThreadPriority}, + {"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground}, {"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority}, {"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority}, {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup}, diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp index df3b95218deb..8f648f4e6286 100644 --- a/core/jni/android_view_KeyEvent.cpp +++ b/core/jni/android_view_KeyEvent.cpp @@ -76,8 +76,23 @@ void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, int32_t natur milliseconds_to_nanoseconds(eventTime)); } +static jboolean native_isSystemKey(JNIEnv* env, jobject clazz, jint keyCode) { + return KeyEvent::isSystemKey(keyCode); +} + +static jboolean native_hasDefaultAction(JNIEnv* env, jobject clazz, jint keyCode) { + return KeyEvent::hasDefaultAction(keyCode); +} + // ---------------------------------------------------------------------------- +static const JNINativeMethod g_methods[] = { + { "native_isSystemKey", "(I)Z", (void*)native_isSystemKey }, + { "native_hasDefaultAction", "(I)Z", (void*)native_hasDefaultAction }, +}; + +static const char* const kKeyEventPathName = "android/view/KeyEvent"; + #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ LOG_FATAL_IF(! var, "Unable to find class " className); \ @@ -92,8 +107,8 @@ void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, int32_t natur LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_view_KeyEvent(JNIEnv* env) { - FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); - + FIND_CLASS(gKeyEventClassInfo.clazz, kKeyEventPathName); + GET_METHOD_ID(gKeyEventClassInfo.ctor, gKeyEventClassInfo.clazz, "<init>", "(JJIIIIIII)V"); @@ -118,7 +133,9 @@ int register_android_view_KeyEvent(JNIEnv* env) { GET_FIELD_ID(gKeyEventClassInfo.mCharacters, gKeyEventClassInfo.clazz, "mCharacters", "Ljava/lang/String;"); - return 0; + return AndroidRuntime::registerNativeMethods( + env, kKeyEventPathName, + g_methods, NELEM(g_methods)); } } // namespace android diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index cef5c107ec86..a82abc93eacf 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -33,6 +33,7 @@ #include "jni.h" #include <android_runtime/AndroidRuntime.h> +#include "android_view_Surface.h" #include <utils/misc.h> @@ -179,7 +180,7 @@ static sp<Surface> getSurface(JNIEnv* env, jobject clazz) return result; } -EGLNativeWindowType android_Surface_getEGLNativeWindow( +sp<ANativeWindow> android_Surface_getNativeWindow( JNIEnv* env, jobject clazz) { return getSurface(env, clazz).get(); } diff --git a/core/jni/android_view_Surface.h b/core/jni/android_view_Surface.h new file mode 100644 index 000000000000..c37932e74c4f --- /dev/null +++ b/core/jni/android_view_Surface.h @@ -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. + */ + +#ifndef _ANDROID_VIEW_SURFACE_H +#define _ANDROID_VIEW_SURFACE_H + +#include <android/native_window.h> + +#include "jni.h" + +namespace android { + +extern sp<ANativeWindow> android_Surface_getNativeWindow( + JNIEnv* env, jobject clazz); + +} // namespace android + +#endif // _ANDROID_VIEW_SURFACE_H diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp index d5cde48b8538..866c038eac43 100644 --- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp @@ -25,10 +25,9 @@ #include <SkBitmap.h> #include <SkPixelRef.h> -namespace android { +#include "android_view_Surface.h" -extern EGLNativeWindowType android_Surface_getEGLNativeWindow( - JNIEnv* env, jobject clazz); +namespace android { static jclass gDisplay_class; static jclass gContext_class; @@ -325,7 +324,7 @@ static jint jni_eglCreateWindowSurface(JNIEnv *_env, jobject _this, jobject disp } EGLDisplay dpy = getDisplay(_env, display); EGLContext cnf = getConfig(_env, config); - EGLNativeWindowType window = 0; + sp<ANativeWindow> window; if (native_window == NULL) { not_valid_surface: doThrow(_env, "java/lang/IllegalArgumentException", @@ -333,12 +332,12 @@ not_valid_surface: return 0; } - window = android_Surface_getEGLNativeWindow(_env, native_window); + window = android_Surface_getNativeWindow(_env, native_window); if (window == NULL) goto not_valid_surface; jint* base = beginNativeAttribList(_env, attrib_list); - EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window, base); + EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window.get(), base); endNativeAttributeList(_env, attrib_list, base); return (jint)sur; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b1f81df7faf7..82f822f0dc67 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -82,6 +82,7 @@ <protected-broadcast android:name="android.hardware.action.USB_CONNECTED" /> <protected-broadcast android:name="android.hardware.action.USB_DISCONNECTED" /> + <protected-broadcast android:name="android.hardware.action.USB_STATE" /> <!-- ====================================== --> <!-- Permissions for things that cost money --> diff --git a/core/res/res/anim/priority_alert_enter.xml b/core/res/res/anim/priority_alert_enter.xml new file mode 100644 index 000000000000..c8ce23c6b66a --- /dev/null +++ b/core/res/res/anim/priority_alert_enter.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android" + > + <scale + android:interpolator="@android:anim/overshoot_interpolator" + android:fromXScale="0.7" android:toXScale="1.0" + android:fromYScale="0.7" android:toYScale="1.0" + android:pivotX="50%" android:pivotY="50%" + android:duration="@android:integer/config_shortAnimTime" /> + <alpha + android:interpolator="@android:anim/decelerate_interpolator" + android:fromAlpha="0.0" android:toAlpha="1.0" + android:duration="@android:integer/config_shortAnimTime" /> +</set> diff --git a/core/res/res/anim/priority_alert_exit.xml b/core/res/res/anim/priority_alert_exit.xml new file mode 100644 index 000000000000..b538cb28b5c9 --- /dev/null +++ b/core/res/res/anim/priority_alert_exit.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android" + > + <scale + android:interpolator="@android:anim/accelerate_interpolator" + android:fromXScale="1.0" android:toXScale="0.7" + android:fromYScale="1.0" android:toYScale="0.7" + android:pivotX="50%" android:pivotY="50%" + android:duration="@android:integer/config_shortAnimTime" /> + <alpha + android:interpolator="@android:anim/accelerate_interpolator" + android:fromAlpha="1.0" android:toAlpha="0.0" + android:duration="@android:integer/config_shortAnimTime" /> +</set> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index cffcd1d1a1e7..d565c68e5c57 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -22,7 +22,7 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Component to be used as the status bar service. Must implement the IStatusBar interface. This name is in the ComponentName flattened format (package/class) --> - <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.PhoneStatusBarService</string> + <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.StatusBarService</string> <!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the icons in the status bar that are not notifications. --> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 8a927576bed0..99c0b3d00905 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -28,6 +28,10 @@ <dimen name="toast_y_offset">64dip</dimen> <!-- Height of the status bar --> <dimen name="status_bar_height">25dip</dimen> + <!-- Height of the status bar --> + <dimen name="status_bar_icon_size">25dip</dimen> + <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. --> + <dimen name="status_bar_edge_ignore">5dp</dimen> <!-- Size of the fastscroll hint letter --> <dimen name="fastscroll_overlay_size">104dp</dimen> <!-- Width of the fastscroll thumb --> @@ -38,6 +42,4 @@ <dimen name="password_keyboard_key_height">56dip</dimen> <!-- Default correction for the space key in the password keyboard --> <dimen name="password_keyboard_spacebar_vertical_correction">4dip</dimen> - <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. --> - <dimen name="status_bar_edge_ignore">5dp</dimen> </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index af04117db6c7..02a601a491ba 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -88,6 +88,13 @@ <item name="windowExitAnimation">@anim/status_bar_exit</item> </style> + <!-- {@hide} --> + <style name="Animation.StatusBar.IntruderAlert" + parent="@android:style/Animation.StatusBar"> + <item name="android:windowEnterAnimation">@anim/priority_alert_enter</item> + <item name="android:windowExitAnimation">@anim/priority_alert_exit</item> + </style> + <!-- Standard animations for a translucent window or activity. This style is <em>not<em> used by default for the translucent theme (since translucent activities are a special case that have no diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml index ce623e8d4d35..30312b369499 100644 --- a/core/res/res/xml/power_profile.xml +++ b/core/res/res/xml/power_profile.xml @@ -18,6 +18,7 @@ --> <device name="Android"> + <!-- All values are in mAh except as noted --> <item name="none">0</item> <item name="screen.on">0.1</item> <item name="bluetooth.active">0.1</item> @@ -48,4 +49,6 @@ <array name="cpu.active"> <value>0.2</value> </array> + <!-- This is the battery capacity in mAh --> + <item name="battery.capacity">1000</item> </device> diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml index 548099397c3b..d298d40cafde 100644 --- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml +++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml @@ -43,6 +43,16 @@ android:label="Test runner for Connectivity Manager Tests" /> + <!-- + To run the unit tests use the command: + "adb shell am instrument -w + com.android.connectivitymanagertest/.ConnectivityManagerUnitTestRunner" + --> + <instrumentation android:name=".ConnectivityManagerUnitTestRunner" + android:targetPackage="com.android.connectivitymanagertest" + android.label="Test runner for unit tests" + /> + <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java new file mode 100644 index 000000000000..6adfc7489910 --- /dev/null +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java @@ -0,0 +1,47 @@ +/* + * 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 com.android.connectivitymanagertest; + +import android.os.Bundle; +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; +import android.util.Log; +import com.android.connectivitymanagertest.unit.WifiSoftAPTest; + +import junit.framework.TestSuite; + +/** + * Instrumentation Test Runner for all unit tests + * + * adb shell am instrument \ + * -w com.android.connectivitymanagertest/.ConnectivityManagerUnitTestRunner + */ + +public class ConnectivityManagerUnitTestRunner extends InstrumentationTestRunner { + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(WifiSoftAPTest.class); + return suite; + } + + + @Override + public ClassLoader getLoader() { + return ConnectivityManagerUnitTestRunner.class.getClassLoader(); + } +} diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java new file mode 100644 index 000000000000..3f43e4851c4f --- /dev/null +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java @@ -0,0 +1,86 @@ +/* + * 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 com.android.connectivitymanagertest.unit; + +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.Context; +import android.app.Instrumentation; +import android.os.Handler; +import android.os.Message; +import android.net.ConnectivityManager; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiConfiguration.KeyMgmt; + +import android.test.suitebuilder.annotation.LargeTest; +import android.test.AndroidTestCase; + +import java.util.ArrayList; + +import android.util.Log; + +/** + * Test Wifi soft AP configuration + */ +public class WifiSoftAPTest extends AndroidTestCase { + + private WifiManager mWifiManager; + private WifiConfiguration mWifiConfig = null; + private final String TAG = "WifiSoftAPTest"; + private final int DURATION = 10000; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE); + assertNotNull(mWifiManager); + assertTrue(mWifiManager.setWifiApEnabled(null, true)); + mWifiConfig = mWifiManager.getWifiApConfiguration(); + if (mWifiConfig != null) { + Log.v(TAG, "mWifiConfig is " + mWifiConfig.toString()); + } else { + Log.v(TAG, "mWifiConfig is null."); + } + } + + @Override + protected void tearDown() throws Exception { + Log.v(TAG, "turn off wifi tethering"); + mWifiManager.setWifiApEnabled(null, false); + super.tearDown(); + } + + // Test case 1: Test the soft AP SSID with letters + @LargeTest + public void testApSsidWithAlphabet() { + WifiConfiguration config = new WifiConfiguration(); + config.SSID = "abcdefghijklmnopqrstuvwxyz"; + config.allowedKeyManagement.set(KeyMgmt.NONE); + mWifiConfig = config; + assertTrue(mWifiManager.setWifiApEnabled(mWifiConfig, true)); + try { + Thread.sleep(DURATION); + } catch (InterruptedException e) { + Log.v(TAG, "exception " + e.getStackTrace()); + assertFalse(true); + } + assertNotNull(mWifiManager.getWifiApConfiguration()); + assertEquals("wifi AP state is not enabled", WifiManager.WIFI_AP_STATE_ENABLED, + mWifiManager.getWifiApState()); + } +} diff --git a/docs/html/guide/developing/eclipse-adt.jd b/docs/html/guide/developing/eclipse-adt.jd index cf2a45716d34..66379a3945d2 100644 --- a/docs/html/guide/developing/eclipse-adt.jd +++ b/docs/html/guide/developing/eclipse-adt.jd @@ -527,7 +527,7 @@ Marking a project as an Android library project. </p> <p>A library project's manifest file must declare all of the shared components that it includes, just as would a standard Android application. For more information, see the documentation for <a -href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> +href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> <p>For example, the <a href="{@docRoot}resources/samples/TicTacToeLib/AndroidManifest.html">TicTacToeLib</a> @@ -613,7 +613,8 @@ like this: </p> ... </manifest></pre> -<p>For more information about the manifest file, see the documentation for <a href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> +<p>For more information about the manifest file, see the documentation for <a +href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> <h3 id="considerations">Development considerations</h3> diff --git a/docs/html/guide/developing/other-ide.jd b/docs/html/guide/developing/other-ide.jd index e8a6fb6494f8..1d67aa90ede4 100644 --- a/docs/html/guide/developing/other-ide.jd +++ b/docs/html/guide/developing/other-ide.jd @@ -687,7 +687,7 @@ so that other applications can use it, you can do so by adding a the <p>A library project's manifest file must declare all of the shared components that it includes, just as would a standard Android application. For more information, see the documentation for <a -href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> +href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> <p>For example, the <a href="{@docRoot}resources/samples/TicTacToeLib/AndroidManifest.html">TicTacToeLib</a> @@ -799,7 +799,8 @@ like this: </p> ... </manifest></pre> -<p>For more information about the manifest file, see the documentation for <a href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> +<p>For more information about the manifest file, see the documentation for <a +href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p> <h3 id="depAppBuild">Building a dependent application</h3> diff --git a/docs/html/guide/samples/index.jd b/docs/html/guide/samples/index.jd index 2f3ac5ebb6bc..bd9ea52bc6ff 100644 --- a/docs/html/guide/samples/index.jd +++ b/docs/html/guide/samples/index.jd @@ -3,99 +3,13 @@ page.title=Sample Code @jd:body -<p>Sometimes, the best way to learn how things are done is to look at some code. -Here, you can browse the source of some sample Android applications that are included -in the Android SDK.</p> +<script type="text/javascript"> + window.location = toRoot + "resources/samples/index.html"; +</script> -<p>Each version of the Android platform available for the SDK includes a full set of sample -applications (which may vary between different versions of the platform). -You can find the samples in your SDK at:</p> +<p><strong>This document has moved. Please go to <a +href="http://developer.android.com/resources/samples/index.html">List of Sample +Apps</a>.</strong></p> -<p style="margin-left:2em"> -<code><em><sdk></em>/platforms/android-<em><version></em>/samples/</code> -</p> - -<p>You can easily create new Android projects with these samples, modify them -if you'd like, then run them on an emulator or device. For example, to create -a project for the API Demos app from Eclipse, -start a new Android Project, select "Create project from existing source", then select -{@code ApiDemos} in the {@code samples/} directory. To create the API Demos project -using the {@code android} tool, execute:</p> -<pre> -android update project -s -n API Demos -t <em><target_ID></em> -p <em><path-to-platform></em>/samples/ApiDemos/ -</pre> - -<p>The pages below provide an overview of each sample application (available with most -platforms) and allow you to view the source files in your browser. </p> - -<div class="special"> - <p>Some of the samples in this listing are not yet available in the - SDK. While we work to update the SDK, you can - <a href="{@docRoot}shareables/latest_samples.zip">download the latest samples</a> as a ZIP - archive.</p> -</div> - -<dl> - - <dt><a href="{@docRoot}resources/samples/ApiDemos/index.html">API Demos</a></dt> - <dd>A variety of small applications that demonstrate an extensive collection of - framework topics.</dd> - - <dt><a href="{@docRoot}resources/samples/BackupRestore/index.html">Backup and Restore</a></dt> - <dd>An simple example that illustrates a few different ways for an application to - implement support for the Android data backup and restore mechanism.</dd> - - <dt><a href="{@docRoot}resources/samples/BluetoothChat/index.html">Bluetooth Chat</a></dt> - <dd>An application for two-way text messaging over Bluetooth.</dd> - - <dt><a href="{@docRoot}resources/samples/ContactManager/index.html">Contact Manager</a></dt> - <dd>An application that demonstrates how to query the system contacts provider - using the <code>ContactsContract</code> API, as - well as insert contacts into a specific account.</dd> - - <dt><a href="{@docRoot}resources/samples/Home/index.html">Home</a></dt> - <dd>A home screen replacement application.</dd> - - <dt><a href="{@docRoot}resources/samples/JetBoy/index.html">JetBoy</a></dt> - <dd>JetBoy is a game that demonstrates the SONiVOX JET interactive music technology, - with {@link android.media.JetPlayer}.</dd> - - <dt><a href="{@docRoot}resources/samples/LunarLander/index.html">Lunar Lander</a></dt> - <dd>A classic Lunar Lander game.</dd> - - <dt><a href="{@docRoot}resources/samples/MultiResolution/index.html">Multiple Resolutions</a></dt> - <dd>A sample application that shows how to use resource directory qualifiers to - provide different resources for different screen configurations.</dd> - - <dt><a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a></dt> - <dd>An application for saving notes. Similar (but not identical) to the - <a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad tutorial</a>.</dd> - - <dt><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary</a></dt> - <dd>A sample application that demonstrates Android's search framework, - including how to provide search suggestions for Quick Search Box.</dd> - - <dt><a href="{@docRoot}resources/samples/Snake/index.html">Snake</a></dt> - <dd>An implementation of the classic game "Snake."</dd> - - <dt><a href="{@docRoot}resources/samples/SoftKeyboard/index.html">Soft Keyboard</a></dt> - <dd>An example of writing an input method for a software keyboard.</dd> - - <dt><a href=""{@docRoot}resources/samples/Wiktionary/index.html">Wiktionary</a></dt> - <dd>An example of creating interactive widgets for display on the Android - home screen.</dd> - - <dt><a href="{@docRoot}resources/samples/WiktionarySimple/index.html">Wiktionary (Simplified)</a></dt> - <dd>A simple Android home screen widgets example.</dd> - -</dl> - - -<div class="special"> -<p>For more sample applications, check out -<a href="http://code.google.com/p/apps-for-android/">apps-for-android</a>, a -collection of open source applications that demonstrate various Android APIs. -</p> -</div> diff --git a/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd index 4279d7d474ad..6c02031fd4f5 100644 --- a/docs/html/guide/topics/data/backup.jd +++ b/docs/html/guide/topics/data/backup.jd @@ -240,7 +240,7 @@ Backup Service Key is ignored.</p> <h2 id="BackupAgent">Extending BackupAgent</h2> <p>Most applications shouldn't need to extend the {@link android.app.backup.BackupAgent} class -directly, but should instead <a href="BackupAgentHelper">extend BackupAgentHelper</a> to take +directly, but should instead <a href="#BackupAgentHelper">extend BackupAgentHelper</a> to take advantage of the built-in helper classes that automatically backup and restore your files. However, you might want to extend {@link android.app.backup.BackupAgent} directly if you need to:</p> <ul> @@ -262,7 +262,7 @@ create your table and insert the data during a restore operation.</li> <p>If you don't need to perform any of the tasks above and want to back up complete files from {@link android.content.SharedPreferences} or <a href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>, you -should skip to <a href="BackupAgentHelper">Extending BackupAgentHelper</a>.</p> +should skip to <a href="#BackupAgentHelper">Extending BackupAgentHelper</a>.</p> @@ -576,8 +576,8 @@ helpers.</p> <h3 id="SharedPreferences">Backing up SharedPreferences</h3> -<p>When you instantiate a {@link android.app.backup.SharedPreferencesBackupHelper}, you must the -name of one or more {@link android.content.SharedPreferences} files.</p> +<p>When you instantiate a {@link android.app.backup.SharedPreferencesBackupHelper}, you must +include the name of one or more {@link android.content.SharedPreferences} files.</p> <p>For example, to back up a {@link android.content.SharedPreferences} file named "user_preferences", a complete backup agent using {@link android.app.backup.BackupAgentHelper} looks diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd index cac85e835fe0..7e2f8a0cea04 100644 --- a/docs/html/guide/topics/resources/providing-resources.jd +++ b/docs/html/guide/topics/resources/providing-resources.jd @@ -761,7 +761,7 @@ Android runs your application, it will crash if you do not provide default resou cannot use the resources named with the new qualifier. For example, if your <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> is set to 4, and you qualify all of your drawable resources using <a -href="NightQualifier">night mode</a> ({@code night} or {@code notnight}, which were added in API +href="#NightQualifier">night mode</a> ({@code night} or {@code notnight}, which were added in API Level 8), then an API Level 4 device cannot access your drawable resources and will crash. In this case, you probably want {@code notnight} to be your default resources, so you should exclude that qualifier so your drawable resources are in either {@code drawable/} or {@code drawable-night/}.</p> diff --git a/docs/html/guide/topics/ui/menus.jd b/docs/html/guide/topics/ui/menus.jd index cf3c7decbcfd..b4e467c273f2 100644 --- a/docs/html/guide/topics/ui/menus.jd +++ b/docs/html/guide/topics/ui/menus.jd @@ -5,198 +5,324 @@ parent.link=index.html <div id="qv-wrapper"> <div id="qv"> - <h2>Key classes</h2> - <ol> - <li>{@link android.view.Menu}</li> - <li>{@link android.view.ContextMenu}</li> - <li>{@link android.view.SubMenu}</li> - </ol> <h2>In this document</h2> <ol> - <li><a href="#options-menu">Options Menu</a></li> - <li><a href="#context-menu">Context Menu</a></li> - <li><a href="#submenu">Submenu</a></li> - <li><a href="#xml">Define Menus in XML</a></li> - <li><a href="#features">Menu Features</a> + <li><a href="#xml">Defining Menus</a></li> + <li><a href="#Inflating">Inflating a Menu Resource</a> + <li><a href="#options-menu">Creating an Options Menu</a> + <ol> + <li><a href="#ChangingTheMenu">Changing the menu when it opens</a></li> + </ol> + </li> + <li><a href="#context-menu">Creating a Context Menu</a></li> + <li><a href="#submenu">Creating a Submenu</a></li> + <li><a href="#features">Other Menu Features</a> <ol> <li><a href="#groups">Menu groups</a></li> <li><a href="#checkable">Checkable menu items</a></li> <li><a href="#shortcuts">Shortcut keys</a></li> - <li><a href="#intents">Menu item intents</a></li> + <li><a href="#intents">Intents for menu items</a></li> </ol> </li> </ol> + + <h2>Key classes</h2> + <ol> + <li>{@link android.view.Menu}</li> + <li>{@link android.view.MenuItem}</li> + <li>{@link android.view.ContextMenu}</li> + <li>{@link android.view.SubMenu}</li> + </ol> + + <h2>See also</h2> + <ol> + <li><a href="{@docRoot}guide/topics/resources/menu-resource.html">Menu Resource</a></li> + </ol> </div> </div> -<p>Menus are an important part of any application. They provide familiar interfaces -that reveal application functions and settings. Android offers an easy programming interface -for developers to provide standardized application menus for various situations.</p> +<p>Menus are an important part of an application that provide a familiar interface for the user +to access application functions and settings. Android offers an easy programming interface +for you to provide application menus in your application.</p> -<p>Android offers three fundamental types of application menus:</p> +<p>Android provides three types of application menus:</p> <dl> <dt><strong>Options Menu</strong></dt> - <dd>This is the primary set of menu items for an Activity. It is revealed by pressing - the device MENU key. Within the Options Menu are two groups of menu items: + <dd>The primary menu for an Activity, which appears when the user presses + the device MENU key. Within the Options Menu are two groups: <dl style="margin-top:1em"> <dt><em>Icon Menu</em></dt> - <dd>This is the collection of items initially visible at the bottom of the screen + <dd>The menu items visible at the bottom of the screen at the press of the MENU key. It supports a maximum of six menu items. These are the only menu items that support icons and the only menu items that <em>do not</em> support checkboxes or radio buttons.</dd> <dt><em>Expanded Menu</em></dt> - <dd>This is a vertical list of items exposed by the "More" menu item from the Icon Menu. - It exists only when the Icon Menu becomes over-loaded and is comprised of the sixth - Option Menu item and the rest.</dd> + <dd>The vertical list of menu items exposed by the "More" menu item in the Icon Menu. + When the Icon Menu is full, the expanded menu is comprised of the sixth + menu item and the rest.</dd> </dl> </dd> <dt><strong>Context Menu</strong></dt> - <dd>This is a floating list of menu items that may appear when you perform a long-press on a View - (such as a list item). </dd> + <dd>A floating list of menu items that appears when the user performs a long-press on a View. +</dd> <dt><strong>Submenu</strong></dt> - <dd>This is a floating list of menu items that is revealed by an item in the Options Menu - or a Context Menu. A Submenu item cannot support nested Submenus. </dd> + <dd>A floating list of menu items that the user opens by pressing a menu item in the Options +Menu or a context menu. A submenu item cannot support a nested submenu. </dd> </dl> -<h2 id="options-menu">Options Menu</h2> -<img align="right" src="{@docRoot}images/options_menu.png" /> -<p>The Options Menu is opened by pressing the device MENU key. -When opened, the Icon Menu is displayed, which holds the first six menu items. -If more than six items are added to the Options Menu, then those that can't fit -in the Icon Menu are revealed in the Expanded Menu, via the "More" menu item. The Expanded Menu -is automatically added when there are more than six items.</p> -<p>The Options Menu is where you should include basic application functions -and any necessary navigation items (e.g., to a home screen or application settings). -You can also add <a href="#submenu">Submenus</a> for organizing topics -and including extra menu functionality.</p> - -<p>When this menu is opened for the first time, -the Android system will call the Activity <code>{@link android.app.Activity#onCreateOptionsMenu(Menu) -onCreateOptionsMenu()}</code> callback method. Override this method in your Activity -and populate the {@link android.view.Menu} object given to you. You can populate the menu by -inflating a menu resource that was <a href="#xml">defined in XML</a>, or by calling -<code>{@link android.view.Menu#add(CharSequence) add()}</code> -for each item you'd like in the menu. This method adds a {@link android.view.MenuItem}, and returns the -newly created object to you. You can use the returned MenuItem to set additional properties like -an icon, a keyboard shortcut, an intent, and other settings for the item.</p> - -<p>There are multiple <code>{@link android.view.Menu#add(CharSequence) add()}</code> methods. -Usually, you'll want to use one that accepts an <var>itemId</var> argument. -This is a unique integer that allows you to identify the item during a callback.</p> - -<p>When a menu item is selected from the Options Menu, you will receive a callback to the -<code>{@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}</code> -method of your Activity. This callback passes you the -<code>MenuItem</code> that has been selected. You can identify the item by requesting the -<var>itemId</var>, with <code>{@link android.view.MenuItem#getItemId() getItemId()}</code>, -which returns the integer that was assigned with the <code>add()</code> method. Once you identify -the menu item, you can take the appropriate action.</p> - -<p>Here's an example of this procedure, inside an Activity, wherein we create an -Options Menu and handle item selections:</p> +<h2 id="xml">Defining Menus</h2> + +<p>Instead of instantiating {@link android.view.Menu} objects in your application code, you should +define a menu and all its items in an XML <a +href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>, then inflate the menu +resource (load it as a programmable object) in your application code. Defining your menus in XML is +a good practice because it separates your interface design from your application code (the same as +when you <a href="{@docRoot}guide/topics/ui/declaring-layout.html">define your Activity +layout</a>).</p> + +<p>To define a menu, create an XML file inside your project's <code>res/menu/</code> +directory and build the menu with the following elements:</p> +<dl> + <dt><code><menu></code></dt> + <dd>Creates a {@link android.view.Menu}, which is a container for menu items. It must be +the root node and holds one or more of the following elements. You can also nest this element +in an {@code <item>} to create a submenu.</dd> + <dt><code><item></code></dt> + <dd>Creates a {@link android.view.MenuItem}, which represents a single item in a menu.</dd> + <dt><code><group></code></dt> + <dd>An optional, invisible container for {@code <item>} elements. It allows you to +categorize menu items so they share properties such as active state and visibility. See <a +href="#groups">Menu groups</a>.</dd> +</dl> +<p>For example, here is a file in <code>res/menu/</code> named <code>game_menu.xml</code>:</p> <pre> -/* Creates the menu items */ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/new_game" + android:icon="@drawable/ic_new_game" + android:title="@string/new_game" /> + <item android:id="@+id/quit" + android:icon="@drawable/ic_quit" + android:title="@string/quit" /> +</menu> +</pre> + +<p>This example defines a menu with two menu items. Each item includes the attributes:</p> +<dl> + <dt>{@code android:id}</dt> + <dd>A resource ID that's unique to the item so that the application can recognize the item when +the user selects it.</dd> + <dt>{@code android:icon}</dt> + <dd>A drawable resource that is the icon visible to the user.</dd> + <dt>{@code android:title}</dt> + <dd>A string resource that is the title visible to the user.</dd> +</dl> + +<p>For more about the XML syntax and attributes for a menu resource, see the <a +href="{@docRoot}guide/topics/resources/menu-resource.html">Menu Resource</a> reference.</p> + + +<h2 id="Inflating">Inflating a Menu Resource</h2> + +<p>You can inflate your menu resource (convert the XML resource into a programmable object) using +{@link android.view.MenuInflater#inflate(int,Menu) MenuInflater.inflate()}. For +example, the following code inflates the <code>game_menu.xml</code> file defined above during the +{@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} callback method, to be +used for the Options Menu:</p> + +<pre> +@Override public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, MENU_NEW_GAME, 0, "New Game"); - menu.add(0, MENU_QUIT, 0, "Quit"); + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.game_menu, menu); return true; } +</pre> + +<p>The {@link android.app.Activity#getMenuInflater()} method returns a {@link +android.view.MenuInflater} for the Activity. With this object, you can call {@link +android.view.MenuInflater#inflate(int,Menu) inflate()}, which inflates a menu resource into a +{@link android.view.Menu} object. In this example, the menu resource defined by +<code>game_menu.xml</code> +is inflated into the {@link android.view.Menu} that was passed into {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()}. (This callback method for +creating an option menu is discussed more in the next section.)</p> + + + +<h2 id="options-menu">Creating an Options Menu</h2> + +<div class="figure" style="width:200px"> + <img src="{@docRoot}images/options_menu.png" height="300" alt="" /> + <p class="img-caption"><strong>Figure 1.</strong> Screenshot of an Options Menu.</p> +</div> + + +<p>The Options Menu is where you should include basic application functions +and necessary navigation items (for example, a button +to open application settings). The user +can open the Options Menu with the device MENU key. +Figure 1 shows a screenshot of an Options Menu.</p> + +<p>When opened, the first visible portion of the Options Menu is called the Icon Menu. It +holds the first six menu items. +If you add more than six items to the Options Menu, Android places the sixth item and those after it +into the Expanded Menu, which the user can open with the "More" menu item.</p> + +<p>When the user opens the Options Menu for the first time, Android calls your Activity's +{@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} method. Override this method in your Activity +and populate the {@link android.view.Menu} that is passed into the method. Populate the +{@link android.view.Menu} by inflating a menu resource as described in <a +href="#Inflating">Inflating a Menu Resource</a>. (You can +also populate the menu in code, using {@link android.view.Menu#add(int,int,int,int) +add()} to add menu items.)</p> + +<p>When the user selects a menu item from the Options Menu, the system calls your Activity's +{@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} +method. This method passes the +{@link android.view.MenuItem} that the user selected. You can identify the menu item by calling +{@link android.view.MenuItem#getItemId()}, which returns the unique ID for the menu +item (defined by the {@code android:id} attribute in the menu resource or with an integer passed +to the {@link android.view.Menu#add(int,int,int,int) add()} method). You can match this ID +against known menu items and perform the appropriate action.</p> + +<p>For example:</p> -/* Handles item selections */ +<pre> +@Override public boolean onOptionsItemSelected(MenuItem item) { + // Handle item selection switch (item.getItemId()) { - case MENU_NEW_GAME: + case R.id.new_game: newGame(); return true; - case MENU_QUIT: + case R.id.quit: quit(); return true; + default: + return super.onOptionsItemSelected(item); } - return false; } </pre> -<p>The <code>add()</code> method used in this sample takes four arguments: -<var>groupId</var>, <var>itemId</var>, <var>order</var>, and <var>title</var>. -The <var>groupId</var> allows you to associate this menu item with a group of other items -(more about <a href="#groups">Menu groups</a>, below) — in -this example, we ignore it. <var>itemId</var> is a unique integer that we give the -MenuItem so that can identify it in the next callback. <var>order</var> allows us to -define the display order of the item — by default, they are displayed by the -order in which we add them. <var>title</var> is, of course, the name that goes on the -menu item (this can also be a -<a href="{@docRoot}guide/topics/resources/available-resources.html#stringresources">string resource</a>, -and we recommend you do it that way for easier localization).</p> - -<p class="note"><strong>Tip:</strong> -If you have several menu items that can be grouped together with a title, -consider organizing them into a <a href="#submenu">Submenu</a>.</p> - -<h3>Adding icons</h3> -<p>Icons can also be added to items that appears in the Icon Menu with -<code>{@link android.view.MenuItem#setIcon(Drawable) setIcon()}</code>. For example:</p> -<pre> -menu.add(0, MENU_QUIT, 0, "Quit") - .setIcon(R.drawable.menu_quit_icon);</pre> - -<h3>Modifying the menu</h3> -<p>If you want to sometimes re-write the Options Menu as it is opened, override the -<code>{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()}</code> method, which is -called each time the menu is opened. This will pass you the Menu object, just like the -<code>onCreateOptionsMenu()</code> callback. This is useful if you'd like to add or remove -menu options depending on the current state of an application or game.</p> +<p>In this example, {@link android.view.MenuItem#getItemId()} queries the ID for the selected menu +item and the switch statement compares the ID against the resource IDs that were assigned to menu +items in the XML resource. When a switch case successfully handles the item, it +returns "true" to indicate that the item selection was handled. Otherwise, the default statement +passes the menu item to the super class in +case it can handle the item selected. (If you've directly extended the {@link android.app.Activity} +class, then the super class returns "false", but it's a good practice to +pass unhandled menu items to the super class instead of directly returning "false".)</p> + +<p class="note"><strong>Tip:</strong> If your application contains multiple activities and +some of them provide the same Options Menu, consider creating +an Activity that implements nothing except the {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} and {@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()} methods. Then extend this class for each Activity that should share the +same Options Menu. This way, you have to manage only one set of code for handling menu +actions and each decendent class inherits the menu behaviors.<br/><br/> +If you want to add menu items to one of your decendent activities, +override {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} in that Activity. Call {@code super.onCreateOptionsMenu(menu)} so the +original menu items are created, then add new menu items with {@link +android.view.Menu#add(int,int,int,int) menu.add()}. You can also override the super class's +behavior for individual menu items.</p> + + +<h3 id="ChangingTheMenu">Changing the menu when it opens</h3> + +<p>The {@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} method is +called only the first time the Options Menu is opened. The system keeps and re-uses the {@link +android.view.Menu} you define in this method until your Activity is destroyed. If you want to change +the Options Menu each time it opens, you must override the +{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()} method. This passes +you the {@link android.view.Menu} object as it currently exists. This is useful if you'd like to +remove, add, disable, or enable menu items depending on the current state of your application.</p> <p class="note"><strong>Note:</strong> -When changing items in the menu, it's bad practice to do so based on the currently selected item. -Keep in mind that, when in touch mode, there will not be a selected (or focused) item. Instead, you -should use a <a href="#context-menu">Context Menu</a> for such behaviors, when you want to provide -functionality based on a particular item in the UI.</p> - - -<h2 id="context-menu">Context Menu</h2> -<p>The Android context menu is similar, in concept, to the menu revealed with a "right-click" on a PC. -When a view is registered to a context menu, -performing a "long-press" (press and hold for about two seconds) on the object -will reveal a floating menu that provides functions relating to that item. -Context menus can be registered to any View object, -however, they are most often used for items in a -{@link android.widget.ListView}, which helpfully indicates the presence of the context menu -by transforming the background color of the ListView item when pressed. -(The items in the phone's contact list offer an example of this feature.) -</p> - -<p class="note"><strong>Note:</strong> Context menu items do not support icons or shortcut keys.</p> - -<p>To create a context menu, you must override the Activity's context menu callback methods: -<code>{@link android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo) onCreateContextMenu()}</code> and -<code>{@link android.app.Activity#onContextItemSelected(MenuItem) onContextItemSelected()}</code>. -Inside the <code>onCreateContextMenu()</code> callback method, you can add menu items using one of the -<code>{@link android.view.Menu#add(CharSequence) add()}</code> methods, or by -inflating a menu resource that was <a href="#xml">defined in XML</a>. -Then, register a {@link android.view.ContextMenu} for the View, with -<code>{@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()}</code>.</p> - -<p>For example, here is some code that can be used with the -<a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad application</a> -to add a context menu for each note in the list:</p> +You should never change items in the Options Menu based on the {@link android.view.View} currently +in focus. When in touch mode (when the user is not using a trackball or d-pad), Views +cannot take focus, so you should never use focus as the basis for modifying +items in the Options Menu. If you want to provide menu items that are context-sensitive to a {@link +android.view.View}, use a <a href="#context-menu">Context Menu</a>.</p> + + + +<h2 id="context-menu">Creating a Context Menu</h2> + +<p>A context menu is conceptually similar to the menu displayed when the user performs a +"right-click" on a PC. You should use a context menu to provide the user access to +actions that pertain to a specific item in the user interface. On Android, a context menu is +displayed when the user performs a "long press" (press and hold) on an item.</p> + +<p>You can create a context menu for any View, though context menus are most often used for items in +a {@link android.widget.ListView}. When the user performs a long-press on an item in a ListView and +the list is registered to provide a context menu, the list item signals to the user that a context +menu is available by animating its background color—it transitions from +orange to white before opening the context menu. (The Contacts application demonstrates this +feature.)</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h3>Register a ListView</h3> +<p>If your Activity uses a {@link android.widget.ListView} and +you want all list items to provide a context menu, register all items for a context +menu by passing the {@link android.widget.ListView} to {@link +android.app.Activity#registerForContextMenu(View) registerForContextMenu()}. For +example, if you're using a {@link android.app.ListActivity}, register all list items like this:</p> +<p><code>registerForContextMenu({@link android.app.ListActivity#getListView()});</code></p> +</div> +</div> + +<p>In order for a View to provide a context menu, you must "register" the view for a context +menu. Call {@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()} and +pass it the {@link android.view.View} you want to give a context menu. When this View then +receives a long-press, it displays a context menu.</p> + +<p>To define the context menu's appearance and behavior, override your Activity's context menu +callback methods, {@link android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo) +onCreateContextMenu()} and +{@link android.app.Activity#onContextItemSelected(MenuItem) onContextItemSelected()}.</p> + +<p>For example, here's an {@link +android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo) +onCreateContextMenu()} that uses the {@code context_menu.xml} menu resource:</p> <pre> +@Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); - menu.add(0, EDIT_ID, 0, "Edit"); - menu.add(0, DELETE_ID, 0, "Delete"); + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.context_menu, menu); } +</pre> + +<p>{@link android.view.MenuInflater} is used to inflate the context menu from a <a +href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>. (You can also use +{@link android.view.Menu#add(int,int,int,int) add()} to add menu items.) The callback method +parameters include the {@link android.view.View} +that the user selected and a {@link android.view.ContextMenu.ContextMenuInfo} object that provides +additional information about the item selected. You might use these parameters to determine +which context menu should be created, but in this example, all context menus for the Activity are +the same.</p> + +<p>Then when the user selects an item from the context menu, the system calls {@link +android.app.Activity#onContextItemSelected(MenuItem) onContextItemSelected()}. Here is an example +of how you can handle selected items:</p> +<pre> +@Override public boolean onContextItemSelected(MenuItem item) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); switch (item.getItemId()) { - case EDIT_ID: + case R.id.edit: editNote(info.id); return true; - case DELETE_ID: + case R.id.delete: deleteNote(info.id); return true; default: @@ -205,285 +331,276 @@ public boolean onContextItemSelected(MenuItem item) { } </pre> -<p>In <code>onCreateContextMenu()</code>, we are given not only the ContextMenu to -which we will add {@link android.view.MenuItem}s, but also the {@link android.view.View} -that was selected and a {@link android.view.ContextMenu.ContextMenuInfo ContextMenuInfo} object, -which provides additional information about the object that was selected. -In this example, nothing special is done in <code>onCreateContextMenu()</code> — just -a couple items are added as usual. In the <code>onContextItemSelected()</code> -callback, we request the {@link android.widget.AdapterView.AdapterContextMenuInfo AdapterContextMenuInfo} -from the {@code MenuItem}, which provides information about the currently selected item. -All we need from -this is the list ID for the selected item, so whether editing a note or deleting it, -we find the ID with the {@code AdapterContextMenuInfo.info} field of the object. This ID -is passed to the <code>editNote()</code> and <code>deleteNote()</code> methods to perform -the respective action.</p> - -<p>Now, to register this context menu for all the items in a {@link android.widget.ListView}, -we pass the entire {@code ListView} to the -<code>{@link android.app.Activity#registerForContextMenu(View)}</code> method:</p> - -<pre>registerForContextMenu(getListView());</pre> -<p>Remember, you can pass any View object to register a context menu. Here, -<code>{@link android.app.ListActivity#getListView()}</code> returns the ListView -object used in the Notepad application's {@link android.app.ListActivity}. As such, each item -in the list is registered to this context menu.</p> - - - -<h2 id="submenu">Submenus</h2> -<p>A sub menu can be added within any menu, except another sub menu. -These are very useful when your application has a lot of functions that may be -organized in topics, like the items in a PC application's menu bar (File, Edit, View, etc.).</p> - -<p>A sub menu is created by adding it to an existing {@link android.view.Menu} -with <code>{@link android.view.Menu#addSubMenu(CharSequence) addSubMenu()}</code>. -This returns a {@link android.view.SubMenu} object (an extension of {@link android.view.Menu}). -You can then add additional items to this menu, with the normal routine, using -the <code>{@link android.view.Menu#add(CharSequence) add()}</code> methods. For example:</p> +<p>The structure of this code is similar to the example for <a href="#options-menu">Creating an +Options Menu</a>, in which {@link android.view.MenuItem#getItemId()} queries the ID for the selected +menu item and a switch statement matches the item to the IDs that are defined in the menu resource. +And like the options menu example, the default statement calls the super class in case it +can handle menu items not handled here, if necessary.</p> -<pre> -public boolean onCreateOptionsMenu(Menu menu) { - boolean result = super.onCreateOptionsMenu(menu); +<p>In this example, the selected item is an item from a {@link android.widget.ListView}. To +perform an action on the selected item, the application needs to know the list +ID for the selected item (it's position in the ListView). To get the ID, the application calls +{@link android.view.MenuItem#getMenuInfo()}, which returns a {@link +android.widget.AdapterView.AdapterContextMenuInfo} object that includes the list ID for the +selected item in the {@link android.widget.AdapterView.AdapterContextMenuInfo#id id} field. The +local methods <code>editNote()</code> and <code>deleteNote()</code> methods accept this list ID to +perform an action on the data specified by the list ID.</p> - SubMenu fileMenu = menu.addSubMenu("File"); - SubMenu editMenu = menu.addSubMenu("Edit"); - fileMenu.add("new"); - fileMenu.add("open"); - fileMenu.add("save"); - editMenu.add("undo"); - editMenu.add("redo"); +<p class="note"><strong>Note:</strong> Items in a context menu do not support icons or shortcut +keys.</p> - return result; -} -</pre> -<p>Callbacks for items selected in a sub menu are made to the parent menu's callback method. -For the example above, selections in the sub menu will be handled by the -<code>onOptionsItemSelected()</code> callback.</p> -<p>You can also add Submenus when you <a href="#xml">define the parent menu in XML</a>.</p> -<h2 id="xml">Define Menus in XML</h2> -<p>Just like Android UI layouts, you can define application menus in XML, then inflate them -in your menu's <code>onCreate...()</code> callback method. This makes your application code cleaner and -separates more interface design into XML, which is easier to visualize.</p> +<h2 id="submenu">Creating Submenus</h2> -<p>To start, create a new folder in your project <code>res/</code> directory called <code>menu</code>. -This is where you should keep all XML files that define your application menus.</p> +<p>A submenu is a menu that the user can open by selecting an item in another menu. You can add a +submenu to any menu (except a submenu). Submenus are useful when your application has a lot of +functions that can be organized into topics, like items in a PC application's menu bar (File, Edit, +View, etc.).</p> -<p>In a menu XML layout, there are -three valid elements: <code><menu></code>, <code><group></code> and <code><item></code>. The -<code>item</code> and <code>group</code> elements must be children of a <code>menu</code>, but <code>item</code> -elements may also be the children of a <code>group</code>, and another <code>menu</code> element may be the child -of an <code>item</code> (to create a Submenu). Of course, the root node of any file -must be a <code>menu</code> element.</p> +<p>When creating your <a href="{@docRoot}guide/topics/resources/menu-resource.html">menu +resource</a>, you can create a submenu by adding a {@code <menu>} element as the child of an +{@code <item>}. For example:</p> -<p>As an example, we'll define the same menu created in the <a href="#options-menu">Options Menu</a> section, -above. We start with an XML file named <code>options_menu.xml</code> inside the <code>res/menu/</code> folder:</p> <pre> -<menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/new_game" - android:title="New Game" /> - <item android:id="@+id/quit" - android:title="Quit" /> -</menu> +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/file" + android:icon="@drawable/file" + android:title="@string/file" > + <!-- "file" submenu --> + <menu"> + <item android:id="@+id/new" + android:title="@string/new" /> + <item android:id="@+id/open" + android:title="@string/open" /> + </menu> + </item> +</menu> </pre> -<p>Then, in the <code>onCreateOptionsMenu()</code> method, we inflate this resource using -<code>{@link android.view.MenuInflater#inflate(int,Menu) MenuInflater.inflate()}</code>:</p> -<pre> -public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.options_menu, menu); - return true; -} -</pre> +<p>When the user selects an item from a submenu, the parent menu's respective on-item-selected +callback method receives the event. For instance, if the above menu is applied as an Options Menu, +then the {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} method +is called when a submenu item is selected.</p> -<p>The <code>{@link android.app.Activity#getMenuInflater()}</code> method returns the {@link android.view.MenuInflater} -for our activity's context. We then call <code>{@link android.view.MenuInflater#inflate(int,Menu) inflate()}</code>, -passing it a pointer to our menu resource and the Menu object given by the callback.</code></p> +<p>You can also use {@link android.view.Menu#addSubMenu(int,int,int,int) addSubMenu()} to +dynamically add a {@link android.view.SubMenu} to an existing {@link android.view.Menu}. This +returns the new {@link android.view.SubMenu} object, to which you can add +submenu items, using {@link android.view.Menu#add(int,int,int,int) add()}</p> -<p>While this small sample may seem like more effort, compared to creating the menu items in the -<code>onCreateOptionsMenu()</code> method, this will save a lot of trouble when dealing with more items -and it keeps your application code clean.</p> -<p>You can define <a href="#groups">menu groups</a> by wrapping <code>item</code> elements in a <code>group</code> -element, and create Submenus by nesting another <code>menu</code> inside an <code>item</code>. -Each element also supports all the necessary attributes to control features like shortcut keys, -checkboxes, icons, and more. To learn about these attributes and more about the XML syntax, see the Menus -topic in the <a href="{@docRoot}guide/topics/resources/available-resources.html#menus">Available -Resource Types</a> document.</p> -<h2 id="features">Menu Features</h2> -<p>Here are some other features that can be applied to most menu items.</p> +<h2 id="features">Other Menu Features</h2> + +<p>Here are some other features that you can apply to most menu items.</p> <h3 id="groups">Menu groups</h3> -<p>When adding new items to a menu, you can optionally include each item in a group. -A menu group is a collection of menu items that can share certain traits, like -whether they are visible, enabled, or checkable.</p> - -<p>A group is defined by an integer (or a resource id, in XML). A menu item is added to the group when it is -added to the menu, using one of the <code>add()</code> methods that accepts a <var>groupId</var> -as an argument, such as <code>{@link android.view.Menu#add(int,int,int,int)}</code>.</p> - -<p>You can show or hide the entire group with -<code>{@link android.view.Menu#setGroupVisible(int,boolean) setGroupVisible()}</code>; -enable or disable the group with -<code>{@link android.view.Menu#setGroupEnabled(int,boolean) setGroupEnabled()}</code>; -and set whether the items can be checkable with -<code>{@link android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()}</code>. -</p> + +<p>A menu group is a collection of menu items that share certain traits. With a group, you +can:</p> +<ul> + <li>Show or hide all items with {@link android.view.Menu#setGroupVisible(int,boolean) +setGroupVisible()}</li> + <li>Enable or disable all items with {@link android.view.Menu#setGroupEnabled(int,boolean) +setGroupEnabled()}</li> + <li>Specify whether all items are checkable with {@link +android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()}</li> +</ul> + +<p>You can create a group by nesting {@code <item>} elements inside a {@code <group>} +element in your menu resource or by specifying a group ID with the the {@link +android.view.Menu#add(int,int,int,int) add()} method.</p> + +<p>Here's an example menu resource that includes a group:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/item1" + android:icon="@drawable/item1" + android:title="@string/item1" /> + <!-- menu group --> + <group android:id="@+id/group1"> + <item android:id="@+id/groupItem1" + android:title="@string/groupItem1" /> + <item android:id="@+id/groupItem2" + android:title="@string/groupItem2" /> + </group> +</menu> +</pre> + +<p>The items that are in the group appear the same as the first item that is not in a +group—all three items in the menu are siblings. However, you can modify the traits of the two +items in the group by referencing the group ID and using the methods listed above.</p> + <h3 id="checkable">Checkable menu items</h3> -<img align="right" src="{@docRoot}images/radio_buttons.png" alt="" /> -<p>Any menu item can be used as an interface for turning options on and off. This can -be indicated with a checkbox for stand-alone options, or radio buttons for groups of -mutually exclusive options (see the screenshot, to the right).</p> -<p class="note"><strong>Note:</strong> Menu items in the Icon Menu cannot +<div class="figure" style="width:200px"> + <img src="{@docRoot}images/radio_buttons.png" height="300" alt="" /> + <p class="img-caption"><strong>Figure 2.</strong> Screenshot of checkable menu items</p> +</div> + +<p>A menu can be useful as an interface for turning options on and off, using a checkbox for +stand-alone options, or radio buttons for groups of +mutually exclusive options. Figure 2 shows a submenu with items that are checkable with radio +buttons.</p> + +<p class="note"><strong>Note:</strong> Menu items in the Icon Menu (from the Options Menu) cannot display a checkbox or radio button. If you choose to make items in the Icon Menu checkable, -then you must personally indicate the state by swapping the icon and/or text -each time the state changes between on and off.</p> +you must manually indicate the checked state by swapping the icon and/or text +each time the state changes.</p> + +<p>You can define the checkable behavior for individual menu items using the {@code +android:checkable} attribute in the {@code <item>} element, or for an entire group with +the {@code android:checkableBehavior} attribute in the {@code <group>} element. For +example, all items in this menu group are checkable with a radio button:</p> -<p>To make a single item checkable, use the <code>{@link android.view.MenuItem#setCheckable(boolean) -setCheckable()}</code> method, like so:</p> <pre> -menu.add(0, VIBRATE_SETTING_ID, 0, "Vibrate") - .setCheckable(true); +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <group android:checkableBehavior="single"> + <item android:id="@+id/red" + android:title="@string/red" /> + <item android:id="@+id/blue" + android:title="@string/blue" /> + </group> +</menu> </pre> -<p>This will display a checkbox with the menu item (unless it's in the Icon Menu). When the item -is selected, the <code>onOptionsItemSelected()</code> callback is called as usual. It is here that -you must set the state of the checkbox. You can query the current state of the item with -<code>{@link android.view.MenuItem#isChecked()}</code> and set the checked state with -<code>{@link android.view.MenuItem#setChecked(boolean) setChecked()}</code>. -Here's what this looks like inside the -<code>onOptionsItemSelected()</code> callback:</p> + +<p>The {@code android:checkableBehavior} attribute accepts either: +<dl> + <dt>{@code single}</dt> + <dd>Only one item from the group can be checked (radio buttons)</dd> + <dt>{@code all}</dt> + <dd>All items can be checked (checkboxes)</dd> + <dt>{@code none}</dt> + <dd>No items are checkable</dd> +</dl> + +<p>You can apply a default checked state to an item using the {@code android:checked} attribute in +the {@code <item>} element and change it in code with the {@link +android.view.MenuItem#setChecked(boolean) setChecked()} method.</p> + +<p>When a checkable item is selected, the system calls your respective item-selected callback method +(such as {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}). It +is here that you must set the state of the checkbox, because a checkbox or radio button does not +change its state automatically. You can query the current state of the item (as it was before the +user selected it) with {@link android.view.MenuItem#isChecked()} and then set the checked state with +{@link android.view.MenuItem#setChecked(boolean) setChecked()}. For example:</p> + <pre> -switch (item.getItemId()) { -case VIBRATE_SETTING_ID: - if (item.isChecked()) item.setChecked(false); - else item.setChecked(true); - return true; -... +@Override +public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.vibrate: + case R.id.dont_vibrate: + if (item.isChecked()) item.setChecked(false); + else item.setChecked(true); + return true; + default: + return super.onOptionsItemSelected(item); + } } </pre> -<p>To make a group of mutually exclusive radio button items, simply -assign the same group ID to each menu item -and call <code>{@link android.view.Menu#setGroupCheckable(int,boolean,boolean) -setGroupCheckable()}</code>. In this case, you don't need to call <code>setCheckable()</code> -on each menu items, because the group as a whole is set checkable. Here's an example of -two mutually exclusive options in a Submenu:</p> -<pre> -SubMenu subMenu = menu.addSubMenu("Color"); -subMenu.add(COLOR_MENU_GROUP, COLOR_RED_ID, 0, "Red"); -subMenu.add(COLOR_MENU_GROUP, COLOR_BLUE_ID, 0, "Blue"); -subMenu.setGroupCheckable(COLOR_MENU_GROUP, true, true); -</pre> -<p>In the <code>setGroupCheckable()</code> method, the first argument is the group ID -that we want to set checkable. The second argument is whether we want the group items -to be checkable. The last one is whether we want each item to be exclusively checkable -(if we set this <em>false</em>, then all the items will be checkboxes instead of radio buttons). -When the group is set to be exclusive (radio buttons), each time a new item is selected, -all other are automatically de-selected.</p> -<p> +<p>If you don't set the checked state this way, then the visible state of the item (the checkbox or +radio button) will not +change when the user selects it. When you do set the state, the Activity preserves the checked state +of the item so that when the user opens the menu later, the checked state that you +set is visible.</p> <p class="note"><strong>Note:</strong> -Checkable menu items are intended to be used only on a per-session basis and not saved to the device -(e.g., the <em>Map mode</em> setting in the Maps application is not saved — screenshot above). -If there are application settings that you would like to save for the user, -then you should store the data using <a href="#{@docRoot}guide/topics/data/data-storage.html#pref">Preferences</a>, -and manage them with a {@link android.preference.PreferenceActivity}.</p> +Checkable menu items are intended to be used only on a per-session basis and not saved after the +application is destroyed. If you have application settings that you would like to save for the user, +you should store the data using <a +href="#{@docRoot}guide/topics/data/data-storage.html#pref">Shared Preferences</a>.</p> <h3 id="shortcuts">Shortcut keys</h3> -<p>Quick access shortcut keys using letters and/or numbers can be added to menu items with -<code>setAlphabeticShortcut(char)</code> (to set char shortcut), <code>setNumericShortcut(int)</code> -(to set numeric shortcut), -or <code>setShortcut(char,int)</code> (to set both)</code>. Case is <em>not</em> sensitive. -For example:</p> -<pre> -menu.add(0, MENU_QUIT, 0, "Quit") - .setAlphabeticShortcut('q'); -</pre> -<p>Now, when the menu is open (or while holding the MENU key), pressing the "q" key will -select this item.</p> -<p>This shortcut key will be displayed as a tip in the menu item, below the menu item name -(except for items in the Icon Menu).</p> -<p class="note"><strong>Note:</strong> Shortcuts cannot be added to items in a Context Menu.</p> - - -<h3 id="intents">Menu item intents</h3> -<p>If you've read the <a href="{@docRoot}guide/topics/fundamentals.html">Application -Fundamentals</a>, then you're at least a little familiar -with Android Intents. These allow applications to bind with each other, share information, -and perform user tasks cooperatively. Just like your application might fire an Intent to launch a web browser, -an email client, or another Activity in your application, -you can perform such actions from within a menu. -There are two ways to do this: define an Intent and assign it to a single menu item, or -define an Intent and allow Android to search the device for activities and dynamically add a -menu item for each one that meets the Intent criteria.</p> - -<p>For more information on creating Intents and providing your application's services to other applications, -read the <a href="/guide/topics/intents/intents-filters.html">Intents -and Intent Filters</a> document.</p> - -<h4>Set an intent for a single menu item</h4> -<p>If you want to offer a specific menu item that launches a new Activity, then you -can specifically define an Intent for the menu item with the -<code>{@link android.view.MenuItem#setIntent(Intent) -setIntent()}</code> method.</p> - -<p>For example, inside the <code>{@link android.app.Activity#onCreateOptionsMenu(Menu) -onCreateOptionsMenu()}</code> method, you can define a new menu item with an Intent like this:</p> -<pre> -MenuItem menuItem = menu.add(0, PHOTO_PICKER_ID, 0, "Select Photo"); -menuItem.setIntent(new Intent(this, PhotoPicker.class)); -</pre> -<p>Android will automatically launch the Activity when the item is selected.</p> - -<p class="note"><strong>Note:</strong> This will not return a result to your Activity. -If you wish to be returned a result, then do not use <code>setIntent()</code>. -Instead, handle the selection as usual in the <code>onOptionsMenuItemSelected()</code> -or <code>onContextMenuItemSelected()</code> callback and call -<code>{@link android.app.Activity#startActivityForResult(Intent,int) startActivityForResult()}</code>. -</p> - -<h4>Dynamically add intents</h4> - -<p>If there are potentially multiple activities that are relevant to your current -Activity or selected item, then the application can dynamically add menu items that execute other -services.</p> -<p>During menu creation, define an Intent with the category <var>Intent.ALTERNATIVE_CATEGORY</var> and/or -<var>Intent.SELECTED_ALTERNATIVE</var>, the MIME type currently selected (if any), and any other -requirements, the same way as you would satisfy an intent filter to open a new -Activity. Then call -<code>{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) -addIntentOptions()}</code> to have Android search for any services meeting those requirements -and add them to the menu for you. If there are no applications installed -that satisfy the Intent, then no additional menu items are added.</p> +<p>You can add quick-access shortcut keys using letters and/or numbers to menu items with the +{@code android:alphabeticShortcut} and {@code android:numericShortcut} attributes in the {@code +<item>} element. You can also use the methods {@link +android.view.MenuItem#setAlphabeticShortcut(char)} and {@link +android.view.MenuItem#setNumericShortcut(char)}. Shortcut keys are <em>not</em> +case sensitive.</p> + +<p>For example, if you apply the "s" character as an alphabetic shortcut to a "save" menu item, then +when the menu is open (or while the user holds the MENU key) and the user presses the "s" key, +the "save" menu item is selected.</p> + +<p>This shortcut key is displayed as a tip in the menu item, below the menu item name +(except for items in the Icon Menu, which are displayed only if the user holds the MENU +key).</p> + +<p class="note"><strong>Note:</strong> Shortcut keys for menu items only work on devices with a +hardware keyboard. Shortcuts cannot be added to items in a Context Menu.</p> + + +<h3 id="intents">Intents for menu items</h3> + +<p>Sometimes you'll want a menu item to launch an Activity using an Intent (whether it's an +Actvitity in your application or another application). When you know the Intent you want to use and +have a specific menu item that should initiate the Intent, you can execute the Intent with {@link +android.app.Activity#startActivity(Intent) startActivity()} during the appropriate on-item-selected +callback method (such as the {@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()} callback).</p> + +<p>However, if you are not certain that the user's device +contains an application that handles the Intent, then adding a menu item that executes the +Intent can result in a non-functioning menu item, because the Intent might not resolve to an +Activity that accepts it. To solve this, Android lets you dynamically add menu items to your menu +when Android finds activities on the device that handle your Intent.</p> + +<p>If you're not familiar with creating Intents, read the <a +href="/guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>.</p> + + +<h4>Dynamically adding Intents</h4> + +<p>When you don't know if the user's device has an application that handles a specific Intent, +you can define the Intent and let Android search the device for activities that accept the Intent. +When it finds activies that handle the Intent, it adds a menu item for +each one to your menu and attaches the appropriate Intent to open the Activity when the user +selects it.</p> + +<p>To add menu items based on available activities that accept an Intent:</p> +<ol> + <li>Define an +Intent with the category {@link android.content.Intent#CATEGORY_ALTERNATIVE} and/or +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE}, plus any other requirements.</li> + <li>Call {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +Menu.addIntentOptions()}. Android then searches for any applications that can perform the Intent +and adds them to your menu.</li> +</ol> + +<p>If there are no applications installed +that satisfy the Intent, then no menu items are added.</p> <p class="note"><strong>Note:</strong> -<var>SELECTED_ALTERNATIVE</var> is used to handle the currently selected element on the -screen. So, it should only be used when creating a Menu in <code>onCreateContextMenu()</code> or -<code>onPrepareOptionsMenu()</code>, which is called every time the Options Menu is opened.</p> +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} is used to handle the currently +selected element on the screen. So, it should only be used when creating a Menu in {@link +android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo) +onCreateContextMenu()}.</p> -<p>Here's an example demonstrating how an application would search for -additional services to display on its menu.</p> +<p>For example:</p> <pre> +@Override public boolean onCreateOptionsMenu(Menu menu){ super.onCreateOptionsMenu(menu); // Create an Intent that describes the requirements to fulfill, to be included - // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE. - Intent intent = new Intent(null, getIntent().getData()); + // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE. + Intent intent = new Intent(null, dataUri); intent.addCategory(Intent.CATEGORY_ALTERNATIVE); - - // Search for, and populate the menu with, acceptable offering applications. + + // Search and populate the menu with acceptable offering applications. menu.addIntentOptions( - thisClass.INTENT_OPTIONS, // Menu group + R.id.intent_group, // Menu group to which new items will be added 0, // Unique item ID (none) 0, // Order for the items (none) this.getComponentName(), // The current Activity name @@ -495,17 +612,27 @@ public boolean onCreateOptionsMenu(Menu menu){ return true; }</pre> -<p>For each Activity found that provides an Intent Filter matching the Intent defined, a menu -item will be added, using the <var>android:label</var> value of the intent filter as the text -for the menu item. -The <code>{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) addIntentOptions()}</code> method will also return the number of menu items added.</p> -<p>Also be aware that, when <code>addIntentOptions()</code> is called, it will override any and all -menu items in the menu group specified in the first argument.</p> - -<p>If you wish to offer the services of your Activity to other application menus, then you -only need to define an intent filter as usual. Just be sure to include the <var>ALTERNATIVE</var> and/or -<var>SELECTED_ALTERNATIVE</var> values in the <var>name</var> attribute of -a <code><category></code> element in the intent filter. For example:</p> +<p>For each Activity found that provides an Intent filter matching the Intent defined, a menu +item is added, using the value in the Intent filter's <code>android:label</code> as the +menu item title and the application icon as the menu item icon. The +{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()} method returns the number of menu items added.</p> + +<p class="note"><strong>Note:</strong> When you call {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()}, it overrides any and all menu items by the menu group specified in the first +argument.</p> + + +<h4>Allowing your Activity to be added to menus</h4> + +<p>You can also offer the services of your Activity to other applications, so your +application can be included in the menu of others (reverse the roles described above).</p> + +<p>To be included in other application menus, you need to define an Intent +filter as usual, but be sure to include the {@link android.content.Intent#CATEGORY_ALTERNATIVE} +and/or {@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} values for the Intent filter +category. For example:</p> <pre> <intent-filter label="Resize Image"> ... @@ -514,9 +641,10 @@ a <code><category></code> element in the intent filter. For example:</p> ... </intent-filter> </pre> -<p>read more about writing intent filters in the + +<p>Read more about writing Intent filters in the <a href="/guide/topics/intents/intents-filters.html">Intents and Intent Filters</a> document.</p> <p>For a sample application using this technique, see the -<a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a> -sample code.</p> +<a href="{@docRoot}resources/samples/NotePad/src/com/example/android/notepad/NoteEditor.html">Note +Pad</a> sample code.</p> diff --git a/docs/html/images/axis_device.png b/docs/html/images/axis_device.png Binary files differnew file mode 100644 index 000000000000..f1f666a79a79 --- /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 000000000000..dccb58b82228 --- /dev/null +++ b/docs/html/images/axis_globe.png diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd index 5e751057e17d..6cb7228016b5 100644 --- a/docs/html/resources/dashboard/platform-versions.jd +++ b/docs/html/resources/dashboard/platform-versions.jd @@ -43,29 +43,73 @@ the development of your application features for the devices currently in the hands of users. For information about how to target your application to devices based on platform version, see <a href="{@docRoot}guide/appendix/api-levels.html">API Levels</a>.</p> -<p class="note"><strong>Note:</strong> This data is based on the number -of Android devices that have accessed Android Market within a 14-day period -ending on the data collection date noted below.</p> + +<h3 id="Current">Current Distribution</h3> + +<p>The following pie chart and table is based on the number of Android devices that have accessed +Android Market within a 14-day period ending on the data collection date noted below.</p> <div class="dashboard-panel"> -<img alt="" width="460" height="250" -src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.1,24.6,25.0,0.1,0.3,50.0&chl= -Android%201.1|Android%201.5|Android%201.6|Android%202.0|Android%202.0.1|Android%202.1&chco=c4df9b, -6fad0c" /> +<img alt="" height="250" width="460" +src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.3,21.3,23.5,53.1,1.8&chl=Other*| +Android%201.5|Android%201.6|Android%202.1|Android%202.2&chco=c4df9b,6fad0c" /> <table> <tr> - <th>Android Platform</th> - <th>Percent of Devices</th> + <th>Platform</th> + <th>API Level</th> + <th>Distribution</th> </tr> -<tr><td>Android 1.1</td><td>0.1%</td></tr> -<tr><td>Android 1.5</td><td>24.6%</td></tr> -<tr><td>Android 1.6</td><td>25.0%</td></tr> -<tr><td>Android 2.0</td><td>0.1%</td></tr> -<tr><td>Android 2.0.1</td><td>0.3%</td></tr> -<tr><td>Android 2.1</td><td>50.0%</td></tr> +<tr><td>Android 1.5</td><td>3</td><td>21.3%</td></tr> +<tr><td>Android 1.6</td><td>4</td><td>23.5%</td></tr> +<tr><td>Android 2.1</td><td>7</td><td>53.1%</td></tr> +<tr><td>Android 2.2</td><td>8</td><td>1.8%</td></tr> </table> -<p><em>Data collected during two weeks ending on June 16, 2010</em></p> -</div> + +<p><em>Data collected during two weeks ending on July 1, 2010</em></p> +<p style="font-size:.9em">* <em>Other: 0.3% of devices running obsolete versions</em></p> + +</div><!-- end dashboard-panel --> + + +<h3 id="Historical">Historical Distribution</h3> + +<p>The following stacked line graph provides a history of the relative number of +active Android devices running different versions of the Android platform. It also provides a +valuable perspective of how many devices your application is compatible with, based on the +platform version.</p> + +<p>Notice that the platform versions are stacked on top of each other with the oldest active +version at the top. This format indicates the total percent of active devices that are compatible +with a given version of Android. For example, if you develop your application for +the version that is at the very top of the chart, then your application is +compatible with 100% of active devices (and all future versions), because all Android APIs are +forward compatible. Or, if you develop your application for a version lower on the chart, +then it is currently compatible with the percentage of devices indicated on the y-axis, where the +line for that version meets the y-axis on the right.</p> + +<p>Each dataset in the timeline is based on the number of Android devices that accessed +Android Market within a 14-day period ending on the date indicated on the x-axis.</p> + +<div class="dashboard-panel"> + +<img alt="" height="265" width="700" style="padding:5px;background:#fff" +src="http://chart.apis.google.com/chart?&cht=lc&chs=700x265&chxt=x,y,r&chxr=0,0,10%7C1,0,100%7C2,0, +100&chxl=0%3A%7C2010/02/01%7C02/15%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15% +7C2010/07/01%7C1%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25% +7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10&chxtc=0,5&chd=t:99.0,99.2,99.4,99.5,99.6,99.6,99.6,99.7,100.6 +,101.1,99.9%7C63.4,62.5,61.6,60.6,61.5,61.7,62.3,63.5,73.0,76.4,78.6%7C22.6,23.2,24.3,25.4,29.4,30.2 +,32.7,35.3,46.2,51.3,55.1%7C0.0,0.0,0.0,0.0,4.0,28.3,32.0,34.9,45.9,51.0,54.9%7C0.0,0.0,0.0,0.0,0.0, +0.0,0.0,0.0,0.8,1.2,1.8&chm=tAndroid%201.5,7caa36,0,0,15,,t::-5%7Cb,c3df9b,0,1,0%7CtAndroid%201.6, +638d23,1,0,15,,t::-5%7Cb,b0db6e,1,2,0%7CtAndroid%202.0.1,496c13,2,0,15,,t::-5%7Cb,9ddb3d,2,3,0% +7CtAndroid%202.1,2f4708,3,5,15,,t::-5%7Cb,89cf19,3,4,0%7CB,6fad0c,4,5,0&chg=9,25&chdl=Android%201.5% +20(API%20Level%203)%7CAndroid%201.6%20(API%20Level%204)%7CAndroid%202.0.1%20(API%20Level%206)% +7CAndroid%202.1%20(API%20Level%207)%7CAndroid%202.2%20(API%20Level %208)&chco=add274, +9ad145,84c323,6ba213,507d08" /> + +<p><em>Last historical dataset collected during two weeks ending on July 1, 2010</em></p> + + +</div><!-- end dashboard-panel --> diff --git a/docs/html/resources/dashboard/screens.jd b/docs/html/resources/dashboard/screens.jd index f8130ea50e9d..89fdd2d30352 100644 --- a/docs/html/resources/dashboard/screens.jd +++ b/docs/html/resources/dashboard/screens.jd @@ -49,7 +49,7 @@ ending on the data collection date noted below.</p> <div class="dashboard-panel"> <img alt="" width="460" height="250" -src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.1,57.8,41.0&chl=Small%20/%20ldpi| +src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.4,54.5,44.1&chl=Small%20/%20ldpi| Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" /> <table> @@ -60,14 +60,14 @@ Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" /> <th scope="col">High Density</th> </tr> <tr><th scope="row">Small</th> -<td class='cent hi'>1.1%</td> +<td class='cent hi'>1.4%</td> <td></td> <td></td> </tr> <tr><th scope="row">Normal</th> <td></td> -<td class='cent hi'>57.8%</td> -<td class='cent hi'>41.0%</td> +<td class='cent hi'>54.5%</td> +<td class='cent hi'>44.1%</td> </tr> <tr><th scope="row">Large</th> <td></td> @@ -76,6 +76,6 @@ Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" /> </tr> </table> -<p><em>Data collected during two weeks ending on June 16, 2010</em></p> +<p><em>Data collected during two weeks ending on July 1, 2010</em></p> </div> diff --git a/docs/html/sdk/older_releases.jd b/docs/html/sdk/older_releases.jd index c3ba49566e70..77f7e43783b6 100644 --- a/docs/html/sdk/older_releases.jd +++ b/docs/html/sdk/older_releases.jd @@ -47,7 +47,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.6_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.6_r1.zip">android-sdk- windows-1 .6_r1.zip</a> </td> <td>260529085 bytes</td> @@ -57,7 +57,7 @@ windows-1 .6_r1.zip</a> <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.6_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.6_r1.zip">android-sdk- mac_x86-1 .6_r1.zip</a> </td> <td>247412515 bytes</td> @@ -67,7 +67,7 @@ mac_x86-1 .6_r1.zip</a> <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.6_r1.tgz">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.6_r1.tgz">android- sdk- linux_x86-1.6_r1.tgz</a> </td> <td>238224860 bytes</td> @@ -92,7 +92,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.5_r3.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r3.zip">android-sdk- windows-1 .5_r3.zip</a> </td> <td>191477853 bytes</td> @@ -102,7 +102,7 @@ windows-1 .5_r3.zip</a> <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r3.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r3.zip">android-sdk- mac_x86-1 .5_r3.zip</a> </td> <td>183024673 bytes</td> @@ -112,7 +112,7 @@ mac_x86-1 .5_r3.zip</a> <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r3.zip">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r3.zip">android- sdk- linux_x86-1.5_r3.zip</a> </td> <td>178117561 bytes</td> @@ -137,7 +137,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.1_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.1_r1.zip">android-sdk- windows-1 .1_r1.zip</a> </td> @@ -148,7 +148,7 @@ windows-1 <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.1_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.1_r1.zip">android-sdk- mac_x86-1 .1_r1.zip</a> </td> @@ -159,7 +159,7 @@ mac_x86-1 <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.1_r1.zip">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.1_r1.zip">android- sdk- linux_x86-1.1_r1.zip</a> </td> @@ -185,7 +185,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.0_r2.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.0_r2.zip">android-sdk- windows-1 .0_r2.zip</a> </td> @@ -196,7 +196,7 @@ windows-1 <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.0_r2.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.0_r2.zip">android-sdk- mac_x86-1 .0_r2.zip</a> </td> @@ -207,7 +207,7 @@ mac_x86-1 <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.0_r2.zip">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.0_r2.zip">android- sdk- linux_x86-1.0_r2.zip</a> </td> @@ -241,7 +241,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.5_r2.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r2.zip">android-sdk- windows-1 .5_r2.zip</a> </td> <td>178346828 bytes</td> @@ -251,7 +251,7 @@ windows-1 .5_r2.zip</a> <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r2.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r2.zip">android-sdk- mac_x86-1 .5_r2.zip</a> </td> <td>169945128 bytes</td> @@ -261,7 +261,7 @@ mac_x86-1 .5_r2.zip</a> <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r2.zip">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r2.zip">android- sdk- linux_x86-1.5_r2.zip</a> </td> <td>165035130 bytes</td> @@ -286,7 +286,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.5_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r1.zip">android-sdk- windows-1 .5_r1.zip</a> </td> <td>176263368 bytes</td> @@ -296,7 +296,7 @@ windows-1 .5_r1.zip</a> <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r1.zip">android-sdk- mac_x86-1 .5_r1.zip</a> </td> <td>167848675 bytes</td> @@ -306,7 +306,7 @@ mac_x86-1 .5_r1.zip</a> <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r1.zip">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r1.zip">android- sdk- linux_x86-1.5_r1.zip</a> </td> <td>162938845 bytes</td> @@ -331,7 +331,7 @@ Notes</a></em></p> <td>Windows</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-windows-1.0_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-windows-1.0_r1.zip">android-sdk- windows-1 .0_r1.zip</a> </td> <td>89.7 MB bytes</td> @@ -341,7 +341,7 @@ windows-1 .0_r1.zip</a> <td>Mac OS X (intel)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.0_r1.zip">android-sdk- +href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.0_r1.zip">android-sdk- mac_x86-1 .0_r1.zip</a> </td> <td>87.5 MB bytes</td> @@ -351,7 +351,7 @@ mac_x86-1 .0_r1.zip</a> <td>Linux (i386)</td> <td> <a -href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.0_r1.zip">android- +href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.0_r1.zip">android- sdk- linux_x86-1.0_r1.zip</a> </td> <td>87.8 MB bytes</td> diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h index a4eea2a79a81..c3cd361b25f1 100644 --- a/include/media/MediaProfiles.h +++ b/include/media/MediaProfiles.h @@ -48,8 +48,8 @@ public: static MediaProfiles* getInstance(); /** - * Returns the value for the given param name at the given quality level, - * or -1 if error. + * Returns the value for the given param name for the given camera at + * the given quality level, or -1 if error. * * Supported param name are: * duration - the recording duration. @@ -64,7 +64,8 @@ public: * aud.hz - audio sample rate * aud.ch - number of audio channels */ - int getCamcorderProfileParamByName(const char *name, camcorder_quality quality) const; + int getCamcorderProfileParamByName(const char *name, int cameraId, + camcorder_quality quality) const; /** * Returns the output file formats supported. @@ -124,12 +125,7 @@ public: /** * Returns the number of image encoding quality levels supported. */ - Vector<int> getImageEncodingQualityLevels() const; - - /** - * Returns the maximum amount of memory in bytes we can use for decoding a JPEG file. - */ - int getImageDecodingMaxMemory() const; + Vector<int> getImageEncodingQualityLevels(int cameraId) const; private: MediaProfiles& operator=(const MediaProfiles&); // Don't call me @@ -171,7 +167,8 @@ private: struct CamcorderProfile { CamcorderProfile() - : mFileFormat(OUTPUT_FORMAT_THREE_GPP), + : mCameraId(0), + mFileFormat(OUTPUT_FORMAT_THREE_GPP), mQuality(CAMCORDER_QUALITY_HIGH), mDuration(0), mVideoCodec(0), @@ -182,6 +179,7 @@ private: delete mAudioCodec; } + int mCameraId; output_format mFileFormat; camcorder_quality mQuality; int mDuration; @@ -249,6 +247,11 @@ private: int tag; }; + struct ImageEncodingQualityLevels { + int mCameraId; + Vector<int> mLevels; + }; + // Debug static void logVideoCodec(const VideoCodec& codec); static void logAudioCodec(const AudioCodec& codec); @@ -267,9 +270,11 @@ private: static VideoDecoderCap* createVideoDecoderCap(const char **atts); static VideoEncoderCap* createVideoEncoderCap(const char **atts); static AudioEncoderCap* createAudioEncoderCap(const char **atts); - static CamcorderProfile* createCamcorderProfile(const char **atts); - static int getImageEncodingQualityLevel(const char **atts); - static int getImageDecodingMaxMemory(const char **atts); + static CamcorderProfile* createCamcorderProfile(int cameraId, const char **atts); + static int getCameraId(const char **atts); + + ImageEncodingQualityLevels* findImageEncodingQualityLevels(int cameraId) const; + void addImageEncodingQualityLevel(int cameraId, const char** atts); // Customized element tag handler for parsing the xml configuration file. static void startElementHandler(void *userData, const char *name, const char **atts); @@ -303,6 +308,7 @@ private: static bool sIsInitialized; static MediaProfiles *sInstance; static Mutex sLock; + int mCurrentCameraId; Vector<CamcorderProfile*> mCamcorderProfiles; Vector<AudioEncoderCap*> mAudioEncoders; @@ -310,8 +316,7 @@ private: Vector<AudioDecoderCap*> mAudioDecoders; Vector<VideoDecoderCap*> mVideoDecoders; Vector<output_format> mEncoderOutputFileFormats; - Vector<int> mImageEncodingQualityLevels; - int mImageDecodingMaxMemory; + Vector<ImageEncodingQualityLevels *> mImageEncodingQualityLevels; }; }; // namespace android diff --git a/include/media/stagefright/ColorConverter.h b/include/media/stagefright/ColorConverter.h index 1e341b9dfa05..bc3f464a9b97 100644 --- a/include/media/stagefright/ColorConverter.h +++ b/include/media/stagefright/ColorConverter.h @@ -58,6 +58,11 @@ private: const void *srcBits, size_t srcSkip, void *dstBits, size_t dstSkip); + void convertYUV420SemiPlanar( + size_t width, size_t height, + const void *srcBits, size_t srcSkip, + void *dstBits, size_t dstSkip); + ColorConverter(const ColorConverter &); ColorConverter &operator=(const ColorConverter &); }; diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h index e91d066c04f6..8d3a9df39302 100644 --- a/include/media/stagefright/MediaWriter.h +++ b/include/media/stagefright/MediaWriter.h @@ -27,7 +27,10 @@ struct MediaSource; struct MetaData; struct MediaWriter : public RefBase { - MediaWriter() {} + MediaWriter() + : mMaxFileSizeLimitBytes(0), + mMaxFileDurationLimitUs(0) { + } virtual status_t addSource(const sp<MediaSource> &source) = 0; virtual bool reachedEOS() = 0; diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index 95fe6f6e987d..73f55478f748 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 c95fc021b140..214f43a5f5df 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 { @@ -172,9 +173,18 @@ private: void setVideoInputFormat( const char *mime, const sp<MetaData>& meta); + status_t setupBitRate(int32_t bitRate); + status_t setupErrorCorrectionParameters(); + status_t setupH263EncoderParameters(const sp<MetaData>& meta); 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 9fcceadd828f..b008b54a80e6 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/ALooper.h b/include/media/stagefright/foundation/ALooper.h index 69ad837413be..194f1fc92529 100644 --- a/include/media/stagefright/foundation/ALooper.h +++ b/include/media/stagefright/foundation/ALooper.h @@ -39,7 +39,10 @@ struct ALooper : public RefBase { handler_id registerHandler(const sp<AHandler> &handler); void unregisterHandler(handler_id handlerID); - status_t start(bool runOnCallingThread = false); + status_t start( + bool runOnCallingThread = false, + bool canCallJava = false); + status_t stop(); static int64_t GetNowUs(); diff --git a/include/media/stagefright/foundation/ALooperRoster.h b/include/media/stagefright/foundation/ALooperRoster.h index 1c6869cb4c23..c1bd4ed6b0fa 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/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h index 139c6205529f..c674cbaf3471 100644 --- a/include/media/stagefright/foundation/AMessage.h +++ b/include/media/stagefright/foundation/AMessage.h @@ -60,6 +60,8 @@ struct AMessage : public RefBase { sp<AMessage> dup() const; + AString debugString(int32_t indent = 0) const; + protected: virtual ~AMessage(); diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h index f3339117d8c4..4fd0681596c6 100644 --- a/include/surfaceflinger/Surface.h +++ b/include/surfaceflinger/Surface.h @@ -131,7 +131,7 @@ private: // --------------------------------------------------------------------------- class Surface - : public EGLNativeBase<android_native_window_t, Surface, RefBase> + : public EGLNativeBase<ANativeWindow, Surface, RefBase> { public: struct SurfaceInfo { @@ -195,14 +195,14 @@ private: /* - * android_native_window_t hooks + * ANativeWindow hooks */ - static int setSwapInterval(android_native_window_t* window, int interval); - static int dequeueBuffer(android_native_window_t* window, android_native_buffer_t** buffer); - static int lockBuffer(android_native_window_t* window, android_native_buffer_t* buffer); - static int queueBuffer(android_native_window_t* window, android_native_buffer_t* buffer); - static int query(android_native_window_t* window, int what, int* value); - static int perform(android_native_window_t* window, int operation, ...); + static int setSwapInterval(ANativeWindow* window, int interval); + static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer); + static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer); + static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer); + static int query(ANativeWindow* window, int what, int* value); + static int perform(ANativeWindow* window, int operation, ...); int dequeueBuffer(android_native_buffer_t** buffer); int lockBuffer(android_native_buffer_t* buffer); diff --git a/include/ui/FramebufferNativeWindow.h b/include/ui/FramebufferNativeWindow.h index 8ea3ab973a9a..0f4594ffbd49 100644 --- a/include/ui/FramebufferNativeWindow.h +++ b/include/ui/FramebufferNativeWindow.h @@ -43,7 +43,7 @@ class NativeBuffer; class FramebufferNativeWindow : public EGLNativeBase< - android_native_window_t, + ANativeWindow, FramebufferNativeWindow, LightRefBase<FramebufferNativeWindow> > { @@ -59,12 +59,12 @@ public: private: friend class LightRefBase<FramebufferNativeWindow>; ~FramebufferNativeWindow(); // this class cannot be overloaded - static int setSwapInterval(android_native_window_t* window, int interval); - static int dequeueBuffer(android_native_window_t* window, android_native_buffer_t** buffer); - static int lockBuffer(android_native_window_t* window, android_native_buffer_t* buffer); - static int queueBuffer(android_native_window_t* window, android_native_buffer_t* buffer); - static int query(android_native_window_t* window, int what, int* value); - static int perform(android_native_window_t* window, int operation, ...); + static int setSwapInterval(ANativeWindow* window, int interval); + static int dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer); + static int lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer); + static int queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer); + static int query(ANativeWindow* window, int what, int* value); + static int perform(ANativeWindow* window, int operation, ...); framebuffer_device_t* fbDev; alloc_device_t* grDev; diff --git a/include/ui/Input.h b/include/ui/Input.h index 57b292bbcf24..a2e0ba06a2c2 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -43,7 +43,7 @@ enum { /* * Declare a concrete type for the NDK's input event forward declaration. */ -struct input_event_t { }; +struct AInputEvent { }; namespace android { @@ -87,9 +87,6 @@ enum { // Indicates that the screen was dim when the event was received and the event // should brighten the device. POLICY_FLAG_BRIGHT_HERE = 0x20000000, - - // Indicates that the dispatcher should call back into the policy before dispatching. */ - POLICY_FLAG_INTERCEPT_DISPATCH = 0x40000000, }; /* @@ -136,7 +133,7 @@ struct PointerCoords { /* * Input events. */ -class InputEvent : public input_event_t { +class InputEvent : public AInputEvent { public: virtual ~InputEvent() { } @@ -145,7 +142,7 @@ public: inline int32_t getDeviceId() const { return mDeviceId; } inline int32_t getNature() const { return mNature; } - + protected: void initialize(int32_t deviceId, int32_t nature); @@ -179,6 +176,14 @@ public: inline nsecs_t getEventTime() const { return mEventTime; } + // Return true if this event may have a default action implementation. + static bool hasDefaultAction(int32_t keyCode); + bool hasDefaultAction() const; + + // Return true if this event represents a system key. + static bool isSystemKey(int32_t keyCode); + bool isSystemKey() const; + void initialize( int32_t deviceId, int32_t nature, diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h index 20935604e96f..781da356a07f 100644 --- a/include/ui/InputReader.h +++ b/include/ui/InputReader.h @@ -53,6 +53,8 @@ extern int32_t rotateKeyCode(int32_t keyCode, int32_t orientation); */ struct InputDevice { struct AbsoluteAxisInfo { + bool valid; // set to true if axis parameters are known, false otherwise + int32_t minValue; // minimum value int32_t maxValue; // maximum value int32_t range; // range of values, equal to maxValue - minValue @@ -272,9 +274,16 @@ struct InputDevice { } jumpyTouchFilter; struct Precalculated { + int32_t xOrigin; float xScale; + + int32_t yOrigin; float yScale; + + int32_t pressureOrigin; float pressureScale; + + int32_t sizeOrigin; float sizeScale; } precalculated; @@ -362,10 +371,6 @@ public: // The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it // passes through the dispatch pipeline. ACTION_BRIGHT_HERE = 0x00000008, - - // The input dispatcher should add POLICY_FLAG_INTERCEPT_DISPATCH to the policy flags - // it passed through the dispatch pipeline. - ACTION_INTERCEPT_DISPATCH = 0x00000010 }; /* Describes a virtual key. */ diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h index d6bded620786..11714d536285 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> @@ -333,21 +334,27 @@ private: /* * NDK input queue API. */ -struct input_queue_t { +struct AInputQueue { public: /* Creates a consumer associated with an input channel. */ - explicit input_queue_t(const android::sp<android::InputChannel>& channel); + explicit AInputQueue(const android::sp<android::InputChannel>& channel); /* Destroys the consumer and releases its input channel. */ - ~input_queue_t(); + virtual ~AInputQueue(); inline android::InputConsumer& getConsumer() { return mConsumer; } 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/egl/android_natives.h b/include/ui/egl/android_natives.h index 171f3df3a179..ca89b06816cb 100644 --- a/include/ui/egl/android_natives.h +++ b/include/ui/egl/android_natives.h @@ -22,6 +22,8 @@ #include <hardware/gralloc.h> +#include <android/native_window.h> + #ifdef __cplusplus extern "C" { #endif @@ -90,19 +92,19 @@ enum { NATIVE_WINDOW_API_EGL = 1 }; -typedef struct android_native_window_t +struct ANativeWindow { #ifdef __cplusplus - android_native_window_t() + ANativeWindow() : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0) { common.magic = ANDROID_NATIVE_WINDOW_MAGIC; - common.version = sizeof(android_native_window_t); + common.version = sizeof(ANativeWindow); memset(common.reserved, 0, sizeof(common.reserved)); } - // Implement the methods that sp<android_native_window_t> expects so that it - // can be used to automatically refcount android_native_window_t's. + // Implement the methods that sp<ANativeWindow> expects so that it + // can be used to automatically refcount ANativeWindow's. void incStrong(const void* id) const { common.incRef(const_cast<android_native_base_t*>(&common)); } @@ -135,7 +137,7 @@ typedef struct android_native_window_t * * Returns 0 on success or -errno on error. */ - int (*setSwapInterval)(struct android_native_window_t* window, + int (*setSwapInterval)(struct ANativeWindow* window, int interval); /* @@ -145,7 +147,7 @@ typedef struct android_native_window_t * * Returns 0 on success or -errno on error. */ - int (*dequeueBuffer)(struct android_native_window_t* window, + int (*dequeueBuffer)(struct ANativeWindow* window, struct android_native_buffer_t** buffer); /* @@ -155,7 +157,7 @@ typedef struct android_native_window_t * * Returns 0 on success or -errno on error. */ - int (*lockBuffer)(struct android_native_window_t* window, + int (*lockBuffer)(struct ANativeWindow* window, struct android_native_buffer_t* buffer); /* * hook called by EGL when modifications to the render buffer are done. @@ -165,7 +167,7 @@ typedef struct android_native_window_t * * Returns 0 on success or -errno on error. */ - int (*queueBuffer)(struct android_native_window_t* window, + int (*queueBuffer)(struct ANativeWindow* window, struct android_native_buffer_t* buffer); /* @@ -173,13 +175,13 @@ typedef struct android_native_window_t * * Returns 0 on success or -errno on error. */ - int (*query)(struct android_native_window_t* window, + int (*query)(struct ANativeWindow* window, int what, int* value); /* * hook used to perform various operations on the surface. * (*perform)() is a generic mechanism to add functionality to - * android_native_window_t while keeping backward binary compatibility. + * ANativeWindow while keeping backward binary compatibility. * * This hook should not be called directly, instead use the helper functions * defined below. @@ -197,12 +199,14 @@ typedef struct android_native_window_t * */ - int (*perform)(struct android_native_window_t* window, + int (*perform)(struct ANativeWindow* window, int operation, ... ); void* reserved_proc[3]; -} android_native_window_t; +}; +// Backwards compatibility... please switch to ANativeWindow. +typedef struct ANativeWindow android_native_window_t; /* * native_window_set_usage(..., usage) @@ -216,7 +220,7 @@ typedef struct android_native_window_t */ static inline int native_window_set_usage( - android_native_window_t* window, int usage) + ANativeWindow* window, int usage) { return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage); } @@ -228,7 +232,7 @@ static inline int native_window_set_usage( * can happen if it's connected to some other API. */ static inline int native_window_connect( - android_native_window_t* window, int api) + ANativeWindow* window, int api) { return window->perform(window, NATIVE_WINDOW_CONNECT, api); } @@ -240,7 +244,7 @@ static inline int native_window_connect( * first place. */ static inline int native_window_disconnect( - android_native_window_t* window, int api) + ANativeWindow* window, int api) { return window->perform(window, NATIVE_WINDOW_DISCONNECT, api); } @@ -258,7 +262,7 @@ static inline int native_window_disconnect( * out of the buffer's bound or if the window is invalid. */ static inline int native_window_set_crop( - android_native_window_t* window, + ANativeWindow* window, android_native_rect_t const * crop) { return window->perform(window, NATIVE_WINDOW_SET_CROP, crop); @@ -269,7 +273,7 @@ static inline int native_window_set_crop( * Sets the number of buffers associated with this native window. */ static inline int native_window_set_buffer_count( - android_native_window_t* window, + ANativeWindow* window, size_t bufferCount) { return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount); @@ -287,7 +291,7 @@ static inline int native_window_set_buffer_count( * */ static inline int native_window_set_buffers_geometry( - android_native_window_t* window, + ANativeWindow* window, int w, int h, int format) { return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY, diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h index a95fb171cf56..b3651caeacb9 100644 --- a/include/utils/PollLoop.h +++ b/include/utils/PollLoop.h @@ -22,12 +22,22 @@ #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(); @@ -83,6 +93,11 @@ 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 +115,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,6 +138,7 @@ private: int fd; int events; Callback callback; + ALooper_callbackFunc* looperCallback; void* data; }; @@ -130,8 +159,11 @@ private: 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/rs/rs.spec b/libs/rs/rs.spec index cb9937c785a7..5ae8d0120e39 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -29,7 +29,7 @@ ContextResume { ContextSetSurface { param uint32_t width param uint32_t height - param android_native_window_t *sur + param ANativeWindow *sur } ContextDump { diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index d8a9a997b9df..596f533f988c 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -473,7 +473,7 @@ Context::~Context() objDestroyOOBDestroy(); } -void Context::setSurface(uint32_t w, uint32_t h, android_native_window_t *sur) +void Context::setSurface(uint32_t w, uint32_t h, ANativeWindow *sur) { rsAssert(mIsGraphicsContext); @@ -888,7 +888,7 @@ void rsi_ContextResume(Context *rsc) rsc->resume(); } -void rsi_ContextSetSurface(Context *rsc, uint32_t w, uint32_t h, android_native_window_t *sur) +void rsi_ContextSetSurface(Context *rsc, uint32_t w, uint32_t h, ANativeWindow *sur) { rsc->setSurface(w, h, sur); } diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h index 82c368747cc5..709730efe5f1 100644 --- a/libs/rs/rsContext.h +++ b/libs/rs/rsContext.h @@ -98,7 +98,7 @@ public: void pause(); void resume(); - void setSurface(uint32_t w, uint32_t h, android_native_window_t *sur); + void setSurface(uint32_t w, uint32_t h, ANativeWindow *sur); void setPriority(int32_t p); void assignName(ObjectBase *obj, const char *name, uint32_t len); @@ -246,7 +246,7 @@ private: static void * threadProc(void *); - android_native_window_t *mWndSurface; + ANativeWindow *mWndSurface; Vector<ObjectBase *> mNames; diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk index 0879a66de4f9..a14bfb569b8b 100644 --- a/libs/surfaceflinger/Android.mk +++ b/libs/surfaceflinger/Android.mk @@ -6,6 +6,7 @@ LOCAL_SRC_FILES:= \ DisplayHardware/DisplayHardware.cpp \ DisplayHardware/DisplayHardwareBase.cpp \ BlurFilter.cpp.arm \ + GLExtensions.cpp \ Layer.cpp \ LayerBase.cpp \ LayerBuffer.cpp \ @@ -19,8 +20,8 @@ LOCAL_SRC_FILES:= \ LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -ifeq ($(TARGET_BOARD_PLATFORM), msm7k) - LOCAL_CFLAGS += -DDIM_WITH_TEXTURE +ifeq ($(TARGET_BOARD_PLATFORM), omap3) + LOCAL_CFLAGS += -DNO_RGBX_8888 endif # need "-lrt" on Linux simulator to pick up clock_gettime diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp index 51de1da723ef..2eac0a80a08b 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -40,6 +40,8 @@ #include <hardware/overlay.h> #include <hardware/gralloc.h> +#include "GLExtensions.h" + using namespace android; @@ -73,7 +75,8 @@ void checkEGLErrors(const char* token) DisplayHardware::DisplayHardware( const sp<SurfaceFlinger>& flinger, uint32_t dpy) - : DisplayHardwareBase(flinger, dpy), mFlags(0) + : DisplayHardwareBase(flinger, dpy), + mFlags(0) { init(dpy); } @@ -97,6 +100,9 @@ void DisplayHardware::init(uint32_t dpy) { mNativeWindow = new FramebufferNativeWindow(); framebuffer_device_t const * fbDev = mNativeWindow->getDevice(); + mDpiX = mNativeWindow->xdpi; + mDpiY = mNativeWindow->ydpi; + mRefreshRate = fbDev->fps; mOverlayEngine = NULL; hw_module_t const* module; @@ -104,6 +110,11 @@ void DisplayHardware::init(uint32_t dpy) overlay_control_open(module, &mOverlayEngine); } + EGLint w, h, dummy; + EGLint numConfigs=0; + EGLSurface surface; + EGLContext context; + // initialize EGL EGLint attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, @@ -121,11 +132,6 @@ void DisplayHardware::init(uint32_t dpy) } } - EGLint w, h, dummy; - EGLint numConfigs=0; - EGLSurface surface; - EGLContext context; - // TODO: all the extensions below should be queried through // eglGetProcAddress(). @@ -144,22 +150,6 @@ void DisplayHardware::init(uint32_t dpy) eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); - /* - * Gather EGL extensions - */ - - const char* const egl_extensions = eglQueryString( - display, EGL_EXTENSIONS); - - LOGI("EGL informations:"); - LOGI("# of configs : %d", numConfigs); - LOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); - LOGI("version : %s", eglQueryString(display, EGL_VERSION)); - LOGI("extensions: %s", egl_extensions); - LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); - LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); - - if (mNativeWindow->isUpdateOnDemand()) { mFlags |= PARTIAL_UPDATES; } @@ -174,6 +164,8 @@ void DisplayHardware::init(uint32_t dpy) */ surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL); + eglQuerySurface(display, surface, EGL_WIDTH, &mWidth); + eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight); if (mFlags & PARTIAL_UPDATES) { // if we have partial updates, we definitely don't need to @@ -187,31 +179,6 @@ void DisplayHardware::init(uint32_t dpy) mFlags |= BUFFER_PRESERVED; } } - - eglQuerySurface(display, surface, EGL_WIDTH, &mWidth); - eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight); - -#ifdef EGL_ANDROID_swap_rectangle - if (strstr(egl_extensions, "EGL_ANDROID_swap_rectangle")) { - if (eglSetSwapRectangleANDROID(display, surface, - 0, 0, mWidth, mHeight) == EGL_TRUE) { - // This could fail if this extension is not supported by this - // specific surface (of config) - mFlags |= SWAP_RECTANGLE; - } - } - // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE - // choose PARTIAL_UPDATES, which should be more efficient - if (mFlags & PARTIAL_UPDATES) - mFlags &= ~SWAP_RECTANGLE; -#endif - - - LOGI("flags : %08x", mFlags); - - mDpiX = mNativeWindow->xdpi; - mDpiY = mNativeWindow->ydpi; - mRefreshRate = fbDev->fps; /* Read density from build-specific ro.sf.lcd_density property * except if it is overridden by qemu.sf.lcd_density. @@ -234,49 +201,67 @@ void DisplayHardware::init(uint32_t dpy) context = eglCreateContext(display, config, NULL, NULL); + mDisplay = display; + mConfig = config; + mSurface = surface; + mContext = context; + mFormat = fbDev->format; + mPageFlipCount = 0; + /* * Gather OpenGL ES extensions */ eglMakeCurrent(display, surface, surface, context); - const char* const gl_extensions = (const char*)glGetString(GL_EXTENSIONS); - const char* const gl_renderer = (const char*)glGetString(GL_RENDERER); - LOGI("OpenGL informations:"); - LOGI("vendor : %s", glGetString(GL_VENDOR)); - LOGI("renderer : %s", gl_renderer); - LOGI("version : %s", glGetString(GL_VERSION)); - LOGI("extensions: %s", gl_extensions); + + GLExtensions& extensions(GLExtensions::getInstance()); + extensions.initWithGLStrings( + glGetString(GL_VENDOR), + glGetString(GL_RENDERER), + glGetString(GL_VERSION), + glGetString(GL_EXTENSIONS), + eglQueryString(display, EGL_VENDOR), + eglQueryString(display, EGL_VERSION), + eglQueryString(display, EGL_EXTENSIONS)); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims); - LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize); - LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims); - if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) { - mFlags |= NPOT_EXTENSION; - } -#ifdef EGL_ANDROID_image_native_buffer - if (strstr( gl_extensions, "GL_OES_EGL_image") && - (strstr(egl_extensions, "EGL_KHR_image_base") || - strstr(egl_extensions, "EGL_KHR_image")) && - strstr(egl_extensions, "EGL_ANDROID_image_native_buffer")) { - mFlags |= DIRECT_TEXTURE; +#ifdef EGL_ANDROID_swap_rectangle + if (extensions.hasExtension("EGL_ANDROID_swap_rectangle")) { + if (eglSetSwapRectangleANDROID(display, surface, + 0, 0, mWidth, mHeight) == EGL_TRUE) { + // This could fail if this extension is not supported by this + // specific surface (of config) + mFlags |= SWAP_RECTANGLE; + } } -#else -#warning "EGL_ANDROID_image_native_buffer not supported" + // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE + // choose PARTIAL_UPDATES, which should be more efficient + if (mFlags & PARTIAL_UPDATES) + mFlags &= ~SWAP_RECTANGLE; #endif + LOGI("EGL informations:"); + LOGI("# of configs : %d", numConfigs); + LOGI("vendor : %s", extensions.getEglVendor()); + LOGI("version : %s", extensions.getEglVersion()); + LOGI("extensions: %s", extensions.getEglExtension()); + LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); + LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); + + LOGI("OpenGL informations:"); + LOGI("vendor : %s", extensions.getVendor()); + LOGI("renderer : %s", extensions.getRenderer()); + LOGI("version : %s", extensions.getVersion()); + LOGI("extensions: %s", extensions.getExtension()); + LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize); + LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims); + LOGI("flags = %08x", mFlags); // Unbind the context from this thread eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - mDisplay = display; - mConfig = config; - mSurface = surface; - mContext = context; - mFormat = fbDev->format; - mPageFlipCount = 0; } /* diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h index ebd7c4271c04..66bf5215d5d6 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -29,6 +29,8 @@ #include <pixelflinger/pixelflinger.h> +#include "GLExtensions.h" + #include "DisplayHardware/DisplayHardwareBase.h" struct overlay_control_device_t; @@ -43,13 +45,11 @@ class DisplayHardware : public DisplayHardwareBase { public: enum { - DIRECT_TEXTURE = 0x00000002, - COPY_BITS_EXTENSION = 0x00000008, - NPOT_EXTENSION = 0x00000100, - BUFFER_PRESERVED = 0x00010000, - PARTIAL_UPDATES = 0x00020000, // video driver feature - SLOW_CONFIG = 0x00040000, // software - SWAP_RECTANGLE = 0x00080000, + COPY_BITS_EXTENSION = 0x00000008, + BUFFER_PRESERVED = 0x00010000, + PARTIAL_UPDATES = 0x00020000, // video driver feature + SLOW_CONFIG = 0x00040000, // software + SWAP_RECTANGLE = 0x00080000, }; DisplayHardware( diff --git a/libs/surfaceflinger/GLExtensions.cpp b/libs/surfaceflinger/GLExtensions.cpp new file mode 100644 index 000000000000..7f4f9fc074b4 --- /dev/null +++ b/libs/surfaceflinger/GLExtensions.cpp @@ -0,0 +1,133 @@ +/* + * 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 <stdlib.h> +#include <stdio.h> +#include <stdint.h> + +#include "GLExtensions.h" + +namespace android { +// --------------------------------------------------------------------------- + +ANDROID_SINGLETON_STATIC_INSTANCE( GLExtensions ) + +GLExtensions::GLExtensions() + : mHaveTextureExternal(false), + mHaveNpot(false), + mHaveDirectTexture(false) +{ +} + +void GLExtensions::initWithGLStrings( + GLubyte const* vendor, + GLubyte const* renderer, + GLubyte const* version, + GLubyte const* extensions, + char const* egl_vendor, + char const* egl_version, + char const* egl_extensions) +{ + mVendor = (char const*)vendor; + mRenderer = (char const*)renderer; + mVersion = (char const*)version; + mExtensions = (char const*)extensions; + mEglVendor = egl_vendor; + mEglVersion = egl_version; + mEglExtensions = egl_extensions; + + char const* curr = (char const*)extensions; + char const* head = curr; + do { + head = strchr(curr, ' '); + String8 s(curr, head ? head-curr : strlen(curr)); + if (s.length()) { + mExtensionList.add(s); + } + curr = head+1; + } while (head); + + curr = egl_extensions; + head = curr; + do { + head = strchr(curr, ' '); + String8 s(curr, head ? head-curr : strlen(curr)); + if (s.length()) { + mExtensionList.add(s); + } + curr = head+1; + } while (head); + +#ifdef EGL_ANDROID_image_native_buffer + if (hasExtension("GL_OES_EGL_image") && + (hasExtension("EGL_KHR_image_base") || hasExtension("EGL_KHR_image")) && + hasExtension("EGL_ANDROID_image_native_buffer")) + { + mHaveDirectTexture = true; + } +#else +#warning "EGL_ANDROID_image_native_buffer not supported" +#endif + + if (hasExtension("GL_ARB_texture_non_power_of_two")) { + mHaveNpot = true; + } + + if (hasExtension("GL_OES_texture_external")) { + mHaveTextureExternal = true; + } else if (strstr(mRenderer.string(), "Adreno")) { + // hack for Adreno 200 + mHaveTextureExternal = true; + } +} + +bool GLExtensions::hasExtension(char const* extension) const +{ + const String8 s(extension); + return mExtensionList.indexOf(s) >= 0; +} + +char const* GLExtensions::getVendor() const { + return mVendor.string(); +} + +char const* GLExtensions::getRenderer() const { + return mRenderer.string(); +} + +char const* GLExtensions::getVersion() const { + return mVersion.string(); +} + +char const* GLExtensions::getExtension() const { + return mExtensions.string(); +} + +char const* GLExtensions::getEglVendor() const { + return mEglVendor.string(); +} + +char const* GLExtensions::getEglVersion() const { + return mEglVersion.string(); +} + +char const* GLExtensions::getEglExtension() const { + return mEglExtensions.string(); +} + + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/surfaceflinger/GLExtensions.h b/libs/surfaceflinger/GLExtensions.h new file mode 100644 index 000000000000..bbb284e76892 --- /dev/null +++ b/libs/surfaceflinger/GLExtensions.h @@ -0,0 +1,94 @@ +/* + * 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_SF_GLEXTENSION_H +#define ANDROID_SF_GLEXTENSION_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/String8.h> +#include <utils/SortedVector.h> +#include <utils/Singleton.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> +#include <GLES/glext.h> + +namespace android { +// --------------------------------------------------------------------------- + +class GLExtensions : public Singleton<GLExtensions> +{ + friend class Singleton<GLExtensions>; + + bool mHaveTextureExternal : 1; + bool mHaveNpot : 1; + bool mHaveDirectTexture : 1; + + String8 mVendor; + String8 mRenderer; + String8 mVersion; + String8 mExtensions; + String8 mEglVendor; + String8 mEglVersion; + String8 mEglExtensions; + SortedVector<String8> mExtensionList; + + GLExtensions(const GLExtensions&); + GLExtensions& operator = (const GLExtensions&); + +protected: + GLExtensions(); + +public: + inline bool haveTextureExternal() const { + return mHaveTextureExternal; + } + inline bool haveNpot() const { + return mHaveNpot; + } + inline bool haveDirectTexture() const { + return mHaveDirectTexture; + } + + void initWithGLStrings( + GLubyte const* vendor, + GLubyte const* renderer, + GLubyte const* version, + GLubyte const* extensions, + char const* egl_vendor, + char const* egl_version, + char const* egl_extensions); + + char const* getVendor() const; + char const* getRenderer() const; + char const* getVersion() const; + char const* getExtension() const; + + char const* getEglVendor() const; + char const* getEglVersion() const; + char const* getEglExtension() const; + + bool hasExtension(char const* extension) const; +}; + + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_SF_GLEXTENSION_H diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index e606f713e07d..758da4e87653 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -31,6 +31,7 @@ #include <surfaceflinger/Surface.h> #include "clz.h" +#include "GLExtensions.h" #include "Layer.h" #include "SurfaceFlinger.h" #include "DisplayHardware/DisplayHardware.h" @@ -50,10 +51,11 @@ template <typename T> inline T min(T a, T b) { Layer::Layer(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client) : LayerBaseClient(flinger, display, client), + mGLExtensions(GLExtensions::getInstance()), mNeedsBlending(true), mNeedsDithering(false), mSecure(false), - mTextureManager(mFlags), + mTextureManager(), mBufferManager(mTextureManager), mWidth(0), mHeight(0), mFixedSize(false) { @@ -185,17 +187,13 @@ void Layer::reloadTexture(const Region& dirty) return; } -#ifdef EGL_ANDROID_image_native_buffer - if (mFlags & DisplayHardware::DIRECT_TEXTURE) { + if (mGLExtensions.haveDirectTexture()) { EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); if (mBufferManager.initEglImage(dpy, buffer) != NO_ERROR) { // not sure what we can do here... - mFlags &= ~DisplayHardware::DIRECT_TEXTURE; goto slowpath; } - } else -#endif - { + } else { slowpath: GGLSurface t; status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); @@ -786,19 +784,24 @@ status_t Layer::BufferManager::initEglImage(EGLDisplay dpy, status_t err = NO_INIT; ssize_t index = mActiveBuffer; if (index >= 0) { - Image& texture(mBufferData[index].texture); - err = mTextureManager.initEglImage(&texture, dpy, buffer); - // if EGLImage fails, we switch to regular texture mode, and we - // free all resources associated with using EGLImages. - if (err == NO_ERROR) { - mFailover = false; - destroyTexture(&mFailoverTexture, dpy); - } else { - mFailover = true; - const size_t num = mNumBuffers; - for (size_t i=0 ; i<num ; i++) { - destroyTexture(&mBufferData[i].texture, dpy); + if (!mFailover) { + Image& texture(mBufferData[index].texture); + err = mTextureManager.initEglImage(&texture, dpy, buffer); + // if EGLImage fails, we switch to regular texture mode, and we + // free all resources associated with using EGLImages. + if (err == NO_ERROR) { + mFailover = false; + destroyTexture(&mFailoverTexture, dpy); + } else { + mFailover = true; + const size_t num = mNumBuffers; + for (size_t i=0 ; i<num ; i++) { + destroyTexture(&mBufferData[i].texture, dpy); + } } + } else { + // we failed once, don't try again + err = BAD_VALUE; } } return err; diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h index dcb27a002bb3..e1d283beddb7 100644 --- a/libs/surfaceflinger/Layer.h +++ b/libs/surfaceflinger/Layer.h @@ -37,9 +37,10 @@ namespace android { // --------------------------------------------------------------------------- +class FreezeLock; class Client; +class GLExtensions; class UserClient; -class FreezeLock; // --------------------------------------------------------------------------- @@ -206,6 +207,7 @@ private: // constants sp<Surface> mSurface; PixelFormat mFormat; + const GLExtensions& mGLExtensions; bool mNeedsBlending; bool mNeedsDithering; diff --git a/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp index c1c440b966ca..64a43c78f446 100644 --- a/libs/surfaceflinger/LayerBlur.cpp +++ b/libs/surfaceflinger/LayerBlur.cpp @@ -147,7 +147,9 @@ void LayerBlur::onDraw(const Region& clip) const Region::const_iterator const end = clip.end(); if (it != end) { #if defined(GL_OES_texture_external) - glDisable(GL_TEXTURE_EXTERNAL_OES); + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } #endif glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, mTextureName); @@ -181,7 +183,7 @@ void LayerBlur::onDraw(const Region& clip) const bl.data = (GGLubyte*)pixels; blurFilter(&bl, 8, 2); - if (mFlags & (DisplayHardware::NPOT_EXTENSION)) { + if (GLExtensions::getInstance().haveNpot()) { glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, w, h, 0, mReadFormat, mReadType, pixels); mWidthScale = 1.0f / w; diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp index 732a4ec1dc5e..5f836366c431 100644 --- a/libs/surfaceflinger/LayerBuffer.cpp +++ b/libs/surfaceflinger/LayerBuffer.cpp @@ -315,8 +315,7 @@ void LayerBuffer::Source::unregisterBuffers() { LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, const ISurface::BufferHeap& buffers) - : Source(layer), mStatus(NO_ERROR), mBufferSize(0), - mTextureManager(layer.mFlags) + : Source(layer), mStatus(NO_ERROR), mBufferSize(0) { if (buffers.heap == NULL) { // this is allowed, but in this case, it is illegal to receive @@ -374,11 +373,11 @@ LayerBuffer::BufferSource::~BufferSource() if (mTexture.name != -1U) { // GL textures can only be destroyed from the GL thread - mLayer.mFlinger->mEventQueue.postMessage( - new MessageDestroyTexture(mLayer.mFlinger.get(), mTexture.name) ); + getFlinger()->mEventQueue.postMessage( + new MessageDestroyTexture(getFlinger(), mTexture.name) ); } if (mTexture.image != EGL_NO_IMAGE_KHR) { - EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay()); + EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay()); eglDestroyImageKHR(dpy, mTexture.image); } } @@ -444,7 +443,7 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const const Rect transformedBounds(mLayer.getTransformedBounds()); #if defined(EGL_ANDROID_image_native_buffer) - if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) { + if (GLExtensions::getInstance().haveDirectTexture()) { err = INVALID_OPERATION; if (ourBuffer->supportsCopybit()) { copybit_device_t* copybit = mLayer.mBlitEngine; @@ -549,7 +548,7 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const dst.crop.r = w; dst.crop.b = h; - EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay()); + EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay()); err = mTextureManager.initEglImage(&mTexture, dpy, buffer); } @@ -559,7 +558,7 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const void LayerBuffer::BufferSource::clearTempBufferImage() const { // delete the image - EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay()); + EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay()); eglDestroyImageKHR(dpy, mTexture.image); // and the associated texture (recreate a name) @@ -576,7 +575,7 @@ LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, : Source(layer), mVisibilityChanged(false), mOverlay(0), mOverlayHandle(0), mOverlayDevice(0), mOrientation(orientation) { - overlay_control_device_t* overlay_dev = mLayer.mFlinger->getOverlayEngine(); + overlay_control_device_t* overlay_dev = getFlinger()->getOverlayEngine(); if (overlay_dev == NULL) { // overlays not supported return; @@ -607,7 +606,7 @@ LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, *overlayRef = new OverlayRef(mOverlayHandle, channel, mWidth, mHeight, mFormat, mWidthStride, mHeightStride); - mLayer.mFlinger->signalEvent(); + getFlinger()->signalEvent(); } LayerBuffer::OverlaySource::~OverlaySource() diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h index 413b8a45bc5f..1c0bf830e1ff 100644 --- a/libs/surfaceflinger/LayerBuffer.h +++ b/libs/surfaceflinger/LayerBuffer.h @@ -47,6 +47,7 @@ class LayerBuffer : public LayerBaseClient virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); virtual void destroy() { } + SurfaceFlinger* getFlinger() const { return mLayer.mFlinger.get(); } protected: LayerBuffer& mLayer; }; diff --git a/libs/surfaceflinger/LayerDim.cpp b/libs/surfaceflinger/LayerDim.cpp index 906a583af835..a1f339e488be 100644 --- a/libs/surfaceflinger/LayerDim.cpp +++ b/libs/surfaceflinger/LayerDim.cpp @@ -51,54 +51,6 @@ void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h) sWidth = w; sHeight = h; sUseTexture = false; - -#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer) - -#warning "using a texture to implement LayerDim" - - /* On some h/w like msm7K, it is faster to use a texture because the - * software renderer will defer to copybit, for this to work we need to - * use an EGLImage texture so copybit can actually make use of it. - * This burns a full-screen worth of graphic memory. - */ - - const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); - uint32_t flags = hw.getFlags(); - - if (LIKELY(flags & DisplayHardware::DIRECT_TEXTURE)) { - sp<GraphicBuffer> buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGB_565, - GraphicBuffer::USAGE_SW_WRITE_OFTEN | - GraphicBuffer::USAGE_HW_TEXTURE); - - android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); - - glGenTextures(1, &sTexId); - glBindTexture(GL_TEXTURE_2D, sTexId); - - EGLDisplay dpy = eglGetCurrentDisplay(); - sImage = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, - EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)clientBuf, 0); - if (sImage == EGL_NO_IMAGE_KHR) { - LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError()); - return; - } - - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)sImage); - GLint error = glGetError(); - if (error != GL_NO_ERROR) { - eglDestroyImageKHR(dpy, sImage); - LOGE("glEGLImageTargetTexture2DOES() failed. err=0x%4x", error); - return; - } - - // initialize the texture with zeros - GGLSurface t; - buffer->lock(&t, GRALLOC_USAGE_SW_WRITE_OFTEN); - memset(t.data, 0, t.stride * t.height * 2); - buffer->unlock(); - sUseTexture = true; - } -#endif } LayerDim::~LayerDim() @@ -112,36 +64,19 @@ void LayerDim::onDraw(const Region& clip) const Region::const_iterator const end = clip.end(); if (s.alpha>0 && (it != end)) { const DisplayHardware& hw(graphicPlane(0).displayHardware()); - const GGLfixed alpha = (s.alpha << 16)/255; + const GLfloat alpha = s.alpha/255.0f; const uint32_t fbHeight = hw.getHeight(); glDisable(GL_DITHER); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glColor4x(0, 0, 0, alpha); - + glColor4f(0, 0, 0, alpha); + #if defined(GL_OES_texture_external) - glDisable(GL_TEXTURE_EXTERNAL_OES); -#endif -#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer) - if (sUseTexture) { - glBindTexture(GL_TEXTURE_2D, sTexId); - glEnable(GL_TEXTURE_2D); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - const GLshort texCoords[4][2] = { - { 0, 0 }, - { 0, 1 }, - { 1, 1 }, - { 1, 0 } - }; - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(2, GL_SHORT, 0, texCoords); - } else -#endif - { - glDisable(GL_TEXTURE_2D); + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); } +#endif + glDisable(GL_TEXTURE_2D); GLshort w = sWidth; GLshort h = sHeight; diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 96a541184edb..68e8f19e7ec0 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -44,6 +44,7 @@ #include <GLES/gl.h> #include "clz.h" +#include "GLExtensions.h" #include "Layer.h" #include "LayerBlur.h" #include "LayerBuffer.h" @@ -993,7 +994,9 @@ void SurfaceFlinger::drawWormhole() const glTexCoordPointer(2, GL_SHORT, 0, tcoords); glEnableClientState(GL_TEXTURE_COORD_ARRAY); #if defined(GL_OES_texture_external) - glDisable(GL_TEXTURE_EXTERNAL_OES); + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } #endif glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, mWormholeTexName); @@ -1260,10 +1263,19 @@ sp<Layer> SurfaceFlinger::createNormalSurface( format = PIXEL_FORMAT_RGBA_8888; break; case PIXEL_FORMAT_OPAQUE: +#ifdef NO_RGBX_8888 + format = PIXEL_FORMAT_RGB_565; +#else format = PIXEL_FORMAT_RGBX_8888; +#endif break; } +#ifdef NO_RGBX_8888 + if (format == PIXEL_FORMAT_RGBX_8888) + format = PIXEL_FORMAT_RGBA_8888; +#endif + sp<Layer> layer = new Layer(this, display, client); status_t err = layer->setBuffers(w, h, format, flags); if (LIKELY(err != NO_ERROR)) { diff --git a/libs/surfaceflinger/TextureManager.cpp b/libs/surfaceflinger/TextureManager.cpp index d9bdc6acec34..65260325b21f 100644 --- a/libs/surfaceflinger/TextureManager.cpp +++ b/libs/surfaceflinger/TextureManager.cpp @@ -30,14 +30,15 @@ #include "clz.h" #include "DisplayHardware/DisplayHardware.h" +#include "GLExtensions.h" #include "TextureManager.h" namespace android { // --------------------------------------------------------------------------- -TextureManager::TextureManager(uint32_t flags) - : mFlags(flags) +TextureManager::TextureManager() + : mGLExtensions(GLExtensions::getInstance()) { } @@ -85,9 +86,11 @@ status_t TextureManager::initTexture(Image* pImage, int32_t format) GLenum target = GL_TEXTURE_2D; #if defined(GL_OES_texture_external) - if (format && isSupportedYuvFormat(format)) { - target = GL_TEXTURE_EXTERNAL_OES; - pImage->target = Texture::TEXTURE_EXTERNAL; + if (GLExtensions::getInstance().haveTextureExternal()) { + if (format && isYuvFormat(format)) { + target = GL_TEXTURE_EXTERNAL_OES; + pImage->target = Texture::TEXTURE_EXTERNAL; + } } #endif @@ -102,23 +105,32 @@ status_t TextureManager::initTexture(Image* pImage, int32_t format) bool TextureManager::isSupportedYuvFormat(int format) { - return isYuvFormat(format); + switch (format) { + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_YV16: + return true; + } + return false; } bool TextureManager::isYuvFormat(int format) { switch (format) { - case HAL_PIXEL_FORMAT_NV16: - case HAL_PIXEL_FORMAT_NV21: - case HAL_PIXEL_FORMAT_IYUV: - case HAL_PIXEL_FORMAT_YUV9: - case HAL_PIXEL_FORMAT_YUY2: - case HAL_PIXEL_FORMAT_UYVY: - case HAL_PIXEL_FORMAT_NV12: - case HAL_PIXEL_FORMAT_NV61: - case HAL_PIXEL_FORMAT_NV12_ADRENO_TILED: + // supported YUV formats + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_YV16: + // Legacy/deprecated YUV formats + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + case HAL_PIXEL_FORMAT_YCbCr_422_I: + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: return true; } + + // Any OEM format needs to be considered + if (format>=0x100 && format<=0x1FF) + return true; + return false; } @@ -208,7 +220,7 @@ status_t TextureManager::loadTexture(Texture* texture, /* * round to POT if needed */ - if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { + if (!mGLExtensions.haveNpot()) { texture->NPOTAdjust = true; } @@ -252,7 +264,7 @@ status_t TextureManager::loadTexture(Texture* texture, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->potWidth, texture->potHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - } else if (isYuvFormat(t.format)) { + } else if (isSupportedYuvFormat(t.format)) { // just show the Y plane of YUV buffers glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, texture->potWidth, texture->potHeight, 0, @@ -280,7 +292,7 @@ status_t TextureManager::loadTexture(Texture* texture, 0, bounds.top, t.width, bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, t.data + bounds.top*t.stride*4); - } else if (isYuvFormat(t.format)) { + } else if (isSupportedYuvFormat(t.format)) { // just show the Y plane of YUV buffers glTexSubImage2D(GL_TEXTURE_2D, 0, 0, bounds.top, t.width, bounds.height(), @@ -294,17 +306,19 @@ status_t TextureManager::loadTexture(Texture* texture, void TextureManager::activateTexture(const Texture& texture, bool filter) { const GLenum target = getTextureTarget(&texture); - - glBindTexture(target, texture.name); - glEnable(target); - + if (target == GL_TEXTURE_2D) { + glBindTexture(GL_TEXTURE_2D, texture.name); + glEnable(GL_TEXTURE_2D); #if defined(GL_OES_texture_external) - if (texture.target == Texture::TEXTURE_2D) { - glDisable(GL_TEXTURE_EXTERNAL_OES); + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } } else { + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name); + glEnable(GL_TEXTURE_EXTERNAL_OES); glDisable(GL_TEXTURE_2D); - } #endif + } if (filter) { glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -319,7 +333,9 @@ void TextureManager::deactivateTextures() { glDisable(GL_TEXTURE_2D); #if defined(GL_OES_texture_external) - glDisable(GL_TEXTURE_EXTERNAL_OES); + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } #endif } diff --git a/libs/surfaceflinger/TextureManager.h b/libs/surfaceflinger/TextureManager.h index 1f7fe3f0cae3..c7c14e70bb44 100644 --- a/libs/surfaceflinger/TextureManager.h +++ b/libs/surfaceflinger/TextureManager.h @@ -32,6 +32,7 @@ namespace android { // --------------------------------------------------------------------------- +class GLExtensions; class GraphicBuffer; // --------------------------------------------------------------------------- @@ -61,7 +62,7 @@ struct Texture : public Image { // --------------------------------------------------------------------------- class TextureManager { - uint32_t mFlags; + const GLExtensions& mGLExtensions; static status_t initTexture(Image* texture, int32_t format); static status_t initTexture(Texture* texture); static bool isSupportedYuvFormat(int format); @@ -69,7 +70,7 @@ class TextureManager { static GLenum getTextureTarget(const Image* pImage); public: - TextureManager(uint32_t flags); + TextureManager(); // load bitmap data into the active buffer status_t loadTexture(Texture* texture, diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp index 8617d94a449b..dc6332cb6802 100644 --- a/libs/surfaceflinger_client/Surface.cpp +++ b/libs/surfaceflinger_client/Surface.cpp @@ -387,21 +387,21 @@ sp<Surface> Surface::readFromParcel( void Surface::init() { - android_native_window_t::setSwapInterval = setSwapInterval; - android_native_window_t::dequeueBuffer = dequeueBuffer; - android_native_window_t::lockBuffer = lockBuffer; - android_native_window_t::queueBuffer = queueBuffer; - android_native_window_t::query = query; - android_native_window_t::perform = perform; + ANativeWindow::setSwapInterval = setSwapInterval; + ANativeWindow::dequeueBuffer = dequeueBuffer; + ANativeWindow::lockBuffer = lockBuffer; + ANativeWindow::queueBuffer = queueBuffer; + ANativeWindow::query = query; + ANativeWindow::perform = perform; DisplayInfo dinfo; SurfaceComposerClient::getDisplayInfo(0, &dinfo); - const_cast<float&>(android_native_window_t::xdpi) = dinfo.xdpi; - const_cast<float&>(android_native_window_t::ydpi) = dinfo.ydpi; + const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi; + const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi; // FIXME: set real values here - const_cast<int&>(android_native_window_t::minSwapInterval) = 1; - const_cast<int&>(android_native_window_t::maxSwapInterval) = 1; - const_cast<uint32_t&>(android_native_window_t::flags) = 0; + const_cast<int&>(ANativeWindow::minSwapInterval) = 1; + const_cast<int&>(ANativeWindow::maxSwapInterval) = 1; + const_cast<uint32_t&>(ANativeWindow::flags) = 0; mConnected = 0; mSwapRectangle.makeInvalid(); @@ -485,35 +485,35 @@ sp<ISurface> Surface::getISurface() const { // ---------------------------------------------------------------------------- -int Surface::setSwapInterval(android_native_window_t* window, int interval) { +int Surface::setSwapInterval(ANativeWindow* window, int interval) { return 0; } -int Surface::dequeueBuffer(android_native_window_t* window, +int Surface::dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer) { Surface* self = getSelf(window); return self->dequeueBuffer(buffer); } -int Surface::lockBuffer(android_native_window_t* window, +int Surface::lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer) { Surface* self = getSelf(window); return self->lockBuffer(buffer); } -int Surface::queueBuffer(android_native_window_t* window, +int Surface::queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer) { Surface* self = getSelf(window); return self->queueBuffer(buffer); } -int Surface::query(android_native_window_t* window, +int Surface::query(ANativeWindow* window, int what, int* value) { Surface* self = getSelf(window); return self->query(what, value); } -int Surface::perform(android_native_window_t* window, +int Surface::perform(ANativeWindow* window, int operation, ...) { va_list args; va_start(args, operation); @@ -803,7 +803,7 @@ status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking) { if (getConnectedApi()) { LOGE("Surface::lock(%p) failed. Already connected to another API", - (android_native_window_t*)this); + (ANativeWindow*)this); CallStack stack; stack.update(); stack.dump(""); diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index 52380a077c7b..6f8948d70e8d 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -67,7 +67,7 @@ private: * This implements the (main) framebuffer management. This class is used * mostly by SurfaceFlinger, but also by command line GL application. * - * In fact this is an implementation of android_native_window_t on top of + * In fact this is an implementation of ANativeWindow on top of * the framebuffer. * * Currently it is pretty simple, it manages only two buffers (the front and @@ -117,23 +117,23 @@ FramebufferNativeWindow::FramebufferNativeWindow() LOGE_IF(err, "fb buffer 1 allocation failed w=%d, h=%d, err=%s", fbDev->width, fbDev->height, strerror(-err)); - const_cast<uint32_t&>(android_native_window_t::flags) = fbDev->flags; - const_cast<float&>(android_native_window_t::xdpi) = fbDev->xdpi; - const_cast<float&>(android_native_window_t::ydpi) = fbDev->ydpi; - const_cast<int&>(android_native_window_t::minSwapInterval) = + const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags; + const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi; + const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi; + const_cast<int&>(ANativeWindow::minSwapInterval) = fbDev->minSwapInterval; - const_cast<int&>(android_native_window_t::maxSwapInterval) = + const_cast<int&>(ANativeWindow::maxSwapInterval) = fbDev->maxSwapInterval; } else { LOGE("Couldn't get gralloc module"); } - android_native_window_t::setSwapInterval = setSwapInterval; - android_native_window_t::dequeueBuffer = dequeueBuffer; - android_native_window_t::lockBuffer = lockBuffer; - android_native_window_t::queueBuffer = queueBuffer; - android_native_window_t::query = query; - android_native_window_t::perform = perform; + ANativeWindow::setSwapInterval = setSwapInterval; + ANativeWindow::dequeueBuffer = dequeueBuffer; + ANativeWindow::lockBuffer = lockBuffer; + ANativeWindow::queueBuffer = queueBuffer; + ANativeWindow::query = query; + ANativeWindow::perform = perform; } FramebufferNativeWindow::~FramebufferNativeWindow() @@ -168,13 +168,13 @@ status_t FramebufferNativeWindow::compositionComplete() } int FramebufferNativeWindow::setSwapInterval( - android_native_window_t* window, int interval) + ANativeWindow* window, int interval) { framebuffer_device_t* fb = getSelf(window)->fbDev; return fb->setSwapInterval(fb, interval); } -int FramebufferNativeWindow::dequeueBuffer(android_native_window_t* window, +int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer) { FramebufferNativeWindow* self = getSelf(window); @@ -196,7 +196,7 @@ int FramebufferNativeWindow::dequeueBuffer(android_native_window_t* window, return 0; } -int FramebufferNativeWindow::lockBuffer(android_native_window_t* window, +int FramebufferNativeWindow::lockBuffer(ANativeWindow* window, android_native_buffer_t* buffer) { FramebufferNativeWindow* self = getSelf(window); @@ -210,7 +210,7 @@ int FramebufferNativeWindow::lockBuffer(android_native_window_t* window, return NO_ERROR; } -int FramebufferNativeWindow::queueBuffer(android_native_window_t* window, +int FramebufferNativeWindow::queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer) { FramebufferNativeWindow* self = getSelf(window); @@ -224,7 +224,7 @@ int FramebufferNativeWindow::queueBuffer(android_native_window_t* window, return res; } -int FramebufferNativeWindow::query(android_native_window_t* window, +int FramebufferNativeWindow::query(ANativeWindow* window, int what, int* value) { FramebufferNativeWindow* self = getSelf(window); @@ -245,7 +245,7 @@ int FramebufferNativeWindow::query(android_native_window_t* window, return BAD_VALUE; } -int FramebufferNativeWindow::perform(android_native_window_t* window, +int FramebufferNativeWindow::perform(ANativeWindow* window, int operation, ...) { switch (operation) { diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 4b5f02517a04..519c277c603a 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -127,18 +127,6 @@ status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format, { GraphicBufferAllocator& allocator = GraphicBufferAllocator::get(); status_t err = allocator.alloc(w, h, format, reqUsage, &handle, &stride); - - if (err<0 && format == PIXEL_FORMAT_RGBX_8888) { - /* - * There is currently a bug with some gralloc implementations - * not supporting RGBX_8888. In this case, we revert to using RGBA_8888 - * which is not exactly the same, as GL_REPLACE will yield a different - * result. - */ - format = PIXEL_FORMAT_RGBA_8888; - err = allocator.alloc(w, h, format, reqUsage, &handle, &stride); - } - if (err == NO_ERROR) { this->width = w; this->height = h; diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index 4121b5a438c2..a64251f15adc 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -20,6 +20,70 @@ void InputEvent::initialize(int32_t deviceId, int32_t nature) { // class KeyEvent +bool KeyEvent::hasDefaultAction(int32_t keyCode) { + switch (keyCode) { + case KEYCODE_HOME: + case KEYCODE_BACK: + case KEYCODE_CALL: + case KEYCODE_ENDCALL: + case KEYCODE_VOLUME_UP: + case KEYCODE_VOLUME_DOWN: + case KEYCODE_POWER: + case KEYCODE_CAMERA: + case KEYCODE_HEADSETHOOK: + case KEYCODE_MENU: + case KEYCODE_NOTIFICATION: + case KEYCODE_FOCUS: + case KEYCODE_SEARCH: + 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: + case KEYCODE_MUTE: + return true; + } + + return false; +} + +bool KeyEvent::hasDefaultAction() const { + return hasDefaultAction(getKeyCode()); +} + +bool KeyEvent::isSystemKey(int32_t keyCode) { + switch (keyCode) { + case KEYCODE_MENU: + case KEYCODE_SOFT_RIGHT: + case KEYCODE_HOME: + case KEYCODE_BACK: + case KEYCODE_CALL: + case KEYCODE_ENDCALL: + case KEYCODE_VOLUME_UP: + case KEYCODE_VOLUME_DOWN: + case KEYCODE_MUTE: + case KEYCODE_POWER: + 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: + case KEYCODE_CAMERA: + case KEYCODE_FOCUS: + case KEYCODE_SEARCH: + return true; + } + + return false; +} + +bool KeyEvent::isSystemKey() const { + return isSystemKey(getKeyCode()); +} + void KeyEvent::initialize( int32_t deviceId, int32_t nature, diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index 0fc29b24f780..8f6d1fe0c0cb 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -232,6 +232,9 @@ void InputDispatcher::processConfigurationChangedLockedInterruptible( LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime); #endif + // Reset key repeating in case a keyboard device was added or removed or something. + resetKeyRepeatLocked(); + mLock.unlock(); mPolicy->notifyConfigurationChanged(entry->eventTime); diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index 8087f84bc54c..899027c9b920 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -387,6 +387,11 @@ void InputDevice::TouchScreenState::calculatePointerIds() { * points has moved more than a screen height from the last position, * then drop it. */ bool InputDevice::TouchScreenState::applyBadTouchFilter() { + // This hack requires valid axis parameters. + if (! parameters.yAxis.valid) { + return false; + } + uint32_t pointerCount = currentTouch.pointerCount; // Nothing to do if there are no points. @@ -466,6 +471,11 @@ bool InputDevice::TouchScreenState::applyBadTouchFilter() { * the coordinate value for one axis has jumped to the other pointer's location. */ bool InputDevice::TouchScreenState::applyJumpyTouchFilter() { + // This hack requires valid axis parameters. + if (! parameters.yAxis.valid) { + return false; + } + uint32_t pointerCount = currentTouch.pointerCount; if (lastTouch.pointerCount != pointerCount) { #if DEBUG_HACKS @@ -724,6 +734,12 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() { } bool InputDevice::TouchScreenState::isPointInsideDisplay(int32_t x, int32_t y) const { + if (! parameters.xAxis.valid || ! parameters.yAxis.valid) { + // Assume all points on a touch screen without valid axis parameters are + // inside the display. + return true; + } + return x >= parameters.xAxis.minValue && x <= parameters.xAxis.maxValue && y >= parameters.yAxis.minValue @@ -1435,6 +1451,9 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli int32_t pointerIds[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; + const InputDevice::TouchScreenState::Precalculated& precalculated = + device->touchScreen.precalculated; + // Walk through the the active pointers and map touch screen coordinates (TouchData) into // display coordinates (PointerCoords) and adjust for display orientation. while (! idBits.isEmpty()) { @@ -1442,18 +1461,14 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli idBits.clearBit(id); uint32_t index = touch->idToIndex[id]; - float x = (float(touch->pointers[index].x) - - device->touchScreen.parameters.xAxis.minValue) - * device->touchScreen.precalculated.xScale; - float y = (float(touch->pointers[index].y) - - device->touchScreen.parameters.yAxis.minValue) - * device->touchScreen.precalculated.yScale; - float pressure = (float(touch->pointers[index].pressure) - - device->touchScreen.parameters.pressureAxis.minValue) - * device->touchScreen.precalculated.pressureScale; - float size = (float(touch->pointers[index].size) - - device->touchScreen.parameters.sizeAxis.minValue) - * device->touchScreen.precalculated.sizeScale; + float x = float(touch->pointers[index].x + - precalculated.xOrigin) * precalculated.xScale; + float y = float(touch->pointers[index].y + - precalculated.yOrigin) * precalculated.yScale; + float pressure = float(touch->pointers[index].pressure + - precalculated.pressureOrigin) * precalculated.pressureScale; + float size = float(touch->pointers[index].size + - precalculated.sizeOrigin) * precalculated.sizeScale; switch (mDisplayOrientation) { case InputReaderPolicyInterface::ROTATION_90: { @@ -1624,10 +1639,6 @@ bool InputReader::applyStandardInputDispatchPolicyActions(nsecs_t when, *policyFlags |= POLICY_FLAG_BRIGHT_HERE; } - if (policyActions & InputReaderPolicyInterface::ACTION_INTERCEPT_DISPATCH) { - *policyFlags |= POLICY_FLAG_INTERCEPT_DISPATCH; - } - return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH; } @@ -1651,7 +1662,11 @@ bool InputReader::refreshDisplayProperties() { } } - mDisplayOrientation = newOrientation; + if (newOrientation != mDisplayOrientation) { + LOGD("Display orientation changed to %d", mDisplayOrientation); + + mDisplayOrientation = newOrientation; + } return true; } else { resetDisplayProperties(); @@ -1740,10 +1755,25 @@ void InputReader::configureDevice(InputDevice* device) { device->touchScreen.parameters.useJumpyTouchFilter = mPolicy->filterJumpyTouchEvents(); - device->touchScreen.precalculated.pressureScale = - 1.0f / device->touchScreen.parameters.pressureAxis.range; - device->touchScreen.precalculated.sizeScale = - 1.0f / device->touchScreen.parameters.sizeAxis.range; + if (device->touchScreen.parameters.pressureAxis.valid) { + device->touchScreen.precalculated.pressureOrigin = + device->touchScreen.parameters.pressureAxis.minValue; + device->touchScreen.precalculated.pressureScale = + 1.0f / device->touchScreen.parameters.pressureAxis.range; + } else { + device->touchScreen.precalculated.pressureOrigin = 0; + device->touchScreen.precalculated.pressureScale = 1.0f; + } + + if (device->touchScreen.parameters.sizeAxis.valid) { + device->touchScreen.precalculated.sizeOrigin = + device->touchScreen.parameters.sizeAxis.minValue; + device->touchScreen.precalculated.sizeScale = + 1.0f / device->touchScreen.parameters.sizeAxis.range; + } else { + device->touchScreen.precalculated.sizeOrigin = 0; + device->touchScreen.precalculated.sizeScale = 1.0f; + } } if (device->isTrackball()) { @@ -1758,22 +1788,42 @@ void InputReader::configureDevice(InputDevice* device) { void InputReader::configureDeviceForCurrentDisplaySize(InputDevice* device) { if (device->isTouchScreen()) { - if (mDisplayWidth < 0) { - LOGD("Skipping part of touch screen configuration since display size is unknown."); + if (device->touchScreen.parameters.xAxis.valid + && device->touchScreen.parameters.yAxis.valid) { + device->touchScreen.precalculated.xOrigin = + device->touchScreen.parameters.xAxis.minValue; + device->touchScreen.precalculated.yOrigin = + device->touchScreen.parameters.yAxis.minValue; + + if (mDisplayWidth < 0) { + LOGD("Skipping part of touch screen configuration since display size is unknown."); + + device->touchScreen.precalculated.xScale = 1.0f; + device->touchScreen.precalculated.yScale = 1.0f; + } else { + LOGI("Device configured: id=0x%x, name=%s (display size was changed)", device->id, + device->name.string()); + + device->touchScreen.precalculated.xScale = + float(mDisplayWidth) / device->touchScreen.parameters.xAxis.range; + device->touchScreen.precalculated.yScale = + float(mDisplayHeight) / device->touchScreen.parameters.yAxis.range; + + configureVirtualKeys(device); + } } else { - LOGI("Device configured: id=0x%x, name=%s (display size was changed)", device->id, - device->name.string()); - configureVirtualKeys(device); - - device->touchScreen.precalculated.xScale = - float(mDisplayWidth) / device->touchScreen.parameters.xAxis.range; - device->touchScreen.precalculated.yScale = - float(mDisplayHeight) / device->touchScreen.parameters.yAxis.range; + device->touchScreen.precalculated.xOrigin = 0; + device->touchScreen.precalculated.xScale = 1.0f; + device->touchScreen.precalculated.yOrigin = 0; + device->touchScreen.precalculated.yScale = 1.0f; } } } void InputReader::configureVirtualKeys(InputDevice* device) { + assert(device->touchScreen.parameters.xAxis.valid + && device->touchScreen.parameters.yAxis.valid); + device->touchScreen.virtualKeys.clear(); Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions; @@ -1837,16 +1887,18 @@ void InputReader::configureAbsoluteAxisInfo(InputDevice* device, if (out->range != 0) { LOGI(" %s: min=%d max=%d flat=%d fuzz=%d", name, out->minValue, out->maxValue, out->flat, out->fuzz); + out->valid = true; return; } } + out->valid = false; out->minValue = 0; out->maxValue = 0; out->flat = 0; out->fuzz = 0; out->range = 0; - LOGI(" %s: unknown axis values, setting to zero", name); + LOGI(" %s: unknown axis values, marking as invalid", name); } void InputReader::configureExcludedDevices() { diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp index f56537ab7b2b..25def3c18ca1 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/ui/InputTransport.cpp @@ -691,7 +691,7 @@ void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const { } // namespace android -// --- input_queue_t --- +// --- AInputQueue --- using android::InputEvent; using android::InputChannel; @@ -699,13 +699,13 @@ using android::InputConsumer; using android::sp; using android::status_t; -input_queue_t::input_queue_t(const sp<InputChannel>& channel) : +AInputQueue::AInputQueue(const sp<InputChannel>& channel) : mConsumer(channel) { } -input_queue_t::~input_queue_t() { +AInputQueue::~AInputQueue() { } -status_t input_queue_t::consume(InputEvent** event) { +status_t AInputQueue::consume(InputEvent** event) { return mConsumer.consume(&mInputEventFactory, event); } diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp index c9a5950d1532..b205418e0f70 100644 --- a/libs/ui/PixelFormat.cpp +++ b/libs/ui/PixelFormat.cpp @@ -59,16 +59,13 @@ status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info) // YUV format from the HAL are handled here switch (format) { case HAL_PIXEL_FORMAT_YCbCr_422_SP: - case HAL_PIXEL_FORMAT_YCrCb_422_SP: - case HAL_PIXEL_FORMAT_YCbCr_422_P: case HAL_PIXEL_FORMAT_YCbCr_422_I: - case HAL_PIXEL_FORMAT_CbYCrY_422_I: + case HAL_PIXEL_FORMAT_YV16: info->bitsPerPixel = 16; goto done; - case HAL_PIXEL_FORMAT_YCbCr_420_SP: case HAL_PIXEL_FORMAT_YCrCb_420_SP: case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: - case HAL_PIXEL_FORMAT_YCbCr_420_P: + case HAL_PIXEL_FORMAT_YV12: info->bitsPerPixel = 12; done: info->format = format; diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp index 20a4d1385bd1..58fe1411d99a 100644 --- a/libs/utils/PollLoop.cpp +++ b/libs/utils/PollLoop.cpp @@ -21,6 +21,10 @@ namespace android { +static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; +static bool gHaveTLS = false; +static pthread_key_t gTLS = 0; + PollLoop::PollLoop() : mPolling(false), mWaiters(0) { openWakePipe(); @@ -30,6 +34,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 +93,7 @@ void PollLoop::openWakePipe() { RequestedCallback requestedCallback; requestedCallback.callback = NULL; + requestedCallback.looperCallback = NULL; requestedCallback.data = NULL; mRequestedCallbacks.insertAt(requestedCallback, 0); } @@ -123,12 +163,14 @@ bool PollLoop::pollOnce(int timeoutMillis) { if (revents) { const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i); Callback callback = requestedCallback.callback; + ALooper_callbackFunc* looperCallback = requestedCallback.looperCallback; - if (callback) { + if (callback || looperCallback) { PendingCallback pendingCallback; pendingCallback.fd = requestedFd.fd; pendingCallback.events = requestedFd.revents; pendingCallback.callback = callback; + pendingCallback.looperCallback = looperCallback; pendingCallback.data = requestedCallback.data; mPendingCallbacks.push(pendingCallback); } else { @@ -172,8 +214,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); } @@ -200,11 +248,22 @@ void PollLoop::wake() { } 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) { + if (! events || (! callback && ! looperCallback)) { LOGE("Invalid attempt to set a callback with no selected poll events or no callback."); removeCallback(fd); return; @@ -218,6 +277,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/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 50f0674c28ab..9212708e6c13 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -16,6 +16,7 @@ package android.media; +import java.util.NoSuchElementException; import android.app.ActivityManagerNative; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -1016,7 +1017,11 @@ public class AudioService extends IAudioService.Stub { } else { mStartcount--; if (mStartcount == 0) { - mCb.unlinkToDeath(this, 0); + try { + mCb.unlinkToDeath(this, 0); + } catch (NoSuchElementException e) { + Log.w(TAG, "decCount() going to 0 but not registered to binder"); + } } requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED); } @@ -1025,8 +1030,14 @@ public class AudioService extends IAudioService.Stub { public void clearCount(boolean stopSco) { synchronized(mScoClients) { + if (mStartcount != 0) { + try { + mCb.unlinkToDeath(this, 0); + } catch (NoSuchElementException e) { + Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder"); + } + } mStartcount = 0; - mCb.unlinkToDeath(this, 0); if (stopSco) { requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED); } diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java index 64d6460dbd68..a27df575749f 100644 --- a/media/java/android/media/CamcorderProfile.java +++ b/media/java/android/media/CamcorderProfile.java @@ -119,15 +119,26 @@ public class CamcorderProfile public int audioChannels; /** - * Returns the camcorder profile for the given quality level. + * Returns the camcorder profile for the default camera at the given + * quality level. * @param quality the target quality level for the camcorder profile */ public static CamcorderProfile get(int quality) { + return get(0, quality); + } + + /** + * Returns the camcorder profile for the given camera at the given + * quality level. + * @param cameraId the id for the camera + * @param quality the target quality level for the camcorder profile + */ + public static CamcorderProfile get(int cameraId, int quality) { if (quality < QUALITY_LOW || quality > QUALITY_HIGH) { String errMessage = "Unsupported quality level: " + quality; throw new IllegalArgumentException(errMessage); } - return native_get_camcorder_profile(quality); + return native_get_camcorder_profile(cameraId, quality); } static { @@ -165,5 +176,6 @@ public class CamcorderProfile // Methods implemented by JNI private static native final void native_init(); - private static native final CamcorderProfile native_get_camcorder_profile(int quality); + private static native final CamcorderProfile native_get_camcorder_profile( + int cameraId, int quality); } diff --git a/media/java/android/media/CameraProfile.java b/media/java/android/media/CameraProfile.java index f8d393512502..6a0be08defdd 100644 --- a/media/java/android/media/CameraProfile.java +++ b/media/java/android/media/CameraProfile.java @@ -17,6 +17,7 @@ package android.media; import java.util.Arrays; +import java.util.HashMap; /** * The CameraProfile class is used to retrieve the pre-defined still image @@ -40,36 +41,55 @@ public class CameraProfile /* * Cache the Jpeg encoding quality parameters */ - private static final int[] sJpegEncodingQualityParameters; + private static final HashMap<Integer, int[]> sCache = new HashMap<Integer, int[]>(); /** * Returns a pre-defined still image capture (jpeg) quality level - * used for the given quality level in the Camera application. + * used for the given quality level in the Camera application for + * the default camera. * * @param quality The target quality level */ public static int getJpegEncodingQualityParameter(int quality) { + return getJpegEncodingQualityParameter(0, quality); + } + + /** + * Returns a pre-defined still image capture (jpeg) quality level + * used for the given quality level in the Camera application for + * the specified camera. + * + * @param cameraId The id of the camera + * @param quality The target quality level + */ + public static int getJpegEncodingQualityParameter(int cameraId, int quality) { if (quality < QUALITY_LOW || quality > QUALITY_HIGH) { throw new IllegalArgumentException("Unsupported quality level: " + quality); } - return sJpegEncodingQualityParameters[quality]; + synchronized (sCache) { + int[] levels = sCache.get(cameraId); + if (levels == null) { + levels = getImageEncodingQualityLevels(cameraId); + sCache.put(cameraId, levels); + } + return levels[quality]; + } } static { System.loadLibrary("media_jni"); native_init(); - sJpegEncodingQualityParameters = getImageEncodingQualityLevels(); } - private static int[] getImageEncodingQualityLevels() { - int nLevels = native_get_num_image_encoding_quality_levels(); + private static int[] getImageEncodingQualityLevels(int cameraId) { + int nLevels = native_get_num_image_encoding_quality_levels(cameraId); if (nLevels != QUALITY_HIGH + 1) { throw new RuntimeException("Unexpected Jpeg encoding quality levels " + nLevels); } int[] levels = new int[nLevels]; for (int i = 0; i < nLevels; ++i) { - levels[i] = native_get_image_encoding_quality_level(i); + levels[i] = native_get_image_encoding_quality_level(cameraId, i); } Arrays.sort(levels); // Lower quality level ALWAYS comes before higher one return levels; @@ -77,6 +97,6 @@ public class CameraProfile // Methods implemented by JNI private static native final void native_init(); - private static native final int native_get_num_image_encoding_quality_levels(); - private static native final int native_get_image_encoding_quality_level(int index); + private static native final int native_get_num_image_encoding_quality_levels(int cameraId); + private static native final int native_get_image_encoding_quality_level(int cameraId, int index); } diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp index 7d7533a23556..cce9fd04e3cf 100644 --- a/media/jni/android_media_MediaProfiles.cpp +++ b/media/jni/android_media_MediaProfiles.cpp @@ -162,26 +162,26 @@ android_media_MediaProfiles_native_get_audio_encoder_cap(JNIEnv *env, jobject th } static jobject -android_media_MediaProfiles_native_get_camcorder_profile(JNIEnv *env, jobject thiz, jint quality) +android_media_MediaProfiles_native_get_camcorder_profile(JNIEnv *env, jobject thiz, jint id, jint quality) { - LOGV("native_get_camcorder_profile: %d", quality); + LOGV("native_get_camcorder_profile: %d %d", id, quality); if (quality != CAMCORDER_QUALITY_HIGH && quality != CAMCORDER_QUALITY_LOW) { jniThrowException(env, "java/lang/RuntimeException", "Unknown camcorder profile quality"); return NULL; } camcorder_quality q = static_cast<camcorder_quality>(quality); - int duration = sProfiles->getCamcorderProfileParamByName("duration", q); - int fileFormat = sProfiles->getCamcorderProfileParamByName("file.format", q); - int videoCodec = sProfiles->getCamcorderProfileParamByName("vid.codec", q); - int videoBitRate = sProfiles->getCamcorderProfileParamByName("vid.bps", q); - int videoFrameRate = sProfiles->getCamcorderProfileParamByName("vid.fps", q); - int videoFrameWidth = sProfiles->getCamcorderProfileParamByName("vid.width", q); - int videoFrameHeight = sProfiles->getCamcorderProfileParamByName("vid.height", q); - int audioCodec = sProfiles->getCamcorderProfileParamByName("aud.codec", q); - int audioBitRate = sProfiles->getCamcorderProfileParamByName("aud.bps", q); - int audioSampleRate = sProfiles->getCamcorderProfileParamByName("aud.hz", q); - int audioChannels = sProfiles->getCamcorderProfileParamByName("aud.ch", q); + int duration = sProfiles->getCamcorderProfileParamByName("duration", id, q); + int fileFormat = sProfiles->getCamcorderProfileParamByName("file.format", id, q); + int videoCodec = sProfiles->getCamcorderProfileParamByName("vid.codec", id, q); + int videoBitRate = sProfiles->getCamcorderProfileParamByName("vid.bps", id, q); + int videoFrameRate = sProfiles->getCamcorderProfileParamByName("vid.fps", id, q); + int videoFrameWidth = sProfiles->getCamcorderProfileParamByName("vid.width", id, q); + int videoFrameHeight = sProfiles->getCamcorderProfileParamByName("vid.height", id, q); + int audioCodec = sProfiles->getCamcorderProfileParamByName("aud.codec", id, q); + int audioBitRate = sProfiles->getCamcorderProfileParamByName("aud.bps", id, q); + int audioSampleRate = sProfiles->getCamcorderProfileParamByName("aud.hz", id, q); + int audioChannels = sProfiles->getCamcorderProfileParamByName("aud.ch", id, q); // Check on the values retrieved if (duration == -1 || fileFormat == -1 || videoCodec == -1 || audioCodec == -1 || @@ -253,17 +253,17 @@ android_media_MediaProfiles_native_get_audio_decoder_type(JNIEnv *env, jobject t } static jint -android_media_MediaProfiles_native_get_num_image_encoding_quality_levels(JNIEnv *env, jobject thiz) +android_media_MediaProfiles_native_get_num_image_encoding_quality_levels(JNIEnv *env, jobject thiz, jint cameraId) { LOGV("native_get_num_image_encoding_quality_levels"); - return sProfiles->getImageEncodingQualityLevels().size(); + return sProfiles->getImageEncodingQualityLevels(cameraId).size(); } static jint -android_media_MediaProfiles_native_get_image_encoding_quality_level(JNIEnv *env, jobject thiz, jint index) +android_media_MediaProfiles_native_get_image_encoding_quality_level(JNIEnv *env, jobject thiz, jint cameraId, jint index) { LOGV("native_get_image_encoding_quality_level"); - Vector<int> levels = sProfiles->getImageEncodingQualityLevels(); + Vector<int> levels = sProfiles->getImageEncodingQualityLevels(cameraId); if (index < 0 || index >= levels.size()) { jniThrowException(env, "java/lang/IllegalArgumentException", "out of array boundary"); return -1; @@ -287,7 +287,7 @@ static JNINativeMethod gMethodsForEncoderCapabilitiesClass[] = { static JNINativeMethod gMethodsForCamcorderProfileClass[] = { {"native_init", "()V", (void *)android_media_MediaProfiles_native_init}, - {"native_get_camcorder_profile", "(I)Landroid/media/CamcorderProfile;", + {"native_get_camcorder_profile", "(II)Landroid/media/CamcorderProfile;", (void *)android_media_MediaProfiles_native_get_camcorder_profile}, }; @@ -302,8 +302,8 @@ static JNINativeMethod gMethodsForDecoderCapabilitiesClass[] = { static JNINativeMethod gMethodsForCameraProfileClass[] = { {"native_init", "()V", (void *)android_media_MediaProfiles_native_init}, {"native_get_num_image_encoding_quality_levels", - "()I", (void *)android_media_MediaProfiles_native_get_num_image_encoding_quality_levels}, - {"native_get_image_encoding_quality_level","(I)I", (void *)android_media_MediaProfiles_native_get_image_encoding_quality_level}, + "(I)I", (void *)android_media_MediaProfiles_native_get_num_image_encoding_quality_levels}, + {"native_get_image_encoding_quality_level","(II)I", (void *)android_media_MediaProfiles_native_get_image_encoding_quality_level}, }; static const char* const kEncoderCapabilitiesClassPathName = "android/media/EncoderCapabilities"; diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index 126337395d2a..3869389613ca 100644 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -272,7 +272,7 @@ MediaProfiles::createEncoderOutputFileFormat(const char **atts) } /*static*/ MediaProfiles::CamcorderProfile* -MediaProfiles::createCamcorderProfile(const char **atts) +MediaProfiles::createCamcorderProfile(int cameraId, const char **atts) { CHECK(!strcmp("quality", atts[0]) && !strcmp("fileFormat", atts[2]) && @@ -287,16 +287,47 @@ MediaProfiles::createCamcorderProfile(const char **atts) CHECK(fileFormat != -1); MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile; + profile->mCameraId = cameraId; profile->mFileFormat = static_cast<output_format>(fileFormat); profile->mQuality = static_cast<camcorder_quality>(quality); profile->mDuration = atoi(atts[5]); return profile; } -/*static*/ int -MediaProfiles::getImageEncodingQualityLevel(const char** atts) +MediaProfiles::ImageEncodingQualityLevels* +MediaProfiles::findImageEncodingQualityLevels(int cameraId) const +{ + int n = mImageEncodingQualityLevels.size(); + for (int i = 0; i < n; i++) { + ImageEncodingQualityLevels *levels = mImageEncodingQualityLevels[i]; + if (levels->mCameraId == cameraId) { + return levels; + } + } + return NULL; +} + +void MediaProfiles::addImageEncodingQualityLevel(int cameraId, const char** atts) { CHECK(!strcmp("quality", atts[0])); + int quality = atoi(atts[1]); + LOGV("%s: cameraId=%d, quality=%d\n", __func__, cameraId, quality); + ImageEncodingQualityLevels *levels = findImageEncodingQualityLevels(cameraId); + + if (levels == NULL) { + levels = new ImageEncodingQualityLevels(); + levels->mCameraId = cameraId; + mImageEncodingQualityLevels.add(levels); + } + + levels->mLevels.add(quality); +} + +/*static*/ int +MediaProfiles::getCameraId(const char** atts) +{ + if (!atts[0]) return 0; // default cameraId = 0 + CHECK(!strcmp("cameraId", atts[0])); return atoi(atts[1]); } @@ -322,10 +353,13 @@ MediaProfiles::startElementHandler(void *userData, const char *name, const char profiles->mAudioDecoders.add(createAudioDecoderCap(atts)); } else if (strcmp("EncoderOutputFileFormat", name) == 0) { profiles->mEncoderOutputFileFormats.add(createEncoderOutputFileFormat(atts)); + } else if (strcmp("CamcorderProfiles", name) == 0) { + profiles->mCurrentCameraId = getCameraId(atts); } else if (strcmp("EncoderProfile", name) == 0) { - profiles->mCamcorderProfiles.add(createCamcorderProfile(atts)); + profiles->mCamcorderProfiles.add( + createCamcorderProfile(profiles->mCurrentCameraId, atts)); } else if (strcmp("ImageEncoding", name) == 0) { - profiles->mImageEncodingQualityLevels.add(getImageEncodingQualityLevel(atts)); + profiles->addImageEncodingQualityLevel(profiles->mCurrentCameraId, atts); } } @@ -383,7 +417,8 @@ MediaProfiles::createDefaultCamcorderHighProfile() new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 360000, 352, 288, 20); AudioCodec *audioCodec = new AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1); - CamcorderProfile *profile = new CamcorderProfile; + CamcorderProfile *profile = new MediaProfiles::CamcorderProfile; + profile->mCameraId = 0; profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP; profile->mQuality = CAMCORDER_QUALITY_HIGH; profile->mDuration = 60; @@ -402,6 +437,7 @@ MediaProfiles::createDefaultCamcorderLowProfile() new MediaProfiles::AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1); MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile; + profile->mCameraId = 0; profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP; profile->mQuality = CAMCORDER_QUALITY_LOW; profile->mDuration = 30; @@ -458,9 +494,12 @@ MediaProfiles::createDefaultAmrNBEncoderCap() /*static*/ void MediaProfiles::createDefaultImageEncodingQualityLevels(MediaProfiles *profiles) { - profiles->mImageEncodingQualityLevels.add(70); - profiles->mImageEncodingQualityLevels.add(80); - profiles->mImageEncodingQualityLevels.add(90); + ImageEncodingQualityLevels *levels = new ImageEncodingQualityLevels(); + levels->mCameraId = 0; + levels->mLevels.add(70); + levels->mLevels.add(80); + levels->mLevels.add(90); + profiles->mImageEncodingQualityLevels.add(levels); } /*static*/ MediaProfiles* @@ -629,19 +668,24 @@ Vector<audio_decoder> MediaProfiles::getAudioDecoders() const return decoders; // copy out } -int MediaProfiles::getCamcorderProfileParamByName(const char *name, camcorder_quality quality) const +int MediaProfiles::getCamcorderProfileParamByName(const char *name, + int cameraId, + camcorder_quality quality) const { - LOGV("getCamcorderProfileParamByName: %s for quality %d", name, quality); + LOGV("getCamcorderProfileParamByName: %s for camera %d, quality %d", + name, cameraId, quality); int index = -1; for (size_t i = 0, n = mCamcorderProfiles.size(); i < n; ++i) { - if (mCamcorderProfiles[i]->mQuality == quality) { + if (mCamcorderProfiles[i]->mCameraId == cameraId && + mCamcorderProfiles[i]->mQuality == quality) { index = i; break; } } if (index == -1) { - LOGE("The given camcorder profile quality %d is not found", quality); + LOGE("The given camcorder profile camera %d quality %d is not found", + cameraId, quality); return -1; } @@ -657,13 +701,18 @@ int MediaProfiles::getCamcorderProfileParamByName(const char *name, camcorder_qu if (!strcmp("aud.ch", name)) return mCamcorderProfiles[index]->mAudioCodec->mChannels; if (!strcmp("aud.hz", name)) return mCamcorderProfiles[index]->mAudioCodec->mSampleRate; - LOGE("The given camcorder profile param name %s is not found", name); + LOGE("The given camcorder profile param id %d name %s is not found", cameraId, name); return -1; } -Vector<int> MediaProfiles::getImageEncodingQualityLevels() const +Vector<int> MediaProfiles::getImageEncodingQualityLevels(int cameraId) const { - return mImageEncodingQualityLevels; // copy out + Vector<int> result; + ImageEncodingQualityLevels *levels = findImageEncodingQualityLevels(cameraId); + if (levels != NULL) { + result = levels->mLevels; // copy out + } + return result; } MediaProfiles::~MediaProfiles() diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index c4aeec391cd7..50f74f2227b7 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -42,11 +42,16 @@ namespace android { -StagefrightRecorder::StagefrightRecorder() { +StagefrightRecorder::StagefrightRecorder() + : mWriter(NULL), + mOutputFd(-1) { + + LOGV("Constructor"); reset(); } StagefrightRecorder::~StagefrightRecorder() { + LOGV("Destructor"); stop(); if (mOutputFd >= 0) { @@ -56,40 +61,97 @@ StagefrightRecorder::~StagefrightRecorder() { } status_t StagefrightRecorder::init() { + LOGV("init"); return OK; } status_t StagefrightRecorder::setAudioSource(audio_source as) { - mAudioSource = as; + LOGV("setAudioSource: %d", as); + if (as < AUDIO_SOURCE_DEFAULT || + as >= AUDIO_SOURCE_LIST_END) { + LOGE("Invalid audio source: %d", as); + return BAD_VALUE; + } + + if (as == AUDIO_SOURCE_DEFAULT) { + mAudioSource = AUDIO_SOURCE_MIC; + } else { + mAudioSource = as; + } return OK; } status_t StagefrightRecorder::setVideoSource(video_source vs) { - mVideoSource = vs; + LOGV("setVideoSource: %d", vs); + if (vs < VIDEO_SOURCE_DEFAULT || + vs >= VIDEO_SOURCE_LIST_END) { + LOGE("Invalid video source: %d", vs); + return BAD_VALUE; + } + + if (vs == VIDEO_SOURCE_DEFAULT) { + mVideoSource = VIDEO_SOURCE_CAMERA; + } else { + mVideoSource = vs; + } return OK; } status_t StagefrightRecorder::setOutputFormat(output_format of) { - mOutputFormat = of; + LOGV("setOutputFormat: %d", of); + if (of < OUTPUT_FORMAT_DEFAULT || + of >= OUTPUT_FORMAT_LIST_END) { + LOGE("Invalid output format: %d", of); + return BAD_VALUE; + } + + if (of == OUTPUT_FORMAT_DEFAULT) { + mOutputFormat = OUTPUT_FORMAT_THREE_GPP; + } else { + mOutputFormat = of; + } return OK; } status_t StagefrightRecorder::setAudioEncoder(audio_encoder ae) { - mAudioEncoder = ae; + LOGV("setAudioEncoder: %d", ae); + if (ae < AUDIO_ENCODER_DEFAULT || + ae >= AUDIO_ENCODER_LIST_END) { + LOGE("Invalid audio encoder: %d", ae); + return BAD_VALUE; + } + + if (ae == AUDIO_ENCODER_DEFAULT) { + mAudioEncoder = AUDIO_ENCODER_AMR_NB; + } else { + mAudioEncoder = ae; + } return OK; } status_t StagefrightRecorder::setVideoEncoder(video_encoder ve) { - mVideoEncoder = ve; + LOGV("setVideoEncoder: %d", ve); + if (ve < VIDEO_ENCODER_DEFAULT || + ve >= VIDEO_ENCODER_LIST_END) { + LOGE("Invalid video encoder: %d", ve); + return BAD_VALUE; + } + + if (ve == VIDEO_ENCODER_DEFAULT) { + mVideoEncoder = VIDEO_ENCODER_H263; + } else { + mVideoEncoder = ve; + } return OK; } status_t StagefrightRecorder::setVideoSize(int width, int height) { + LOGV("setVideoSize: %dx%d", width, height); if (width <= 0 || height <= 0) { LOGE("Invalid video size: %dx%d", width, height); return BAD_VALUE; @@ -103,6 +165,7 @@ status_t StagefrightRecorder::setVideoSize(int width, int height) { } status_t StagefrightRecorder::setVideoFrameRate(int frames_per_second) { + LOGV("setVideoFrameRate: %d", frames_per_second); if (frames_per_second <= 0 || frames_per_second > 30) { LOGE("Invalid video frame rate: %d", frames_per_second); return BAD_VALUE; @@ -118,7 +181,7 @@ status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera) { LOGV("setCamera"); if (camera == 0) { LOGE("camera is NULL"); - return UNKNOWN_ERROR; + return BAD_VALUE; } int64_t token = IPCThreadState::self()->clearCallingIdentity(); @@ -127,7 +190,7 @@ status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera) { if (mCamera == 0) { LOGE("Unable to connect to camera"); IPCThreadState::self()->restoreCallingIdentity(token); - return UNKNOWN_ERROR; + return -EBUSY; } LOGV("Connected to camera"); @@ -141,23 +204,31 @@ status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera) { } status_t StagefrightRecorder::setPreviewSurface(const sp<ISurface> &surface) { + LOGV("setPreviewSurface: %p", surface.get()); mPreviewSurface = surface; return OK; } status_t StagefrightRecorder::setOutputFile(const char *path) { + LOGE("setOutputFile(const char*) must not be called"); // We don't actually support this at all, as the media_server process // no longer has permissions to create files. - return UNKNOWN_ERROR; + return -EPERM; } status_t StagefrightRecorder::setOutputFile(int fd, int64_t offset, int64_t length) { + LOGV("setOutputFile: %d, %lld, %lld", fd, offset, length); // These don't make any sense, do they? CHECK_EQ(offset, 0); CHECK_EQ(length, 0); + if (fd < 0) { + LOGE("Invalid file descriptor: %d", fd); + return -EBADF; + } + if (mOutputFd >= 0) { ::close(mOutputFd); } @@ -233,6 +304,7 @@ status_t StagefrightRecorder::setParamAudioNumberOfChannels(int32_t channels) { LOGV("setParamAudioNumberOfChannels: %d", channels); if (channels <= 0 || channels >= 3) { LOGE("Invalid number of audio channels: %d", channels); + return BAD_VALUE; } // Additional check on the number of channels will be performed later. @@ -270,21 +342,23 @@ status_t StagefrightRecorder::setParamVideoEncodingBitRate(int32_t bitRate) { return OK; } -status_t StagefrightRecorder::setParamMaxDurationOrFileSize(int64_t limit, - bool limit_is_duration) { - LOGV("setParamMaxDurationOrFileSize: limit (%lld) for %s", - limit, limit_is_duration?"duration":"size"); - if (limit_is_duration) { // limit is in ms - if (limit <= 1000) { // XXX: 1 second - LOGE("Max file duration is too short: %lld us", limit); - } - mMaxFileDurationUs = limit * 1000LL; - } else { - if (limit <= 1024) { // XXX: 1 kB - LOGE("Max file size is too small: %lld bytes", limit); - } - mMaxFileSizeBytes = limit; +status_t StagefrightRecorder::setParamMaxFileDurationUs(int64_t timeUs) { + LOGV("setParamMaxFileDurationUs: %lld us", timeUs); + if (timeUs <= 1000000LL) { // XXX: 1 second + LOGE("Max file duration is too short: %lld us", timeUs); + return BAD_VALUE; + } + mMaxFileDurationUs = timeUs; + return OK; +} + +status_t StagefrightRecorder::setParamMaxFileSizeBytes(int64_t bytes) { + LOGV("setParamMaxFileSizeBytes: %lld bytes", bytes); + if (bytes <= 1024) { // XXX: 1 kB + LOGE("Max file size is too small: %lld bytes", bytes); + return BAD_VALUE; } + mMaxFileSizeBytes = bytes; return OK; } @@ -309,7 +383,7 @@ status_t StagefrightRecorder::setParamInterleaveDuration(int32_t durationUs) { // If interval < 0, only the first frame is I frame, and rest are all P frames // If interval == 0, all frames are encoded as I frames. No P frames -// If interval > 0, it is the time spacing between 2 neighboring I frames +// If interval > 0, it is the time spacing (seconds) between 2 neighboring I frames status_t StagefrightRecorder::setParamVideoIFramesInterval(int32_t interval) { LOGV("setParamVideoIFramesInterval: %d seconds", interval); mIFramesInterval = interval; @@ -335,6 +409,7 @@ status_t StagefrightRecorder::setParamVideoCameraId(int32_t cameraId) { status_t StagefrightRecorder::setParamTrackFrameStatus(int32_t nFrames) { LOGV("setParamTrackFrameStatus: %d", nFrames); if (nFrames <= 0) { + LOGE("Invalid number of frames to track: %d", nFrames); return BAD_VALUE; } mTrackEveryNumberOfFrames = nFrames; @@ -344,26 +419,43 @@ status_t StagefrightRecorder::setParamTrackFrameStatus(int32_t nFrames) { status_t StagefrightRecorder::setParamTrackTimeStatus(int64_t timeDurationUs) { LOGV("setParamTrackTimeStatus: %lld", timeDurationUs); if (timeDurationUs < 20000) { // Infeasible if shorter than 20 ms? + LOGE("Tracking time duration too short: %lld us", timeDurationUs); return BAD_VALUE; } mTrackEveryTimeDurationUs = 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()); if (key == "max-duration") { int64_t max_duration_ms; if (safe_strtoi64(value.string(), &max_duration_ms)) { - return setParamMaxDurationOrFileSize( - max_duration_ms, true /* limit_is_duration */); + return setParamMaxFileDurationUs(1000LL * max_duration_ms); } } else if (key == "max-filesize") { int64_t max_filesize_bytes; if (safe_strtoi64(value.string(), &max_filesize_bytes)) { - return setParamMaxDurationOrFileSize( - max_filesize_bytes, false /* limit is filesize */); + return setParamMaxFileSizeBytes(max_filesize_bytes); } } else if (key == "interleave-duration-us") { int32_t durationUs; @@ -410,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)) { @@ -467,7 +569,10 @@ status_t StagefrightRecorder::prepare() { } status_t StagefrightRecorder::start() { + CHECK(mOutputFd >= 0); + if (mWriter != NULL) { + LOGE("File writer is not avaialble"); return UNKNOWN_ERROR; } @@ -486,6 +591,7 @@ status_t StagefrightRecorder::start() { return startAACRecording(); default: + LOGE("Unsupported output file format: %d", mOutputFormat); return UNKNOWN_ERROR; } } @@ -549,7 +655,6 @@ status_t StagefrightRecorder::startAACRecording() { CHECK(mAudioEncoder == AUDIO_ENCODER_AAC); CHECK(mAudioSource != AUDIO_SOURCE_LIST_END); - CHECK(mOutputFd >= 0); CHECK(0 == "AACWriter is not implemented yet"); @@ -565,34 +670,34 @@ status_t StagefrightRecorder::startAMRRecording() { mAudioEncoder != AUDIO_ENCODER_AMR_NB) { LOGE("Invalid encoder %d used for AMRNB recording", mAudioEncoder); - return UNKNOWN_ERROR; + return BAD_VALUE; } if (mSampleRate != 8000) { LOGE("Invalid sampling rate %d used for AMRNB recording", mSampleRate); - return UNKNOWN_ERROR; + return BAD_VALUE; } } else { // mOutputFormat must be OUTPUT_FORMAT_AMR_WB if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) { LOGE("Invlaid encoder %d used for AMRWB recording", mAudioEncoder); - return UNKNOWN_ERROR; + return BAD_VALUE; } if (mSampleRate != 16000) { LOGE("Invalid sample rate %d used for AMRWB recording", mSampleRate); - return UNKNOWN_ERROR; + return BAD_VALUE; } } if (mAudioChannels != 1) { LOGE("Invalid number of audio channels %d used for amr recording", mAudioChannels); - return UNKNOWN_ERROR; + return BAD_VALUE; } if (mAudioSource >= AUDIO_SOURCE_LIST_END) { LOGE("Invalid audio source: %d", mAudioSource); - return UNKNOWN_ERROR; + return BAD_VALUE; } sp<MediaSource> audioEncoder = createAudioSource(); @@ -601,7 +706,6 @@ status_t StagefrightRecorder::startAMRRecording() { return UNKNOWN_ERROR; } - CHECK(mOutputFd >= 0); mWriter = new AMRWriter(dup(mOutputFd)); mWriter->addSource(audioEncoder); @@ -668,6 +772,54 @@ void StagefrightRecorder::clipVideoFrameWidth() { } } +status_t StagefrightRecorder::setupCameraSource() { + clipVideoBitRate(); + clipVideoFrameRate(); + clipVideoFrameWidth(); + clipVideoFrameHeight(); + + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + if (mCamera == 0) { + mCamera = Camera::connect(mCameraId); + if (mCamera == 0) { + LOGE("Camera connection could not be established."); + return -EBUSY; + } + mFlags &= ~FLAGS_HOT_CAMERA; + mCamera->lock(); + } + + // Set the actual video recording frame size + CameraParameters params(mCamera->getParameters()); + params.setPreviewSize(mVideoWidth, mVideoHeight); + params.setPreviewFrameRate(mFrameRate); + String8 s = params.flatten(); + CHECK_EQ(OK, mCamera->setParameters(s)); + CameraParameters newCameraParams(mCamera->getParameters()); + + // Check on video frame size + int frameWidth = 0, frameHeight = 0; + newCameraParams.getPreviewSize(&frameWidth, &frameHeight); + if (frameWidth < 0 || frameWidth != mVideoWidth || + frameHeight < 0 || frameHeight != mVideoHeight) { + LOGE("Failed to set the video frame size to %dx%d", + mVideoWidth, mVideoHeight); + IPCThreadState::self()->restoreCallingIdentity(token); + return UNKNOWN_ERROR; + } + + // Check on video frame rate + int frameRate = newCameraParams.getPreviewFrameRate(); + if (frameRate < 0 || (frameRate - mFrameRate) != 0) { + LOGE("Failed to set frame rate to %d fps. The actual " + "frame rate is %d", mFrameRate, frameRate); + } + + CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface)); + IPCThreadState::self()->restoreCallingIdentity(token); + return OK; +} + void StagefrightRecorder::clipVideoFrameHeight() { LOGV("clipVideoFrameHeight: encoder %d", mVideoEncoder); int minFrameHeight = mEncoderProfiles->getVideoEncoderParamByName( @@ -685,140 +837,116 @@ void StagefrightRecorder::clipVideoFrameHeight() { } } -status_t StagefrightRecorder::startMPEG4Recording() { - mWriter = new MPEG4Writer(dup(mOutputFd)); - int32_t totalBitRate = 0; - - // Add audio source first if it exists - if (mAudioSource != AUDIO_SOURCE_LIST_END) { - sp<MediaSource> audioEncoder; - switch(mAudioEncoder) { - case AUDIO_ENCODER_AMR_NB: - case AUDIO_ENCODER_AMR_WB: - case AUDIO_ENCODER_AAC: - audioEncoder = createAudioSource(); - break; - default: - LOGE("Unsupported audio encoder: %d", mAudioEncoder); - return UNKNOWN_ERROR; - } - - if (audioEncoder == NULL) { - return UNKNOWN_ERROR; - } - totalBitRate += mAudioBitRate; - mWriter->addSource(audioEncoder); - } - if (mVideoSource == VIDEO_SOURCE_DEFAULT - || mVideoSource == VIDEO_SOURCE_CAMERA) { - - clipVideoBitRate(); - clipVideoFrameRate(); - clipVideoFrameWidth(); - clipVideoFrameHeight(); - - int64_t token = IPCThreadState::self()->clearCallingIdentity(); - if (mCamera == 0) { - mCamera = Camera::connect(mCameraId); - mCamera->lock(); - } - - // Set the actual video recording frame size - CameraParameters params(mCamera->getParameters()); - params.setPreviewSize(mVideoWidth, mVideoHeight); - params.setPreviewFrameRate(mFrameRate); - String8 s = params.flatten(); - CHECK_EQ(OK, mCamera->setParameters(s)); - CameraParameters newCameraParams(mCamera->getParameters()); - - // Check on video frame size - int frameWidth = 0, frameHeight = 0; - newCameraParams.getPreviewSize(&frameWidth, &frameHeight); - if (frameWidth < 0 || frameWidth != mVideoWidth || - frameHeight < 0 || frameHeight != mVideoHeight) { - LOGE("Failed to set the video frame size to %dx%d", - mVideoWidth, mVideoHeight); - IPCThreadState::self()->restoreCallingIdentity(token); - return UNKNOWN_ERROR; - } - - // Check on video frame rate - int frameRate = newCameraParams.getPreviewFrameRate(); - if (frameRate < 0 || (frameRate - mFrameRate) != 0) { - LOGE("Failed to set frame rate to %d fps. The actual " - "frame rate is %d", mFrameRate, frameRate); - } +status_t StagefrightRecorder::setupVideoEncoder(const sp<MediaWriter>& writer) { + status_t err = setupCameraSource(); + if (err != OK) return err; - CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface)); - IPCThreadState::self()->restoreCallingIdentity(token); + sp<CameraSource> cameraSource = CameraSource::CreateFromCamera(mCamera); + CHECK(cameraSource != NULL); - sp<CameraSource> cameraSource = - CameraSource::CreateFromCamera(mCamera); + sp<MetaData> enc_meta = new MetaData; + enc_meta->setInt32(kKeyBitRate, mVideoBitRate); + enc_meta->setInt32(kKeySampleRate, mFrameRate); - CHECK(cameraSource != NULL); + switch (mVideoEncoder) { + case VIDEO_ENCODER_H263: + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263); + break; - sp<MetaData> enc_meta = new MetaData; - enc_meta->setInt32(kKeyBitRate, mVideoBitRate); - enc_meta->setInt32(kKeySampleRate, mFrameRate); + case VIDEO_ENCODER_MPEG_4_SP: + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); + break; - switch (mVideoEncoder) { - case VIDEO_ENCODER_H263: - enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263); - break; + case VIDEO_ENCODER_H264: + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); + break; - case VIDEO_ENCODER_MPEG_4_SP: - enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); - break; + default: + CHECK(!"Should not be here, unsupported video encoding."); + break; + } - case VIDEO_ENCODER_H264: - enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); - break; + sp<MetaData> meta = cameraSource->getFormat(); + + int32_t width, height, stride, sliceHeight; + CHECK(meta->findInt32(kKeyWidth, &width)); + CHECK(meta->findInt32(kKeyHeight, &height)); + CHECK(meta->findInt32(kKeyStride, &stride)); + CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); + + enc_meta->setInt32(kKeyWidth, width); + enc_meta->setInt32(kKeyHeight, height); + 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); + } - default: - CHECK(!"Should not be here, unsupported video encoding."); - break; - } + OMXClient client; + CHECK_EQ(client.connect(), OK); - sp<MetaData> meta = cameraSource->getFormat(); + sp<MediaSource> encoder = OMXCodec::Create( + client.interface(), enc_meta, + true /* createEncoder */, cameraSource); + if (encoder == NULL) { + return UNKNOWN_ERROR; + } - int32_t width, height, stride, sliceHeight; - CHECK(meta->findInt32(kKeyWidth, &width)); - CHECK(meta->findInt32(kKeyHeight, &height)); - CHECK(meta->findInt32(kKeyStride, &stride)); - CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); + writer->addSource(encoder); + return OK; +} - enc_meta->setInt32(kKeyWidth, width); - enc_meta->setInt32(kKeyHeight, height); - enc_meta->setInt32(kKeyIFramesInterval, mIFramesInterval); - enc_meta->setInt32(kKeyStride, stride); - enc_meta->setInt32(kKeySliceHeight, sliceHeight); +status_t StagefrightRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) { + sp<MediaSource> audioEncoder; + switch(mAudioEncoder) { + case AUDIO_ENCODER_AMR_NB: + case AUDIO_ENCODER_AMR_WB: + case AUDIO_ENCODER_AAC: + audioEncoder = createAudioSource(); + break; + default: + LOGE("Unsupported audio encoder: %d", mAudioEncoder); + return UNKNOWN_ERROR; + } - OMXClient client; - CHECK_EQ(client.connect(), OK); + if (audioEncoder == NULL) { + return UNKNOWN_ERROR; + } + writer->addSource(audioEncoder); + return OK; +} - sp<MediaSource> encoder = - OMXCodec::Create( - client.interface(), enc_meta, - true /* createEncoder */, cameraSource); +status_t StagefrightRecorder::startMPEG4Recording() { + int32_t totalBitRate = 0; + status_t err = OK; + sp<MediaWriter> writer = new MPEG4Writer(dup(mOutputFd)); - CHECK(mOutputFd >= 0); + // Add audio source first if it exists + if (mAudioSource != AUDIO_SOURCE_LIST_END) { + err = setupAudioEncoder(writer); + if (err != OK) return err; + totalBitRate += mAudioBitRate; + } + if (mVideoSource == VIDEO_SOURCE_DEFAULT + || mVideoSource == VIDEO_SOURCE_CAMERA) { + err = setupVideoEncoder(writer); + if (err != OK) return err; totalBitRate += mVideoBitRate; - mWriter->addSource(encoder); } - { - // MPEGWriter specific handling - MPEG4Writer *writer = ((MPEG4Writer *) mWriter.get()); - writer->setInterleaveDuration(mInterleaveDurationUs); - } + reinterpret_cast<MPEG4Writer *>(writer.get())-> + setInterleaveDuration(mInterleaveDurationUs); if (mMaxFileDurationUs != 0) { - mWriter->setMaxFileDuration(mMaxFileDurationUs); + writer->setMaxFileDuration(mMaxFileDurationUs); } if (mMaxFileSizeBytes != 0) { - mWriter->setMaxFileSize(mMaxFileSizeBytes); + writer->setMaxFileSize(mMaxFileSizeBytes); } - mWriter->setListener(mListener); sp<MetaData> meta = new MetaData; meta->setInt64(kKeyTime, systemTime() / 1000); meta->setInt32(kKeyFileType, mOutputFormat); @@ -830,11 +958,13 @@ status_t StagefrightRecorder::startMPEG4Recording() { if (mTrackEveryTimeDurationUs > 0) { meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs); } - mWriter->start(meta.get()); - return OK; + writer->setListener(mListener); + mWriter = writer; + return mWriter->start(meta.get()); } status_t StagefrightRecorder::pause() { + LOGV("pause"); if (mWriter == NULL) { return UNKNOWN_ERROR; } @@ -843,34 +973,37 @@ status_t StagefrightRecorder::pause() { } status_t StagefrightRecorder::stop() { - if (mWriter == NULL) { - return UNKNOWN_ERROR; + LOGV("stop"); + if (mWriter != NULL) { + mWriter->stop(); + mWriter.clear(); } - mWriter->stop(); - mWriter = NULL; - - return OK; -} - -status_t StagefrightRecorder::close() { - stop(); - if (mCamera != 0) { + LOGV("Disconnect camera"); int64_t token = IPCThreadState::self()->clearCallingIdentity(); if ((mFlags & FLAGS_HOT_CAMERA) == 0) { LOGV("Camera was cold when we started, stopping preview"); mCamera->stopPreview(); } mCamera->unlock(); - mCamera = NULL; + mCamera.clear(); IPCThreadState::self()->restoreCallingIdentity(token); mFlags = 0; } + + return OK; +} + +status_t StagefrightRecorder::close() { + LOGV("close"); + stop(); + return OK; } status_t StagefrightRecorder::reset() { + LOGV("reset"); stop(); // No audio or video source by default @@ -893,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(); @@ -904,6 +1041,13 @@ status_t StagefrightRecorder::reset() { } status_t StagefrightRecorder::getMaxAmplitude(int *max) { + LOGV("getMaxAmplitude"); + + if (max == NULL) { + LOGE("Null pointer argument"); + return BAD_VALUE; + } + if (mAudioSourceNode != 0) { *max = mAudioSourceNode->getMaxAmplitude(); } else { diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index f4488b641c3f..85d2557d430e 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -82,6 +82,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; @@ -97,18 +99,26 @@ private: status_t startAMRRecording(); status_t startAACRecording(); sp<MediaSource> createAudioSource(); + status_t setupCameraSource(); + status_t setupAudioEncoder(const sp<MediaWriter>& writer); + status_t setupVideoEncoder(const sp<MediaWriter>& writer); + + // Encoding parameter handling utilities status_t setParameter(const String8 &key, const String8 &value); status_t setParamAudioEncodingBitRate(int32_t bitRate); status_t setParamAudioNumberOfChannels(int32_t channles); 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); status_t setParamInterleaveDuration(int32_t durationUs); status_t setParam64BitFileOffset(bool use64BitFileOffset); - status_t setParamMaxDurationOrFileSize(int64_t limit, bool limit_is_duration); + status_t setParamMaxFileDurationUs(int64_t timeUs); + status_t setParamMaxFileSizeBytes(int64_t bytes); void clipVideoBitRate(); void clipVideoFrameRate(); void clipVideoFrameWidth(); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 4a1580f5f977..ffed74f1b064 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/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index a52c88850320..6a4a13118bc2 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -34,6 +34,8 @@ #include <media/mediarecorder.h> #include <cutils/properties.h> +#include "include/ESDS.h" + namespace android { class MPEG4Writer::Track { @@ -126,6 +128,8 @@ private: int32_t *min, int32_t *avg, int32_t *max); void findMinMaxChunkDurations(int64_t *min, int64_t *max); + void getCodecSpecificDataFromInputFormatIfPossible(); + Track(const Track &); Track &operator=(const Track &); }; @@ -678,6 +682,38 @@ MPEG4Writer::Track::Track( mCodecSpecificDataSize(0), mGotAllCodecSpecificData(false), mReachedEOS(false) { + getCodecSpecificDataFromInputFormatIfPossible(); +} + +void MPEG4Writer::Track::getCodecSpecificDataFromInputFormatIfPossible() { + const char *mime; + CHECK(mMeta->findCString(kKeyMIMEType, &mime)); + + if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + uint32_t type; + const void *data; + size_t size; + if (mMeta->findData(kKeyAVCC, &type, &data, &size)) { + mCodecSpecificData = malloc(size); + mCodecSpecificDataSize = size; + memcpy(mCodecSpecificData, data, size); + mGotAllCodecSpecificData = true; + } + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) + || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { + uint32_t type; + const void *data; + size_t size; + if (mMeta->findData(kKeyESDS, &type, &data, &size)) { + ESDS esds(data, size); + if (esds.getCodecSpecificInfo(&data, &size) == OK) { + mCodecSpecificData = malloc(size); + mCodecSpecificDataSize = size; + memcpy(mCodecSpecificData, data, size); + mGotAllCodecSpecificData = true; + } + } + } } MPEG4Writer::Track::~Track() { @@ -721,7 +757,10 @@ status_t MPEG4Writer::Track::start(MetaData *params) { } int64_t startTimeUs; - CHECK(params && params->findInt64(kKeyTime, &startTimeUs)); + if (params == NULL || !params->findInt64(kKeyTime, &startTimeUs)) { + startTimeUs = 0; + } + initTrackingProgressStatus(params); sp<MetaData> meta = new MetaData; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 02a073ea2c99..83f7040200dc 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -149,13 +149,16 @@ static const CodecInfo kDecoderInfo[] = { { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" }, { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" }, // { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" }, + { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" }, // { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4dec" }, + { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.decoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" }, // { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263dec" }, + { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.decoder.avc" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" }, { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" }, @@ -171,16 +174,19 @@ static const CodecInfo kEncoderInfo[] = { { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBEncoder" }, { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" }, { MEDIA_MIMETYPE_AUDIO_AAC, "AACEncoder" }, - { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacenc" }, +// { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacenc" }, + { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.encoder" }, - { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4enc" }, +// { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4enc" }, + { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.encoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.encoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.encoder" }, - { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263enc" }, +// { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263enc" }, + { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.encoder.avc" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.encoder.avc" }, { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" }, - { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcenc" }, +// { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcenc" }, }; #undef OPTIONAL @@ -334,10 +340,17 @@ uint32_t OMXCodec::getComponentQuirks(const char *componentName) { quirks |= kRequiresAllocateBufferOnInputPorts; quirks |= kRequiresAllocateBufferOnOutputPorts; } + if (!strncmp(componentName, "OMX.qcom.7x30.video.encoder.", 28)) { + } if (!strncmp(componentName, "OMX.qcom.video.decoder.", 23)) { quirks |= kRequiresAllocateBufferOnOutputPorts; quirks |= kDefersOutputBufferAllocation; } + if (!strncmp(componentName, "OMX.qcom.7x30.video.decoder.", 28)) { + quirks |= kRequiresAllocateBufferOnInputPorts; + quirks |= kRequiresAllocateBufferOnOutputPorts; + quirks |= kDefersOutputBufferAllocation; + } if (!strncmp(componentName, "OMX.TI.", 7)) { // Apparently I must not use OMX_UseBuffer on either input or @@ -818,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; @@ -836,6 +849,7 @@ void OMXCodec::setVideoInputFormat( } case OMX_VIDEO_CodingH263: + CHECK_EQ(setupH263EncoderParameters(meta), OK); break; case OMX_VIDEO_CodingAVC: @@ -861,6 +875,142 @@ static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) { return ret; } +status_t OMXCodec::setupErrorCorrectionParameters() { + OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType; + InitOMXParams(&errorCorrectionType); + errorCorrectionType.nPortIndex = kPortIndexOutput; + + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamVideoErrorCorrection, + &errorCorrectionType, sizeof(errorCorrectionType)); + CHECK_EQ(err, OK); + + errorCorrectionType.bEnableHEC = OMX_FALSE; + errorCorrectionType.bEnableResync = OMX_TRUE; + errorCorrectionType.nResynchMarkerSpacing = 256; + errorCorrectionType.bEnableDataPartitioning = OMX_FALSE; + errorCorrectionType.bEnableRVLC = OMX_FALSE; + + err = mOMX->setParameter( + mNode, OMX_IndexParamVideoErrorCorrection, + &errorCorrectionType, sizeof(errorCorrectionType)); + CHECK_EQ(err, OK); + return OK; +} + +status_t OMXCodec::setupBitRate(int32_t bitRate) { + OMX_VIDEO_PARAM_BITRATETYPE bitrateType; + InitOMXParams(&bitrateType); + bitrateType.nPortIndex = kPortIndexOutput; + + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamVideoBitrate, + &bitrateType, sizeof(bitrateType)); + CHECK_EQ(err, OK); + + bitrateType.eControlRate = OMX_Video_ControlRateVariable; + bitrateType.nTargetBitrate = bitRate; + + err = mOMX->setParameter( + mNode, OMX_IndexParamVideoBitrate, + &bitrateType, sizeof(bitrateType)); + CHECK_EQ(err, OK); + 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: %ld, level %ld", + 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); + success = success && meta->findInt32(kKeySampleRate, &frameRate); + success = success && meta->findInt32(kKeyIFramesInterval, &iFramesInterval); + CHECK(success); + OMX_VIDEO_PARAM_H263TYPE h263type; + InitOMXParams(&h263type); + h263type.nPortIndex = kPortIndexOutput; + + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type)); + CHECK_EQ(err, OK); + + h263type.nAllowedPictureTypes = + OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; + + h263type.nPFrames = setPFramesSpacing(iFramesInterval, frameRate); + if (h263type.nPFrames == 0) { + h263type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; + } + h263type.nBFrames = 0; + + // 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; + h263type.nPictureHeaderRepetition = 0; + h263type.nGOBHeaderInterval = 0; + + err = mOMX->setParameter( + mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type)); + CHECK_EQ(err, OK); + + CHECK_EQ(setupBitRate(bitRate), OK); + CHECK_EQ(setupErrorCorrectionParameters(), OK); + + return OK; +} + status_t OMXCodec::setupMPEG4EncoderParameters(const sp<MetaData>& meta) { int32_t iFramesInterval, frameRate, bitRate; bool success = meta->findInt32(kKeyBitRate, &bitRate); @@ -894,53 +1044,21 @@ status_t OMXCodec::setupMPEG4EncoderParameters(const sp<MetaData>& meta) { mpeg4type.nHeaderExtension = 0; mpeg4type.bReversibleVLC = OMX_FALSE; - mpeg4type.eProfile = OMX_VIDEO_MPEG4ProfileCore; - 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)); CHECK_EQ(err, OK); - // ---------------- - - OMX_VIDEO_PARAM_BITRATETYPE bitrateType; - InitOMXParams(&bitrateType); - bitrateType.nPortIndex = kPortIndexOutput; - - err = mOMX->getParameter( - mNode, OMX_IndexParamVideoBitrate, - &bitrateType, sizeof(bitrateType)); - CHECK_EQ(err, OK); - - bitrateType.eControlRate = OMX_Video_ControlRateVariable; - bitrateType.nTargetBitrate = bitRate; - - err = mOMX->setParameter( - mNode, OMX_IndexParamVideoBitrate, - &bitrateType, sizeof(bitrateType)); - CHECK_EQ(err, OK); - - // ---------------- - - OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType; - InitOMXParams(&errorCorrectionType); - errorCorrectionType.nPortIndex = kPortIndexOutput; - - err = mOMX->getParameter( - mNode, OMX_IndexParamVideoErrorCorrection, - &errorCorrectionType, sizeof(errorCorrectionType)); - CHECK_EQ(err, OK); - - errorCorrectionType.bEnableHEC = OMX_FALSE; - errorCorrectionType.bEnableResync = OMX_TRUE; - errorCorrectionType.nResynchMarkerSpacing = 256; - errorCorrectionType.bEnableDataPartitioning = OMX_FALSE; - errorCorrectionType.bEnableRVLC = OMX_FALSE; - - err = mOMX->setParameter( - mNode, OMX_IndexParamVideoErrorCorrection, - &errorCorrectionType, sizeof(errorCorrectionType)); - CHECK_EQ(err, OK); + CHECK_EQ(setupBitRate(bitRate), OK); + CHECK_EQ(setupErrorCorrectionParameters(), OK); return OK; } @@ -969,44 +1087,46 @@ 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( mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type)); CHECK_EQ(err, OK); - OMX_VIDEO_PARAM_BITRATETYPE bitrateType; - InitOMXParams(&bitrateType); - bitrateType.nPortIndex = kPortIndexOutput; - - err = mOMX->getParameter( - mNode, OMX_IndexParamVideoBitrate, - &bitrateType, sizeof(bitrateType)); - CHECK_EQ(err, OK); - - bitrateType.eControlRate = OMX_Video_ControlRateVariable; - bitrateType.nTargetBitrate = bitRate; - - err = mOMX->setParameter( - mNode, OMX_IndexParamVideoBitrate, - &bitrateType, sizeof(bitrateType)); - CHECK_EQ(err, OK); + CHECK_EQ(setupBitRate(bitRate), OK); return OK; } diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp index b9140234c058..2317de6c5bbf 100644 --- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp +++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp @@ -132,7 +132,10 @@ AACEncoder::~AACEncoder() { } status_t AACEncoder::start(MetaData *params) { - CHECK(!mStarted); + if (mStarted) { + LOGW("Call start() when encoder already started"); + return OK; + } mBufferGroup = new MediaBufferGroup; mBufferGroup->add_buffer(new MediaBuffer(2048)); @@ -150,7 +153,10 @@ status_t AACEncoder::start(MetaData *params) { } status_t AACEncoder::stop() { - CHECK(mStarted); + if (!mStarted) { + LOGW("Call stop() when encoder has not started"); + return OK; + } if (mInputBuffer) { mInputBuffer->release(); diff --git a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp index 445438f0cb32..4c02fe98f49a 100644 --- a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp +++ b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp @@ -70,7 +70,10 @@ static Mode PickModeFromBitrate(int32_t bps) { } status_t AMRNBEncoder::start(MetaData *params) { - CHECK(!mStarted); + if (mStarted) { + LOGW("Call start() when encoder already started"); + return OK; + } mBufferGroup = new MediaBufferGroup; mBufferGroup->add_buffer(new MediaBuffer(32)); @@ -97,7 +100,10 @@ status_t AMRNBEncoder::start(MetaData *params) { } status_t AMRNBEncoder::stop() { - CHECK(mStarted); + if (!mStarted) { + LOGW("Call stop() when encoder has not started."); + return OK; + } if (mInputBuffer) { mInputBuffer->release(); diff --git a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp index b70cff17bb59..4257c6acb764 100644 --- a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp +++ b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp @@ -124,7 +124,10 @@ AMRWBEncoder::~AMRWBEncoder() { } status_t AMRWBEncoder::start(MetaData *params) { - CHECK(!mStarted); + if (mStarted) { + LOGW("Call start() when encoder already started"); + return OK; + } mBufferGroup = new MediaBufferGroup; @@ -142,8 +145,10 @@ status_t AMRWBEncoder::start(MetaData *params) { } status_t AMRWBEncoder::stop() { - CHECK(mStarted); - + if (!mStarted) { + LOGW("Call stop() when encoder has not started"); + return OK; + } if (mInputBuffer) { mInputBuffer->release(); diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp index e74782f9cb1f..5b16997ad4d5 100644 --- a/media/libstagefright/colorconversion/ColorConverter.cpp +++ b/media/libstagefright/colorconversion/ColorConverter.cpp @@ -42,6 +42,7 @@ bool ColorConverter::isValid() const { case OMX_COLOR_FormatYUV420Planar: case OMX_COLOR_FormatCbYCrY: case OMX_QCOM_COLOR_FormatYVU420SemiPlanar: + case OMX_COLOR_FormatYUV420SemiPlanar: return true; default: @@ -71,6 +72,11 @@ void ColorConverter::convert( width, height, srcBits, srcSkip, dstBits, dstSkip); break; + case OMX_COLOR_FormatYUV420SemiPlanar: + convertYUV420SemiPlanar( + width, height, srcBits, srcSkip, dstBits, dstSkip); + break; + default: { CHECK(!"Should not be here. Unknown color conversion."); @@ -279,6 +285,68 @@ void ColorConverter::convertQCOMYUV420SemiPlanar( } } +void ColorConverter::convertYUV420SemiPlanar( + size_t width, size_t height, + const void *srcBits, size_t srcSkip, + void *dstBits, size_t dstSkip) { + CHECK_EQ(srcSkip, 0); // Doesn't really make sense for YUV formats. + CHECK(dstSkip >= width * 2); + CHECK((dstSkip & 3) == 0); + + uint8_t *kAdjustedClip = initClip(); + + uint32_t *dst_ptr = (uint32_t *)dstBits; + const uint8_t *src_y = (const uint8_t *)srcBits; + + const uint8_t *src_u = + (const uint8_t *)src_y + width * height; + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; x += 2) { + signed y1 = (signed)src_y[x] - 16; + signed y2 = (signed)src_y[x + 1] - 16; + + signed v = (signed)src_u[x & ~1] - 128; + signed u = (signed)src_u[(x & ~1) + 1] - 128; + + signed u_b = u * 517; + signed u_g = -u * 100; + signed v_g = -v * 208; + signed v_r = v * 409; + + signed tmp1 = y1 * 298; + signed b1 = (tmp1 + u_b) / 256; + signed g1 = (tmp1 + v_g + u_g) / 256; + signed r1 = (tmp1 + v_r) / 256; + + signed tmp2 = y2 * 298; + signed b2 = (tmp2 + u_b) / 256; + signed g2 = (tmp2 + v_g + u_g) / 256; + signed r2 = (tmp2 + v_r) / 256; + + uint32_t rgb1 = + ((kAdjustedClip[b1] >> 3) << 11) + | ((kAdjustedClip[g1] >> 2) << 5) + | (kAdjustedClip[r1] >> 3); + + uint32_t rgb2 = + ((kAdjustedClip[b2] >> 3) << 11) + | ((kAdjustedClip[g2] >> 2) << 5) + | (kAdjustedClip[r2] >> 3); + + dst_ptr[x / 2] = (rgb2 << 16) | rgb1; + } + + src_y += width; + + if (y & 1) { + src_u += width; + } + + dst_ptr += dstSkip / 4; + } +} + uint8_t *ColorConverter::initClip() { static const signed kClipMin = -278; static const signed kClipMax = 535; diff --git a/media/libstagefright/foundation/AHandler.cpp b/media/libstagefright/foundation/AHandler.cpp new file mode 100644 index 000000000000..bd5f7e940a2c --- /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/ALooper.cpp b/media/libstagefright/foundation/ALooper.cpp index 831fa2a2ba3b..cd4f34992f0d 100644 --- a/media/libstagefright/foundation/ALooper.cpp +++ b/media/libstagefright/foundation/ALooper.cpp @@ -31,8 +31,9 @@ namespace android { ALooperRoster gLooperRoster; struct ALooper::LooperThread : public Thread { - LooperThread(ALooper *looper) - : mLooper(looper) { + LooperThread(ALooper *looper, bool canCallJava) + : Thread(canCallJava), + mLooper(looper) { } virtual bool threadLoop() { @@ -72,7 +73,7 @@ void ALooper::unregisterHandler(handler_id handlerID) { gLooperRoster.unregisterHandler(handlerID); } -status_t ALooper::start(bool runOnCallingThread) { +status_t ALooper::start(bool runOnCallingThread, bool canCallJava) { if (runOnCallingThread) { { Mutex::Autolock autoLock(mLock); @@ -96,7 +97,7 @@ status_t ALooper::start(bool runOnCallingThread) { return INVALID_OPERATION; } - mThread = new LooperThread(this); + mThread = new LooperThread(this, canCallJava); status_t err = mThread->run("ALooper"); if (err != OK) { diff --git a/media/libstagefright/foundation/ALooperRoster.cpp b/media/libstagefright/foundation/ALooperRoster.cpp index 5bb1cf9cce4b..65f759343b64 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/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp index dfd1ae335098..26c6d42ca606 100644 --- a/media/libstagefright/foundation/AMessage.cpp +++ b/media/libstagefright/foundation/AMessage.cpp @@ -16,6 +16,8 @@ #include "AMessage.h" +#include <ctype.h> + #include "AAtomizer.h" #include "ADebug.h" #include "ALooperRoster.h" @@ -238,4 +240,105 @@ sp<AMessage> AMessage::dup() const { return msg; } +static void appendIndent(AString *s, int32_t indent) { + static const char kWhitespace[] = + " " + " "; + + CHECK_LT((size_t)indent, sizeof(kWhitespace)); + + s->append(kWhitespace, indent); +} + +static bool isFourcc(uint32_t what) { + return isprint(what & 0xff) + && isprint((what >> 8) & 0xff) + && isprint((what >> 16) & 0xff) + && isprint((what >> 24) & 0xff); +} + +AString AMessage::debugString(int32_t indent) const { + AString s = "AMessage(what = "; + + AString tmp; + if (isFourcc(mWhat)) { + tmp = StringPrintf( + "'%c%c%c%c'", + (char)(mWhat >> 24), + (char)((mWhat >> 16) & 0xff), + (char)((mWhat >> 8) & 0xff), + (char)(mWhat & 0xff)); + } else { + tmp = StringPrintf("0x%08x", mWhat); + } + s.append(tmp); + + if (mTarget != 0) { + tmp = StringPrintf(", target = %d", mTarget); + s.append(tmp); + } + s.append(") = {\n"); + + for (size_t i = 0; i < mNumItems; ++i) { + const Item &item = mItems[i]; + + switch (item.mType) { + case kTypeInt32: + tmp = StringPrintf( + "int32_t %s = %d", item.mName, item.u.int32Value); + break; + case kTypeInt64: + tmp = StringPrintf( + "int64_t %s = %lld", item.mName, item.u.int64Value); + break; + case kTypeSize: + tmp = StringPrintf( + "size_t %s = %d", item.mName, item.u.sizeValue); + break; + case kTypeFloat: + tmp = StringPrintf( + "float %s = %f", item.mName, item.u.floatValue); + break; + case kTypeDouble: + tmp = StringPrintf( + "double %s = %f", item.mName, item.u.doubleValue); + break; + case kTypePointer: + tmp = StringPrintf( + "void *%s = %p", item.mName, item.u.ptrValue); + break; + case kTypeString: + tmp = StringPrintf( + "string %s = \"%s\"", + item.mName, + item.u.stringValue->c_str()); + break; + case kTypeObject: + tmp = StringPrintf( + "RefBase *%s = %p", item.mName, item.u.refValue); + break; + case kTypeMessage: + tmp = StringPrintf( + "AMessage %s = %s", + item.mName, + static_cast<AMessage *>( + item.u.refValue)->debugString( + indent + strlen(item.mName) + 14).c_str()); + break; + default: + TRESPASS(); + } + + appendIndent(&s, indent); + s.append(" "); + s.append(tmp); + s.append("\n"); + } + + appendIndent(&s, indent); + s.append("}"); + + return s; +} + } // namespace android diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk index 73047e7592f2..35eea7e18724 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/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 2a9f21b4b3b0..8d0877cbdd93 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 ad5b0f9c15e0..6de761f06fe9 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 056537df1f62..b1ad315fcb46 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 a52fd76acdbc..23326577ef9a 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 8c621b601736..376c64f2bf60 100644 --- a/native/android/Android.mk +++ b/native/android/Android.mk @@ -7,7 +7,9 @@ include $(CLEAR_VARS) # LOCAL_SRC_FILES:= \ activity.cpp \ - input.cpp + input.cpp \ + looper.cpp \ + native_window.cpp LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ diff --git a/native/android/input.cpp b/native/android/input.cpp index 38d8567e313c..015a1ced432b 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> @@ -27,168 +28,176 @@ using android::InputEvent; using android::KeyEvent; using android::MotionEvent; -int32_t input_event_get_type(const input_event_t* event) { +int32_t AInputEvent_getType(const AInputEvent* event) { return static_cast<const InputEvent*>(event)->getType(); } -int32_t input_event_get_device_id(const input_event_t* event) { +int32_t AInputEvent_getDeviceId(const AInputEvent* event) { return static_cast<const InputEvent*>(event)->getDeviceId(); } -int32_t input_event_get_nature(const input_event_t* event) { +int32_t AInputEvent_getNature(const AInputEvent* event) { return static_cast<const InputEvent*>(event)->getNature(); } -int32_t key_event_get_action(const input_event_t* key_event) { +int32_t AKeyEvent_getAction(const AInputEvent* key_event) { return static_cast<const KeyEvent*>(key_event)->getAction(); } -int32_t key_event_get_flags(const input_event_t* key_event) { +int32_t AKeyEvent_getFlags(const AInputEvent* key_event) { return static_cast<const KeyEvent*>(key_event)->getFlags(); } -int32_t key_event_get_key_code(const input_event_t* key_event) { +int32_t AKeyEvent_getKeyCode(const AInputEvent* key_event) { return static_cast<const KeyEvent*>(key_event)->getKeyCode(); } -int32_t key_event_get_scan_code(const input_event_t* key_event) { +int32_t AKeyEvent_getScanCode(const AInputEvent* key_event) { return static_cast<const KeyEvent*>(key_event)->getScanCode(); } -int32_t key_event_get_meta_state(const input_event_t* key_event) { +int32_t AKeyEvent_getMetaState(const AInputEvent* key_event) { return static_cast<const KeyEvent*>(key_event)->getMetaState(); } -int32_t key_event_get_repeat_count(const input_event_t* key_event) { +int32_t AKeyEvent_getRepeatCount(const AInputEvent* key_event) { return static_cast<const KeyEvent*>(key_event)->getRepeatCount(); } -int64_t key_event_get_down_time(const input_event_t* key_event) { +int64_t AKeyEvent_getDownTime(const AInputEvent* key_event) { return static_cast<const KeyEvent*>(key_event)->getDownTime(); } -int64_t key_event_get_event_time(const input_event_t* key_event) { +int64_t AKeyEvent_getEventTime(const AInputEvent* key_event) { return static_cast<const KeyEvent*>(key_event)->getEventTime(); } -int32_t motion_event_get_action(const input_event_t* motion_event) { +int32_t AMotionEvent_getAction(const AInputEvent* motion_event) { return static_cast<const MotionEvent*>(motion_event)->getAction(); } -int32_t motion_event_get_meta_state(const input_event_t* motion_event) { +int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event) { return static_cast<const MotionEvent*>(motion_event)->getMetaState(); } -int32_t motion_event_get_edge_flags(const input_event_t* motion_event) { +int32_t AMotionEvent_getEdgeFlags(const AInputEvent* motion_event) { return reinterpret_cast<const MotionEvent*>(motion_event)->getEdgeFlags(); } -int64_t motion_event_get_down_time(const input_event_t* motion_event) { +int64_t AMotionEvent_getDownTime(const AInputEvent* motion_event) { return static_cast<const MotionEvent*>(motion_event)->getDownTime(); } -int64_t motion_event_get_event_time(const input_event_t* motion_event) { +int64_t AMotionEvent_getEventTime(const AInputEvent* motion_event) { return static_cast<const MotionEvent*>(motion_event)->getEventTime(); } -float motion_event_get_x_offset(const input_event_t* motion_event) { +float AMotionEvent_getXOffset(const AInputEvent* motion_event) { return static_cast<const MotionEvent*>(motion_event)->getXOffset(); } -float motion_event_get_y_offset(const input_event_t* motion_event) { +float AMotionEvent_getYOffset(const AInputEvent* motion_event) { return static_cast<const MotionEvent*>(motion_event)->getYOffset(); } -float motion_event_get_x_precision(const input_event_t* motion_event) { +float AMotionEvent_getXPrecision(const AInputEvent* motion_event) { return static_cast<const MotionEvent*>(motion_event)->getXPrecision(); } -float motion_event_get_y_precision(const input_event_t* motion_event) { +float AMotionEvent_getYPrecision(const AInputEvent* motion_event) { return static_cast<const MotionEvent*>(motion_event)->getYPrecision(); } -size_t motion_event_get_pointer_count(const input_event_t* motion_event) { +size_t AMotionEvent_getPointerCount(const AInputEvent* motion_event) { return static_cast<const MotionEvent*>(motion_event)->getPointerCount(); } -int32_t motion_event_get_pointer_id(const input_event_t* motion_event, size_t pointer_index) { +int32_t AMotionEvent_getPointerId(const AInputEvent* motion_event, size_t pointer_index) { return static_cast<const MotionEvent*>(motion_event)->getPointerId(pointer_index); } -float motion_event_get_raw_x(const input_event_t* motion_event, size_t pointer_index) { +float AMotionEvent_getRawX(const AInputEvent* motion_event, size_t pointer_index) { return static_cast<const MotionEvent*>(motion_event)->getRawX(pointer_index); } -float motion_event_get_raw_y(const input_event_t* motion_event, size_t pointer_index) { +float AMotionEvent_getRawY(const AInputEvent* motion_event, size_t pointer_index) { return static_cast<const MotionEvent*>(motion_event)->getRawY(pointer_index); } -float motion_event_get_x(const input_event_t* motion_event, size_t pointer_index) { +float AMotionEvent_getX(const AInputEvent* motion_event, size_t pointer_index) { return static_cast<const MotionEvent*>(motion_event)->getX(pointer_index); } -float motion_event_get_y(const input_event_t* motion_event, size_t pointer_index) { +float AMotionEvent_getY(const AInputEvent* motion_event, size_t pointer_index) { return static_cast<const MotionEvent*>(motion_event)->getY(pointer_index); } -float motion_event_get_pressure(const input_event_t* motion_event, size_t pointer_index) { +float AMotionEvent_getPressure(const AInputEvent* motion_event, size_t pointer_index) { return static_cast<const MotionEvent*>(motion_event)->getPressure(pointer_index); } -float motion_event_get_size(const input_event_t* motion_event, size_t pointer_index) { +float AMotionEvent_getSize(const AInputEvent* motion_event, size_t pointer_index) { return static_cast<const MotionEvent*>(motion_event)->getSize(pointer_index); } -size_t motion_event_get_history_size(const input_event_t* motion_event) { +size_t AMotionEvent_getHistorySize(const AInputEvent* motion_event) { return static_cast<const MotionEvent*>(motion_event)->getHistorySize(); } -int64_t motion_event_get_historical_event_time(input_event_t* motion_event, +int64_t AMotionEvent_getHistoricalEventTime(AInputEvent* motion_event, size_t history_index) { return static_cast<const MotionEvent*>(motion_event)->getHistoricalEventTime( history_index); } -float motion_event_get_historical_raw_x(input_event_t* motion_event, size_t pointer_index, +float AMotionEvent_getHistoricalRawX(AInputEvent* motion_event, size_t pointer_index, size_t history_index) { return static_cast<const MotionEvent*>(motion_event)->getHistoricalRawX( pointer_index, history_index); } -float motion_event_get_historical_raw_y(input_event_t* motion_event, size_t pointer_index, +float AMotionEvent_getHistoricalRawY(AInputEvent* motion_event, size_t pointer_index, size_t history_index) { return static_cast<const MotionEvent*>(motion_event)->getHistoricalRawY( pointer_index, history_index); } -float motion_event_get_historical_x(input_event_t* motion_event, size_t pointer_index, +float AMotionEvent_getHistoricalX(AInputEvent* motion_event, size_t pointer_index, size_t history_index) { return static_cast<const MotionEvent*>(motion_event)->getHistoricalX( pointer_index, history_index); } -float motion_event_get_historical_y(input_event_t* motion_event, size_t pointer_index, +float AMotionEvent_getHistoricalY(AInputEvent* motion_event, size_t pointer_index, size_t history_index) { return static_cast<const MotionEvent*>(motion_event)->getHistoricalY( pointer_index, history_index); } -float motion_event_get_historical_pressure(input_event_t* motion_event, size_t pointer_index, +float AMotionEvent_getHistoricalPressure(AInputEvent* motion_event, size_t pointer_index, size_t history_index) { return static_cast<const MotionEvent*>(motion_event)->getHistoricalPressure( pointer_index, history_index); } -float motion_event_get_historical_size(input_event_t* motion_event, size_t pointer_index, +float AMotionEvent_getHistoricalSize(AInputEvent* motion_event, size_t pointer_index, size_t history_index) { return static_cast<const MotionEvent*>(motion_event)->getHistoricalSize( pointer_index, history_index); } -int input_queue_get_fd(input_queue_t* 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_setCallback(looper, queue->getConsumer().getChannel()->getReceivePipeFd(), + POLLIN, callback, data); } -int input_queue_has_events(input_queue_t* queue) { +void AInputQueue_detachLooper(AInputQueue* queue) { + queue->getPollLoop()->removeCallback( + queue->getConsumer().getChannel()->getReceivePipeFd()); +} + +int AInputQueue_hasEvents(AInputQueue* queue) { struct pollfd pfd; pfd.fd = queue->getConsumer().getChannel()->getReceivePipeFd(); @@ -200,7 +209,7 @@ int input_queue_has_events(input_queue_t* queue) { return pfd.revents == POLLIN ? 1 : -1; } -int32_t input_queue_get_event(input_queue_t* queue, input_event_t** outEvent) { +int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent) { *outEvent = NULL; int32_t res = queue->getConsumer().receiveDispatchSignal(); @@ -223,8 +232,17 @@ int32_t input_queue_get_event(input_queue_t* queue, input_event_t** outEvent) { return 0; } -void input_queue_finish_event(input_queue_t* queue, input_event_t* event, +void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled) { + if (!handled && ((InputEvent*)event)->getType() == INPUT_EVENT_TYPE_KEY + && ((KeyEvent*)event)->hasDefaultAction()) { + // The app didn't handle this, but it may have a default action + // associated with it. We need to hand this back to Java to be + // executed. + queue->doDefaultKey((KeyEvent*)event); + return; + } + int32_t res = queue->getConsumer().sendFinishedSignal(); if (res != android::OK) { LOGW("Failed to send finished signal on channel '%s'. status=%d", diff --git a/native/android/looper.cpp b/native/android/looper.cpp new file mode 100644 index 000000000000..6e78bbd03d12 --- /dev/null +++ b/native/android/looper.cpp @@ -0,0 +1,63 @@ +/* + * 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() { + sp<PollLoop> loop = PollLoop::getForThread(); + if (loop == NULL) { + loop = new PollLoop(); + PollLoop::setForThread(loop); + } + return loop.get(); +} + +int32_t ALooper_pollOnce(int timeoutMillis) { + sp<PollLoop> loop = PollLoop::getForThread(); + if (loop == NULL) { + LOGW("ALooper_pollOnce: No looper for this thread!"); + return -1; + } + return loop->pollOnce(timeoutMillis) ? 1 : 0; +} + +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_setCallback(ALooper* looper, int fd, int events, + ALooper_callbackFunc* callback, void* data) { + static_cast<PollLoop*>(looper)->setLooperCallback(fd, events, callback, data); +} + +int32_t ALooper_removeCallback(ALooper* looper, int fd) { + return static_cast<PollLoop*>(looper)->removeCallback(fd) ? 1 : 0; +} diff --git a/native/android/native_window.cpp b/native/android/native_window.cpp new file mode 100644 index 000000000000..448cbfccb666 --- /dev/null +++ b/native/android/native_window.cpp @@ -0,0 +1,47 @@ +/* + * 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 "Surface" +#include <utils/Log.h> + +#include <android/native_window.h> +#include <surfaceflinger/Surface.h> + +using android::Surface; + +static int32_t getWindowProp(ANativeWindow* window, int what) { + int value; + int res = window->query(window, what, &value); + return res < 0 ? res : value; +} + +int32_t ANativeWindow_getWidth(ANativeWindow* window) { + return getWindowProp(window, NATIVE_WINDOW_WIDTH); +} + +int32_t ANativeWindow_getHeight(ANativeWindow* window) { + return getWindowProp(window, NATIVE_WINDOW_HEIGHT); +} + +int32_t ANativeWindow_getFormat(ANativeWindow* window) { + return getWindowProp(window, NATIVE_WINDOW_FORMAT); +} + +int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, + int32_t height, int32_t format) { + native_window_set_buffers_geometry(window, width, height, format); + return 0; +} diff --git a/native/include/android/input.h b/native/include/android/input.h index 2441af0ad8ba..75be85abb95c 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" { @@ -125,8 +126,8 @@ enum { * Input events are opaque structures. Use the provided accessors functions to * read their properties. */ -struct input_event_t; -typedef struct input_event_t input_event_t; +struct AInputEvent; +typedef struct AInputEvent AInputEvent; /* * Input event types. @@ -319,7 +320,7 @@ enum { /*** Accessors for all input events. ***/ /* Get the input event type. */ -int32_t input_event_get_type(const input_event_t* event); +int32_t AInputEvent_getType(const AInputEvent* event); /* Get the id for the device that an input event came from. * @@ -331,128 +332,128 @@ int32_t input_event_get_type(const input_event_t* event); * other numbers are arbitrary and you shouldn't depend on the values. * Use the provided input device query API to obtain information about input devices. */ -int32_t input_event_get_device_id(const input_event_t* event); +int32_t AInputEvent_getDeviceId(const AInputEvent* event); /* Get the input event nature. */ -int32_t input_event_get_nature(const input_event_t* event); +int32_t AInputEvent_getNature(const AInputEvent* event); /*** Accessors for key events only. ***/ /* Get the key event action. */ -int32_t key_event_get_action(const input_event_t* key_event); +int32_t AKeyEvent_getAction(const AInputEvent* key_event); /* Get the key event flags. */ -int32_t key_event_get_flags(const input_event_t* key_event); +int32_t AKeyEvent_getFlags(const AInputEvent* key_event); /* Get the key code of the key event. * This is the physical key that was pressed, not the Unicode character. */ -int32_t key_event_get_key_code(const input_event_t* key_event); +int32_t AKeyEvent_getKeyCode(const AInputEvent* key_event); /* Get the hardware key id of this key event. * These values are not reliable and vary from device to device. */ -int32_t key_event_get_scan_code(const input_event_t* key_event); +int32_t AKeyEvent_getScanCode(const AInputEvent* key_event); /* Get the meta key state. */ -int32_t key_event_get_meta_state(const input_event_t* key_event); +int32_t AKeyEvent_getMetaState(const AInputEvent* key_event); /* Get the repeat count of the event. * For both key up an key down events, this is the number of times the key has * repeated with the first down starting at 0 and counting up from there. For * multiple key events, this is the number of down/up pairs that have occurred. */ -int32_t key_event_get_repeat_count(const input_event_t* key_event); +int32_t AKeyEvent_getRepeatCount(const AInputEvent* key_event); /* Get the time of the most recent key down event, in the * java.lang.System.nanoTime() time base. If this is a down event, * this will be the same as eventTime. * Note that when chording keys, this value is the down time of the most recently * pressed key, which may not be the same physical key of this event. */ -int64_t key_event_get_down_time(const input_event_t* key_event); +int64_t AKeyEvent_getDownTime(const AInputEvent* key_event); /* Get the time this event occurred, in the * java.lang.System.nanoTime() time base. */ -int64_t key_event_get_event_time(const input_event_t* key_event); +int64_t AKeyEvent_getEventTime(const AInputEvent* key_event); /*** Accessors for motion events only. ***/ /* Get the combined motion event action code and pointer index. */ -int32_t motion_event_get_action(const input_event_t* motion_event); +int32_t AMotionEvent_getAction(const AInputEvent* motion_event); /* Get the state of any meta / modifier keys that were in effect when the * event was generated. */ -int32_t motion_event_get_meta_state(const input_event_t* motion_event); +int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event); /* Get a bitfield indicating which edges, if any, were touched by this motion event. * For touch events, clients can use this to determine if the user's finger was * touching the edge of the display. */ -int32_t motion_event_get_edge_flags(const input_event_t* motion_event); +int32_t AMotionEvent_getEdgeFlags(const AInputEvent* motion_event); /* Get the time when the user originally pressed down to start a stream of * position events, in the java.lang.System.nanoTime() time base. */ -int64_t motion_event_get_down_time(const input_event_t* motion_event); +int64_t AMotionEvent_getDownTime(const AInputEvent* motion_event); /* Get the time when this specific event was generated, * in the java.lang.System.nanoTime() time base. */ -int64_t motion_event_get_event_time(const input_event_t* motion_event); +int64_t AMotionEvent_getEventTime(const AInputEvent* motion_event); /* Get the X coordinate offset. * For touch events on the screen, this is the delta that was added to the raw * screen coordinates to adjust for the absolute position of the containing windows * and views. */ -float motion_event_get_x_offset(const input_event_t* motion_event); +float AMotionEvent_getXOffset(const AInputEvent* motion_event); /* Get the precision of the Y coordinates being reported. * For touch events on the screen, this is the delta that was added to the raw * screen coordinates to adjust for the absolute position of the containing windows * and views. */ -float motion_event_get_y_offset(const input_event_t* motion_event); +float AMotionEvent_getYOffset(const AInputEvent* motion_event); /* Get the precision of the X coordinates being reported. * You can multiply this number with an X coordinate sample to find the * actual hardware value of the X coordinate. */ -float motion_event_get_x_precision(const input_event_t* motion_event); +float AMotionEvent_getXPrecision(const AInputEvent* motion_event); /* Get the precision of the Y coordinates being reported. * You can multiply this number with a Y coordinate sample to find the * actual hardware value of the Y coordinate. */ -float motion_event_get_y_precision(const input_event_t* motion_event); +float AMotionEvent_getYPrecision(const AInputEvent* motion_event); /* Get the number of pointers of data contained in this event. * Always >= 1. */ -size_t motion_event_get_pointer_count(const input_event_t* motion_event); +size_t AMotionEvent_getPointerCount(const AInputEvent* motion_event); /* Get the pointer identifier associated with a particular pointer * data index is this event. The identifier tells you the actual pointer * number associated with the data, accounting for individual pointers * going up and down since the start of the current gesture. */ -int32_t motion_event_get_pointer_id(const input_event_t* motion_event, size_t pointer_index); +int32_t AMotionEvent_getPointerId(const AInputEvent* motion_event, size_t pointer_index); /* Get the original raw X coordinate of this event. * For touch events on the screen, this is the original location of the event * on the screen, before it had been adjusted for the containing window * and views. */ -float motion_event_get_raw_x(const input_event_t* motion_event, size_t pointer_index); +float AMotionEvent_getRawX(const AInputEvent* motion_event, size_t pointer_index); /* Get the original raw X coordinate of this event. * For touch events on the screen, this is the original location of the event * on the screen, before it had been adjusted for the containing window * and views. */ -float motion_event_get_raw_y(const input_event_t* motion_event, size_t pointer_index); +float AMotionEvent_getRawY(const AInputEvent* motion_event, size_t pointer_index); /* Get the current X coordinate of this event for the given pointer index. * Whole numbers are pixels; the value may have a fraction for input devices * that are sub-pixel precise. */ -float motion_event_get_x(const input_event_t* motion_event, size_t pointer_index); +float AMotionEvent_getX(const AInputEvent* motion_event, size_t pointer_index); /* Get the current Y coordinate of this event for the given pointer index. * Whole numbers are pixels; the value may have a fraction for input devices * that are sub-pixel precise. */ -float motion_event_get_y(const input_event_t* motion_event, size_t pointer_index); +float AMotionEvent_getY(const AInputEvent* motion_event, size_t pointer_index); /* Get the current pressure of this event for the given pointer index. * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure), * however values higher than 1 may be generated depending on the calibration of * the input device. */ -float motion_event_get_pressure(const input_event_t* motion_event, size_t pointer_index); +float AMotionEvent_getPressure(const AInputEvent* motion_event, size_t pointer_index); /* Get the current scaled value of the approximate size for the given pointer index. * This represents some approximation of the area of the screen being @@ -460,17 +461,17 @@ float motion_event_get_pressure(const input_event_t* motion_event, size_t pointe * touch is normalized with the device specific range of values * and scaled to a value between 0 and 1. The value of size can be used to * determine fat touch events. */ -float motion_event_get_size(const input_event_t* motion_event, size_t pointer_index); +float AMotionEvent_getSize(const AInputEvent* motion_event, size_t pointer_index); /* Get the number of historical points in this event. These are movements that * have occurred between this event and the previous event. This only applies * to MOTION_EVENT_ACTION_MOVE events -- all other actions will have a size of 0. * Historical samples are indexed from oldest to newest. */ -size_t motion_event_get_history_size(const input_event_t* motion_event); +size_t AMotionEvent_get_history_size(const AInputEvent* motion_event); /* Get the time that a historical movement occurred between this event and * the previous event, in the java.lang.System.nanoTime() time base. */ -int64_t motion_event_get_historical_event_time(input_event_t* motion_event, +int64_t AMotionEvent_getHistoricalEventTime(AInputEvent* motion_event, size_t history_index); /* Get the historical raw X coordinate of this event for the given pointer index that @@ -480,7 +481,7 @@ int64_t motion_event_get_historical_event_time(input_event_t* motion_event, * and views. * Whole numbers are pixels; the value may have a fraction for input devices * that are sub-pixel precise. */ -float motion_event_get_historical_raw_x(const input_event_t* motion_event, size_t pointer_index); +float AMotionEvent_getHistoricalRawX(const AInputEvent* motion_event, size_t pointer_index); /* Get the historical raw Y coordinate of this event for the given pointer index that * occurred between this event and the previous motion event. @@ -489,20 +490,20 @@ float motion_event_get_historical_raw_x(const input_event_t* motion_event, size_ * and views. * Whole numbers are pixels; the value may have a fraction for input devices * that are sub-pixel precise. */ -float motion_event_get_historical_raw_y(const input_event_t* motion_event, size_t pointer_index); +float AMotionEvent_getHistoricalRawY(const AInputEvent* motion_event, size_t pointer_index); /* Get the historical X coordinate of this event for the given pointer index that * occurred between this event and the previous motion event. * Whole numbers are pixels; the value may have a fraction for input devices * that are sub-pixel precise. */ -float motion_event_get_historical_x(input_event_t* motion_event, size_t pointer_index, +float AMotionEvent_getHistoricalX(AInputEvent* motion_event, size_t pointer_index, size_t history_index); /* Get the historical Y coordinate of this event for the given pointer index that * occurred between this event and the previous motion event. * Whole numbers are pixels; the value may have a fraction for input devices * that are sub-pixel precise. */ -float motion_event_get_historical_y(input_event_t* motion_event, size_t pointer_index, +float AMotionEvent_getHistoricalY(AInputEvent* motion_event, size_t pointer_index, size_t history_index); /* Get the historical pressure of this event for the given pointer index that @@ -510,7 +511,7 @@ float motion_event_get_historical_y(input_event_t* motion_event, size_t pointer_ * The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure), * however values higher than 1 may be generated depending on the calibration of * the input device. */ -float motion_event_get_historical_pressure(input_event_t* motion_event, size_t pointer_index, +float AMotionEvent_getHistoricalPressure(AInputEvent* motion_event, size_t pointer_index, size_t history_index); /* Get the current scaled value of the approximate size for the given pointer index that @@ -520,7 +521,7 @@ float motion_event_get_historical_pressure(input_event_t* motion_event, size_t p * touch is normalized with the device specific range of values * and scaled to a value between 0 and 1. The value of size can be used to * determine fat touch events. */ -float motion_event_get_historical_size(input_event_t* motion_event, size_t pointer_index, +float AMotionEvent_getHistoricalSize(AInputEvent* motion_event, size_t pointer_index, size_t history_index); /* @@ -529,35 +530,38 @@ float motion_event_get_historical_size(input_event_t* motion_event, size_t point * An input queue is the facility through which you retrieve input * events. */ -struct input_queue_t; -typedef struct input_queue_t input_queue_t; +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. */ -int input_queue_get_fd(input_queue_t* 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 * input queue. Returns 1 if the queue has events; 0 if * it does not have events; and a negative value if there is an error. */ -int input_queue_has_events(input_queue_t* queue); +int AInputQueue_hasEvents(AInputQueue* queue); /* * Returns the next available event from the queue. Returns a negative * value if no events are available or an error has occurred. */ -int32_t input_queue_get_event(input_queue_t* queue, input_event_t** outEvent); +int32_t AInputQueue_getEvent(AInputQueue* queue, AInputEvent** outEvent); /* * Report that dispatching has finished with the given event. - * This must be called after receiving an event with input_queue_get_event(). + * This must be called after receiving an event with AInputQueue_get_event(). */ -void input_queue_finish_event(input_queue_t* queue, input_event_t* event, int handled); +void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled); #ifdef __cplusplus } diff --git a/native/include/android/looper.h b/native/include/android/looper.h new file mode 100644 index 000000000000..90a89838a57e --- /dev/null +++ b/native/include/android/looper.h @@ -0,0 +1,51 @@ +/* + * 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 + +struct ALooper; +typedef struct ALooper ALooper; + +typedef int ALooper_callbackFunc(int fd, int events, void* data); + +ALooper* ALooper_forThread(); + +ALooper* ALooper_prepare(); + +int32_t ALooper_pollOnce(int timeoutMillis); + +void ALooper_acquire(ALooper* looper); + +void ALooper_release(ALooper* looper); + +void ALooper_setCallback(ALooper* looper, int fd, int events, + ALooper_callbackFunc* callback, void* data); + +int32_t ALooper_removeCallback(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 a58a7d27c604..a31c5af74f08 100644 --- a/native/include/android/native_activity.h +++ b/native/include/android/native_activity.h @@ -24,48 +24,67 @@ #include <jni.h> #include <android/input.h> +#include <android/native_window.h> #ifdef __cplusplus extern "C" { #endif -// Temporary until native surface API is defined. -struct android_surface_t; -typedef struct android_surface_t android_surface_t; - -struct android_activity_callbacks_t; +struct ANativeActivityCallbacks; /** * This structure defines the native side of an android.app.NativeActivity. * It is created by the framework, and handed to the application's native * code as it is being launched. */ -typedef struct android_activity_t { +typedef struct ANativeActivity { /** * Pointer to the callback function table of the native application. * You can set the functions here to your own callbacks. The callbacks * pointer itself here should not be changed; it is allocated and managed * for you by the framework. */ - struct android_activity_callbacks_t* callbacks; + struct ANativeActivityCallbacks* callbacks; /** - * JNI context for the main thread of the app. + * The global handle on the process's Java VM. + */ + JavaVM* vm; + + /** + * JNI context for the main thread of the app. Note that this field + * can ONLY be used from the main thread of the process; that is, the + * thread that calls into the ANativeActivityCallbacks. */ JNIEnv* env; - + /** * The NativeActivity Java class. */ 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. */ void* instance; -} android_activity_t; +} ANativeActivity; /** * These are the callbacks the framework makes into a native application. @@ -73,18 +92,18 @@ typedef struct android_activity_t { * By default, all callbacks are NULL; set to a pointer to your own function * to have it called. */ -typedef struct android_activity_callbacks_t { +typedef struct ANativeActivityCallbacks { /** * NativeActivity has started. See Java documentation for Activity.onStart() * for more information. */ - void (*onStart)(android_activity_t* activity); + void (*onStart)(ANativeActivity* activity); /** * NativeActivity has resumed. See Java documentation for Activity.onResume() * for more information. */ - void (*onResume)(android_activity_t* activity); + void (*onResume)(ANativeActivity* activity); /** * Framework is asking NativeActivity to save its current instance state. @@ -95,78 +114,68 @@ typedef struct android_activity_callbacks_t { * saved state will be persisted, so it can not contain any active * entities (pointers to memory, file descriptors, etc). */ - void* (*onSaveInstanceState)(android_activity_t* activity, size_t* outSize); + void* (*onSaveInstanceState)(ANativeActivity* activity, size_t* outSize); /** * NativeActivity has paused. See Java documentation for Activity.onPause() * for more information. */ - void (*onPause)(android_activity_t* activity); + void (*onPause)(ANativeActivity* activity); /** * NativeActivity has stopped. See Java documentation for Activity.onStop() * for more information. */ - void (*onStop)(android_activity_t* activity); + void (*onStop)(ANativeActivity* activity); /** * NativeActivity is being destroyed. See Java documentation for Activity.onDestroy() * for more information. */ - void (*onDestroy)(android_activity_t* activity); + void (*onDestroy)(ANativeActivity* activity); /** * Focus has changed in this NativeActivity's window. This is often used, * for example, to pause a game when it loses input focus. */ - void (*onWindowFocusChanged)(android_activity_t* activity, int hasFocus); + void (*onWindowFocusChanged)(ANativeActivity* activity, int hasFocus); /** - * The drawing surface for this native activity has been created. You - * can use the given surface object to start drawing. NOTE: surface - * drawing API is not yet defined. - */ - void (*onSurfaceCreated)(android_activity_t* activity, android_surface_t* surface); - - /** - * The drawing surface for this native activity has changed. The surface - * given here is guaranteed to be the same as the one last given to - * onSurfaceCreated. This is simply to inform you about interesting - * changed to that surface. + * The drawing window for this native activity has been created. You + * can use the given native window object to start drawing. */ - void (*onSurfaceChanged)(android_activity_t* activity, android_surface_t* surface, - int format, int width, int height); + void (*onNativeWindowCreated)(ANativeActivity* activity, ANativeWindow* window); /** - * The drawing surface for this native activity is going to be destroyed. - * You MUST ensure that you do not touch the surface object after returning - * from this function: in the common case of drawing to the surface from + * The drawing window for this native activity is going to be destroyed. + * You MUST ensure that you do not touch the window object after returning + * from this function: in the common case of drawing to the window from * another thread, that means the implementation of this callback must * properly synchronize with the other thread to stop its drawing before * returning from here. */ - void (*onSurfaceDestroyed)(android_activity_t* activity, android_surface_t* surface); + void (*onNativeWindowDestroyed)(ANativeActivity* activity, ANativeWindow* window); /** * The input queue for this native activity's window has been created. * You can use the given input queue to start retrieving input events. */ - void (*onInputQueueCreated)(android_activity_t* activity, input_queue_t* queue); + void (*onInputQueueCreated)(ANativeActivity* activity, AInputQueue* queue); /** * The input queue for this native activity's window is being destroyed. * You should no longer try to reference this object upon returning from this * function. */ - void (*onInputQueueDestroyed)(android_activity_t* activity, input_queue_t* queue); + void (*onInputQueueDestroyed)(ANativeActivity* activity, AInputQueue* queue); /** * The system is running low on memory. Use this callback to release * resources you do not need, to help the system avoid killing more * important processes. */ - void (*onLowMemory)(android_activity_t* activity); -} android_activity_callbacks_t; + void (*onLowMemory)(ANativeActivity* activity); +} ANativeActivityCallbacks; /** * This is the function that must be in the native code to instantiate the @@ -174,14 +183,14 @@ typedef struct android_activity_callbacks_t { * above); if the code is being instantiated from a previously saved instance, * the savedState will be non-NULL and point to the saved data. */ -typedef void android_activity_create_t(android_activity_t* activity, +typedef void ANativeActivity_createFunc(ANativeActivity* activity, void* savedState, size_t savedStateSize); /** * The name of the function that NativeInstance looks for when launching its * native code. */ -extern android_activity_create_t android_onCreateActivity; +extern ANativeActivity_createFunc ANativeActivity_onCreate; #ifdef __cplusplus }; diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h new file mode 100644 index 000000000000..678ba3d7c7e2 --- /dev/null +++ b/native/include/android/native_window.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_NATIVE_WINDOW_H +#define ANDROID_NATIVE_WINDOW_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Pixel formats that a window can use. + */ +enum { + WINDOW_FORMAT_RGBA_8888 = 1, + WINDOW_FORMAT_RGBX_8888 = 2, + WINDOW_FORMAT_RGB_565 = 4, +}; + +struct ANativeWindow; +typedef struct ANativeWindow ANativeWindow; + +/* + * Return the current width in pixels of the window surface. Returns a + * negative value on error. + */ +int32_t ANativeWindow_getWidth(ANativeWindow* window); + +/* + * Return the current height in pixels of the window surface. Returns a + * negative value on error. + */ +int32_t ANativeWindow_getHeight(ANativeWindow* window); + +/* + * Return the current pixel format of the window surface. Returns a + * negative value on error. + */ +int32_t ANativeWindow_getFormat(ANativeWindow* window); + +/* + * Change the format and size of the window buffers. + * + * The width and height control the number of pixels in the buffers, not the + * dimensions of the window on screen. If these are different than the + * 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 + * value will come back in force. + */ +int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, + int32_t height, int32_t format); + +#ifdef __cplusplus +}; +#endif + +#endif // ANDROID_NATIVE_WINDOW_H diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h index a2c6a7d8edae..25d7697ee3be 100644 --- a/opengl/include/EGL/eglplatform.h +++ b/opengl/include/EGL/eglplatform.h @@ -91,10 +91,11 @@ typedef Window EGLNativeWindowType; #elif defined(ANDROID) -struct android_native_window_t; +#include <android/native_window.h> + struct egl_native_pixmap_t; -typedef struct android_native_window_t* EGLNativeWindowType; +typedef struct ANativeWindow* EGLNativeWindowType; typedef struct egl_native_pixmap_t* EGLNativePixmapType; typedef void* EGLNativeDisplayType; diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp index 7cb01d0e1a0b..54d7307132dd 100644 --- a/opengl/libagl/egl.cpp +++ b/opengl/libagl/egl.cpp @@ -213,7 +213,7 @@ struct egl_window_surface_v2_t : public egl_surface_t egl_window_surface_v2_t( EGLDisplay dpy, EGLConfig config, int32_t depthFormat, - android_native_window_t* window); + ANativeWindow* window); ~egl_window_surface_v2_t(); @@ -235,7 +235,7 @@ struct egl_window_surface_v2_t : public egl_surface_t private: status_t lock(android_native_buffer_t* buf, int usage, void** vaddr); status_t unlock(android_native_buffer_t* buf); - android_native_window_t* nativeWindow; + ANativeWindow* nativeWindow; android_native_buffer_t* buffer; android_native_buffer_t* previousBuffer; gralloc_module_t const* module; @@ -355,7 +355,7 @@ private: egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy, EGLConfig config, int32_t depthFormat, - android_native_window_t* window) + ANativeWindow* window) : egl_surface_t(dpy, config, depthFormat), nativeWindow(window), buffer(0), previousBuffer(0), module(0), blitengine(0), bits(NULL) @@ -1300,7 +1300,7 @@ static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config, if (!(surfaceType & EGL_WINDOW_BIT)) return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - if (static_cast<android_native_window_t*>(window)->common.magic != + if (static_cast<ANativeWindow*>(window)->common.magic != ANDROID_NATIVE_WINDOW_MAGIC) { return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); } @@ -1323,7 +1323,7 @@ static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config, egl_surface_t* surface; surface = new egl_window_surface_v2_t(dpy, config, depthFormat, - static_cast<android_native_window_t*>(window)); + static_cast<ANativeWindow*>(window)); if (!surface->initCheck()) { // there was a problem in the ctor, the error diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index ba09d082feec..714fd3eec7c5 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -1045,6 +1045,10 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, int i=0, index=0; egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); if (cnx) { + if (share_list != EGL_NO_CONTEXT) { + egl_context_t* const c = get_context(share_list); + share_list = c->context; + } EGLContext context = cnx->egl.eglCreateContext( dp->disp[i].dpy, dp->disp[i].config[index], share_list, attrib_list); diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 74d87ba01835..75045d77e9d7 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -1,6 +1,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.systemui" - android:sharedUserId="android.uid.system"> + android:sharedUserId="android.uid.system" + android:process="system" + > <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> @@ -10,7 +12,7 @@ android:icon="@drawable/ic_launcher_settings"> <service - android:name=".statusbar.PhoneStatusBarService" + android:name=".statusbar.StatusBarService" android:exported="false" /> diff --git a/packages/SystemUI/res/drawable-hdpi/alert_bar_background.9.png b/packages/SystemUI/res/drawable-hdpi/alert_bar_background.9.png Binary files differdeleted file mode 100644 index 7000eee842e6..000000000000 --- a/packages/SystemUI/res/drawable-hdpi/alert_bar_background.9.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/alert_bar_background_normal.9.png b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_normal.9.png Binary files differnew file mode 100644 index 000000000000..bc127bde4ad1 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_normal.9.png diff --git a/packages/SystemUI/res/drawable-hdpi/alert_bar_background_pressed.9.png b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_pressed.9.png Binary files differnew file mode 100644 index 000000000000..59af8043f073 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_pressed.9.png diff --git a/packages/SystemUI/res/drawable-mdpi/alert_bar_background.9.png b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_normal.9.png Binary files differindex 258de13c1426..258de13c1426 100644 --- a/packages/SystemUI/res/drawable-mdpi/alert_bar_background.9.png +++ b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_normal.9.png diff --git a/packages/SystemUI/res/drawable-mdpi/alert_bar_background_pressed.9.png b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_pressed.9.png Binary files differnew file mode 100644 index 000000000000..258de13c1426 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_pressed.9.png diff --git a/packages/SystemUI/res/drawable/alert_bar_background.xml b/packages/SystemUI/res/drawable/alert_bar_background.xml new file mode 100644 index 000000000000..24b6aa326096 --- /dev/null +++ b/packages/SystemUI/res/drawable/alert_bar_background.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" + android:drawable="@drawable/alert_bar_background_pressed" /> + <item + android:drawable="@drawable/alert_bar_background_normal" /> +</selector> + diff --git a/packages/SystemUI/res/layout/intruder_alert.xml b/packages/SystemUI/res/layout/intruder_alert.xml index 58dc333c5de8..ba4a77471ef0 100644 --- a/packages/SystemUI/res/layout/intruder_alert.xml +++ b/packages/SystemUI/res/layout/intruder_alert.xml @@ -21,29 +21,35 @@ <!-- android:background="@drawable/status_bar_closed_default_background" --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:background="@drawable/alert_bar_background" - android:orientation="horizontal" - android:focusable="true" - android:descendantFocusability="afterDescendants" + android:layout_height="32dip" + android:layout_width="match_parent" + android:paddingLeft="8dip" + android:paddingRight="8dip" > <LinearLayout + android:id="@+id/intruder_alert_content" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingLeft="6dip" + android:gravity="center" android:animationCache="false" android:orientation="horizontal" + android:background="@drawable/alert_bar_background" + android:clickable="true" + android:focusable="true" + android:descendantFocusability="afterDescendants" > <ImageView android:id="@+id/alertIcon" android:layout_width="25dip" android:layout_height="25dip" + android:layout_marginLeft="6dip" android:layout_marginRight="8dip" /> <TextView android:id="@+id/alertText" - android:textAppearance="@*android:style/TextAppearance.StatusBar.EventContent" + android:textAppearance="@style/TextAppearance.StatusBar.IntruderAlert" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 10fa9309b5a7..816f34a418a9 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -22,6 +22,7 @@ <item name="android:textColor">?android:attr/textColorPrimary</item> </style> - - + <style name="TextAppearance.StatusBar.IntruderAlert" + parent="@android:style/TextAppearance.StatusBar"> + </style> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java index 0f6723e06e1b..f45caf51662b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java @@ -23,7 +23,7 @@ import android.widget.LinearLayout; public class CloseDragHandle extends LinearLayout { - PhoneStatusBarService mService; + StatusBarService mService; public CloseDragHandle(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java index c5b901f74b4e..3d85f27519db 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java @@ -27,7 +27,7 @@ import android.util.Slog; public class ExpandedView extends LinearLayout { - PhoneStatusBarService mService; + StatusBarService mService; int mPrevHeight = -1; public ExpandedView(Context context, AttributeSet attrs) { @@ -50,10 +50,10 @@ public class ExpandedView extends LinearLayout { super.onLayout(changed, left, top, right, bottom); int height = bottom - top; if (height != mPrevHeight) { - //Slog.d(PhoneStatusBarService.TAG, "height changed old=" + mPrevHeight + //Slog.d(StatusBarService.TAG, "height changed old=" + mPrevHeight // + " new=" + height); mPrevHeight = height; - mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE); + mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java deleted file mode 100644 index 3c14fb569988..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java +++ /dev/null @@ -1,1544 +0,0 @@ -/* - * 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 com.android.systemui.statusbar; - -import com.android.internal.statusbar.IStatusBar; -import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.statusbar.StatusBarIcon; -import com.android.internal.statusbar.StatusBarNotification; - -import android.app.ActivityManagerNative; -import android.app.Dialog; -import android.app.Notification; -import android.app.PendingIntent; -import android.app.Service; -import android.app.StatusBarManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.Binder; -import android.os.Handler; -import android.os.Message; -import android.os.SystemClock; -import android.util.Slog; -import android.view.Display; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.view.WindowManagerImpl; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.RemoteViews; -import android.widget.ScrollView; -import android.widget.TextView; -import android.widget.FrameLayout; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Set; - -import com.android.systemui.R; -import com.android.systemui.statusbar.policy.StatusBarPolicy; - -public class PhoneStatusBarService extends StatusBarService { - static final String TAG = "PhoneStatusBarService"; - static final boolean SPEW = false; - - public static final String ACTION_STATUSBAR_START - = "com.android.internal.policy.statusbar.START"; - - static final int EXPANDED_LEAVE_ALONE = -10000; - static final int EXPANDED_FULL_OPEN = -10001; - - private static final int MSG_ANIMATE = 1000; - private static final int MSG_ANIMATE_REVEAL = 1001; - private static final int MSG_SHOW_INTRUDER = 1002; - private static final int MSG_HIDE_INTRUDER = 1003; - - // will likely move to a resource or other tunable param at some point - private static final int INTRUDER_ALERT_DECAY_MS = 10000; - - private class ExpandedDialog extends Dialog { - ExpandedDialog(Context context) { - super(context, com.android.internal.R.style.Theme_Light_NoTitleBar); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - boolean down = event.getAction() == KeyEvent.ACTION_DOWN; - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_BACK: - if (!down) { - animateCollapse(); - } - return true; - } - return super.dispatchKeyEvent(event); - } - } - - StatusBarPolicy mIconPolicy; - - int mHeight; - int mIconWidth; - - Display mDisplay; - StatusBarView mStatusBarView; - int mPixelFormat; - H mHandler = new H(); - Object mQueueLock = new Object(); - - // icons - LinearLayout mIcons; - IconMerger mNotificationIcons; - LinearLayout mStatusIcons; - - // expanded notifications - Dialog mExpandedDialog; - ExpandedView mExpandedView; - WindowManager.LayoutParams mExpandedParams; - ScrollView mScrollView; - View mNotificationLinearLayout; - View mExpandedContents; - // top bar - TextView mNoNotificationsTitle; - TextView mClearButton; - // drag bar - CloseDragHandle mCloseView; - // ongoing - NotificationData mOngoing = new NotificationData(); - TextView mOngoingTitle; - LinearLayout mOngoingItems; - // latest - NotificationData mLatest = new NotificationData(); - TextView mLatestTitle; - LinearLayout mLatestItems; - // position - int[] mPositionTmp = new int[2]; - boolean mExpanded; - boolean mExpandedVisible; - - // the date view - DateView mDateView; - - // the tracker view - TrackingView mTrackingView; - WindowManager.LayoutParams mTrackingParams; - int mTrackingPosition; // the position of the top of the tracking view. - private boolean mPanelSlightlyVisible; - - // ticker - private Ticker mTicker; - private View mTickerView; - private boolean mTicking; - - // Tracking finger for opening/closing. - int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore - boolean mTracking; - VelocityTracker mVelocityTracker; - - static final int ANIM_FRAME_DURATION = (1000/60); - - boolean mAnimating; - long mCurAnimationTime; - float mDisplayHeight; - float mAnimY; - float mAnimVel; - float mAnimAccel; - long mAnimLastTime; - boolean mAnimatingReveal = false; - int mViewDelta; - int[] mAbsPos = new int[2]; - - // for disabling the status bar - int mDisabled = 0; - - // for immersive activities - private View mIntruderAlertView; - - /** - * Construct the service, add the status bar view to the window manager - */ - @Override - public void onCreate() { - // First set up our views and stuff. - mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); - makeStatusBarView(this); - - // Next, call super.onCreate(), which will populate our views. - super.onCreate(); - - // Lastly, call to the icon policy to install/update all the icons. - mIconPolicy = new StatusBarPolicy(this); - } - - // ================================================================================ - // Constructing the view - // ================================================================================ - private void makeStatusBarView(Context context) { - Resources res = context.getResources(); - - mHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); - mIconWidth = mHeight; - - ExpandedView expanded = (ExpandedView)View.inflate(context, - R.layout.status_bar_expanded, null); - expanded.mService = this; - - mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null); - mIntruderAlertView.setVisibility(View.GONE); - mIntruderAlertView.setClickable(true); - mIntruderAlertView.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - Slog.d(TAG, "Intruder Alert clicked!"); - mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER); - } - }); - - StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null); - sb.mService = this; - - // figure out which pixel-format to use for the status bar. - mPixelFormat = PixelFormat.TRANSLUCENT; - Drawable bg = sb.getBackground(); - if (bg != null) { - mPixelFormat = bg.getOpacity(); - } - - mStatusBarView = sb; - mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons); - mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons); - mIcons = (LinearLayout)sb.findViewById(R.id.icons); - mTickerView = sb.findViewById(R.id.ticker); - mDateView = (DateView)sb.findViewById(R.id.date); - - mExpandedDialog = new ExpandedDialog(context); - mExpandedView = expanded; - mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout); - mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle); - mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems); - mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle); - mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems); - mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle); - mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button); - mClearButton.setOnClickListener(mClearButtonListener); - mScrollView = (ScrollView)expanded.findViewById(R.id.scroll); - mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout); - - mOngoingTitle.setVisibility(View.GONE); - mLatestTitle.setVisibility(View.GONE); - - mTicker = new MyTicker(context, sb); - - TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText); - tickerView.mTicker = mTicker; - - mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null); - mTrackingView.mService = this; - mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close); - mCloseView.mService = this; - - mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); - - // the more notifications icon - StatusBarIconView moreView = new StatusBarIconView(this, "more"); - moreView.set(new StatusBarIcon(null, R.drawable.stat_notify_more, 0)); - mNotificationIcons.addMoreView(moreView, - new LinearLayout.LayoutParams(mIconWidth, mHeight)); - - // set the inital view visibility - setAreThereNotifications(); - mDateView.setVisibility(View.INVISIBLE); - - // receive broadcasts - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - filter.addAction(Intent.ACTION_SCREEN_OFF); - context.registerReceiver(mBroadcastReceiver, filter); - } - - @Override - protected void addStatusBarView() { - final StatusBarView view = mStatusBarView; - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - mHeight, - WindowManager.LayoutParams.TYPE_STATUS_BAR, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING, - PixelFormat.RGBX_8888); - lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; - lp.setTitle("StatusBar"); - // TODO lp.windowAnimations = R.style.Animation_StatusBar; - - WindowManagerImpl.getDefault().addView(view, lp); - - lp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - mHeight, - WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, - WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, - PixelFormat.TRANSLUCENT); - lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; - lp.y += mHeight * 1.5; // for now - lp.setTitle("IntruderAlert"); - lp.windowAnimations = android.R.style.Animation_Dialog; - - WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp); - } - - public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { - Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex - + " icon=" + icon); - StatusBarIconView view = new StatusBarIconView(this, slot); - view.set(icon); - mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconWidth, mHeight)); - } - - public void updateIcon(String slot, int index, int viewIndex, - StatusBarIcon old, StatusBarIcon icon) { - Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex - + " old=" + old + " icon=" + icon); - StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex); - view.set(icon); - } - - public void removeIcon(String slot, int index, int viewIndex) { - Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex); - mStatusIcons.removeViewAt(viewIndex); - } - - public void addNotification(IBinder key, StatusBarNotification notification) { - StatusBarIconView iconView = addNotificationViews(key, notification); - if (iconView == null) return; - - boolean immersive = false; - try { - immersive = ActivityManagerNative.getDefault().isTopActivityImmersive(); - Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive")); - } catch (RemoteException ex) { - } - if (immersive) { - if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) { - Slog.d(TAG, "Presenting high-priority notification in immersive activity"); - // @@@ special new transient ticker mode - // 1. Populate mIntruderAlertView - - ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon); - TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText); - alertIcon.setImageDrawable(StatusBarIconView.getIcon( - alertIcon.getContext(), - iconView.getStatusBarIcon())); - alertText.setText(notification.notification.tickerText); - - // 2. Animate mIntruderAlertView in - mHandler.removeMessages(MSG_HIDE_INTRUDER); - mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER); - mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS); - - // 3. Set alarm to age the notification off (TODO) - - } - } else if (notification.notification.fullScreenIntent != null) { - // not immersive & a full-screen alert should be shown - Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive; sending fullScreenIntent"); - try { - notification.notification.fullScreenIntent.send(); - } catch (PendingIntent.CanceledException e) { - } - } else { - // usual case: status bar visible & not immersive - - // show the ticker - tick(notification); - } - - // Recalculate the position of the sliding windows and the titles. - setAreThereNotifications(); - updateExpandedViewPos(EXPANDED_LEAVE_ALONE); - } - - public void updateNotification(IBinder key, StatusBarNotification notification) { - Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification); - - NotificationData oldList; - int oldIndex = mOngoing.findEntry(key); - if (oldIndex >= 0) { - oldList = mOngoing; - } else { - oldIndex = mLatest.findEntry(key); - if (oldIndex < 0) { - Slog.w(TAG, "updateNotification for unknown key: " + key); - return; - } - oldList = mLatest; - } - final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex); - final StatusBarNotification oldNotification = oldEntry.notification; - final RemoteViews oldContentView = oldNotification.notification.contentView; - - final RemoteViews contentView = notification.notification.contentView; - - if (false) { - Slog.d(TAG, "old notification: when=" + oldNotification.notification.when - + " ongoing=" + oldNotification.isOngoing() - + " expanded=" + oldEntry.expanded - + " contentView=" + oldContentView); - Slog.d(TAG, "new notification: when=" + notification.notification.when - + " ongoing=" + oldNotification.isOngoing() - + " contentView=" + contentView); - } - - // Can we just reapply the RemoteViews in place? If when didn't change, the order - // didn't change. - if (notification.notification.when == oldNotification.notification.when - && notification.isOngoing() == oldNotification.isOngoing() - && oldEntry.expanded != null - && contentView != null && oldContentView != null - && contentView.getPackage() != null - && oldContentView.getPackage() != null - && oldContentView.getPackage().equals(contentView.getPackage()) - && oldContentView.getLayoutId() == contentView.getLayoutId()) { - Slog.d(TAG, "reusing notification"); - oldEntry.notification = notification; - try { - // Reapply the RemoteViews - contentView.reapply(this, oldEntry.content); - // update the contentIntent - final PendingIntent contentIntent = notification.notification.contentIntent; - if (contentIntent != null) { - oldEntry.content.setOnClickListener(new Launcher(contentIntent, - notification.pkg, notification.tag, notification.id)); - } - // Update the icon. - final StatusBarIcon ic = new StatusBarIcon(notification.pkg, - notification.notification.icon, notification.notification.iconLevel, - notification.notification.number); - if (!oldEntry.icon.set(ic)) { - handleNotificationError(key, notification, "Couldn't update icon: " + ic); - return; - } - } - catch (RuntimeException e) { - // It failed to add cleanly. Log, and remove the view from the panel. - Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e); - removeNotificationViews(key); - addNotificationViews(key, notification); - } - } else { - Slog.d(TAG, "not reusing notification"); - removeNotificationViews(key); - addNotificationViews(key, notification); - } - - // Restart the ticker if it's still running - tick(notification); - - // Recalculate the position of the sliding windows and the titles. - setAreThereNotifications(); - updateExpandedViewPos(EXPANDED_LEAVE_ALONE); - } - - public void removeNotification(IBinder key) { - Slog.d(TAG, "removeNotification key=" + key); - StatusBarNotification old = removeNotificationViews(key); - - if (old != null) { - // Cancel the ticker if it's still running - mTicker.removeEntry(old); - - // Recalculate the position of the sliding windows and the titles. - setAreThereNotifications(); - updateExpandedViewPos(EXPANDED_LEAVE_ALONE); - } - } - - private int chooseIconIndex(boolean isOngoing, int viewIndex) { - final int latestSize = mLatest.size(); - if (isOngoing) { - return latestSize + (mOngoing.size() - viewIndex); - } else { - return latestSize - viewIndex; - } - } - - View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) { - Notification n = notification.notification; - RemoteViews remoteViews = n.contentView; - if (remoteViews == null) { - return null; - } - - // create the row view - LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false); - - // bind the click event to the content area - ViewGroup content = (ViewGroup)row.findViewById(R.id.content); - content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); - content.setOnFocusChangeListener(mFocusChangeListener); - PendingIntent contentIntent = n.contentIntent; - if (contentIntent != null) { - content.setOnClickListener(new Launcher(contentIntent, notification.pkg, - notification.tag, notification.id)); - } - - View expanded = null; - Exception exception = null; - try { - expanded = remoteViews.apply(this, content); - } - catch (RuntimeException e) { - exception = e; - } - if (expanded == null) { - String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id); - Slog.e(TAG, "couldn't inflate view for notification " + ident); - return null; - } else { - content.addView(expanded); - row.setDrawingCacheEnabled(true); - } - - return new View[] { row, content, expanded }; - } - - StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) { - NotificationData list; - ViewGroup parent; - final boolean isOngoing = notification.isOngoing(); - if (isOngoing) { - list = mOngoing; - parent = mOngoingItems; - } else { - list = mLatest; - parent = mLatestItems; - } - // Construct the expanded view. - final View[] views = makeNotificationView(notification, parent); - if (views == null) { - handleNotificationError(key, notification, "Couldn't expand RemoteViews for: " - + notification); - return null; - } - final View row = views[0]; - final View content = views[1]; - final View expanded = views[2]; - // Construct the icon. - final StatusBarIconView iconView = new StatusBarIconView(this, - notification.pkg + "/0x" + Integer.toHexString(notification.id)); - final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon, - notification.notification.iconLevel, notification.notification.number); - if (!iconView.set(ic)) { - handleNotificationError(key, notification, "Coulding create icon: " + ic); - return null; - } - // Add the expanded view. - final int viewIndex = list.add(key, notification, row, content, expanded, iconView); - parent.addView(row, viewIndex); - // Add the icon. - final int iconIndex = chooseIconIndex(isOngoing, viewIndex); - mNotificationIcons.addView(iconView, iconIndex, - new LinearLayout.LayoutParams(mIconWidth, mHeight)); - - return iconView; - } - - StatusBarNotification removeNotificationViews(IBinder key) { - NotificationData.Entry entry = mOngoing.remove(key); - if (entry == null) { - entry = mLatest.remove(key); - if (entry == null) { - Slog.w(TAG, "removeNotification for unknown key: " + key); - return null; - } - } - // Remove the expanded view. - ((ViewGroup)entry.row.getParent()).removeView(entry.row); - // Remove the icon. - ((ViewGroup)entry.icon.getParent()).removeView(entry.icon); - - return entry.notification; - } - - private void setAreThereNotifications() { - boolean ongoing = mOngoing.hasVisibleItems(); - boolean latest = mLatest.hasVisibleItems(); - - // (no ongoing notifications are clearable) - if (mLatest.hasClearableItems()) { - mClearButton.setVisibility(View.VISIBLE); - } else { - mClearButton.setVisibility(View.INVISIBLE); - } - - mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE); - mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE); - - if (ongoing || latest) { - mNoNotificationsTitle.setVisibility(View.GONE); - } else { - mNoNotificationsTitle.setVisibility(View.VISIBLE); - } - } - - - /** - * State is one or more of the DISABLE constants from StatusBarManager. - */ - public void disable(int state) { - final int old = mDisabled; - final int diff = state ^ old; - mDisabled = state; - - if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { - if ((state & StatusBarManager.DISABLE_EXPAND) != 0) { - Slog.d(TAG, "DISABLE_EXPAND: yes"); - animateCollapse(); - } - } - if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { - if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { - Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); - if (mTicking) { - mTicker.halt(); - } else { - setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out); - } - } else { - Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no"); - if (!mExpandedVisible) { - setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); - } - } - } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { - if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { - Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes"); - mTicker.halt(); - } - } - } - - /** - * All changes to the status bar and notifications funnel through here and are batched. - */ - private class H extends Handler { - public void handleMessage(Message m) { - switch (m.what) { - case MSG_ANIMATE: - doAnimation(); - break; - case MSG_ANIMATE_REVEAL: - doRevealAnimation(); - break; - case MSG_SHOW_INTRUDER: - setIntruderAlertVisibility(true); - break; - case MSG_HIDE_INTRUDER: - setIntruderAlertVisibility(false); - break; - } - } - } - - View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() { - public void onFocusChange(View v, boolean hasFocus) { - // Because 'v' is a ViewGroup, all its children will be (un)selected - // too, which allows marqueeing to work. - v.setSelected(hasFocus); - } - }; - - private void makeExpandedVisible() { - if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); - if (mExpandedVisible) { - return; - } - mExpandedVisible = true; - visibilityChanged(true); - - updateExpandedViewPos(EXPANDED_LEAVE_ALONE); - mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; - mExpandedDialog.getWindow().setAttributes(mExpandedParams); - mExpandedView.requestFocus(View.FOCUS_FORWARD); - mTrackingView.setVisibility(View.VISIBLE); - - if (!mTicking) { - setDateViewVisibility(true, com.android.internal.R.anim.fade_in); - } - } - - public void animateExpand() { - if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded); - if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { - return ; - } - if (mExpanded) { - return; - } - - prepareTracking(0, true); - performFling(0, 2000.0f, true); - } - - public void animateCollapse() { - if (SPEW) { - Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded - + " mExpandedVisible=" + mExpandedVisible - + " mExpanded=" + mExpanded - + " mAnimating=" + mAnimating - + " mAnimY=" + mAnimY - + " mAnimVel=" + mAnimVel); - } - - if (!mExpandedVisible) { - return; - } - - int y; - if (mAnimating) { - y = (int)mAnimY; - } else { - y = mDisplay.getHeight()-1; - } - // Let the fling think that we're open so it goes in the right direction - // and doesn't try to re-open the windowshade. - mExpanded = true; - prepareTracking(y, false); - performFling(y, -2000.0f, true); - } - - void performExpand() { - if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded); - if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { - return ; - } - if (mExpanded) { - return; - } - - mExpanded = true; - makeExpandedVisible(); - updateExpandedViewPos(EXPANDED_FULL_OPEN); - - if (false) postStartTracing(); - } - - void performCollapse() { - if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded - + " mExpandedVisible=" + mExpandedVisible); - - if (!mExpandedVisible) { - return; - } - mExpandedVisible = false; - visibilityChanged(false); - mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; - mExpandedDialog.getWindow().setAttributes(mExpandedParams); - mTrackingView.setVisibility(View.GONE); - - if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) { - setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); - } - setDateViewVisibility(false, com.android.internal.R.anim.fade_out); - - if (!mExpanded) { - return; - } - mExpanded = false; - } - - void doAnimation() { - if (mAnimating) { - if (SPEW) Slog.d(TAG, "doAnimation"); - if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY); - incrementAnim(); - if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY); - if (mAnimY >= mDisplay.getHeight()-1) { - if (SPEW) Slog.d(TAG, "Animation completed to expanded state."); - mAnimating = false; - updateExpandedViewPos(EXPANDED_FULL_OPEN); - performExpand(); - } - else if (mAnimY < mStatusBarView.getHeight()) { - if (SPEW) Slog.d(TAG, "Animation completed to collapsed state."); - mAnimating = false; - updateExpandedViewPos(0); - performCollapse(); - } - else { - updateExpandedViewPos((int)mAnimY); - mCurAnimationTime += ANIM_FRAME_DURATION; - mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime); - } - } - } - - void stopTracking() { - mTracking = false; - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - - void incrementAnim() { - long now = SystemClock.uptimeMillis(); - float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s - final float y = mAnimY; - final float v = mAnimVel; // px/s - final float a = mAnimAccel; // px/s/s - mAnimY = y + (v*t) + (0.5f*a*t*t); // px - mAnimVel = v + (a*t); // px/s - mAnimLastTime = now; // ms - //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY - // + " mAnimAccel=" + mAnimAccel); - } - - void doRevealAnimation() { - final int h = mCloseView.getHeight() + mStatusBarView.getHeight(); - if (mAnimatingReveal && mAnimating && mAnimY < h) { - incrementAnim(); - if (mAnimY >= h) { - mAnimY = h; - updateExpandedViewPos((int)mAnimY); - } else { - updateExpandedViewPos((int)mAnimY); - mCurAnimationTime += ANIM_FRAME_DURATION; - mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL), - mCurAnimationTime); - } - } - } - - void prepareTracking(int y, boolean opening) { - mTracking = true; - mVelocityTracker = VelocityTracker.obtain(); - if (opening) { - mAnimAccel = 2000.0f; - mAnimVel = 200; - mAnimY = mStatusBarView.getHeight(); - updateExpandedViewPos((int)mAnimY); - mAnimating = true; - mAnimatingReveal = true; - mHandler.removeMessages(MSG_ANIMATE); - mHandler.removeMessages(MSG_ANIMATE_REVEAL); - long now = SystemClock.uptimeMillis(); - mAnimLastTime = now; - mCurAnimationTime = now + ANIM_FRAME_DURATION; - mAnimating = true; - mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL), - mCurAnimationTime); - makeExpandedVisible(); - } else { - // it's open, close it? - if (mAnimating) { - mAnimating = false; - mHandler.removeMessages(MSG_ANIMATE); - } - updateExpandedViewPos(y + mViewDelta); - } - } - - void performFling(int y, float vel, boolean always) { - mAnimatingReveal = false; - mDisplayHeight = mDisplay.getHeight(); - - mAnimY = y; - mAnimVel = vel; - - //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel); - - if (mExpanded) { - if (!always && ( - vel > 200.0f - || (y > (mDisplayHeight-25) && vel > -200.0f))) { - // We are expanded, but they didn't move sufficiently to cause - // us to retract. Animate back to the expanded position. - mAnimAccel = 2000.0f; - if (vel < 0) { - mAnimVel = 0; - } - } - else { - // We are expanded and are now going to animate away. - mAnimAccel = -2000.0f; - if (vel > 0) { - mAnimVel = 0; - } - } - } else { - if (always || ( - vel > 200.0f - || (y > (mDisplayHeight/2) && vel > -200.0f))) { - // We are collapsed, and they moved enough to allow us to - // expand. Animate in the notifications. - mAnimAccel = 2000.0f; - if (vel < 0) { - mAnimVel = 0; - } - } - else { - // We are collapsed, but they didn't move sufficiently to cause - // us to retract. Animate back to the collapsed position. - mAnimAccel = -2000.0f; - if (vel > 0) { - mAnimVel = 0; - } - } - } - //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel - // + " mAnimAccel=" + mAnimAccel); - - long now = SystemClock.uptimeMillis(); - mAnimLastTime = now; - mCurAnimationTime = now + ANIM_FRAME_DURATION; - mAnimating = true; - mHandler.removeMessages(MSG_ANIMATE); - mHandler.removeMessages(MSG_ANIMATE_REVEAL); - mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime); - stopTracking(); - } - - boolean interceptTouchEvent(MotionEvent event) { - if (SPEW) { - Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled=" - + mDisabled); - } - - if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { - return false; - } - - final int statusBarSize = mStatusBarView.getHeight(); - final int hitSize = statusBarSize*2; - if (event.getAction() == MotionEvent.ACTION_DOWN) { - final int y = (int)event.getRawY(); - - if (!mExpanded) { - mViewDelta = statusBarSize - y; - } else { - mTrackingView.getLocationOnScreen(mAbsPos); - mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y; - } - if ((!mExpanded && y < hitSize) || - (mExpanded && y > (mDisplay.getHeight()-hitSize))) { - - // We drop events at the edge of the screen to make the windowshade come - // down by accident less, especially when pushing open a device with a keyboard - // that rotates (like g1 and droid) - int x = (int)event.getRawX(); - final int edgeBorder = mEdgeBorder; - if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) { - prepareTracking(y, !mExpanded);// opening if we're not already fully visible - mVelocityTracker.addMovement(event); - } - } - } else if (mTracking) { - mVelocityTracker.addMovement(event); - final int minY = statusBarSize + mCloseView.getHeight(); - if (event.getAction() == MotionEvent.ACTION_MOVE) { - int y = (int)event.getRawY(); - if (mAnimatingReveal && y < minY) { - // nothing - } else { - mAnimatingReveal = false; - updateExpandedViewPos(y + mViewDelta); - } - } else if (event.getAction() == MotionEvent.ACTION_UP) { - mVelocityTracker.computeCurrentVelocity(1000); - - float yVel = mVelocityTracker.getYVelocity(); - boolean negative = yVel < 0; - - float xVel = mVelocityTracker.getXVelocity(); - if (xVel < 0) { - xVel = -xVel; - } - if (xVel > 150.0f) { - xVel = 150.0f; // limit how much we care about the x axis - } - - float vel = (float)Math.hypot(yVel, xVel); - if (negative) { - vel = -vel; - } - - performFling((int)event.getRawY(), vel, false); - } - - } - return false; - } - - private class Launcher implements View.OnClickListener { - private PendingIntent mIntent; - private String mPkg; - private String mTag; - private int mId; - - Launcher(PendingIntent intent, String pkg, String tag, int id) { - mIntent = intent; - mPkg = pkg; - mTag = tag; - mId = id; - } - - public void onClick(View v) { - try { - // The intent we are sending is for the application, which - // won't have permission to immediately start an activity after - // the user switches to home. We know it is safe to do at this - // point, so make sure new activity switches are now allowed. - ActivityManagerNative.getDefault().resumeAppSwitches(); - } catch (RemoteException e) { - } - int[] pos = new int[2]; - v.getLocationOnScreen(pos); - Intent overlay = new Intent(); - overlay.setSourceBounds( - new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight())); - try { - mIntent.send(PhoneStatusBarService.this, 0, overlay); - } catch (PendingIntent.CanceledException e) { - // the stack trace isn't very helpful here. Just log the exception message. - Slog.w(TAG, "Sending contentIntent failed: " + e); - } - try { - mBarService.onNotificationClick(mPkg, mTag, mId); - } catch (RemoteException ex) { - // system process is dead if we're here. - } - animateCollapse(); - } - } - - private void tick(StatusBarNotification n) { - // Show the ticker if one is requested. Also don't do this - // until status bar window is attached to the window manager, - // because... well, what's the point otherwise? And trying to - // run a ticker without being attached will crash! - if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) { - if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS - | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { - mTicker.addEntry(n); - } - } - } - - /** - * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService - * about the failure. - * - * WARNING: this will call back into us. Don't hold any locks. - */ - void handleNotificationError(IBinder key, StatusBarNotification n, String message) { - removeNotification(key); - try { - mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message); - } catch (RemoteException ex) { - // The end is nigh. - } - } - - private class MyTicker extends Ticker { - MyTicker(Context context, StatusBarView sb) { - super(context, sb); - } - - @Override - void tickerStarting() { - mTicking = true; - mIcons.setVisibility(View.GONE); - mTickerView.setVisibility(View.VISIBLE); - mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null)); - mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null)); - if (mExpandedVisible) { - setDateViewVisibility(false, com.android.internal.R.anim.push_up_out); - } - } - - @Override - void tickerDone() { - mIcons.setVisibility(View.VISIBLE); - mTickerView.setVisibility(View.GONE); - mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null)); - mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, - mTickingDoneListener)); - if (mExpandedVisible) { - setDateViewVisibility(true, com.android.internal.R.anim.push_down_in); - } - } - - void tickerHalting() { - mIcons.setVisibility(View.VISIBLE); - mTickerView.setVisibility(View.GONE); - mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null)); - mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out, - mTickingDoneListener)); - if (mExpandedVisible) { - setDateViewVisibility(true, com.android.internal.R.anim.fade_in); - } - } - } - - Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {; - public void onAnimationEnd(Animation animation) { - mTicking = false; - } - public void onAnimationRepeat(Animation animation) { - } - public void onAnimationStart(Animation animation) { - } - }; - - private Animation loadAnim(int id, Animation.AnimationListener listener) { - Animation anim = AnimationUtils.loadAnimation(PhoneStatusBarService.this, id); - if (listener != null) { - anim.setAnimationListener(listener); - } - return anim; - } - - public String viewInfo(View v) { - return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() - + " " + v.getWidth() + "x" + v.getHeight() + ")"; - } - - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump StatusBar from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - - synchronized (mQueueLock) { - pw.println("Current Status Bar state:"); - pw.println(" mExpanded=" + mExpanded - + ", mExpandedVisible=" + mExpandedVisible); - pw.println(" mTicking=" + mTicking); - pw.println(" mTracking=" + mTracking); - pw.println(" mAnimating=" + mAnimating - + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel - + ", mAnimAccel=" + mAnimAccel); - pw.println(" mCurAnimationTime=" + mCurAnimationTime - + " mAnimLastTime=" + mAnimLastTime); - pw.println(" mDisplayHeight=" + mDisplayHeight - + " mAnimatingReveal=" + mAnimatingReveal - + " mViewDelta=" + mViewDelta); - pw.println(" mDisplayHeight=" + mDisplayHeight); - pw.println(" mExpandedParams: " + mExpandedParams); - pw.println(" mExpandedView: " + viewInfo(mExpandedView)); - pw.println(" mExpandedDialog: " + mExpandedDialog); - pw.println(" mTrackingParams: " + mTrackingParams); - pw.println(" mTrackingView: " + viewInfo(mTrackingView)); - pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle)); - pw.println(" mOngoingItems: " + viewInfo(mOngoingItems)); - pw.println(" mLatestTitle: " + viewInfo(mLatestTitle)); - pw.println(" mLatestItems: " + viewInfo(mLatestItems)); - pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle)); - pw.println(" mCloseView: " + viewInfo(mCloseView)); - pw.println(" mTickerView: " + viewInfo(mTickerView)); - pw.println(" mScrollView: " + viewInfo(mScrollView) - + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY()); - pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout)); - } - /* - synchronized (mNotificationData) { - int N = mNotificationData.ongoingCount(); - pw.println(" ongoingCount.size=" + N); - for (int i=0; i<N; i++) { - StatusBarNotification n = mNotificationData.getOngoing(i); - pw.println(" [" + i + "] key=" + n.key + " view=" + n.view); - pw.println(" data=" + n.data); - } - N = mNotificationData.latestCount(); - pw.println(" ongoingCount.size=" + N); - for (int i=0; i<N; i++) { - StatusBarNotification n = mNotificationData.getLatest(i); - pw.println(" [" + i + "] key=" + n.key + " view=" + n.view); - pw.println(" data=" + n.data); - } - } - */ - - if (false) { - pw.println("see the logcat for a dump of the views we have created."); - // must happen on ui thread - mHandler.post(new Runnable() { - public void run() { - mStatusBarView.getLocationOnScreen(mAbsPos); - Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] - + ") " + mStatusBarView.getWidth() + "x" - + mStatusBarView.getHeight()); - mStatusBarView.debug(); - - mExpandedView.getLocationOnScreen(mAbsPos); - Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] - + ") " + mExpandedView.getWidth() + "x" - + mExpandedView.getHeight()); - mExpandedView.debug(); - - mTrackingView.getLocationOnScreen(mAbsPos); - Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] - + ") " + mTrackingView.getWidth() + "x" - + mTrackingView.getHeight()); - mTrackingView.debug(); - } - }); - } - } - - void onBarViewAttached() { - WindowManager.LayoutParams lp; - int pixelFormat; - Drawable bg; - - /// ---------- Tracking View -------------- - pixelFormat = PixelFormat.RGBX_8888; - bg = mTrackingView.getBackground(); - if (bg != null) { - pixelFormat = bg.getOpacity(); - } - - lp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, - WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS - | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, - pixelFormat); -// lp.token = mStatusBarView.getWindowToken(); - lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; - lp.setTitle("TrackingView"); - lp.y = mTrackingPosition; - mTrackingParams = lp; - - WindowManagerImpl.getDefault().addView(mTrackingView, lp); - } - - void onTrackingViewAttached() { - WindowManager.LayoutParams lp; - int pixelFormat; - Drawable bg; - - /// ---------- Expanded View -------------- - pixelFormat = PixelFormat.TRANSLUCENT; - - final int disph = mDisplay.getHeight(); - lp = mExpandedDialog.getWindow().getAttributes(); - lp.width = ViewGroup.LayoutParams.MATCH_PARENT; - lp.height = getExpandedHeight(); - lp.x = 0; - mTrackingPosition = lp.y = -disph; // sufficiently large negative - lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; - lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_DITHER - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - lp.format = pixelFormat; - lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; - lp.setTitle("StatusBarExpanded"); - mExpandedDialog.getWindow().setAttributes(lp); - mExpandedDialog.getWindow().setFormat(pixelFormat); - mExpandedParams = lp; - - mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); - mExpandedDialog.setContentView(mExpandedView, - new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - mExpandedDialog.getWindow().setBackgroundDrawable(null); - mExpandedDialog.show(); - FrameLayout hack = (FrameLayout)mExpandedView.getParent(); - } - - void setDateViewVisibility(boolean visible, int anim) { - mDateView.setUpdates(visible); - mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); - mDateView.startAnimation(loadAnim(anim, null)); - } - - void setNotificationIconVisibility(boolean visible, int anim) { - int old = mNotificationIcons.getVisibility(); - int v = visible ? View.VISIBLE : View.INVISIBLE; - if (old != v) { - mNotificationIcons.setVisibility(v); - mNotificationIcons.startAnimation(loadAnim(anim, null)); - } - } - - void updateExpandedViewPos(int expandedPosition) { - if (SPEW) { - Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition - + " mTrackingParams.y=" + mTrackingParams.y - + " mTrackingPosition=" + mTrackingPosition); - } - - int h = mStatusBarView.getHeight(); - int disph = mDisplay.getHeight(); - - // If the expanded view is not visible, make sure they're still off screen. - // Maybe the view was resized. - if (!mExpandedVisible) { - if (mTrackingView != null) { - mTrackingPosition = -disph; - if (mTrackingParams != null) { - mTrackingParams.y = mTrackingPosition; - WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams); - } - } - if (mExpandedParams != null) { - mExpandedParams.y = -disph; - mExpandedDialog.getWindow().setAttributes(mExpandedParams); - } - return; - } - - // tracking view... - int pos; - if (expandedPosition == EXPANDED_FULL_OPEN) { - pos = h; - } - else if (expandedPosition == EXPANDED_LEAVE_ALONE) { - pos = mTrackingPosition; - } - else { - if (expandedPosition <= disph) { - pos = expandedPosition; - } else { - pos = disph; - } - pos -= disph-h; - } - mTrackingPosition = mTrackingParams.y = pos; - mTrackingParams.height = disph-h; - WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams); - - if (mExpandedParams != null) { - mCloseView.getLocationInWindow(mPositionTmp); - final int closePos = mPositionTmp[1]; - - mExpandedContents.getLocationInWindow(mPositionTmp); - final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight(); - - mExpandedParams.y = pos + mTrackingView.getHeight() - - (mTrackingParams.height-closePos) - contentsBottom; - int max = h; - if (mExpandedParams.y > max) { - mExpandedParams.y = max; - } - int min = mTrackingPosition; - if (mExpandedParams.y < min) { - mExpandedParams.y = min; - } - - boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h; - if (!visible) { - // if the contents aren't visible, move the expanded view way off screen - // because the window itself extends below the content view. - mExpandedParams.y = -disph; - } - mExpandedDialog.getWindow().setAttributes(mExpandedParams); - - // As long as this isn't just a repositioning that's not supposed to affect - // the user's perception of what's showing, call to say that the visibility - // has changed. (Otherwise, someone else will call to do that). - if (expandedPosition != EXPANDED_LEAVE_ALONE) { - Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")"); - visibilityChanged(visible); - } - } - - if (SPEW) { - Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition - + " mTrackingParams.y=" + mTrackingParams.y - + " mTrackingPosition=" + mTrackingPosition - + " mExpandedParams.y=" + mExpandedParams.y - + " mExpandedParams.height=" + mExpandedParams.height); - } - } - - int getExpandedHeight() { - return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight(); - } - - void updateExpandedHeight() { - if (mExpandedView != null) { - mExpandedParams.height = getExpandedHeight(); - mExpandedDialog.getWindow().setAttributes(mExpandedParams); - } - } - - /** - * The LEDs are turned o)ff when the notification panel is shown, even just a little bit. - * This was added last-minute and is inconsistent with the way the rest of the notifications - * are handled, because the notification isn't really cancelled. The lights are just - * turned off. If any other notifications happen, the lights will turn back on. Steve says - * this is what he wants. (see bug 1131461) - */ - void visibilityChanged(boolean visible) { - if (mPanelSlightlyVisible != visible) { - mPanelSlightlyVisible = visible; - try { - mBarService.onPanelRevealed(); - } catch (RemoteException ex) { - // Won't fail unless the world has ended. - } - } - } - - void performDisableActions(int net) { - int old = mDisabled; - int diff = net ^ old; - mDisabled = net; - - // act accordingly - if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { - if ((net & StatusBarManager.DISABLE_EXPAND) != 0) { - Slog.d(TAG, "DISABLE_EXPAND: yes"); - animateCollapse(); - } - } - if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { - if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { - Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); - if (mTicking) { - mNotificationIcons.setVisibility(View.INVISIBLE); - mTicker.halt(); - } else { - setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out); - } - } else { - Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no"); - if (!mExpandedVisible) { - setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); - } - } - } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { - if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { - mTicker.halt(); - } - } - } - - private View.OnClickListener mClearButtonListener = new View.OnClickListener() { - public void onClick(View v) { - try { - mBarService.onClearAllNotifications(); - } catch (RemoteException ex) { - // system process is dead if we're here. - } - animateCollapse(); - } - }; - - private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) - || Intent.ACTION_SCREEN_OFF.equals(action)) { - //collapse(); - } - else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { - updateResources(); - } - } - }; - - private void setIntruderAlertVisibility(boolean vis) { - mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE); - } - - /** - * Reload some of our resources when the configuration changes. - * - * We don't reload everything when the configuration changes -- we probably - * should, but getting that smooth is tough. Someday we'll fix that. In the - * meantime, just update the things that we know change. - */ - void updateResources() { - Resources res = getResources(); - - mClearButton.setText(getText(R.string.status_bar_clear_all_button)); - mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title)); - mLatestTitle.setText(getText(R.string.status_bar_latest_events_title)); - mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title)); - - mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); - - if (false) Slog.v(TAG, "updateResources"); - } - - // - // tracing - // - - void postStartTracing() { - mHandler.postDelayed(mStartTracing, 3000); - } - - void vibrate() { - android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE); - vib.vibrate(250); - } - - Runnable mStartTracing = new Runnable() { - public void run() { - vibrate(); - SystemClock.sleep(250); - Slog.d(TAG, "startTracing"); - android.os.Debug.startMethodTracing("/data/statusbar-traces/trace"); - mHandler.postDelayed(mStopTracing, 10000); - } - }; - - Runnable mStopTracing = new Runnable() { - public void run() { - android.os.Debug.stopMethodTracing(); - Slog.d(TAG, "stopTracing"); - vibrate(); - } - }; -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index bc1e798e9d50..d98bd7dca5cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -65,7 +65,7 @@ public class StatusBarIconView extends AnimatedImageView { if (!iconEquals) { Drawable drawable = getIcon(icon); if (drawable == null) { - Slog.w(PhoneStatusBarService.TAG, "No icon for slot " + mSlot); + Slog.w(StatusBarService.TAG, "No icon for slot " + mSlot); return false; } setImageDrawable(drawable); @@ -99,7 +99,7 @@ public class StatusBarIconView extends AnimatedImageView { try { r = context.getPackageManager().getResourcesForApplication(icon.iconPackage); } catch (PackageManager.NameNotFoundException ex) { - Slog.e(PhoneStatusBarService.TAG, "Icon package not found: " + icon.iconPackage); + Slog.e(StatusBarService.TAG, "Icon package not found: " + icon.iconPackage); return null; } } else { @@ -113,7 +113,7 @@ public class StatusBarIconView extends AnimatedImageView { try { return r.getDrawable(icon.iconId); } catch (RuntimeException e) { - Slog.w(PhoneStatusBarService.TAG, "Icon not found in " + Slog.w(StatusBarService.TAG, "Icon not found in " + (icon.iconPackage != null ? icon.iconId : "<system>") + ": " + Integer.toHexString(icon.iconId)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java index d200886a4539..07bcce7056be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java @@ -17,39 +17,189 @@ package com.android.systemui.statusbar; import android.app.Service; +import com.android.internal.statusbar.IStatusBar; +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.StatusBarIcon; +import com.android.internal.statusbar.StatusBarIconList; +import com.android.internal.statusbar.StatusBarNotification; + +import android.app.ActivityManagerNative; +import android.app.Dialog; +import android.app.Notification; +import android.app.PendingIntent; +import android.app.Service; +import android.app.StatusBarManager; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.res.Resources; import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; +import android.os.Binder; +import android.os.Handler; +import android.os.Message; import android.os.ServiceManager; import android.os.SystemClock; -import android.util.Log; import android.util.Slog; +import android.util.Log; +import android.view.Display; import android.view.Gravity; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.WindowManagerImpl; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RemoteViews; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.FrameLayout; -import com.android.internal.statusbar.IStatusBar; -import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.statusbar.StatusBarIcon; -import com.android.internal.statusbar.StatusBarIconList; -import com.android.internal.statusbar.StatusBarNotification; - -import java.util.Arrays; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Set; + +import com.android.systemui.R; +import com.android.systemui.statusbar.policy.StatusBarPolicy; + + + +public class StatusBarService extends Service implements CommandQueue.Callbacks { + static final String TAG = "StatusBarService"; + static final boolean SPEW = false; -public abstract class StatusBarService extends Service implements CommandQueue.Callbacks { - private static final String TAG = "StatusBarService"; + public static final String ACTION_STATUSBAR_START + = "com.android.internal.policy.statusbar.START"; + + static final int EXPANDED_LEAVE_ALONE = -10000; + static final int EXPANDED_FULL_OPEN = -10001; + + private static final int MSG_ANIMATE = 1000; + private static final int MSG_ANIMATE_REVEAL = 1001; + private static final int MSG_SHOW_INTRUDER = 1002; + private static final int MSG_HIDE_INTRUDER = 1003; + + // will likely move to a resource or other tunable param at some point + private static final int INTRUDER_ALERT_DECAY_MS = 10000; + + StatusBarPolicy mIconPolicy; CommandQueue mCommandQueue; IStatusBarService mBarService; + int mIconSize; + Display mDisplay; + StatusBarView mStatusBarView; + int mPixelFormat; + H mHandler = new H(); + Object mQueueLock = new Object(); + + // icons + LinearLayout mIcons; + IconMerger mNotificationIcons; + LinearLayout mStatusIcons; + + // expanded notifications + Dialog mExpandedDialog; + ExpandedView mExpandedView; + WindowManager.LayoutParams mExpandedParams; + ScrollView mScrollView; + View mNotificationLinearLayout; + View mExpandedContents; + // top bar + TextView mNoNotificationsTitle; + TextView mClearButton; + // drag bar + CloseDragHandle mCloseView; + // ongoing + NotificationData mOngoing = new NotificationData(); + TextView mOngoingTitle; + LinearLayout mOngoingItems; + // latest + NotificationData mLatest = new NotificationData(); + TextView mLatestTitle; + LinearLayout mLatestItems; + // position + int[] mPositionTmp = new int[2]; + boolean mExpanded; + boolean mExpandedVisible; + + // the date view + DateView mDateView; + + // the tracker view + TrackingView mTrackingView; + WindowManager.LayoutParams mTrackingParams; + int mTrackingPosition; // the position of the top of the tracking view. + private boolean mPanelSlightlyVisible; + + // ticker + private Ticker mTicker; + private View mTickerView; + private boolean mTicking; + + // Tracking finger for opening/closing. + int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore + boolean mTracking; + VelocityTracker mVelocityTracker; + + static final int ANIM_FRAME_DURATION = (1000/60); + + boolean mAnimating; + long mCurAnimationTime; + float mDisplayHeight; + float mAnimY; + float mAnimVel; + float mAnimAccel; + long mAnimLastTime; + boolean mAnimatingReveal = false; + int mViewDelta; + int[] mAbsPos = new int[2]; + + // for disabling the status bar + int mDisabled = 0; + + private class ExpandedDialog extends Dialog { + ExpandedDialog(Context context) { + super(context, com.android.internal.R.style.Theme_Light_NoTitleBar); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + boolean down = event.getAction() == KeyEvent.ACTION_DOWN; + switch (event.getKeyCode()) { + case KeyEvent.KEYCODE_BACK: + if (!down) { + animateCollapse(); + } + return true; + } + return super.dispatchKeyEvent(event); + } + } + + @Override public void onCreate() { + // First set up our views and stuff. + mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + makeStatusBarView(this); + // Connect in to the status bar manager service StatusBarIconList iconList = new StatusBarIconList(); ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>(); @@ -87,6 +237,9 @@ public abstract class StatusBarService extends Service implements CommandQueue.C // Put up the view addStatusBarView(); + + // Lastly, call to the icon policy to install/update all the icons. + mIconPolicy = new StatusBarPolicy(this); } @Override @@ -94,6 +247,9 @@ public abstract class StatusBarService extends Service implements CommandQueue.C // we're never destroyed } + // for immersive activities + private View mIntruderAlertView; + /** * Nobody binds to us. */ @@ -102,9 +258,1349 @@ public abstract class StatusBarService extends Service implements CommandQueue.C return null; } + // ================================================================================ + // Constructing the view + // ================================================================================ + private void makeStatusBarView(Context context) { + Resources res = context.getResources(); + + mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); + + ExpandedView expanded = (ExpandedView)View.inflate(context, + R.layout.status_bar_expanded, null); + expanded.mService = this; + + mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null); + mIntruderAlertView.setVisibility(View.GONE); + mIntruderAlertView.setClickable(true); + + StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null); + sb.mService = this; + + // figure out which pixel-format to use for the status bar. + mPixelFormat = PixelFormat.TRANSLUCENT; + Drawable bg = sb.getBackground(); + if (bg != null) { + mPixelFormat = bg.getOpacity(); + } + + mStatusBarView = sb; + mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons); + mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons); + mIcons = (LinearLayout)sb.findViewById(R.id.icons); + mTickerView = sb.findViewById(R.id.ticker); + mDateView = (DateView)sb.findViewById(R.id.date); + + mExpandedDialog = new ExpandedDialog(context); + mExpandedView = expanded; + mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout); + mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle); + mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems); + mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle); + mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems); + mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle); + mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button); + mClearButton.setOnClickListener(mClearButtonListener); + mScrollView = (ScrollView)expanded.findViewById(R.id.scroll); + mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout); + + mOngoingTitle.setVisibility(View.GONE); + mLatestTitle.setVisibility(View.GONE); + + mTicker = new MyTicker(context, sb); + + TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText); + tickerView.mTicker = mTicker; + + mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null); + mTrackingView.mService = this; + mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close); + mCloseView.mService = this; + + mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); + + // the more notifications icon + StatusBarIconView moreView = new StatusBarIconView(this, "more"); + moreView.set(new StatusBarIcon(null, R.drawable.stat_notify_more, 0)); + mNotificationIcons.addMoreView(moreView, + new LinearLayout.LayoutParams(mIconSize, mIconSize)); + + // set the inital view visibility + setAreThereNotifications(); + mDateView.setVisibility(View.INVISIBLE); + + // receive broadcasts + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); + filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + filter.addAction(Intent.ACTION_SCREEN_OFF); + context.registerReceiver(mBroadcastReceiver, filter); + } + + protected void addStatusBarView() { + Resources res = getResources(); + final int height= res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + + final StatusBarView view = mStatusBarView; + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + height, + WindowManager.LayoutParams.TYPE_STATUS_BAR, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING, + PixelFormat.RGBX_8888); + lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; + lp.setTitle("StatusBar"); + // TODO lp.windowAnimations = R.style.Animation_StatusBar; + + WindowManagerImpl.getDefault().addView(view, lp); + + lp = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, + PixelFormat.TRANSLUCENT); + lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; + lp.y += height * 1.5; // FIXME + lp.setTitle("IntruderAlert"); + lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar_IntruderAlert; + + WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp); + } + + public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { + if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex + + " icon=" + icon); + StatusBarIconView view = new StatusBarIconView(this, slot); + view.set(icon); + mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize)); + } + + public void updateIcon(String slot, int index, int viewIndex, + StatusBarIcon old, StatusBarIcon icon) { + if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex + + " old=" + old + " icon=" + icon); + StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex); + view.set(icon); + } + + public void removeIcon(String slot, int index, int viewIndex) { + if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex); + mStatusIcons.removeViewAt(viewIndex); + } + + public void addNotification(IBinder key, StatusBarNotification notification) { + StatusBarIconView iconView = addNotificationViews(key, notification); + if (iconView == null) return; + + boolean immersive = false; + try { + immersive = ActivityManagerNative.getDefault().isTopActivityImmersive(); + Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive")); + } catch (RemoteException ex) { + } + if (immersive) { + if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) { + Slog.d(TAG, "Presenting high-priority notification in immersive activity"); + // @@@ special new transient ticker mode + // 1. Populate mIntruderAlertView + + ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon); + TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText); + alertIcon.setImageDrawable(StatusBarIconView.getIcon( + alertIcon.getContext(), + iconView.getStatusBarIcon())); + alertText.setText(notification.notification.tickerText); + + View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content); + button.setOnClickListener( + new Launcher(notification.notification.contentIntent, + notification.pkg, notification.tag, notification.id)); + + // 2. Animate mIntruderAlertView in + mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER); + + // 3. Set alarm to age the notification off (TODO) + mHandler.removeMessages(MSG_HIDE_INTRUDER); + mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS); + } + } else if (notification.notification.fullScreenIntent != null) { + // not immersive & a full-screen alert should be shown + Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;" + + " sending fullScreenIntent"); + try { + notification.notification.fullScreenIntent.send(); + } catch (PendingIntent.CanceledException e) { + } + } else { + // usual case: status bar visible & not immersive + + // show the ticker + tick(notification); + } + + // Recalculate the position of the sliding windows and the titles. + setAreThereNotifications(); + updateExpandedViewPos(EXPANDED_LEAVE_ALONE); + } + + public void updateNotification(IBinder key, StatusBarNotification notification) { + Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification); + + NotificationData oldList; + int oldIndex = mOngoing.findEntry(key); + if (oldIndex >= 0) { + oldList = mOngoing; + } else { + oldIndex = mLatest.findEntry(key); + if (oldIndex < 0) { + Slog.w(TAG, "updateNotification for unknown key: " + key); + return; + } + oldList = mLatest; + } + final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex); + final StatusBarNotification oldNotification = oldEntry.notification; + final RemoteViews oldContentView = oldNotification.notification.contentView; + + final RemoteViews contentView = notification.notification.contentView; + + if (false) { + Slog.d(TAG, "old notification: when=" + oldNotification.notification.when + + " ongoing=" + oldNotification.isOngoing() + + " expanded=" + oldEntry.expanded + + " contentView=" + oldContentView); + Slog.d(TAG, "new notification: when=" + notification.notification.when + + " ongoing=" + oldNotification.isOngoing() + + " contentView=" + contentView); + } + + // Can we just reapply the RemoteViews in place? If when didn't change, the order + // didn't change. + if (notification.notification.when == oldNotification.notification.when + && notification.isOngoing() == oldNotification.isOngoing() + && oldEntry.expanded != null + && contentView != null && oldContentView != null + && contentView.getPackage() != null + && oldContentView.getPackage() != null + && oldContentView.getPackage().equals(contentView.getPackage()) + && oldContentView.getLayoutId() == contentView.getLayoutId()) { + if (SPEW) Slog.d(TAG, "reusing notification"); + oldEntry.notification = notification; + try { + // Reapply the RemoteViews + contentView.reapply(this, oldEntry.content); + // update the contentIntent + final PendingIntent contentIntent = notification.notification.contentIntent; + if (contentIntent != null) { + oldEntry.content.setOnClickListener(new Launcher(contentIntent, + notification.pkg, notification.tag, notification.id)); + } + // Update the icon. + final StatusBarIcon ic = new StatusBarIcon(notification.pkg, + notification.notification.icon, notification.notification.iconLevel, + notification.notification.number); + if (!oldEntry.icon.set(ic)) { + handleNotificationError(key, notification, "Couldn't update icon: " + ic); + return; + } + } + catch (RuntimeException e) { + // It failed to add cleanly. Log, and remove the view from the panel. + Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e); + removeNotificationViews(key); + addNotificationViews(key, notification); + } + } else { + if (SPEW) Slog.d(TAG, "not reusing notification"); + removeNotificationViews(key); + addNotificationViews(key, notification); + } + + // Restart the ticker if it's still running + tick(notification); + + // Recalculate the position of the sliding windows and the titles. + setAreThereNotifications(); + updateExpandedViewPos(EXPANDED_LEAVE_ALONE); + } + + public void removeNotification(IBinder key) { + if (SPEW) Slog.d(TAG, "removeNotification key=" + key); + StatusBarNotification old = removeNotificationViews(key); + + if (old != null) { + // Cancel the ticker if it's still running + mTicker.removeEntry(old); + + // Recalculate the position of the sliding windows and the titles. + setAreThereNotifications(); + updateExpandedViewPos(EXPANDED_LEAVE_ALONE); + } + } + + private int chooseIconIndex(boolean isOngoing, int viewIndex) { + final int latestSize = mLatest.size(); + if (isOngoing) { + return latestSize + (mOngoing.size() - viewIndex); + } else { + return latestSize - viewIndex; + } + } + + View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) { + Notification n = notification.notification; + RemoteViews remoteViews = n.contentView; + if (remoteViews == null) { + return null; + } + + // create the row view + LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false); + + // bind the click event to the content area + ViewGroup content = (ViewGroup)row.findViewById(R.id.content); + content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); + content.setOnFocusChangeListener(mFocusChangeListener); + PendingIntent contentIntent = n.contentIntent; + if (contentIntent != null) { + content.setOnClickListener(new Launcher(contentIntent, notification.pkg, + notification.tag, notification.id)); + } + + View expanded = null; + Exception exception = null; + try { + expanded = remoteViews.apply(this, content); + } + catch (RuntimeException e) { + exception = e; + } + if (expanded == null) { + String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id); + Slog.e(TAG, "couldn't inflate view for notification " + ident, exception); + return null; + } else { + content.addView(expanded); + row.setDrawingCacheEnabled(true); + } + + return new View[] { row, content, expanded }; + } + + StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) { + NotificationData list; + ViewGroup parent; + final boolean isOngoing = notification.isOngoing(); + if (isOngoing) { + list = mOngoing; + parent = mOngoingItems; + } else { + list = mLatest; + parent = mLatestItems; + } + // Construct the expanded view. + final View[] views = makeNotificationView(notification, parent); + if (views == null) { + handleNotificationError(key, notification, "Couldn't expand RemoteViews for: " + + notification); + return null; + } + final View row = views[0]; + final View content = views[1]; + final View expanded = views[2]; + // Construct the icon. + final StatusBarIconView iconView = new StatusBarIconView(this, + notification.pkg + "/0x" + Integer.toHexString(notification.id)); + final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon, + notification.notification.iconLevel, notification.notification.number); + if (!iconView.set(ic)) { + handleNotificationError(key, notification, "Coulding create icon: " + ic); + return null; + } + // Add the expanded view. + final int viewIndex = list.add(key, notification, row, content, expanded, iconView); + parent.addView(row, viewIndex); + // Add the icon. + final int iconIndex = chooseIconIndex(isOngoing, viewIndex); + mNotificationIcons.addView(iconView, iconIndex, + new LinearLayout.LayoutParams(mIconSize, mIconSize)); + return iconView; + } + + StatusBarNotification removeNotificationViews(IBinder key) { + NotificationData.Entry entry = mOngoing.remove(key); + if (entry == null) { + entry = mLatest.remove(key); + if (entry == null) { + Slog.w(TAG, "removeNotification for unknown key: " + key); + return null; + } + } + // Remove the expanded view. + ((ViewGroup)entry.row.getParent()).removeView(entry.row); + // Remove the icon. + ((ViewGroup)entry.icon.getParent()).removeView(entry.icon); + + return entry.notification; + } + + private void setAreThereNotifications() { + boolean ongoing = mOngoing.hasVisibleItems(); + boolean latest = mLatest.hasVisibleItems(); + + // (no ongoing notifications are clearable) + if (mLatest.hasClearableItems()) { + mClearButton.setVisibility(View.VISIBLE); + } else { + mClearButton.setVisibility(View.INVISIBLE); + } + + mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE); + mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE); + + if (ongoing || latest) { + mNoNotificationsTitle.setVisibility(View.GONE); + } else { + mNoNotificationsTitle.setVisibility(View.VISIBLE); + } + } + + + /** + * State is one or more of the DISABLE constants from StatusBarManager. + */ + public void disable(int state) { + final int old = mDisabled; + final int diff = state ^ old; + mDisabled = state; + + if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { + if ((state & StatusBarManager.DISABLE_EXPAND) != 0) { + Slog.d(TAG, "DISABLE_EXPAND: yes"); + animateCollapse(); + } + } + if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { + if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { + Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); + if (mTicking) { + mTicker.halt(); + } else { + setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out); + } + } else { + Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no"); + if (!mExpandedVisible) { + setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); + } + } + } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { + if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { + Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes"); + mTicker.halt(); + } + } + } + + /** + * All changes to the status bar and notifications funnel through here and are batched. + */ + private class H extends Handler { + public void handleMessage(Message m) { + switch (m.what) { + case MSG_ANIMATE: + doAnimation(); + break; + case MSG_ANIMATE_REVEAL: + doRevealAnimation(); + break; + case MSG_SHOW_INTRUDER: + setIntruderAlertVisibility(true); + break; + case MSG_HIDE_INTRUDER: + setIntruderAlertVisibility(false); + break; + } + } + } + + View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() { + public void onFocusChange(View v, boolean hasFocus) { + // Because 'v' is a ViewGroup, all its children will be (un)selected + // too, which allows marqueeing to work. + v.setSelected(hasFocus); + } + }; + + private void makeExpandedVisible() { + if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); + if (mExpandedVisible) { + return; + } + mExpandedVisible = true; + visibilityChanged(true); + + updateExpandedViewPos(EXPANDED_LEAVE_ALONE); + mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + mExpandedDialog.getWindow().setAttributes(mExpandedParams); + mExpandedView.requestFocus(View.FOCUS_FORWARD); + mTrackingView.setVisibility(View.VISIBLE); + + if (!mTicking) { + setDateViewVisibility(true, com.android.internal.R.anim.fade_in); + } + } + + public void animateExpand() { + if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded); + if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { + return ; + } + if (mExpanded) { + return; + } + + prepareTracking(0, true); + performFling(0, 2000.0f, true); + } + + public void animateCollapse() { + if (SPEW) { + Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded + + " mExpandedVisible=" + mExpandedVisible + + " mExpanded=" + mExpanded + + " mAnimating=" + mAnimating + + " mAnimY=" + mAnimY + + " mAnimVel=" + mAnimVel); + } + + if (!mExpandedVisible) { + return; + } + + int y; + if (mAnimating) { + y = (int)mAnimY; + } else { + y = mDisplay.getHeight()-1; + } + // Let the fling think that we're open so it goes in the right direction + // and doesn't try to re-open the windowshade. + mExpanded = true; + prepareTracking(y, false); + performFling(y, -2000.0f, true); + } + + void performExpand() { + if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded); + if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { + return ; + } + if (mExpanded) { + return; + } + + mExpanded = true; + makeExpandedVisible(); + updateExpandedViewPos(EXPANDED_FULL_OPEN); + + if (false) postStartTracing(); + } + + void performCollapse() { + if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded + + " mExpandedVisible=" + mExpandedVisible); + + if (!mExpandedVisible) { + return; + } + mExpandedVisible = false; + visibilityChanged(false); + mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + mExpandedDialog.getWindow().setAttributes(mExpandedParams); + mTrackingView.setVisibility(View.GONE); + + if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) { + setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); + } + setDateViewVisibility(false, com.android.internal.R.anim.fade_out); + + if (!mExpanded) { + return; + } + mExpanded = false; + } + + void doAnimation() { + if (mAnimating) { + if (SPEW) Slog.d(TAG, "doAnimation"); + if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY); + incrementAnim(); + if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY); + if (mAnimY >= mDisplay.getHeight()-1) { + if (SPEW) Slog.d(TAG, "Animation completed to expanded state."); + mAnimating = false; + updateExpandedViewPos(EXPANDED_FULL_OPEN); + performExpand(); + } + else if (mAnimY < mStatusBarView.getHeight()) { + if (SPEW) Slog.d(TAG, "Animation completed to collapsed state."); + mAnimating = false; + updateExpandedViewPos(0); + performCollapse(); + } + else { + updateExpandedViewPos((int)mAnimY); + mCurAnimationTime += ANIM_FRAME_DURATION; + mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime); + } + } + } + + void stopTracking() { + mTracking = false; + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + + void incrementAnim() { + long now = SystemClock.uptimeMillis(); + float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s + final float y = mAnimY; + final float v = mAnimVel; // px/s + final float a = mAnimAccel; // px/s/s + mAnimY = y + (v*t) + (0.5f*a*t*t); // px + mAnimVel = v + (a*t); // px/s + mAnimLastTime = now; // ms + //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY + // + " mAnimAccel=" + mAnimAccel); + } + + void doRevealAnimation() { + final int h = mCloseView.getHeight() + mStatusBarView.getHeight(); + if (mAnimatingReveal && mAnimating && mAnimY < h) { + incrementAnim(); + if (mAnimY >= h) { + mAnimY = h; + updateExpandedViewPos((int)mAnimY); + } else { + updateExpandedViewPos((int)mAnimY); + mCurAnimationTime += ANIM_FRAME_DURATION; + mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL), + mCurAnimationTime); + } + } + } + + void prepareTracking(int y, boolean opening) { + mTracking = true; + mVelocityTracker = VelocityTracker.obtain(); + if (opening) { + mAnimAccel = 2000.0f; + mAnimVel = 200; + mAnimY = mStatusBarView.getHeight(); + updateExpandedViewPos((int)mAnimY); + mAnimating = true; + mAnimatingReveal = true; + mHandler.removeMessages(MSG_ANIMATE); + mHandler.removeMessages(MSG_ANIMATE_REVEAL); + long now = SystemClock.uptimeMillis(); + mAnimLastTime = now; + mCurAnimationTime = now + ANIM_FRAME_DURATION; + mAnimating = true; + mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL), + mCurAnimationTime); + makeExpandedVisible(); + } else { + // it's open, close it? + if (mAnimating) { + mAnimating = false; + mHandler.removeMessages(MSG_ANIMATE); + } + updateExpandedViewPos(y + mViewDelta); + } + } + + void performFling(int y, float vel, boolean always) { + mAnimatingReveal = false; + mDisplayHeight = mDisplay.getHeight(); + + mAnimY = y; + mAnimVel = vel; + + //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel); + + if (mExpanded) { + if (!always && ( + vel > 200.0f + || (y > (mDisplayHeight-25) && vel > -200.0f))) { + // We are expanded, but they didn't move sufficiently to cause + // us to retract. Animate back to the expanded position. + mAnimAccel = 2000.0f; + if (vel < 0) { + mAnimVel = 0; + } + } + else { + // We are expanded and are now going to animate away. + mAnimAccel = -2000.0f; + if (vel > 0) { + mAnimVel = 0; + } + } + } else { + if (always || ( + vel > 200.0f + || (y > (mDisplayHeight/2) && vel > -200.0f))) { + // We are collapsed, and they moved enough to allow us to + // expand. Animate in the notifications. + mAnimAccel = 2000.0f; + if (vel < 0) { + mAnimVel = 0; + } + } + else { + // We are collapsed, but they didn't move sufficiently to cause + // us to retract. Animate back to the collapsed position. + mAnimAccel = -2000.0f; + if (vel > 0) { + mAnimVel = 0; + } + } + } + //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel + // + " mAnimAccel=" + mAnimAccel); + + long now = SystemClock.uptimeMillis(); + mAnimLastTime = now; + mCurAnimationTime = now + ANIM_FRAME_DURATION; + mAnimating = true; + mHandler.removeMessages(MSG_ANIMATE); + mHandler.removeMessages(MSG_ANIMATE_REVEAL); + mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime); + stopTracking(); + } + + boolean interceptTouchEvent(MotionEvent event) { + if (SPEW) { + Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled=" + + mDisabled); + } + + if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { + return false; + } + + final int statusBarSize = mStatusBarView.getHeight(); + final int hitSize = statusBarSize*2; + if (event.getAction() == MotionEvent.ACTION_DOWN) { + final int y = (int)event.getRawY(); + + if (!mExpanded) { + mViewDelta = statusBarSize - y; + } else { + mTrackingView.getLocationOnScreen(mAbsPos); + mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y; + } + if ((!mExpanded && y < hitSize) || + (mExpanded && y > (mDisplay.getHeight()-hitSize))) { + + // We drop events at the edge of the screen to make the windowshade come + // down by accident less, especially when pushing open a device with a keyboard + // that rotates (like g1 and droid) + int x = (int)event.getRawX(); + final int edgeBorder = mEdgeBorder; + if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) { + prepareTracking(y, !mExpanded);// opening if we're not already fully visible + mVelocityTracker.addMovement(event); + } + } + } else if (mTracking) { + mVelocityTracker.addMovement(event); + final int minY = statusBarSize + mCloseView.getHeight(); + if (event.getAction() == MotionEvent.ACTION_MOVE) { + int y = (int)event.getRawY(); + if (mAnimatingReveal && y < minY) { + // nothing + } else { + mAnimatingReveal = false; + updateExpandedViewPos(y + mViewDelta); + } + } else if (event.getAction() == MotionEvent.ACTION_UP) { + mVelocityTracker.computeCurrentVelocity(1000); + + float yVel = mVelocityTracker.getYVelocity(); + boolean negative = yVel < 0; + + float xVel = mVelocityTracker.getXVelocity(); + if (xVel < 0) { + xVel = -xVel; + } + if (xVel > 150.0f) { + xVel = 150.0f; // limit how much we care about the x axis + } + + float vel = (float)Math.hypot(yVel, xVel); + if (negative) { + vel = -vel; + } + + performFling((int)event.getRawY(), vel, false); + } + + } + return false; + } + + private class Launcher implements View.OnClickListener { + private PendingIntent mIntent; + private String mPkg; + private String mTag; + private int mId; + + Launcher(PendingIntent intent, String pkg, String tag, int id) { + mIntent = intent; + mPkg = pkg; + mTag = tag; + mId = id; + } + + public void onClick(View v) { + try { + // The intent we are sending is for the application, which + // won't have permission to immediately start an activity after + // the user switches to home. We know it is safe to do at this + // point, so make sure new activity switches are now allowed. + ActivityManagerNative.getDefault().resumeAppSwitches(); + } catch (RemoteException e) { + } + + if (mIntent != null) { + int[] pos = new int[2]; + v.getLocationOnScreen(pos); + Intent overlay = new Intent(); + overlay.setSourceBounds( + new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight())); + try { + mIntent.send(StatusBarService.this, 0, overlay); + } catch (PendingIntent.CanceledException e) { + // the stack trace isn't very helpful here. Just log the exception message. + Slog.w(TAG, "Sending contentIntent failed: " + e); + } + } + + try { + mBarService.onNotificationClick(mPkg, mTag, mId); + } catch (RemoteException ex) { + // system process is dead if we're here. + } + + // close the shade if it was open + animateCollapse(); + + // If this click was on the intruder alert, hide that instead + mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER); + } + } + + private void tick(StatusBarNotification n) { + // Show the ticker if one is requested. Also don't do this + // until status bar window is attached to the window manager, + // because... well, what's the point otherwise? And trying to + // run a ticker without being attached will crash! + if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) { + if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS + | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { + mTicker.addEntry(n); + } + } + } + + /** + * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService + * about the failure. + * + * WARNING: this will call back into us. Don't hold any locks. + */ + void handleNotificationError(IBinder key, StatusBarNotification n, String message) { + removeNotification(key); + try { + mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message); + } catch (RemoteException ex) { + // The end is nigh. + } + } + + private class MyTicker extends Ticker { + MyTicker(Context context, StatusBarView sb) { + super(context, sb); + } + + @Override + void tickerStarting() { + mTicking = true; + mIcons.setVisibility(View.GONE); + mTickerView.setVisibility(View.VISIBLE); + mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null)); + mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null)); + if (mExpandedVisible) { + setDateViewVisibility(false, com.android.internal.R.anim.push_up_out); + } + } + + @Override + void tickerDone() { + mIcons.setVisibility(View.VISIBLE); + mTickerView.setVisibility(View.GONE); + mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null)); + mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, + mTickingDoneListener)); + if (mExpandedVisible) { + setDateViewVisibility(true, com.android.internal.R.anim.push_down_in); + } + } + + void tickerHalting() { + mIcons.setVisibility(View.VISIBLE); + mTickerView.setVisibility(View.GONE); + mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null)); + mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out, + mTickingDoneListener)); + if (mExpandedVisible) { + setDateViewVisibility(true, com.android.internal.R.anim.fade_in); + } + } + } + + Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {; + public void onAnimationEnd(Animation animation) { + mTicking = false; + } + public void onAnimationRepeat(Animation animation) { + } + public void onAnimationStart(Animation animation) { + } + }; + + private Animation loadAnim(int id, Animation.AnimationListener listener) { + Animation anim = AnimationUtils.loadAnimation(StatusBarService.this, id); + if (listener != null) { + anim.setAnimationListener(listener); + } + return anim; + } + + public String viewInfo(View v) { + return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() + + " " + v.getWidth() + "x" + v.getHeight() + ")"; + } + + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump StatusBar from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + synchronized (mQueueLock) { + pw.println("Current Status Bar state:"); + pw.println(" mExpanded=" + mExpanded + + ", mExpandedVisible=" + mExpandedVisible); + pw.println(" mTicking=" + mTicking); + pw.println(" mTracking=" + mTracking); + pw.println(" mAnimating=" + mAnimating + + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel + + ", mAnimAccel=" + mAnimAccel); + pw.println(" mCurAnimationTime=" + mCurAnimationTime + + " mAnimLastTime=" + mAnimLastTime); + pw.println(" mDisplayHeight=" + mDisplayHeight + + " mAnimatingReveal=" + mAnimatingReveal + + " mViewDelta=" + mViewDelta); + pw.println(" mDisplayHeight=" + mDisplayHeight); + pw.println(" mExpandedParams: " + mExpandedParams); + pw.println(" mExpandedView: " + viewInfo(mExpandedView)); + pw.println(" mExpandedDialog: " + mExpandedDialog); + pw.println(" mTrackingParams: " + mTrackingParams); + pw.println(" mTrackingView: " + viewInfo(mTrackingView)); + pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle)); + pw.println(" mOngoingItems: " + viewInfo(mOngoingItems)); + pw.println(" mLatestTitle: " + viewInfo(mLatestTitle)); + pw.println(" mLatestItems: " + viewInfo(mLatestItems)); + pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle)); + pw.println(" mCloseView: " + viewInfo(mCloseView)); + pw.println(" mTickerView: " + viewInfo(mTickerView)); + pw.println(" mScrollView: " + viewInfo(mScrollView) + + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY()); + pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout)); + } + /* + synchronized (mNotificationData) { + int N = mNotificationData.ongoingCount(); + pw.println(" ongoingCount.size=" + N); + for (int i=0; i<N; i++) { + StatusBarNotification n = mNotificationData.getOngoing(i); + pw.println(" [" + i + "] key=" + n.key + " view=" + n.view); + pw.println(" data=" + n.data); + } + N = mNotificationData.latestCount(); + pw.println(" ongoingCount.size=" + N); + for (int i=0; i<N; i++) { + StatusBarNotification n = mNotificationData.getLatest(i); + pw.println(" [" + i + "] key=" + n.key + " view=" + n.view); + pw.println(" data=" + n.data); + } + } + */ + + if (false) { + pw.println("see the logcat for a dump of the views we have created."); + // must happen on ui thread + mHandler.post(new Runnable() { + public void run() { + mStatusBarView.getLocationOnScreen(mAbsPos); + Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] + + ") " + mStatusBarView.getWidth() + "x" + + mStatusBarView.getHeight()); + mStatusBarView.debug(); + + mExpandedView.getLocationOnScreen(mAbsPos); + Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] + + ") " + mExpandedView.getWidth() + "x" + + mExpandedView.getHeight()); + mExpandedView.debug(); + + mTrackingView.getLocationOnScreen(mAbsPos); + Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] + + ") " + mTrackingView.getWidth() + "x" + + mTrackingView.getHeight()); + mTrackingView.debug(); + } + }); + } + } + + void onBarViewAttached() { + WindowManager.LayoutParams lp; + int pixelFormat; + Drawable bg; + + /// ---------- Tracking View -------------- + pixelFormat = PixelFormat.RGBX_8888; + bg = mTrackingView.getBackground(); + if (bg != null) { + pixelFormat = bg.getOpacity(); + } + + lp = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, + pixelFormat); +// lp.token = mStatusBarView.getWindowToken(); + lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; + lp.setTitle("TrackingView"); + lp.y = mTrackingPosition; + mTrackingParams = lp; + + WindowManagerImpl.getDefault().addView(mTrackingView, lp); + } + + void onTrackingViewAttached() { + WindowManager.LayoutParams lp; + int pixelFormat; + Drawable bg; + + /// ---------- Expanded View -------------- + pixelFormat = PixelFormat.TRANSLUCENT; + + final int disph = mDisplay.getHeight(); + lp = mExpandedDialog.getWindow().getAttributes(); + lp.width = ViewGroup.LayoutParams.MATCH_PARENT; + lp.height = getExpandedHeight(); + lp.x = 0; + mTrackingPosition = lp.y = -disph; // sufficiently large negative + lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; + lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_DITHER + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + lp.format = pixelFormat; + lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; + lp.setTitle("StatusBarExpanded"); + mExpandedDialog.getWindow().setAttributes(lp); + mExpandedDialog.getWindow().setFormat(pixelFormat); + mExpandedParams = lp; + + mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); + mExpandedDialog.setContentView(mExpandedView, + new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + mExpandedDialog.getWindow().setBackgroundDrawable(null); + mExpandedDialog.show(); + FrameLayout hack = (FrameLayout)mExpandedView.getParent(); + } + + void setDateViewVisibility(boolean visible, int anim) { + mDateView.setUpdates(visible); + mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + mDateView.startAnimation(loadAnim(anim, null)); + } + + void setNotificationIconVisibility(boolean visible, int anim) { + int old = mNotificationIcons.getVisibility(); + int v = visible ? View.VISIBLE : View.INVISIBLE; + if (old != v) { + mNotificationIcons.setVisibility(v); + mNotificationIcons.startAnimation(loadAnim(anim, null)); + } + } + + void updateExpandedViewPos(int expandedPosition) { + if (SPEW) { + Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition + + " mTrackingParams.y=" + mTrackingParams.y + + " mTrackingPosition=" + mTrackingPosition); + } + + int h = mStatusBarView.getHeight(); + int disph = mDisplay.getHeight(); + + // If the expanded view is not visible, make sure they're still off screen. + // Maybe the view was resized. + if (!mExpandedVisible) { + if (mTrackingView != null) { + mTrackingPosition = -disph; + if (mTrackingParams != null) { + mTrackingParams.y = mTrackingPosition; + WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams); + } + } + if (mExpandedParams != null) { + mExpandedParams.y = -disph; + mExpandedDialog.getWindow().setAttributes(mExpandedParams); + } + return; + } + + // tracking view... + int pos; + if (expandedPosition == EXPANDED_FULL_OPEN) { + pos = h; + } + else if (expandedPosition == EXPANDED_LEAVE_ALONE) { + pos = mTrackingPosition; + } + else { + if (expandedPosition <= disph) { + pos = expandedPosition; + } else { + pos = disph; + } + pos -= disph-h; + } + mTrackingPosition = mTrackingParams.y = pos; + mTrackingParams.height = disph-h; + WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams); + + if (mExpandedParams != null) { + mCloseView.getLocationInWindow(mPositionTmp); + final int closePos = mPositionTmp[1]; + + mExpandedContents.getLocationInWindow(mPositionTmp); + final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight(); + + mExpandedParams.y = pos + mTrackingView.getHeight() + - (mTrackingParams.height-closePos) - contentsBottom; + int max = h; + if (mExpandedParams.y > max) { + mExpandedParams.y = max; + } + int min = mTrackingPosition; + if (mExpandedParams.y < min) { + mExpandedParams.y = min; + } + + boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h; + if (!visible) { + // if the contents aren't visible, move the expanded view way off screen + // because the window itself extends below the content view. + mExpandedParams.y = -disph; + } + mExpandedDialog.getWindow().setAttributes(mExpandedParams); + + // As long as this isn't just a repositioning that's not supposed to affect + // the user's perception of what's showing, call to say that the visibility + // has changed. (Otherwise, someone else will call to do that). + if (expandedPosition != EXPANDED_LEAVE_ALONE) { + if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")"); + visibilityChanged(visible); + } + } + + if (SPEW) { + Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition + + " mTrackingParams.y=" + mTrackingParams.y + + " mTrackingPosition=" + mTrackingPosition + + " mExpandedParams.y=" + mExpandedParams.y + + " mExpandedParams.height=" + mExpandedParams.height); + } + } + + int getExpandedHeight() { + return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight(); + } + + void updateExpandedHeight() { + if (mExpandedView != null) { + mExpandedParams.height = getExpandedHeight(); + mExpandedDialog.getWindow().setAttributes(mExpandedParams); + } + } + + /** + * The LEDs are turned o)ff when the notification panel is shown, even just a little bit. + * This was added last-minute and is inconsistent with the way the rest of the notifications + * are handled, because the notification isn't really cancelled. The lights are just + * turned off. If any other notifications happen, the lights will turn back on. Steve says + * this is what he wants. (see bug 1131461) + */ + void visibilityChanged(boolean visible) { + if (mPanelSlightlyVisible != visible) { + mPanelSlightlyVisible = visible; + try { + mBarService.onPanelRevealed(); + } catch (RemoteException ex) { + // Won't fail unless the world has ended. + } + } + } + + void performDisableActions(int net) { + int old = mDisabled; + int diff = net ^ old; + mDisabled = net; + + // act accordingly + if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { + if ((net & StatusBarManager.DISABLE_EXPAND) != 0) { + Slog.d(TAG, "DISABLE_EXPAND: yes"); + animateCollapse(); + } + } + if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { + if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { + Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); + if (mTicking) { + mNotificationIcons.setVisibility(View.INVISIBLE); + mTicker.halt(); + } else { + setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out); + } + } else { + Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no"); + if (!mExpandedVisible) { + setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); + } + } + } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { + if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { + mTicker.halt(); + } + } + } + + private View.OnClickListener mClearButtonListener = new View.OnClickListener() { + public void onClick(View v) { + try { + mBarService.onClearAllNotifications(); + } catch (RemoteException ex) { + // system process is dead if we're here. + } + animateCollapse(); + } + }; + + private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) + || Intent.ACTION_SCREEN_OFF.equals(action)) { + //collapse(); + } + else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { + updateResources(); + } + } + }; + + private void setIntruderAlertVisibility(boolean vis) { + mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE); + } + /** - * Implement this to add the main status bar view. + * Reload some of our resources when the configuration changes. + * + * We don't reload everything when the configuration changes -- we probably + * should, but getting that smooth is tough. Someday we'll fix that. In the + * meantime, just update the things that we know change. */ - protected abstract void addStatusBarView(); + void updateResources() { + Resources res = getResources(); + + mClearButton.setText(getText(R.string.status_bar_clear_all_button)); + mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title)); + mLatestTitle.setText(getText(R.string.status_bar_latest_events_title)); + mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title)); + + mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); + + if (false) Slog.v(TAG, "updateResources"); + } + + // + // tracing + // + + void postStartTracing() { + mHandler.postDelayed(mStartTracing, 3000); + } + + void vibrate() { + android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE); + vib.vibrate(250); + } + + Runnable mStartTracing = new Runnable() { + public void run() { + vibrate(); + SystemClock.sleep(250); + Slog.d(TAG, "startTracing"); + android.os.Debug.startMethodTracing("/data/statusbar-traces/trace"); + mHandler.postDelayed(mStopTracing, 10000); + } + }; + + Runnable mStopTracing = new Runnable() { + public void run() { + android.os.Debug.stopMethodTracing(); + Slog.d(TAG, "stopTracing"); + vibrate(); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java index 466cc751e446..1e140b94c6f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java @@ -34,7 +34,7 @@ public class StatusBarView extends FrameLayout { static final int DIM_ANIM_TIME = 400; - PhoneStatusBarService mService; + StatusBarService mService; boolean mTracking; int mStartX, mStartY; ViewGroup mNotificationIcons; @@ -94,7 +94,7 @@ public class StatusBarView extends FrameLayout { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE); + mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java index c59eb6a29d50..9108eee1814e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java @@ -26,7 +26,7 @@ import android.widget.LinearLayout; public class TrackingView extends LinearLayout { final Display mDisplay; - PhoneStatusBarService mService; + StatusBarService mService; boolean mTracking; int mStartX, mStartY; diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 3e2c1229e53e..25de8b0c49b8 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -38,9 +38,11 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.database.ContentObserver; +import android.hardware.Usb; import android.media.AudioManager; import android.net.Uri; import android.os.BatteryManager; +import android.os.Bundle; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -111,8 +113,6 @@ class NotificationManagerService extends INotificationManager.Stub private boolean mNotificationPulseEnabled; // for adb connected notifications - private boolean mUsbConnected; - private boolean mAdbEnabled = false; private boolean mAdbNotificationShown = false; private Notification mAdbNotification; @@ -346,12 +346,14 @@ class NotificationManagerService extends INotificationManager.Stub mBatteryFull = batteryFull; updateLights(); } - } else if (action.equals(Intent.ACTION_UMS_CONNECTED)) { - mUsbConnected = true; - updateAdbNotification(); - } else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) { - mUsbConnected = false; - updateAdbNotification(); + } else if (action.equals(Usb.ACTION_USB_STATE)) { + Bundle extras = intent.getExtras(); + boolean usbConnected = extras.getBoolean(Usb.USB_CONNECTED); + boolean adbEnabled = (Usb.USB_FUNCTION_ENABLED.equals( + extras.getString(Usb.USB_FUNCTION_ADB))); + updateAdbNotification(usbConnected && adbEnabled); + } else if (action.equals(Usb.ACTION_USB_DISCONNECTED)) { + updateAdbNotification(false); } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED) || action.equals(Intent.ACTION_PACKAGE_RESTARTED) || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) @@ -397,8 +399,6 @@ class NotificationManagerService extends INotificationManager.Stub void observe() { ContentResolver resolver = mContext.getContentResolver(); - resolver.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ADB_ENABLED), false, this); resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.NOTIFICATION_LIGHT_PULSE), false, this); update(); @@ -410,12 +410,6 @@ class NotificationManagerService extends INotificationManager.Stub public void update() { ContentResolver resolver = mContext.getContentResolver(); - boolean adbEnabled = Settings.Secure.getInt(resolver, - Settings.Secure.ADB_ENABLED, 0) != 0; - if (mAdbEnabled != adbEnabled) { - mAdbEnabled = adbEnabled; - updateAdbNotification(); - } boolean pulseEnabled = Settings.System.getInt(resolver, Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0; if (mNotificationPulseEnabled != pulseEnabled) { @@ -464,8 +458,7 @@ class NotificationManagerService extends INotificationManager.Stub // register for battery changed notifications IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_BATTERY_CHANGED); - filter.addAction(Intent.ACTION_UMS_CONNECTED); - filter.addAction(Intent.ACTION_UMS_DISCONNECTED); + filter.addAction(Usb.ACTION_USB_STATE); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); @@ -1137,8 +1130,8 @@ class NotificationManagerService extends INotificationManager.Stub // This is here instead of StatusBarPolicy because it is an important // security feature that we don't want people customizing the platform // to accidentally lose. - private void updateAdbNotification() { - if (mAdbEnabled && mUsbConnected) { + private void updateAdbNotification(boolean adbEnabled) { + if (adbEnabled) { if ("0".equals(SystemProperties.get("persist.adb.notify"))) { return; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 71306361506a..c01680e9005f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -80,7 +80,8 @@ class ServerThread extends Thread { android.os.Process.THREAD_PRIORITY_FOREGROUND); BinderInternal.disableBackgroundScheduling(true); - + android.os.Process.setCanSelfBackground(false); + String factoryTestStr = SystemProperties.get("ro.factorytest"); int factoryTest = "".equals(factoryTestStr) ? SystemServer.FACTORY_TEST_OFF : Integer.parseInt(factoryTestStr); diff --git a/services/java/com/android/server/UsbObserver.java b/services/java/com/android/server/UsbObserver.java index 3993a7f04241..d08fe9b3964b 100644 --- a/services/java/com/android/server/UsbObserver.java +++ b/services/java/com/android/server/UsbObserver.java @@ -159,6 +159,16 @@ class UsbObserver extends UEventObserver { } private final Handler mHandler = new Handler() { + private void addEnabledFunctions(Intent intent) { + // include state of all USB functions in our extras + for (int i = 0; i < mEnabledFunctions.size(); i++) { + intent.putExtra(mEnabledFunctions.get(i), Usb.USB_FUNCTION_ENABLED); + } + for (int i = 0; i < mDisabledFunctions.size(); i++) { + intent.putExtra(mDisabledFunctions.get(i), Usb.USB_FUNCTION_DISABLED); + } + } + @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -174,21 +184,21 @@ class UsbObserver extends UEventObserver { // Send an Intent containing connected/disconnected state // and the enabled/disabled state of all USB functions Intent intent; - if (mUsbConfig != 0) { + boolean usbConnected = (mUsbConfig != 0); + if (usbConnected) { intent = new Intent(Usb.ACTION_USB_CONNECTED); - - // include state of all USB functions in our extras - for (int i = 0; i < mEnabledFunctions.size(); i++) { - intent.putExtra(mEnabledFunctions.get(i), Usb.USB_FUNCTION_ENABLED); - } - for (int i = 0; i < mDisabledFunctions.size(); i++) { - intent.putExtra(mDisabledFunctions.get(i), Usb.USB_FUNCTION_DISABLED); - } + addEnabledFunctions(intent); } else { intent = new Intent(Usb.ACTION_USB_DISCONNECTED); } - - mContext.sendBroadcast(intent, android.Manifest.permission.ACCESS_USB); + mContext.sendBroadcast(intent); + + // send a sticky broadcast for clients interested in both connect and disconnect + intent = new Intent(Usb.ACTION_USB_STATE); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.putExtra(Usb.USB_CONNECTED, usbConnected); + addEnabledFunctions(intent); + mContext.sendStickyBroadcast(intent); } break; } diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index d4133f309338..a7420938445d 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -57,20 +57,10 @@ public class Watchdog extends Thread { static final boolean RECORD_KERNEL_THREADS = true; static final int MONITOR = 2718; - static final int GLOBAL_PSS = 2719; static final int TIME_TO_RESTART = DB ? 15*1000 : 60*1000; static final int TIME_TO_WAIT = TIME_TO_RESTART / 2; - static final int MEMCHECK_DEFAULT_INTERVAL = DB ? 30 : 30*60; // 30 minutes - static final int MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL = DB ? 60 : 2*60*60; // 2 hours - static final int MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD = (DB ? 10:16)*1024*1024; // 16MB - static final int MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD = (DB ? 14:20)*1024*1024; // 20MB - static final int MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD = (DB ? 4:8)*1024*1024; // 8MB - static final int MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD = (DB ? 8:12)*1024*1024; // 12MB - - static final int MEMCHECK_DEFAULT_EXEC_START_TIME = 1*60*60; // 1:00am - static final int MEMCHECK_DEFAULT_EXEC_END_TIME = 5*60*60; // 5:00am static final int MEMCHECK_DEFAULT_MIN_SCREEN_OFF = DB ? 1*60 : 5*60; // 5 minutes static final int MEMCHECK_DEFAULT_MIN_ALARM = DB ? 1*60 : 3*60; // 3 minutes static final int MEMCHECK_DEFAULT_RECHECK_INTERVAL = DB ? 1*60 : 5*60; // 5 minutes @@ -79,14 +69,12 @@ public class Watchdog extends Thread { static final int REBOOT_DEFAULT_START_TIME = 3*60*60; // 3:00am static final int REBOOT_DEFAULT_WINDOW = 60*60; // within 1 hour - static final String CHECKUP_ACTION = "com.android.service.Watchdog.CHECKUP"; static final String REBOOT_ACTION = "com.android.service.Watchdog.REBOOT"; static Watchdog sWatchdog; /* This handler will be used to post message back onto the main thread */ final Handler mHandler; - final Runnable mGlobalPssCollected; final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>(); ContentResolver mResolver; BatteryService mBattery; @@ -97,31 +85,9 @@ public class Watchdog extends Thread { boolean mForceKillSystem; Monitor mCurrentMonitor; - PssRequestor mPhoneReq; int mPhonePid; - int mPhonePss; - - long mLastMemCheckTime = -(MEMCHECK_DEFAULT_INTERVAL*1000); - boolean mHavePss; - long mLastMemCheckRealtime = -(MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL*1000); - boolean mHaveGlobalPss; - final MemMonitor mSystemMemMonitor = new MemMonitor("system", - Settings.Secure.MEMCHECK_SYSTEM_ENABLED, - Settings.Secure.MEMCHECK_SYSTEM_SOFT_THRESHOLD, - MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD, - Settings.Secure.MEMCHECK_SYSTEM_HARD_THRESHOLD, - MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD); - final MemMonitor mPhoneMemMonitor = new MemMonitor("com.android.phone", - Settings.Secure.MEMCHECK_PHONE_ENABLED, - Settings.Secure.MEMCHECK_PHONE_SOFT_THRESHOLD, - MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD, - Settings.Secure.MEMCHECK_PHONE_HARD_THRESHOLD, - MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD); final Calendar mCalendar = Calendar.getInstance(); - long mMemcheckLastTime; - long mMemcheckExecStartTime; - long mMemcheckExecEndTime; int mMinScreenOff = MEMCHECK_DEFAULT_MIN_SCREEN_OFF; int mMinAlarm = MEMCHECK_DEFAULT_MIN_ALARM; boolean mNeedScheduledCheck; @@ -140,126 +106,13 @@ public class Watchdog extends Thread { int mReqRecheckInterval= -1; // >= 0 if a specific recheck interval has been requested /** - * This class monitors the memory in a particular process. - */ - final class MemMonitor { - final String mProcessName; - final String mEnabledSetting; - final String mSoftSetting; - final String mHardSetting; - - int mSoftThreshold; - int mHardThreshold; - boolean mEnabled; - long mLastPss; - - static final int STATE_OK = 0; - static final int STATE_SOFT = 1; - static final int STATE_HARD = 2; - int mState; - - MemMonitor(String processName, String enabledSetting, - String softSetting, int defSoftThreshold, - String hardSetting, int defHardThreshold) { - mProcessName = processName; - mEnabledSetting = enabledSetting; - mSoftSetting = softSetting; - mHardSetting = hardSetting; - mSoftThreshold = defSoftThreshold; - mHardThreshold = defHardThreshold; - } - - void retrieveSettings(ContentResolver resolver) { - mSoftThreshold = Settings.Secure.getInt( - resolver, mSoftSetting, mSoftThreshold); - mHardThreshold = Settings.Secure.getInt( - resolver, mHardSetting, mHardThreshold); - mEnabled = Settings.Secure.getInt( - resolver, mEnabledSetting, 0) != 0; - } - - boolean checkLocked(long curTime, int pid, int pss) { - mLastPss = pss; - if (mLastPss < mSoftThreshold) { - mState = STATE_OK; - } else if (mLastPss < mHardThreshold) { - mState = STATE_SOFT; - } else { - mState = STATE_HARD; - } - EventLog.writeEvent(EventLogTags.WATCHDOG_PROC_PSS, mProcessName, pid, mLastPss); - - if (mState == STATE_OK) { - // Memory is good, don't recover. - return false; - } - - if (mState == STATE_HARD) { - // Memory is really bad, kill right now. - EventLog.writeEvent(EventLogTags.WATCHDOG_HARD_RESET, mProcessName, pid, - mHardThreshold, mLastPss); - return mEnabled; - } - - // It is time to schedule a reset... - // Check if we are currently within the time to kill processes due - // to memory use. - computeMemcheckTimesLocked(curTime); - String skipReason = null; - if (curTime < mMemcheckExecStartTime || curTime > mMemcheckExecEndTime) { - skipReason = "time"; - } else { - skipReason = shouldWeBeBrutalLocked(curTime); - } - EventLog.writeEvent(EventLogTags.WATCHDOG_SOFT_RESET, mProcessName, pid, - mSoftThreshold, mLastPss, skipReason != null ? skipReason : ""); - if (skipReason != null) { - mNeedScheduledCheck = true; - return false; - } - return mEnabled; - } - - void clear() { - mLastPss = 0; - mState = STATE_OK; - } - } - - /** * Used for scheduling monitor callbacks and checking memory usage. */ final class HeartbeatHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { - case GLOBAL_PSS: { - if (mHaveGlobalPss) { - // During the last pass we collected pss information, so - // now it is time to report it. - mHaveGlobalPss = false; - if (localLOGV) Slog.v(TAG, "Received global pss, logging."); - logGlobalMemory(); - } - } break; - case MONITOR: { - if (mHavePss) { - // During the last pass we collected pss information, so - // now it is time to report it. - mHavePss = false; - if (localLOGV) Slog.v(TAG, "Have pss, checking memory."); - checkMemory(); - } - - if (mHaveGlobalPss) { - // During the last pass we collected pss information, so - // now it is time to report it. - mHaveGlobalPss = false; - if (localLOGV) Slog.v(TAG, "Have global pss, logging."); - logGlobalMemory(); - } - long now = SystemClock.uptimeMillis(); // See if we should force a reboot. @@ -274,32 +127,6 @@ public class Watchdog extends Thread { checkReboot(false); } - // See if we should check memory conditions. - long memCheckInterval = Settings.Secure.getLong( - mResolver, Settings.Secure.MEMCHECK_INTERVAL, - MEMCHECK_DEFAULT_INTERVAL) * 1000; - if ((mLastMemCheckTime+memCheckInterval) < now) { - // It is now time to collect pss information. This - // is async so we won't report it now. And to keep - // things simple, we will assume that everyone has - // reported back by the next MONITOR message. - mLastMemCheckTime = now; - if (localLOGV) Slog.v(TAG, "Collecting memory usage."); - collectMemory(); - mHavePss = true; - - long memCheckRealtimeInterval = Settings.Secure.getLong( - mResolver, Settings.Secure.MEMCHECK_LOG_REALTIME_INTERVAL, - MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL) * 1000; - long realtimeNow = SystemClock.elapsedRealtime(); - if ((mLastMemCheckRealtime+memCheckRealtimeInterval) < realtimeNow) { - mLastMemCheckRealtime = realtimeNow; - if (localLOGV) Slog.v(TAG, "Collecting global memory usage."); - collectGlobalMemory(); - mHaveGlobalPss = true; - } - } - final int size = mMonitors.size(); for (int i = 0 ; i < size ; i++) { mCurrentMonitor = mMonitors.get(i); @@ -315,20 +142,6 @@ public class Watchdog extends Thread { } } - final class GlobalPssCollected implements Runnable { - public void run() { - mHandler.sendEmptyMessage(GLOBAL_PSS); - } - } - - final class CheckupReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context c, Intent intent) { - if (localLOGV) Slog.v(TAG, "Alarm went off, checking memory."); - checkMemory(); - } - } - final class RebootReceiver extends BroadcastReceiver { @Override public void onReceive(Context c, Intent intent) { @@ -359,27 +172,6 @@ public class Watchdog extends Thread { void monitor(); } - public interface PssRequestor { - void requestPss(); - } - - public class PssStats { - public int mEmptyPss; - public int mEmptyCount; - public int mBackgroundPss; - public int mBackgroundCount; - public int mServicePss; - public int mServiceCount; - public int mVisiblePss; - public int mVisibleCount; - public int mForegroundPss; - public int mForegroundCount; - - public int mNoPssCount; - - public int mProcDeaths[] = new int[10]; - } - public static Watchdog getInstance() { if (sWatchdog == null) { sWatchdog = new Watchdog(); @@ -391,7 +183,6 @@ public class Watchdog extends Thread { private Watchdog() { super("watchdog"); mHandler = new HeartbeatHandler(); - mGlobalPssCollected = new GlobalPssCollected(); } public void init(Context context, BatteryService battery, @@ -403,11 +194,6 @@ public class Watchdog extends Thread { mAlarm = alarm; mActivity = activity; - context.registerReceiver(new CheckupReceiver(), - new IntentFilter(CHECKUP_ACTION)); - mCheckupIntent = PendingIntent.getBroadcast(context, - 0, new Intent(CHECKUP_ACTION), 0); - context.registerReceiver(new RebootReceiver(), new IntentFilter(REBOOT_ACTION)); mRebootIntent = PendingIntent.getBroadcast(context, @@ -420,20 +206,10 @@ public class Watchdog extends Thread { mBootTime = System.currentTimeMillis(); } - public void processStarted(PssRequestor req, String name, int pid) { + public void processStarted(String name, int pid) { synchronized (this) { if ("com.android.phone".equals(name)) { - mPhoneReq = req; mPhonePid = pid; - mPhonePss = 0; - } - } - } - - public void reportPss(PssRequestor req, String name, int pss) { - synchronized (this) { - if (mPhoneReq == req) { - mPhonePss = pss; } } } @@ -447,152 +223,6 @@ public class Watchdog extends Thread { } } - /** - * Retrieve memory usage information from specific processes being - * monitored. This is an async operation, so must be done before doing - * memory checks. - */ - void collectMemory() { - synchronized (this) { - if (mPhoneReq != null) { - mPhoneReq.requestPss(); - } - } - } - - /** - * Retrieve memory usage over all application processes. This is an - * async operation, so must be done before doing memory checks. - */ - void collectGlobalMemory() { - mActivity.requestPss(mGlobalPssCollected); - } - - /** - * Check memory usage in the system, scheduling kills/reboots as needed. - * This always runs on the mHandler thread. - */ - void checkMemory() { - boolean needScheduledCheck; - long curTime; - long nextTime = 0; - - long recheckInterval = Settings.Secure.getLong( - mResolver, Settings.Secure.MEMCHECK_RECHECK_INTERVAL, - MEMCHECK_DEFAULT_RECHECK_INTERVAL) * 1000; - - mSystemMemMonitor.retrieveSettings(mResolver); - mPhoneMemMonitor.retrieveSettings(mResolver); - retrieveBrutalityAmount(); - - synchronized (this) { - curTime = System.currentTimeMillis(); - mNeedScheduledCheck = false; - - // How is the system doing? - if (mSystemMemMonitor.checkLocked(curTime, Process.myPid(), - (int)Process.getPss(Process.myPid()))) { - // Not good! Time to suicide. - mForceKillSystem = true; - notifyAll(); - return; - } - - // How is the phone process doing? - if (mPhoneReq != null) { - if (mPhoneMemMonitor.checkLocked(curTime, mPhonePid, - mPhonePss)) { - // Just kill the phone process and let it restart. - Slog.i(TAG, "Watchdog is killing the phone process"); - Process.killProcess(mPhonePid); - } - } else { - mPhoneMemMonitor.clear(); - } - - needScheduledCheck = mNeedScheduledCheck; - if (needScheduledCheck) { - // Something is going bad, but now is not a good time to - // tear things down... schedule an alarm to check again soon. - nextTime = curTime + recheckInterval; - if (nextTime < mMemcheckExecStartTime) { - nextTime = mMemcheckExecStartTime; - } else if (nextTime >= mMemcheckExecEndTime){ - // Need to check during next exec time... so that needs - // to be computed. - if (localLOGV) Slog.v(TAG, "Computing next time range"); - computeMemcheckTimesLocked(nextTime); - nextTime = mMemcheckExecStartTime; - } - - if (localLOGV) { - mCalendar.setTimeInMillis(nextTime); - Slog.v(TAG, "Next Alarm Time: " + mCalendar); - } - } - } - - if (needScheduledCheck) { - if (localLOGV) Slog.v(TAG, "Scheduling next memcheck alarm for " - + ((nextTime-curTime)/1000/60) + "m from now"); - mAlarm.remove(mCheckupIntent); - mAlarm.set(AlarmManager.RTC_WAKEUP, nextTime, mCheckupIntent); - } else { - if (localLOGV) Slog.v(TAG, "No need to schedule a memcheck alarm!"); - mAlarm.remove(mCheckupIntent); - } - } - - final PssStats mPssStats = new PssStats(); - final String[] mMemInfoFields = new String[] { - "MemFree:", "Buffers:", "Cached:", - "Active:", "Inactive:", - "AnonPages:", "Mapped:", "Slab:", - "SReclaimable:", "SUnreclaim:", "PageTables:" }; - final long[] mMemInfoSizes = new long[mMemInfoFields.length]; - final String[] mVMStatFields = new String[] { - "pgfree ", "pgactivate ", "pgdeactivate ", - "pgfault ", "pgmajfault " }; - final long[] mVMStatSizes = new long[mVMStatFields.length]; - final long[] mPrevVMStatSizes = new long[mVMStatFields.length]; - long mLastLogGlobalMemoryTime; - - void logGlobalMemory() { - PssStats stats = mPssStats; - mActivity.collectPss(stats); - EventLog.writeEvent(EventLogTags.WATCHDOG_PSS_STATS, - stats.mEmptyPss, stats.mEmptyCount, - stats.mBackgroundPss, stats.mBackgroundCount, - stats.mServicePss, stats.mServiceCount, - stats.mVisiblePss, stats.mVisibleCount, - stats.mForegroundPss, stats.mForegroundCount, - stats.mNoPssCount); - EventLog.writeEvent(EventLogTags.WATCHDOG_PROC_STATS, - stats.mProcDeaths[0], stats.mProcDeaths[1], stats.mProcDeaths[2], - stats.mProcDeaths[3], stats.mProcDeaths[4]); - Process.readProcLines("/proc/meminfo", mMemInfoFields, mMemInfoSizes); - for (int i=0; i<mMemInfoSizes.length; i++) { - mMemInfoSizes[i] *= 1024; - } - EventLog.writeEvent(EventLogTags.WATCHDOG_MEMINFO, - (int)mMemInfoSizes[0], (int)mMemInfoSizes[1], (int)mMemInfoSizes[2], - (int)mMemInfoSizes[3], (int)mMemInfoSizes[4], - (int)mMemInfoSizes[5], (int)mMemInfoSizes[6], (int)mMemInfoSizes[7], - (int)mMemInfoSizes[8], (int)mMemInfoSizes[9], (int)mMemInfoSizes[10]); - long now = SystemClock.uptimeMillis(); - long dur = now - mLastLogGlobalMemoryTime; - mLastLogGlobalMemoryTime = now; - Process.readProcLines("/proc/vmstat", mVMStatFields, mVMStatSizes); - for (int i=0; i<mVMStatSizes.length; i++) { - long v = mVMStatSizes[i]; - mVMStatSizes[i] -= mPrevVMStatSizes[i]; - mPrevVMStatSizes[i] = v; - } - EventLog.writeEvent(EventLogTags.WATCHDOG_VMSTAT, dur, - (int)mVMStatSizes[0], (int)mVMStatSizes[1], (int)mVMStatSizes[2], - (int)mVMStatSizes[3], (int)mVMStatSizes[4]); - } - void checkReboot(boolean fromAlarm) { int rebootInterval = mReqRebootInterval >= 0 ? mReqRebootInterval : Settings.Secure.getInt( @@ -730,47 +360,6 @@ public class Watchdog extends Thread { return null; } - /** - * Compute the times during which we next would like to perform process - * restarts. - * - * @param curTime The current system time. - */ - void computeMemcheckTimesLocked(long curTime) { - if (mMemcheckLastTime == curTime) { - return; - } - - mMemcheckLastTime = curTime; - - long memcheckExecStartTime = Settings.Secure.getLong( - mResolver, Settings.Secure.MEMCHECK_EXEC_START_TIME, - MEMCHECK_DEFAULT_EXEC_START_TIME); - long memcheckExecEndTime = Settings.Secure.getLong( - mResolver, Settings.Secure.MEMCHECK_EXEC_END_TIME, - MEMCHECK_DEFAULT_EXEC_END_TIME); - - mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime, - memcheckExecEndTime); - if (mMemcheckExecEndTime < curTime) { - memcheckExecStartTime += 24*60*60; - memcheckExecEndTime += 24*60*60; - mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime, - memcheckExecEndTime); - } - mMemcheckExecStartTime = computeCalendarTime(mCalendar, curTime, - memcheckExecStartTime); - - if (localLOGV) { - mCalendar.setTimeInMillis(curTime); - Slog.v(TAG, "Current Time: " + mCalendar); - mCalendar.setTimeInMillis(mMemcheckExecStartTime); - Slog.v(TAG, "Start Check Time: " + mCalendar); - mCalendar.setTimeInMillis(mMemcheckExecEndTime); - Slog.v(TAG, "End Check Time: " + mCalendar); - } - } - static long computeCalendarTime(Calendar c, long curTime, long secondsSinceMidnight) { diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index bf86b2301e9b..483f9ebde7e4 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -572,6 +572,7 @@ public class WindowManagerService extends IWindowManager.Stub mHaveInputMethods); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_DISPLAY); + android.os.Process.setCanSelfBackground(false); synchronized (this) { mService = s; @@ -607,6 +608,7 @@ public class WindowManagerService extends IWindowManager.Stub // Log.VERBOSE, "WindowManagerPolicy", Log.LOG_ID_SYSTEM)); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND); + android.os.Process.setCanSelfBackground(false); mPolicy.init(mContext, mService, mPM); synchronized (this) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 2c6806bcace7..93122c460b75 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -880,7 +880,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (localLOGV) Slog.v( TAG, "Death received in " + this + " for thread " + mAppThread.asBinder()); - removeRequestedPss(mApp); synchronized(ActivityManagerService.this) { appDiedLocked(mApp, mPid, mAppThread); } @@ -1260,6 +1259,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND); + android.os.Process.setCanSelfBackground(false); ActivityManagerService m = new ActivityManagerService(); @@ -1785,7 +1785,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen hostingNameStr != null ? hostingNameStr : ""); if (app.persistent) { - Watchdog.getInstance().processStarted(app, app.processName, pid); + Watchdog.getInstance().processStarted(app.processName, pid); } StringBuilder buf = mStringBuilder; @@ -5662,123 +5662,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return killed; } - public void reportPss(IApplicationThread caller, int pss) { - Watchdog.PssRequestor req; - String name; - ProcessRecord callerApp; - synchronized (this) { - if (caller == null) { - return; - } - callerApp = getRecordForAppLocked(caller); - if (callerApp == null) { - return; - } - callerApp.lastPss = pss; - req = callerApp; - name = callerApp.processName; - } - Watchdog.getInstance().reportPss(req, name, pss); - if (!callerApp.persistent) { - removeRequestedPss(callerApp); - } - } - - public void requestPss(Runnable completeCallback) { - ArrayList<ProcessRecord> procs; - synchronized (this) { - mRequestPssCallback = completeCallback; - mRequestPssList.clear(); - for (int i=mLruProcesses.size()-1; i>=0; i--) { - ProcessRecord proc = mLruProcesses.get(i); - if (!proc.persistent) { - mRequestPssList.add(proc); - } - } - procs = new ArrayList<ProcessRecord>(mRequestPssList); - } - - int oldPri = Process.getThreadPriority(Process.myTid()); - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - for (int i=procs.size()-1; i>=0; i--) { - ProcessRecord proc = procs.get(i); - proc.lastPss = 0; - proc.requestPss(); - } - Process.setThreadPriority(oldPri); - } - - void removeRequestedPss(ProcessRecord proc) { - Runnable callback = null; - synchronized (this) { - if (mRequestPssList.remove(proc)) { - if (mRequestPssList.size() == 0) { - callback = mRequestPssCallback; - mRequestPssCallback = null; - } - } - } - - if (callback != null) { - callback.run(); - } - } - - public void collectPss(Watchdog.PssStats stats) { - stats.mEmptyPss = 0; - stats.mEmptyCount = 0; - stats.mBackgroundPss = 0; - stats.mBackgroundCount = 0; - stats.mServicePss = 0; - stats.mServiceCount = 0; - stats.mVisiblePss = 0; - stats.mVisibleCount = 0; - stats.mForegroundPss = 0; - stats.mForegroundCount = 0; - stats.mNoPssCount = 0; - synchronized (this) { - int i; - int NPD = mProcDeaths.length < stats.mProcDeaths.length - ? mProcDeaths.length : stats.mProcDeaths.length; - int aggr = 0; - for (i=0; i<NPD; i++) { - aggr += mProcDeaths[i]; - stats.mProcDeaths[i] = aggr; - } - while (i<stats.mProcDeaths.length) { - stats.mProcDeaths[i] = 0; - i++; - } - - for (i=mLruProcesses.size()-1; i>=0; i--) { - ProcessRecord proc = mLruProcesses.get(i); - if (proc.persistent) { - continue; - } - //Slog.i(TAG, "Proc " + proc + ": pss=" + proc.lastPss); - if (proc.lastPss == 0) { - stats.mNoPssCount++; - continue; - } - if (proc.setAdj >= HIDDEN_APP_MIN_ADJ) { - if (proc.empty) { - stats.mEmptyPss += proc.lastPss; - stats.mEmptyCount++; - } else { - stats.mBackgroundPss += proc.lastPss; - stats.mBackgroundCount++; - } - } else if (proc.setAdj >= VISIBLE_APP_ADJ) { - stats.mVisiblePss += proc.lastPss; - stats.mVisibleCount++; - } else { - stats.mForegroundPss += proc.lastPss; - stats.mForegroundCount++; - } - } - } - } - public final void startRunning(String pkg, String cls, String action, String data) { synchronized(this) { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 18fd9d64d3b3..18b1acbf4484 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -39,7 +39,7 @@ import java.util.HashSet; * Full information about a particular process that * is currently running. */ -class ProcessRecord implements Watchdog.PssRequestor { +class ProcessRecord { final BatteryStatsImpl.Uid.Proc batteryStats; // where to collect runtime statistics final ApplicationInfo info; // all about the first app in the process final String processName; // name of the process @@ -264,16 +264,6 @@ class ProcessRecord implements Watchdog.PssRequestor { } } - public void requestPss() { - IApplicationThread localThread = thread; - if (localThread != null) { - try { - localThread.requestPss(); - } catch (RemoteException e) { - } - } - } - public String toShortString() { if (shortStringName != null) { return shortStringName; diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 0106e6c16eae..d0f856b84f5a 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -374,6 +374,9 @@ private: int32_t identifyTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); + bool interceptKeyBeforeDispatching(const InputTarget& target, + const KeyEvent* keyEvent, uint32_t policyFlags); + void pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType); void pokeUserActivity(nsecs_t eventTime, int32_t eventType); bool checkInjectionPermission(const InputWindow* window, @@ -395,7 +398,7 @@ private: NativeInputManager::NativeInputManager(jobject callbacksObj) : mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), - mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1), + mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0), mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true), mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL), mFocusedApplication(NULL) { @@ -633,8 +636,6 @@ int32_t NativeInputManager::interceptKey(nsecs_t when, } } - // TODO Be smarter about which keys cause us to request interception during dispatch. - actions |= InputReaderPolicyInterface::ACTION_INTERCEPT_DISPATCH; return actions; } @@ -1530,34 +1531,11 @@ int32_t NativeInputManager::waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t windowType = focusedWindow->layoutParamsType; } // release lock - if (policyFlags & POLICY_FLAG_INTERCEPT_DISPATCH) { - const InputTarget& target = outTargets.top(); - - JNIEnv* env = jniEnv(); - - jobject inputChannelObj = getInputChannelObjLocal(env, target.inputChannel); - if (inputChannelObj) { - jboolean consumed = env->CallBooleanMethod(mCallbacksObj, - gCallbacksClassInfo.interceptKeyBeforeDispatching, - inputChannelObj, keyEvent->getKeyCode(), keyEvent->getMetaState(), - keyEvent->getAction() == KEY_EVENT_ACTION_DOWN, - keyEvent->getRepeatCount(), policyFlags); - bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatch"); - - env->DeleteLocalRef(inputChannelObj); - - if (error) { - return INPUT_EVENT_INJECTION_FAILED; - } - - if (consumed) { - outTargets.clear(); - return INPUT_EVENT_INJECTION_SUCCEEDED; - } - } else { - LOGW("Could not apply key dispatch policy because input channel '%s' is " - "no longer valid.", target.inputChannel->getName().string()); - } + 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); @@ -1656,6 +1634,29 @@ int32_t NativeInputManager::identifyTouchEventTargets(MotionEvent* motionEvent, return INPUT_EVENT_INJECTION_SUCCEEDED; } +bool NativeInputManager::interceptKeyBeforeDispatching(const InputTarget& target, + const KeyEvent* keyEvent, uint32_t policyFlags) { + JNIEnv* env = jniEnv(); + + jobject inputChannelObj = getInputChannelObjLocal(env, target.inputChannel); + if (inputChannelObj) { + jboolean consumed = env->CallBooleanMethod(mCallbacksObj, + gCallbacksClassInfo.interceptKeyBeforeDispatching, + inputChannelObj, keyEvent->getKeyCode(), keyEvent->getMetaState(), + keyEvent->getAction() == KEY_EVENT_ACTION_DOWN, + keyEvent->getRepeatCount(), policyFlags); + bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching"); + + env->DeleteLocalRef(inputChannelObj); + + return consumed && ! error; + } else { + LOGW("Could not apply key dispatch policy because input channel '%s' is " + "no longer valid.", target.inputChannel->getName().string()); + return false; + } +} + void NativeInputManager::pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType) { if (windowType != TYPE_KEYGUARD) { nsecs_t eventTime = now(); diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp index 0b41dd8096b2..59d7cdecb984 100755 --- a/services/jni/com_android_server_location_GpsLocationProvider.cpp +++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "GpsLocationProvider" -//#define LOG_NDDEBUG 0 +//#define LOG_NDEBUG 0 #include "JNIHelp.h" #include "jni.h" @@ -66,7 +66,6 @@ static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodNa static void location_callback(GpsLocation* location) { - LOGD("location_callback\n"); JNIEnv* env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod(mCallbacksObj, method_reportLocation, location->flags, (jdouble)location->latitude, (jdouble)location->longitude, @@ -78,16 +77,13 @@ static void location_callback(GpsLocation* location) static void status_callback(GpsStatus* status) { - LOGD("status_callback\n"); JNIEnv* env = AndroidRuntime::getJNIEnv(); - LOGD("env: %p obj: %p\n", env, mCallbacksObj); env->CallVoidMethod(mCallbacksObj, method_reportStatus, status->status); checkAndClearExceptionFromCallback(env, __FUNCTION__); } static void sv_status_callback(GpsSvStatus* sv_status) { - LOGD("sv_status_callback\n"); JNIEnv* env = AndroidRuntime::getJNIEnv(); memcpy(&sGpsSvStatus, sv_status, sizeof(sGpsSvStatus)); env->CallVoidMethod(mCallbacksObj, method_reportSvStatus); @@ -96,7 +92,6 @@ static void sv_status_callback(GpsSvStatus* sv_status) static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length) { - LOGD("nmea_callback\n"); JNIEnv* env = AndroidRuntime::getJNIEnv(); // The Java code will call back to read these values // We do this to avoid creating unnecessary String objects @@ -108,7 +103,6 @@ static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length) static void set_capabilities_callback(uint32_t capabilities) { - LOGD("set_capabilities_callback\n"); JNIEnv* env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod(mCallbacksObj, method_setEngineCapabilities, capabilities); checkAndClearExceptionFromCallback(env, __FUNCTION__); @@ -116,19 +110,16 @@ static void set_capabilities_callback(uint32_t capabilities) static void acquire_wakelock_callback() { - LOGD("acquire_wakelock_callback\n"); acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); } static void release_wakelock_callback() { - LOGD("release_wakelock_callback\n"); release_wake_lock(WAKE_LOCK_NAME); } static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg) { - LOGD("create_thread_callback\n"); return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg); } @@ -146,7 +137,6 @@ GpsCallbacks sGpsCallbacks = { static void xtra_download_request_callback() { - LOGD("xtra_download_request_callback\n"); JNIEnv* env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod(mCallbacksObj, method_xtraDownloadRequest); checkAndClearExceptionFromCallback(env, __FUNCTION__); @@ -159,7 +149,6 @@ GpsXtraCallbacks sGpsXtraCallbacks = { static void agps_status_callback(AGpsStatus* agps_status) { - LOGD("agps_status_callback\n"); JNIEnv* env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus, agps_status->type, agps_status->status); @@ -241,8 +230,6 @@ static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, j static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj) { - LOGD("android_location_GpsLocationProvider_init obj: %p\n", obj); - // this must be set before calling into the HAL library if (!mCallbacksObj) mCallbacksObj = env->NewGlobalRef(obj); 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 000000000000..98155538e364 --- /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 000000000000..402abc75b71d --- /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 8c343b57403a..b665d2f09b4a 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); @@ -104,6 +105,8 @@ public class StatusBarTest extends TestActivity 0, fullScreenIntent, PendingIntent.FLAG_CANCEL_CURRENT); + // if you tap on it you should get the original alert box + not.contentIntent = not.fullScreenIntent; mNotificationManager.notify(id, not); } }, |