summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.xml30
-rw-r--r--camera/libcameraservice/CameraService.cpp1
-rw-r--r--cmds/stagefright/Android.mk2
-rw-r--r--cmds/stagefright/stagefright.cpp37
-rw-r--r--core/java/android/app/Activity.java1
-rw-r--r--core/java/android/app/ActivityManagerNative.java18
-rw-r--r--core/java/android/app/ActivityThread.java8
-rw-r--r--core/java/android/app/ApplicationThreadNative.java15
-rw-r--r--core/java/android/app/IActivityManager.java4
-rw-r--r--core/java/android/app/IApplicationThread.java3
-rw-r--r--core/java/android/app/NativeActivity.java79
-rw-r--r--core/java/android/hardware/Usb.java21
-rw-r--r--core/java/android/net/SSLCertificateSocketFactory.java133
-rw-r--r--core/java/android/os/Process.java9
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java11
-rw-r--r--core/java/android/view/InputTarget.java57
-rwxr-xr-xcore/java/android/view/KeyEvent.java36
-rw-r--r--core/java/android/view/ViewRoot.java9
-rw-r--r--core/java/android/view/WindowManagerPolicy.java2
-rw-r--r--core/java/com/android/internal/os/PowerProfile.java18
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_app_NativeActivity.cpp230
-rw-r--r--core/jni/android_util_Process.cpp39
-rw-r--r--core/jni/android_view_InputQueue.cpp4
-rw-r--r--core/jni/android_view_InputTarget.cpp97
-rw-r--r--core/jni/android_view_KeyEvent.cpp23
-rw-r--r--core/jni/android_view_Surface.cpp3
-rw-r--r--core/jni/android_view_Surface.h (renamed from core/jni/android_view_InputTarget.h)14
-rw-r--r--core/jni/com_google_android_gles_jni_EGLImpl.cpp11
-rw-r--r--core/res/AndroidManifest.xml1
-rw-r--r--core/res/res/anim/priority_alert_enter.xml14
-rw-r--r--core/res/res/anim/priority_alert_exit.xml14
-rw-r--r--core/res/res/drawable-hdpi/presence_audio_away.pngbin1328 -> 1092 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_audio_busy.pngbin1234 -> 1056 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_audio_online.pngbin1434 -> 1075 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_away.pngbin1306 -> 1164 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_busy.pngbin1241 -> 1127 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_invisible.pngbin1094 -> 1062 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_offline.pngbin1340 -> 1273 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_online.pngbin1265 -> 1145 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_video_away.pngbin1328 -> 1022 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_video_busy.pngbin1234 -> 1028 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_video_online.pngbin1434 -> 991 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_audio_away.pngbin954 -> 896 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_audio_busy.pngbin824 -> 905 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_audio_online.pngbin869 -> 919 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_away.pngbin810 -> 797 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_busy.pngbin811 -> 761 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_invisible.pngbin693 -> 739 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_offline.pngbin767 -> 862 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_online.pngbin812 -> 795 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_video_away.pngbin954 -> 749 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_video_busy.pngbin824 -> 699 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_video_online.pngbin869 -> 671 bytes
-rw-r--r--core/res/res/values/config.xml2
-rw-r--r--core/res/res/values/dimens.xml6
-rw-r--r--core/res/res/values/styles.xml7
-rw-r--r--core/res/res/xml/power_profile.xml3
-rw-r--r--core/tests/ConnectivityManagerTest/AndroidManifest.xml10
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java47
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java86
-rw-r--r--docs/html/guide/developing/eclipse-adt.jd5
-rw-r--r--docs/html/guide/developing/other-ide.jd5
-rw-r--r--docs/html/guide/developing/tools/bmgr.jd13
-rw-r--r--docs/html/guide/samples/index.jd98
-rw-r--r--docs/html/guide/topics/data/backup.jd74
-rw-r--r--docs/html/guide/topics/resources/providing-resources.jd2
-rw-r--r--docs/html/guide/topics/ui/menus.jd892
-rw-r--r--docs/html/resources/dashboard/platform-versions.jd78
-rw-r--r--docs/html/resources/dashboard/screens.jd10
-rw-r--r--docs/html/sdk/older_releases.jd42
-rw-r--r--include/media/IMediaRecorder.h4
-rw-r--r--include/media/IMediaRecorderClient.h48
-rw-r--r--include/media/MediaProfiles.h35
-rw-r--r--include/media/MediaRecorderBase.h2
-rw-r--r--include/media/PVMediaRecorder.h4
-rw-r--r--include/media/mediarecorder.h4
-rw-r--r--include/media/stagefright/ColorConverter.h5
-rw-r--r--include/media/stagefright/MediaWriter.h11
-rw-r--r--include/media/stagefright/OMXCodec.h3
-rw-r--r--include/media/stagefright/foundation/ALooper.h5
-rw-r--r--include/media/stagefright/foundation/AMessage.h2
-rw-r--r--include/surfaceflinger/Surface.h16
-rw-r--r--include/ui/FramebufferNativeWindow.h14
-rw-r--r--include/ui/Input.h14
-rw-r--r--include/ui/InputDispatcher.h20
-rw-r--r--include/ui/InputManager.h10
-rw-r--r--include/ui/InputReader.h11
-rw-r--r--include/ui/InputTransport.h8
-rw-r--r--include/ui/egl/android_natives.h42
-rw-r--r--libs/rs/rs.spec2
-rw-r--r--libs/rs/rsContext.cpp4
-rw-r--r--libs/rs/rsContext.h4
-rw-r--r--libs/surfaceflinger/Android.mk5
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp133
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardware.h14
-rw-r--r--libs/surfaceflinger/GLExtensions.cpp133
-rw-r--r--libs/surfaceflinger/GLExtensions.h94
-rw-r--r--libs/surfaceflinger/Layer.cpp41
-rw-r--r--libs/surfaceflinger/Layer.h4
-rw-r--r--libs/surfaceflinger/LayerBlur.cpp6
-rw-r--r--libs/surfaceflinger/LayerBuffer.cpp19
-rw-r--r--libs/surfaceflinger/LayerBuffer.h1
-rw-r--r--libs/surfaceflinger/LayerDim.cpp79
-rw-r--r--libs/surfaceflinger/SurfaceFlinger.cpp14
-rw-r--r--libs/surfaceflinger/TextureManager.cpp68
-rw-r--r--libs/surfaceflinger/TextureManager.h5
-rw-r--r--libs/surfaceflinger_client/Surface.cpp36
-rw-r--r--libs/ui/FramebufferNativeWindow.cpp36
-rw-r--r--libs/ui/GraphicBuffer.cpp12
-rw-r--r--libs/ui/Input.cpp64
-rw-r--r--libs/ui/InputDispatcher.cpp89
-rw-r--r--libs/ui/InputManager.cpp4
-rw-r--r--libs/ui/InputReader.cpp163
-rw-r--r--libs/ui/InputTransport.cpp12
-rw-r--r--libs/ui/PixelFormat.cpp7
-rw-r--r--media/java/android/media/AudioService.java15
-rw-r--r--media/java/android/media/CamcorderProfile.java18
-rw-r--r--media/java/android/media/CameraProfile.java38
-rw-r--r--media/jni/android_media_MediaProfiles.cpp40
-rw-r--r--media/libmedia/Android.mk1
-rw-r--r--media/libmedia/IMediaRecorder.cpp8
-rw-r--r--media/libmedia/IMediaRecorderClient.cpp70
-rw-r--r--media/libmedia/MediaProfiles.cpp81
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.cpp2
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.h2
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.cpp428
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.h12
-rw-r--r--media/libstagefright/MPEG4Writer.cpp107
-rw-r--r--media/libstagefright/OMXCodec.cpp167
-rw-r--r--media/libstagefright/codecs/aacenc/AACEncoder.cpp10
-rw-r--r--media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp10
-rw-r--r--media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp11
-rw-r--r--media/libstagefright/colorconversion/ColorConverter.cpp68
-rw-r--r--media/libstagefright/foundation/ALooper.cpp9
-rw-r--r--media/libstagefright/foundation/AMessage.cpp103
-rw-r--r--native/android/Android.mk3
-rw-r--r--native/android/input.cpp89
-rw-r--r--native/android/native_window.cpp47
-rw-r--r--native/include/android/input.h90
-rw-r--r--native/include/android/native_activity.h76
-rw-r--r--native/include/android/native_window.h75
-rw-r--r--opengl/include/EGL/eglplatform.h5
-rw-r--r--opengl/libagl/egl.cpp10
-rw-r--r--packages/SystemUI/AndroidManifest.xml6
-rw-r--r--packages/SystemUI/res/drawable-hdpi/alert_bar_background_normal.9.pngbin0 -> 1078 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/alert_bar_background_pressed.9.pngbin0 -> 1093 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/alert_bar_background_normal.9.pngbin0 -> 2896 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/alert_bar_background_pressed.9.pngbin0 -> 2896 bytes
-rw-r--r--packages/SystemUI/res/drawable/alert_bar_background.xml23
-rw-r--r--packages/SystemUI/res/layout/intruder_alert.xml58
-rw-r--r--packages/SystemUI/res/values/styles.xml5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java1493
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java1520
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java2
-rw-r--r--services/java/com/android/server/InputApplication.java33
-rw-r--r--services/java/com/android/server/InputManager.java92
-rw-r--r--services/java/com/android/server/InputTargetList.java104
-rw-r--r--services/java/com/android/server/InputWindow.java66
-rw-r--r--services/java/com/android/server/InputWindowList.java89
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java33
-rw-r--r--services/java/com/android/server/SystemServer.java3
-rw-r--r--services/java/com/android/server/UsbObserver.java32
-rw-r--r--services/java/com/android/server/Watchdog.java413
-rw-r--r--services/java/com/android/server/WindowManagerService.java927
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java121
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java12
-rw-r--r--services/jni/com_android_server_InputManager.cpp1371
-rwxr-xr-xservices/jni/com_android_server_location_GpsLocationProvider.cpp15
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java2
175 files changed, 7135 insertions, 4532 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..161161c7d8fe 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -5,10 +5,16 @@ 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.Bundle;
+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 +28,12 @@ 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);
private native void unloadNativeCode(int handle);
private native void onStartNative(int handle);
@@ -32,10 +43,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 +57,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 +90,7 @@ 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());
if (mNativeHandle == 0) {
throw new IllegalArgumentException("Unable to load native library: " + path);
}
@@ -87,6 +99,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 +145,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/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/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 3f5d6cacc089..249ad6279bfe 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -773,8 +773,6 @@ public abstract class WallpaperService extends Service {
if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
if (mInputChannel != null) {
InputQueue.unregisterInputChannel(mInputChannel);
- mInputChannel.dispose();
- mInputChannel = null;
}
}
@@ -783,6 +781,15 @@ public abstract class WallpaperService extends Service {
}
mSurfaceHolder.mSurface.release();
mCreated = false;
+
+ if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+ // Dispose the input channel after removing the window so the Window Manager
+ // doesn't interpret the input channel being closed as an abnormal termination.
+ if (mInputChannel != null) {
+ mInputChannel.dispose();
+ mInputChannel = null;
+ }
+ }
}
}
}
diff --git a/core/java/android/view/InputTarget.java b/core/java/android/view/InputTarget.java
deleted file mode 100644
index 6ff73054d86b..000000000000
--- a/core/java/android/view/InputTarget.java
+++ /dev/null
@@ -1,57 +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 android.view;
-
-/**
- * An input target specifies how an input event is to be dispatched to a particular window
- * including the window's input channel, control flags, a timeout, and an X / Y offset to
- * be added to input event coordinates to compensate for the absolute position of the
- * window area.
- *
- * These parameters are used by the native input dispatching code.
- * @hide
- */
-public final class InputTarget {
- public InputChannel mInputChannel;
- public int mFlags;
- public long mTimeoutNanos;
- public float mXOffset;
- public float mYOffset;
-
- /**
- * This flag indicates that subsequent event delivery should be held until the
- * current event is delivered to this target or a timeout occurs.
- */
- public static int FLAG_SYNC = 0x01;
-
- /**
- * This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of
- * this target and so should instead be delivered as an ACTION_OUTSIDE to this target.
- */
- public static int FLAG_OUTSIDE = 0x02;
-
- /*
- * This flag indicates that a KeyEvent or MotionEvent is being canceled.
- * In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set.
- * In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL.
- */
- public static int FLAG_CANCEL = 0x04;
-
- public void recycle() {
- mInputChannel = null;
- }
-}
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/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 4854190b3e9a..1dc82e8dbdf4 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1762,6 +1762,15 @@ public final class ViewRoot extends Handler implements ViewParent,
sWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
+
+ if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+ // Dispose the input channel after removing the window so the Window Manager
+ // doesn't interpret the input channel being closed as an abnormal termination.
+ if (mInputChannel != null) {
+ mInputChannel.dispose();
+ mInputChannel = null;
+ }
+ }
}
void updateConfiguration(Configuration config, boolean force) {
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 431b7860eec7..be1f6d2860ca 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -83,7 +83,7 @@ public interface WindowManagerPolicy {
* Temporary flag added during the transition to the new native input dispatcher.
* This will be removed when the old input dispatch code is deleted.
*/
- public final static boolean ENABLE_NATIVE_INPUT_DISPATCH = false;
+ public final static boolean ENABLE_NATIVE_INPUT_DISPATCH = true;
// flags for interceptKeyTq
/**
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.mk b/core/jni/Android.mk
index d854e8759370..a008e96401fc 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -47,7 +47,6 @@ LOCAL_SRC_FILES:= \
android_view_ViewRoot.cpp \
android_view_InputChannel.cpp \
android_view_InputQueue.cpp \
- android_view_InputTarget.cpp \
android_view_KeyEvent.cpp \
android_view_MotionEvent.cpp \
android_text_AndroidCharacter.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 3e69c7822540..6fb1369dd80a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -164,7 +164,6 @@ extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env);
extern int register_android_app_NativeActivity(JNIEnv *env);
extern int register_android_view_InputChannel(JNIEnv* env);
extern int register_android_view_InputQueue(JNIEnv* env);
-extern int register_android_view_InputTarget(JNIEnv* env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
@@ -1297,7 +1296,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_app_NativeActivity),
REG_JNI(register_android_view_InputChannel),
REG_JNI(register_android_view_InputQueue),
- REG_JNI(register_android_view_InputTarget),
REG_JNI(register_android_view_KeyEvent),
REG_JNI(register_android_view_MotionEvent),
};
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 5e5e47e0ac57..dab1dba811cb 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,51 @@ 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;
- 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)
{
const char* pathStr = env->GetStringUTFChars(path, NULL);
NativeCode* code = NULL;
@@ -108,16 +214,39 @@ 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);
code->createActivityFunc(&code->activity, NULL, 0);
}
@@ -217,9 +346,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 +359,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 +379,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 +420,7 @@ 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;)I", (void*)loadNativeCode_native },
{ "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
{ "onStartNative", "(I)V", (void*)onStart_native },
{ "onResumeNative", "(I)V", (void*)onResume_native },
@@ -292,23 +429,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_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_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index 63e00d7dfd30..6fb3cf793e33 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -19,10 +19,10 @@
//#define LOG_NDEBUG 0
// Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 1
+#define DEBUG_DISPATCH_CYCLE 0
// Log debug messages about registrations.
-#define DEBUG_REGISTRATION 1
+#define DEBUG_REGISTRATION 0
#include "JNIHelp.h"
diff --git a/core/jni/android_view_InputTarget.cpp b/core/jni/android_view_InputTarget.cpp
deleted file mode 100644
index a0a70546af20..000000000000
--- a/core/jni/android_view_InputTarget.cpp
+++ /dev/null
@@ -1,97 +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.
- */
-
-#define LOG_TAG "InputTarget-JNI"
-
-#include "JNIHelp.h"
-
-#include <utils/Log.h>
-#include <ui/InputDispatcher.h>
-#include <ui/InputTransport.h>
-#include "android_view_InputTarget.h"
-#include "android_view_InputChannel.h"
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static struct {
- jclass clazz;
-
- jfieldID mInputChannel;
- jfieldID mFlags;
- jfieldID mTimeoutNanos;
- jfieldID mXOffset;
- jfieldID mYOffset;
-} gInputTargetClassInfo;
-
-// ----------------------------------------------------------------------------
-
-void android_view_InputTarget_toNative(JNIEnv* env, jobject inputTargetObj,
- InputTarget* outInputTarget) {
- jobject inputChannelObj = env->GetObjectField(inputTargetObj,
- gInputTargetClassInfo.mInputChannel);
- jint flags = env->GetIntField(inputTargetObj,
- gInputTargetClassInfo.mFlags);
- jlong timeoutNanos = env->GetLongField(inputTargetObj,
- gInputTargetClassInfo.mTimeoutNanos);
- jfloat xOffset = env->GetFloatField(inputTargetObj,
- gInputTargetClassInfo.mXOffset);
- jfloat yOffset = env->GetFloatField(inputTargetObj,
- gInputTargetClassInfo.mYOffset);
-
- outInputTarget->inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
- outInputTarget->flags = flags;
- outInputTarget->timeout = timeoutNanos;
- outInputTarget->xOffset = xOffset;
- outInputTarget->yOffset = yOffset;
-
- env->DeleteLocalRef(inputChannelObj);
-}
-
-// ----------------------------------------------------------------------------
-
-#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_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
- var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(! var, "Unable to find field " fieldName);
-
-int register_android_view_InputTarget(JNIEnv* env) {
- FIND_CLASS(gInputTargetClassInfo.clazz, "android/view/InputTarget");
-
- GET_FIELD_ID(gInputTargetClassInfo.mInputChannel, gInputTargetClassInfo.clazz,
- "mInputChannel", "Landroid/view/InputChannel;");
-
- GET_FIELD_ID(gInputTargetClassInfo.mFlags, gInputTargetClassInfo.clazz,
- "mFlags", "I");
-
- GET_FIELD_ID(gInputTargetClassInfo.mTimeoutNanos, gInputTargetClassInfo.clazz,
- "mTimeoutNanos", "J");
-
- GET_FIELD_ID(gInputTargetClassInfo.mXOffset, gInputTargetClassInfo.clazz,
- "mXOffset", "F");
-
- GET_FIELD_ID(gInputTargetClassInfo.mYOffset, gInputTargetClassInfo.clazz,
- "mYOffset", "F");
-
- return 0;
-}
-
-} // namespace android
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_InputTarget.h b/core/jni/android_view_Surface.h
index 9230b1b8f1a8..c37932e74c4f 100644
--- a/core/jni/android_view_InputTarget.h
+++ b/core/jni/android_view_Surface.h
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-#ifndef _ANDROID_VIEW_INPUTTARGET_H
-#define _ANDROID_VIEW_INPUTTARGET_H
+#ifndef _ANDROID_VIEW_SURFACE_H
+#define _ANDROID_VIEW_SURFACE_H
+
+#include <android/native_window.h>
#include "jni.h"
namespace android {
-class InputTarget;
-
-extern void android_view_InputTarget_toNative(JNIEnv* env, jobject inputTargetObj,
- InputTarget* outInputTarget);
+extern sp<ANativeWindow> android_Surface_getNativeWindow(
+ JNIEnv* env, jobject clazz);
} // namespace android
-#endif // _ANDROID_OS_INPUTTARGET_H
+#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/drawable-hdpi/presence_audio_away.png b/core/res/res/drawable-hdpi/presence_audio_away.png
index f18b6c9eac88..fb5e1232bb65 100644
--- a/core/res/res/drawable-hdpi/presence_audio_away.png
+++ b/core/res/res/drawable-hdpi/presence_audio_away.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_audio_busy.png b/core/res/res/drawable-hdpi/presence_audio_busy.png
index ae7b983f4a08..81aeb0161556 100644
--- a/core/res/res/drawable-hdpi/presence_audio_busy.png
+++ b/core/res/res/drawable-hdpi/presence_audio_busy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_audio_online.png b/core/res/res/drawable-hdpi/presence_audio_online.png
index 15e4513c81af..553acbb4841e 100644
--- a/core/res/res/drawable-hdpi/presence_audio_online.png
+++ b/core/res/res/drawable-hdpi/presence_audio_online.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_away.png b/core/res/res/drawable-hdpi/presence_away.png
index d4aa66ba2b15..84b00bbfcc5e 100644
--- a/core/res/res/drawable-hdpi/presence_away.png
+++ b/core/res/res/drawable-hdpi/presence_away.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_busy.png b/core/res/res/drawable-hdpi/presence_busy.png
index 4b278539d527..d77a46396e9f 100644
--- a/core/res/res/drawable-hdpi/presence_busy.png
+++ b/core/res/res/drawable-hdpi/presence_busy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_invisible.png b/core/res/res/drawable-hdpi/presence_invisible.png
index 0e27fbaa6051..0d83e70a7ff1 100644
--- a/core/res/res/drawable-hdpi/presence_invisible.png
+++ b/core/res/res/drawable-hdpi/presence_invisible.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_offline.png b/core/res/res/drawable-hdpi/presence_offline.png
index e511aa4b95bf..2619e9fcf3a3 100644
--- a/core/res/res/drawable-hdpi/presence_offline.png
+++ b/core/res/res/drawable-hdpi/presence_offline.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_online.png b/core/res/res/drawable-hdpi/presence_online.png
index d787d2f78a35..7e846efe467e 100644
--- a/core/res/res/drawable-hdpi/presence_online.png
+++ b/core/res/res/drawable-hdpi/presence_online.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_video_away.png b/core/res/res/drawable-hdpi/presence_video_away.png
index f18b6c9eac88..ab5493b743e9 100644
--- a/core/res/res/drawable-hdpi/presence_video_away.png
+++ b/core/res/res/drawable-hdpi/presence_video_away.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_video_busy.png b/core/res/res/drawable-hdpi/presence_video_busy.png
index ae7b983f4a08..456cdb0e4bd8 100644
--- a/core/res/res/drawable-hdpi/presence_video_busy.png
+++ b/core/res/res/drawable-hdpi/presence_video_busy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_video_online.png b/core/res/res/drawable-hdpi/presence_video_online.png
index 15e4513c81af..bd9f8a420a19 100644
--- a/core/res/res/drawable-hdpi/presence_video_online.png
+++ b/core/res/res/drawable-hdpi/presence_video_online.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_audio_away.png b/core/res/res/drawable-mdpi/presence_audio_away.png
index f67e64e792ec..db96754ae915 100644
--- a/core/res/res/drawable-mdpi/presence_audio_away.png
+++ b/core/res/res/drawable-mdpi/presence_audio_away.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_audio_busy.png b/core/res/res/drawable-mdpi/presence_audio_busy.png
index 0ad991b25b88..0a299481bd53 100644
--- a/core/res/res/drawable-mdpi/presence_audio_busy.png
+++ b/core/res/res/drawable-mdpi/presence_audio_busy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_audio_online.png b/core/res/res/drawable-mdpi/presence_audio_online.png
index 6cc3d1a7eb20..df415ba1ea7b 100644
--- a/core/res/res/drawable-mdpi/presence_audio_online.png
+++ b/core/res/res/drawable-mdpi/presence_audio_online.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_away.png b/core/res/res/drawable-mdpi/presence_away.png
index f8120df43bfe..f98e908f1323 100644
--- a/core/res/res/drawable-mdpi/presence_away.png
+++ b/core/res/res/drawable-mdpi/presence_away.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_busy.png b/core/res/res/drawable-mdpi/presence_busy.png
index 9d7620b1d0c8..933261f48904 100644
--- a/core/res/res/drawable-mdpi/presence_busy.png
+++ b/core/res/res/drawable-mdpi/presence_busy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_invisible.png b/core/res/res/drawable-mdpi/presence_invisible.png
index 21399a4f3618..8d7275632164 100644
--- a/core/res/res/drawable-mdpi/presence_invisible.png
+++ b/core/res/res/drawable-mdpi/presence_invisible.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_offline.png b/core/res/res/drawable-mdpi/presence_offline.png
index 3941b8205251..fc783962206f 100644
--- a/core/res/res/drawable-mdpi/presence_offline.png
+++ b/core/res/res/drawable-mdpi/presence_offline.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_online.png b/core/res/res/drawable-mdpi/presence_online.png
index 22d5683e2f59..943eee0ad78e 100644
--- a/core/res/res/drawable-mdpi/presence_online.png
+++ b/core/res/res/drawable-mdpi/presence_online.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_video_away.png b/core/res/res/drawable-mdpi/presence_video_away.png
index f67e64e792ec..f3ec5d47be20 100644
--- a/core/res/res/drawable-mdpi/presence_video_away.png
+++ b/core/res/res/drawable-mdpi/presence_video_away.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_video_busy.png b/core/res/res/drawable-mdpi/presence_video_busy.png
index 0ad991b25b88..7379e004031b 100644
--- a/core/res/res/drawable-mdpi/presence_video_busy.png
+++ b/core/res/res/drawable-mdpi/presence_video_busy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/presence_video_online.png b/core/res/res/drawable-mdpi/presence_video_online.png
index 6cc3d1a7eb20..24ceb39a8641 100644
--- a/core/res/res/drawable-mdpi/presence_video_online.png
+++ b/core/res/res/drawable-mdpi/presence_video_online.png
Binary files differ
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>
...
&lt;/manifest&gt;</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>
...
&lt;/manifest&gt;</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/developing/tools/bmgr.jd b/docs/html/guide/developing/tools/bmgr.jd
index 2f495323e67a..57deb254c5c1 100644
--- a/docs/html/guide/developing/tools/bmgr.jd
+++ b/docs/html/guide/developing/tools/bmgr.jd
@@ -15,6 +15,11 @@ page.title=bmgr
<li><a href="#other">Other Commands</a></li>
</ol>
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a></li>
+ </ol>
+
</div>
</div>
@@ -26,6 +31,9 @@ and restore operations so that you don't need to repeatedly wipe data or take si
intrusive steps in order to test your application's backup agent. These commands are
accessed via the <a href="{@docRoot}guide/developing/tools/adb.html">adb</a> shell.
+<p>For information about adding support for backup in your application, read <a
+href="{@docRoot}guide/topics/data/backup.html">Data Backup</a>, which includes a guide to testing
+your application using {@code bmgr}.</p>
<h2 id="backup">Forcing a Backup Operation</h2>
@@ -90,6 +98,8 @@ will happen even if your application is not currently running.
<h2 id="other">Other Commands</h2>
+<h3>Wiping data</h3>
+
<p>The data for a single application can be erased from the active data set on demand. This is
very useful while you're developing a backup agent, in case bugs lead you to write corrupt data
or saved state information. You can wipe an application's data with the <code>bmgr wipe</code>
@@ -102,6 +112,9 @@ you wish to
erase. The next backup operation that the application's agent processes will look as
though the application had never backed anything up before.
+
+<h3>Enabling and disabling backup</h3>
+
<p>You can see whether the Backup Manager is operational at all with the <code>bmgr
enabled</code> command:
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>&lt;sdk&gt;</em>/platforms/android-<em>&lt;version&gt;</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>&lt;target_ID></em> -p <em>&lt;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 4e74a833897a..6c02031fd4f5 100644
--- a/docs/html/guide/topics/data/backup.jd
+++ b/docs/html/guide/topics/data/backup.jd
@@ -33,7 +33,7 @@ page.title=Data Backup
<li><a href="#RestoreVersion">Checking the Restore Data Version</a></li>
<li><a href="#RequestingBackup">Requesting Backup</a></li>
<li><a href="#RequestingRestore">Requesting Restore</a></li>
- <li><a href="#DevelopingTesting">Developing and Testing Your Backup Agent</a></li>
+ <li><a href="#Testing">Testing Your Backup Agent</a></li>
</ol>
<h2>Key classes</h2>
@@ -43,6 +43,11 @@ page.title=Data Backup
<li>{@link android.app.backup.BackupAgentHelper}</li>
</ol>
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr} tool</a></li>
+ </ol>
+
</div>
</div>
@@ -235,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>
@@ -257,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>
@@ -308,7 +313,7 @@ backup for all applications that have requested a backup since the last backup w
<p class="note"><strong>Tip:</strong> While developing your application, you can initiate an
immediate backup operation from the Backup Manager with the <a
-href="{@docRoot}guide/developing/tools/bmgr.html">bmgr tool</a>.</p>
+href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr} tool</a>.</p>
<p>When the Backup Manager calls your {@link
android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
@@ -452,7 +457,7 @@ android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore(
href="#RequestingRestore">Requesting restore</a> for more information).</p>
<p class="note"><strong>Note:</strong> While developing your application, you can also request a
-restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">bmgr
+restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}
tool</a>.</p>
<p>When the Backup Manager calls your {@link
@@ -571,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
@@ -813,7 +818,7 @@ onBackup()}.</p>
<p class="note"><strong>Note:</strong> While developing your application, you can request a
backup and initiate an immediate backup operation with the <a
-href="{@docRoot}guide/developing/tools/bmgr.html">bmgr
+href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}
tool</a>.</p>
@@ -828,25 +833,52 @@ android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescripto
implementation, passing the data from the current set of backup data.</p>
<p class="note"><strong>Note:</strong> While developing your application, you can request a
-restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">bmgr
+restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}
tool</a>.</p>
-<h2 id="DevelopingTesting">Developing and Testing Your Backup Agent</h2>
+<h2 id="Testing">Testing Your Backup Agent</h2>
-<p>To develop and test your backup agent:</p>
-<ul>
- <li>Set your build target to a platform using API Level 8 or higher</li>
- <li>Run your application on a suitable Android system image:
+<p>Once you've implemented your backup agent, you can test the backup and restore functionality
+with the following procedure, using <a
+href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}</a>.</p>
+
+<ol>
+ <li>Install your application on a suitable Android system image
<ul>
- <li>If using the emulator, create and use an AVD with the Google APIs add-on (API Level
-8) &mdash; the Google APIs add-on is available as an SDK component through the SDK and AVD
-Manager</li>
+ <li>If using the emulator, create and use an AVD with Android 2.2 (API Level 8).</li>
<li>If using a device, the device must be running Android 2.2 or greater and have Android
-Market built in</li>
+Market built in.</li>
</ul>
</li>
- <li>Test your backup agent using the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code
-bmgr}</a> tool to initiate backup and restore operations</li>
-</ul>
+ <li>Ensure that backup is enabled
+ <ul>
+ <li>If using the emulator, you can enable backup with the following command from your SDK
+{@code tools/} path:
+<pre class="no-pretty-print">adb shell bmgr enable true</pre>
+ </li>
+ <li>If using a device, open the system <b>Settings</b>, select <b>Privacy</b>, then enable
+<b>Back up my data</b> and <b>Automatic restore</b>.
+ </ul>
+ </li>
+ <li>Open your application and initialize some data
+ <p>If you've properly implemented backup in your application, then it should request a
+backup each time the data changes. For example, each time the user changes some data, your app
+should call {@link android.app.backup.BackupManager#dataChanged()}, which adds a backup request to
+the Backup Manager queue. For testing purposes, you can also make a request with the following
+{@code bmgr} command:</p>
+<pre class="no-pretty-print">adb shell bmgr backup <em>your.package.name</em></pre>
+ </li>
+ <li>Initiate a backup operation:
+<pre class="no-pretty-print">adb shell bmgr run</pre>
+ <p>This forces the Backup Manager to perform all backup requests that are in its
+queue.</p>
+ <li>Uninstall your application:
+<pre class="no-pretty-print">adb uninstall <em>your.package.name</em></pre>
+ </li>
+ <li>Re-install your application.</li>
+</ol>
+
+<p>If your backup agent is successful, all the data you initialized in step 4 is restored.</p>
+
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>&lt;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 &lt;item&gt;} to create a submenu.</dd>
+ <dt><code>&lt;item></code></dt>
+ <dd>Creates a {@link android.view.MenuItem}, which represents a single item in a menu.</dd>
+ <dt><code>&lt;group></code></dt>
+ <dd>An optional, invisible container for {@code &lt;item&gt;} 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 */
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;item android:id="@+id/new_game"
+ android:icon="@drawable/ic_new_game"
+ android:title="@string/new_game" /&gt;
+ &lt;item android:id="@+id/quit"
+ android:icon="@drawable/ic_quit"
+ android:title="@string/quit" /&gt;
+&lt;/menu&gt;
+</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>
+&#64;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>
+&#64;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) &mdash; 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 &mdash; 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&mdash;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>
+&#64;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>
+&#64;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> &mdash; 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>&lt;menu></code>, <code>&lt;group></code> and <code>&lt;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 &lt;menu&gt;} element as the child of an
+{@code &lt;item&gt;}. 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>
-&lt;menu xmlns:android="http://schemas.android.com/apk/res/android">
- &lt;item android:id="@+id/new_game"
- android:title="New Game" />
- &lt;item android:id="@+id/quit"
- android:title="Quit" />
-&lt;/menu>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;item android:id="@+id/file"
+ android:icon="@drawable/file"
+ android:title="@string/file" &gt;
+ &lt;!-- "file" submenu --&gt;
+ &lt;menu"&gt;
+ &lt;item android:id="@+id/new"
+ android:title="@string/new" /&gt;
+ &lt;item android:id="@+id/open"
+ android:title="@string/open" /&gt;
+ &lt;/menu&gt;
+ &lt;/item&gt;
+&lt;/menu&gt;
</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 &lt;item&gt;} elements inside a {@code &lt;group&gt;}
+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>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;item android:id="@+id/item1"
+ android:icon="@drawable/item1"
+ android:title="@string/item1" /&gt;
+ &lt;!-- menu group --&gt;
+ &lt;group android:id="@+id/group1"&gt;
+ &lt;item android:id="@+id/groupItem1"
+ android:title="@string/groupItem1" /&gt;
+ &lt;item android:id="@+id/groupItem2"
+ android:title="@string/groupItem2" /&gt;
+ &lt;/group&gt;
+&lt;/menu&gt;
+</pre>
+
+<p>The items that are in the group appear the same as the first item that is not in a
+group&mdash;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 &lt;item&gt;} element, or for an entire group with
+the {@code android:checkableBehavior} attribute in the {@code &lt;group&gt;} 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);
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;group android:checkableBehavior="single"&gt;
+ &lt;item android:id="@+id/red"
+ android:title="@string/red" /&gt;
+ &lt;item android:id="@+id/blue"
+ android:title="@string/blue" /&gt;
+ &lt;/group&gt;
+&lt;/menu&gt;
</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 &lt;item&gt;} 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;
-...
+&#64;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 &mdash; 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
+&lt;item&gt;} 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>
+&#64;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>&lt;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>
&lt;intent-filter label="Resize Image">
...
@@ -514,9 +641,10 @@ a <code>&lt;category></code> element in the intent filter. For example:</p>
...
&lt;/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/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/IMediaRecorder.h b/include/media/IMediaRecorder.h
index 24ac82b23771..cfc17a54ecb6 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -24,7 +24,7 @@ namespace android {
class ISurface;
class ICamera;
-class IMediaPlayerClient;
+class IMediaRecorderClient;
class IMediaRecorder: public IInterface
{
@@ -43,7 +43,7 @@ public:
virtual status_t setVideoSize(int width, int height) = 0;
virtual status_t setVideoFrameRate(int frames_per_second) = 0;
virtual status_t setParameters(const String8& params) = 0;
- virtual status_t setListener(const sp<IMediaPlayerClient>& listener) = 0;
+ virtual status_t setListener(const sp<IMediaRecorderClient>& listener) = 0;
virtual status_t prepare() = 0;
virtual status_t getMaxAmplitude(int* max) = 0;
virtual status_t start() = 0;
diff --git a/include/media/IMediaRecorderClient.h b/include/media/IMediaRecorderClient.h
new file mode 100644
index 000000000000..0058ef25163f
--- /dev/null
+++ b/include/media/IMediaRecorderClient.h
@@ -0,0 +1,48 @@
+/*
+ * 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_IMEDIARECORDERCLIENT_H
+#define ANDROID_IMEDIARECORDERCLIENT_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IMediaRecorderClient: public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(MediaRecorderClient);
+
+ virtual void notify(int msg, int ext1, int ext2) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnMediaRecorderClient: public BnInterface<IMediaRecorderClient>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IMEDIARECORDERCLIENT_H
+
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/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index 5b787a72548f..497965c9a6da 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -41,7 +41,7 @@ struct MediaRecorderBase {
virtual status_t setOutputFile(const char *path) = 0;
virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0;
virtual status_t setParameters(const String8& params) = 0;
- virtual status_t setListener(const sp<IMediaPlayerClient>& listener) = 0;
+ virtual status_t setListener(const sp<IMediaRecorderClient>& listener) = 0;
virtual status_t prepare() = 0;
virtual status_t start() = 0;
virtual status_t stop() = 0;
diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h
index 4f17c1ae81a6..c04105eae55f 100644
--- a/include/media/PVMediaRecorder.h
+++ b/include/media/PVMediaRecorder.h
@@ -18,7 +18,7 @@
#ifndef ANDROID_PVMEDIARECORDER_H
#define ANDROID_PVMEDIARECORDER_H
-#include <media/IMediaPlayerClient.h>
+#include <media/IMediaRecorderClient.h>
#include <media/MediaRecorderBase.h>
namespace android {
@@ -45,7 +45,7 @@ public:
virtual status_t setOutputFile(const char *path);
virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
virtual status_t setParameters(const String8& params);
- virtual status_t setListener(const sp<IMediaPlayerClient>& listener);
+ virtual status_t setListener(const sp<IMediaRecorderClient>& listener);
virtual status_t prepare();
virtual status_t start();
virtual status_t stop();
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 4f4ec43ef7b0..b21bc4d5bdda 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -22,7 +22,7 @@
#include <utils/threads.h>
#include <utils/List.h>
#include <utils/Errors.h>
-#include <media/IMediaPlayerClient.h>
+#include <media/IMediaRecorderClient.h>
#include <media/IMediaDeathNotifier.h>
namespace android {
@@ -149,7 +149,7 @@ public:
virtual void notify(int msg, int ext1, int ext2) = 0;
};
-class MediaRecorder : public BnMediaPlayerClient,
+class MediaRecorder : public BnMediaRecorderClient,
public virtual IMediaDeathNotifier
{
public:
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 46aaf7cad84d..8d3a9df39302 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -19,7 +19,7 @@
#define MEDIA_WRITER_H_
#include <utils/RefBase.h>
-#include <media/IMediaPlayerClient.h>
+#include <media/IMediaRecorderClient.h>
namespace android {
@@ -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;
@@ -36,7 +39,7 @@ struct MediaWriter : public RefBase {
virtual void pause() = 0;
virtual void setMaxFileSize(int64_t bytes) { mMaxFileSizeLimitBytes = bytes; }
virtual void setMaxFileDuration(int64_t durationUs) { mMaxFileDurationLimitUs = durationUs; }
- virtual void setListener(const sp<IMediaPlayerClient>& listener) {
+ virtual void setListener(const sp<IMediaRecorderClient>& listener) {
mListener = listener;
}
@@ -44,7 +47,7 @@ protected:
virtual ~MediaWriter() {}
int64_t mMaxFileSizeLimitBytes;
int64_t mMaxFileDurationLimitUs;
- sp<IMediaPlayerClient> mListener;
+ sp<IMediaRecorderClient> mListener;
void notify(int msg, int ext1, int ext2) {
if (mListener != NULL) {
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index c95fc021b140..99ec8e62c006 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -172,6 +172,9 @@ 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);
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/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 32f85b3b6eaa..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 {
@@ -133,7 +133,7 @@ struct PointerCoords {
/*
* Input events.
*/
-class InputEvent : public input_event_t {
+class InputEvent : public AInputEvent {
public:
virtual ~InputEvent() { }
@@ -142,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);
@@ -176,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/InputDispatcher.h b/include/ui/InputDispatcher.h
index 511ad20cfbed..eb8f820f085b 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -126,21 +126,21 @@ public:
/* Gets the key repeat timeout or -1 if automatic key repeating is disabled. */
virtual nsecs_t getKeyRepeatTimeout() = 0;
- /* Gets the input targets for a key event.
+ /* Waits for key event input targets to become available.
* If the event is being injected, injectorPid and injectorUid should specify the
* process id and used id of the injecting application, otherwise they should both
* be -1.
* Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
- virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+ virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) = 0;
- /* Gets the input targets for a motion event.
+ /* Waits for motion event targets to become available.
* If the event is being injected, injectorPid and injectorUid should specify the
* process id and used id of the injecting application, otherwise they should both
* be -1.
* Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
- virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) = 0;
};
@@ -186,6 +186,16 @@ public:
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
+ /* Preempts input dispatch in progress by making pending synchronous
+ * dispatches asynchronous instead. This method is generally called during a focus
+ * transition from one application to the next so as to enable the new application
+ * to start receiving input as soon as possible without having to wait for the
+ * old application to finish up.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void preemptInputDispatch() = 0;
+
/* Registers or unregister input channels that may be used as targets for input events.
*
* These methods may be called on any thread (usually by the input manager).
@@ -233,6 +243,8 @@ public:
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
+ virtual void preemptInputDispatch();
+
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h
index 7509dd205e10..e7552381162c 100644
--- a/include/ui/InputManager.h
+++ b/include/ui/InputManager.h
@@ -87,6 +87,14 @@ public:
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
+ /* Preempts input dispatch in progress by making pending synchronous
+ * dispatches asynchronous instead. This method is generally called during a focus
+ * transition from one application to the next so as to enable the new application
+ * to start receiving input as soon as possible without having to wait for the
+ * old application to finish up.
+ */
+ virtual void preemptInputDispatch() = 0;
+
/* Gets input device configuration. */
virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0;
@@ -130,6 +138,8 @@ public:
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
+ virtual void preemptInputDispatch();
+
virtual void getInputConfiguration(InputConfiguration* outConfiguration) const;
virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t scanCode) const;
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index d76b8fed003b..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;
@@ -361,7 +370,7 @@ public:
// The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it
// passes through the dispatch pipeline.
- ACTION_BRIGHT_HERE = 0x00000008
+ ACTION_BRIGHT_HERE = 0x00000008,
};
/* Describes a virtual key. */
diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h
index d6bded620786..2dfe2a866ef1 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -333,18 +333,20 @@ 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);
+ virtual void doDefaultKey(android::KeyEvent* keyEvent) = 0;
+
private:
android::InputConsumer mConsumer;
android::PreallocatedInputEventFactory mInputEventFactory;
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/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 b3103a47c804..8f6d1fe0c0cb 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -8,25 +8,25 @@
//#define LOG_NDEBUG 0
// Log detailed debug messages about each inbound event notification to the dispatcher.
-#define DEBUG_INBOUND_EVENT_DETAILS 1
+#define DEBUG_INBOUND_EVENT_DETAILS 0
// Log detailed debug messages about each outbound event processed by the dispatcher.
-#define DEBUG_OUTBOUND_EVENT_DETAILS 1
+#define DEBUG_OUTBOUND_EVENT_DETAILS 0
// Log debug messages about batching.
-#define DEBUG_BATCHING 1
+#define DEBUG_BATCHING 0
// Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 1
+#define DEBUG_DISPATCH_CYCLE 0
// Log debug messages about registrations.
-#define DEBUG_REGISTRATION 1
+#define DEBUG_REGISTRATION 0
// Log debug messages about performance statistics.
-#define DEBUG_PERFORMANCE_STATISTICS 1
+#define DEBUG_PERFORMANCE_STATISTICS 0
// Log debug messages about input event injection.
-#define DEBUG_INJECTION 1
+#define DEBUG_INJECTION 0
#include <cutils/log.h>
#include <ui/InputDispatcher.h>
@@ -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);
@@ -249,9 +252,7 @@ void InputDispatcher::processKeyLockedInterruptible(
entry->downTime);
#endif
- // TODO: Poke user activity.
-
- if (entry->action == KEY_EVENT_ACTION_DOWN) {
+ if (entry->action == KEY_EVENT_ACTION_DOWN && ! entry->isInjected()) {
if (mKeyRepeatState.lastKeyEntry
&& mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
// We have seen two identical key downs in a row which indicates that the device
@@ -277,14 +278,24 @@ void InputDispatcher::processKeyLockedInterruptible(
void InputDispatcher::processKeyRepeatLockedInterruptible(
nsecs_t currentTime, nsecs_t keyRepeatTimeout) {
- // TODO Old WindowManagerServer code sniffs the input queue for following key up
- // events and drops the repeat if one is found. We should do something similar.
- // One good place to do it is in notifyKey as soon as the key up enters the
- // inbound event queue.
+ KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+
+ // Search the inbound queue for a key up corresponding to this device.
+ // It doesn't make sense to generate a key repeat event if the key is already up.
+ for (EventEntry* queuedEntry = mInboundQueue.head.next;
+ queuedEntry != & mInboundQueue.tail; queuedEntry = entry->next) {
+ if (queuedEntry->type == EventEntry::TYPE_KEY) {
+ KeyEntry* queuedKeyEntry = static_cast<KeyEntry*>(queuedEntry);
+ if (queuedKeyEntry->deviceId == entry->deviceId
+ && entry->action == KEY_EVENT_ACTION_UP) {
+ resetKeyRepeatLocked();
+ return;
+ }
+ }
+ }
// Synthesize a key repeat after the repeat timeout expired.
- // We reuse the previous key entry if otherwise unreferenced.
- KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+ // Reuse the repeated key entry if it is otherwise unreferenced.
uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
if (entry->refCount == 1) {
entry->eventTime = currentTime;
@@ -366,7 +377,7 @@ void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
entry->downTime, entry->eventTime);
mCurrentInputTargets.clear();
- int32_t injectionResult = mPolicy->getKeyEventTargets(& mReusableKeyEvent,
+ int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent,
entry->policyFlags, entry->injectorPid, entry->injectorUid,
mCurrentInputTargets);
@@ -375,7 +386,9 @@ void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
setInjectionResultLocked(entry, injectionResult);
- dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ }
}
void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
@@ -395,7 +408,7 @@ void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
entry->firstSample.pointerCoords);
mCurrentInputTargets.clear();
- int32_t injectionResult = mPolicy->getMotionEventTargets(& mReusableMotionEvent,
+ int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent,
entry->policyFlags, entry->injectorPid, entry->injectorUid,
mCurrentInputTargets);
@@ -404,7 +417,9 @@ void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
setInjectionResultLocked(entry, injectionResult);
- dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ }
}
void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
@@ -514,7 +529,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
connection->getInputChannelName());
} else if (status == status_t(FAILED_TRANSACTION)) {
LOGD("channel '%s' ~ Could not append motion sample to currently "
- "dispatchedmove event because the event has already been consumed. "
+ "dispatched move event because the event has already been consumed. "
"(Waiting for next dispatch cycle to start.)",
connection->getInputChannelName());
} else {
@@ -1253,9 +1268,37 @@ void InputDispatcher::resetKeyRepeatLocked() {
}
}
+void InputDispatcher::preemptInputDispatch() {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("preemptInputDispatch");
+#endif
+
+ bool preemptedOne = false;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ Connection* connection = mActiveConnections[i];
+ if (connection->hasPendingSyncTarget()) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
+ connection->getInputChannelName());
+#endif
+ connection->outboundQueue.tail.prev->targetFlags &= ~ InputTarget::FLAG_SYNC;
+ preemptedOne = true;
+ }
+ }
+ } // release lock
+
+ if (preemptedOne) {
+ // Wake up the poll loop so it can get a head start dispatching the next event.
+ mPollLoop->wake();
+ }
+}
+
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
- LOGD("channel '%s' - Registered", inputChannel->getName().string());
+ LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string());
#endif
int receiveFd;
@@ -1288,7 +1331,7 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan
status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
- LOGD("channel '%s' - Unregistered", inputChannel->getName().string());
+ LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string());
#endif
int32_t receiveFd;
diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp
index 32c58b4fa6d1..e1d15a4ba977 100644
--- a/libs/ui/InputManager.cpp
+++ b/libs/ui/InputManager.cpp
@@ -85,6 +85,10 @@ int32_t InputManager::injectInputEvent(const InputEvent* event,
return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, sync, timeoutMillis);
}
+void InputManager::preemptInputDispatch() {
+ mDispatcher->preemptInputDispatch();
+}
+
void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const {
mReader->getCurrentInputConfiguration(outConfiguration);
}
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 1824054e0b57..899027c9b920 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -11,13 +11,13 @@
#define DEBUG_RAW_EVENTS 0
// Log debug messages about touch screen filtering hacks.
-#define DEBUG_HACKS 1
+#define DEBUG_HACKS 0
// Log debug messages about virtual key processing.
-#define DEBUG_VIRTUAL_KEYS 1
+#define DEBUG_VIRTUAL_KEYS 0
// Log debug messages about pointers.
-#define DEBUG_POINTERS 1
+#define DEBUG_POINTERS 0
// Log debug messages about pointer assignment calculations.
#define DEBUG_POINTER_ASSIGNMENT 0
@@ -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
@@ -630,7 +640,8 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
int32_t pressure = currentTouch.pointers[currentIndex].pressure;
if (lastTouch.idBits.hasBit(id)) {
- // Pointer still down compute average.
+ // Pointer was down before and is still down now.
+ // Compute average over history trace.
uint32_t start = averagingTouchFilter.historyStart[id];
uint32_t end = averagingTouchFilter.historyEnd[id];
@@ -644,11 +655,15 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
#endif
if (distance < AVERAGING_DISTANCE_LIMIT) {
+ // Increment end index in preparation for recording new historical data.
end += 1;
if (end > AVERAGING_HISTORY_SIZE) {
end = 0;
}
+ // If the end index has looped back to the start index then we have filled
+ // the historical trace up to the desired size so we drop the historical
+ // data at the start of the trace.
if (end == start) {
start += 1;
if (start > AVERAGING_HISTORY_SIZE) {
@@ -656,23 +671,25 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
}
}
+ // Add the raw data to the historical trace.
averagingTouchFilter.historyStart[id] = start;
averagingTouchFilter.historyEnd[id] = end;
averagingTouchFilter.historyData[end].pointers[id].x = x;
averagingTouchFilter.historyData[end].pointers[id].y = y;
averagingTouchFilter.historyData[end].pointers[id].pressure = pressure;
+ // Average over all historical positions in the trace by total pressure.
int32_t averagedX = 0;
int32_t averagedY = 0;
int32_t totalPressure = 0;
for (;;) {
int32_t historicalX = averagingTouchFilter.historyData[start].pointers[id].x;
- int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].x;
+ int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].y;
int32_t historicalPressure = averagingTouchFilter.historyData[start]
.pointers[id].pressure;
- averagedX += historicalX;
- averagedY += historicalY;
+ averagedX += historicalX * historicalPressure;
+ averagedY += historicalY * historicalPressure;
totalPressure += historicalPressure;
if (start == end) {
@@ -717,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
@@ -1144,12 +1167,6 @@ void InputReader::onMultiTouchScreenStateChanged(nsecs_t when,
void InputReader::onSingleTouchScreenStateChanged(nsecs_t when,
InputDevice* device) {
- static const uint32_t POSITION_FIELDS =
- InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X
- | InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y
- | InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE
- | InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH;
-
/* Refresh display properties so we can map touch screen coords into display coords */
if (! refreshDisplayProperties()) {
@@ -1167,10 +1184,19 @@ void InputReader::onSingleTouchScreenStateChanged(nsecs_t when,
in->current.down = in->accumulator.btnTouch;
}
- if ((fields & POSITION_FIELDS) == POSITION_FIELDS) {
+ if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X) {
in->current.x = in->accumulator.absX;
+ }
+
+ if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y) {
in->current.y = in->accumulator.absY;
+ }
+
+ if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE) {
in->current.pressure = in->accumulator.absPressure;
+ }
+
+ if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH) {
in->current.size = in->accumulator.absToolWidth;
}
@@ -1323,18 +1349,23 @@ bool InputReader::consumeVirtualKeyTouches(nsecs_t when,
void InputReader::dispatchVirtualKey(nsecs_t when,
InputDevice* device, uint32_t policyFlags,
int32_t keyEventAction, int32_t keyEventFlags) {
+ updateExportedVirtualKeyState();
+
int32_t keyCode = device->touchScreen.currentVirtualKey.keyCode;
int32_t scanCode = device->touchScreen.currentVirtualKey.scanCode;
nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime;
int32_t metaState = globalMetaState();
- updateExportedVirtualKeyState();
-
mPolicy->virtualKeyFeedback(when, device->id, keyEventAction, keyEventFlags,
keyCode, scanCode, metaState, downTime);
- mDispatcher->notifyKey(when, device->id, INPUT_EVENT_NATURE_KEY, policyFlags,
- keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+ int32_t policyActions = mPolicy->interceptKey(when, device->id,
+ keyEventAction == KEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
+
+ if (applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) {
+ mDispatcher->notifyKey(when, device->id, INPUT_EVENT_NATURE_KEY, policyFlags,
+ keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+ }
}
void InputReader::dispatchTouches(nsecs_t when,
@@ -1420,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()) {
@@ -1427,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: {
@@ -1632,7 +1662,11 @@ bool InputReader::refreshDisplayProperties() {
}
}
- mDisplayOrientation = newOrientation;
+ if (newOrientation != mDisplayOrientation) {
+ LOGD("Display orientation changed to %d", mDisplayOrientation);
+
+ mDisplayOrientation = newOrientation;
+ }
return true;
} else {
resetDisplayProperties();
@@ -1721,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()) {
@@ -1739,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;
@@ -1818,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 b2842d0aef81..25def3c18ca1 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -430,10 +430,12 @@ status_t InputPublisher::appendMotionSample(
reinterpret_cast<char*>(mSharedMessage);
if (newBytesUsed > mAshmemSize) {
+#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory "
"buffer is full. Buffer size: %d bytes, pointers: %d, samples: %d",
mChannel->getName().string(),
mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount);
+#endif
return NO_MEMORY;
}
@@ -444,8 +446,10 @@ status_t InputPublisher::appendMotionSample(
if (errno == EAGAIN) {
// Only possible source of contention is the consumer having consumed (or being in the
// process of consuming) the message and left the semaphore count at 0.
+#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ Cannot append motion sample because the message has "
"already been consumed.", mChannel->getName().string());
+#endif
return FAILED_TRANSACTION;
} else {
LOGE("channel '%s' publisher ~ Error %d in sem_trywait.",
@@ -687,7 +691,7 @@ void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const {
} // namespace android
-// --- input_queue_t ---
+// --- AInputQueue ---
using android::InputEvent;
using android::InputChannel;
@@ -695,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/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/Android.mk b/media/libmedia/Android.mk
index 7908f5de0e86..de9e51da4cbf 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -12,6 +12,7 @@ LOCAL_SRC_FILES:= \
mediaplayer.cpp \
IMediaPlayerService.cpp \
IMediaPlayerClient.cpp \
+ IMediaRecorderClient.cpp \
IMediaPlayer.cpp \
IMediaRecorder.cpp \
Metadata.cpp \
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 2bc2a7e776f1..9fe207c55541 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -21,7 +21,7 @@
#include <binder/Parcel.h>
#include <surfaceflinger/ISurface.h>
#include <camera/ICamera.h>
-#include <media/IMediaPlayerClient.h>
+#include <media/IMediaRecorderClient.h>
#include <media/IMediaRecorder.h>
namespace android {
@@ -189,7 +189,7 @@ public:
return reply.readInt32();
}
- status_t setListener(const sp<IMediaPlayerClient>& listener)
+ status_t setListener(const sp<IMediaRecorderClient>& listener)
{
LOGV("setListener(%p)", listener.get());
Parcel data, reply;
@@ -399,8 +399,8 @@ status_t BnMediaRecorder::onTransact(
case SET_LISTENER: {
LOGV("SET_LISTENER");
CHECK_INTERFACE(IMediaRecorder, data, reply);
- sp<IMediaPlayerClient> listener =
- interface_cast<IMediaPlayerClient>(data.readStrongBinder());
+ sp<IMediaRecorderClient> listener =
+ interface_cast<IMediaRecorderClient>(data.readStrongBinder());
reply->writeInt32(setListener(listener));
return NO_ERROR;
} break;
diff --git a/media/libmedia/IMediaRecorderClient.cpp b/media/libmedia/IMediaRecorderClient.cpp
new file mode 100644
index 000000000000..ff235c9c790a
--- /dev/null
+++ b/media/libmedia/IMediaRecorderClient.cpp
@@ -0,0 +1,70 @@
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+#include <media/IMediaRecorderClient.h>
+
+namespace android {
+
+enum {
+ NOTIFY = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BpMediaRecorderClient: public BpInterface<IMediaRecorderClient>
+{
+public:
+ BpMediaRecorderClient(const sp<IBinder>& impl)
+ : BpInterface<IMediaRecorderClient>(impl)
+ {
+ }
+
+ virtual void notify(int msg, int ext1, int ext2)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaRecorderClient::getInterfaceDescriptor());
+ data.writeInt32(msg);
+ data.writeInt32(ext1);
+ data.writeInt32(ext2);
+ remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(MediaRecorderClient, "android.media.IMediaRecorderClient");
+
+// ----------------------------------------------------------------------
+
+status_t BnMediaRecorderClient::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case NOTIFY: {
+ CHECK_INTERFACE(IMediaRecorderClient, data, reply);
+ int msg = data.readInt32();
+ int ext1 = data.readInt32();
+ int ext2 = data.readInt32();
+ notify(msg, ext1, ext2);
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
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/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 084f03991e39..80b1cfd00e2e 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -318,7 +318,7 @@ MediaRecorderClient::~MediaRecorderClient()
release();
}
-status_t MediaRecorderClient::setListener(const sp<IMediaPlayerClient>& listener)
+status_t MediaRecorderClient::setListener(const sp<IMediaRecorderClient>& listener)
{
LOGV("setListener");
Mutex::Autolock lock(mLock);
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index e07306bca99e..b53d950ab6f2 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -40,7 +40,7 @@ public:
virtual status_t setVideoSize(int width, int height);
virtual status_t setVideoFrameRate(int frames_per_second);
virtual status_t setParameters(const String8& params);
- virtual status_t setListener(const sp<IMediaPlayerClient>& listener);
+ virtual status_t setListener(const sp<IMediaRecorderClient>& listener);
virtual status_t prepare();
virtual status_t getMaxAmplitude(int* max);
virtual status_t start();
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 1e20f7e59bc7..91c5b92e528c 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,6 +419,7 @@ 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;
@@ -356,14 +432,12 @@ status_t StagefrightRecorder::setParameter(
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;
@@ -456,7 +530,7 @@ status_t StagefrightRecorder::setParameters(const String8 &params) {
return OK;
}
-status_t StagefrightRecorder::setListener(const sp<IMediaPlayerClient> &listener) {
+status_t StagefrightRecorder::setListener(const sp<IMediaRecorderClient> &listener) {
mListener = listener;
return OK;
@@ -467,7 +541,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 +563,7 @@ status_t StagefrightRecorder::start() {
return startAACRecording();
default:
+ LOGE("Unsupported output file format: %d", mOutputFormat);
return UNKNOWN_ERROR;
}
}
@@ -549,7 +627,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 +642,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 +678,6 @@ status_t StagefrightRecorder::startAMRRecording() {
return UNKNOWN_ERROR;
}
- CHECK(mOutputFd >= 0);
mWriter = new AMRWriter(dup(mOutputFd));
mWriter->addSource(audioEncoder);
@@ -668,6 +744,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 +809,110 @@ void StagefrightRecorder::clipVideoFrameHeight() {
}
}
-status_t StagefrightRecorder::startMPEG4Recording() {
- mWriter = new MPEG4Writer(dup(mOutputFd));
- int32_t totalBitRate = 0;
+status_t StagefrightRecorder::setupVideoEncoder(const sp<MediaWriter>& writer) {
+ status_t err = setupCameraSource();
+ if (err != OK) return err;
- // 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;
- }
+ sp<CameraSource> cameraSource = CameraSource::CreateFromCamera(mCamera);
+ CHECK(cameraSource != NULL);
- if (audioEncoder == NULL) {
- return UNKNOWN_ERROR;
- }
- totalBitRate += mAudioBitRate;
- mWriter->addSource(audioEncoder);
- }
- if (mVideoSource == VIDEO_SOURCE_DEFAULT
- || mVideoSource == VIDEO_SOURCE_CAMERA) {
-
- clipVideoBitRate();
- clipVideoFrameRate();
- clipVideoFrameWidth();
- clipVideoFrameHeight();
+ sp<MetaData> enc_meta = new MetaData;
+ enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
+ enc_meta->setInt32(kKeySampleRate, mFrameRate);
- 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);
- }
-
- CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface));
- IPCThreadState::self()->restoreCallingIdentity(token);
+ switch (mVideoEncoder) {
+ case VIDEO_ENCODER_H263:
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
+ break;
- sp<CameraSource> cameraSource =
- CameraSource::CreateFromCamera(mCamera);
+ case VIDEO_ENCODER_MPEG_4_SP:
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ break;
- CHECK(cameraSource != NULL);
+ case VIDEO_ENCODER_H264:
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ break;
- sp<MetaData> enc_meta = new MetaData;
- enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
- enc_meta->setInt32(kKeySampleRate, mFrameRate);
+ default:
+ CHECK(!"Should not be here, unsupported video encoding.");
+ break;
+ }
- switch (mVideoEncoder) {
- case VIDEO_ENCODER_H263:
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
- break;
+ sp<MetaData> meta = cameraSource->getFormat();
- case VIDEO_ENCODER_MPEG_4_SP:
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
- break;
+ 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));
- case VIDEO_ENCODER_H264:
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
- break;
+ 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);
- 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 +924,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 +939,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
@@ -904,6 +1003,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 9fb7e8f75e33..cb055718908d 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -46,7 +46,7 @@ struct StagefrightRecorder : public MediaRecorderBase {
virtual status_t setOutputFile(const char *path);
virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
virtual status_t setParameters(const String8& params);
- virtual status_t setListener(const sp<IMediaPlayerClient>& listener);
+ virtual status_t setListener(const sp<IMediaRecorderClient>& listener);
virtual status_t prepare();
virtual status_t start();
virtual status_t pause();
@@ -63,7 +63,7 @@ private:
sp<Camera> mCamera;
sp<ISurface> mPreviewSurface;
- sp<IMediaPlayerClient> mListener;
+ sp<IMediaRecorderClient> mListener;
sp<MediaWriter> mWriter;
sp<AudioSource> mAudioSourceNode;
@@ -97,6 +97,11 @@ 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);
@@ -108,7 +113,8 @@ private:
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/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index b3e1a0189ce9..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 {
@@ -115,12 +117,19 @@ private:
status_t makeAVCCodecSpecificData(
const uint8_t *data, size_t size);
void writeOneChunk(bool isAvc);
- void logStatisticalData(bool isAudio);
- void findMinMaxFrameRates(float *minFps, float *maxFps);
- void findMinMaxChunkDurations(int64_t *min, int64_t *max);
+
+ // Track authoring progress status
void trackProgressStatus(int32_t nFrames, int64_t timeUs);
void initTrackingProgressStatus(MetaData *params);
+ // Utilities for collecting statistical data
+ void logStatisticalData(bool isAudio);
+ void findMinAvgMaxSampleDurationMs(
+ 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 &);
};
@@ -673,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() {
@@ -716,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;
@@ -1184,14 +1228,16 @@ void MPEG4Writer::Track::threadEntry() {
void MPEG4Writer::Track::trackProgressStatus(int32_t nFrames, int64_t timeUs) {
LOGV("trackProgressStatus: %d frames and %lld us", nFrames, timeUs);
- if (nFrames % mTrackEveryNumberOfFrames == 0) {
+ if (mTrackEveryNumberOfFrames > 0 &&
+ nFrames % mTrackEveryNumberOfFrames == 0) {
LOGV("Fire frame tracking progress status at frame %d", nFrames);
mOwner->notify(MEDIA_RECORDER_EVENT_INFO,
MEDIA_RECORDER_INFO_PROGRESS_FRAME_STATUS,
nFrames);
}
- if (timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
+ if (mTrackEveryTimeDurationUs > 0 &&
+ timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
LOGV("Fire time tracking progress status at %lld us", timeUs);
mOwner->notify(MEDIA_RECORDER_EVENT_INFO,
MEDIA_RECORDER_INFO_PROGRESS_TIME_STATUS,
@@ -1200,21 +1246,28 @@ void MPEG4Writer::Track::trackProgressStatus(int32_t nFrames, int64_t timeUs) {
}
}
-void MPEG4Writer::Track::findMinMaxFrameRates(float *minFps, float *maxFps) {
- int32_t minSampleDuration = 0x7FFFFFFF;
- int32_t maxSampleDuration = 0;
+void MPEG4Writer::Track::findMinAvgMaxSampleDurationMs(
+ int32_t *min, int32_t *avg, int32_t *max) {
+ CHECK(!mSampleInfos.empty());
+ int32_t avgSampleDurationMs = mMaxTimeStampUs / 1000/ mSampleInfos.size();
+ int32_t minSampleDurationMs = 0x7FFFFFFF;
+ int32_t maxSampleDurationMs = 0;
for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
it != mSttsTableEntries.end(); ++it) {
- int32_t sampleDuration = static_cast<int32_t>(it->sampleDuration);
- if (sampleDuration > maxSampleDuration) {
- maxSampleDuration = sampleDuration;
- } else if (sampleDuration < minSampleDuration) {
- minSampleDuration = sampleDuration;
+ int32_t sampleDurationMs = static_cast<int32_t>(it->sampleDuration);
+ if (sampleDurationMs > maxSampleDurationMs) {
+ maxSampleDurationMs = sampleDurationMs;
+ } else if (sampleDurationMs < minSampleDurationMs) {
+ minSampleDurationMs = sampleDurationMs;
}
+ LOGI("sample duration: %d ms", sampleDurationMs);
}
- CHECK(minSampleDuration != 0 && maxSampleDuration != 0);
- *minFps = 1000.0 / maxSampleDuration;
- *maxFps = 1000.0 / minSampleDuration;
+ CHECK(minSampleDurationMs != 0);
+ CHECK(avgSampleDurationMs != 0);
+ CHECK(maxSampleDurationMs != 0);
+ *min = minSampleDurationMs;
+ *avg = avgSampleDurationMs;
+ *max = maxSampleDurationMs;
}
// Don't count the last duration
@@ -1250,16 +1303,18 @@ void MPEG4Writer::Track::logStatisticalData(bool isAudio) {
}
if (collectStats) {
- if (isAudio) {
- LOGI("audio track - duration %lld us", mMaxTimeStampUs);
- } else {
- float fps = (mSampleInfos.size() * 1000000.0) / mMaxTimeStampUs;
- float minFps;
- float maxFps;
- findMinMaxFrameRates(&minFps, &maxFps);
- LOGI("video track - duration %lld us", mMaxTimeStampUs);
+ LOGI("%s track - duration %lld us, total %d frames",
+ isAudio? "audio": "video", mMaxTimeStampUs,
+ mSampleInfos.size());
+ int32_t min, avg, max;
+ findMinAvgMaxSampleDurationMs(&min, &avg, &max);
+ LOGI("min/avg/max sample duration (ms): %d/%d/%d", min, avg, max);
+ if (!isAudio) {
+ float avgFps = 1000.0 / avg;
+ float minFps = 1000.0 / max;
+ float maxFps = 1000.0 / min;
LOGI("min/avg/max frame rate (fps): %.2f/%.2f/%.2f",
- minFps, fps, maxFps);
+ minFps, avgFps, maxFps);
}
int64_t totalBytes = 0;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 02a073ea2c99..dacb8d374849 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
@@ -836,6 +849,7 @@ void OMXCodec::setVideoInputFormat(
}
case OMX_VIDEO_CodingH263:
+ CHECK_EQ(setupH263EncoderParameters(meta), OK);
break;
case OMX_VIDEO_CodingAVC:
@@ -861,6 +875,90 @@ 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::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;
+
+ h263type.eProfile = OMX_VIDEO_H263ProfileBaseline;
+ h263type.eLevel = OMX_VIDEO_H263Level45;
+
+ 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 +992,15 @@ status_t OMXCodec::setupMPEG4EncoderParameters(const sp<MetaData>& meta) {
mpeg4type.nHeaderExtension = 0;
mpeg4type.bReversibleVLC = OMX_FALSE;
- mpeg4type.eProfile = OMX_VIDEO_MPEG4ProfileCore;
+ mpeg4type.eProfile = OMX_VIDEO_MPEG4ProfileSimple;
mpeg4type.eLevel = OMX_VIDEO_MPEG4Level2;
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;
}
@@ -991,22 +1051,7 @@ status_t OMXCodec::setupAVCEncoderParameters(const sp<MetaData>& meta) {
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/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/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/native/android/Android.mk b/native/android/Android.mk
index 8c621b601736..fe8ed0003a0a 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -7,7 +7,8 @@ include $(CLEAR_VARS)
#
LOCAL_SRC_FILES:= \
activity.cpp \
- input.cpp
+ input.cpp \
+ native_window.cpp
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 38d8567e313c..84988403e301 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -27,168 +27,168 @@ 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) {
+int AInputQueue_getFd(AInputQueue* queue) {
return queue->getConsumer().getChannel()->getReceivePipeFd();
}
-int input_queue_has_events(input_queue_t* queue) {
+int AInputQueue_hasEvents(AInputQueue* queue) {
struct pollfd pfd;
pfd.fd = queue->getConsumer().getChannel()->getReceivePipeFd();
@@ -200,7 +200,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 +223,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/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..76176624a9ae 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -125,8 +125,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 +319,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 +331,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 +460,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 +480,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 +489,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 +510,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 +520,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,8 +529,8 @@ 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
@@ -538,26 +538,26 @@ typedef struct input_queue_t input_queue_t;
* is typically used with select() or poll() to multiplex
* with other kinds of events.
*/
-int input_queue_get_fd(input_queue_t* queue);
+int AInputQueue_getFd(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/native_activity.h b/native/include/android/native_activity.h
index a58a7d27c604..bf5c641fc162 100644
--- a/native/include/android/native_activity.h
+++ b/native/include/android/native_activity.h
@@ -24,36 +24,40 @@
#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.
*/
@@ -65,7 +69,7 @@ typedef struct android_activity_t {
* state.
*/
void* instance;
-} android_activity_t;
+} ANativeActivity;
/**
* These are the callbacks the framework makes into a native application.
@@ -73,18 +77,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 +99,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 +168,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/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_normal.9.png b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_normal.9.png
new file mode 100644
index 000000000000..bc127bde4ad1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_normal.9.png
Binary files differ
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
new file mode 100644
index 000000000000..59af8043f073
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/alert_bar_background_normal.9.png b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_normal.9.png
new file mode 100644
index 000000000000..258de13c1426
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_normal.9.png
Binary files differ
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
new file mode 100644
index 000000000000..258de13c1426
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_pressed.9.png
Binary files differ
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
new file mode 100644
index 000000000000..24eb960da745
--- /dev/null
+++ b/packages/SystemUI/res/layout/intruder_alert.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<!-- android:background="@drawable/status_bar_closed_default_background" -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ 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: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:paddingLeft="6dip"
+ android:layout_marginRight="8dip"
+ />
+ <TextView
+ android:id="@+id/alertText"
+ android:textAppearance="@style/TextAppearance.StatusBar.IntruderAlert"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ />
+ </LinearLayout>
+</FrameLayout>
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 5d16e93e7ddd..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
+++ /dev/null
@@ -1,1493 +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.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 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;
-
- /**
- * 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;
- 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);
- }
-
- 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) {
- addNotificationViews(key, notification);
-
- 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 mAlertBarView
-
- ImageView alertIcon = (ImageView) mAlertBarView.findViewById(R.id.alertIcon);
- TextView alertText = (TextView) mAlertBarView.findViewById(R.id.alertText);
- alertIcon.setImageDrawable(StatusBarIconView.getIcon(
- alertIcon.getContext(),
- iconView.getStatusBarIcon()));
- alertText.setText(notification.notification.tickerText);
-
- // 2. Animate mAlertBarView in
- mAlertBarView.setVisibility(View.VISIBLE);
-
- // 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 };
- }
-
- void 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;
- }
- 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;
- }
- // 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));
- }
-
- 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;
- }
- }
- }
-
- 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();
- }
- }
- };
-
- /**
- * 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/InputApplication.java b/services/java/com/android/server/InputApplication.java
new file mode 100644
index 000000000000..38420d4f899f
--- /dev/null
+++ b/services/java/com/android/server/InputApplication.java
@@ -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.
+ */
+
+package com.android.server;
+
+/**
+ * Describes input-related application properties for use by the input dispatcher.
+ *
+ * @hide
+ */
+public final class InputApplication {
+ // Application name.
+ public String name;
+
+ // Dispatching timeout.
+ public long dispatchingTimeoutNanos;
+
+ // The application window token.
+ public Object token;
+}
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 8d9bb2940ab2..2ba29141e18a 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -17,23 +17,20 @@
package com.android.server;
import com.android.internal.util.XmlUtils;
-import com.android.server.KeyInputQueue.VirtualKey;
import org.xmlpull.v1.XmlPullParser;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Environment;
import android.os.LocalPowerManager;
import android.os.PowerManager;
-import android.util.Log;
import android.util.Slog;
import android.util.Xml;
import android.view.InputChannel;
-import android.view.InputTarget;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.RawInputEvent;
import android.view.Surface;
import android.view.WindowManagerPolicy;
@@ -85,6 +82,10 @@ public class InputManager {
int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
private static native int nativeInjectMotionEvent(MotionEvent event, int nature,
int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
+ private static native void nativeSetInputWindows(InputWindow[] windows);
+ private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
+ private static native void nativeSetFocusedApplication(InputApplication application);
+ private static native void nativePreemptInputDispatch();
// Device class as defined by EventHub.
private static final int CLASS_KEYBOARD = 0x00000001;
@@ -284,6 +285,22 @@ public class InputManager {
sync, timeoutMillis);
}
+ public void setInputWindows(InputWindow[] windows) {
+ nativeSetInputWindows(windows);
+ }
+
+ public void setFocusedApplication(InputApplication application) {
+ nativeSetFocusedApplication(application);
+ }
+
+ public void preemptInputDispatch() {
+ nativePreemptInputDispatch();
+ }
+
+ public void setInputDispatchMode(boolean enabled, boolean frozen) {
+ nativeSetInputDispatchMode(enabled, frozen);
+ }
+
public void dump(PrintWriter pw) {
// TODO
}
@@ -344,32 +361,43 @@ public class InputManager {
@SuppressWarnings("unused")
public void notifyInputChannelBroken(InputChannel inputChannel) {
- mWindowManagerService.notifyInputChannelBroken(inputChannel);
+ mWindowManagerService.mInputMonitor.notifyInputChannelBroken(inputChannel);
}
@SuppressWarnings("unused")
public long notifyInputChannelANR(InputChannel inputChannel) {
- return mWindowManagerService.notifyInputChannelANR(inputChannel);
+ return mWindowManagerService.mInputMonitor.notifyInputChannelANR(inputChannel);
}
@SuppressWarnings("unused")
public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
- mWindowManagerService.notifyInputChannelRecoveredFromANR(inputChannel);
+ mWindowManagerService.mInputMonitor.notifyInputChannelRecoveredFromANR(inputChannel);
+ }
+
+ @SuppressWarnings("unused")
+ public long notifyANR(Object token) {
+ return mWindowManagerService.mInputMonitor.notifyANR(token);
}
@SuppressWarnings("unused")
- public int hackInterceptKey(int deviceId, int type, int scanCode,
+ public int interceptKeyBeforeQueueing(int deviceId, int type, int scanCode,
int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) {
- RawInputEvent event = new RawInputEvent();
- event.deviceId = deviceId;
- event.type = type;
- event.scancode = scanCode;
- event.keycode = keyCode;
- event.flags = policyFlags;
- event.value = value;
- event.when = whenNanos / 1000000;
-
- return mWindowManagerPolicy.interceptKeyTq(event, isScreenOn);
+ return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(deviceId, type,
+ scanCode, keyCode, policyFlags, value, whenNanos, isScreenOn);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean interceptKeyBeforeDispatching(InputChannel focus, int keyCode,
+ int metaState, boolean down, int repeatCount, int policyFlags) {
+ return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus,
+ keyCode, metaState, down, repeatCount, policyFlags);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean checkInjectEventsPermission(int injectorPid, int injectorUid) {
+ return mContext.checkPermission(
+ android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
+ == PackageManager.PERMISSION_GRANTED;
}
@SuppressWarnings("unused")
@@ -379,15 +407,14 @@ public class InputManager {
}
@SuppressWarnings("unused")
- public void pokeUserActivityForKey(long whenNanos) {
- long when = whenNanos / 1000000;
- mPowerManagerService.userActivity(when, false,
- LocalPowerManager.BUTTON_EVENT, false);
+ public void pokeUserActivity(long eventTimeNanos, int eventType) {
+ long eventTime = eventTimeNanos / 1000000;
+ mPowerManagerService.userActivity(eventTime, false, eventType, false);
}
@SuppressWarnings("unused")
public void notifyAppSwitchComing() {
- mWindowManagerService.mKeyWaiter.appSwitchComing();
+ mWindowManagerService.mInputMonitor.notifyAppSwitchComing();
}
@SuppressWarnings("unused")
@@ -485,24 +512,5 @@ public class InputManager {
return names.toArray(new String[names.size()]);
}
-
- // TODO All code related to target identification should be moved down into native.
- @SuppressWarnings("unused")
- public int getKeyEventTargets(InputTargetList inputTargets,
- KeyEvent event, int nature, int policyFlags,
- int injectorPid, int injectorUid) {
- inputTargets.clear();
- return mWindowManagerService.getKeyEventTargetsTd(
- inputTargets, event, nature, policyFlags, injectorPid, injectorUid);
- }
-
- @SuppressWarnings("unused")
- public int getMotionEventTargets(InputTargetList inputTargets,
- MotionEvent event, int nature, int policyFlags,
- int injectorPid, int injectorUid) {
- inputTargets.clear();
- return mWindowManagerService.getMotionEventTargetsTd(
- inputTargets, event, nature, policyFlags, injectorPid, injectorUid);
- }
}
}
diff --git a/services/java/com/android/server/InputTargetList.java b/services/java/com/android/server/InputTargetList.java
deleted file mode 100644
index 83acc8fd4744..000000000000
--- a/services/java/com/android/server/InputTargetList.java
+++ /dev/null
@@ -1,104 +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.server;
-
-import android.view.InputChannel;
-import android.view.InputTarget;
-
-/**
- * A specialized list of input targets backed by an array.
- *
- * This class is part of an InputManager optimization to avoid allocating and copying
- * input target arrays unnecessarily on return from JNI callbacks. Internally, it keeps
- * an array full of demand-allocated InputTarget objects that it recycles each time the
- * list is cleared. The used portion of the array is padded with a null.
- *
- * @hide
- */
-public final class InputTargetList {
- private InputTarget[] mArray;
- private int mCount;
-
- /**
- * Creates an empty input target list.
- */
- public InputTargetList() {
- mArray = new InputTarget[8];
- }
-
- /**
- * Clears the input target list.
- */
- public void clear() {
- if (mCount == 0) {
- return;
- }
-
- int count = mCount;
- mCount = 0;
- mArray[count] = mArray[0];
- while (count > 0) {
- count -= 1;
- mArray[count].recycle();
- }
- mArray[0] = null;
- }
-
- /**
- * Adds a new input target to the input target list.
- * @param inputChannel The input channel of the target window.
- * @param flags Input target flags.
- * @param timeoutNanos The input dispatch timeout (before ANR) in nanoseconds or -1 if none.
- * @param xOffset An offset to add to motion X coordinates during delivery.
- * @param yOffset An offset to add to motion Y coordinates during delivery.
- */
- public void add(InputChannel inputChannel, int flags, long timeoutNanos,
- float xOffset, float yOffset) {
- if (inputChannel == null) {
- throw new IllegalArgumentException("inputChannel must not be null");
- }
-
- if (mCount + 1 == mArray.length) {
- InputTarget[] oldArray = mArray;
- mArray = new InputTarget[oldArray.length * 2];
- System.arraycopy(oldArray, 0, mArray, 0, mCount);
- }
-
- // Grab InputTarget from tail (after used section) if available.
- InputTarget inputTarget = mArray[mCount + 1];
- if (inputTarget == null) {
- inputTarget = new InputTarget();
- }
- inputTarget.mInputChannel = inputChannel;
- inputTarget.mFlags = flags;
- inputTarget.mTimeoutNanos = timeoutNanos;
- inputTarget.mXOffset = xOffset;
- inputTarget.mYOffset = yOffset;
-
- mArray[mCount] = inputTarget;
- mCount += 1;
- mArray[mCount] = null;
- }
-
- /**
- * Gets the input targets as a null-terminated array.
- * @return The input target array.
- */
- public InputTarget[] toNullTerminatedArray() {
- return mArray;
- }
-} \ No newline at end of file
diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/InputWindow.java
new file mode 100644
index 000000000000..8da0cf110f1f
--- /dev/null
+++ b/services/java/com/android/server/InputWindow.java
@@ -0,0 +1,66 @@
+/*
+ * 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.server;
+
+import android.view.InputChannel;
+
+/**
+ * Describes input-related window properties for use by the input dispatcher.
+ *
+ * @hide
+ */
+public final class InputWindow {
+ // The input channel associated with the window.
+ public InputChannel inputChannel;
+
+ // Window layout params attributes. (WindowManager.LayoutParams)
+ public int layoutParamsFlags;
+ public int layoutParamsType;
+
+ // Dispatching timeout.
+ public long dispatchingTimeoutNanos;
+
+ // Window frame position.
+ public int frameLeft;
+ public int frameTop;
+
+ // Window touchable area.
+ public int touchableAreaLeft;
+ public int touchableAreaTop;
+ public int touchableAreaRight;
+ public int touchableAreaBottom;
+
+ // Window is visible.
+ public boolean visible;
+
+ // Window has focus.
+ public boolean hasFocus;
+
+ // Window has wallpaper. (window is the current wallpaper target)
+ public boolean hasWallpaper;
+
+ // Input event dispatching is paused.
+ public boolean paused;
+
+ // Id of process and user that owns the window.
+ public int ownerPid;
+ public int ownerUid;
+
+ public void recycle() {
+ inputChannel = null;
+ }
+}
diff --git a/services/java/com/android/server/InputWindowList.java b/services/java/com/android/server/InputWindowList.java
new file mode 100644
index 000000000000..1cbb2cc6f9c0
--- /dev/null
+++ b/services/java/com/android/server/InputWindowList.java
@@ -0,0 +1,89 @@
+/*
+ * 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.server;
+
+
+/**
+ * A specialized list of window information objects backed by an array.
+ *
+ * This class is part of an InputManager optimization to avoid allocating objects and arrays
+ * unnecessarily. Internally, it keeps an array full of demand-allocated objects that it
+ * recycles each time the list is cleared. The used portion of the array is padded with a null.
+ *
+ * The contents of the list are intended to be Z-ordered from top to bottom.
+ *
+ * @hide
+ */
+public final class InputWindowList {
+ private InputWindow[] mArray;
+ private int mCount;
+
+ /**
+ * Creates an empty list.
+ */
+ public InputWindowList() {
+ mArray = new InputWindow[8];
+ }
+
+ /**
+ * Clears the list.
+ */
+ public void clear() {
+ if (mCount == 0) {
+ return;
+ }
+
+ int count = mCount;
+ mCount = 0;
+ mArray[count] = mArray[0];
+ while (count > 0) {
+ count -= 1;
+ mArray[count].recycle();
+ }
+ mArray[0] = null;
+ }
+
+ /**
+ * Adds an uninitialized input window object to the list and returns it.
+ */
+ public InputWindow add() {
+ if (mCount + 1 == mArray.length) {
+ InputWindow[] oldArray = mArray;
+ mArray = new InputWindow[oldArray.length * 2];
+ System.arraycopy(oldArray, 0, mArray, 0, mCount);
+ }
+
+ // Grab object from tail (after used section) if available.
+ InputWindow item = mArray[mCount + 1];
+ if (item == null) {
+ item = new InputWindow();
+ }
+
+ mArray[mCount] = item;
+ mCount += 1;
+ mArray[mCount] = null;
+ return item;
+ }
+
+ /**
+ * Gets the input window objects as a null-terminated array.
+ * @return The input window array.
+ */
+ public InputWindow[] toNullTerminatedArray() {
+ return mArray;
+ }
+} \ No newline at end of file
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 95ab5bc56dff..483f9ebde7e4 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -103,7 +103,6 @@ import android.view.IWindowManager;
import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InputQueue;
-import android.view.InputTarget;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RawInputEvent;
@@ -207,6 +206,9 @@ public class WindowManagerService extends IWindowManager.Stub
// Maximum number of milliseconds to wait for input event injection.
// FIXME is this value reasonable?
private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
+
+ // Default input dispatching timeout in nanoseconds.
+ private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
static final int INJECT_FAILED = 0;
static final int INJECT_SUCCEEDED = 1;
@@ -570,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;
@@ -605,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) {
@@ -2061,8 +2065,8 @@ public class WindowManagerService extends IWindowManager.Stub
boolean focusChanged = false;
if (win.canReceiveKeys()) {
- if ((focusChanged=updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS))
- == true) {
+ focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS);
+ if (focusChanged) {
imMayMove = false;
}
}
@@ -2078,10 +2082,9 @@ public class WindowManagerService extends IWindowManager.Stub
//dump();
if (focusChanged) {
- if (mCurrentFocus != null) {
- mKeyWaiter.handleNewWindowLocked(mCurrentFocus);
- }
+ finishUpdateFocusedWindowAfterAssignLayersLocked();
}
+
if (localLOGV) Slog.v(
TAG, "New client " + client.asBinder()
+ ": window=" + win);
@@ -2183,10 +2186,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
private void removeWindowInnerLocked(Session session, WindowState win) {
- mKeyWaiter.finishedKey(session, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
- mKeyWaiter.releasePendingPointerLocked(win.mSession);
- mKeyWaiter.releasePendingTrackballLocked(win.mSession);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBeingRemovedLw(win);
+ } else {
+ mKeyWaiter.finishedKey(session, win.mClient, true,
+ KeyWaiter.RETURN_NOTHING);
+ mKeyWaiter.releasePendingPointerLocked(win.mSession);
+ mKeyWaiter.releasePendingTrackballLocked(win.mSession);
+ }
win.mRemoved = true;
@@ -2554,8 +2561,12 @@ public class WindowManagerService extends IWindowManager.Stub
applyAnimationLocked(win, transit, false)) {
focusMayChange = true;
win.mExiting = true;
- mKeyWaiter.finishedKey(session, client, true,
- KeyWaiter.RETURN_NOTHING);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBecomingInvisibleLw(win);
+ } else {
+ mKeyWaiter.finishedKey(session, client, true,
+ KeyWaiter.RETURN_NOTHING);
+ }
} else if (win.isAnimating()) {
// Currently in a hide animation... turn this into
// an exit.
@@ -3016,8 +3027,12 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.isVisibleNow()) {
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
- mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBeingRemovedLw(win);
+ } else {
+ mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
+ KeyWaiter.RETURN_NOTHING);
+ }
changed = true;
}
}
@@ -3048,6 +3063,20 @@ public class WindowManagerService extends IWindowManager.Stub
"addAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
+
+ // Get the dispatching timeout here while we are not holding any locks so that it
+ // can be cached by the AppWindowToken. The timeout value is used later by the
+ // input dispatcher in code that does hold locks. If we did not cache the value
+ // here we would run the chance of introducing a deadlock between the window manager
+ // (which holds locks while updating the input dispatcher state) and the activity manager
+ // (which holds locks while querying the application token).
+ long inputDispatchingTimeoutNanos;
+ try {
+ inputDispatchingTimeoutNanos = token.getKeyDispatchingTimeout() * 1000000L;
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Could not get dispatching timeout.", ex);
+ inputDispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ }
synchronized(mWindowMap) {
AppWindowToken wtoken = findAppWindowToken(token.asBinder());
@@ -3056,6 +3085,7 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
wtoken = new AppWindowToken(token);
+ wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
wtoken.groupId = groupId;
wtoken.appFullscreen = fullscreen;
wtoken.requestedOrientation = requestedOrientation;
@@ -3319,7 +3349,13 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FOCUS) Slog.v(TAG, "Clearing focused app, was " + mFocusedApp);
changed = mFocusedApp != null;
mFocusedApp = null;
- mKeyWaiter.tickle();
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ if (changed) {
+ mInputMonitor.setFocusedAppLw(null);
+ }
+ } else {
+ mKeyWaiter.tickle();
+ }
} else {
AppWindowToken newFocus = findAppWindowToken(token);
if (newFocus == null) {
@@ -3329,7 +3365,13 @@ public class WindowManagerService extends IWindowManager.Stub
changed = mFocusedApp != newFocus;
mFocusedApp = newFocus;
if (DEBUG_FOCUS) Slog.v(TAG, "Set focused app to: " + mFocusedApp);
- mKeyWaiter.tickle();
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ if (changed) {
+ mInputMonitor.setFocusedAppLw(newFocus);
+ }
+ } else {
+ mKeyWaiter.tickle();
+ }
}
if (moveFocusNow && changed) {
@@ -3640,8 +3682,12 @@ public class WindowManagerService extends IWindowManager.Stub
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
}
- mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBecomingInvisibleLw(win);
+ } else {
+ mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
+ KeyWaiter.RETURN_NOTHING);
+ }
changed = true;
}
}
@@ -3926,7 +3972,11 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FOCUS) Slog.v(TAG, "Removing focused app token:" + wtoken);
mFocusedApp = null;
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
- mKeyWaiter.tickle();
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.setFocusedAppLw(null);
+ } else {
+ mKeyWaiter.tickle();
+ }
}
} else {
Slog.w(TAG, "Attempted to remove non-existing app token: " + token);
@@ -5075,456 +5125,375 @@ public class WindowManagerService extends IWindowManager.Stub
return true;
}
- /* Notifies the window manager about a broken input channel.
- *
- * Called by the InputManager.
- */
- public void notifyInputChannelBroken(InputChannel inputChannel) {
- synchronized (mWindowMap) {
- WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
- if (windowState == null) {
- return; // irrelevant
- }
-
- Slog.i(TAG, "WINDOW DIED " + windowState);
- removeWindowLocked(windowState.mSession, windowState);
- }
- }
-
- /* Notifies the window manager about a broken input channel.
- *
- * Called by the InputManager.
- */
- public long notifyInputChannelANR(InputChannel inputChannel) {
- IApplicationToken appToken;
- synchronized (mWindowMap) {
- WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
- if (windowState == null) {
- return -2; // irrelevant, abort dispatching (-2)
- }
-
- Slog.i(TAG, "Input event dispatching timed out sending to "
- + windowState.mAttrs.getTitle());
- appToken = windowState.getAppToken();
- }
-
- try {
- // Notify the activity manager about the timeout and let it decide whether
- // to abort dispatching or keep waiting.
- boolean abort = appToken.keyDispatchingTimedOut();
- if (abort) {
- return -2; // abort dispatching
- }
-
- // Return new timeout.
- // We use -1 for infinite timeout to avoid clash with -2 magic number.
- long newTimeout = appToken.getKeyDispatchingTimeout() * 1000000;
- return newTimeout < 0 ? -1 : newTimeout;
- } catch (RemoteException ex) {
- return -2; // abort dispatching
- }
- }
-
- /* Notifies the window manager about a broken input channel.
- *
- * Called by the InputManager.
- */
- public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
- // Nothing to do just now.
- // Just wait for the user to dismiss the ANR dialog.
-
- // TODO We could try to automatically dismiss the ANR dialog on recovery
- // although that might be disorienting.
- }
-
- private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) {
- int windowCount = mWindows.size();
- for (int i = 0; i < windowCount; i++) {
- WindowState windowState = (WindowState) mWindows.get(i);
- if (windowState.mInputChannel == inputChannel) {
- return windowState;
- }
- }
-
- return null;
- }
-
// -------------------------------------------------------------
// Input Events and Focus Management
// -------------------------------------------------------------
- private boolean checkInjectionPermissionTd(WindowState focus,
- int injectorPid, int injectorUid) {
- if (injectorUid > 0 && (focus == null || injectorUid != focus.mSession.mUid)) {
- if (mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission denied: injecting key event from pid "
- + injectorPid + " uid " + injectorUid + " to window " + focus
- + " owned by uid " + focus.mSession.mUid);
- return false;
- }
- }
- return true;
- }
+ InputMonitor mInputMonitor = new InputMonitor();
- /* Gets the input targets for a key event.
- *
- * Called by the InputManager on the InputDispatcher thread.
- */
- public int getKeyEventTargetsTd(InputTargetList inputTargets,
- KeyEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) {
- if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event);
-
- // TODO what do we do with mDisplayFrozen?
- // TODO what do we do with focus.mToken.paused?
+ /* Tracks the progress of input dispatch and ensures that input dispatch state
+ * is kept in sync with changes in window focus, visibility, registration, and
+ * other relevant Window Manager state transitions. */
+ final class InputMonitor {
+ // Current window with input focus for keys and other non-touch events. May be null.
+ private WindowState mInputFocus;
- WindowState focus = getFocusedWindow();
+ // When true, prevents input dispatch from proceeding until set to false again.
+ private boolean mInputDispatchFrozen;
- if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) {
- return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- }
+ // When true, input dispatch proceeds normally. Otherwise all events are dropped.
+ private boolean mInputDispatchEnabled = true;
+
+ // Temporary list of windows information to provide to the input dispatcher.
+ private InputWindowList mTempInputWindows = new InputWindowList();
- if (mPolicy.interceptKeyTi(focus, event.getKeyCode(), event.getMetaState(),
- event.getAction() == KeyEvent.ACTION_DOWN,
- event.getRepeatCount(), event.getFlags())) {
- // Policy consumed the event.
- return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
- }
+ // Temporary input application object to provide to the input dispatcher.
+ private InputApplication mTempInputApplication = new InputApplication();
- if (focus == null) {
- return InputManager.INPUT_EVENT_INJECTION_FAILED;
+ /* Notifies the window manager about a broken input channel.
+ *
+ * Called by the InputManager.
+ */
+ public void notifyInputChannelBroken(InputChannel inputChannel) {
+ synchronized (mWindowMap) {
+ WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+ if (windowState == null) {
+ return; // irrelevant
+ }
+
+ Slog.i(TAG, "WINDOW DIED " + windowState);
+ removeWindowLocked(windowState.mSession, windowState);
+ }
}
- wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
-
- addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC);
- return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
- }
-
- /* Gets the input targets for a motion event.
- *
- * Called by the InputManager on the InputDispatcher thread.
- */
- public int getMotionEventTargetsTd(InputTargetList inputTargets,
- MotionEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) {
- switch (nature) {
- case InputQueue.INPUT_EVENT_NATURE_TRACKBALL:
- return getMotionEventTargetsForTrackballTd(inputTargets, event, policyFlags,
- injectorPid, injectorUid);
-
- case InputQueue.INPUT_EVENT_NATURE_TOUCH:
- return getMotionEventTargetsForTouchTd(inputTargets, event, policyFlags,
- injectorPid, injectorUid);
+ /* Notifies the window manager about an input channel that is not responding.
+ * The method can either cause dispatching to be aborted by returning -2 or
+ * return a new timeout in nanoseconds.
+ *
+ * Called by the InputManager.
+ */
+ public long notifyInputChannelANR(InputChannel inputChannel) {
+ AppWindowToken token;
+ synchronized (mWindowMap) {
+ WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+ if (windowState == null) {
+ return -2; // irrelevant, abort dispatching (-2)
+ }
- default:
- return InputManager.INPUT_EVENT_INJECTION_FAILED;
+ Slog.i(TAG, "Input event dispatching timed out sending to "
+ + windowState.mAttrs.getTitle());
+ token = windowState.mAppToken;
+ }
+
+ return notifyANRInternal(token);
}
- }
- /* Gets the input targets for a trackball event.
- *
- * Called by the InputManager on the InputDispatcher thread.
- */
- private int getMotionEventTargetsForTrackballTd(InputTargetList inputTargets,
- MotionEvent event, int policyFlags, int injectorPid, int injectorUid) {
- WindowState focus = getFocusedWindow();
-
- if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) {
- return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- }
-
- if (focus == null) {
- return InputManager.INPUT_EVENT_INJECTION_FAILED;
+ /* Notifies the window manager about an input channel spontaneously recovering from ANR
+ * by successfully delivering the event that originally timed out.
+ *
+ * Called by the InputManager.
+ */
+ public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
+ // Nothing to do just now.
+ // Just wait for the user to dismiss the ANR dialog.
}
- wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
-
- addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC);
- return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
- }
-
- /* Set to true when a fat touch has been detected during the processing of a touch event.
- *
- * Only used by getMotionEventTargetsForTouchTd.
- * Set to true whenever a fat touch is detected and reset to false on ACTION_UP.
- */
- private boolean mFatTouch;
-
- /* Set to true when we think the touch event.
- *
- * Only used by getMotionEventTargetsForTouchTd.
- * Set to true on ACTION_DOWN and set to false on ACTION_UP.
- */
- private boolean mTouchDown;
-
- /* Current target of Motion events.
- *
- * Only used by getMotionEventTargetsForTouchTd.
- * Initialized on ACTION_DOWN and cleared on ACTION_UP.
- */
- private WindowState mTouchFocus;
+ /* Notifies the window manager about an application that is not responding
+ * in general rather than with respect to a particular input channel.
+ * The method can either cause dispatching to be aborted by returning -2 or
+ * return a new timeout in nanoseconds.
+ *
+ * Called by the InputManager.
+ */
+ public long notifyANR(Object token) {
+ AppWindowToken appWindowToken = (AppWindowToken) token;
- /* Windows above the target that would like to receive an "outside" touch event
- * for any down events outside of them.
- *
- * Only used by getMotionEventTargetsForTouchTd.
- * Initialized on ACTION_DOWN and cleared immediately afterwards.
- */
- private ArrayList<WindowState> mOutsideTouchTargets = new ArrayList<WindowState>();
-
- /* Wallpaper windows that are currently receiving touch events.
- *
- * Only used by getMotionEventTargetsForTouchTd.
- * Initialized on ACTION_DOWN and cleared on ACTION_UP.
- */
- private ArrayList<WindowState> mWallpaperTouchTargets = new ArrayList<WindowState>();
-
- /* Gets the input targets for a touch event.
- *
- * Called by the InputManager on the InputDispatcher thread.
- */
- private int getMotionEventTargetsForTouchTd(InputTargetList inputTargets,
- MotionEvent event, int policyFlags, int injectorPid, int injectorUid) {
- final int action = event.getAction();
-
- if (action == MotionEvent.ACTION_DOWN) {
- updateTouchFocusBeforeDownTd(event, policyFlags);
- } else {
- updateTouchFocusBeforeNonDownTd(event, policyFlags);
+ Slog.i(TAG, "Input event dispatching timed out sending to application "
+ + appWindowToken.stringName);
+ return notifyANRInternal(appWindowToken);
}
-
- boolean skipDelivery = false;
- int touchTargetFlags = 0;
- int injectionResult = InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
- WindowState focusedTouchTarget = mTouchFocus;
- if (focusedTouchTarget == null) {
- // In this case we are either dropping the event, or have received
- // a move or up without a down. It is common to receive move
- // events in such a way, since this means the user is moving the
- // pointer without actually pressing down. All other cases should
- // be atypical, so let's log them.
- if (action != MotionEvent.ACTION_MOVE) {
- Slog.w(TAG, "No window to dispatch pointer action " + action);
- injectionResult = InputManager.INPUT_EVENT_INJECTION_FAILED;
- }
- } else {
- // We have a valid focused touch target.
- if (! checkInjectionPermissionTd(focusedTouchTarget, injectorPid, injectorUid)) {
- return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- }
-
- wakeupIfNeeded(focusedTouchTarget, eventType(event));
-
- if ((focusedTouchTarget.mAttrs.flags &
- WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
- // Target wants to ignore fat touch events
- boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event);
-
- if (cheekPress) {
- if ((action == MotionEvent.ACTION_DOWN)) {
- mFatTouch = true;
- skipDelivery = true;
- } else {
- if (! mFatTouch) {
- // cancel the earlier event
- touchTargetFlags |= InputTarget.FLAG_CANCEL;
- mFatTouch = true;
- } else {
- skipDelivery = true;
- }
+ private long notifyANRInternal(AppWindowToken token) {
+ if (token != null && token.appToken != null) {
+ try {
+ // Notify the activity manager about the timeout and let it decide whether
+ // to abort dispatching or keep waiting.
+ boolean abort = token.appToken.keyDispatchingTimedOut();
+ if (! abort) {
+ // The activity manager declined to abort dispatching.
+ // Wait a bit longer and timeout again later.
+ return token.inputDispatchingTimeoutNanos;
}
+ } catch (RemoteException ex) {
}
}
+ return -2; // abort dispatching
}
- if (! skipDelivery) {
- int outsideTargetCount = mOutsideTouchTargets.size();
- for (int i = 0; i < outsideTargetCount; i++) {
- WindowState outsideTouchTarget = mOutsideTouchTargets.get(i);
- addInputTargetTd(inputTargets, outsideTouchTarget,
- InputTarget.FLAG_OUTSIDE | touchTargetFlags);
- }
-
- int wallpaperTargetCount = mWallpaperTouchTargets.size();
- for (int i = 0; i < wallpaperTargetCount; i++) {
- WindowState wallpaperTouchTarget = mWallpaperTouchTargets.get(i);
- addInputTargetTd(inputTargets, wallpaperTouchTarget,
- touchTargetFlags);
- }
-
- if (focusedTouchTarget != null) {
- addInputTargetTd(inputTargets, focusedTouchTarget,
- InputTarget.FLAG_SYNC | touchTargetFlags);
+ private WindowState getWindowStateForInputChannel(InputChannel inputChannel) {
+ synchronized (mWindowMap) {
+ return getWindowStateForInputChannelLocked(inputChannel);
}
}
-
- if (action == MotionEvent.ACTION_UP) {
- updateTouchFocusAfterUpTd(event, policyFlags);
- }
- return injectionResult;
- }
-
- private void updateTouchFocusBeforeDownTd(MotionEvent event, int policyFlags) {
- if (mTouchDown) {
- // This is weird, we got a down, but we thought it was already down!
- // XXX: We should probably send an ACTION_UP to the current target.
- Slog.w(TAG, "Pointer down received while already down in: " + mTouchFocus);
- updateTouchFocusAfterUpTd(event, policyFlags);
+ private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) {
+ int windowCount = mWindows.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowState windowState = (WindowState) mWindows.get(i);
+ if (windowState.mInputChannel == inputChannel) {
+ return windowState;
+ }
+ }
+
+ return null;
}
- mTouchDown = true;
- mPowerManager.logPointerDownEvent();
-
- final boolean screenWasOff = (policyFlags & WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
- synchronized (mWindowMap) {
- final int x = (int) event.getX();
- final int y = (int) event.getY();
-
+ /* Updates the cached window information provided to the input dispatcher. */
+ public void updateInputWindowsLw() {
+ // Populate the input window list with information about all of the windows that
+ // could potentially receive input.
+ // As an optimization, we could try to prune the list of windows but this turns
+ // out to be difficult because only the native code knows for sure which window
+ // currently has touch focus.
final ArrayList windows = mWindows;
final int N = windows.size();
- WindowState topErrWindow = null;
- final Rect tmpRect = mTempRect;
- for (int i= N - 1; i >= 0; i--) {
- WindowState child = (WindowState) windows.get(i);
- //Slog.i(TAG, "Checking dispatch to: " + child);
+ for (int i = N - 1; i >= 0; i--) {
+ final WindowState child = (WindowState) windows.get(i);
+ if (child.mInputChannel == null) {
+ // Skip this window because it cannot possibly receive input.
+ continue;
+ }
final int flags = child.mAttrs.flags;
- if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
- if (topErrWindow == null) {
- topErrWindow = child;
- }
- }
+ final int type = child.mAttrs.type;
- if (!child.isVisibleLw()) {
- //Slog.i(TAG, "Not visible!");
- continue;
- }
+ final boolean hasFocus = (child == mInputFocus);
+ final boolean isVisible = child.isVisibleLw();
+ final boolean hasWallpaper = (child == mWallpaperTarget)
+ && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
- if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) {
- tmpRect.set(child.mFrame);
- if (child.mTouchableInsets == ViewTreeObserver
- .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
- // The touch is inside of the window if it is
- // inside the frame, AND the content part of that
- // frame that was given by the application.
- tmpRect.left += child.mGivenContentInsets.left;
- tmpRect.top += child.mGivenContentInsets.top;
- tmpRect.right -= child.mGivenContentInsets.right;
- tmpRect.bottom -= child.mGivenContentInsets.bottom;
- } else if (child.mTouchableInsets == ViewTreeObserver
- .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
- // The touch is inside of the window if it is
- // inside the frame, AND the visible part of that
- // frame that was given by the application.
- tmpRect.left += child.mGivenVisibleInsets.left;
- tmpRect.top += child.mGivenVisibleInsets.top;
- tmpRect.right -= child.mGivenVisibleInsets.right;
- tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
- }
- final int touchFlags = flags &
- (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
- if (tmpRect.contains(x, y) || touchFlags == 0) {
- //Slog.i(TAG, "Using this target!");
- if (! screenWasOff || (flags &
- WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
- mTouchFocus = child;
- } else {
- //Slog.i(TAG, "Waking, skip!");
- mTouchFocus = null;
- }
+ // Add a window to our list of input windows.
+ final InputWindow inputWindow = mTempInputWindows.add();
+ inputWindow.inputChannel = child.mInputChannel;
+ inputWindow.layoutParamsFlags = flags;
+ inputWindow.layoutParamsType = type;
+ inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
+ inputWindow.visible = isVisible;
+ inputWindow.hasFocus = hasFocus;
+ inputWindow.hasWallpaper = hasWallpaper;
+ inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false;
+ inputWindow.ownerPid = child.mSession.mPid;
+ inputWindow.ownerUid = child.mSession.mUid;
+
+ final Rect frame = child.mFrame;
+ inputWindow.frameLeft = frame.left;
+ inputWindow.frameTop = frame.top;
+
+ switch (child.mTouchableInsets) {
+ default:
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+ inputWindow.touchableAreaLeft = frame.left;
+ inputWindow.touchableAreaTop = frame.top;
+ inputWindow.touchableAreaRight = frame.right;
+ inputWindow.touchableAreaBottom = frame.bottom;
+ break;
+
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
+ Rect inset = child.mGivenContentInsets;
+ inputWindow.touchableAreaLeft = frame.left + inset.left;
+ inputWindow.touchableAreaTop = frame.top + inset.top;
+ inputWindow.touchableAreaRight = frame.right - inset.right;
+ inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
+ break;
+ }
+
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
+ Rect inset = child.mGivenVisibleInsets;
+ inputWindow.touchableAreaLeft = frame.left + inset.left;
+ inputWindow.touchableAreaTop = frame.top + inset.top;
+ inputWindow.touchableAreaRight = frame.right - inset.right;
+ inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
break;
}
- }
-
- if ((flags & WindowManager.LayoutParams
- .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
- //Slog.i(TAG, "Adding to outside target list: " + child);
- mOutsideTouchTargets.add(child);
}
}
- // If there's an error window but it's not accepting focus (typically because
- // it is not yet visible) just wait for it -- any other focused window may in fact
- // be in ANR state.
- if (topErrWindow != null && mTouchFocus != topErrWindow) {
- mTouchFocus = null;
- }
+ // Send windows to native code.
+ mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray());
- // Drop the touch focus if the window is not visible.
- if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) {
- mTouchFocus = null;
- }
+ // Clear the list in preparation for the next round.
+ // Also avoids keeping InputChannel objects referenced unnecessarily.
+ mTempInputWindows.clear();
+ }
+
+ /* Notifies that an app switch key (BACK / HOME) has just been pressed.
+ * This essentially starts a .5 second timeout for the application to process
+ * subsequent input events while waiting for the app switch to occur. If it takes longer
+ * than this, the pending events will be dropped.
+ */
+ public void notifyAppSwitchComing() {
+ // TODO Not implemented yet. Should go in the native side.
+ }
+
+ /* Provides an opportunity for the window manager policy to intercept early key
+ * processing as soon as the key has been read from the device. */
+ public int interceptKeyBeforeQueueing(int deviceId, int type, int scanCode,
+ int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) {
+ RawInputEvent event = new RawInputEvent();
+ event.deviceId = deviceId;
+ event.type = type;
+ event.scancode = scanCode;
+ event.keycode = keyCode;
+ event.flags = policyFlags;
+ event.value = value;
+ event.when = whenNanos / 1000000;
- // Determine wallpaper targets.
- if (mTouchFocus != null
- && mTouchFocus == mWallpaperTarget
- && mTouchFocus.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD) {
- int curTokenIndex = mWallpaperTokens.size();
- while (curTokenIndex > 0) {
- curTokenIndex--;
- WindowToken token = mWallpaperTokens.get(curTokenIndex);
-
- int curWallpaperIndex = token.windows.size();
- while (curWallpaperIndex > 0) {
- curWallpaperIndex--;
- WindowState wallpaper = token.windows.get(curWallpaperIndex);
- if ((wallpaper.mAttrs.flags &
- WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) {
- mWallpaperTouchTargets.add(wallpaper);
+ return mPolicy.interceptKeyTq(event, isScreenOn);
+ }
+
+ /* Provides an opportunity for the window manager policy to process a key before
+ * ordinary dispatch. */
+ public boolean interceptKeyBeforeDispatching(InputChannel focus, int keyCode,
+ int metaState, boolean down, int repeatCount, int policyFlags) {
+ WindowState windowState = getWindowStateForInputChannel(focus);
+ return mPolicy.interceptKeyTi(windowState, keyCode, metaState, down, repeatCount,
+ policyFlags);
+ }
+
+ /* Called when the current input focus changes.
+ * Layer assignment is assumed to be complete by the time this is called.
+ */
+ public void setInputFocusLw(WindowState newWindow) {
+ if (DEBUG_INPUT) {
+ Slog.d(TAG, "Input focus has changed to " + newWindow);
+ }
+
+ if (newWindow != mInputFocus) {
+ if (newWindow != null && newWindow.canReceiveKeys()) {
+ // If the new input focus is an error window or appears above the current
+ // input focus, preempt any pending synchronous dispatch so that we can
+ // start delivering events to the new input focus as soon as possible.
+ if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "New SYSTEM_ERROR window; resetting state");
}
+ preemptInputDispatchLw();
+ } else if (mInputFocus != null && newWindow.mLayer > mInputFocus.mLayer) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Transferring focus to new window at higher layer: "
+ + "old win layer=" + mInputFocus.mLayer
+ + ", new win layer=" + newWindow.mLayer);
+ }
+ preemptInputDispatchLw();
}
+
+ // Displaying a window implicitly causes dispatching to be unpaused.
+ // This is to protect against bugs if someone pauses dispatching but
+ // forgets to resume.
+ newWindow.mToken.paused = false;
}
+
+ mInputFocus = newWindow;
+ updateInputWindowsLw();
}
}
- }
-
- private void updateTouchFocusBeforeNonDownTd(MotionEvent event, int policyFlags) {
- synchronized (mWindowMap) {
- // Drop the touch focus if the window is not visible.
- if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) {
- mTouchFocus = null;
- mWallpaperTouchTargets.clear();
+
+ public void windowIsBecomingInvisibleLw(WindowState window) {
+ // The window is becoming invisible. Preempt input dispatch in progress
+ // so that the next window below can receive focus.
+ if (window == mInputFocus) {
+ mInputFocus = null;
+ preemptInputDispatchLw();
}
+
+ updateInputWindowsLw();
}
- }
-
- private void updateTouchFocusAfterUpTd(MotionEvent event, int policyFlags) {
- mFatTouch = false;
- mTouchDown = false;
- mTouchFocus = null;
- mOutsideTouchTargets.clear();
- mWallpaperTouchTargets.clear();
- mPowerManager.logPointerUpEvent();
- }
-
- /* Adds a window to a list of input targets.
- * Do NOT call this method while holding any locks because the call to
- * appToken.getKeyDispatchingTimeout() can potentially call into the ActivityManager
- * and create a deadlock hazard.
- */
- private void addInputTargetTd(InputTargetList inputTargets, WindowState window, int flags) {
- if (window.mInputChannel == null) {
- return;
+ /* Tells the dispatcher to stop waiting for its current synchronous event targets.
+ * Essentially, just makes those dispatches asynchronous so a new dispatch cycle
+ * can begin.
+ */
+ private void preemptInputDispatchLw() {
+ mInputManager.preemptInputDispatch();
}
- long timeoutNanos = -1;
- IApplicationToken appToken = window.getAppToken();
-
- if (appToken != null) {
- try {
- timeoutNanos = appToken.getKeyDispatchingTimeout() * 1000000;
- } catch (RemoteException ex) {
- Slog.w(TAG, "Could not get key dispatching timeout.", ex);
+ public void setFocusedAppLw(AppWindowToken newApp) {
+ // Focused app has changed.
+ if (newApp == null) {
+ mInputManager.setFocusedApplication(null);
+ } else {
+ mTempInputApplication.name = newApp.toString();
+ mTempInputApplication.dispatchingTimeoutNanos =
+ newApp.inputDispatchingTimeoutNanos;
+ mTempInputApplication.token = newApp;
+
+ mInputManager.setFocusedApplication(mTempInputApplication);
+ }
+ }
+
+ public void windowIsBeingRemovedLw(WindowState window) {
+ // Window is being removed.
+ updateInputWindowsLw();
+ }
+
+ public void pauseDispatchingLw(WindowToken window) {
+ if (! window.paused) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Pausing WindowToken " + window);
+ }
+
+ window.paused = true;
+ updateInputWindowsLw();
+ }
+ }
+
+ public void resumeDispatchingLw(WindowToken window) {
+ if (window.paused) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Resuming WindowToken " + window);
+ }
+
+ window.paused = false;
+ updateInputWindowsLw();
}
}
- inputTargets.add(window.mInputChannel, flags, timeoutNanos,
- - window.mFrame.left, - window.mFrame.top);
+ public void freezeInputDispatchingLw() {
+ if (! mInputDispatchFrozen) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Freezing input dispatching");
+ }
+
+ mInputDispatchFrozen = true;
+ updateInputDispatchModeLw();
+ }
+ }
+
+ public void thawInputDispatchingLw() {
+ if (mInputDispatchFrozen) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Thawing input dispatching");
+ }
+
+ mInputDispatchFrozen = false;
+ updateInputDispatchModeLw();
+ }
+ }
+
+ public void setEventDispatchingLw(boolean enabled) {
+ if (mInputDispatchEnabled != enabled) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Setting event dispatching to " + enabled);
+ }
+
+ mInputDispatchEnabled = enabled;
+ updateInputDispatchModeLw();
+ }
+ }
+
+ private void updateInputDispatchModeLw() {
+ mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
+ }
}
private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
@@ -5582,6 +5551,8 @@ public class WindowManagerService extends IWindowManager.Stub
return OTHER_EVENT;
}
}
+
+ private boolean mFatTouch; // remove me together with dispatchPointer
/**
* @return Returns true if event was dispatched, false if it was dropped for any reason
@@ -5978,7 +5949,11 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
WindowToken token = mTokenMap.get(_token);
if (token != null) {
- mKeyWaiter.pauseDispatchingLocked(token);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.pauseDispatchingLw(token);
+ } else {
+ mKeyWaiter.pauseDispatchingLocked(token);
+ }
}
}
}
@@ -5992,7 +5967,11 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
WindowToken token = mTokenMap.get(_token);
if (token != null) {
- mKeyWaiter.resumeDispatchingLocked(token);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.resumeDispatchingLw(token);
+ } else {
+ mKeyWaiter.resumeDispatchingLocked(token);
+ }
}
}
}
@@ -6004,7 +5983,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
synchronized (mWindowMap) {
- mKeyWaiter.setEventDispatchingLocked(enabled);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.setEventDispatchingLw(enabled);
+ } else {
+ mKeyWaiter.setEventDispatchingLocked(enabled);
+ }
}
}
@@ -7383,6 +7366,9 @@ public class WindowManagerService extends IWindowManager.Stub
public void finishKey(IWindow window) {
if (localLOGV) Slog.v(
TAG, "IWindow finishKey called for " + window);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ throw new IllegalStateException("Should not be called anymore.");
+ }
mKeyWaiter.finishedKey(this, window, false,
KeyWaiter.RETURN_NOTHING);
}
@@ -7390,6 +7376,9 @@ public class WindowManagerService extends IWindowManager.Stub
public MotionEvent getPendingPointerMove(IWindow window) {
if (localLOGV) Slog.v(
TAG, "IWindow getPendingMotionEvent called for " + window);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ throw new IllegalStateException("Should not be called anymore.");
+ }
return mKeyWaiter.finishedKey(this, window, false,
KeyWaiter.RETURN_PENDING_POINTER);
}
@@ -7397,6 +7386,9 @@ public class WindowManagerService extends IWindowManager.Stub
public MotionEvent getPendingTrackballMove(IWindow window) {
if (localLOGV) Slog.v(
TAG, "IWindow getPendingMotionEvent called for " + window);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ throw new IllegalStateException("Should not be called anymore.");
+ }
return mKeyWaiter.finishedKey(this, window, false,
KeyWaiter.RETURN_PENDING_TRACKBALL);
}
@@ -7943,6 +7935,12 @@ public class WindowManagerService extends IWindowManager.Stub
public IApplicationToken getAppToken() {
return mAppToken != null ? mAppToken.appToken : null;
}
+
+ public long getInputDispatchingTimeoutNanos() {
+ return mAppToken != null
+ ? mAppToken.inputDispatchingTimeoutNanos
+ : DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ }
public boolean hasAppShownWindows() {
return mAppToken != null ? mAppToken.firstWindowDrawn : false;
@@ -8079,10 +8077,12 @@ public class WindowManagerService extends IWindowManager.Stub
// Window is no longer on-screen, so can no longer receive
// key events... if we were waiting for it to finish
// handling a key event, the wait is over!
- mKeyWaiter.finishedKey(mSession, mClient, true,
- KeyWaiter.RETURN_NOTHING);
- mKeyWaiter.releasePendingPointerLocked(mSession);
- mKeyWaiter.releasePendingTrackballLocked(mSession);
+ if (! ENABLE_NATIVE_INPUT_DISPATCH) {
+ mKeyWaiter.finishedKey(mSession, mClient, true,
+ KeyWaiter.RETURN_NOTHING);
+ mKeyWaiter.releasePendingPointerLocked(mSession);
+ mKeyWaiter.releasePendingTrackballLocked(mSession);
+ }
if (mAppToken != null && this == mAppToken.startingWindow) {
mAppToken.startingDisplayed = false;
@@ -8098,6 +8098,10 @@ public class WindowManagerService extends IWindowManager.Stub
i--;
WindowState c = (WindowState)mChildWindows.get(i);
c.mAttachedHidden = true;
+
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBecomingInvisibleLw(c);
+ }
}
if (mReportDestroySurface) {
@@ -8405,7 +8409,14 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "Error hiding surface in " + this, e);
}
mLastHidden = true;
- mKeyWaiter.releasePendingPointerLocked(mSession);
+
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ for (int i=0; i<N; i++) {
+ mInputMonitor.windowIsBecomingInvisibleLw((WindowState)mChildWindows.get(i));
+ }
+ } else {
+ mKeyWaiter.releasePendingPointerLocked(mSession);
+ }
}
mExiting = false;
if (mRemoveOnExit) {
@@ -9098,6 +9109,9 @@ public class WindowManagerService extends IWindowManager.Stub
int groupId = -1;
boolean appFullscreen;
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+ // The input dispatching timeout for this application token in nanoseconds.
+ long inputDispatchingTimeoutNanos;
// These are used for determining when all windows associated with
// an activity have been drawn, so they can be made visible together
@@ -10158,6 +10172,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
}
+
+ // Window frames may have changed. Tell the input dispatcher about it.
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.updateInputWindowsLw();
+ }
return mPolicy.finishLayoutLw();
}
@@ -10958,7 +10977,11 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "Exception hiding surface in " + w);
}
}
- mKeyWaiter.releasePendingPointerLocked(w.mSession);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBecomingInvisibleLw(w);
+ } else {
+ mKeyWaiter.releasePendingPointerLocked(w.mSession);
+ }
}
// If we are waiting for this window to handle an
// orientation change, well, it is hidden, so
@@ -11548,14 +11571,26 @@ public class WindowManagerService extends IWindowManager.Stub
assignLayersLocked();
}
}
-
- if (newFocus != null && mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
- mKeyWaiter.handleNewWindowLocked(newFocus);
+
+ if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
+ // If we defer assigning layers, then the caller is responsible for
+ // doing this part.
+ finishUpdateFocusedWindowAfterAssignLayersLocked();
}
return true;
}
return false;
}
+
+ private void finishUpdateFocusedWindowAfterAssignLayersLocked() {
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.setInputFocusLw(mCurrentFocus);
+ } else {
+ if (mCurrentFocus != null) {
+ mKeyWaiter.handleNewWindowLocked(mCurrentFocus);
+ }
+ }
+ }
private WindowState computeFocusedWindowLocked() {
WindowState result = null;
@@ -11634,8 +11669,10 @@ public class WindowManagerService extends IWindowManager.Stub
// still frozen, the events will continue to be blocked while the
// successive orientation change is processed. To prevent spurious
// ANRs, we reset the event dispatch timeout in this case.
- synchronized (mKeyWaiter) {
- mKeyWaiter.mWasFrozen = true;
+ if (! ENABLE_NATIVE_INPUT_DISPATCH) {
+ synchronized (mKeyWaiter) {
+ mKeyWaiter.mWasFrozen = true;
+ }
}
return;
}
@@ -11658,6 +11695,11 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FREEZE) Slog.v(TAG, "*** FREEZING DISPLAY", new RuntimeException());
mDisplayFrozen = true;
+
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.freezeInputDispatchingLw();
+ }
+
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
mNextAppTransitionPackage = null;
@@ -11691,9 +11733,13 @@ public class WindowManagerService extends IWindowManager.Stub
// Reset the key delivery timeout on unfreeze, too. We force a wakeup here
// too because regular key delivery processing should resume immediately.
- synchronized (mKeyWaiter) {
- mKeyWaiter.mWasFrozen = true;
- mKeyWaiter.notifyAll();
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.thawInputDispatchingLw();
+ } else {
+ synchronized (mKeyWaiter) {
+ mKeyWaiter.mWasFrozen = true;
+ mKeyWaiter.notifyAll();
+ }
}
// While the display is frozen we don't re-compute the orientation
@@ -11949,20 +11995,25 @@ public class WindowManagerService extends IWindowManager.Stub
}
pw.print(" DisplayWidth="); pw.print(mDisplay.getWidth());
pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight());
- pw.println(" KeyWaiter state:");
- pw.print(" mLastWin="); pw.print(mKeyWaiter.mLastWin);
- pw.print(" mLastBinder="); pw.println(mKeyWaiter.mLastBinder);
- pw.print(" mFinished="); pw.print(mKeyWaiter.mFinished);
- pw.print(" mGotFirstWindow="); pw.print(mKeyWaiter.mGotFirstWindow);
- pw.print(" mEventDispatching="); pw.print(mKeyWaiter.mEventDispatching);
- pw.print(" mTimeToSwitch="); pw.println(mKeyWaiter.mTimeToSwitch);
+
+ if (! ENABLE_NATIVE_INPUT_DISPATCH) {
+ pw.println(" KeyWaiter state:");
+ pw.print(" mLastWin="); pw.print(mKeyWaiter.mLastWin);
+ pw.print(" mLastBinder="); pw.println(mKeyWaiter.mLastBinder);
+ pw.print(" mFinished="); pw.print(mKeyWaiter.mFinished);
+ pw.print(" mGotFirstWindow="); pw.print(mKeyWaiter.mGotFirstWindow);
+ pw.print(" mEventDispatching="); pw.print(mKeyWaiter.mEventDispatching);
+ pw.print(" mTimeToSwitch="); pw.println(mKeyWaiter.mTimeToSwitch);
+ }
}
}
+ // Called by the heartbeat to ensure locks are not held indefnitely (for deadlock detection).
public void monitor() {
synchronized (mWindowMap) { }
synchronized (mKeyguardTokenWatcher) { }
synchronized (mKeyWaiter) { }
+ synchronized (mInputMonitor) { }
}
public void virtualKeyFeedback(KeyEvent event) {
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 1a6119a3794c..d0f856b84f5a 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -19,14 +19,17 @@
//#define LOG_NDEBUG 0
// Log debug messages about InputReaderPolicy
-#define DEBUG_INPUT_READER_POLICY 1
+#define DEBUG_INPUT_READER_POLICY 0
// Log debug messages about InputDispatcherPolicy
-#define DEBUG_INPUT_DISPATCHER_POLICY 1
+#define DEBUG_INPUT_DISPATCHER_POLICY 0
+// Log debug messages about input focus tracking
+#define DEBUG_FOCUS 0
#include "JNIHelp.h"
#include "jni.h"
+#include <limits.h>
#include <android_runtime/AndroidRuntime.h>
#include <ui/InputReader.h>
#include <ui/InputDispatcher.h>
@@ -37,10 +40,94 @@
#include "../../core/jni/android_view_KeyEvent.h"
#include "../../core/jni/android_view_MotionEvent.h"
#include "../../core/jni/android_view_InputChannel.h"
-#include "../../core/jni/android_view_InputTarget.h"
namespace android {
+// Window flags from WindowManager.LayoutParams
+enum {
+ FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
+ FLAG_DIM_BEHIND = 0x00000002,
+ FLAG_BLUR_BEHIND = 0x00000004,
+ FLAG_NOT_FOCUSABLE = 0x00000008,
+ FLAG_NOT_TOUCHABLE = 0x00000010,
+ FLAG_NOT_TOUCH_MODAL = 0x00000020,
+ FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
+ FLAG_KEEP_SCREEN_ON = 0x00000080,
+ FLAG_LAYOUT_IN_SCREEN = 0x00000100,
+ FLAG_LAYOUT_NO_LIMITS = 0x00000200,
+ FLAG_FULLSCREEN = 0x00000400,
+ FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
+ FLAG_DITHER = 0x00001000,
+ FLAG_SECURE = 0x00002000,
+ FLAG_SCALED = 0x00004000,
+ FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
+ FLAG_LAYOUT_INSET_DECOR = 0x00010000,
+ FLAG_ALT_FOCUSABLE_IM = 0x00020000,
+ FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
+ FLAG_SHOW_WHEN_LOCKED = 0x00080000,
+ FLAG_SHOW_WALLPAPER = 0x00100000,
+ FLAG_TURN_SCREEN_ON = 0x00200000,
+ FLAG_DISMISS_KEYGUARD = 0x00400000,
+ FLAG_IMMERSIVE = 0x00800000,
+ FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
+ FLAG_COMPATIBLE_WINDOW = 0x20000000,
+ FLAG_SYSTEM_ERROR = 0x40000000,
+};
+
+// Window types from WindowManager.LayoutParams
+enum {
+ FIRST_APPLICATION_WINDOW = 1,
+ TYPE_BASE_APPLICATION = 1,
+ TYPE_APPLICATION = 2,
+ TYPE_APPLICATION_STARTING = 3,
+ LAST_APPLICATION_WINDOW = 99,
+ FIRST_SUB_WINDOW = 1000,
+ TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW,
+ TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1,
+ TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
+ TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
+ TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4,
+ LAST_SUB_WINDOW = 1999,
+ FIRST_SYSTEM_WINDOW = 2000,
+ TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW,
+ TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1,
+ TYPE_PHONE = FIRST_SYSTEM_WINDOW+2,
+ TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3,
+ TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4,
+ TYPE_TOAST = FIRST_SYSTEM_WINDOW+5,
+ TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6,
+ TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7,
+ TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8,
+ TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9,
+ TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10,
+ TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11,
+ TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
+ TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13,
+ TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14,
+ LAST_SYSTEM_WINDOW = 2999,
+};
+
+enum {
+ POWER_MANAGER_OTHER_EVENT = 0,
+ POWER_MANAGER_CHEEK_EVENT = 1,
+ POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either
+ // up events or LONG_TOUCH events.
+ POWER_MANAGER_LONG_TOUCH_EVENT = 3,
+ POWER_MANAGER_TOUCH_UP_EVENT = 4,
+ POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
+};
+
+// Delay between reporting long touch events to the power manager.
+const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms
+
+// Default input dispatching timeout if there is no focused application or paused window
+// from which to determine an appropriate dispatching timeout.
+const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+
+// Minimum amount of time to provide to the input dispatcher for delivery of an event
+// regardless of how long the application window was paused.
+const nsecs_t MIN_INPUT_DISPATCHING_TIMEOUT = 1000 * 1000000LL; // 1 sec
+
// ----------------------------------------------------------------------------
static struct {
@@ -53,17 +140,18 @@ static struct {
jmethodID notifyInputChannelBroken;
jmethodID notifyInputChannelANR;
jmethodID notifyInputChannelRecoveredFromANR;
+ jmethodID notifyANR;
jmethodID virtualKeyFeedback;
- jmethodID hackInterceptKey;
+ jmethodID interceptKeyBeforeQueueing;
+ jmethodID interceptKeyBeforeDispatching;
+ jmethodID checkInjectEventsPermission;
jmethodID goToSleep;
- jmethodID pokeUserActivityForKey;
+ jmethodID pokeUserActivity;
jmethodID notifyAppSwitchComing;
jmethodID filterTouchEvents;
jmethodID filterJumpyTouchEvents;
jmethodID getVirtualKeyDefinitions;
jmethodID getExcludedDeviceNames;
- jmethodID getKeyEventTargets;
- jmethodID getMotionEventTargets;
} gCallbacksClassInfo;
static struct {
@@ -79,9 +167,37 @@ static struct {
static struct {
jclass clazz;
- jmethodID ctor;
- jfieldID mArray;
-} gInputTargetListClassInfo;
+ jfieldID inputChannel;
+ jfieldID layoutParamsFlags;
+ jfieldID layoutParamsType;
+ jfieldID dispatchingTimeoutNanos;
+ jfieldID frameLeft;
+ jfieldID frameTop;
+ jfieldID touchableAreaLeft;
+ jfieldID touchableAreaTop;
+ jfieldID touchableAreaRight;
+ jfieldID touchableAreaBottom;
+ jfieldID visible;
+ jfieldID hasFocus;
+ jfieldID hasWallpaper;
+ jfieldID paused;
+ jfieldID ownerPid;
+ jfieldID ownerUid;
+} gInputWindowClassInfo;
+
+static struct {
+ jclass clazz;
+
+ jfieldID name;
+ jfieldID dispatchingTimeoutNanos;
+ jfieldID token;
+} gInputApplicationClassInfo;
+
+// ----------------------------------------------------------------------------
+
+static inline nsecs_t now() {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
// ----------------------------------------------------------------------------
@@ -103,6 +219,11 @@ public:
jweak inputChannelObjWeak);
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
+ void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
+ void setFocusedApplication(JNIEnv* env, jobject applicationObj);
+ void setInputDispatchMode(bool enabled, bool frozen);
+ void preemptInputDispatch();
+
/* --- InputReaderPolicyInterface implementation --- */
virtual bool getDisplayInfo(int32_t displayId,
@@ -130,16 +251,66 @@ public:
nsecs_t& outNewTimeout);
virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
virtual nsecs_t getKeyRepeatTimeout();
- virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+ virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
- virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
private:
+ struct InputWindow {
+ sp<InputChannel> inputChannel;
+ int32_t layoutParamsFlags;
+ int32_t layoutParamsType;
+ nsecs_t dispatchingTimeout;
+ int32_t frameLeft;
+ int32_t frameTop;
+ int32_t touchableAreaLeft;
+ int32_t touchableAreaTop;
+ int32_t touchableAreaRight;
+ int32_t touchableAreaBottom;
+ bool visible;
+ bool hasFocus;
+ bool hasWallpaper;
+ bool paused;
+ int32_t ownerPid;
+ int32_t ownerUid;
+
+ inline bool touchableAreaContainsPoint(int32_t x, int32_t y) {
+ return x >= touchableAreaLeft && x <= touchableAreaRight
+ && y >= touchableAreaTop && y <= touchableAreaBottom;
+ }
+ };
+
+ struct InputApplication {
+ String8 name;
+ nsecs_t dispatchingTimeout;
+ jweak tokenObjWeak;
+ };
+
+ class ANRTimer {
+ enum Budget {
+ SYSTEM = 0,
+ APPLICATION = 1
+ };
+
+ Budget mBudget;
+ nsecs_t mStartTime;
+ bool mFrozen;
+ InputWindow* mPausedWindow;
+
+ public:
+ ANRTimer();
+
+ void dispatchFrozenBySystem();
+ void dispatchPausedByApplication(InputWindow* pausedWindow);
+ bool waitForDispatchStateChangeLd(NativeInputManager* inputManager);
+
+ nsecs_t getTimeSpentWaitingForApplication() const;
+ };
+
sp<InputManager> mInputManager;
jobject mCallbacksObj;
- jobject mReusableInputTargetListObj;
// Cached filtering policies.
int32_t mFilterTouchEvents;
@@ -160,30 +331,81 @@ private:
jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel);
+ // Input target and focus tracking. (lock mDispatchLock)
+ Mutex mDispatchLock;
+ Condition mDispatchStateChanged;
+
+ bool mDispatchEnabled;
+ bool mDispatchFrozen;
+ bool mWindowsReady;
+ Vector<InputWindow> mWindows;
+ Vector<InputWindow*> mWallpaperWindows;
+
+ // Focus tracking for keys, trackball, etc.
+ InputWindow* mFocusedWindow;
+
+ // Focus tracking for touch.
+ bool mTouchDown;
+ InputWindow* mTouchedWindow; // primary target for current down
+ Vector<InputWindow*> mTouchedWallpaperWindows; // wallpaper targets
+
+ Vector<InputWindow*> mTempTouchedOutsideWindows; // temporary outside touch targets
+ Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
+
+ // Focused application.
+ InputApplication* mFocusedApplication;
+ InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
+
+ void dumpDispatchStateLd();
+
+ bool notifyANR(jobject tokenObj, nsecs_t& outNewTimeout);
+ void releaseFocusedApplicationLd(JNIEnv* env);
+
+ int32_t waitForFocusedWindowLd(uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets, InputWindow*& outFocusedWindow);
+ int32_t waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets, InputWindow*& outTouchedWindow);
+
+ void releaseTouchedWindowLd();
+
+ int32_t identifyTrackballEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+ 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,
+ int32_t injectorPid, int32_t injectorUid);
+
+ static bool populateWindow(JNIEnv* env, jobject windowObj, InputWindow& outWindow);
+ static void addTarget(const InputWindow* window, int32_t targetFlags,
+ nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets);
+
static inline JNIEnv* jniEnv() {
return AndroidRuntime::getJNIEnv();
}
static bool isAppSwitchKey(int32_t keyCode);
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
- static void populateInputTargets(JNIEnv* env, jobject inputTargetListObj,
- Vector<InputTarget>& outTargets);
};
// ----------------------------------------------------------------------------
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) {
JNIEnv* env = jniEnv();
mCallbacksObj = env->NewGlobalRef(callbacksObj);
- jobject inputTargetListObj = env->NewObject(gInputTargetListClassInfo.clazz,
- gInputTargetListClassInfo.ctor);
- mReusableInputTargetListObj = env->NewGlobalRef(inputTargetListObj);
- env->DeleteLocalRef(inputTargetListObj);
-
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
@@ -192,7 +414,8 @@ NativeInputManager::~NativeInputManager() {
JNIEnv* env = jniEnv();
env->DeleteGlobalRef(mCallbacksObj);
- env->DeleteGlobalRef(mReusableInputTargetListObj);
+
+ releaseFocusedApplicationLd(env);
}
bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
@@ -362,7 +585,7 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
#if DEBUG_INPUT_READER_POLICY
LOGD("interceptKey - when=%lld, deviceId=%d, down=%d, keyCode=%d, scanCode=%d, "
- "policyFlags=%d",
+ "policyFlags=0x%x",
when, deviceId, down, keyCode, scanCode, policyFlags);
#endif
@@ -375,9 +598,10 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
bool isScreenOn = this->isScreenOn();
bool isScreenBright = this->isScreenBright();
- jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.hackInterceptKey,
+ jint wmActions = env->CallIntMethod(mCallbacksObj,
+ gCallbacksClassInfo.interceptKeyBeforeQueueing,
deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn);
- if (checkAndClearExceptionFromCallback(env, "hackInterceptKey")) {
+ if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
wmActions = 0;
}
@@ -398,8 +622,7 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
}
if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
- env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivityForKey, when);
- checkAndClearExceptionFromCallback(env, "pokeUserActivityForKey");
+ pokeUserActivity(when, POWER_MANAGER_BUTTON_EVENT);
}
if (wmActions & WM_ACTION_PASS_TO_USER) {
@@ -412,6 +635,7 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING;
}
}
+
return actions;
}
@@ -423,12 +647,13 @@ int32_t NativeInputManager::interceptTouch(nsecs_t when) {
int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
if (isScreenOn()) {
// Only dispatch touch events when the device is awake.
+ // Do not wake the device.
actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
- }
- if (! isScreenBright()) {
- // Brighten the screen if dimmed.
- actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+ if (! isScreenBright()) {
+ // Brighten the screen if dimmed.
+ actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+ }
}
return actions;
@@ -444,12 +669,13 @@ int32_t NativeInputManager::interceptTrackball(nsecs_t when,
int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
if (isScreenOn()) {
// Only dispatch trackball events when the device is awake.
+ // Do not wake the device.
actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
- }
- if (! isScreenBright()) {
- // Brighten the screen if dimmed.
- actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+ if (! isScreenBright()) {
+ // Brighten the screen if dimmed.
+ actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+ }
}
return actions;
@@ -639,6 +865,27 @@ void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChanne
}
}
+bool NativeInputManager::notifyANR(jobject tokenObj, nsecs_t& outNewTimeout) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("notifyANR");
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ jlong newTimeout = env->CallLongMethod(mCallbacksObj,
+ gCallbacksClassInfo.notifyANR, tokenObj);
+ if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
+ newTimeout = -2;
+ }
+
+ if (newTimeout == -2) {
+ return false; // abort
+ }
+
+ outNewTimeout = newTimeout;
+ return true; // resume
+}
+
nsecs_t NativeInputManager::getKeyRepeatTimeout() {
if (! isScreenOn()) {
// Disable key repeat when the screen is off.
@@ -649,85 +896,898 @@ nsecs_t NativeInputManager::getKeyRepeatTimeout() {
}
}
-int32_t NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("getKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
- policyFlags, injectorPid, injectorUid);
+void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
+#if DEBUG_FOCUS
+ LOGD("setInputWindows");
#endif
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
- JNIEnv* env = jniEnv();
+ sp<InputChannel> touchedWindowChannel;
+ if (mTouchedWindow) {
+ touchedWindowChannel = mTouchedWindow->inputChannel;
+ mTouchedWindow = NULL;
+ }
+ size_t numTouchedWallpapers = mTouchedWallpaperWindows.size();
+ if (numTouchedWallpapers != 0) {
+ for (size_t i = 0; i < numTouchedWallpapers; i++) {
+ mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel);
+ }
+ mTouchedWallpaperWindows.clear();
+ }
+
+ mWindows.clear();
+ mFocusedWindow = NULL;
+ mWallpaperWindows.clear();
+
+ if (windowObjArray) {
+ mWindowsReady = true;
+
+ jsize length = env->GetArrayLength(windowObjArray);
+ for (jsize i = 0; i < length; i++) {
+ jobject inputTargetObj = env->GetObjectArrayElement(windowObjArray, i);
+ if (! inputTargetObj) {
+ break; // found null element indicating end of used portion of the array
+ }
+
+ mWindows.push();
+ InputWindow& window = mWindows.editTop();
+ bool valid = populateWindow(env, inputTargetObj, window);
+ if (! valid) {
+ mWindows.pop();
+ }
+
+ env->DeleteLocalRef(inputTargetObj);
+ }
- jint injectionResult;
- jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
- if (! keyEventObj) {
- LOGE("Could not obtain DVM KeyEvent object to get key event targets.");
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ InputWindow* window = & mWindows.editItemAt(i);
+ if (window->hasFocus) {
+ mFocusedWindow = window;
+ }
+
+ if (window->layoutParamsType == TYPE_WALLPAPER) {
+ mWallpaperWindows.push(window);
+
+ for (size_t j = 0; j < numTouchedWallpapers; j++) {
+ if (window->inputChannel == mTempTouchedWallpaperChannels[i]) {
+ mTouchedWallpaperWindows.push(window);
+ }
+ }
+ }
+
+ if (window->inputChannel == touchedWindowChannel) {
+ mTouchedWindow = window;
+ }
+ }
+ } else {
+ mWindowsReady = false;
+ }
+
+ mTempTouchedWallpaperChannels.clear();
+
+ mDispatchStateChanged.broadcast();
+
+#if DEBUG_FOCUS
+ dumpDispatchStateLd();
+#endif
+ } // release lock
+}
+
+bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj,
+ InputWindow& outWindow) {
+ bool valid = false;
+
+ jobject inputChannelObj = env->GetObjectField(windowObj,
+ gInputWindowClassInfo.inputChannel);
+ if (inputChannelObj) {
+ sp<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
+ if (inputChannel != NULL) {
+ jint layoutParamsFlags = env->GetIntField(windowObj,
+ gInputWindowClassInfo.layoutParamsFlags);
+ jint layoutParamsType = env->GetIntField(windowObj,
+ gInputWindowClassInfo.layoutParamsType);
+ jlong dispatchingTimeoutNanos = env->GetLongField(windowObj,
+ gInputWindowClassInfo.dispatchingTimeoutNanos);
+ jint frameLeft = env->GetIntField(windowObj,
+ gInputWindowClassInfo.frameLeft);
+ jint frameTop = env->GetIntField(windowObj,
+ gInputWindowClassInfo.frameTop);
+ jint touchableAreaLeft = env->GetIntField(windowObj,
+ gInputWindowClassInfo.touchableAreaLeft);
+ jint touchableAreaTop = env->GetIntField(windowObj,
+ gInputWindowClassInfo.touchableAreaTop);
+ jint touchableAreaRight = env->GetIntField(windowObj,
+ gInputWindowClassInfo.touchableAreaRight);
+ jint touchableAreaBottom = env->GetIntField(windowObj,
+ gInputWindowClassInfo.touchableAreaBottom);
+ jboolean visible = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.visible);
+ jboolean hasFocus = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.hasFocus);
+ jboolean hasWallpaper = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.hasWallpaper);
+ jboolean paused = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.paused);
+ jint ownerPid = env->GetIntField(windowObj,
+ gInputWindowClassInfo.ownerPid);
+ jint ownerUid = env->GetIntField(windowObj,
+ gInputWindowClassInfo.ownerUid);
+
+ outWindow.inputChannel = inputChannel;
+ outWindow.layoutParamsFlags = layoutParamsFlags;
+ outWindow.layoutParamsType = layoutParamsType;
+ outWindow.dispatchingTimeout = dispatchingTimeoutNanos;
+ outWindow.frameLeft = frameLeft;
+ outWindow.frameTop = frameTop;
+ outWindow.touchableAreaLeft = touchableAreaLeft;
+ outWindow.touchableAreaTop = touchableAreaTop;
+ outWindow.touchableAreaRight = touchableAreaRight;
+ outWindow.touchableAreaBottom = touchableAreaBottom;
+ outWindow.visible = visible;
+ outWindow.hasFocus = hasFocus;
+ outWindow.hasWallpaper = hasWallpaper;
+ outWindow.paused = paused;
+ outWindow.ownerPid = ownerPid;
+ outWindow.ownerUid = ownerUid;
+ valid = true;
+ } else {
+ LOGW("Dropping input target because its input channel is not initialized.");
+ }
+
+ env->DeleteLocalRef(inputChannelObj);
} else {
- jint injectionResult = env->CallIntMethod(mCallbacksObj,
- gCallbacksClassInfo.getKeyEventTargets, mReusableInputTargetListObj,
- keyEventObj, jint(keyEvent->getNature()), jint(policyFlags),
- jint(injectorPid), jint(injectorUid));
- if (checkAndClearExceptionFromCallback(env, "getKeyEventTargets")) {
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ LOGW("Dropping input target because the input channel object was null.");
+ }
+ return valid;
+}
+
+void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationObj) {
+#if DEBUG_FOCUS
+ LOGD("setFocusedApplication");
+#endif
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ releaseFocusedApplicationLd(env);
+
+ if (applicationObj) {
+ jstring nameObj = jstring(env->GetObjectField(applicationObj,
+ gInputApplicationClassInfo.name));
+ jlong dispatchingTimeoutNanos = env->GetLongField(applicationObj,
+ gInputApplicationClassInfo.dispatchingTimeoutNanos);
+ jobject tokenObj = env->GetObjectField(applicationObj,
+ gInputApplicationClassInfo.token);
+ jweak tokenObjWeak = env->NewWeakGlobalRef(tokenObj);
+ if (! tokenObjWeak) {
+ LOGE("Could not create weak reference for application token.");
+ LOGE_EX(env);
+ env->ExceptionClear();
+ }
+ env->DeleteLocalRef(tokenObj);
+
+ mFocusedApplication = & mFocusedApplicationStorage;
+
+ if (nameObj) {
+ const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
+ mFocusedApplication->name.setTo(nameStr);
+ env->ReleaseStringUTFChars(nameObj, nameStr);
+ env->DeleteLocalRef(nameObj);
+ } else {
+ LOGE("InputApplication.name should not be null.");
+ mFocusedApplication->name.setTo("unknown");
+ }
+
+ mFocusedApplication->dispatchingTimeout = dispatchingTimeoutNanos;
+ mFocusedApplication->tokenObjWeak = tokenObjWeak;
+ }
+
+ mDispatchStateChanged.broadcast();
+
+#if DEBUG_FOCUS
+ dumpDispatchStateLd();
+#endif
+ } // release lock
+}
+
+void NativeInputManager::releaseFocusedApplicationLd(JNIEnv* env) {
+ if (mFocusedApplication) {
+ env->DeleteWeakGlobalRef(mFocusedApplication->tokenObjWeak);
+ mFocusedApplication = NULL;
+ }
+}
+
+void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) {
+#if DEBUG_FOCUS
+ LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
+ mDispatchEnabled = enabled;
+ mDispatchFrozen = frozen;
+
+ mDispatchStateChanged.broadcast();
+ }
+
+#if DEBUG_FOCUS
+ dumpDispatchStateLd();
+#endif
+ } // release lock
+}
+
+void NativeInputManager::preemptInputDispatch() {
+#if DEBUG_FOCUS
+ LOGD("preemptInputDispatch");
+#endif
+
+ mInputManager->preemptInputDispatch();
+}
+
+int32_t NativeInputManager::waitForFocusedWindowLd(uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
+ InputWindow*& outFocusedWindow) {
+
+ int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ bool firstIteration = true;
+ ANRTimer anrTimer;
+ for (;;) {
+ if (firstIteration) {
+ firstIteration = false;
} else {
- populateInputTargets(env, mReusableInputTargetListObj, outTargets);
+ if (! anrTimer.waitForDispatchStateChangeLd(this)) {
+ LOGW("Dropping event because the dispatcher timed out waiting to identify "
+ "the window that should receive it.");
+ injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ break;
+ }
+ }
+
+ // If dispatch is not enabled then fail.
+ if (! mDispatchEnabled) {
+ LOGI("Dropping event because input dispatch is disabled.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ break;
+ }
+
+ // If dispatch is frozen or we don't have valid window data yet then wait.
+ if (mDispatchFrozen || ! mWindowsReady) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because dispatch is frozen or windows are not ready.");
+#endif
+ anrTimer.dispatchFrozenBySystem();
+ continue;
+ }
+
+ // If there is no currently focused window and no focused application
+ // then drop the event.
+ if (! mFocusedWindow) {
+ if (mFocusedApplication) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because there is no focused window but there is a "
+ "focused application that may yet introduce a new target: '%s'.",
+ mFocusedApplication->name.string());
+#endif
+ continue;
+ }
+
+ LOGI("Dropping event because there is no focused window or focused application.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ break;
}
- env->DeleteLocalRef(keyEventObj);
+
+ // Check permissions.
+ if (! checkInjectionPermission(mFocusedWindow, injectorPid, injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ break;
+ }
+
+ // If the currently focused window is paused then keep waiting.
+ if (mFocusedWindow->paused) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because focused window is paused.");
+#endif
+ anrTimer.dispatchPausedByApplication(mFocusedWindow);
+ continue;
+ }
+
+ // Success!
+ break; // done waiting, exit loop
}
+
+ // Output targets.
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ addTarget(mFocusedWindow, InputTarget::FLAG_SYNC,
+ anrTimer.getTimeSpentWaitingForApplication(), outTargets);
+
+ outFocusedWindow = mFocusedWindow;
+ } else {
+ outFocusedWindow = NULL;
+ }
+
+#if DEBUG_FOCUS
+ LOGD("waitForFocusedWindow finished: injectionResult=%d",
+ injectionResult);
+ dumpDispatchStateLd();
+#endif
return injectionResult;
}
-int32_t NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
+int32_t NativeInputManager::waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
+ InputWindow*& outTouchedWindow) {
+ nsecs_t startTime = now();
+
+ int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ int32_t action = motionEvent->getAction();
+
+ // For security reasons, we defer updating the touch state until we are sure that
+ // event injection will be allowed.
+ //
+ // FIXME In the original code, screenWasOff could never be set to true.
+ // The reason is that the POLICY_FLAG_WOKE_HERE
+ // and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
+ // EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was
+ // actually enqueued using the policyFlags that appeared in the final EV_SYN
+ // events upon which no preprocessing took place. So policyFlags was always 0.
+ // In the new native input dispatcher we're a bit more careful about event
+ // preprocessing so the touches we receive can actually have non-zero policyFlags.
+ // Unfortunately we obtain undesirable behavior.
+ //
+ // Here's what happens:
+ //
+ // When the device dims in anticipation of going to sleep, touches
+ // in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
+ // the device to brighten and reset the user activity timer.
+ // Touches on other windows (such as the launcher window)
+ // are dropped. Then after a moment, the device goes to sleep. Oops.
+ //
+ // Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
+ // instead of POLICY_FLAG_WOKE_HERE...
+ //
+ bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
+ bool firstIteration = true;
+ ANRTimer anrTimer;
+ for (;;) {
+ if (firstIteration) {
+ firstIteration = false;
+ } else {
+ if (! anrTimer.waitForDispatchStateChangeLd(this)) {
+ LOGW("Dropping event because the dispatcher timed out waiting to identify "
+ "the window that should receive it.");
+ injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ break;
+ }
+ }
+
+ // If dispatch is not enabled then fail.
+ if (! mDispatchEnabled) {
+ LOGI("Dropping event because input dispatch is disabled.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ break; // failed, exit wait loop
+ }
+
+ // If dispatch is frozen or we don't have valid window data yet then wait.
+ if (mDispatchFrozen || ! mWindowsReady) {
#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("getMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
- policyFlags, injectorPid, injectorUid);
+ LOGD("Waiting because dispatch is frozen or windows are not ready.");
+#endif
+ anrTimer.dispatchFrozenBySystem();
+ continue;
+ }
+
+ // Update the touch state as needed based on the properties of the touch event.
+ if (action == MOTION_EVENT_ACTION_DOWN) {
+ InputWindow* newTouchedWindow = NULL;
+ mTempTouchedOutsideWindows.clear();
+
+ int32_t x = int32_t(motionEvent->getX(0));
+ int32_t y = int32_t(motionEvent->getY(0));
+ InputWindow* topErrorWindow = NULL;
+
+ // Traverse windows from front to back to find touched window and outside targets.
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ InputWindow* window = & mWindows.editItemAt(i);
+ int32_t flags = window->layoutParamsFlags;
+
+ if (flags & FLAG_SYSTEM_ERROR) {
+ if (! topErrorWindow) {
+ topErrorWindow = window;
+ }
+ }
+
+ if (window->visible) {
+ if (! (flags & FLAG_NOT_TOUCHABLE)) {
+ bool isTouchModal = (flags &
+ (FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL)) == 0;
+ if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
+ if (! screenWasOff || flags & FLAG_TOUCHABLE_WHEN_WAKING) {
+ newTouchedWindow = window;
+ }
+ break; // found touched window, exit window loop
+ }
+ }
+
+ if (flags & FLAG_WATCH_OUTSIDE_TOUCH) {
+ mTempTouchedOutsideWindows.push(window);
+ }
+ }
+ }
+
+ // If there is an error window but it is not taking focus (typically because
+ // it is invisible) then wait for it. Any other focused window may in
+ // fact be in ANR state.
+ if (topErrorWindow && newTouchedWindow != topErrorWindow) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because system error window is pending.");
#endif
+ anrTimer.dispatchFrozenBySystem();
+ continue; // wait some more
+ }
- JNIEnv* env = jniEnv();
+ // If we did not find a touched window then fail.
+ if (! newTouchedWindow) {
+ if (mFocusedApplication) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because there is no focused window but there is a "
+ "focused application that may yet introduce a new target: '%s'.",
+ mFocusedApplication->name.string());
+#endif
+ continue;
+ }
+
+ LOGI("Dropping event because there is no touched window or focused application.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ break; // failed, exit wait loop
+ }
+
+ // Check permissions.
+ if (! checkInjectionPermission(newTouchedWindow, injectorPid, injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ break; // failed, exit wait loop
+ }
+
+ // If the touched window is paused then keep waiting.
+ if (newTouchedWindow->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because touched window is paused.");
+#endif
+ anrTimer.dispatchPausedByApplication(newTouchedWindow);
+ continue; // wait some more
+ }
+
+ // Success! Update the touch dispatch state for real.
+ releaseTouchedWindowLd();
+
+ mTouchedWindow = newTouchedWindow;
+
+ if (newTouchedWindow->hasWallpaper) {
+ mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
+ }
+ break; // done
+ } else {
+ // Check permissions.
+ if (! checkInjectionPermission(mTouchedWindow, injectorPid, injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ break; // failed, exit wait loop
+ }
- jint injectionResult;
- jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent);
- if (! motionEventObj) {
- LOGE("Could not obtain DVM MotionEvent object to get key event targets.");
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ // If there is no currently touched window then fail.
+ if (! mTouchedWindow || ! mTouchDown) {
+ LOGI("Dropping event because touched window is no longer valid.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ break; // failed, exit wait loop
+ }
+
+ // If the touched window is paused then keep waiting.
+ if (mTouchedWindow->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because touched window is paused.");
+#endif
+ anrTimer.dispatchPausedByApplication(mTouchedWindow);
+ continue; // wait some more
+ }
+
+ // Success!
+ break; // done
+ }
+ }
+
+ // Output targets.
+ bool havePermission;
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ // Injection succeeded so the injector must have permission.
+ havePermission = true;
+
+ size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
+ for (size_t i = 0; i < numWallpaperWindows; i++) {
+ addTarget(mTouchedWallpaperWindows[i], 0, 0, outTargets);
+ }
+
+ size_t numOutsideWindows = mTempTouchedOutsideWindows.size();
+ for (size_t i = 0; i < numOutsideWindows; i++) {
+ addTarget(mTempTouchedOutsideWindows[i], InputTarget::FLAG_OUTSIDE, 0, outTargets);
+ }
+
+ addTarget(mTouchedWindow, InputTarget::FLAG_SYNC,
+ anrTimer.getTimeSpentWaitingForApplication(), outTargets);
+ outTouchedWindow = mTouchedWindow;
} else {
- jint injectionResult = env->CallIntMethod(mCallbacksObj,
- gCallbacksClassInfo.getMotionEventTargets, mReusableInputTargetListObj,
- motionEventObj, jint(motionEvent->getNature()), jint(policyFlags),
- jint(injectorPid), jint(injectorUid));
- if (checkAndClearExceptionFromCallback(env, "getMotionEventTargets")) {
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED
+ && checkInjectionPermission(NULL, injectorPid, injectorUid)) {
+ // Injection failed but the injector does have permission to inject events.
+ // While we might not have found a valid target for the injected event, we
+ // still want to update the dispatch state to take it into account.
+ havePermission = true;
} else {
- populateInputTargets(env, mReusableInputTargetListObj, outTargets);
+ // Injector does not have permission to inject events.
+ // We make sure to leave the dispatch state unchanged.
+ havePermission = false;
}
- android_view_MotionEvent_recycle(env, motionEventObj);
- env->DeleteLocalRef(motionEventObj);
+ outTouchedWindow = NULL;
}
+ mTempTouchedOutsideWindows.clear();
+
+ // Update final pieces of touch state now that we know for sure whether the injector
+ // had permission to perform the injection.
+ if (havePermission) {
+ if (action == MOTION_EVENT_ACTION_DOWN) {
+ if (mTouchDown) {
+ // This is weird. We got a down but we thought it was already down!
+ LOGW("Pointer down received while already down.");
+ } else {
+ mTouchDown = true;
+ }
+
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ // Since we failed to identify a target for this touch down, we may still
+ // be holding on to an earlier target from a previous touch down. Release it.
+ releaseTouchedWindowLd();
+ }
+ } else if (action == MOTION_EVENT_ACTION_UP) {
+ mTouchDown = false;
+ releaseTouchedWindowLd();
+ }
+ }
+
+#if DEBUG_FOCUS
+ LOGD("waitForTouchedWindow finished: injectionResult=%d",
+ injectionResult);
+ dumpDispatchStateLd();
+#endif
return injectionResult;
}
-void NativeInputManager::populateInputTargets(JNIEnv* env, jobject inputTargetListObj,
+void NativeInputManager::releaseTouchedWindowLd() {
+ mTouchedWindow = NULL;
+ mTouchedWallpaperWindows.clear();
+}
+
+void NativeInputManager::addTarget(const InputWindow* window, int32_t targetFlags,
+ nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets) {
+ nsecs_t timeout = window->dispatchingTimeout - timeSpentWaitingForApplication;
+ if (timeout < MIN_INPUT_DISPATCHING_TIMEOUT) {
+ timeout = MIN_INPUT_DISPATCHING_TIMEOUT;
+ }
+
+ outTargets.push();
+
+ InputTarget& target = outTargets.editTop();
+ target.inputChannel = window->inputChannel;
+ target.flags = targetFlags;
+ target.timeout = timeout;
+ target.xOffset = - window->frameLeft;
+ target.yOffset = - window->frameTop;
+}
+
+bool NativeInputManager::checkInjectionPermission(const InputWindow* window,
+ int32_t injectorPid, int32_t injectorUid) {
+ if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
+ JNIEnv* env = jniEnv();
+ jboolean result = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.checkInjectEventsPermission, injectorPid, injectorUid);
+ checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission");
+
+ if (! result) {
+ if (window) {
+ LOGW("Permission denied: injecting event from pid %d uid %d to window "
+ "with input channel %s owned by uid %d",
+ injectorPid, injectorUid, window->inputChannel->getName().string(),
+ window->ownerUid);
+ } else {
+ LOGW("Permission denied: injecting event from pid %d uid %d",
+ injectorPid, injectorUid);
+ }
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int32_t NativeInputManager::waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("waitForKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ policyFlags, injectorPid, injectorUid);
+#endif
+
+ int32_t windowType;
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ InputWindow* focusedWindow;
+ int32_t injectionResult = waitForFocusedWindowLd(policyFlags,
+ injectorPid, injectorUid, outTargets, /*out*/ focusedWindow);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return injectionResult;
+ }
+
+ windowType = focusedWindow->layoutParamsType;
+ } // release lock
+
+ const InputTarget& target = outTargets.top();
+ bool consumed = interceptKeyBeforeDispatching(target, keyEvent, policyFlags);
+ if (consumed) {
+ outTargets.clear();
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
+ }
+
+ pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+int32_t NativeInputManager::waitForMotionEventTargets(MotionEvent* motionEvent,
+ uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("waitForMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ policyFlags, injectorPid, injectorUid);
+#endif
+
+ switch (motionEvent->getNature()) {
+ case INPUT_EVENT_NATURE_TRACKBALL:
+ return identifyTrackballEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+ outTargets);
+
+ case INPUT_EVENT_NATURE_TOUCH:
+ return identifyTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+ outTargets);
+
+ default:
+ assert(false);
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
+}
+
+int32_t NativeInputManager::identifyTrackballEventTargets(MotionEvent* motionEvent,
+ uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("identifyTrackballEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ policyFlags, injectorPid, injectorUid);
+#endif
+
+ int32_t windowType;
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ InputWindow* focusedWindow;
+ int32_t injectionResult = waitForFocusedWindowLd(policyFlags,
+ injectorPid, injectorUid, outTargets, /*out*/ focusedWindow);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return injectionResult;
+ }
+
+ windowType = focusedWindow->layoutParamsType;
+ } // release lock
+
+ pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+int32_t NativeInputManager::identifyTouchEventTargets(MotionEvent* motionEvent,
+ uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) {
- jobjectArray inputTargetArray = jobjectArray(env->GetObjectField(
- inputTargetListObj, gInputTargetListClassInfo.mArray));
-
- jsize length = env->GetArrayLength(inputTargetArray);
- for (jsize i = 0; i < length; i++) {
- jobject item = env->GetObjectArrayElement(inputTargetArray, i);
- if (! item) {
- break; // found null element indicating end of used portion of the array
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("identifyTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ policyFlags, injectorPid, injectorUid);
+#endif
+
+ int32_t windowType;
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ InputWindow* touchedWindow;
+ int32_t injectionResult = waitForTouchedWindowLd(motionEvent, policyFlags,
+ injectorPid, injectorUid, outTargets, /*out*/ touchedWindow);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return injectionResult;
+ }
+
+ windowType = touchedWindow->layoutParamsType;
+ } // release lock
+
+ int32_t eventType;
+ switch (motionEvent->getAction()) {
+ case MOTION_EVENT_ACTION_DOWN:
+ eventType = POWER_MANAGER_TOUCH_EVENT;
+ break;
+ case MOTION_EVENT_ACTION_UP:
+ eventType = POWER_MANAGER_TOUCH_UP_EVENT;
+ break;
+ default:
+ if (motionEvent->getEventTime() - motionEvent->getDownTime()
+ >= EVENT_IGNORE_DURATION) {
+ eventType = POWER_MANAGER_TOUCH_EVENT;
+ } else {
+ eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
}
+ break;
+ }
+ pokeUserActivityIfNeeded(windowType, eventType);
+ 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");
- outTargets.add();
- android_view_InputTarget_toNative(env, item, & outTargets.editTop());
+ env->DeleteLocalRef(inputChannelObj);
- env->DeleteLocalRef(item);
+ 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();
+ pokeUserActivity(eventTime, eventType);
}
- env->DeleteLocalRef(inputTargetArray);
}
+void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
+ JNIEnv* env = jniEnv();
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivity,
+ eventTime, eventType);
+ checkAndClearExceptionFromCallback(env, "pokeUserActivity");
+}
+
+void NativeInputManager::dumpDispatchStateLd() {
+#if DEBUG_FOCUS
+ LOGD(" dispatcherState: dispatchEnabled=%d, dispatchFrozen=%d, windowsReady=%d",
+ mDispatchEnabled, mDispatchFrozen, mWindowsReady);
+ if (mFocusedApplication) {
+ LOGD(" focusedApplication: name='%s', dispatchingTimeout=%0.3fms",
+ mFocusedApplication->name.string(),
+ mFocusedApplication->dispatchingTimeout / 1000000.0);
+ } else {
+ LOGD(" focusedApplication: <null>");
+ }
+ LOGD(" focusedWindow: '%s'",
+ mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>");
+ LOGD(" touchedWindow: '%s', touchDown=%d",
+ mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>",
+ mTouchDown);
+ for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
+ LOGD(" touchedWallpaperWindows[%d]: '%s'",
+ i, mTouchedWallpaperWindows[i]->inputChannel->getName().string());
+ }
+ for (size_t i = 0; i < mWindows.size(); i++) {
+ LOGD(" windows[%d]: '%s', paused=%d, hasFocus=%d, hasWallpaper=%d, visible=%d, "
+ "flags=0x%08x, type=0x%08x, "
+ "frame=[%d,%d], touchableArea=[%d,%d][%d,%d], "
+ "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms",
+ i, mWindows[i].inputChannel->getName().string(),
+ mWindows[i].paused, mWindows[i].hasFocus, mWindows[i].hasWallpaper,
+ mWindows[i].visible, mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
+ mWindows[i].frameLeft, mWindows[i].frameTop,
+ mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop,
+ mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom,
+ mWindows[i].ownerPid, mWindows[i].ownerUid,
+ mWindows[i].dispatchingTimeout / 1000000.0);
+ }
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+NativeInputManager::ANRTimer::ANRTimer() :
+ mBudget(APPLICATION), mStartTime(now()), mFrozen(false), mPausedWindow(NULL) {
+}
+
+void NativeInputManager::ANRTimer::dispatchFrozenBySystem() {
+ mFrozen = true;
+}
+
+void NativeInputManager::ANRTimer::dispatchPausedByApplication(InputWindow* pausedWindow) {
+ mPausedWindow = pausedWindow;
+}
+
+bool NativeInputManager::ANRTimer::waitForDispatchStateChangeLd(NativeInputManager* inputManager) {
+ nsecs_t currentTime = now();
+
+ Budget newBudget;
+ nsecs_t dispatchingTimeout;
+ sp<InputChannel> pausedChannel = NULL;
+ jobject tokenObj = NULL;
+ if (mFrozen) {
+ newBudget = SYSTEM;
+ dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+ mFrozen = false;
+ } else if (mPausedWindow) {
+ newBudget = APPLICATION;
+ dispatchingTimeout = mPausedWindow->dispatchingTimeout;
+ pausedChannel = mPausedWindow->inputChannel;
+ mPausedWindow = NULL;
+ } else if (inputManager->mFocusedApplication) {
+ newBudget = APPLICATION;
+ dispatchingTimeout = inputManager->mFocusedApplication->dispatchingTimeout;
+ tokenObj = jniEnv()->NewLocalRef(inputManager->mFocusedApplication->tokenObjWeak);
+ } else {
+ newBudget = APPLICATION;
+ dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+ }
+
+ if (mBudget != newBudget) {
+ mBudget = newBudget;
+ mStartTime = currentTime;
+ }
+
+ bool result = false;
+ nsecs_t timeoutRemaining = mStartTime + dispatchingTimeout - currentTime;
+ if (timeoutRemaining > 0
+ && inputManager->mDispatchStateChanged.waitRelative(inputManager->mDispatchLock,
+ timeoutRemaining) == OK) {
+ result = true;
+ } else {
+ if (pausedChannel != NULL || tokenObj != NULL) {
+ bool resumed;
+ nsecs_t newTimeout = 0;
+
+ inputManager->mDispatchLock.unlock(); // release lock
+ if (pausedChannel != NULL) {
+ resumed = inputManager->notifyInputChannelANR(pausedChannel, /*out*/ newTimeout);
+ } else {
+ resumed = inputManager->notifyANR(tokenObj, /*out*/ newTimeout);
+ }
+ inputManager->mDispatchLock.lock(); // re-acquire lock
+
+ if (resumed) {
+ mStartTime = now() - dispatchingTimeout + newTimeout;
+ result = true;
+ }
+ }
+ }
+
+ if (tokenObj) {
+ jniEnv()->DeleteLocalRef(tokenObj);
+ }
+
+ return result;
+}
+
+nsecs_t NativeInputManager::ANRTimer::getTimeSpentWaitingForApplication() const {
+ return mBudget == APPLICATION ? now() - mStartTime : 0;
+}
// ----------------------------------------------------------------------------
@@ -926,6 +1986,42 @@ static jint android_server_InputManager_nativeInjectMotionEvent(JNIEnv* env, jcl
injectorPid, injectorUid, sync, timeoutMillis);
}
+static void android_server_InputManager_nativeSetInputWindows(JNIEnv* env, jclass clazz,
+ jobjectArray windowObjArray) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->setInputWindows(env, windowObjArray);
+}
+
+static void android_server_InputManager_nativeSetFocusedApplication(JNIEnv* env, jclass clazz,
+ jobject applicationObj) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->setFocusedApplication(env, applicationObj);
+}
+
+static void android_server_InputManager_nativeSetInputDispatchMode(JNIEnv* env,
+ jclass clazz, jboolean enabled, jboolean frozen) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->setInputDispatchMode(enabled, frozen);
+}
+
+static void android_server_InputManager_nativePreemptInputDispatch(JNIEnv* env,
+ jclass clazz) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->preemptInputDispatch();
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gInputManagerMethods[] = {
@@ -953,7 +2049,15 @@ static JNINativeMethod gInputManagerMethods[] = {
{ "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIIZI)I",
(void*) android_server_InputManager_nativeInjectKeyEvent },
{ "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIIZI)I",
- (void*) android_server_InputManager_nativeInjectMotionEvent }
+ (void*) android_server_InputManager_nativeInjectMotionEvent },
+ { "nativeSetInputWindows", "([Lcom/android/server/InputWindow;)V",
+ (void*) android_server_InputManager_nativeSetInputWindows },
+ { "nativeSetFocusedApplication", "(Lcom/android/server/InputApplication;)V",
+ (void*) android_server_InputManager_nativeSetFocusedApplication },
+ { "nativeSetInputDispatchMode", "(ZZ)V",
+ (void*) android_server_InputManager_nativeSetInputDispatchMode },
+ { "nativePreemptInputDispatch", "()V",
+ (void*) android_server_InputManager_nativePreemptInputDispatch }
};
#define FIND_CLASS(var, className) \
@@ -999,17 +2103,26 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, gCallbacksClassInfo.clazz,
"notifyInputChannelRecoveredFromANR", "(Landroid/view/InputChannel;)V");
+ GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz,
+ "notifyANR", "(Ljava/lang/Object;)J");
+
GET_METHOD_ID(gCallbacksClassInfo.virtualKeyFeedback, gCallbacksClassInfo.clazz,
"virtualKeyFeedback", "(JIIIIIIJ)V");
- GET_METHOD_ID(gCallbacksClassInfo.hackInterceptKey, gCallbacksClassInfo.clazz,
- "hackInterceptKey", "(IIIIIIJZ)I");
+ GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz,
+ "interceptKeyBeforeQueueing", "(IIIIIIJZ)I");
+
+ GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz,
+ "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIZII)Z");
+
+ GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
+ "checkInjectEventsPermission", "(II)Z");
GET_METHOD_ID(gCallbacksClassInfo.goToSleep, gCallbacksClassInfo.clazz,
"goToSleep", "(J)V");
- GET_METHOD_ID(gCallbacksClassInfo.pokeUserActivityForKey, gCallbacksClassInfo.clazz,
- "pokeUserActivityForKey", "(J)V");
+ GET_METHOD_ID(gCallbacksClassInfo.pokeUserActivity, gCallbacksClassInfo.clazz,
+ "pokeUserActivity", "(JI)V");
GET_METHOD_ID(gCallbacksClassInfo.notifyAppSwitchComing, gCallbacksClassInfo.clazz,
"notifyAppSwitchComing", "()V");
@@ -1027,14 +2140,6 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
"getExcludedDeviceNames", "()[Ljava/lang/String;");
- GET_METHOD_ID(gCallbacksClassInfo.getKeyEventTargets, gCallbacksClassInfo.clazz,
- "getKeyEventTargets",
- "(Lcom/android/server/InputTargetList;Landroid/view/KeyEvent;IIII)I");
-
- GET_METHOD_ID(gCallbacksClassInfo.getMotionEventTargets, gCallbacksClassInfo.clazz,
- "getMotionEventTargets",
- "(Lcom/android/server/InputTargetList;Landroid/view/MotionEvent;IIII)I");
-
// VirtualKeyDefinition
FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,
@@ -1055,15 +2160,71 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz,
"height", "I");
- // InputTargetList
+ // InputWindow
+
+ FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow");
+
+ GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz,
+ "inputChannel", "Landroid/view/InputChannel;");
+
+ GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, gInputWindowClassInfo.clazz,
+ "layoutParamsFlags", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.layoutParamsType, gInputWindowClassInfo.clazz,
+ "layoutParamsType", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.dispatchingTimeoutNanos, gInputWindowClassInfo.clazz,
+ "dispatchingTimeoutNanos", "J");
+
+ GET_FIELD_ID(gInputWindowClassInfo.frameLeft, gInputWindowClassInfo.clazz,
+ "frameLeft", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.frameTop, gInputWindowClassInfo.clazz,
+ "frameTop", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.touchableAreaLeft, gInputWindowClassInfo.clazz,
+ "touchableAreaLeft", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.touchableAreaTop, gInputWindowClassInfo.clazz,
+ "touchableAreaTop", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.touchableAreaRight, gInputWindowClassInfo.clazz,
+ "touchableAreaRight", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.touchableAreaBottom, gInputWindowClassInfo.clazz,
+ "touchableAreaBottom", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.visible, gInputWindowClassInfo.clazz,
+ "visible", "Z");
+
+ GET_FIELD_ID(gInputWindowClassInfo.hasFocus, gInputWindowClassInfo.clazz,
+ "hasFocus", "Z");
+
+ GET_FIELD_ID(gInputWindowClassInfo.hasWallpaper, gInputWindowClassInfo.clazz,
+ "hasWallpaper", "Z");
+
+ GET_FIELD_ID(gInputWindowClassInfo.paused, gInputWindowClassInfo.clazz,
+ "paused", "Z");
+
+ GET_FIELD_ID(gInputWindowClassInfo.ownerPid, gInputWindowClassInfo.clazz,
+ "ownerPid", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.ownerUid, gInputWindowClassInfo.clazz,
+ "ownerUid", "I");
+
+ // InputApplication
+
+ FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/InputApplication");
- FIND_CLASS(gInputTargetListClassInfo.clazz, "com/android/server/InputTargetList");
+ GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz,
+ "name", "Ljava/lang/String;");
- GET_METHOD_ID(gInputTargetListClassInfo.ctor, gInputTargetListClassInfo.clazz,
- "<init>", "()V");
+ GET_FIELD_ID(gInputApplicationClassInfo.dispatchingTimeoutNanos,
+ gInputApplicationClassInfo.clazz,
+ "dispatchingTimeoutNanos", "J");
- GET_FIELD_ID(gInputTargetListClassInfo.mArray, gInputTargetListClassInfo.clazz,
- "mArray", "[Landroid/view/InputTarget;");
+ GET_FIELD_ID(gInputApplicationClassInfo.token, gInputApplicationClassInfo.clazz,
+ "token", "Ljava/lang/Object;");
return 0;
}
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/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 8c343b57403a..c6a4134d8a8a 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -104,6 +104,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);
}
},