Merge "Fix a bug that prevented the creation of EGL shared contexts." into gingerbread
diff --git a/api/current.xml b/api/current.xml
index 9145462..5d71cad 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 75948a5..10668a4 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -1031,6 +1031,7 @@
mHardware->getRawHeap());
mSurface->registerBuffers(buffers);
+ IPCThreadState::self()->flushCommands();
}
mLock.unlock();
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 33696f4..9a97284 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -7,7 +7,7 @@
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 877b908..b7a3f99 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 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 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 @@
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 @@
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 @@
break;
}
+ case 'w':
+ {
+ gWriteMP4 = true;
+ gWriteMP4Filename.setTo(optarg);
+ break;
+ }
+
case 'p':
{
dumpProfiles = true;
@@ -554,7 +583,11 @@
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 9b9ae52..985f591 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -39,7 +39,6 @@
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 e56fee9..1fe85e6 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1021,16 +1021,6 @@
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 @@
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 03bb858..883366b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -616,14 +616,6 @@
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 360959d..1c20062 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -341,13 +341,6 @@
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 @@
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 bf02d5a..20c9a80 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -247,8 +247,6 @@
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 @@
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 ffb8651..c8ef17f 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -95,7 +95,6 @@
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 @@
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 429d164..161161c 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -5,10 +5,16 @@
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 @@
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 @@
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 @@
getWindow().takeSurface(this);
getWindow().takeInputQueue(this);
+ getWindow().setFormat(PixelFormat.RGB_565);
try {
ai = getPackageManager().getActivityInfo(
@@ -78,7 +90,7 @@
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 @@
@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 @@
@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 e9c2cf7..57271d4 100644
--- a/core/java/android/hardware/Usb.java
+++ b/core/java/android/hardware/Usb.java
@@ -39,6 +39,27 @@
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 a8c6f9b..9ad125b 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -35,6 +35,11 @@
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 @@
/**
* 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 @@
}
};
+ private static final HostnameVerifier HOSTNAME_VERIFIER =
+ HttpsURLConnection.getDefaultHostnameVerifier();
+
private SSLSocketFactory mInsecureFactory = null;
private SSLSocketFactory mSecureFactory = null;
@@ -95,7 +119,7 @@
*
* @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 @@
* @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 @@
/**
* 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 @@
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 @@
}
}
+ 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 @@
}
}
+ /**
+ * {@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 @@
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 @@
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 @@
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 5640a06..f695dbb 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -626,6 +626,15 @@
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 3f5d6ca..249ad62 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -773,8 +773,6 @@
if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
if (mInputChannel != null) {
InputQueue.unregisterInputChannel(mInputChannel);
- mInputChannel.dispose();
- mInputChannel = null;
}
}
@@ -783,6 +781,15 @@
}
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 6ff7305..0000000
--- 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 ae9746e..0bfb6d6 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -676,33 +676,12 @@
* 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 @@
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 4854190..1dc82e8 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1762,6 +1762,15 @@
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 431b786..be1f6d2 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -83,7 +83,7 @@
* 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 2369d25..127ed68 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -126,6 +126,11 @@
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 @@
}
}
+ /**
+ * 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 d854e87..a008e96 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -47,7 +47,6 @@
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 3e69c78..6fb1369 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -164,7 +164,6 @@
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 @@
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 5e5e47e..dab1dba 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 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
{
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 @@
}
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 @@
{ "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 68be741..7c99271 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -52,9 +52,15 @@
#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 @@
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 @@
{"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 63e00d7..6fb3cf7 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 a0a7054..0000000
--- 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 df3b952..8f648f4 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -76,8 +76,23 @@
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 @@
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 @@
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 cef5c10..a82abc93 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 @@
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
similarity index 73%
rename from core/jni/android_view_InputTarget.h
rename to core/jni/android_view_Surface.h
index 9230b1b..c37932e 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 d5cde48..866c038 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 @@
}
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 @@
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 b1f81df..82f822f 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 0000000..c8ce23c
--- /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 0000000..b538cb2
--- /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 f18b6c9..fb5e123 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 ae7b983..81aeb01 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 15e4513..553acbb 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 d4aa66b..84b00bb 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 4b27853..d77a463 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 0e27fba..0d83e70a 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 e511aa4..2619e9f 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 d787d2f..7e846ef 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 f18b6c9..ab5493b 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 ae7b983..456cdb0 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 15e4513..bd9f8a4 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 f67e64e..db96754 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 0ad991b..0a29948 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 6cc3d1a..df415ba 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 f8120df..f98e908 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 9d7620b..933261f4 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 21399a4..8d72756 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 3941b82..fc78396 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 22d5683..943eee0 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 f67e64e..f3ec5d4 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 0ad991b..7379e00 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 6cc3d1a..24ceb39 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 cffcd1d..d565c68 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 8a92757..99c0b3d 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 af04117..02a601a 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 ce623e8..30312b3 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 5480993..d298d40 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 0000000..6adfc74
--- /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 0000000..3f43e48
--- /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 cf2a457..66379a3 100644
--- a/docs/html/guide/developing/eclipse-adt.jd
+++ b/docs/html/guide/developing/eclipse-adt.jd
@@ -527,7 +527,7 @@
<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 @@
...
</manifest></pre>
-<p>For more information about the manifest file, see the documentation for <a href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
+<p>For more information about the manifest file, see the documentation for <a
+href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
<h3 id="considerations">Development considerations</h3>
diff --git a/docs/html/guide/developing/other-ide.jd b/docs/html/guide/developing/other-ide.jd
index e8a6fb6..1d67aa9 100644
--- a/docs/html/guide/developing/other-ide.jd
+++ b/docs/html/guide/developing/other-ide.jd
@@ -687,7 +687,7 @@
<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 @@
...
</manifest></pre>
-<p>For more information about the manifest file, see the documentation for <a href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
+<p>For more information about the manifest file, see the documentation for <a
+href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
<h3 id="depAppBuild">Building a dependent application</h3>
diff --git a/docs/html/guide/developing/tools/bmgr.jd b/docs/html/guide/developing/tools/bmgr.jd
index 2f49532..57deb25 100644
--- a/docs/html/guide/developing/tools/bmgr.jd
+++ b/docs/html/guide/developing/tools/bmgr.jd
@@ -15,6 +15,11 @@
<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 @@
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 @@
<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 @@
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 2f3ac5e..bd9ea52 100644
--- a/docs/html/guide/samples/index.jd
+++ b/docs/html/guide/samples/index.jd
@@ -3,99 +3,13 @@
@jd:body
-<p>Sometimes, the best way to learn how things are done is to look at some code.
-Here, you can browse the source of some sample Android applications that are included
-in the Android SDK.</p>
+<script type="text/javascript">
+ window.location = toRoot + "resources/samples/index.html";
+</script>
-<p>Each version of the Android platform available for the SDK includes a full set of sample
-applications (which may vary between different versions of the platform).
-You can find the samples in your SDK at:</p>
+<p><strong>This document has moved. Please go to <a
+href="http://developer.android.com/resources/samples/index.html">List of Sample
+Apps</a>.</strong></p>
-<p style="margin-left:2em">
-<code><em><sdk></em>/platforms/android-<em><version></em>/samples/</code>
-</p>
-
-<p>You can easily create new Android projects with these samples, modify them
-if you'd like, then run them on an emulator or device. For example, to create
-a project for the API Demos app from Eclipse,
-start a new Android Project, select "Create project from existing source", then select
-{@code ApiDemos} in the {@code samples/} directory. To create the API Demos project
-using the {@code android} tool, execute:</p>
-<pre>
-android update project -s -n API Demos -t <em><target_ID></em> -p <em><path-to-platform></em>/samples/ApiDemos/
-</pre>
-
-<p>The pages below provide an overview of each sample application (available with most
-platforms) and allow you to view the source files in your browser. </p>
-
-<div class="special">
- <p>Some of the samples in this listing are not yet available in the
- SDK. While we work to update the SDK, you can
- <a href="{@docRoot}shareables/latest_samples.zip">download the latest samples</a> as a ZIP
- archive.</p>
-</div>
-
-<dl>
-
- <dt><a href="{@docRoot}resources/samples/ApiDemos/index.html">API Demos</a></dt>
- <dd>A variety of small applications that demonstrate an extensive collection of
- framework topics.</dd>
-
- <dt><a href="{@docRoot}resources/samples/BackupRestore/index.html">Backup and Restore</a></dt>
- <dd>An simple example that illustrates a few different ways for an application to
- implement support for the Android data backup and restore mechanism.</dd>
-
- <dt><a href="{@docRoot}resources/samples/BluetoothChat/index.html">Bluetooth Chat</a></dt>
- <dd>An application for two-way text messaging over Bluetooth.</dd>
-
- <dt><a href="{@docRoot}resources/samples/ContactManager/index.html">Contact Manager</a></dt>
- <dd>An application that demonstrates how to query the system contacts provider
- using the <code>ContactsContract</code> API, as
- well as insert contacts into a specific account.</dd>
-
- <dt><a href="{@docRoot}resources/samples/Home/index.html">Home</a></dt>
- <dd>A home screen replacement application.</dd>
-
- <dt><a href="{@docRoot}resources/samples/JetBoy/index.html">JetBoy</a></dt>
- <dd>JetBoy is a game that demonstrates the SONiVOX JET interactive music technology,
- with {@link android.media.JetPlayer}.</dd>
-
- <dt><a href="{@docRoot}resources/samples/LunarLander/index.html">Lunar Lander</a></dt>
- <dd>A classic Lunar Lander game.</dd>
-
- <dt><a href="{@docRoot}resources/samples/MultiResolution/index.html">Multiple Resolutions</a></dt>
- <dd>A sample application that shows how to use resource directory qualifiers to
- provide different resources for different screen configurations.</dd>
-
- <dt><a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a></dt>
- <dd>An application for saving notes. Similar (but not identical) to the
- <a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad tutorial</a>.</dd>
-
- <dt><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary</a></dt>
- <dd>A sample application that demonstrates Android's search framework,
- including how to provide search suggestions for Quick Search Box.</dd>
-
- <dt><a href="{@docRoot}resources/samples/Snake/index.html">Snake</a></dt>
- <dd>An implementation of the classic game "Snake."</dd>
-
- <dt><a href="{@docRoot}resources/samples/SoftKeyboard/index.html">Soft Keyboard</a></dt>
- <dd>An example of writing an input method for a software keyboard.</dd>
-
- <dt><a href=""{@docRoot}resources/samples/Wiktionary/index.html">Wiktionary</a></dt>
- <dd>An example of creating interactive widgets for display on the Android
- home screen.</dd>
-
- <dt><a href="{@docRoot}resources/samples/WiktionarySimple/index.html">Wiktionary (Simplified)</a></dt>
- <dd>A simple Android home screen widgets example.</dd>
-
-</dl>
-
-
-<div class="special">
-<p>For more sample applications, check out
-<a href="http://code.google.com/p/apps-for-android/">apps-for-android</a>, a
-collection of open source applications that demonstrate various Android APIs.
-</p>
-</div>
diff --git a/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd
index 4e74a83..6c02031 100644
--- a/docs/html/guide/topics/data/backup.jd
+++ b/docs/html/guide/topics/data/backup.jd
@@ -33,7 +33,7 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
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 @@
<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 @@
<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 @@
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) — 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 cac85e8..7e2f8a0 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -761,7 +761,7 @@
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 cf3c7de..b4e467c 100644
--- a/docs/html/guide/topics/ui/menus.jd
+++ b/docs/html/guide/topics/ui/menus.jd
@@ -5,198 +5,324 @@
<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>
+<h2 id="xml">Defining Menus</h2>
-<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>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>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>To define a menu, create an XML file inside your project's <code>res/menu/</code>
+directory and build the menu with the following elements:</p>
+<dl>
+ <dt><code><menu></code></dt>
+ <dd>Creates a {@link android.view.Menu}, which is a container for menu items. It must be
+the root node and holds one or more of the following elements. You can also nest this element
+in an {@code <item>} to create a submenu.</dd>
+ <dt><code><item></code></dt>
+ <dd>Creates a {@link android.view.MenuItem}, which represents a single item in a menu.</dd>
+ <dt><code><group></code></dt>
+ <dd>An optional, invisible container for {@code <item>} elements. It allows you to
+categorize menu items so they share properties such as active state and visibility. See <a
+href="#groups">Menu groups</a>.</dd>
+</dl>
-<p>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>For example, here is a file in <code>res/menu/</code> named <code>game_menu.xml</code>:</p>
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/new_game"
+ android:icon="@drawable/ic_new_game"
+ android:title="@string/new_game" />
+ <item android:id="@+id/quit"
+ android:icon="@drawable/ic_quit"
+ android:title="@string/quit" />
+</menu>
+</pre>
-<p>Here's an example of this procedure, inside an Activity, wherein we create an
-Options Menu and handle item selections:</p>
+<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>
-/* Creates the menu items */
+@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;
}
-
-/* Handles item selections */
-public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case MENU_NEW_GAME:
- newGame();
- return true;
- case MENU_QUIT:
- quit();
- return true;
- }
- return false;
-}
</pre>
-<p>The <code>add()</code> method used in this sample takes four arguments:
-<var>groupId</var>, <var>itemId</var>, <var>order</var>, and <var>title</var>.
-The <var>groupId</var> allows you to associate this menu item with a group of other items
-(more about <a href="#groups">Menu groups</a>, below) — in
-this example, we ignore it. <var>itemId</var> is a unique integer that we give the
-MenuItem so that can identify it in the next callback. <var>order</var> allows us to
-define the display order of the item — by default, they are displayed by the
-order in which we add them. <var>title</var> is, of course, the name that goes on the
-menu item (this can also be a
-<a href="{@docRoot}guide/topics/resources/available-resources.html#stringresources">string resource</a>,
-and we recommend you do it that way for easier localization).</p>
+<p>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>
-<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>
+
+<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>
+
<pre>
-menu.add(0, MENU_QUIT, 0, "Quit")
- .setIcon(R.drawable.menu_quit_icon);</pre>
+@Override
+public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle item selection
+ switch (item.getItemId()) {
+ case R.id.new_game:
+ newGame();
+ return true;
+ case R.id.quit:
+ quit();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+}
+</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>
+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">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>
+<h2 id="context-menu">Creating a Context Menu</h2>
-<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>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>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>
+<p>You can create a context menu for any View, though context menus are most often used for items in
+a {@link android.widget.ListView}. When the user performs a long-press on an item in a ListView and
+the list is registered to provide a context menu, the list item signals to the user that a context
+menu is available by animating its background color—it transitions from
+orange to white before opening the context menu. (The Contacts application demonstrates this
+feature.)</p>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h3>Register a ListView</h3>
+<p>If your Activity uses a {@link android.widget.ListView} and
+you want all list items to provide a context menu, register all items for a context
+menu by passing the {@link android.widget.ListView} to {@link
+android.app.Activity#registerForContextMenu(View) registerForContextMenu()}. For
+example, if you're using a {@link android.app.ListActivity}, register all list items like this:</p>
+<p><code>registerForContextMenu({@link android.app.ListActivity#getListView()});</code></p>
+</div>
+</div>
+
+<p>In order for a View to provide a context menu, you must "register" the view for a context
+menu. Call {@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()} and
+pass it the {@link android.view.View} you want to give a context menu. When this View then
+receives a long-press, it displays a context menu.</p>
+
+<p>To define the context menu's appearance and behavior, override your Activity's context menu
+callback methods, {@link android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo)
+onCreateContextMenu()} and
+{@link android.app.Activity#onContextItemSelected(MenuItem) onContextItemSelected()}.</p>
+
+<p>For example, here's an {@link
+android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo)
+onCreateContextMenu()} that uses the {@code context_menu.xml} menu resource:</p>
<pre>
+@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
- menu.add(0, EDIT_ID, 0, "Edit");
- menu.add(0, DELETE_ID, 0, "Delete");
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.context_menu, menu);
}
+</pre>
+<p>{@link android.view.MenuInflater} is used to inflate the context menu from a <a
+href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>. (You can also use
+{@link android.view.Menu#add(int,int,int,int) add()} to add menu items.) The callback method
+parameters include the {@link android.view.View}
+that the user selected and a {@link android.view.ContextMenu.ContextMenuInfo} object that provides
+additional information about the item selected. You might use these parameters to determine
+which context menu should be created, but in this example, all context menus for the Activity are
+the same.</p>
+
+<p>Then when the user selects an item from the context menu, the system calls {@link
+android.app.Activity#onContextItemSelected(MenuItem) onContextItemSelected()}. Here is an example
+of how you can handle selected items:</p>
+
+<pre>
+@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
- case EDIT_ID:
+ case R.id.edit:
editNote(info.id);
return true;
- case DELETE_ID:
+ case R.id.delete:
deleteNote(info.id);
return true;
default:
@@ -205,285 +331,276 @@
}
</pre>
-<p>In <code>onCreateContextMenu()</code>, we are given not only the ContextMenu to
-which we will add {@link android.view.MenuItem}s, but also the {@link android.view.View}
-that was selected and a {@link android.view.ContextMenu.ContextMenuInfo ContextMenuInfo} object,
-which provides additional information about the object that was selected.
-In this example, nothing special is done in <code>onCreateContextMenu()</code> — just
-a couple items are added as usual. In the <code>onContextItemSelected()</code>
-callback, we request the {@link android.widget.AdapterView.AdapterContextMenuInfo AdapterContextMenuInfo}
-from the {@code MenuItem}, which provides information about the currently selected item.
-All we need from
-this is the list ID for the selected item, so whether editing a note or deleting it,
-we find the ID with the {@code AdapterContextMenuInfo.info} field of the object. This ID
-is passed to the <code>editNote()</code> and <code>deleteNote()</code> methods to perform
-the respective action.</p>
+<p>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>
-<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>
+<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>
-<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>
+<p class="note"><strong>Note:</strong> Items in a context menu do not support icons or shortcut
+keys.</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>
+<h2 id="submenu">Creating Submenus</h2>
-<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>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>When creating your <a href="{@docRoot}guide/topics/resources/menu-resource.html">menu
+resource</a>, you can create a submenu by adding a {@code <menu>} element as the child of an
+{@code <item>}. For example:</p>
<pre>
-public boolean onCreateOptionsMenu(Menu menu) {
- boolean result = super.onCreateOptionsMenu(menu);
-
- 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");
-
- 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>
-
-<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>In a menu XML layout, there are
-three valid elements: <code><menu></code>, <code><group></code> and <code><item></code>. The
-<code>item</code> and <code>group</code> elements must be children of a <code>menu</code>, but <code>item</code>
-elements may also be the children of a <code>group</code>, and another <code>menu</code> element may be the child
-of an <code>item</code> (to create a Submenu). Of course, the root node of any file
-must be a <code>menu</code> element.</p>
-
-<p>As an example, we'll define the same menu created in the <a href="#options-menu">Options Menu</a> section,
-above. We start with an XML file named <code>options_menu.xml</code> inside the <code>res/menu/</code> folder:</p>
-<pre>
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/new_game"
- android:title="New Game" />
- <item android:id="@+id/quit"
- android:title="Quit" />
-</menu>
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/file"
+ android:icon="@drawable/file"
+ android:title="@string/file" >
+ <!-- "file" submenu -->
+ <menu">
+ <item android:id="@+id/new"
+ android:title="@string/new" />
+ <item android:id="@+id/open"
+ android:title="@string/open" />
+ </menu>
+ </item>
+</menu>
</pre>
-<p>Then, in the <code>onCreateOptionsMenu()</code> method, we inflate this resource using
-<code>{@link android.view.MenuInflater#inflate(int,Menu) MenuInflater.inflate()}</code>:</p>
-<pre>
-public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.options_menu, menu);
- return true;
-}
-</pre>
+<p>When the user selects an item from a submenu, the parent menu's respective on-item-selected
+callback method receives the event. For instance, if the above menu is applied as an Options Menu,
+then the {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} method
+is called when a submenu item is selected.</p>
-<p>The <code>{@link android.app.Activity#getMenuInflater()}</code> method returns the {@link android.view.MenuInflater}
-for our activity's context. We then call <code>{@link android.view.MenuInflater#inflate(int,Menu) inflate()}</code>,
-passing it a pointer to our menu resource and the Menu object given by the callback.</code></p>
+<p>You can also use {@link android.view.Menu#addSubMenu(int,int,int,int) addSubMenu()} to
+dynamically add a {@link android.view.SubMenu} to an existing {@link android.view.Menu}. This
+returns the new {@link android.view.SubMenu} object, to which you can add
+submenu items, using {@link android.view.Menu#add(int,int,int,int) add()}</p>
-<p>While this small sample may seem like more effort, compared to creating the menu items in the
-<code>onCreateOptionsMenu()</code> method, this will save a lot of trouble when dealing with more items
-and it keeps your application code clean.</p>
-<p>You can define <a href="#groups">menu groups</a> by wrapping <code>item</code> elements in a <code>group</code>
-element, and create Submenus by nesting another <code>menu</code> inside an <code>item</code>.
-Each element also supports all the necessary attributes to control features like shortcut keys,
-checkboxes, icons, and more. To learn about these attributes and more about the XML syntax, see the Menus
-topic in the <a href="{@docRoot}guide/topics/resources/available-resources.html#menus">Available
-Resource Types</a> document.</p>
-<h2 id="features">Menu Features</h2>
-<p>Here are some other features that can be applied to most menu items.</p>
+<h2 id="features">Other Menu Features</h2>
+
+<p>Here are some other features that you can apply to most menu items.</p>
<h3 id="groups">Menu groups</h3>
-<p>When adding new items to a menu, you can optionally include each item in a group.
-A menu group is a collection of menu items that can share certain traits, like
-whether they are visible, enabled, or checkable.</p>
-<p>A group is defined by an integer (or a resource id, in XML). A menu item is added to the group when it is
-added to the menu, using one of the <code>add()</code> methods that accepts a <var>groupId</var>
-as an argument, such as <code>{@link android.view.Menu#add(int,int,int,int)}</code>.</p>
+<p>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 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>You can create a group by nesting {@code <item>} elements inside a {@code <group>}
+element in your menu resource or by specifying a group ID with the the {@link
+android.view.Menu#add(int,int,int,int) add()} method.</p>
+
+<p>Here's an example menu resource that includes a group:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/item1"
+ android:icon="@drawable/item1"
+ android:title="@string/item1" />
+ <!-- menu group -->
+ <group android:id="@+id/group1">
+ <item android:id="@+id/groupItem1"
+ android:title="@string/groupItem1" />
+ <item android:id="@+id/groupItem2"
+ android:title="@string/groupItem2" />
+ </group>
+</menu>
+</pre>
+
+<p>The items that are in the group appear the same as the first item that is not in a
+group—all three items in the menu are siblings. However, you can modify the traits of the two
+items in the group by referencing the group ID and using the methods listed above.</p>
+
<h3 id="checkable">Checkable menu items</h3>
-<img align="right" src="{@docRoot}images/radio_buttons.png" alt="" />
-<p>Any menu item can be used as an interface for turning options on and off. This can
-be indicated with a checkbox for stand-alone options, or radio buttons for groups of
-mutually exclusive options (see the screenshot, to the right).</p>
-<p class="note"><strong>Note:</strong> Menu items in the Icon Menu cannot
+<div class="figure" style="width:200px">
+ <img src="{@docRoot}images/radio_buttons.png" height="300" alt="" />
+ <p class="img-caption"><strong>Figure 2.</strong> Screenshot of checkable menu items</p>
+</div>
+
+<p>A menu can be useful as an interface for turning options on and off, using a checkbox for
+stand-alone options, or radio buttons for groups of
+mutually exclusive options. Figure 2 shows a submenu with items that are checkable with radio
+buttons.</p>
+
+<p class="note"><strong>Note:</strong> Menu items in the Icon Menu (from the Options Menu) cannot
display a checkbox or radio button. If you choose to make items in the Icon Menu checkable,
-then you must personally indicate the state by swapping the icon and/or text
-each time the state changes between on and off.</p>
+you must manually indicate the checked state by swapping the icon and/or text
+each time the state changes.</p>
-<p>To make a single item checkable, use the <code>{@link android.view.MenuItem#setCheckable(boolean)
-setCheckable()}</code> method, like so:</p>
+<p>You can define the checkable behavior for individual menu items using the {@code
+android:checkable} attribute in the {@code <item>} element, or for an entire group with
+the {@code android:checkableBehavior} attribute in the {@code <group>} element. For
+example, all items in this menu group are checkable with a radio button:</p>
+
<pre>
-menu.add(0, VIBRATE_SETTING_ID, 0, "Vibrate")
- .setCheckable(true);
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <group android:checkableBehavior="single">
+ <item android:id="@+id/red"
+ android:title="@string/red" />
+ <item android:id="@+id/blue"
+ android:title="@string/blue" />
+ </group>
+</menu>
</pre>
-<p>This will display a checkbox with the menu item (unless it's in the Icon Menu). When the item
-is selected, the <code>onOptionsItemSelected()</code> callback is called as usual. It is here that
-you must set the state of the checkbox. You can query the current state of the item with
-<code>{@link android.view.MenuItem#isChecked()}</code> and set the checked state with
-<code>{@link android.view.MenuItem#setChecked(boolean) setChecked()}</code>.
-Here's what this looks like inside the
-<code>onOptionsItemSelected()</code> callback:</p>
+
+<p>The {@code android:checkableBehavior} attribute accepts either:
+<dl>
+ <dt>{@code single}</dt>
+ <dd>Only one item from the group can be checked (radio buttons)</dd>
+ <dt>{@code all}</dt>
+ <dd>All items can be checked (checkboxes)</dd>
+ <dt>{@code none}</dt>
+ <dd>No items are checkable</dd>
+</dl>
+
+<p>You can apply a default checked state to an item using the {@code android:checked} attribute in
+the {@code <item>} element and change it in code with the {@link
+android.view.MenuItem#setChecked(boolean) setChecked()} method.</p>
+
+<p>When a checkable item is selected, the system calls your respective item-selected callback method
+(such as {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}). It
+is here that you must set the state of the checkbox, because a checkbox or radio button does not
+change its state automatically. You can query the current state of the item (as it was before the
+user selected it) with {@link android.view.MenuItem#isChecked()} and then set the checked state with
+{@link android.view.MenuItem#setChecked(boolean) setChecked()}. For example:</p>
+
<pre>
-switch (item.getItemId()) {
-case VIBRATE_SETTING_ID:
- if (item.isChecked()) item.setChecked(false);
- else item.setChecked(true);
- return true;
-...
+@Override
+public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.vibrate:
+ case R.id.dont_vibrate:
+ if (item.isChecked()) item.setChecked(false);
+ else item.setChecked(true);
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
}
</pre>
-<p>To make a group of mutually exclusive radio button items, simply
-assign the same group ID to each menu item
-and call <code>{@link android.view.Menu#setGroupCheckable(int,boolean,boolean)
-setGroupCheckable()}</code>. In this case, you don't need to call <code>setCheckable()</code>
-on each menu items, because the group as a whole is set checkable. Here's an example of
-two mutually exclusive options in a Submenu:</p>
-<pre>
-SubMenu subMenu = menu.addSubMenu("Color");
-subMenu.add(COLOR_MENU_GROUP, COLOR_RED_ID, 0, "Red");
-subMenu.add(COLOR_MENU_GROUP, COLOR_BLUE_ID, 0, "Blue");
-subMenu.setGroupCheckable(COLOR_MENU_GROUP, true, true);
-</pre>
-<p>In the <code>setGroupCheckable()</code> method, the first argument is the group ID
-that we want to set checkable. The second argument is whether we want the group items
-to be checkable. The last one is whether we want each item to be exclusively checkable
-(if we set this <em>false</em>, then all the items will be checkboxes instead of radio buttons).
-When the group is set to be exclusive (radio buttons), each time a new item is selected,
-all other are automatically de-selected.</p>
-<p>
+<p>If you don't set the checked state this way, then the visible state of the item (the checkbox or
+radio button) will not
+change when the user selects it. When you do set the state, the Activity preserves the checked state
+of the item so that when the user opens the menu later, the checked state that you
+set is visible.</p>
<p class="note"><strong>Note:</strong>
-Checkable menu items are intended to be used only on a per-session basis and not saved to the device
-(e.g., the <em>Map mode</em> setting in the Maps application is not saved — screenshot above).
-If there are application settings that you would like to save for the user,
-then you should store the data using <a href="#{@docRoot}guide/topics/data/data-storage.html#pref">Preferences</a>,
-and manage them with a {@link android.preference.PreferenceActivity}.</p>
+Checkable menu items are intended to be used only on a per-session basis and not saved after the
+application is destroyed. If you have application settings that you would like to save for the user,
+you should store the data using <a
+href="#{@docRoot}guide/topics/data/data-storage.html#pref">Shared Preferences</a>.</p>
<h3 id="shortcuts">Shortcut keys</h3>
-<p>Quick access shortcut keys using letters and/or numbers can be added to menu items with
-<code>setAlphabeticShortcut(char)</code> (to set char shortcut), <code>setNumericShortcut(int)</code>
-(to set numeric shortcut),
-or <code>setShortcut(char,int)</code> (to set both)</code>. Case is <em>not</em> sensitive.
-For example:</p>
-<pre>
-menu.add(0, MENU_QUIT, 0, "Quit")
- .setAlphabeticShortcut('q');
-</pre>
-<p>Now, when the menu is open (or while holding the MENU key), pressing the "q" key will
-select this item.</p>
-<p>This shortcut key will be displayed as a tip in the menu item, below the menu item name
-(except for items in the Icon Menu).</p>
-<p class="note"><strong>Note:</strong> Shortcuts cannot be added to items in a Context Menu.</p>
+<p>You can add quick-access shortcut keys using letters and/or numbers to menu items with the
+{@code android:alphabeticShortcut} and {@code android:numericShortcut} attributes in the {@code
+<item>} element. You can also use the methods {@link
+android.view.MenuItem#setAlphabeticShortcut(char)} and {@link
+android.view.MenuItem#setNumericShortcut(char)}. Shortcut keys are <em>not</em>
+case sensitive.</p>
+
+<p>For example, if you apply the "s" character as an alphabetic shortcut to a "save" menu item, then
+when the menu is open (or while the user holds the MENU key) and the user presses the "s" key,
+the "save" menu item is selected.</p>
+
+<p>This shortcut key is displayed as a tip in the menu item, below the menu item name
+(except for items in the Icon Menu, which are displayed only if the user holds the MENU
+key).</p>
+
+<p class="note"><strong>Note:</strong> Shortcut keys for menu items only work on devices with a
+hardware keyboard. Shortcuts cannot be added to items in a Context Menu.</p>
-<h3 id="intents">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>
+<h3 id="intents">Intents for menu items</h3>
-<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>
+<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>
-<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>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>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>If you're not familiar with creating Intents, read the <a
+href="/guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>.</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>
+<h4>Dynamically adding 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>When you don't know if the user's device has an application that handles a specific Intent,
+you can define the Intent and let Android search the device for activities that accept the Intent.
+When it finds activies that handle the Intent, it adds a menu item for
+each one to your menu and attaches the appropriate Intent to open the Activity when the user
+selects it.</p>
+
+<p>To add menu items based on available activities that accept an Intent:</p>
+<ol>
+ <li>Define an
+Intent with the category {@link android.content.Intent#CATEGORY_ALTERNATIVE} and/or
+{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE}, plus any other requirements.</li>
+ <li>Call {@link
+android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[])
+Menu.addIntentOptions()}. Android then searches for any applications that can perform the Intent
+and adds them to your menu.</li>
+</ol>
+
+<p>If there are no applications installed
+that satisfy the Intent, then no menu items are added.</p>
<p class="note"><strong>Note:</strong>
-<var>SELECTED_ALTERNATIVE</var> is used to handle the currently selected element on the
-screen. So, it should only be used when creating a Menu in <code>onCreateContextMenu()</code> or
-<code>onPrepareOptionsMenu()</code>, which is called every time the Options Menu is opened.</p>
+{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} is used to handle the currently
+selected element on the screen. So, it should only be used when creating a Menu in {@link
+android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo)
+onCreateContextMenu()}.</p>
-<p>Here's an example demonstrating how an application would search for
-additional services to display on its menu.</p>
+<p>For example:</p>
<pre>
+@Override
public boolean onCreateOptionsMenu(Menu menu){
super.onCreateOptionsMenu(menu);
// Create an Intent that describes the requirements to fulfill, to be included
- // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
- Intent intent = new Intent(null, getIntent().getData());
+ // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
+ Intent intent = new Intent(null, dataUri);
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
-
- // Search for, and populate the menu with, acceptable offering applications.
+
+ // Search and populate the menu with acceptable offering applications.
menu.addIntentOptions(
- thisClass.INTENT_OPTIONS, // Menu group
+ R.id.intent_group, // Menu group to which new items will be added
0, // Unique item ID (none)
0, // Order for the items (none)
this.getComponentName(), // The current Activity name
@@ -495,17 +612,27 @@
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>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>If you wish to offer the services of your Activity to other application menus, then you
-only need to define an intent filter as usual. Just be sure to include the <var>ALTERNATIVE</var> and/or
-<var>SELECTED_ALTERNATIVE</var> values in the <var>name</var> attribute of
-a <code><category></code> element in the intent filter. For example:</p>
+<p class="note"><strong>Note:</strong> When you call {@link
+android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[])
+addIntentOptions()}, it overrides any and all menu items by the menu group specified in the first
+argument.</p>
+
+
+<h4>Allowing your Activity to be added to menus</h4>
+
+<p>You can also offer the services of your Activity to other applications, so your
+application can be included in the menu of others (reverse the roles described above).</p>
+
+<p>To be included in other application menus, you need to define an Intent
+filter as usual, but be sure to include the {@link android.content.Intent#CATEGORY_ALTERNATIVE}
+and/or {@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} values for the Intent filter
+category. For example:</p>
<pre>
<intent-filter label="Resize Image">
...
@@ -514,9 +641,10 @@
...
</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 5e75105..6cb7228 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -43,29 +43,73 @@
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 f8130ea..89fdd2d 100644
--- a/docs/html/resources/dashboard/screens.jd
+++ b/docs/html/resources/dashboard/screens.jd
@@ -49,7 +49,7 @@
<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 @@
<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 @@
</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 c3ba495..77f7e43 100644
--- a/docs/html/sdk/older_releases.jd
+++ b/docs/html/sdk/older_releases.jd
@@ -47,7 +47,7 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 24ac82b..cfc17a5 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -24,7 +24,7 @@
class ISurface;
class ICamera;
-class IMediaPlayerClient;
+class IMediaRecorderClient;
class IMediaRecorder: public IInterface
{
@@ -43,7 +43,7 @@
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 0000000..0058ef2
--- /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 a4eea2a..c3cd361 100644
--- a/include/media/MediaProfiles.h
+++ b/include/media/MediaProfiles.h
@@ -48,8 +48,8 @@
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 @@
* 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 @@
/**
* 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 @@
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 @@
delete mAudioCodec;
}
+ int mCameraId;
output_format mFileFormat;
camcorder_quality mQuality;
int mDuration;
@@ -249,6 +247,11 @@
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 @@
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 @@
static bool sIsInitialized;
static MediaProfiles *sInstance;
static Mutex sLock;
+ int mCurrentCameraId;
Vector<CamcorderProfile*> mCamcorderProfiles;
Vector<AudioEncoderCap*> mAudioEncoders;
@@ -310,8 +316,7 @@
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 5b787a7..497965c 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -41,7 +41,7 @@
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 4f17c1a..c04105e 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 @@
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 4f4ec43..b21bc4d 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 @@
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 1e341b9..bc3f464 100644
--- a/include/media/stagefright/ColorConverter.h
+++ b/include/media/stagefright/ColorConverter.h
@@ -58,6 +58,11 @@
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 46aaf7c..8d3a9df 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 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 @@
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 @@
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 c95fc02..99ec8e6 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -172,6 +172,9 @@
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 69ad837..194f1fc 100644
--- a/include/media/stagefright/foundation/ALooper.h
+++ b/include/media/stagefright/foundation/ALooper.h
@@ -39,7 +39,10 @@
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 139c620..c674cba 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -60,6 +60,8 @@
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 f333911..4fd0681 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -131,7 +131,7 @@
// ---------------------------------------------------------------------------
class Surface
- : public EGLNativeBase<android_native_window_t, Surface, RefBase>
+ : public EGLNativeBase<ANativeWindow, Surface, RefBase>
{
public:
struct SurfaceInfo {
@@ -195,14 +195,14 @@
/*
- * 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 8ea3ab9..0f4594f 100644
--- a/include/ui/FramebufferNativeWindow.h
+++ b/include/ui/FramebufferNativeWindow.h
@@ -43,7 +43,7 @@
class FramebufferNativeWindow
: public EGLNativeBase<
- android_native_window_t,
+ ANativeWindow,
FramebufferNativeWindow,
LightRefBase<FramebufferNativeWindow> >
{
@@ -59,12 +59,12 @@
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 32f85b3..a2e0ba06 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -43,7 +43,7 @@
/*
* Declare a concrete type for the NDK's input event forward declaration.
*/
-struct input_event_t { };
+struct AInputEvent { };
namespace android {
@@ -133,7 +133,7 @@
/*
* Input events.
*/
-class InputEvent : public input_event_t {
+class InputEvent : public AInputEvent {
public:
virtual ~InputEvent() { }
@@ -142,7 +142,7 @@
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 @@
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 511ad20..eb8f820 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -126,21 +126,21 @@
/* 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 @@
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 @@
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 7509dd2..e755238 100644
--- a/include/ui/InputManager.h
+++ b/include/ui/InputManager.h
@@ -87,6 +87,14 @@
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 @@
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 d76b8fe..781da35 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -53,6 +53,8 @@
*/
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 @@
} 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 @@
// 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 d6bded6..2dfe2a8 100644
--- a/include/ui/InputTransport.h
+++ b/include/ui/InputTransport.h
@@ -333,18 +333,20 @@
/*
* 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 171f3df..ca89b06 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 @@
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 @@
*
* 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 @@
*
* 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 @@
*
* 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 @@
*
* 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 @@
*
* 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 @@
*
*/
- 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 @@
*/
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 @@
* 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 @@
* 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 @@
* 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 @@
* 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_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 cb9937c..5ae8d01 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -29,7 +29,7 @@
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 d8a9a99..596f533 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -473,7 +473,7 @@
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 @@
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 82c3687..709730e 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -98,7 +98,7 @@
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 @@
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 0879a66..a14bfb5 100644
--- a/libs/surfaceflinger/Android.mk
+++ b/libs/surfaceflinger/Android.mk
@@ -6,6 +6,7 @@
DisplayHardware/DisplayHardware.cpp \
DisplayHardware/DisplayHardwareBase.cpp \
BlurFilter.cpp.arm \
+ GLExtensions.cpp \
Layer.cpp \
LayerBase.cpp \
LayerBuffer.cpp \
@@ -19,8 +20,8 @@
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 51de1da..2eac0a8 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 @@
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 @@
{
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 @@
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 @@
}
}
- 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 @@
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 @@
*/
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 @@
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 @@
context = eglCreateContext(display, config, NULL, NULL);
- /*
- * 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);
-
- 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;
- }
-#else
-#warning "EGL_ANDROID_image_native_buffer not supported"
-#endif
-
-
- // 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;
+
+ /*
+ * Gather OpenGL ES extensions
+ */
+
+ eglMakeCurrent(display, surface, surface, context);
+
+ 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);
+
+
+#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;
+ }
+ }
+ // 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);
}
/*
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
index ebd7c42..66bf521 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 @@
{
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 0000000..7f4f9fc
--- /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 0000000..bbb284e
--- /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 e606f71..758da4e 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 @@
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 @@
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 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 dcb27a0..e1d283b 100644
--- a/libs/surfaceflinger/Layer.h
+++ b/libs/surfaceflinger/Layer.h
@@ -37,9 +37,10 @@
// ---------------------------------------------------------------------------
-class Client;
-class UserClient;
class FreezeLock;
+class Client;
+class GLExtensions;
+class UserClient;
// ---------------------------------------------------------------------------
@@ -206,6 +207,7 @@
// 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 c1c440b..64a43c7 100644
--- a/libs/surfaceflinger/LayerBlur.cpp
+++ b/libs/surfaceflinger/LayerBlur.cpp
@@ -147,7 +147,9 @@
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 @@
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 732a4ec..5f836366 100644
--- a/libs/surfaceflinger/LayerBuffer.cpp
+++ b/libs/surfaceflinger/LayerBuffer.cpp
@@ -315,8 +315,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
: 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 @@
*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 413b8a4..1c0bf83 100644
--- a/libs/surfaceflinger/LayerBuffer.h
+++ b/libs/surfaceflinger/LayerBuffer.h
@@ -47,6 +47,7 @@
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 906a583..a1f339e 100644
--- a/libs/surfaceflinger/LayerDim.cpp
+++ b/libs/surfaceflinger/LayerDim.cpp
@@ -51,54 +51,6 @@
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 @@
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 96a5411..68e8f19 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 @@
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 @@
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 d9bdc6a..6526032 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 @@
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 @@
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 @@
/*
* round to POT if needed
*/
- if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) {
+ if (!mGLExtensions.haveNpot()) {
texture->NPOTAdjust = true;
}
@@ -252,7 +264,7 @@
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 @@
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 @@
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 @@
{
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 1f7fe3f..c7c14e7 100644
--- a/libs/surfaceflinger/TextureManager.h
+++ b/libs/surfaceflinger/TextureManager.h
@@ -32,6 +32,7 @@
// ---------------------------------------------------------------------------
+class GLExtensions;
class GraphicBuffer;
// ---------------------------------------------------------------------------
@@ -61,7 +62,7 @@
// ---------------------------------------------------------------------------
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 @@
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 8617d94a..dc6332c 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -387,21 +387,21 @@
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 @@
// ----------------------------------------------------------------------------
-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 @@
{
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 52380a0..6f8948d 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -67,7 +67,7 @@
* 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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 4b5f025..519c277 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -127,18 +127,6 @@
{
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 4121b5a..a64251f 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -20,6 +20,70 @@
// 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 b3103a4..8f6d1fe 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 @@
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 @@
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::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 @@
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 @@
setInjectionResultLocked(entry, injectionResult);
- dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ }
}
void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
@@ -395,7 +408,7 @@
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 @@
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 @@
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::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::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 32c58b4..e1d15a4 100644
--- a/libs/ui/InputManager.cpp
+++ b/libs/ui/InputManager.cpp
@@ -85,6 +85,10 @@
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 1824054..899027c 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 @@
* 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 @@
* 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 @@
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 @@
#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 @@
}
}
+ // 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 @@
}
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::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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- mDisplayOrientation = newOrientation;
+ if (newOrientation != mDisplayOrientation) {
+ LOGD("Display orientation changed to %d", mDisplayOrientation);
+
+ mDisplayOrientation = newOrientation;
+ }
return true;
} else {
resetDisplayProperties();
@@ -1721,10 +1755,25 @@
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::configureDeviceForCurrentDisplaySize(InputDevice* device) {
if (device->isTouchScreen()) {
- if (mDisplayWidth < 0) {
- LOGD("Skipping part of touch screen configuration since display size is unknown.");
- } else {
- LOGI("Device configured: id=0x%x, name=%s (display size was changed)", device->id,
- device->name.string());
- configureVirtualKeys(device);
+ 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;
- device->touchScreen.precalculated.xScale =
- float(mDisplayWidth) / device->touchScreen.parameters.xAxis.range;
- device->touchScreen.precalculated.yScale =
- float(mDisplayHeight) / device->touchScreen.parameters.yAxis.range;
+ 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 {
+ 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 @@
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 b2842d0..25def3c 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -430,10 +430,12 @@
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 @@
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 @@
} // namespace android
-// --- input_queue_t ---
+// --- AInputQueue ---
using android::InputEvent;
using android::InputChannel;
@@ -695,13 +699,13 @@
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 c9a5950..b205418 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -59,16 +59,13 @@
// 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 50f0674..9212708 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 @@
} 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 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 64d6460..a27df57 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -119,15 +119,26 @@
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 @@
// 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 f8d3935..6a0be08 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 @@
/*
* 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 @@
// 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 7d7533a..cce9fd0 100644
--- a/media/jni/android_media_MediaProfiles.cpp
+++ b/media/jni/android_media_MediaProfiles.cpp
@@ -162,26 +162,26 @@
}
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 @@
}
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 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 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 7908f5d..de9e51d 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -12,6 +12,7 @@
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 2bc2a7e..9fe207c 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 @@
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 @@
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 0000000..ff235c9
--- /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 1263373..3869389 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -272,7 +272,7 @@
}
/*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 @@
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 @@
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 @@
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 @@
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 @@
/*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 @@
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 @@
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 084f039..80b1cfd 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -318,7 +318,7 @@
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 e07306b..b53d950 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -40,7 +40,7 @@
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 1e20f7e..91c5b92 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 @@
}
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::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 @@
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 @@
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::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 @@
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 @@
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 @@
// 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::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::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 @@
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 @@
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::start() {
+ CHECK(mOutputFd >= 0);
+
if (mWriter != NULL) {
+ LOGE("File writer is not avaialble");
return UNKNOWN_ERROR;
}
@@ -486,6 +563,7 @@
return startAACRecording();
default:
+ LOGE("Unsupported output file format: %d", mOutputFormat);
return UNKNOWN_ERROR;
}
}
@@ -549,7 +627,6 @@
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 @@
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 @@
return UNKNOWN_ERROR;
}
- CHECK(mOutputFd >= 0);
mWriter = new AMRWriter(dup(mOutputFd));
mWriter->addSource(audioEncoder);
@@ -668,6 +744,54 @@
}
}
+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 @@
}
}
+status_t StagefrightRecorder::setupVideoEncoder(const sp<MediaWriter>& writer) {
+ status_t err = setupCameraSource();
+ if (err != OK) return err;
+
+ sp<CameraSource> cameraSource = CameraSource::CreateFromCamera(mCamera);
+ CHECK(cameraSource != NULL);
+
+ sp<MetaData> enc_meta = new MetaData;
+ enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
+ enc_meta->setInt32(kKeySampleRate, mFrameRate);
+
+ switch (mVideoEncoder) {
+ case VIDEO_ENCODER_H263:
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
+ break;
+
+ case VIDEO_ENCODER_MPEG_4_SP:
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ break;
+
+ case VIDEO_ENCODER_H264:
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ break;
+
+ default:
+ CHECK(!"Should not be here, unsupported video encoding.");
+ break;
+ }
+
+ sp<MetaData> meta = cameraSource->getFormat();
+
+ int32_t width, height, stride, sliceHeight;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+ CHECK(meta->findInt32(kKeyStride, &stride));
+ CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight));
+
+ enc_meta->setInt32(kKeyWidth, width);
+ enc_meta->setInt32(kKeyHeight, height);
+ enc_meta->setInt32(kKeyIFramesInterval, mIFramesInterval);
+ enc_meta->setInt32(kKeyStride, stride);
+ enc_meta->setInt32(kKeySliceHeight, sliceHeight);
+
+ OMXClient client;
+ CHECK_EQ(client.connect(), OK);
+
+ sp<MediaSource> encoder = OMXCodec::Create(
+ client.interface(), enc_meta,
+ true /* createEncoder */, cameraSource);
+ if (encoder == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ writer->addSource(encoder);
+ return OK;
+}
+
+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;
+ }
+
+ if (audioEncoder == NULL) {
+ return UNKNOWN_ERROR;
+ }
+ writer->addSource(audioEncoder);
+ return OK;
+}
+
status_t StagefrightRecorder::startMPEG4Recording() {
- mWriter = new MPEG4Writer(dup(mOutputFd));
int32_t totalBitRate = 0;
+ status_t err = OK;
+ sp<MediaWriter> writer = new MPEG4Writer(dup(mOutputFd));
// Add audio source first if it exists
if (mAudioSource != AUDIO_SOURCE_LIST_END) {
- sp<MediaSource> audioEncoder;
- switch(mAudioEncoder) {
- case AUDIO_ENCODER_AMR_NB:
- case AUDIO_ENCODER_AMR_WB:
- case AUDIO_ENCODER_AAC:
- audioEncoder = createAudioSource();
- break;
- default:
- LOGE("Unsupported audio encoder: %d", mAudioEncoder);
- return UNKNOWN_ERROR;
- }
-
- if (audioEncoder == NULL) {
- return UNKNOWN_ERROR;
- }
+ err = setupAudioEncoder(writer);
+ if (err != OK) return err;
totalBitRate += mAudioBitRate;
- mWriter->addSource(audioEncoder);
}
if (mVideoSource == VIDEO_SOURCE_DEFAULT
|| mVideoSource == VIDEO_SOURCE_CAMERA) {
-
- clipVideoBitRate();
- clipVideoFrameRate();
- clipVideoFrameWidth();
- clipVideoFrameHeight();
-
- int64_t token = IPCThreadState::self()->clearCallingIdentity();
- if (mCamera == 0) {
- mCamera = Camera::connect(mCameraId);
- mCamera->lock();
- }
-
- // Set the actual video recording frame size
- CameraParameters params(mCamera->getParameters());
- params.setPreviewSize(mVideoWidth, mVideoHeight);
- params.setPreviewFrameRate(mFrameRate);
- String8 s = params.flatten();
- CHECK_EQ(OK, mCamera->setParameters(s));
- CameraParameters newCameraParams(mCamera->getParameters());
-
- // Check on video frame size
- int frameWidth = 0, frameHeight = 0;
- newCameraParams.getPreviewSize(&frameWidth, &frameHeight);
- if (frameWidth < 0 || frameWidth != mVideoWidth ||
- frameHeight < 0 || frameHeight != mVideoHeight) {
- LOGE("Failed to set the video frame size to %dx%d",
- mVideoWidth, mVideoHeight);
- IPCThreadState::self()->restoreCallingIdentity(token);
- return UNKNOWN_ERROR;
- }
-
- // Check on video frame rate
- int frameRate = newCameraParams.getPreviewFrameRate();
- if (frameRate < 0 || (frameRate - mFrameRate) != 0) {
- LOGE("Failed to set frame rate to %d fps. The actual "
- "frame rate is %d", mFrameRate, frameRate);
- }
-
- CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface));
- IPCThreadState::self()->restoreCallingIdentity(token);
-
- sp<CameraSource> cameraSource =
- CameraSource::CreateFromCamera(mCamera);
-
- CHECK(cameraSource != NULL);
-
- sp<MetaData> enc_meta = new MetaData;
- enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
- enc_meta->setInt32(kKeySampleRate, mFrameRate);
-
- switch (mVideoEncoder) {
- case VIDEO_ENCODER_H263:
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
- break;
-
- case VIDEO_ENCODER_MPEG_4_SP:
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
- break;
-
- case VIDEO_ENCODER_H264:
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
- break;
-
- default:
- CHECK(!"Should not be here, unsupported video encoding.");
- break;
- }
-
- sp<MetaData> meta = cameraSource->getFormat();
-
- int32_t width, height, stride, sliceHeight;
- CHECK(meta->findInt32(kKeyWidth, &width));
- CHECK(meta->findInt32(kKeyHeight, &height));
- CHECK(meta->findInt32(kKeyStride, &stride));
- CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight));
-
- enc_meta->setInt32(kKeyWidth, width);
- enc_meta->setInt32(kKeyHeight, height);
- enc_meta->setInt32(kKeyIFramesInterval, mIFramesInterval);
- enc_meta->setInt32(kKeyStride, stride);
- enc_meta->setInt32(kKeySliceHeight, sliceHeight);
-
- OMXClient client;
- CHECK_EQ(client.connect(), OK);
-
- sp<MediaSource> encoder =
- OMXCodec::Create(
- client.interface(), enc_meta,
- true /* createEncoder */, cameraSource);
-
- CHECK(mOutputFd >= 0);
+ 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 @@
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::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::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 9fb7e8f..cb05571 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -46,7 +46,7 @@
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 @@
sp<Camera> mCamera;
sp<ISurface> mPreviewSurface;
- sp<IMediaPlayerClient> mListener;
+ sp<IMediaRecorderClient> mListener;
sp<MediaWriter> mWriter;
sp<AudioSource> mAudioSourceNode;
@@ -97,6 +97,11 @@
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 @@
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 b3e1a01..6a4a131 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 @@
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 @@
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 @@
}
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::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::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 @@
}
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 02a073e..dacb8d3 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -149,13 +149,16 @@
{ 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 @@
{ 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 @@
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 @@
}
case OMX_VIDEO_CodingH263:
+ CHECK_EQ(setupH263EncoderParameters(meta), OK);
break;
case OMX_VIDEO_CodingAVC:
@@ -861,6 +875,90 @@
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 @@
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 @@
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 b914023..2317de6 100644
--- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp
+++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp
@@ -132,7 +132,10 @@
}
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::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 445438f..4c02fe9 100644
--- a/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/AMRNBEncoder.cpp
@@ -70,7 +70,10 @@
}
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::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 b70cff1..4257c6a 100644
--- a/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
+++ b/media/libstagefright/codecs/amrwbenc/AMRWBEncoder.cpp
@@ -124,7 +124,10 @@
}
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::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 e74782f..5b16997 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -42,6 +42,7 @@
case OMX_COLOR_FormatYUV420Planar:
case OMX_COLOR_FormatCbYCrY:
case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
+ case OMX_COLOR_FormatYUV420SemiPlanar:
return true;
default:
@@ -71,6 +72,11 @@
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::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 831fa2a..cd4f349 100644
--- a/media/libstagefright/foundation/ALooper.cpp
+++ b/media/libstagefright/foundation/ALooper.cpp
@@ -31,8 +31,9 @@
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 @@
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 @@
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 dfd1ae3..26c6d42 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 @@
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 8c621b6..fe8ed00 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -7,7 +7,8 @@
#
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 38d8567..8498840 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -27,168 +27,168 @@
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 @@
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 @@
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 0000000..448cbfc
--- /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 2441af0..7617662 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -125,8 +125,8 @@
* 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 @@
/*** 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 a58a7d2..bf5c641 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 @@
* state.
*/
void* instance;
-} android_activity_t;
+} ANativeActivity;
/**
* These are the callbacks the framework makes into a native application.
@@ -73,18 +77,18 @@
* 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 @@
* 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.
+ * The drawing window for this native activity has been created. You
+ * can use the given native window object to start drawing.
*/
- void (*onSurfaceCreated)(android_activity_t* activity, android_surface_t* surface);
+ void (*onNativeWindowCreated)(ANativeActivity* activity, ANativeWindow* window);
/**
- * 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.
- */
- void (*onSurfaceChanged)(android_activity_t* activity, android_surface_t* surface,
- int format, int width, int height);
-
- /**
- * 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 @@
* 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 0000000..678ba3d
--- /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 a2c6a7d..25d7697 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -91,10 +91,11 @@
#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 7cb01d0..54d7307 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -213,7 +213,7 @@
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 @@
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 @@
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 @@
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 @@
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 74d87ba..75045d7 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 0000000..bc127bd
--- /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 0000000..59af804
--- /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 0000000..258de13
--- /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 0000000..258de13
--- /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 0000000..24b6aa3
--- /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 0000000..24eb960
--- /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 10fa930..816f34a4 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 0f6723e..f45caf51 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 @@
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 c5b901f..3d85f27 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 @@
public class ExpandedView extends LinearLayout {
- PhoneStatusBarService mService;
+ StatusBarService mService;
int mPrevHeight = -1;
public ExpandedView(Context context, AttributeSet attrs) {
@@ -50,10 +50,10 @@
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 5d16e93..0000000
--- 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 bc1e798..d98bd7d 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 @@
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 @@
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 @@
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 d200886..07bcce7 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 android.content.Context;
-import android.content.Intent;
-import android.graphics.PixelFormat;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.Slog;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-
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.util.ArrayList;
+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.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;
-public abstract class StatusBarService extends Service implements CommandQueue.Callbacks {
- private static final String TAG = "StatusBarService";
+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 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 @@
// 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 @@
// we're never destroyed
}
+ // for immersive activities
+ private View mIntruderAlertView;
+
/**
* Nobody binds to us.
*/
@@ -102,9 +258,1349 @@
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);
+ }
+ }
+
+
/**
- * Implement this to add the main status bar view.
+ * State is one or more of the DISABLE constants from StatusBarManager.
*/
- protected abstract void addStatusBarView();
+ 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);
+ }
+
+ /**
+ * 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/StatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
index 466cc75..1e140b9 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 @@
static final int DIM_ANIM_TIME = 400;
- PhoneStatusBarService mService;
+ StatusBarService mService;
boolean mTracking;
int mStartX, mStartY;
ViewGroup mNotificationIcons;
@@ -94,7 +94,7 @@
@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 c59eb6a..9108eee 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 @@
public class TrackingView extends LinearLayout {
final Display mDisplay;
- PhoneStatusBarService mService;
+ StatusBarService mService;
boolean mTracking;
int mStartX, mStartY;
diff --git a/core/jni/android_view_InputTarget.h b/services/java/com/android/server/InputApplication.java
similarity index 62%
copy from core/jni/android_view_InputTarget.h
copy to services/java/com/android/server/InputApplication.java
index 9230b1b..38420d4 100644
--- a/core/jni/android_view_InputTarget.h
+++ b/services/java/com/android/server/InputApplication.java
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-#ifndef _ANDROID_VIEW_INPUTTARGET_H
-#define _ANDROID_VIEW_INPUTTARGET_H
+package com.android.server;
-#include "jni.h"
-
-namespace android {
-
-class InputTarget;
-
-extern void android_view_InputTarget_toNative(JNIEnv* env, jobject inputTargetObj,
- InputTarget* outInputTarget);
-
-} // namespace android
-
-#endif // _ANDROID_OS_INPUTTARGET_H
+/**
+ * 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 8d9bb29..2ba2914 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 @@
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 @@
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 @@
@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 int hackInterceptKey(int deviceId, int type, int scanCode,
+ public long notifyANR(Object token) {
+ return mWindowManagerService.mInputMonitor.notifyANR(token);
+ }
+
+ @SuppressWarnings("unused")
+ 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 @@
}
@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 @@
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 83acc8f..0000000
--- 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 0000000..8da0cf1
--- /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 0000000..1cbb2cc
--- /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 3e2c122..25de8b0 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.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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 7130636..c01680e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -80,7 +80,8 @@
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 3993a7f..d08fe9b 100644
--- a/services/java/com/android/server/UsbObserver.java
+++ b/services/java/com/android/server/UsbObserver.java
@@ -159,6 +159,16 @@
}
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 @@
// 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);
- mContext.sendBroadcast(intent, android.Manifest.permission.ACCESS_USB);
+ // 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 d4133f3..a742093 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -57,20 +57,10 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
private Watchdog() {
super("watchdog");
mHandler = new HeartbeatHandler();
- mGlobalPssCollected = new GlobalPssCollected();
}
public void init(Context context, BatteryService battery,
@@ -403,11 +194,6 @@
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 @@
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 @@
}
}
- /**
- * 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 @@
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 95ab5bc..483f9eb 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -103,7 +103,6 @@
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 @@
// 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 @@
mHaveInputMethods);
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_DISPLAY);
+ android.os.Process.setCanSelfBackground(false);
synchronized (this) {
mService = s;
@@ -605,6 +608,7 @@
// 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 @@
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 @@
//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 @@
}
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 @@
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 @@
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 @@
"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 @@
return;
}
wtoken = new AppWindowToken(token);
+ wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
wtoken.groupId = groupId;
wtoken.appFullscreen = fullscreen;
wtoken.requestedOrientation = requestedOrientation;
@@ -3319,7 +3349,13 @@
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 @@
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 @@
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 @@
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 @@
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;
+ InputMonitor mInputMonitor = new InputMonitor();
+
+ /* 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;
+
+ // When true, prevents input dispatch from proceeding until set to false again.
+ private boolean mInputDispatchFrozen;
+
+ // 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();
+
+ // Temporary input application object to provide to the input dispatcher.
+ private InputApplication mTempInputApplication = new InputApplication();
+
+ /* 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);
}
}
- return true;
- }
-
- /* 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?
- WindowState focus = getFocusedWindow();
-
- if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) {
- return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- }
-
- 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;
- }
-
- if (focus == null) {
- return InputManager.INPUT_EVENT_INJECTION_FAILED;
- }
-
- 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);
+ /* 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)
+ }
- case InputQueue.INPUT_EVENT_NATURE_TOUCH:
- return getMotionEventTargetsForTouchTd(inputTargets, event, policyFlags,
- injectorPid, injectorUid);
-
- default:
- return InputManager.INPUT_EVENT_INJECTION_FAILED;
- }
- }
-
- /* 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;
- }
-
- 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;
-
- /* 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);
- }
-
- 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;
+ Slog.i(TAG, "Input event dispatching timed out sending to "
+ + windowState.mAttrs.getTitle());
+ token = windowState.mAppToken;
}
- 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;
- }
+ return notifyANRInternal(token);
+ }
+
+ /* 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.
+ }
+
+ /* 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;
+
+ Slog.i(TAG, "Input event dispatching timed out sending to application "
+ + appWindowToken.stringName);
+ return notifyANRInternal(appWindowToken);
+ }
+
+ 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);
+ private WindowState getWindowStateForInputChannel(InputChannel inputChannel) {
+ synchronized (mWindowMap) {
+ return getWindowStateForInputChannelLocked(inputChannel);
+ }
+ }
+
+ 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;
+ }
}
- 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);
- }
- }
-
- if (action == MotionEvent.ACTION_UP) {
- updateTouchFocusAfterUpTd(event, policyFlags);
+ return null;
}
- 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);
- }
-
- 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);
-
- final int flags = child.mAttrs.flags;
- if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
- if (topErrWindow == null) {
- topErrWindow = child;
- }
- }
-
- if (!child.isVisibleLw()) {
- //Slog.i(TAG, "Not visible!");
+ 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;
}
- 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 flags = child.mAttrs.flags;
+ final int type = child.mAttrs.type;
+
+ final boolean hasFocus = (child == mInputFocus);
+ final boolean isVisible = child.isVisibleLw();
+ final boolean hasWallpaper = (child == mWallpaperTarget)
+ && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
+
+ // 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;
}
- 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;
- }
+
+ 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;
}
- }
- }
- }
-
- 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();
- }
- }
- }
-
- 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;
- }
-
- 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);
+
+ mInputFocus = newWindow;
+ updateInputWindowsLw();
}
}
- inputTargets.add(window.mInputChannel, flags, timeoutNanos,
- - window.mFrame.left, - window.mFrame.top);
+ 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();
+ }
+
+ /* 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();
+ }
+
+ 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();
+ }
+ }
+
+ 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 @@
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 @@
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 @@
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 @@
}
synchronized (mWindowMap) {
- mKeyWaiter.setEventDispatchingLocked(enabled);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.setEventDispatchingLw(enabled);
+ } else {
+ mKeyWaiter.setEventDispatchingLocked(enabled);
+ }
}
}
@@ -7383,6 +7366,9 @@
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 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 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 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 @@
// 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 @@
i--;
WindowState c = (WindowState)mChildWindows.get(i);
c.mAttachedHidden = true;
+
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBecomingInvisibleLw(c);
+ }
}
if (mReportDestroySurface) {
@@ -8405,7 +8409,14 @@
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 @@
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 @@
}
}
}
+
+ // 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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
}
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 2c6806b..93122c4 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -880,7 +880,6 @@
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 @@
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
+ android.os.Process.setCanSelfBackground(false);
ActivityManagerService m = new ActivityManagerService();
@@ -1785,7 +1785,7 @@
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 @@
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 18fd9d6..18b1acb 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -39,7 +39,7 @@
* 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 @@
}
}
- 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 1a6119a..d0f856b 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 @@
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 {
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 @@
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 @@
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 @@
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 @@
JNIEnv* env = jniEnv();
env->DeleteGlobalRef(mCallbacksObj);
- env->DeleteGlobalRef(mReusableInputTargetListObj);
+
+ releaseFocusedApplicationLd(env);
}
bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
@@ -362,7 +585,7 @@
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 @@
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 @@
}
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 @@
actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING;
}
}
+
return actions;
}
@@ -423,12 +647,13 @@
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 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 @@
}
}
+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 @@
}
}
-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);
+
+ 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);
+ }
+
+ 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 {
+ 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
- JNIEnv* env = jniEnv();
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
- 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;
- } 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;
- } else {
- populateInputTargets(env, mReusableInputTargetListObj, outTargets);
+ if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
+ mDispatchEnabled = enabled;
+ mDispatchFrozen = frozen;
+
+ mDispatchStateChanged.broadcast();
}
- env->DeleteLocalRef(keyEventObj);
+
+#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 {
+ 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;
+ }
+
+ // 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) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("getMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
- policyFlags, injectorPid, injectorUid);
-#endif
+int32_t NativeInputManager::waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
+ InputWindow*& outTouchedWindow) {
+ nsecs_t startTime = now();
- JNIEnv* env = jniEnv();
+ int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ int32_t action = motionEvent->getAction();
- 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;
- } 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;
+ // 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 {
- 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;
+ }
}
- android_view_MotionEvent_recycle(env, motionEventObj);
- env->DeleteLocalRef(motionEventObj);
+
+ // 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("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
+ }
+
+ // 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
+ }
+
+ // 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 {
+ 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 {
+ // Injector does not have permission to inject events.
+ // We make sure to leave the dispatch state unchanged.
+ havePermission = false;
+ }
+ 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) {
- jobjectArray inputTargetArray = jobjectArray(env->GetObjectField(
- inputTargetListObj, gInputTargetListClassInfo.mArray));
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("waitForMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ policyFlags, injectorPid, injectorUid);
+#endif
- 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
- }
+ switch (motionEvent->getNature()) {
+ case INPUT_EVENT_NATURE_TRACKBALL:
+ return identifyTrackballEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+ outTargets);
- outTargets.add();
- android_view_InputTarget_toNative(env, item, & outTargets.editTop());
+ case INPUT_EVENT_NATURE_TOUCH:
+ return identifyTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+ outTargets);
- env->DeleteLocalRef(item);
+ default:
+ assert(false);
+ return INPUT_EVENT_INJECTION_FAILED;
}
- env->DeleteLocalRef(inputTargetArray);
}
+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) {
+#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");
+
+ env->DeleteLocalRef(inputChannelObj);
+
+ return consumed && ! error;
+ } else {
+ LOGW("Could not apply key dispatch policy because input channel '%s' is "
+ "no longer valid.", target.inputChannel->getName().string());
+ return false;
+ }
+}
+
+void NativeInputManager::pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType) {
+ if (windowType != TYPE_KEYGUARD) {
+ nsecs_t eventTime = now();
+ pokeUserActivity(eventTime, eventType);
+ }
+}
+
+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 @@
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 @@
{ "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 @@
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 @@
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 @@
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz,
"height", "I");
- // InputTargetList
+ // InputWindow
- FIND_CLASS(gInputTargetListClassInfo.clazz, "com/android/server/InputTargetList");
+ FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow");
- GET_METHOD_ID(gInputTargetListClassInfo.ctor, gInputTargetListClassInfo.clazz,
- "<init>", "()V");
+ GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz,
+ "inputChannel", "Landroid/view/InputChannel;");
- GET_FIELD_ID(gInputTargetListClassInfo.mArray, gInputTargetListClassInfo.clazz,
- "mArray", "[Landroid/view/InputTarget;");
+ 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");
+
+ GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz,
+ "name", "Ljava/lang/String;");
+
+ GET_FIELD_ID(gInputApplicationClassInfo.dispatchingTimeoutNanos,
+ gInputApplicationClassInfo.clazz,
+ "dispatchingTimeoutNanos", "J");
+
+ 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 0b41dd8..59d7cde 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 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 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 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 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 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 @@
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 @@
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_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 8c343b5..c6a4134 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -104,6 +104,8 @@
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);
}
},