Merge "Add GC thread to RSGL." into jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index 1e1f01c..4236eae 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18442,6 +18442,7 @@
}
protected static abstract interface ContactsContract.ContactsColumns {
+ field public static final java.lang.String CONTACT_LAST_UPDATED_TIMESTAMP = "contact_last_updated_timestamp";
field public static final java.lang.String DISPLAY_NAME = "display_name";
field public static final java.lang.String HAS_PHONE_NUMBER = "has_phone_number";
field public static final java.lang.String IN_VISIBLE_GROUP = "in_visible_group";
@@ -18506,6 +18507,16 @@
field public static final java.lang.String TIMES_USED = "times_used";
}
+ public static final class ContactsContract.DeletedContacts implements android.provider.ContactsContract.DeletedContactsColumns {
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final long DAYS_KEPT_MILLISECONDS = 2592000000L; // 0x9a7ec800L
+ }
+
+ protected static abstract interface ContactsContract.DeletedContactsColumns {
+ field public static final java.lang.String CONTACT_DELETED_TIMESTAMP = "contact_deleted_timestamp";
+ field public static final java.lang.String CONTACT_ID = "contact_id";
+ }
+
public static final class ContactsContract.Directory implements android.provider.BaseColumns {
method public static void notifyDirectoryChange(android.content.ContentResolver);
field public static final java.lang.String ACCOUNT_NAME = "accountName";
@@ -18585,6 +18596,7 @@
public static final class ContactsContract.Intents {
ctor public ContactsContract.Intents();
field public static final java.lang.String ATTACH_IMAGE = "com.android.contacts.action.ATTACH_IMAGE";
+ field public static final java.lang.String CONTACTS_DATABASE_CREATED = "android.provider.Contacts.DATABASE_CREATED";
field public static final java.lang.String EXTRA_CREATE_DESCRIPTION = "com.android.contacts.action.CREATE_DESCRIPTION";
field public static final java.lang.String EXTRA_FORCE_CREATE = "com.android.contacts.action.FORCE_CREATE";
field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT";
@@ -19649,8 +19661,7 @@
method public int getUsage();
method public void ioReceive();
method public void ioSend();
- method public synchronized void resize(int);
- method public synchronized void resize(int, int);
+ method public deprecated synchronized void resize(int);
method public void setFromFieldPacker(int, android.renderscript.FieldPacker);
method public void setFromFieldPacker(int, int, android.renderscript.FieldPacker);
method public void setSurface(android.view.Surface);
@@ -27589,8 +27600,6 @@
method public synchronized int getMinimumFontSize();
method public synchronized int getMinimumLogicalFontSize();
method public deprecated synchronized android.webkit.WebSettings.PluginState getPluginState();
- method public deprecated synchronized boolean getPluginsEnabled();
- method public deprecated synchronized java.lang.String getPluginsPath();
method public synchronized java.lang.String getSansSerifFontFamily();
method public boolean getSaveFormData();
method public deprecated boolean getSavePassword();
@@ -27636,8 +27645,6 @@
method public synchronized void setMinimumLogicalFontSize(int);
method public void setNeedInitialFocus(boolean);
method public deprecated synchronized void setPluginState(android.webkit.WebSettings.PluginState);
- method public deprecated synchronized void setPluginsEnabled(boolean);
- method public deprecated synchronized void setPluginsPath(java.lang.String);
method public deprecated synchronized void setRenderPriority(android.webkit.WebSettings.RenderPriority);
method public synchronized void setSansSerifFontFamily(java.lang.String);
method public void setSaveFormData(boolean);
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index b39c335..b9afe40 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -7,6 +7,7 @@
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
+ liblog \
libbinder \
libandroid_runtime
@@ -27,6 +28,7 @@
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
+ liblog \
libbinder \
libandroid_runtime
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 8c46b21..d5ff84e 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -9,6 +9,7 @@
LOCAL_SHARED_LIBRARIES := \
libcutils \
+ liblog \
libandroidfw \
libutils \
libbinder \
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index cdbc405..e43501c 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -75,11 +75,14 @@
Float.parseFloat(args[3]), Float.parseFloat(args[4]), -1);
return;
}
- } else if (command.equals("touchscreen") || command.equals("touchpad")) {
+ } else if (command.equals("touchscreen") || command.equals("touchpad")
+ || command.equals("touchnavigation")) {
// determine input source
int inputSource = InputDevice.SOURCE_TOUCHSCREEN;
if (command.equals("touchpad")) {
inputSource = InputDevice.SOURCE_TOUCHPAD;
+ } else if (command.equals("touchnavigation")) {
+ inputSource = InputDevice.SOURCE_TOUCH_NAVIGATION;
}
// determine subcommand
if (args.length > 1) {
@@ -247,8 +250,9 @@
System.err.println("usage: input ...");
System.err.println(" input text <string>");
System.err.println(" input keyevent <key code number or name>");
- System.err.println(" input [touchscreen|touchpad] tap <x> <y>");
- System.err.println(" input [touchscreen|touchpad] swipe <x1> <y1> <x2> <y2> [duration(ms)]");
+ System.err.println(" input [touchscreen|touchpad|touchnavigation] tap <x> <y>");
+ System.err.println(" input [touchscreen|touchpad|touchnavigation] swipe "
+ + "<x1> <y1> <x2> <y2> [duration(ms)]");
System.err.println(" input trackball press");
System.err.println(" input trackball roll <dx> <dy>");
}
diff --git a/cmds/system_server/Android.mk b/cmds/system_server/Android.mk
index ad537977..3083e31 100644
--- a/cmds/system_server/Android.mk
+++ b/cmds/system_server/Android.mk
@@ -7,7 +7,8 @@
LOCAL_SHARED_LIBRARIES := \
libutils \
libbinder \
- libsystem_server
+ libsystem_server \
+ liblog
LOCAL_C_INCLUDES := \
$(JNI_H_INCLUDE)
@@ -17,4 +18,3 @@
include $(BUILD_EXECUTABLE)
include $(LOCAL_PATH)/library/Android.mk
-
diff --git a/cmds/system_server/library/Android.mk b/cmds/system_server/library/Android.mk
index c42424c..d78474e 100644
--- a/cmds/system_server/library/Android.mk
+++ b/cmds/system_server/library/Android.mk
@@ -16,10 +16,11 @@
libandroid_runtime \
libsensorservice \
libsurfaceflinger \
- libinput \
+ libinput \
libutils \
libbinder \
- libcutils
+ libcutils \
+ liblog
LOCAL_MODULE:= libsystem_server
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 68a2397..e6ce963 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4322,6 +4322,10 @@
GLUtils.setTracingLevel(1);
}
+ // Allow application-generated systrace messages if we're debuggable.
+ boolean appTracingAllowed = (data.appInfo.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ Trace.setAppTracingAllowed(appTracingAllowed);
+
/**
* Initialize the default http proxy in this process for the reasons we set the time zone.
*/
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 27ed6b6..310b12c 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -19,41 +19,55 @@
import android.util.Log;
/**
- * Writes trace events to the kernel trace buffer. These trace events can be
- * collected using the "atrace" program for offline analysis.
+ * Writes trace events to the system trace buffer. These trace events can be
+ * collected and visualized using the Systrace tool.
*
* This tracing mechanism is independent of the method tracing mechanism
* offered by {@link Debug#startMethodTracing}. In particular, it enables
- * tracing of events that occur across processes.
+ * tracing of events that occur across multiple processes.
*
* @hide
*/
public final class Trace {
+ /*
+ * Writes trace events to the kernel trace buffer. These trace events can be
+ * collected using the "atrace" program for offline analysis.
+ */
+
private static final String TAG = "Trace";
// These tags must be kept in sync with system/core/include/cutils/trace.h.
+ /** @hide */
public static final long TRACE_TAG_NEVER = 0;
+ /** @hide */
public static final long TRACE_TAG_ALWAYS = 1L << 0;
+ /** @hide */
public static final long TRACE_TAG_GRAPHICS = 1L << 1;
+ /** @hide */
public static final long TRACE_TAG_INPUT = 1L << 2;
+ /** @hide */
public static final long TRACE_TAG_VIEW = 1L << 3;
+ /** @hide */
public static final long TRACE_TAG_WEBVIEW = 1L << 4;
+ /** @hide */
public static final long TRACE_TAG_WINDOW_MANAGER = 1L << 5;
+ /** @hide */
public static final long TRACE_TAG_ACTIVITY_MANAGER = 1L << 6;
+ /** @hide */
public static final long TRACE_TAG_SYNC_MANAGER = 1L << 7;
+ /** @hide */
public static final long TRACE_TAG_AUDIO = 1L << 8;
+ /** @hide */
public static final long TRACE_TAG_VIDEO = 1L << 9;
+ /** @hide */
public static final long TRACE_TAG_CAMERA = 1L << 10;
+ /** @hide */
public static final long TRACE_TAG_HAL = 1L << 11;
+ /** @hide */
+ public static final long TRACE_TAG_APP = 1L << 12;
+
private static final long TRACE_TAG_NOT_READY = 1L << 63;
-
- public static final int TRACE_FLAGS_START_BIT = 1;
- public static final String[] TRACE_TAGS = {
- "Graphics", "Input", "View", "WebView", "Window Manager",
- "Activity Manager", "Sync Manager", "Audio", "Video", "Camera", "HAL",
- };
-
- public static final String PROPERTY_TRACE_TAG_ENABLEFLAGS = "debug.atrace.tags.enableflags";
+ private static final int MAX_SECTION_NAME_LEN = 127;
// Must be volatile to avoid word tearing.
private static volatile long sEnabledTags = TRACE_TAG_NOT_READY;
@@ -62,6 +76,7 @@
private static native void nativeTraceCounter(long tag, String name, int value);
private static native void nativeTraceBegin(long tag, String name);
private static native void nativeTraceEnd(long tag);
+ private static native void nativeSetAppTracingAllowed(boolean allowed);
static {
// We configure two separate change callbacks, one in Trace.cpp and one here. The
@@ -111,6 +126,8 @@
*
* @param traceTag The trace tag to check.
* @return True if the trace tag is valid.
+ *
+ * @hide
*/
public static boolean isTagEnabled(long traceTag) {
long tags = sEnabledTags;
@@ -126,6 +143,8 @@
* @param traceTag The trace tag.
* @param counterName The counter name to appear in the trace.
* @param counterValue The counter value.
+ *
+ * @hide
*/
public static void traceCounter(long traceTag, String counterName, int counterValue) {
if (isTagEnabled(traceTag)) {
@@ -134,11 +153,28 @@
}
/**
- * Writes a trace message to indicate that a given method has begun.
- * Must be followed by a call to {@link #traceEnd} using the same tag.
+ * Set whether application tracing is allowed for this process. This is intended to be set
+ * once at application start-up time based on whether the application is debuggable.
+ *
+ * @hide
+ */
+ public static void setAppTracingAllowed(boolean allowed) {
+ nativeSetAppTracingAllowed(allowed);
+
+ // Setting whether app tracing is allowed may change the tags, so we update the cached
+ // tags here.
+ cacheEnabledTags();
+ }
+
+ /**
+ * Writes a trace message to indicate that a given section of code has
+ * begun. Must be followed by a call to {@link #traceEnd} using the same
+ * tag.
*
* @param traceTag The trace tag.
* @param methodName The method name to appear in the trace.
+ *
+ * @hide
*/
public static void traceBegin(long traceTag, String methodName) {
if (isTagEnabled(traceTag)) {
@@ -151,10 +187,48 @@
* Must be called exactly once for each call to {@link #traceBegin} using the same tag.
*
* @param traceTag The trace tag.
+ *
+ * @hide
*/
public static void traceEnd(long traceTag) {
if (isTagEnabled(traceTag)) {
nativeTraceEnd(traceTag);
}
}
+
+ /**
+ * Writes a trace message to indicate that a given section of code has begun. This call must
+ * be followed by a corresponding call to {@link #traceEnd()} on the same thread.
+ *
+ * <p class="note"> At this time the vertical bar character '|', newline character '\n', and
+ * null character '\0' are used internally by the tracing mechanism. If sectionName contains
+ * these characters they will be replaced with a space character in the trace.
+ *
+ * @param sectionName The name of the code section to appear in the trace. This may be at
+ * most 127 Unicode code units long.
+ *
+ * @hide
+ */
+ public static void traceBegin(String sectionName) {
+ if (isTagEnabled(TRACE_TAG_APP)) {
+ if (sectionName.length() > MAX_SECTION_NAME_LEN) {
+ throw new IllegalArgumentException("sectionName is too long");
+ }
+ nativeTraceBegin(TRACE_TAG_APP, sectionName);
+ }
+ }
+
+ /**
+ * Writes a trace message to indicate that a given section of code has ended. This call must
+ * be preceeded by a corresponding call to {@link #traceBegin(String)}. Calling this method
+ * will mark the end of the most recently begun section of code, so care must be taken to
+ * ensure that traceBegin / traceEnd pairs are properly nested and called from the same thread.
+ *
+ * @hide
+ */
+ public static void traceEnd() {
+ if (isTagEnabled(TRACE_TAG_APP)) {
+ nativeTraceEnd(TRACE_TAG_APP);
+ }
+ }
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 367d576..c41c35e 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -35,9 +35,7 @@
import android.database.DatabaseUtils;
import android.graphics.Rect;
import android.net.Uri;
-import android.os.Bundle;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Pair;
@@ -939,6 +937,15 @@
* its row id changed as a result of a sync or aggregation.
*/
public static final String LOOKUP_KEY = "lookup";
+
+ /**
+ * Timestamp (milliseconds since epoch) of when this contact was last updated. This
+ * includes updates to all data associated with this contact including raw contacts. Any
+ * modification (including deletes and inserts) of underlying contact data are also
+ * reflected in this timestamp.
+ */
+ public static final String CONTACT_LAST_UPDATED_TIMESTAMP =
+ "contact_last_updated_timestamp";
}
/**
@@ -2113,6 +2120,56 @@
return id >= Profile.MIN_ID;
}
+ protected interface DeletedContactsColumns {
+
+ /**
+ * A reference to the {@link ContactsContract.Contacts#_ID} that was deleted.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String CONTACT_ID = "contact_id";
+
+ /**
+ * Time (milliseconds since epoch) that the contact was deleted.
+ */
+ public static final String CONTACT_DELETED_TIMESTAMP = "contact_deleted_timestamp";
+ }
+
+ /**
+ * Constants for the deleted contact table. This table holds a log of deleted contacts.
+ * <p>
+ * Log older than {@link #DAYS_KEPT_MILLISECONDS} may be deleted.
+ */
+ public static final class DeletedContacts implements DeletedContactsColumns {
+
+ /**
+ * This utility class cannot be instantiated
+ */
+ private DeletedContacts() {
+ }
+
+ /**
+ * The content:// style URI for this table, which requests a directory of raw contact rows
+ * matching the selection criteria.
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI,
+ "deleted_contacts");
+
+ /**
+ * Number of days that the delete log will be kept. After this time, delete records may be
+ * deleted.
+ *
+ * @hide
+ */
+ private static final int DAYS_KEPT = 30;
+
+ /**
+ * Milliseconds that the delete log will be kept. After this time, delete records may be
+ * deleted.
+ */
+ public static final long DAYS_KEPT_MILLISECONDS = 1000L * 60L * 60L * 24L * (long)DAYS_KEPT;
+ }
+
+
protected interface RawContactsColumns {
/**
* A reference to the {@link ContactsContract.Contacts#_ID} that this
@@ -7909,6 +7966,13 @@
"android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED";
/**
+ * This is the intent that is fired when the contacts database is created. <p> The
+ * READ_CONTACT permission is required to receive these broadcasts.
+ */
+ public static final String CONTACTS_DATABASE_CREATED =
+ "android.provider.Contacts.DATABASE_CREATED";
+
+ /**
* Starts an Activity that lets the user pick a contact to attach an image to.
* After picking the contact it launches the image cropper in face detection mode.
*/
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 8055077..8308459 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -1368,7 +1368,7 @@
callbacks.onHardwarePreDraw(canvas);
if (displayList != null) {
- status = drawDisplayList(attachInfo, canvas, displayList, status);
+ status |= drawDisplayList(attachInfo, canvas, displayList, status);
} else {
// Shouldn't reach here
view.draw(canvas);
diff --git a/core/java/android/view/SimulatedDpad.java b/core/java/android/view/SimulatedDpad.java
deleted file mode 100644
index c889328..0000000
--- a/core/java/android/view/SimulatedDpad.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2012 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;
-
-import android.app.SearchManager;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.util.Log;
-
-/**
- * This class creates DPAD events from TouchNavigation events.
- *
- * @see ViewRootImpl
- */
-
-//TODO: Make this class an internal class of ViewRootImpl.java
-class SimulatedDpad {
-
- private static final String TAG = "SimulatedDpad";
-
- // Maximum difference in milliseconds between the down and up of a touch
- // event for it to be considered a tap
- // TODO:Read this value from a configuration file
- private static final int MAX_TAP_TIME = 250;
- // Where the cutoff is for determining an edge swipe
- private static final float EDGE_SWIPE_THRESHOLD = 0.9f;
- private static final int MSG_FLICK = 313;
- // TODO: Pass touch slop from the input device
- private static final int TOUCH_SLOP = 30;
- // The position of the previous TouchNavigation event
- private float mLastTouchNavigationXPosition;
- private float mLastTouchNavigationYPosition;
- // Where the Touch Navigation was initially pressed
- private float mTouchNavigationEnterXPosition;
- private float mTouchNavigationEnterYPosition;
- // When the most recent ACTION_HOVER_ENTER occurred
- private long mLastTouchNavigationStartTimeMs = 0;
- // When the most recent direction key was sent
- private long mLastTouchNavigationKeySendTimeMs = 0;
- // When the most recent touch event of any type occurred
- private long mLastTouchNavigationEventTimeMs = 0;
- // Did the swipe begin in a valid region
- private boolean mEdgeSwipePossible;
-
- private final Context mContext;
-
- // How quickly keys were sent;
- private int mKeySendRateMs = 0;
- private int mLastKeySent;
- // Last movement in device screen pixels
- private float mLastMoveX = 0;
- private float mLastMoveY = 0;
- // Offset from the initial touch. Gets reset as direction keys are sent.
- private float mAccumulatedX;
- private float mAccumulatedY;
-
- // Change in position allowed during tap events
- private float mTouchSlop;
- private float mTouchSlopSquared;
- // Has the TouchSlop constraint been invalidated
- private boolean mAlwaysInTapRegion = true;
-
- // Information from the most recent event.
- // Used to determine what device sent the event during a fling.
- private int mLastSource;
- private int mLastMetaState;
- private int mLastDeviceId;
-
- // TODO: Currently using screen dimensions tuned to a Galaxy Nexus, need to
- // read this from a config file instead
- private int mDistancePerTick;
- private int mDistancePerTickSquared;
- // Highest rate that the flinged events can occur at before dying out
- private int mMaxRepeatDelay;
- // The square of the minimum distance needed for a flick to register
- private int mMinFlickDistanceSquared;
- // How quickly the repeated events die off
- private float mFlickDecay;
-
- public SimulatedDpad(Context context) {
- mDistancePerTick = SystemProperties.getInt("persist.vr_dist_tick", 64);
- mDistancePerTickSquared = mDistancePerTick * mDistancePerTick;
- mMaxRepeatDelay = SystemProperties.getInt("persist.vr_repeat_delay", 300);
- mMinFlickDistanceSquared = SystemProperties.getInt("persist.vr_min_flick", 20);
- mMinFlickDistanceSquared *= mMinFlickDistanceSquared;
- mFlickDecay = Float.parseFloat(SystemProperties.get(
- "persist.sys.vr_flick_decay", "1.3"));
- mTouchSlop = TOUCH_SLOP;
- mTouchSlopSquared = mTouchSlop * mTouchSlop;
-
- mContext = context;
- }
-
- private final Handler mHandler = new Handler(true /*async*/) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_FLICK: {
- final long time = SystemClock.uptimeMillis();
- ViewRootImpl viewroot = (ViewRootImpl) msg.obj;
- // Send the key
- viewroot.enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, msg.arg2, 0, mLastMetaState,
- mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
- viewroot.enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, msg.arg2, 0, mLastMetaState,
- mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
-
- // Increase the delay by the decay factor and resend
- final int delay = (int) Math.ceil(mFlickDecay * msg.arg1);
- if (delay <= mMaxRepeatDelay) {
- Message msgCopy = Message.obtain(msg);
- msgCopy.arg1 = delay;
- msgCopy.setAsynchronous(true);
- mHandler.sendMessageDelayed(msgCopy, delay);
- }
- break;
- }
- }
- }
- };
-
- public void updateTouchNavigation(ViewRootImpl viewroot, MotionEvent event,
- boolean synthesizeNewKeys) {
- if (!synthesizeNewKeys) {
- mHandler.removeMessages(MSG_FLICK);
- }
- InputDevice device = event.getDevice();
- if (device == null) {
- return;
- }
- // Store what time the TouchNavigation event occurred
- final long time = SystemClock.uptimeMillis();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mLastTouchNavigationStartTimeMs = time;
- mAlwaysInTapRegion = true;
- mTouchNavigationEnterXPosition = event.getX();
- mTouchNavigationEnterYPosition = event.getY();
- mAccumulatedX = 0;
- mAccumulatedY = 0;
- mLastMoveX = 0;
- mLastMoveY = 0;
- if (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
- * EDGE_SWIPE_THRESHOLD < event.getY()) {
- // Did the swipe begin in a valid region
- mEdgeSwipePossible = true;
- }
- // Clear any flings
- if (synthesizeNewKeys) {
- mHandler.removeMessages(MSG_FLICK);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- // Determine whether the move is slop or an intentional move
- float deltaX = event.getX() - mTouchNavigationEnterXPosition;
- float deltaY = event.getY() - mTouchNavigationEnterYPosition;
- if (mTouchSlopSquared < deltaX * deltaX + deltaY * deltaY) {
- mAlwaysInTapRegion = false;
- }
- // Checks if the swipe has crossed the midpoint
- // and if our swipe gesture is complete
- if (event.getY() < (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
- * .5) && mEdgeSwipePossible) {
- mEdgeSwipePossible = false;
-
- Intent intent =
- ((SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE))
- .getAssistIntent(mContext, false, UserHandle.USER_CURRENT_OR_SELF);
- if (intent != null) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e){
- Log.e(TAG, "Could not start search activity");
- }
- } else {
- Log.e(TAG, "Could not find a search activity");
- }
- }
- // Find the difference in position between the two most recent
- // TouchNavigation events
- mLastMoveX = event.getX() - mLastTouchNavigationXPosition;
- mLastMoveY = event.getY() - mLastTouchNavigationYPosition;
- mAccumulatedX += mLastMoveX;
- mAccumulatedY += mLastMoveY;
- float mAccumulatedXSquared = mAccumulatedX * mAccumulatedX;
- float mAccumulatedYSquared = mAccumulatedY * mAccumulatedY;
- // Determine if we've moved far enough to send a key press
- if (mAccumulatedXSquared > mDistancePerTickSquared ||
- mAccumulatedYSquared > mDistancePerTickSquared) {
- float dominantAxis;
- float sign;
- boolean isXAxis;
- int key;
- int repeatCount = 0;
- // Determine dominant axis
- if (mAccumulatedXSquared > mAccumulatedYSquared) {
- dominantAxis = mAccumulatedX;
- isXAxis = true;
- } else {
- dominantAxis = mAccumulatedY;
- isXAxis = false;
- }
- // Determine sign of axis
- sign = (dominantAxis > 0) ? 1 : -1;
- // Determine key to send
- if (isXAxis) {
- key = (sign == 1) ? KeyEvent.KEYCODE_DPAD_RIGHT :
- KeyEvent.KEYCODE_DPAD_LEFT;
- } else {
- key = (sign == 1) ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
- }
- // Send key until maximum distance constraint is satisfied
- while (dominantAxis * dominantAxis > mDistancePerTickSquared) {
- repeatCount++;
- dominantAxis -= sign * mDistancePerTick;
- if (synthesizeNewKeys) {
- viewroot.enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, key, 0, event.getMetaState(),
- event.getDeviceId(), 0, KeyEvent.FLAG_FALLBACK,
- event.getSource()));
- viewroot.enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, key, 0, event.getMetaState(),
- event.getDeviceId(), 0, KeyEvent.FLAG_FALLBACK,
- event.getSource()));
- }
- }
- // Save new axis values
- mAccumulatedX = isXAxis ? dominantAxis : 0;
- mAccumulatedY = isXAxis ? 0 : dominantAxis;
-
- mLastKeySent = key;
- mKeySendRateMs = (int) (time - mLastTouchNavigationKeySendTimeMs) / repeatCount;
- mLastTouchNavigationKeySendTimeMs = time;
- }
- break;
- case MotionEvent.ACTION_UP:
- if (time - mLastTouchNavigationStartTimeMs < MAX_TAP_TIME && mAlwaysInTapRegion) {
- if (synthesizeNewKeys) {
- viewroot.enqueueInputEvent(new KeyEvent(mLastTouchNavigationStartTimeMs,
- time, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0,
- event.getMetaState(), event.getDeviceId(), 0,
- KeyEvent.FLAG_FALLBACK, event.getSource()));
- viewroot.enqueueInputEvent(new KeyEvent(mLastTouchNavigationStartTimeMs,
- time, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0,
- event.getMetaState(), event.getDeviceId(), 0,
- KeyEvent.FLAG_FALLBACK, event.getSource()));
- }
- } else {
- float xMoveSquared = mLastMoveX * mLastMoveX;
- float yMoveSquared = mLastMoveY * mLastMoveY;
- // Determine whether the last gesture was a fling.
- if (mMinFlickDistanceSquared <= xMoveSquared + yMoveSquared &&
- time - mLastTouchNavigationEventTimeMs <= MAX_TAP_TIME &&
- mKeySendRateMs <= mMaxRepeatDelay && mKeySendRateMs > 0) {
- mLastDeviceId = event.getDeviceId();
- mLastSource = event.getSource();
- mLastMetaState = event.getMetaState();
-
- if (synthesizeNewKeys) {
- Message message = Message.obtain(mHandler, MSG_FLICK,
- mKeySendRateMs, mLastKeySent, viewroot);
- message.setAsynchronous(true);
- mHandler.sendMessageDelayed(message, mKeySendRateMs);
- }
- }
- }
- mEdgeSwipePossible = false;
- break;
- }
-
- // Store touch event position and time
- mLastTouchNavigationEventTimeMs = time;
- mLastTouchNavigationXPosition = event.getX();
- mLastTouchNavigationYPosition = event.getY();
- }
-}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 22586f6..98b7877 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2923,8 +2923,6 @@
private final static int MSG_DISPATCH_DONE_ANIMATING = 22;
private final static int MSG_INVALIDATE_WORLD = 23;
private final static int MSG_WINDOW_MOVED = 24;
- private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 25;
- private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 26;
final class ViewRootHandler extends Handler {
@Override
@@ -2974,10 +2972,6 @@
return "MSG_DISPATCH_DONE_ANIMATING";
case MSG_WINDOW_MOVED:
return "MSG_WINDOW_MOVED";
- case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
- return "MSG_ENQUEUE_X_AXIS_KEY_REPEAT";
- case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT:
- return "MSG_ENQUEUE_Y_AXIS_KEY_REPEAT";
}
return super.getMessageName(message);
}
@@ -3200,18 +3194,6 @@
invalidateWorld(mView);
}
} break;
- case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
- case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: {
- KeyEvent oldEvent = (KeyEvent)msg.obj;
- KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent, SystemClock.uptimeMillis(),
- oldEvent.getRepeatCount() + 1);
- if (mAttachInfo.mHasWindowFocus) {
- enqueueInputEvent(e);
- Message m = obtainMessage(msg.what, e);
- m.setAsynchronous(true);
- sendMessageDelayed(m, mViewConfiguration.getKeyRepeatDelay());
- }
- } break;
}
}
}
@@ -3877,37 +3859,34 @@
}
/**
- * Performs default processing of unhandled input events.
+ * Performs synthesis of new input events from unhandled input events.
*/
final class SyntheticInputStage extends InputStage {
- private final TrackballAxis mTrackballAxisX = new TrackballAxis();
- private final TrackballAxis mTrackballAxisY = new TrackballAxis();
- private long mLastTrackballTime;
-
- private int mLastJoystickXDirection;
- private int mLastJoystickYDirection;
- private int mLastJoystickXKeyCode;
- private int mLastJoystickYKeyCode;
-
- private SimulatedDpad mSimulatedDpad;
+ private final SyntheticTrackballHandler mTrackball = new SyntheticTrackballHandler();
+ private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler();
+ private final SyntheticTouchNavigationHandler mTouchNavigation =
+ new SyntheticTouchNavigationHandler();
public SyntheticInputStage() {
super(null);
- mSimulatedDpad = new SimulatedDpad(mContext);
}
@Override
protected int onProcess(QueuedInputEvent q) {
q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED;
if (q.mEvent instanceof MotionEvent) {
- final int source = q.mEvent.getSource();
+ final MotionEvent event = (MotionEvent)q.mEvent;
+ final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- return processTrackballEvent(q);
+ mTrackball.process(event);
+ return FINISH_HANDLED;
} else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
- return processJoystickEvent(q);
+ mJoystick.process(event);
+ return FINISH_HANDLED;
} else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
== InputDevice.SOURCE_TOUCH_NAVIGATION) {
- return processTouchNavigationEvent(q);
+ mTouchNavigation.process(event);
+ return FINISH_HANDLED;
}
}
return FORWARD;
@@ -3918,49 +3897,55 @@
if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) {
// Cancel related synthetic events if any prior stage has handled the event.
if (q.mEvent instanceof MotionEvent) {
- final int source = q.mEvent.getSource();
+ final MotionEvent event = (MotionEvent)q.mEvent;
+ final int source = event.getSource();
if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- cancelTrackballEvent(q);
+ mTrackball.cancel(event);
} else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
- cancelJoystickEvent(q);
+ mJoystick.cancel(event);
} else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
== InputDevice.SOURCE_TOUCH_NAVIGATION) {
- cancelTouchNavigationEvent(q);
+ mTouchNavigation.cancel(event);
}
}
}
super.onDeliverToNext(q);
}
+ }
- private int processTrackballEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
+ /**
+ * Creates dpad events from unhandled trackball movements.
+ */
+ final class SyntheticTrackballHandler {
+ private final TrackballAxis mX = new TrackballAxis();
+ private final TrackballAxis mY = new TrackballAxis();
+ private long mLastTime;
+ public void process(MotionEvent event) {
// Translate the trackball event into DPAD keys and try to deliver those.
- final TrackballAxis x = mTrackballAxisX;
- final TrackballAxis y = mTrackballAxisY;
long curTime = SystemClock.uptimeMillis();
- if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) {
+ if ((mLastTime + MAX_TRACKBALL_DELAY) < curTime) {
// It has been too long since the last movement,
// so restart at the beginning.
- x.reset(0);
- y.reset(0);
- mLastTrackballTime = curTime;
+ mX.reset(0);
+ mY.reset(0);
+ mLastTime = curTime;
}
final int action = event.getAction();
final int metaState = event.getMetaState();
switch (action) {
case MotionEvent.ACTION_DOWN:
- x.reset(2);
- y.reset(2);
+ mX.reset(2);
+ mY.reset(2);
enqueueInputEvent(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
InputDevice.SOURCE_KEYBOARD));
break;
case MotionEvent.ACTION_UP:
- x.reset(2);
- y.reset(2);
+ mX.reset(2);
+ mY.reset(2);
enqueueInputEvent(new KeyEvent(curTime, curTime,
KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
@@ -3968,14 +3953,14 @@
break;
}
- if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
- + x.step + " dir=" + x.dir + " acc=" + x.acceleration
+ if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + mX.position + " step="
+ + mX.step + " dir=" + mX.dir + " acc=" + mX.acceleration
+ " move=" + event.getX()
- + " / Y=" + y.position + " step="
- + y.step + " dir=" + y.dir + " acc=" + y.acceleration
+ + " / Y=" + mY.position + " step="
+ + mY.step + " dir=" + mY.dir + " acc=" + mY.acceleration
+ " move=" + event.getY());
- final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
- final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
+ final float xOff = mX.collect(event.getX(), event.getEventTime(), "X");
+ final float yOff = mY.collect(event.getY(), event.getEventTime(), "Y");
// Generate DPAD events based on the trackball movement.
// We pick the axis that has moved the most as the direction of
@@ -3987,20 +3972,20 @@
int movement = 0;
float accel = 1;
if (xOff > yOff) {
- movement = x.generate();
+ movement = mX.generate();
if (movement != 0) {
keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
: KeyEvent.KEYCODE_DPAD_LEFT;
- accel = x.acceleration;
- y.reset(2);
+ accel = mX.acceleration;
+ mY.reset(2);
}
} else if (yOff > 0) {
- movement = y.generate();
+ movement = mY.generate();
if (movement != 0) {
keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
: KeyEvent.KEYCODE_DPAD_UP;
- accel = y.acceleration;
- x.reset(2);
+ accel = mY.acceleration;
+ mX.reset(2);
}
}
@@ -4034,16 +4019,12 @@
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
InputDevice.SOURCE_KEYBOARD));
}
- mLastTrackballTime = curTime;
+ mLastTime = curTime;
}
-
- // Unfortunately we can't tell whether the application consumed the keys, so
- // we always consider the trackball event handled.
- return FINISH_HANDLED;
}
- private void cancelTrackballEvent(QueuedInputEvent q) {
- mLastTrackballTime = Integer.MIN_VALUE;
+ public void cancel(MotionEvent event) {
+ mLastTime = Integer.MIN_VALUE;
// If we reach this, we consumed a trackball event.
// Because we will not translate the trackball event into a key event,
@@ -4052,19 +4033,220 @@
ensureTouchMode(false);
}
}
+ }
- private int processJoystickEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- updateJoystickDirection(event, true);
- return FINISH_HANDLED;
+ /**
+ * Maintains state information for a single trackball axis, generating
+ * discrete (DPAD) movements based on raw trackball motion.
+ */
+ static final class TrackballAxis {
+ /**
+ * The maximum amount of acceleration we will apply.
+ */
+ static final float MAX_ACCELERATION = 20;
+
+ /**
+ * The maximum amount of time (in milliseconds) between events in order
+ * for us to consider the user to be doing fast trackball movements,
+ * and thus apply an acceleration.
+ */
+ static final long FAST_MOVE_TIME = 150;
+
+ /**
+ * Scaling factor to the time (in milliseconds) between events to how
+ * much to multiple/divide the current acceleration. When movement
+ * is < FAST_MOVE_TIME this multiplies the acceleration; when >
+ * FAST_MOVE_TIME it divides it.
+ */
+ static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
+
+ static final float FIRST_MOVEMENT_THRESHOLD = 0.5f;
+ static final float SECOND_CUMULATIVE_MOVEMENT_THRESHOLD = 2.0f;
+ static final float SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD = 1.0f;
+
+ float position;
+ float acceleration = 1;
+ long lastMoveTime = 0;
+ int step;
+ int dir;
+ int nonAccelMovement;
+
+ void reset(int _step) {
+ position = 0;
+ acceleration = 1;
+ lastMoveTime = 0;
+ step = _step;
+ dir = 0;
}
- private void cancelJoystickEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- updateJoystickDirection(event, false);
+ /**
+ * Add trackball movement into the state. If the direction of movement
+ * has been reversed, the state is reset before adding the
+ * movement (so that you don't have to compensate for any previously
+ * collected movement before see the result of the movement in the
+ * new direction).
+ *
+ * @return Returns the absolute value of the amount of movement
+ * collected so far.
+ */
+ float collect(float off, long time, String axis) {
+ long normTime;
+ if (off > 0) {
+ normTime = (long)(off * FAST_MOVE_TIME);
+ if (dir < 0) {
+ if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!");
+ position = 0;
+ step = 0;
+ acceleration = 1;
+ lastMoveTime = 0;
+ }
+ dir = 1;
+ } else if (off < 0) {
+ normTime = (long)((-off) * FAST_MOVE_TIME);
+ if (dir > 0) {
+ if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!");
+ position = 0;
+ step = 0;
+ acceleration = 1;
+ lastMoveTime = 0;
+ }
+ dir = -1;
+ } else {
+ normTime = 0;
+ }
+
+ // The number of milliseconds between each movement that is
+ // considered "normal" and will not result in any acceleration
+ // or deceleration, scaled by the offset we have here.
+ if (normTime > 0) {
+ long delta = time - lastMoveTime;
+ lastMoveTime = time;
+ float acc = acceleration;
+ if (delta < normTime) {
+ // The user is scrolling rapidly, so increase acceleration.
+ float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR;
+ if (scale > 1) acc *= scale;
+ if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off="
+ + off + " normTime=" + normTime + " delta=" + delta
+ + " scale=" + scale + " acc=" + acc);
+ acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION;
+ } else {
+ // The user is scrolling slowly, so decrease acceleration.
+ float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR;
+ if (scale > 1) acc /= scale;
+ if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off="
+ + off + " normTime=" + normTime + " delta=" + delta
+ + " scale=" + scale + " acc=" + acc);
+ acceleration = acc > 1 ? acc : 1;
+ }
+ }
+ position += off;
+ return Math.abs(position);
}
- private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
+ /**
+ * Generate the number of discrete movement events appropriate for
+ * the currently collected trackball movement.
+ *
+ * @return Returns the number of discrete movements, either positive
+ * or negative, or 0 if there is not enough trackball movement yet
+ * for a discrete movement.
+ */
+ int generate() {
+ int movement = 0;
+ nonAccelMovement = 0;
+ do {
+ final int dir = position >= 0 ? 1 : -1;
+ switch (step) {
+ // If we are going to execute the first step, then we want
+ // to do this as soon as possible instead of waiting for
+ // a full movement, in order to make things look responsive.
+ case 0:
+ if (Math.abs(position) < FIRST_MOVEMENT_THRESHOLD) {
+ return movement;
+ }
+ movement += dir;
+ nonAccelMovement += dir;
+ step = 1;
+ break;
+ // If we have generated the first movement, then we need
+ // to wait for the second complete trackball motion before
+ // generating the second discrete movement.
+ case 1:
+ if (Math.abs(position) < SECOND_CUMULATIVE_MOVEMENT_THRESHOLD) {
+ return movement;
+ }
+ movement += dir;
+ nonAccelMovement += dir;
+ position -= SECOND_CUMULATIVE_MOVEMENT_THRESHOLD * dir;
+ step = 2;
+ break;
+ // After the first two, we generate discrete movements
+ // consistently with the trackball, applying an acceleration
+ // if the trackball is moving quickly. This is a simple
+ // acceleration on top of what we already compute based
+ // on how quickly the wheel is being turned, to apply
+ // a longer increasing acceleration to continuous movement
+ // in one direction.
+ default:
+ if (Math.abs(position) < SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD) {
+ return movement;
+ }
+ movement += dir;
+ position -= dir * SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD;
+ float acc = acceleration;
+ acc *= 1.1f;
+ acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
+ break;
+ }
+ } while (true);
+ }
+ }
+
+ /**
+ * Creates dpad events from unhandled joystick movements.
+ */
+ final class SyntheticJoystickHandler extends Handler {
+ private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 1;
+ private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 2;
+
+ private int mLastXDirection;
+ private int mLastYDirection;
+ private int mLastXKeyCode;
+ private int mLastYKeyCode;
+
+ public SyntheticJoystickHandler() {
+ super(true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
+ case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: {
+ KeyEvent oldEvent = (KeyEvent)msg.obj;
+ KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent,
+ SystemClock.uptimeMillis(),
+ oldEvent.getRepeatCount() + 1);
+ if (mAttachInfo.mHasWindowFocus) {
+ enqueueInputEvent(e);
+ Message m = obtainMessage(msg.what, e);
+ m.setAsynchronous(true);
+ sendMessageDelayed(m, ViewConfiguration.getKeyRepeatDelay());
+ }
+ } break;
+ }
+ }
+
+ public void process(MotionEvent event) {
+ update(event, true);
+ }
+
+ public void cancel(MotionEvent event) {
+ update(event, false);
+ }
+
+ private void update(MotionEvent event, boolean synthesizeNewKeys) {
final long time = event.getEventTime();
final int metaState = event.getMetaState();
final int deviceId = event.getDeviceId();
@@ -4082,51 +4264,51 @@
yDirection = joystickAxisValueToDirection(event.getY());
}
- if (xDirection != mLastJoystickXDirection) {
- if (mLastJoystickXKeyCode != 0) {
- mHandler.removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
+ if (xDirection != mLastXDirection) {
+ if (mLastXKeyCode != 0) {
+ removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
+ KeyEvent.ACTION_UP, mLastXKeyCode, 0, metaState,
deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
- mLastJoystickXKeyCode = 0;
+ mLastXKeyCode = 0;
}
- mLastJoystickXDirection = xDirection;
+ mLastXDirection = xDirection;
if (xDirection != 0 && synthesizeNewKeys) {
- mLastJoystickXKeyCode = xDirection > 0
+ mLastXKeyCode = xDirection > 0
? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
final KeyEvent e = new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
+ KeyEvent.ACTION_DOWN, mLastXKeyCode, 0, metaState,
deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
enqueueInputEvent(e);
- Message m = mHandler.obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
+ Message m = obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
m.setAsynchronous(true);
mHandler.sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
}
}
- if (yDirection != mLastJoystickYDirection) {
- if (mLastJoystickYKeyCode != 0) {
- mHandler.removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
+ if (yDirection != mLastYDirection) {
+ if (mLastYKeyCode != 0) {
+ removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
+ KeyEvent.ACTION_UP, mLastYKeyCode, 0, metaState,
deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
- mLastJoystickYKeyCode = 0;
+ mLastYKeyCode = 0;
}
- mLastJoystickYDirection = yDirection;
+ mLastYDirection = yDirection;
if (yDirection != 0 && synthesizeNewKeys) {
- mLastJoystickYKeyCode = yDirection > 0
+ mLastYKeyCode = yDirection > 0
? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
final KeyEvent e = new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
+ KeyEvent.ACTION_DOWN, mLastYKeyCode, 0, metaState,
deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
enqueueInputEvent(e);
- Message m = mHandler.obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
+ Message m = obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
m.setAsynchronous(true);
- mHandler.sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
+ sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
}
}
}
@@ -4140,17 +4322,419 @@
return 0;
}
}
+ }
- private int processTouchNavigationEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- mSimulatedDpad.updateTouchNavigation(ViewRootImpl.this, event, true);
- return FINISH_HANDLED;
+ /**
+ * Creates dpad events from unhandled touch navigation movements.
+ */
+ final class SyntheticTouchNavigationHandler extends Handler {
+ private static final String LOCAL_TAG = "SyntheticTouchNavigationHandler";
+ private static final boolean LOCAL_DEBUG = false;
+
+ // Assumed nominal width and height in millimeters of a touch navigation pad,
+ // if no resolution information is available from the input system.
+ private static final float DEFAULT_WIDTH_MILLIMETERS = 48;
+ private static final float DEFAULT_HEIGHT_MILLIMETERS = 48;
+
+ /* TODO: These constants should eventually be moved to ViewConfiguration. */
+
+ // Tap timeout in milliseconds.
+ private static final int TAP_TIMEOUT = 250;
+
+ // The maximum distance traveled for a gesture to be considered a tap in millimeters.
+ private static final int TAP_SLOP_MILLIMETERS = 5;
+
+ // The nominal distance traveled to move by one unit.
+ private static final int TICK_DISTANCE_MILLIMETERS = 12;
+
+ // Minimum and maximum fling velocity in ticks per second.
+ // The minimum velocity should be set such that we perform enough ticks per
+ // second that the fling appears to be fluid. For example, if we set the minimum
+ // to 2 ticks per second, then there may be up to half a second delay between the next
+ // to last and last ticks which is noticeably discrete and jerky. This value should
+ // probably not be set to anything less than about 4.
+ // If fling accuracy is a problem then consider tuning the tick distance instead.
+ private static final float MIN_FLING_VELOCITY_TICKS_PER_SECOND = 6f;
+ private static final float MAX_FLING_VELOCITY_TICKS_PER_SECOND = 20f;
+
+ // Fling velocity decay factor applied after each new key is emitted.
+ // This parameter controls the deceleration and overall duration of the fling.
+ // The fling stops automatically when its velocity drops below the minimum
+ // fling velocity defined above.
+ private static final float FLING_TICK_DECAY = 0.8f;
+
+ /* The input device that we are tracking. */
+
+ private int mCurrentDeviceId = -1;
+ private int mCurrentSource;
+ private boolean mCurrentDeviceSupported;
+
+ /* Configuration for the current input device. */
+
+ // The tap timeout and scaled slop.
+ private int mConfigTapTimeout;
+ private float mConfigTapSlop;
+
+ // The scaled tick distance. A movement of this amount should generally translate
+ // into a single dpad event in a given direction.
+ private float mConfigTickDistance;
+
+ // The minimum and maximum scaled fling velocity.
+ private float mConfigMinFlingVelocity;
+ private float mConfigMaxFlingVelocity;
+
+ /* Tracking state. */
+
+ // The velocity tracker for detecting flings.
+ private VelocityTracker mVelocityTracker;
+
+ // The active pointer id, or -1 if none.
+ private int mActivePointerId = -1;
+
+ // Time and location where tracking started.
+ private long mStartTime;
+ private float mStartX;
+ private float mStartY;
+
+ // Most recently observed position.
+ private float mLastX;
+ private float mLastY;
+
+ // Accumulated movement delta since the last direction key was sent.
+ private float mAccumulatedX;
+ private float mAccumulatedY;
+
+ // Set to true if any movement was delivered to the app.
+ // Implies that tap slop was exceeded.
+ private boolean mConsumedMovement;
+
+ // The most recently sent key down event.
+ // The keycode remains set until the direction changes or a fling ends
+ // so that repeated key events may be generated as required.
+ private long mPendingKeyDownTime;
+ private int mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN;
+ private int mPendingKeyRepeatCount;
+ private int mPendingKeyMetaState;
+
+ // The current fling velocity while a fling is in progress.
+ private boolean mFlinging;
+ private float mFlingVelocity;
+
+ public SyntheticTouchNavigationHandler() {
+ super(true);
}
- private void cancelTouchNavigationEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- mSimulatedDpad.updateTouchNavigation(ViewRootImpl.this, event, false);
+ public void process(MotionEvent event) {
+ // Update the current device information.
+ final long time = event.getEventTime();
+ final int deviceId = event.getDeviceId();
+ final int source = event.getSource();
+ if (mCurrentDeviceId != deviceId || mCurrentSource != source) {
+ finishKeys(time);
+ finishTracking(time);
+ mCurrentDeviceId = deviceId;
+ mCurrentSource = source;
+ mCurrentDeviceSupported = false;
+ InputDevice device = event.getDevice();
+ if (device != null) {
+ // In order to support an input device, we must know certain
+ // characteristics about it, such as its size and resolution.
+ InputDevice.MotionRange xRange = device.getMotionRange(MotionEvent.AXIS_X);
+ InputDevice.MotionRange yRange = device.getMotionRange(MotionEvent.AXIS_Y);
+ if (xRange != null && yRange != null) {
+ mCurrentDeviceSupported = true;
+
+ // Infer the resolution if it not actually known.
+ float xRes = xRange.getResolution();
+ if (xRes <= 0) {
+ xRes = xRange.getRange() / DEFAULT_WIDTH_MILLIMETERS;
+ }
+ float yRes = yRange.getResolution();
+ if (yRes <= 0) {
+ yRes = yRange.getRange() / DEFAULT_HEIGHT_MILLIMETERS;
+ }
+ float nominalRes = (xRes + yRes) * 0.5f;
+
+ // Precompute all of the configuration thresholds we will need.
+ mConfigTapTimeout = TAP_TIMEOUT;
+ mConfigTapSlop = TAP_SLOP_MILLIMETERS * nominalRes;
+ mConfigTickDistance = TICK_DISTANCE_MILLIMETERS * nominalRes;
+ mConfigMinFlingVelocity =
+ MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
+ mConfigMaxFlingVelocity =
+ MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
+
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId
+ + " (" + Integer.toHexString(mCurrentSource) + "): "
+ + "mConfigTapTimeout=" + mConfigTapTimeout
+ + ", mConfigTapSlop=" + mConfigTapSlop
+ + ", mConfigTickDistance=" + mConfigTickDistance
+ + ", mConfigMinFlingVelocity=" + mConfigMinFlingVelocity
+ + ", mConfigMaxFlingVelocity=" + mConfigMaxFlingVelocity);
+ }
+ }
+ }
+ }
+ if (!mCurrentDeviceSupported) {
+ return;
+ }
+
+ // Handle the event.
+ final int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ boolean caughtFling = mFlinging;
+ finishKeys(time);
+ finishTracking(time);
+ mActivePointerId = event.getPointerId(0);
+ mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker.addMovement(event);
+ mStartTime = time;
+ mStartX = event.getX();
+ mStartY = event.getY();
+ mLastX = mStartX;
+ mLastY = mStartY;
+ mAccumulatedX = 0;
+ mAccumulatedY = 0;
+
+ // If we caught a fling, then pretend that the tap slop has already
+ // been exceeded to suppress taps whose only purpose is to stop the fling.
+ mConsumedMovement = caughtFling;
+ break;
+ }
+
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_UP: {
+ if (mActivePointerId < 0) {
+ break;
+ }
+ final int index = event.findPointerIndex(mActivePointerId);
+ if (index < 0) {
+ finishKeys(time);
+ finishTracking(time);
+ break;
+ }
+
+ mVelocityTracker.addMovement(event);
+ final float x = event.getX(index);
+ final float y = event.getY(index);
+ mAccumulatedX += x - mLastX;
+ mAccumulatedY += y - mLastY;
+ mLastX = x;
+ mLastY = y;
+
+ // Consume any accumulated movement so far.
+ final int metaState = event.getMetaState();
+ consumeAccumulatedMovement(time, metaState);
+
+ // Detect taps and flings.
+ if (action == MotionEvent.ACTION_UP) {
+ if (!mConsumedMovement
+ && Math.hypot(mLastX - mStartX, mLastY - mStartY) < mConfigTapSlop
+ && time <= mStartTime + mConfigTapTimeout) {
+ // It's a tap!
+ finishKeys(time);
+ sendKeyDownOrRepeat(time, KeyEvent.KEYCODE_DPAD_CENTER, metaState);
+ sendKeyUp(time);
+ } else if (mConsumedMovement
+ && mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
+ // It might be a fling.
+ mVelocityTracker.computeCurrentVelocity(1000, mConfigMaxFlingVelocity);
+ final float vx = mVelocityTracker.getXVelocity(mActivePointerId);
+ final float vy = mVelocityTracker.getYVelocity(mActivePointerId);
+ if (!startFling(time, vx, vy)) {
+ finishKeys(time);
+ }
+ }
+ finishTracking(time);
+ }
+ break;
+ }
+
+ case MotionEvent.ACTION_CANCEL: {
+ finishKeys(time);
+ finishTracking(time);
+ break;
+ }
+ }
}
+
+ public void cancel(MotionEvent event) {
+ if (mCurrentDeviceId == event.getDeviceId()
+ && mCurrentSource == event.getSource()) {
+ final long time = event.getEventTime();
+ finishKeys(time);
+ finishTracking(time);
+ }
+ }
+
+ private void finishKeys(long time) {
+ cancelFling();
+ sendKeyUp(time);
+ }
+
+ private void finishTracking(long time) {
+ if (mActivePointerId >= 0) {
+ mActivePointerId = -1;
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+
+ private void consumeAccumulatedMovement(long time, int metaState) {
+ final float absX = Math.abs(mAccumulatedX);
+ final float absY = Math.abs(mAccumulatedY);
+ if (absX >= absY) {
+ if (absX >= mConfigTickDistance) {
+ mAccumulatedX = consumeAccumulatedMovement(time, metaState, mAccumulatedX,
+ KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT);
+ mAccumulatedY = 0;
+ mConsumedMovement = true;
+ }
+ } else {
+ if (absY >= mConfigTickDistance) {
+ mAccumulatedY = consumeAccumulatedMovement(time, metaState, mAccumulatedY,
+ KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN);
+ mAccumulatedX = 0;
+ mConsumedMovement = true;
+ }
+ }
+ }
+
+ private float consumeAccumulatedMovement(long time, int metaState,
+ float accumulator, int negativeKeyCode, int positiveKeyCode) {
+ while (accumulator <= -mConfigTickDistance) {
+ sendKeyDownOrRepeat(time, negativeKeyCode, metaState);
+ accumulator += mConfigTickDistance;
+ }
+ while (accumulator >= mConfigTickDistance) {
+ sendKeyDownOrRepeat(time, positiveKeyCode, metaState);
+ accumulator -= mConfigTickDistance;
+ }
+ return accumulator;
+ }
+
+ private void sendKeyDownOrRepeat(long time, int keyCode, int metaState) {
+ if (mPendingKeyCode != keyCode) {
+ sendKeyUp(time);
+ mPendingKeyDownTime = time;
+ mPendingKeyCode = keyCode;
+ mPendingKeyRepeatCount = 0;
+ } else {
+ mPendingKeyRepeatCount += 1;
+ }
+ mPendingKeyMetaState = metaState;
+
+ // Note: Normally we would pass FLAG_LONG_PRESS when the repeat count is 1
+ // but it doesn't quite make sense when simulating the events in this way.
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Sending key down: keyCode=" + mPendingKeyCode
+ + ", repeatCount=" + mPendingKeyRepeatCount
+ + ", metaState=" + Integer.toHexString(mPendingKeyMetaState));
+ }
+ enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time,
+ KeyEvent.ACTION_DOWN, mPendingKeyCode, mPendingKeyRepeatCount,
+ mPendingKeyMetaState, mCurrentDeviceId,
+ KeyEvent.FLAG_FALLBACK, mCurrentSource));
+ }
+
+ private void sendKeyUp(long time) {
+ if (mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Sending key up: keyCode=" + mPendingKeyCode
+ + ", metaState=" + Integer.toHexString(mPendingKeyMetaState));
+ }
+ enqueueInputEvent(new KeyEvent(mPendingKeyDownTime, time,
+ KeyEvent.ACTION_UP, mPendingKeyCode, 0, mPendingKeyMetaState,
+ mCurrentDeviceId, 0, KeyEvent.FLAG_FALLBACK,
+ mCurrentSource));
+ mPendingKeyCode = KeyEvent.KEYCODE_UNKNOWN;
+ }
+ }
+
+ private boolean startFling(long time, float vx, float vy) {
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Considering fling: vx=" + vx + ", vy=" + vy
+ + ", min=" + mConfigMinFlingVelocity);
+ }
+
+ // Flings must be oriented in the same direction as the preceding movements.
+ switch (mPendingKeyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (-vx >= mConfigMinFlingVelocity
+ && Math.abs(vy) < mConfigMinFlingVelocity) {
+ mFlingVelocity = -vx;
+ break;
+ }
+ return false;
+
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (vx >= mConfigMinFlingVelocity
+ && Math.abs(vy) < mConfigMinFlingVelocity) {
+ mFlingVelocity = vx;
+ break;
+ }
+ return false;
+
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (-vy >= mConfigMinFlingVelocity
+ && Math.abs(vx) < mConfigMinFlingVelocity) {
+ mFlingVelocity = -vy;
+ break;
+ }
+ return false;
+
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (vy >= mConfigMinFlingVelocity
+ && Math.abs(vx) < mConfigMinFlingVelocity) {
+ mFlingVelocity = vy;
+ break;
+ }
+ return false;
+ }
+
+ // Post the first fling event.
+ mFlinging = postFling(time);
+ return mFlinging;
+ }
+
+ private boolean postFling(long time) {
+ // The idea here is to estimate the time when the pointer would have
+ // traveled one tick distance unit given the current fling velocity.
+ // This effect creates continuity of motion.
+ if (mFlingVelocity >= mConfigMinFlingVelocity) {
+ long delay = (long)(mConfigTickDistance / mFlingVelocity * 1000);
+ postAtTime(mFlingRunnable, time + delay);
+ if (LOCAL_DEBUG) {
+ Log.d(LOCAL_TAG, "Posted fling: velocity="
+ + mFlingVelocity + ", delay=" + delay
+ + ", keyCode=" + mPendingKeyCode);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void cancelFling() {
+ if (mFlinging) {
+ removeCallbacks(mFlingRunnable);
+ mFlinging = false;
+ }
+ }
+
+ private final Runnable mFlingRunnable = new Runnable() {
+ @Override
+ public void run() {
+ final long time = SystemClock.uptimeMillis();
+ sendKeyDownOrRepeat(time, mPendingKeyCode, mPendingKeyMetaState);
+ mFlingVelocity *= FLING_TICK_DECAY;
+ if (!postFling(time)) {
+ mFlinging = false;
+ finishKeys(time);
+ }
+ }
+ };
}
/**
@@ -5487,174 +6071,6 @@
}
}
- /**
- * Maintains state information for a single trackball axis, generating
- * discrete (DPAD) movements based on raw trackball motion.
- */
- static final class TrackballAxis {
- /**
- * The maximum amount of acceleration we will apply.
- */
- static final float MAX_ACCELERATION = 20;
-
- /**
- * The maximum amount of time (in milliseconds) between events in order
- * for us to consider the user to be doing fast trackball movements,
- * and thus apply an acceleration.
- */
- static final long FAST_MOVE_TIME = 150;
-
- /**
- * Scaling factor to the time (in milliseconds) between events to how
- * much to multiple/divide the current acceleration. When movement
- * is < FAST_MOVE_TIME this multiplies the acceleration; when >
- * FAST_MOVE_TIME it divides it.
- */
- static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
-
- static final float FIRST_MOVEMENT_THRESHOLD = 0.5f;
- static final float SECOND_CUMULATIVE_MOVEMENT_THRESHOLD = 2.0f;
- static final float SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD = 1.0f;
-
- float position;
- float acceleration = 1;
- long lastMoveTime = 0;
- int step;
- int dir;
- int nonAccelMovement;
-
- void reset(int _step) {
- position = 0;
- acceleration = 1;
- lastMoveTime = 0;
- step = _step;
- dir = 0;
- }
-
- /**
- * Add trackball movement into the state. If the direction of movement
- * has been reversed, the state is reset before adding the
- * movement (so that you don't have to compensate for any previously
- * collected movement before see the result of the movement in the
- * new direction).
- *
- * @return Returns the absolute value of the amount of movement
- * collected so far.
- */
- float collect(float off, long time, String axis) {
- long normTime;
- if (off > 0) {
- normTime = (long)(off * FAST_MOVE_TIME);
- if (dir < 0) {
- if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!");
- position = 0;
- step = 0;
- acceleration = 1;
- lastMoveTime = 0;
- }
- dir = 1;
- } else if (off < 0) {
- normTime = (long)((-off) * FAST_MOVE_TIME);
- if (dir > 0) {
- if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!");
- position = 0;
- step = 0;
- acceleration = 1;
- lastMoveTime = 0;
- }
- dir = -1;
- } else {
- normTime = 0;
- }
-
- // The number of milliseconds between each movement that is
- // considered "normal" and will not result in any acceleration
- // or deceleration, scaled by the offset we have here.
- if (normTime > 0) {
- long delta = time - lastMoveTime;
- lastMoveTime = time;
- float acc = acceleration;
- if (delta < normTime) {
- // The user is scrolling rapidly, so increase acceleration.
- float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR;
- if (scale > 1) acc *= scale;
- if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off="
- + off + " normTime=" + normTime + " delta=" + delta
- + " scale=" + scale + " acc=" + acc);
- acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION;
- } else {
- // The user is scrolling slowly, so decrease acceleration.
- float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR;
- if (scale > 1) acc /= scale;
- if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off="
- + off + " normTime=" + normTime + " delta=" + delta
- + " scale=" + scale + " acc=" + acc);
- acceleration = acc > 1 ? acc : 1;
- }
- }
- position += off;
- return Math.abs(position);
- }
-
- /**
- * Generate the number of discrete movement events appropriate for
- * the currently collected trackball movement.
- *
- * @return Returns the number of discrete movements, either positive
- * or negative, or 0 if there is not enough trackball movement yet
- * for a discrete movement.
- */
- int generate() {
- int movement = 0;
- nonAccelMovement = 0;
- do {
- final int dir = position >= 0 ? 1 : -1;
- switch (step) {
- // If we are going to execute the first step, then we want
- // to do this as soon as possible instead of waiting for
- // a full movement, in order to make things look responsive.
- case 0:
- if (Math.abs(position) < FIRST_MOVEMENT_THRESHOLD) {
- return movement;
- }
- movement += dir;
- nonAccelMovement += dir;
- step = 1;
- break;
- // If we have generated the first movement, then we need
- // to wait for the second complete trackball motion before
- // generating the second discrete movement.
- case 1:
- if (Math.abs(position) < SECOND_CUMULATIVE_MOVEMENT_THRESHOLD) {
- return movement;
- }
- movement += dir;
- nonAccelMovement += dir;
- position -= SECOND_CUMULATIVE_MOVEMENT_THRESHOLD * dir;
- step = 2;
- break;
- // After the first two, we generate discrete movements
- // consistently with the trackball, applying an acceleration
- // if the trackball is moving quickly. This is a simple
- // acceleration on top of what we already compute based
- // on how quickly the wheel is being turned, to apply
- // a longer increasing acceleration to continuous movement
- // in one direction.
- default:
- if (Math.abs(position) < SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD) {
- return movement;
- }
- movement += dir;
- position -= dir * SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD;
- float acc = acceleration;
- acc *= 1.1f;
- acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
- break;
- }
- } while (true);
- }
- }
-
public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
public CalledFromWrongThreadException(String msg) {
super(msg);
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index d901d0a..8ae0021 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1004,6 +1004,7 @@
* @param flag true if plugins should be enabled
* @deprecated This method has been deprecated in favor of
* {@link #setPluginState}
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
public synchronized void setPluginsEnabled(boolean flag) {
@@ -1032,6 +1033,7 @@
* @param pluginsPath a String path to the directory containing plugins
* @deprecated This method is no longer used as plugins are loaded from
* their own APK via the system's package manager.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
public synchronized void setPluginsPath(String pluginsPath) {
@@ -1224,6 +1226,7 @@
* @return true if plugins are enabled
* @see #setPluginsEnabled
* @deprecated This method has been replaced by {@link #getPluginState}
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
public synchronized boolean getPluginsEnabled() {
@@ -1249,6 +1252,7 @@
* @return an empty string
* @deprecated This method is no longer used as plugins are loaded from
* their own APK via the system's package manager.
+ * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}
*/
@Deprecated
public synchronized String getPluginsPath() {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3fa0940..94dadb4 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2606,7 +2606,7 @@
mGlobalLayoutListenerAddedFilter = false;
}
- if (mAdapter != null) {
+ if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
mDataSetObserver = null;
}
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index b620568..04931e7 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -179,6 +179,9 @@
animateToTab(position);
}
}
+ if (mTabSpinner != null && position >= 0) {
+ mTabSpinner.setSelection(position);
+ }
}
public void setContentHeight(int contentHeight) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 66cea9d7..3e5586e 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -179,6 +179,7 @@
libandroidfw \
libexpat \
libnativehelper \
+ liblog \
libcutils \
libutils \
libbinder \
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 9a9f6c8..11c7053 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -77,7 +77,14 @@
}
static SkCanvas* initRaster(JNIEnv* env, jobject, SkBitmap* bitmap) {
- return bitmap ? new SkCanvas(*bitmap) : new SkCanvas;
+ if (bitmap) {
+ return new SkCanvas(*bitmap);
+ } else {
+ // Create an empty bitmap device to prevent callers from crashing
+ // if they attempt to draw into this canvas.
+ SkBitmap emptyBitmap;
+ return new SkCanvas(emptyBitmap);
+ }
}
static void copyCanvasState(JNIEnv* env, jobject clazz,
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index f028c86d..00ecd0a 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -18,6 +18,9 @@
#include <JNIHelp.h>
#include <ScopedUtfChars.h>
+#include <ScopedStringChars.h>
+
+#include <utils/String8.h>
#include <cutils/trace.h>
#include <cutils/log.h>
@@ -36,8 +39,20 @@
static void android_os_Trace_nativeTraceBegin(JNIEnv* env, jclass clazz,
jlong tag, jstring nameStr) {
- ScopedUtfChars name(env, nameStr);
- atrace_begin(tag, name.c_str());
+ const size_t MAX_SECTION_NAME_LEN = 127;
+ ScopedStringChars jchars(env, nameStr);
+ String8 utf8Chars(reinterpret_cast<const char16_t*>(jchars.get()),
+ jchars.size());
+ size_t size = utf8Chars.size();
+ char* str = utf8Chars.lockBuffer(size);
+ for (size_t i = 0; i < size; i++) {
+ char c = str[i];
+ if (c == '\0' || c == '\n' || c == '|') {
+ str[i] = ' ';
+ }
+ }
+ utf8Chars.unlockBuffer();
+ atrace_begin(tag, utf8Chars.string());
}
static void android_os_Trace_nativeTraceEnd(JNIEnv* env, jclass clazz,
@@ -45,6 +60,11 @@
atrace_end(tag);
}
+static void android_os_Trace_nativeSetAppTracingAllowed(JNIEnv* env,
+ jclass clazz, jboolean allowed) {
+ atrace_set_debuggable(allowed);
+}
+
static JNINativeMethod gTraceMethods[] = {
/* name, signature, funcPtr */
{ "nativeGetEnabledTags",
@@ -59,6 +79,9 @@
{ "nativeTraceEnd",
"(J)V",
(void*)android_os_Trace_nativeTraceEnd },
+ { "nativeSetAppTracingAllowed",
+ "(Z)V",
+ (void*)android_os_Trace_nativeSetAppTracingAllowed },
};
int register_android_os_Trace(JNIEnv* env) {
diff --git a/core/res/res/anim/rotation_animation_xfade_exit.xml b/core/res/res/anim/rotation_animation_xfade_exit.xml
index 7300724..1dedde4 100644
--- a/core/res/res/anim/rotation_animation_xfade_exit.xml
+++ b/core/res/res/anim/rotation_animation_xfade_exit.xml
@@ -18,6 +18,6 @@
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:duration="500"
+ android:duration="150"
android:interpolator="@interpolator/decelerate_quad" />
</set>
diff --git a/core/res/res/drawable-hdpi/menu_popup_panel_holo_dark.9.png b/core/res/res/drawable-hdpi/menu_popup_panel_holo_dark.9.png
new file mode 100644
index 0000000..e5ff886
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menu_popup_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_popup_panel_holo_light.9.png b/core/res/res/drawable-hdpi/menu_popup_panel_holo_light.9.png
new file mode 100644
index 0000000..06d1653
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menu_popup_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_popup_panel_holo_dark.9.png b/core/res/res/drawable-mdpi/menu_popup_panel_holo_dark.9.png
new file mode 100644
index 0000000..2020a42
--- /dev/null
+++ b/core/res/res/drawable-mdpi/menu_popup_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_popup_panel_holo_light.9.png b/core/res/res/drawable-mdpi/menu_popup_panel_holo_light.9.png
new file mode 100644
index 0000000..7cae402
--- /dev/null
+++ b/core/res/res/drawable-mdpi/menu_popup_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/menu_popup_panel_holo_dark.9.png b/core/res/res/drawable-xhdpi/menu_popup_panel_holo_dark.9.png
new file mode 100644
index 0000000..e85b0c2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/menu_popup_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/menu_popup_panel_holo_light.9.png b/core/res/res/drawable-xhdpi/menu_popup_panel_holo_light.9.png
new file mode 100644
index 0000000..eea215d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/menu_popup_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable/menu_panel_holo_dark.xml b/core/res/res/drawable/menu_panel_holo_dark.xml
new file mode 100644
index 0000000..658a3ac
--- /dev/null
+++ b/core/res/res/drawable/menu_panel_holo_dark.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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_above_anchor="true" android:drawable="@android:drawable/menu_popup_panel_holo_dark" />
+ <item android:drawable="@android:drawable/menu_dropdown_panel_holo_dark" />
+</selector>
diff --git a/core/res/res/drawable/menu_panel_holo_light.xml b/core/res/res/drawable/menu_panel_holo_light.xml
new file mode 100644
index 0000000..a37e934
--- /dev/null
+++ b/core/res/res/drawable/menu_panel_holo_light.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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_above_anchor="true" android:drawable="@android:drawable/menu_popup_panel_holo_light" />
+ <item android:drawable="@android:drawable/menu_dropdown_panel_holo_light" />
+</selector>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index f7ff77b..146607e 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -141,6 +141,10 @@
<item>@drawable/menu_submenu_background</item>
<item>@drawable/menu_dropdown_panel_holo_light</item>
<item>@drawable/menu_dropdown_panel_holo_dark</item>
+ <item>@drawable/menu_popup_panel_holo_light</item>
+ <item>@drawable/menu_popup_panel_holo_dark</item>
+ <item>@drawable/menu_panel_holo_light</item>
+ <item>@drawable/menu_panel_holo_dark</item>
<item>@drawable/overscroll_edge</item>
<item>@drawable/overscroll_glow</item>
<item>@drawable/spinner_16_outer_holo</item>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 56c2235..f494d8c 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1869,7 +1869,7 @@
<style name="Widget.Holo.ListPopupWindow" parent="Widget.ListPopupWindow">
<item name="android:dropDownSelector">@android:drawable/list_selector_holo_dark</item>
- <item name="android:popupBackground">@android:drawable/menu_dropdown_panel_holo_dark</item>
+ <item name="android:popupBackground">@android:drawable/menu_panel_holo_dark</item>
<item name="android:dropDownVerticalOffset">0dip</item>
<item name="android:dropDownHorizontalOffset">0dip</item>
<item name="android:dropDownWidth">wrap_content</item>
@@ -2242,7 +2242,7 @@
<style name="Widget.Holo.Light.ListPopupWindow" parent="Widget.ListPopupWindow">
<item name="android:dropDownSelector">@android:drawable/list_selector_holo_light</item>
- <item name="android:popupBackground">@android:drawable/menu_dropdown_panel_holo_light</item>
+ <item name="android:popupBackground">@android:drawable/menu_panel_holo_light</item>
<item name="android:dropDownVerticalOffset">0dip</item>
<item name="android:dropDownHorizontalOffset">0dip</item>
<item name="android:dropDownWidth">wrap_content</item>
diff --git a/drm/jni/Android.mk b/drm/jni/Android.mk
index fff7eee..474b9b2 100644
--- a/drm/jni/Android.mk
+++ b/drm/jni/Android.mk
@@ -23,6 +23,7 @@
LOCAL_SHARED_LIBRARIES := \
libdrmframework \
+ liblog \
libutils \
libandroid_runtime \
libnativehelper \
@@ -43,4 +44,3 @@
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
-
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 8bc6b96..5751331 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -500,7 +500,9 @@
*/
public void copyFromUnchecked(int[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
@@ -515,7 +517,9 @@
*/
public void copyFromUnchecked(short[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
@@ -530,7 +534,9 @@
*/
public void copyFromUnchecked(byte[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
@@ -545,7 +551,9 @@
*/
public void copyFromUnchecked(float[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFromUnchecked(0, mCurrentCount, d);
@@ -561,7 +569,9 @@
*/
public void copyFrom(int[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFrom(0, mCurrentCount, d);
@@ -577,7 +587,9 @@
*/
public void copyFrom(short[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFrom(0, mCurrentCount, d);
@@ -593,7 +605,9 @@
*/
public void copyFrom(byte[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFrom(0, mCurrentCount, d);
@@ -609,7 +623,9 @@
*/
public void copyFrom(float[] d) {
mRS.validate();
- if (mCurrentDimY > 0) {
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
+ } else if (mCurrentDimY > 0) {
copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
} else {
copy1DRangeFrom(0, mCurrentCount, d);
@@ -975,12 +991,144 @@
Canvas c = new Canvas(newBitmap);
c.drawBitmap(data, 0, 0, null);
copy2DRangeFrom(xoff, yoff, newBitmap);
+ return;
}
validateBitmapFormat(data);
validate2DRange(xoff, yoff, data.getWidth(), data.getHeight());
mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, data);
}
+ private void validate3DRange(int xoff, int yoff, int zoff, int w, int h, int d) {
+ if (mAdaptedAllocation != null) {
+
+ } else {
+
+ if (xoff < 0 || yoff < 0 || zoff < 0) {
+ throw new RSIllegalArgumentException("Offset cannot be negative.");
+ }
+ if (h < 0 || w < 0 || d < 0) {
+ throw new RSIllegalArgumentException("Height or width cannot be negative.");
+ }
+ if (((xoff + w) > mCurrentDimX) || ((yoff + h) > mCurrentDimY) || ((zoff + d) > mCurrentDimZ)) {
+ throw new RSIllegalArgumentException("Updated region larger than allocation.");
+ }
+ }
+ }
+
+ /**
+ * @hide
+ *
+ */
+ void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, byte[] data) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data, data.length);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, short[] data) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data, data.length * 2);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, int[] data) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data, data.length * 4);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, float[] data) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data, data.length * 4);
+ }
+
+
+ /**
+ * @hide
+ * Copy a rectangular region from the array into the allocation.
+ * The incoming array is assumed to be tightly packed.
+ *
+ * @param xoff X offset of the region to update
+ * @param yoff Y offset of the region to update
+ * @param zoff Z offset of the region to update
+ * @param w Width of the incoming region to update
+ * @param h Height of the incoming region to update
+ * @param d Depth of the incoming region to update
+ * @param data to be placed into the allocation
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, byte[] data) {
+ validateIsInt8();
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, short[] data) {
+ validateIsInt16();
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, int[] data) {
+ validateIsInt32();
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ }
+
+ /**
+ * @hide
+ *
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, float[] data) {
+ validateIsFloat32();
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ }
+
+ /**
+ * @hide
+ * Copy a rectangular region into the allocation from another
+ * allocation.
+ *
+ * @param xoff X offset of the region to update.
+ * @param yoff Y offset of the region to update.
+ * @param w Width of the incoming region to update.
+ * @param h Height of the incoming region to update.
+ * @param d Depth of the incoming region to update.
+ * @param data source allocation.
+ * @param dataXoff X offset in data of the region to update.
+ * @param dataYoff Y offset in data of the region to update.
+ * @param dataZoff Z offset in data of the region to update
+ */
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d,
+ Allocation data, int dataXoff, int dataYoff, int dataZoff) {
+ mRS.validate();
+ validate3DRange(xoff, yoff, zoff, w, h, d);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
+ w, h, d, data.getID(mRS), dataXoff, dataYoff, dataZoff,
+ data.mSelectedLOD);
+ }
+
/**
* Copy from the Allocation into a Bitmap. The bitmap must
@@ -1058,6 +1206,10 @@
* A new type will be created with the new dimension.
*
* @param dimX The new size of the allocation.
+ *
+ * @deprecated Renderscript objects should be immutable once
+ * created. The replacement is to create a new allocation and copy the
+ * contents.
*/
public synchronized void resize(int dimX) {
if ((mType.getY() > 0)|| (mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
@@ -1072,38 +1224,6 @@
updateCacheInfo(mType);
}
- /**
- * Resize a 2D allocation. The contents of the allocation are
- * preserved. If new elements are allocated objects are created
- * with null contents and the new region is otherwise undefined.
- *
- * If the new region is smaller the references of any objects
- * outside the new region will be released.
- *
- * A new type will be created with the new dimension.
- *
- * @param dimX The new size of the allocation.
- * @param dimY The new size of the allocation.
- */
- public synchronized void resize(int dimX, int dimY) {
- if ((mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
- throw new RSInvalidStateException(
- "Resize only support for 2D allocations at this time.");
- }
- if (mType.getY() == 0) {
- throw new RSInvalidStateException(
- "Resize only support for 2D allocations at this time.");
- }
- mRS.nAllocationResize2D(getID(mRS), dimX, dimY);
- mRS.finish(); // Necessary because resize is fifoed and update is async.
-
- int typeID = mRS.nAllocationGetType(getID(mRS));
- mType = new Type(typeID, mRS);
- mType.updateFromNative();
- updateCacheInfo(mType);
- }
-
-
// creation
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 60f381c..d5af276 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -83,11 +83,7 @@
native void nContextInitToClient(int con);
native void nContextDeinitToClient(int con);
- /**
- * Name of the file that holds the object cache.
- */
- private static final String CACHE_PATH = "com.android.renderscript.cache";
- static String mCachePath;
+ static File mCacheDir;
/**
* Sets the directory to use as a persistent storage for the
@@ -97,9 +93,8 @@
* @param cacheDir A directory the current process can write to
*/
public static void setupDiskCache(File cacheDir) {
- File f = new File(cacheDir, CACHE_PATH);
- mCachePath = f.getAbsolutePath();
- f.mkdirs();
+ // Defer creation of cache path to nScriptCCreate().
+ mCacheDir = cacheDir;
}
public enum ContextType {
@@ -420,6 +415,46 @@
rsnAllocationData2D(mContext, id, xoff, yoff, mip, face, b);
}
+ native void rsnAllocationData3D(int con,
+ int dstAlloc, int dstXoff, int dstYoff, int dstZoff,
+ int dstMip,
+ int width, int height, int depth,
+ int srcAlloc, int srcXoff, int srcYoff, int srcZoff,
+ int srcMip);
+ synchronized void nAllocationData3D(int dstAlloc, int dstXoff, int dstYoff, int dstZoff,
+ int dstMip,
+ int width, int height, int depth,
+ int srcAlloc, int srcXoff, int srcYoff, int srcZoff,
+ int srcMip) {
+ validate();
+ rsnAllocationData3D(mContext,
+ dstAlloc, dstXoff, dstYoff, dstZoff,
+ dstMip, width, height, depth,
+ srcAlloc, srcXoff, srcYoff, srcZoff, srcMip);
+ }
+
+ native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, byte[] d, int sizeBytes);
+ synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, byte[] d, int sizeBytes) {
+ validate();
+ rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+ }
+ native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, short[] d, int sizeBytes);
+ synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, short[] d, int sizeBytes) {
+ validate();
+ rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+ }
+ native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, int[] d, int sizeBytes);
+ synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, int[] d, int sizeBytes) {
+ validate();
+ rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+ }
+ native void rsnAllocationData3D(int con, int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, float[] d, int sizeBytes);
+ synchronized void nAllocationData3D(int id, int xoff, int yoff, int zoff, int mip, int w, int h, int depth, float[] d, int sizeBytes) {
+ validate();
+ rsnAllocationData3D(mContext, id, xoff, yoff, zoff, mip, w, h, depth, d, sizeBytes);
+ }
+
+
native void rsnAllocationRead(int con, int id, byte[] d);
synchronized void nAllocationRead(int id, byte[] d) {
validate();
@@ -451,11 +486,6 @@
validate();
rsnAllocationResize1D(mContext, id, dimX);
}
- native void rsnAllocationResize2D(int con, int id, int dimX, int dimY);
- synchronized void nAllocationResize2D(int id, int dimX, int dimY) {
- validate();
- rsnAllocationResize2D(mContext, id, dimX, dimY);
- }
native int rsnFileA3DCreateFromAssetStream(int con, int assetStream);
synchronized int nFileA3DCreateFromAssetStream(int assetStream) {
diff --git a/graphics/java/android/renderscript/ScriptC.java b/graphics/java/android/renderscript/ScriptC.java
index 9221c7a..2f69775 100644
--- a/graphics/java/android/renderscript/ScriptC.java
+++ b/graphics/java/android/renderscript/ScriptC.java
@@ -64,6 +64,12 @@
rs.addAllocSizeForGC(mGCSize);
}
+ /**
+ * Name of the file that holds the object cache.
+ */
+ private static final String CACHE_PATH = "com.android.renderscript.cache";
+
+ static String mCachePath;
private static synchronized int internalCreate(RenderScript rs, Resources resources, int resourceID) {
byte[] pgm;
@@ -96,7 +102,13 @@
String resName = resources.getResourceEntryName(resourceID);
+ // Create the RS cache path if we haven't done so already.
+ if (mCachePath == null) {
+ File f = new File(rs.mCacheDir, CACHE_PATH);
+ mCachePath = f.getAbsolutePath();
+ f.mkdirs();
+ }
Log.v(TAG, "Create script for resource = " + resName);
- return rs.nScriptCCreate(resName, rs.mCachePath, pgm, pgmLength);
+ return rs.nScriptCCreate(resName, mCachePath, pgm, pgmLength);
}
}
diff --git a/graphics/jni/Android.mk b/graphics/jni/Android.mk
index 80d7728..e8beae53 100644
--- a/graphics/jni/Android.mk
+++ b/graphics/jni/Android.mk
@@ -10,6 +10,7 @@
libnativehelper \
libRS \
libcutils \
+ liblog \
libskia \
libutils \
libui \
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 8757b19..460a516 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -725,6 +725,72 @@
}
static void
+nAllocationData3D_s(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
+ jint w, jint h, jint d, jshortArray data, int sizeBytes)
+{
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation3DData_s, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
+ jshort *ptr = _env->GetShortArrayElements(data, NULL);
+ rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ _env->ReleaseShortArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData3D_b(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
+ jint w, jint h, jint d, jbyteArray data, int sizeBytes)
+{
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation3DData_b, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
+ jbyte *ptr = _env->GetByteArrayElements(data, NULL);
+ rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData3D_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
+ jint w, jint h, jint d, jintArray data, int sizeBytes)
+{
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation3DData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
+ jint *ptr = _env->GetIntArrayElements(data, NULL);
+ rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData3D_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint zoff, jint lod,
+ jint w, jint h, jint d, jfloatArray data, int sizeBytes)
+{
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation3DData_f, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, zoff, w, h, d, len);
+ jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+ rsAllocation3DData(con, (RsAllocation)alloc, xoff, yoff, zoff, lod, w, h, d, ptr, sizeBytes, 0);
+ _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData3D_alloc(JNIEnv *_env, jobject _this, RsContext con,
+ jint dstAlloc, jint dstXoff, jint dstYoff, jint dstZoff,
+ jint dstMip,
+ jint width, jint height, jint depth,
+ jint srcAlloc, jint srcXoff, jint srcYoff, jint srcZoff,
+ jint srcMip)
+{
+ LOG_API("nAllocationData3D_alloc, con(%p), dstAlloc(%p), dstXoff(%i), dstYoff(%i),"
+ " dstMip(%i), width(%i), height(%i),"
+ " srcAlloc(%p), srcXoff(%i), srcYoff(%i), srcMip(%i)",
+ con, (RsAllocation)dstAlloc, dstXoff, dstYoff, dstMip, dstFace,
+ width, height, (RsAllocation)srcAlloc, srcXoff, srcYoff, srcMip, srcFace);
+
+ rsAllocationCopy3DRange(con,
+ (RsAllocation)dstAlloc,
+ dstXoff, dstYoff, dstZoff, dstMip,
+ width, height, depth,
+ (RsAllocation)srcAlloc,
+ srcXoff, srcYoff, srcZoff, srcMip);
+}
+
+static void
nAllocationRead_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jintArray data)
{
jint len = _env->GetArrayLength(data);
@@ -782,13 +848,6 @@
rsAllocationResize1D(con, (RsAllocation)alloc, dimX);
}
-static void
-nAllocationResize2D(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint dimX, jint dimY)
-{
- LOG_API("nAllocationResize1D, con(%p), alloc(%p), sizeX(%i), sizeY(%i)", con, (RsAllocation)alloc, dimX, dimY);
- rsAllocationResize2D(con, (RsAllocation)alloc, dimX, dimY);
-}
-
// -----------------------------------
static int
@@ -1519,13 +1578,17 @@
{"rsnAllocationData2D", "(IIIIIIII[BI)V", (void*)nAllocationData2D_b },
{"rsnAllocationData2D", "(IIIIIIII[FI)V", (void*)nAllocationData2D_f },
{"rsnAllocationData2D", "(IIIIIIIIIIIII)V", (void*)nAllocationData2D_alloc },
+{"rsnAllocationData3D", "(IIIIIIIII[II)V", (void*)nAllocationData3D_i },
+{"rsnAllocationData3D", "(IIIIIIIII[SI)V", (void*)nAllocationData3D_s },
+{"rsnAllocationData3D", "(IIIIIIIII[BI)V", (void*)nAllocationData3D_b },
+{"rsnAllocationData3D", "(IIIIIIIII[FI)V", (void*)nAllocationData3D_f },
+{"rsnAllocationData3D", "(IIIIIIIIIIIIII)V", (void*)nAllocationData3D_alloc },
{"rsnAllocationRead", "(II[I)V", (void*)nAllocationRead_i },
{"rsnAllocationRead", "(II[S)V", (void*)nAllocationRead_s },
{"rsnAllocationRead", "(II[B)V", (void*)nAllocationRead_b },
{"rsnAllocationRead", "(II[F)V", (void*)nAllocationRead_f },
{"rsnAllocationGetType", "(II)I", (void*)nAllocationGetType},
{"rsnAllocationResize1D", "(III)V", (void*)nAllocationResize1D },
-{"rsnAllocationResize2D", "(IIII)V", (void*)nAllocationResize2D },
{"rsnAllocationGenerateMipmaps", "(II)V", (void*)nAllocationGenerateMipmaps },
{"rsnScriptBindAllocation", "(IIII)V", (void*)nScriptBindAllocation },
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 06e658d..7b59bf24 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -58,7 +58,7 @@
LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DGL_GLEXT_PROTOTYPES
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
- LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui libRS libRScpp
+ LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libGLESv2 libskia libui libRS libRScpp
LOCAL_MODULE := libhwui
LOCAL_MODULE_TAGS := optional
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index 9bc5c14..51f1e39 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -38,6 +38,10 @@
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
if (useFloatTexture) {
+ // We use a R16F texture, let's remap the alpha channel to the
+ // red channel to avoid changing the shader sampling code on GL ES 3.0+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
+
float dither = 1.0f / (255.0f * DITHER_KERNEL_SIZE * DITHER_KERNEL_SIZE);
const GLfloat pattern[] = {
0 * dither, 8 * dither, 2 * dither, 10 * dither,
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 3730017..e18d922 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1905,14 +1905,15 @@
status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty,
int32_t replayFlags) {
+ status_t status;
// All the usual checks and setup operations (quickReject, setupDraw, etc.)
// will be performed by the display list itself
if (displayList && displayList->isRenderable()) {
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
- startFrame();
+ status = startFrame();
ReplayStateStruct replayStruct(*this, dirty, replayFlags);
displayList->replay(replayStruct, 0);
- return replayStruct.mDrawGlStatus;
+ return status | replayStruct.mDrawGlStatus;
}
DeferredDisplayList deferredList;
@@ -1920,9 +1921,9 @@
displayList->defer(deferStruct, 0);
flushLayers();
- startFrame();
+ status = startFrame();
- return deferredList.flush(*this, dirty);
+ return status | deferredList.flush(*this, dirty);
}
return DrawGlInfo::kStatusDone;
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 2479630..8eb85e5 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -186,7 +186,7 @@
// ES 2.0
"texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE),
// ES 3.0
- "texture2D(ditherSampler, ditherTexCoords).r"
+ "texture2D(ditherSampler, ditherTexCoords).a"
};
const char* gFS_Main_AddDitherToGradient =
" gradientColor += %s;\n";
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 773d7b6..637ac85 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -3534,6 +3534,9 @@
onNewPlaybackStateForRcc(msg.arg1 /* rccId */, msg.arg2 /* state */,
(RccPlaybackState)msg.obj /* newState */);
break;
+ case MSG_RCC_SEEK_REQUEST:
+ onSetRemoteControlClientPlaybackPosition(msg.arg1 /* generationId */,
+ ((Long)msg.obj).longValue() /* timeMs */);
case MSG_SET_RSX_CONNECTION_STATE:
onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
@@ -5867,7 +5870,16 @@
}
public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
- sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_QUEUE, generationId /* arg1 */,
+ // ignore position change requests if invalid generation ID
+ synchronized(mRCStack) {
+ synchronized(mCurrentRcLock) {
+ if (mCurrentRcClientGen != generationId) {
+ return;
+ }
+ }
+ }
+ // discard any unprocessed seek request in the message queue, and replace with latest
+ sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */,
0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */);
}
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index e076ef0..93bcf03 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -683,9 +683,12 @@
/**
* Called on the implementer to notify it that the playback head should be set at the given
* position. If the position can be changed from its current value, the implementor of
- * the interface should also update the playback position using
+ * the interface must also update the playback position using
* {@link RemoteControlClient#setPlaybackState(int, long, int)} to reflect the actual new
* position being used, regardless of whether it differs from the requested position.
+ * Failure to do so would cause the system to not know the new actual playback position,
+ * and user interface components would fail to show the user where playback resumed after
+ * the position was updated.
* @param newPositionMs the new requested position in the current media, expressed in ms.
*/
void onPlaybackPositionUpdate(long newPositionMs);
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 6873060..416a2a1 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -28,6 +28,7 @@
libmedia \
libskia \
libui \
+ liblog \
libcutils \
libgui \
libstagefright \
diff --git a/media/jni/audioeffect/Android.mk b/media/jni/audioeffect/Android.mk
index b5d8b7b..3b1fb19 100644
--- a/media/jni/audioeffect/Android.mk
+++ b/media/jni/audioeffect/Android.mk
@@ -6,6 +6,7 @@
android_media_Visualizer.cpp
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libcutils \
libutils \
libandroid_runtime \
diff --git a/media/jni/mediaeditor/Android.mk b/media/jni/mediaeditor/Android.mk
index 040d2ab..6be7fdd 100644
--- a/media/jni/mediaeditor/Android.mk
+++ b/media/jni/mediaeditor/Android.mk
@@ -52,6 +52,7 @@
libaudioutils \
libbinder \
libcutils \
+ liblog \
libdl \
libgui \
libmedia \
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 9b11bfa..5835b9f 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -5,6 +5,7 @@
android_media_SoundPool.cpp
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libcutils \
libutils \
libandroid_runtime \
diff --git a/media/libdrm/mobile1/Android.mk b/media/libdrm/mobile1/Android.mk
index b07d91c..7356f46 100644
--- a/media/libdrm/mobile1/Android.mk
+++ b/media/libdrm/mobile1/Android.mk
@@ -44,6 +44,7 @@
LOCAL_SHARED_LIBRARIES := \
libutils \
libcutils \
+ liblog \
libcrypto
LOCAL_MODULE := libdrm1
@@ -69,12 +70,13 @@
$(LOCAL_PATH)/include/parser \
$(JNI_H_INCLUDE) \
$(call include-path-for, system-core)/cutils
-
+
LOCAL_SHARED_LIBRARIES := libdrm1 \
libnativehelper \
libutils \
- libcutils
+ libcutils \
+ liblog
LOCAL_MODULE := libdrm1_jni
diff --git a/media/mca/filterfw/Android.mk b/media/mca/filterfw/Android.mk
index 1d69799..2a9448d 100644
--- a/media/mca/filterfw/Android.mk
+++ b/media/mca/filterfw/Android.mk
@@ -37,6 +37,7 @@
libdl \
libcutils \
libutils \
+ liblog \
libandroid \
libjnigraphics \
libmedia
@@ -48,5 +49,3 @@
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
-
-
diff --git a/media/mca/filterpacks/Android.mk b/media/mca/filterpacks/Android.mk
index 6166b1e..6e54f60 100644
--- a/media/mca/filterpacks/Android.mk
+++ b/media/mca/filterpacks/Android.mk
@@ -46,10 +46,8 @@
native/imageproc/invert.c \
native/imageproc/to_rgba.c
-LOCAL_SHARED_LIBRARIES := libutils libfilterfw
+LOCAL_SHARED_LIBRARIES := liblog libutils libfilterfw
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
-
-
diff --git a/media/tests/omxjpegdecoder/Android.mk b/media/tests/omxjpegdecoder/Android.mk
index 9dcc7ba..ad874c8 100644
--- a/media/tests/omxjpegdecoder/Android.mk
+++ b/media/tests/omxjpegdecoder/Android.mk
@@ -29,6 +29,7 @@
libstagefright_foundation \
libbinder \
libutils \
+ liblog \
libjpeg
LOCAL_C_INCLUDES := \
diff --git a/media/tests/players/Android.mk b/media/tests/players/Android.mk
index c655ae6..adf0d30 100644
--- a/media/tests/players/Android.mk
+++ b/media/tests/players/Android.mk
@@ -20,7 +20,8 @@
LOCAL_SHARED_LIBRARIES:= \
libbinder \
- libutils
+ libutils \
+ liblog
LOCAL_MODULE:= invoke_mock_media_player
LOCAL_MODULE_TAGS := tests eng
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 00d11da..207cc4b 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -17,6 +17,7 @@
storage_manager.cpp
LOCAL_SHARED_LIBRARIES := \
+ liblog \
libcutils \
libandroidfw \
libutils \
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 5a2e261..daa5d13 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -18,7 +18,6 @@
import java.io.Writer;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
@@ -30,10 +29,13 @@
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
-import android.content.pm.ConfigurationInfo;
-import android.os.SystemProperties;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.Choreographer;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@@ -164,11 +166,26 @@
private final static String TAG = "GLSurfaceView";
private final static boolean LOG_ATTACH_DETACH = false;
private final static boolean LOG_THREADS = false;
- private final static boolean LOG_PAUSE_RESUME = false;
private final static boolean LOG_SURFACE = false;
private final static boolean LOG_RENDERER = false;
private final static boolean LOG_RENDERER_DRAW_FRAME = false;
private final static boolean LOG_EGL = false;
+ private final static boolean TRACE_ENABLED = false;
+
+ private final WeakReference<GLSurfaceView> mThisWeakRef =
+ new WeakReference<GLSurfaceView>(this);
+ private GLThread mGLThread;
+ private Renderer mRenderer;
+ private boolean mDetached;
+ private EGLConfigChooser mEGLConfigChooser;
+ private EGLContextFactory mEGLContextFactory;
+ private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
+ private GLWrapper mGLWrapper;
+ private int mDebugFlags;
+ private int mEGLContextClientVersion;
+ private boolean mPreserveEGLContextOnPause;
+ private int mUserRenderMode;
+
/**
* The renderer only renders
* when the surface is created, or when {@link #requestRender} is called.
@@ -241,13 +258,7 @@
// underlying surface is created and destroyed
SurfaceHolder holder = getHolder();
holder.addCallback(this);
- // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment
- // this statement if back-porting to 2.2 or older:
- // holder.setFormat(PixelFormat.RGB_565);
- //
- // setType is not needed for SDK 2.0 or newer. Uncomment this
- // statement if back-porting this code to older SDKs.
- // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+ mUserRenderMode = RENDERMODE_CONTINUOUSLY;
}
/**
@@ -346,15 +357,16 @@
public void setRenderer(Renderer renderer) {
checkRenderThreadState();
if (mEGLConfigChooser == null) {
- mEGLConfigChooser = new SimpleEGLConfigChooser(true);
+ mEGLConfigChooser = new SimpleEGLConfigChooser(true, mEGLContextClientVersion);
}
if (mEGLContextFactory == null) {
- mEGLContextFactory = new DefaultContextFactory();
+ mEGLContextFactory = new DefaultContextFactory(mEGLContextClientVersion);
}
if (mEGLWindowSurfaceFactory == null) {
mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
}
mRenderer = renderer;
+
mGLThread = new GLThread(mThisWeakRef);
mGLThread.start();
}
@@ -420,7 +432,7 @@
* @param needDepth
*/
public void setEGLConfigChooser(boolean needDepth) {
- setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
+ setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth, mEGLContextClientVersion));
}
/**
@@ -439,7 +451,7 @@
public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
int alphaSize, int depthSize, int stencilSize) {
setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
- blueSize, alphaSize, depthSize, stencilSize));
+ blueSize, alphaSize, depthSize, stencilSize, mEGLContextClientVersion));
}
/**
@@ -466,6 +478,13 @@
* If
* {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
* EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
+ *
+ * This method must be called before:
+ * <ul>
+ * <li>{@link #setEGLConfigChooser(boolean)}
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+ * </ul>
+ *
* @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
*/
public void setEGLContextClientVersion(int version) {
@@ -490,6 +509,14 @@
* @see #RENDERMODE_WHEN_DIRTY
*/
public void setRenderMode(int renderMode) {
+ switch (renderMode) {
+ case RENDERMODE_WHEN_DIRTY:
+ case RENDERMODE_CONTINUOUSLY:
+ break;
+ default:
+ throw new IllegalArgumentException("renderMode");
+ }
+ mUserRenderMode = renderMode;
mGLThread.setRenderMode(renderMode);
}
@@ -501,7 +528,7 @@
* @see #RENDERMODE_WHEN_DIRTY
*/
public int getRenderMode() {
- return mGLThread.getRenderMode();
+ return mUserRenderMode;
}
/**
@@ -582,14 +609,8 @@
Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
}
if (mDetached && (mRenderer != null)) {
- int renderMode = RENDERMODE_CONTINUOUSLY;
- if (mGLThread != null) {
- renderMode = mGLThread.getRenderMode();
- }
mGLThread = new GLThread(mThisWeakRef);
- if (renderMode != RENDERMODE_CONTINUOUSLY) {
- mGLThread.setRenderMode(renderMode);
- }
+ mGLThread.setRenderMode(mUserRenderMode);
mGLThread.start();
}
mDetached = false;
@@ -761,11 +782,15 @@
void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
}
- private class DefaultContextFactory implements EGLContextFactory {
- private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ private static class DefaultContextFactory implements EGLContextFactory {
+ private final int mEGLContextClientVersion;
+
+ public DefaultContextFactory(int version) {
+ mEGLContextClientVersion = version;
+ }
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
- int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
+ int[] attrib_list = {EGL14.EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
EGL10.EGL_NONE };
return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
@@ -775,9 +800,9 @@
public void destroyContext(EGL10 egl, EGLDisplay display,
EGLContext context) {
if (!egl.eglDestroyContext(display, context)) {
- Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
+ Log.e(TAG, "display:" + display + " context: " + context);
if (LOG_THREADS) {
- Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "tid=" + Thread.currentThread().getId());
}
EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
}
@@ -807,8 +832,8 @@
try {
result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
} catch (IllegalArgumentException e) {
- // This exception indicates that the surface flinger surface
- // is not valid. This can happen if the surface flinger surface has
+ // This exception indicates that the surfaceflinger surface
+ // is not valid. This can happen if the surfaceflinger surface has
// been torn down, but the application has not yet been
// notified via SurfaceHolder.Callback.surfaceDestroyed.
// In theory the application should be notified first,
@@ -844,10 +869,11 @@
EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
}
- private abstract class BaseConfigChooser
+ private static abstract class BaseConfigChooser
implements EGLConfigChooser {
- public BaseConfigChooser(int[] configSpec) {
- mConfigSpec = filterConfigSpec(configSpec);
+
+ public BaseConfigChooser(int[] configSpec, int version) {
+ mConfigSpec = filterConfigSpec(configSpec, version);
}
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
@@ -881,8 +907,8 @@
protected int[] mConfigSpec;
- private int[] filterConfigSpec(int[] configSpec) {
- if (mEGLContextClientVersion != 2) {
+ private int[] filterConfigSpec(int[] configSpec, int version) {
+ if (version != 2) {
return configSpec;
}
/* We know none of the subclasses define EGL_RENDERABLE_TYPE.
@@ -892,7 +918,7 @@
int[] newConfigSpec = new int[len + 2];
System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
- newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
+ newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT;
newConfigSpec[len+1] = EGL10.EGL_NONE;
return newConfigSpec;
}
@@ -902,9 +928,9 @@
* Choose a configuration with exactly the specified r,g,b,a sizes,
* and at least the specified depth and stencil sizes.
*/
- private class ComponentSizeChooser extends BaseConfigChooser {
+ private static class ComponentSizeChooser extends BaseConfigChooser {
public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
- int alphaSize, int depthSize, int stencilSize) {
+ int alphaSize, int depthSize, int stencilSize, int version) {
super(new int[] {
EGL10.EGL_RED_SIZE, redSize,
EGL10.EGL_GREEN_SIZE, greenSize,
@@ -912,7 +938,7 @@
EGL10.EGL_ALPHA_SIZE, alphaSize,
EGL10.EGL_DEPTH_SIZE, depthSize,
EGL10.EGL_STENCIL_SIZE, stencilSize,
- EGL10.EGL_NONE});
+ EGL10.EGL_NONE}, version);
mValue = new int[1];
mRedSize = redSize;
mGreenSize = greenSize;
@@ -920,7 +946,7 @@
mAlphaSize = alphaSize;
mDepthSize = depthSize;
mStencilSize = stencilSize;
- }
+ }
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
@@ -931,14 +957,10 @@
int s = findConfigAttrib(egl, display, config,
EGL10.EGL_STENCIL_SIZE, 0);
if ((d >= mDepthSize) && (s >= mStencilSize)) {
- int r = findConfigAttrib(egl, display, config,
- EGL10.EGL_RED_SIZE, 0);
- int g = findConfigAttrib(egl, display, config,
- EGL10.EGL_GREEN_SIZE, 0);
- int b = findConfigAttrib(egl, display, config,
- EGL10.EGL_BLUE_SIZE, 0);
- int a = findConfigAttrib(egl, display, config,
- EGL10.EGL_ALPHA_SIZE, 0);
+ int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
if ((r == mRedSize) && (g == mGreenSize)
&& (b == mBlueSize) && (a == mAlphaSize)) {
return config;
@@ -965,16 +987,16 @@
protected int mAlphaSize;
protected int mDepthSize;
protected int mStencilSize;
- }
+ }
/**
* This class will choose a RGB_888 surface with
* or without a depth buffer.
*
*/
- private class SimpleEGLConfigChooser extends ComponentSizeChooser {
- public SimpleEGLConfigChooser(boolean withDepthBuffer) {
- super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
+ private static class SimpleEGLConfigChooser extends ComponentSizeChooser {
+ public SimpleEGLConfigChooser(boolean withDepthBuffer, int version) {
+ super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0, version);
}
}
@@ -991,9 +1013,9 @@
* Initialize EGL for a given configuration spec.
* @param configSpec
*/
- public void start() {
+ public void initialize() {
if (LOG_EGL) {
- Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "initialize() tid=" + Thread.currentThread().getId());
}
/*
* Get an EGL instance
@@ -1034,7 +1056,7 @@
throwEglException("createContext");
}
if (LOG_EGL) {
- Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
}
mEglSurface = null;
@@ -1048,7 +1070,7 @@
*/
public boolean createSurface() {
if (LOG_EGL) {
- Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "createSurface() tid=" + Thread.currentThread().getId());
}
/*
* Check preconditions.
@@ -1083,7 +1105,7 @@
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
- Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+ Log.e(TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
}
return false;
}
@@ -1097,8 +1119,9 @@
* Could not make the context current, probably because the underlying
* SurfaceView surface has been destroyed.
*/
- logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
- return false;
+ logEglErrorAsWarning(TAG, "eglMakeCurrent", mEgl.eglGetError());
+ // we fall-through to "true" here because we do have a
+ // valid EGLSurface at this point.
}
return true;
@@ -1108,8 +1131,7 @@
* Create a GL object for the current EGL context.
* @return
*/
- GL createGL() {
-
+ public GL createGL() {
GL gl = mEglContext.getGL();
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
@@ -1145,7 +1167,7 @@
public void destroySurface() {
if (LOG_EGL) {
- Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "destroySurface() tid=" + Thread.currentThread().getId());
}
destroySurfaceImp();
}
@@ -1163,9 +1185,9 @@
}
}
- public void finish() {
+ public void terminate() {
if (LOG_EGL) {
- Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
+ Log.d(TAG, "terminate() tid=" + Thread.currentThread().getId());
}
if (mEglContext != null) {
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
@@ -1187,7 +1209,7 @@
public static void throwEglException(String function, int error) {
String message = formatEglError(function, error);
if (LOG_THREADS) {
- Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " "
+ Log.e(TAG, "throwEglException tid=" + Thread.currentThread().getId() + " "
+ message);
}
throw new RuntimeException(message);
@@ -1207,584 +1229,411 @@
EGLSurface mEglSurface;
EGLConfig mEglConfig;
EGLContext mEglContext;
-
}
/**
* A generic GL Thread. Takes care of initializing EGL and GL. Delegates
* to a Renderer instance to do the actual drawing. Can be configured to
* render continuously or on request.
- *
- * All potentially blocking synchronization is done through the
- * sGLThreadManager object. This avoids multiple-lock ordering issues.
- *
*/
- static class GLThread extends Thread {
- GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
- super();
+
+ static class GLThread extends HandlerThread {
+ // only accessed from GLThread
+ private GL10 mGLContext;
+ private int mWidth;
+ private int mHeight;
+ private boolean mSizeChanged;
+ // current render mode
+ private int mRenderMode;
+ // the EGLSurface exists but isn't working for some reason
+ private boolean mEglSurfaceIsBad;
+ // we have an EGLContext
+ private boolean mHaveEglContext;
+ // we have an EGLSurface
+ private boolean mHaveEglSurface;
+ // we have a Surface (i.e.: EGLNativeWindowType)
+ private boolean mHasSurface;
+ // activity is paused
+ private boolean mPaused;
+
+ // constants
+ private EglHelper mEglHelper;
+ private Handler mGLHandler;
+ private Choreographer mChoreographer;
+
+ /*
+ * Set once at thread construction time, nulled out when the parent view is garbage
+ * called. This weak reference allows the GLSurfaceView to be garbage collected while
+ * the GLThread is still alive.
+ */
+ private final WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
+
+ private final Runnable mExecuteDrawAction = new Runnable() {
+ private int mTraceVsyncCounter = 0;
+ @Override
+ public void run() {
+ if (TRACE_ENABLED) {
+ Trace.traceCounter(Trace.TRACE_TAG_GRAPHICS,
+ "GLSurfaceView VSYNC counter", (mTraceVsyncCounter++) & 0xf);
+ }
+ executeDraw();
+ }
+ };
+
+ public GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
+ super("GLThread", android.os.Process.THREAD_PRIORITY_DISPLAY);
+ if (LOG_THREADS) {
+ Log.d(TAG, "*** Starting GLThread ***");
+ }
mWidth = 0;
mHeight = 0;
- mRequestRender = true;
mRenderMode = RENDERMODE_CONTINUOUSLY;
mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
}
+ private void readyToRun() {
+ mChoreographer = Choreographer.getInstance();
+ mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
+ }
+
+ @Override
+ public void start() {
+ super.start();
+ // getLooper() blocks until the thread is running
+ Looper looper = getLooper();
+ mGLHandler = new Handler(looper);
+ // don't return until the GLThread state has been initialized
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ readyToRun();
+ }
+ }, 0);
+ }
+
@Override
public void run() {
- setName("GLThread " + getId());
- if (LOG_THREADS) {
- Log.i("GLThread", "starting tid=" + getId());
- }
-
try {
- guardedRun();
- } catch (InterruptedException e) {
- // fall thru and exit normally
+ super.run();
} finally {
- sGLThreadManager.threadExiting(this);
+ // by definition the GLThread is not running anymore here
+ stopEglContext();
+ stopEglSurface();
}
}
- /*
- * This private method should only be called inside a
- * synchronized(sGLThreadManager) block.
- */
- private void stopEglSurfaceLocked() {
+ // only call from the GLThread
+ private void stopEglSurface() {
if (mHaveEglSurface) {
+ if (LOG_SURFACE) {
+ Log.d(TAG, "releasing EGL surface because paused tid=" + getId());
+ }
mHaveEglSurface = false;
mEglHelper.destroySurface();
}
}
- /*
- * This private method should only be called inside a
- * synchronized(sGLThreadManager) block.
- */
- private void stopEglContextLocked() {
+ // only call from the GLThread
+ private void stopEglContext() {
if (mHaveEglContext) {
- mEglHelper.finish();
+ mEglHelper.terminate();
mHaveEglContext = false;
- sGLThreadManager.releaseEglContextLocked(this);
+ if (LOG_SURFACE) {
+ Log.d(TAG, "releasing EGL context because paused tid=" + getId());
+ }
}
}
- private void guardedRun() throws InterruptedException {
- mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
- mHaveEglContext = false;
- mHaveEglSurface = false;
- try {
- GL10 gl = null;
- boolean createEglContext = false;
- boolean createEglSurface = false;
- boolean createGlInterface = false;
- boolean lostEglContext = false;
- boolean sizeChanged = false;
- boolean wantRenderNotification = false;
- boolean doRenderNotification = false;
- boolean askedToReleaseEglContext = false;
- int w = 0;
- int h = 0;
- Runnable event = null;
- while (true) {
- synchronized (sGLThreadManager) {
- while (true) {
- if (mShouldExit) {
- return;
- }
+ private void updateState() {
+ final boolean wasAbleToDraw = isAbleToDraw();
+ if (!isReadyToDraw()) {
+ return;
+ }
- if (! mEventQueue.isEmpty()) {
- event = mEventQueue.remove(0);
- break;
- }
+ if (!mHaveEglSurface || mSizeChanged) {
+ // create EGL context if needed
+ boolean reportSurfaceCreated = false;
+ if (!mHaveEglContext) {
+ mEglHelper.initialize();
+ mHaveEglContext = true;
+ reportSurfaceCreated = true;
+ }
- // Update the pause state.
- boolean pausing = false;
- if (mPaused != mRequestPaused) {
- pausing = mRequestPaused;
- mPaused = mRequestPaused;
- sGLThreadManager.notifyAll();
- if (LOG_PAUSE_RESUME) {
- Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());
- }
- }
+ // get the GL interface for the active EGLContext
+ mGLContext = (GL10)mEglHelper.createGL();
- // Do we need to give up the EGL context?
- if (mShouldReleaseEglContext) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "releasing EGL context because asked to tid=" + getId());
- }
- stopEglSurfaceLocked();
- stopEglContextLocked();
- mShouldReleaseEglContext = false;
- askedToReleaseEglContext = true;
- }
+ // create EGL Surface
+ mHaveEglSurface = mEglHelper.createSurface();
+ mEglSurfaceIsBad = !mHaveEglSurface;
+ mSizeChanged = false;
- // Have we lost the EGL context?
- if (lostEglContext) {
- stopEglSurfaceLocked();
- stopEglContextLocked();
- lostEglContext = false;
- }
-
- // When pausing, release the EGL surface:
- if (pausing && mHaveEglSurface) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
- }
- stopEglSurfaceLocked();
- }
-
- // When pausing, optionally release the EGL Context:
- if (pausing && mHaveEglContext) {
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- boolean preserveEglContextOnPause = view == null ?
- false : view.mPreserveEGLContextOnPause;
- if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) {
- stopEglContextLocked();
- if (LOG_SURFACE) {
- Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
- }
- }
- }
-
- // When pausing, optionally terminate EGL:
- if (pausing) {
- if (sGLThreadManager.shouldTerminateEGLWhenPausing()) {
- mEglHelper.finish();
- if (LOG_SURFACE) {
- Log.i("GLThread", "terminating EGL because paused tid=" + getId());
- }
- }
- }
-
- // Have we lost the SurfaceView surface?
- if ((! mHasSurface) && (! mWaitingForSurface)) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
- }
- if (mHaveEglSurface) {
- stopEglSurfaceLocked();
- }
- mWaitingForSurface = true;
- mSurfaceIsBad = false;
- sGLThreadManager.notifyAll();
- }
-
- // Have we acquired the surface view surface?
- if (mHasSurface && mWaitingForSurface) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());
- }
- mWaitingForSurface = false;
- sGLThreadManager.notifyAll();
- }
-
- if (doRenderNotification) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "sending render notification tid=" + getId());
- }
- wantRenderNotification = false;
- doRenderNotification = false;
- mRenderComplete = true;
- sGLThreadManager.notifyAll();
- }
-
- // Ready to draw?
- if (readyToDraw()) {
-
- // If we don't have an EGL context, try to acquire one.
- if (! mHaveEglContext) {
- if (askedToReleaseEglContext) {
- askedToReleaseEglContext = false;
- } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) {
- try {
- mEglHelper.start();
- } catch (RuntimeException t) {
- sGLThreadManager.releaseEglContextLocked(this);
- throw t;
- }
- mHaveEglContext = true;
- createEglContext = true;
-
- sGLThreadManager.notifyAll();
- }
- }
-
- if (mHaveEglContext && !mHaveEglSurface) {
- mHaveEglSurface = true;
- createEglSurface = true;
- createGlInterface = true;
- sizeChanged = true;
- }
-
- if (mHaveEglSurface) {
- if (mSizeChanged) {
- sizeChanged = true;
- w = mWidth;
- h = mHeight;
- wantRenderNotification = true;
- if (LOG_SURFACE) {
- Log.i("GLThread",
- "noticing that we want render notification tid="
- + getId());
- }
-
- // Destroy and recreate the EGL surface.
- createEglSurface = true;
-
- mSizeChanged = false;
- }
- mRequestRender = false;
- sGLThreadManager.notifyAll();
- break;
- }
- }
-
- // By design, this is the only place in a GLThread thread where we wait().
- if (LOG_THREADS) {
- Log.i("GLThread", "waiting tid=" + getId()
- + " mHaveEglContext: " + mHaveEglContext
- + " mHaveEglSurface: " + mHaveEglSurface
- + " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface
- + " mPaused: " + mPaused
- + " mHasSurface: " + mHasSurface
- + " mSurfaceIsBad: " + mSurfaceIsBad
- + " mWaitingForSurface: " + mWaitingForSurface
- + " mWidth: " + mWidth
- + " mHeight: " + mHeight
- + " mRequestRender: " + mRequestRender
- + " mRenderMode: " + mRenderMode);
- }
- sGLThreadManager.wait();
- }
- } // end of synchronized(sGLThreadManager)
-
- if (event != null) {
- event.run();
- event = null;
- continue;
- }
-
- if (createEglSurface) {
- if (LOG_SURFACE) {
- Log.w("GLThread", "egl createSurface");
- }
- if (mEglHelper.createSurface()) {
- synchronized(sGLThreadManager) {
- mFinishedCreatingEglSurface = true;
- sGLThreadManager.notifyAll();
- }
- } else {
- synchronized(sGLThreadManager) {
- mFinishedCreatingEglSurface = true;
- mSurfaceIsBad = true;
- sGLThreadManager.notifyAll();
- }
- continue;
- }
- createEglSurface = false;
- }
-
- if (createGlInterface) {
- gl = (GL10) mEglHelper.createGL();
-
- sGLThreadManager.checkGLDriver(gl);
- createGlInterface = false;
- }
-
- if (createEglContext) {
+ // notify use of surface size change
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ if (reportSurfaceCreated) {
if (LOG_RENDERER) {
- Log.w("GLThread", "onSurfaceCreated");
+ Log.d(TAG, "onSurfaceCreated");
}
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- if (view != null) {
- view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
- }
- createEglContext = false;
+ view.mRenderer.onSurfaceCreated(mGLContext, mEglHelper.mEglConfig);
}
- if (sizeChanged) {
- if (LOG_RENDERER) {
- Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
- }
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- if (view != null) {
- view.mRenderer.onSurfaceChanged(gl, w, h);
- }
- sizeChanged = false;
+ if (LOG_RENDERER) {
+ Log.d(TAG, "onSurfaceChanged(" + mWidth + ", " + mHeight + ")");
}
+ view.mRenderer.onSurfaceChanged(mGLContext, mWidth, mHeight);
+ }
+ }
- if (LOG_RENDERER_DRAW_FRAME) {
- Log.w("GLThread", "onDrawFrame tid=" + getId());
- }
- {
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- if (view != null) {
- view.mRenderer.onDrawFrame(gl);
- }
- }
+ // see if we should kick the rendering loop
+ if (!wasAbleToDraw && isAbleToDraw()) {
+ // we're now able to draw
+ if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
+ requestRender();
+ }
+ }
+
+ // By design, this is the only place in a GLThread thread where we wait().
+ if (LOG_THREADS) {
+ Log.d(TAG, "waiting tid=" + getId()
+ + " mHaveEglContext: " + mHaveEglContext
+ + " mHaveEglSurface: " + mHaveEglSurface
+ + " mPaused: " + mPaused
+ + " mHasSurface: " + mHasSurface
+ + " mSurfaceIsBad: " + mEglSurfaceIsBad
+ + " mWidth: " + mWidth
+ + " mHeight: " + mHeight
+ + " mRenderMode: " + mRenderMode);
+ }
+ }
+
+ private void executeDraw() {
+ if (TRACE_ENABLED) {
+ Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "executeDraw");
+ }
+
+ if (isAbleToDraw()) {
+ if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
+ requestRender();
+ }
+
+ if (LOG_RENDERER_DRAW_FRAME) {
+ Log.d(TAG, "onDrawFrame tid=" + getId());
+ }
+
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mRenderer.onDrawFrame(mGLContext);
int swapError = mEglHelper.swap();
switch (swapError) {
case EGL10.EGL_SUCCESS:
break;
case EGL11.EGL_CONTEXT_LOST:
if (LOG_SURFACE) {
- Log.i("GLThread", "egl context lost tid=" + getId());
+ Log.d(TAG, "egl context lost tid=" + getId());
}
- lostEglContext = true;
+ stopEglSurface();
+ stopEglContext();
break;
default:
// Other errors typically mean that the current surface is bad,
// probably because the SurfaceView surface has been destroyed,
// but we haven't been notified yet.
// Log the error to help developers understand why rendering stopped.
- EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);
-
- synchronized(sGLThreadManager) {
- mSurfaceIsBad = true;
- sGLThreadManager.notifyAll();
- }
+ EglHelper.logEglErrorAsWarning(TAG, "eglSwapBuffers", swapError);
+ mEglSurfaceIsBad = true;
break;
}
-
- if (wantRenderNotification) {
- doRenderNotification = true;
- }
}
+ }
- } finally {
- /*
- * clean-up everything...
- */
- synchronized (sGLThreadManager) {
- stopEglSurfaceLocked();
- stopEglContextLocked();
+ if (TRACE_ENABLED) {
+ Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+ }
+ }
+
+ private boolean isAbleToDraw() {
+ return mHaveEglContext && mHaveEglSurface && isReadyToDraw();
+ }
+
+ private boolean isReadyToDraw() {
+ return (!mPaused) && mHasSurface && (!mEglSurfaceIsBad)
+ && (mWidth > 0) && (mHeight > 0);
+ }
+
+ private boolean isEglContextReleasedWhenPausing() {
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ return (view != null) ? !view.mPreserveEGLContextOnPause : false;
+ }
+
+ public void queueEvent(Runnable r) {
+ if (r == null) {
+ throw new IllegalArgumentException("Runnable r must not be null");
+ }
+ mGLHandler.post(r);
+ }
+
+ /*
+ * the call-backs below all run on the GLThread and implement state
+ * changes of the GLSurfaceView and Activity life cycle.
+ */
+
+ private void doSurfaceCreated() {
+ mHasSurface = true;
+ updateState();
+ }
+
+ private void doSurfaceDestroyed() {
+ if (mHasSurface) {
+ if (LOG_SURFACE) {
+ Log.d(TAG, "noticed surfaceView surface lost tid=" + getId());
+ }
+ stopEglSurface();
+ }
+ mHasSurface = false;
+ }
+
+ private void doPause() {
+ if (mPaused == false) {
+ mPaused = true;
+ stopEglSurface();
+ // When pausing, optionally release the EGL Context:
+ if (mHaveEglContext && isEglContextReleasedWhenPausing()) {
+ stopEglContext();
}
}
}
- public boolean ableToDraw() {
- return mHaveEglContext && mHaveEglSurface && readyToDraw();
- }
-
- private boolean readyToDraw() {
- return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
- && (mWidth > 0) && (mHeight > 0)
- && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
- }
-
- public void setRenderMode(int renderMode) {
- if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
- throw new IllegalArgumentException("renderMode");
- }
- synchronized(sGLThreadManager) {
- mRenderMode = renderMode;
- sGLThreadManager.notifyAll();
+ private void doResume() {
+ mPaused = false;
+ updateState();
+ if (mRenderMode == RENDERMODE_WHEN_DIRTY) {
+ executeDraw();
}
}
- public int getRenderMode() {
- synchronized(sGLThreadManager) {
- return mRenderMode;
- }
+ private void doWindowResize(final int width, final int height) {
+ // we were not drawing yet. Update the window size and
+ // state and attempt to draw a frame.
+ mSizeChanged = (mWidth != width || mHeight != height);
+ mWidth = width;
+ mHeight = height;
+ updateState();
+ // we always (attempt to) draw a frame before returning
+ executeDraw();
}
- public void requestRender() {
- synchronized(sGLThreadManager) {
- mRequestRender = true;
- sGLThreadManager.notifyAll();
- }
+ private void doSetRenderMode(final int renderMode) {
+ mRenderMode = renderMode;
+ requestRender();
}
+ /*
+ * the call-backs below run on the main UI thread, they just
+ * wait while executing work on the GLThread.
+ */
+
public void surfaceCreated() {
- synchronized(sGLThreadManager) {
- if (LOG_THREADS) {
- Log.i("GLThread", "surfaceCreated tid=" + getId());
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doSurfaceCreated();
}
- mHasSurface = true;
- mFinishedCreatingEglSurface = false;
- sGLThreadManager.notifyAll();
- while (mWaitingForSurface
- && !mFinishedCreatingEglSurface
- && !mExited) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- }
+ }, 0);
}
public void surfaceDestroyed() {
- synchronized(sGLThreadManager) {
- if (LOG_THREADS) {
- Log.i("GLThread", "surfaceDestroyed tid=" + getId());
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doSurfaceDestroyed();
}
- mHasSurface = false;
- sGLThreadManager.notifyAll();
- while((!mWaitingForSurface) && (!mExited)) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- }
+ }, 0);
}
public void onPause() {
- synchronized (sGLThreadManager) {
- if (LOG_PAUSE_RESUME) {
- Log.i("GLThread", "onPause tid=" + getId());
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doPause();
}
- mRequestPaused = true;
- sGLThreadManager.notifyAll();
- while ((! mExited) && (! mPaused)) {
- if (LOG_PAUSE_RESUME) {
- Log.i("Main thread", "onPause waiting for mPaused.");
- }
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
- }
- }
+ }, 0);
}
public void onResume() {
- synchronized (sGLThreadManager) {
- if (LOG_PAUSE_RESUME) {
- Log.i("GLThread", "onResume tid=" + getId());
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doResume();
}
- mRequestPaused = false;
- mRequestRender = true;
- mRenderComplete = false;
- sGLThreadManager.notifyAll();
- while ((! mExited) && mPaused && (!mRenderComplete)) {
- if (LOG_PAUSE_RESUME) {
- Log.i("Main thread", "onResume waiting for !mPaused.");
- }
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
- }
- }
+ }, 0);
}
public void onWindowResize(int w, int h) {
- synchronized (sGLThreadManager) {
- mWidth = w;
- mHeight = h;
- mSizeChanged = true;
- mRequestRender = true;
- mRenderComplete = false;
- sGLThreadManager.notifyAll();
-
- // Wait for thread to react to resize and render a frame
- while (! mExited && !mPaused && !mRenderComplete
- && ableToDraw()) {
- if (LOG_SURFACE) {
- Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId());
- }
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
+ final int width = w;
+ final int height = h;
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doWindowResize(width, height);
}
+ }, 0);
+ }
+
+ /*
+ * the methods below can be called from any thread
+ */
+
+ public void requestRender() {
+ if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
+ mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
+ mExecuteDrawAction, null);
+ } else {
+ /*
+ * in RENDERMODE_WHEN_DIRTY we schedule the draw callback
+ * immediately because the developer is manager her
+ * timing loop manually -- in particular she could be
+ * using the Choreographer already.
+ */
+ mGLHandler.post(mExecuteDrawAction);
}
}
+ public void setRenderMode(final int renderMode) {
+ mGLHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ doSetRenderMode(renderMode);
+ }
+ }, 0);
+ }
+
public void requestExitAndWait() {
- // don't call this from GLThread thread or it is a guaranteed
- // deadlock!
- synchronized(sGLThreadManager) {
- mShouldExit = true;
- sGLThreadManager.notifyAll();
- while (! mExited) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- }
- }
+ getLooper().quit();
+ try {
+ this.join();
+ } catch (InterruptedException e) {
}
}
-
- public void requestReleaseEglContextLocked() {
- mShouldReleaseEglContext = true;
- sGLThreadManager.notifyAll();
- }
-
- /**
- * Queue an "event" to be run on the GL rendering thread.
- * @param r the runnable to be run on the GL rendering thread.
- */
- public void queueEvent(Runnable r) {
- if (r == null) {
- throw new IllegalArgumentException("r must not be null");
- }
- synchronized(sGLThreadManager) {
- mEventQueue.add(r);
- sGLThreadManager.notifyAll();
- }
- }
-
- // Once the thread is started, all accesses to the following member
- // variables are protected by the sGLThreadManager monitor
- private boolean mShouldExit;
- private boolean mExited;
- private boolean mRequestPaused;
- private boolean mPaused;
- private boolean mHasSurface;
- private boolean mSurfaceIsBad;
- private boolean mWaitingForSurface;
- private boolean mHaveEglContext;
- private boolean mHaveEglSurface;
- private boolean mFinishedCreatingEglSurface;
- private boolean mShouldReleaseEglContext;
- private int mWidth;
- private int mHeight;
- private int mRenderMode;
- private boolean mRequestRender;
- private boolean mRenderComplete;
- private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
- private boolean mSizeChanged = true;
-
- // End of member variables protected by the sGLThreadManager monitor.
-
- private EglHelper mEglHelper;
-
- /**
- * Set once at thread construction time, nulled out when the parent view is garbage
- * called. This weak reference allows the GLSurfaceView to be garbage collected while
- * the GLThread is still alive.
- */
- private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
-
- }
+ } // class GLThread
static class LogWriter extends Writer {
-
- @Override public void close() {
+ @Override
+ public void close() {
flushBuilder();
}
- @Override public void flush() {
+ @Override
+ public void flush() {
flushBuilder();
}
- @Override public void write(char[] buf, int offset, int count) {
- for(int i = 0; i < count; i++) {
+ @Override
+ public void write(char[] buf, int offset, int count) {
+ for (int i = 0; i < count; i++) {
char c = buf[offset + i];
- if ( c == '\n') {
+ if (c == '\n') {
flushBuilder();
- }
- else {
+ } else {
mBuilder.append(c);
}
}
@@ -1792,7 +1641,7 @@
private void flushBuilder() {
if (mBuilder.length() > 0) {
- Log.v("GLSurfaceView", mBuilder.toString());
+ Log.v(TAG, mBuilder.toString());
mBuilder.delete(0, mBuilder.length());
}
}
@@ -1800,141 +1649,10 @@
private StringBuilder mBuilder = new StringBuilder();
}
-
private void checkRenderThreadState() {
if (mGLThread != null) {
throw new IllegalStateException(
"setRenderer has already been called for this instance.");
}
}
-
- private static class GLThreadManager {
- private static String TAG = "GLThreadManager";
-
- public synchronized void threadExiting(GLThread thread) {
- if (LOG_THREADS) {
- Log.i("GLThread", "exiting tid=" + thread.getId());
- }
- thread.mExited = true;
- if (mEglOwner == thread) {
- mEglOwner = null;
- }
- notifyAll();
- }
-
- /*
- * Tries once to acquire the right to use an EGL
- * context. Does not block. Requires that we are already
- * in the sGLThreadManager monitor when this is called.
- *
- * @return true if the right to use an EGL context was acquired.
- */
- public boolean tryAcquireEglContextLocked(GLThread thread) {
- if (mEglOwner == thread || mEglOwner == null) {
- mEglOwner = thread;
- notifyAll();
- return true;
- }
- checkGLESVersion();
- if (mMultipleGLESContextsAllowed) {
- return true;
- }
- // Notify the owning thread that it should release the context.
- // TODO: implement a fairness policy. Currently
- // if the owning thread is drawing continuously it will just
- // reacquire the EGL context.
- if (mEglOwner != null) {
- mEglOwner.requestReleaseEglContextLocked();
- }
- return false;
- }
-
- /*
- * Releases the EGL context. Requires that we are already in the
- * sGLThreadManager monitor when this is called.
- */
- public void releaseEglContextLocked(GLThread thread) {
- if (mEglOwner == thread) {
- mEglOwner = null;
- }
- notifyAll();
- }
-
- public synchronized boolean shouldReleaseEGLContextWhenPausing() {
- // Release the EGL context when pausing even if
- // the hardware supports multiple EGL contexts.
- // Otherwise the device could run out of EGL contexts.
- return mLimitedGLESContexts;
- }
-
- public synchronized boolean shouldTerminateEGLWhenPausing() {
- checkGLESVersion();
- return !mMultipleGLESContextsAllowed;
- }
-
- public synchronized void checkGLDriver(GL10 gl) {
- if (! mGLESDriverCheckComplete) {
- checkGLESVersion();
- String renderer = gl.glGetString(GL10.GL_RENDERER);
- if (mGLESVersion < kGLES_20) {
- mMultipleGLESContextsAllowed =
- ! renderer.startsWith(kMSM7K_RENDERER_PREFIX);
- notifyAll();
- }
- mLimitedGLESContexts = !mMultipleGLESContextsAllowed;
- if (LOG_SURFACE) {
- Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = "
- + mMultipleGLESContextsAllowed
- + " mLimitedGLESContexts = " + mLimitedGLESContexts);
- }
- mGLESDriverCheckComplete = true;
- }
- }
-
- private void checkGLESVersion() {
- if (! mGLESVersionCheckComplete) {
- mGLESVersion = SystemProperties.getInt(
- "ro.opengles.version",
- ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
- if (mGLESVersion >= kGLES_20) {
- mMultipleGLESContextsAllowed = true;
- }
- if (LOG_SURFACE) {
- Log.w(TAG, "checkGLESVersion mGLESVersion =" +
- " " + mGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed);
- }
- mGLESVersionCheckComplete = true;
- }
- }
-
- /**
- * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides
- * support for hardware-accelerated views, therefore multiple EGL contexts are
- * supported on all Android 3.0+ EGL drivers.
- */
- private boolean mGLESVersionCheckComplete;
- private int mGLESVersion;
- private boolean mGLESDriverCheckComplete;
- private boolean mMultipleGLESContextsAllowed;
- private boolean mLimitedGLESContexts;
- private static final int kGLES_20 = 0x20000;
- private static final String kMSM7K_RENDERER_PREFIX =
- "Q3Dimension MSM7500 ";
- private GLThread mEglOwner;
- }
-
- private static final GLThreadManager sGLThreadManager = new GLThreadManager();
-
- private final WeakReference<GLSurfaceView> mThisWeakRef =
- new WeakReference<GLSurfaceView>(this);
- private GLThread mGLThread;
- private Renderer mRenderer;
- private boolean mDetached;
- private EGLConfigChooser mEGLConfigChooser;
- private EGLContextFactory mEGLContextFactory;
- private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
- private GLWrapper mGLWrapper;
- private int mDebugFlags;
- private int mEGLContextClientVersion;
- private boolean mPreserveEGLContextOnPause;
}
diff --git a/packages/DefaultContainerService/jni/Android.mk b/packages/DefaultContainerService/jni/Android.mk
index 79ff451..ef4f699 100644
--- a/packages/DefaultContainerService/jni/Android.mk
+++ b/packages/DefaultContainerService/jni/Android.mk
@@ -28,7 +28,8 @@
LOCAL_SHARED_LIBRARIES := \
libnativehelper \
- libutils
+ libutils \
+ liblog
LOCAL_STATIC_LIBRARIES := \
libdiskusage
@@ -36,4 +37,4 @@
LOCAL_MODULE := libdefcontainer_jni
LOCAL_MODULE_TAGS := optional
-include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/common_time/Android.mk b/services/common_time/Android.mk
index 0606ab4..75eb528 100644
--- a/services/common_time/Android.mk
+++ b/services/common_time/Android.mk
@@ -27,7 +27,8 @@
LOCAL_SHARED_LIBRARIES := \
libbinder \
libcommon_time_client \
- libutils
+ libutils \
+ liblog
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := common_time
diff --git a/services/input/Android.mk b/services/input/Android.mk
index 159800f..5d913f3 100644
--- a/services/input/Android.mk
+++ b/services/input/Android.mk
@@ -29,6 +29,7 @@
LOCAL_SHARED_LIBRARIES := \
libcutils \
+ liblog \
libandroidfw \
libutils \
libhardware \
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 0773afb..f4e1cec 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -40,7 +40,6 @@
#include <androidfw/KeyCharacterMap.h>
#include <androidfw/VirtualKeyMap.h>
-#include <sha1.h>
#include <string.h>
#include <stdint.h>
#include <dirent.h>
@@ -49,6 +48,7 @@
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/limits.h>
+#include <sys/sha1.h>
/* this macro is used to tell if "bit" is set in "array"
* it selects a byte from the array, and does a boolean AND
@@ -162,7 +162,8 @@
next(NULL),
fd(fd), id(id), path(path), identifier(identifier),
classes(0), configuration(NULL), virtualKeyMap(NULL),
- ffEffectPlaying(false), ffEffectId(-1) {
+ ffEffectPlaying(false), ffEffectId(-1),
+ timestampOverrideSec(0), timestampOverrideUsec(0) {
memset(keyBitmask, 0, sizeof(keyBitmask));
memset(absBitmask, 0, sizeof(absBitmask));
memset(relBitmask, 0, sizeof(relBitmask));
@@ -766,12 +767,37 @@
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
- const struct input_event& iev = readBuffer[i];
- ALOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",
+ struct input_event& iev = readBuffer[i];
+ ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
device->path.string(),
(int) iev.time.tv_sec, (int) iev.time.tv_usec,
iev.type, iev.code, iev.value);
+ // Some input devices may have a better concept of the time
+ // when an input event was actually generated than the kernel
+ // which simply timestamps all events on entry to evdev.
+ // This is a custom Android extension of the input protocol
+ // mainly intended for use with uinput based device drivers.
+ if (iev.type == EV_MSC) {
+ if (iev.code == MSC_ANDROID_TIME_SEC) {
+ device->timestampOverrideSec = iev.value;
+ continue;
+ } else if (iev.code == MSC_ANDROID_TIME_USEC) {
+ device->timestampOverrideUsec = iev.value;
+ continue;
+ }
+ }
+ if (device->timestampOverrideSec || device->timestampOverrideUsec) {
+ iev.time.tv_sec = device->timestampOverrideSec;
+ iev.time.tv_usec = device->timestampOverrideUsec;
+ if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
+ device->timestampOverrideSec = 0;
+ device->timestampOverrideUsec = 0;
+ }
+ ALOGV("applied override time %d.%06d",
+ int(iev.time.tv_sec), int(iev.time.tv_usec));
+ }
+
#ifdef HAVE_POSIX_CLOCKS
// Use the time specified in the event instead of the current time
// so that downstream code can get more accurate estimates of
@@ -829,8 +855,8 @@
event->code = iev.code;
event->value = iev.value;
event += 1;
+ capacity -= 1;
}
- capacity -= count;
if (capacity == 0) {
// The result buffer is full. Reset the pending event index
// so we will try to read the device again on the next iteration.
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index afc12ef..c93fc7a 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -42,6 +42,20 @@
#define BTN_FIRST 0x100 // first button code
#define BTN_LAST 0x15f // last button code
+/*
+ * These constants are used privately in Android to pass raw timestamps
+ * through evdev from uinput device drivers because there is currently no
+ * other way to transfer this information. The evdev driver automatically
+ * timestamps all input events with the time they were posted and clobbers
+ * whatever information was passed in.
+ *
+ * For the purposes of this hack, the timestamp is specified in the
+ * CLOCK_MONOTONIC timebase and is split into two EV_MSC events specifying
+ * seconds and microseconds.
+ */
+#define MSC_ANDROID_TIME_SEC 0x6
+#define MSC_ANDROID_TIME_USEC 0x7
+
namespace android {
enum {
@@ -329,6 +343,9 @@
bool ffEffectPlaying;
int16_t ffEffectId; // initially -1
+ int32_t timestampOverrideSec;
+ int32_t timestampOverrideUsec;
+
Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier);
~Device();
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 602afd4..ab38ed20 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -2701,6 +2701,12 @@
mPointerYZoomScale);
dump.appendFormat(INDENT4 "MaxSwipeWidth: %f\n",
mPointerGestureMaxSwipeWidth);
+ } else if (mDeviceMode == DEVICE_MODE_NAVIGATION) {
+ dump.appendFormat(INDENT3 "Navigation Gesture Detector:\n");
+ dump.appendFormat(INDENT4 "AssistStartY: %0.3f\n",
+ mNavigationAssistStartY);
+ dump.appendFormat(INDENT4 "AssistEndY: %0.3f\n",
+ mNavigationAssistEndY);
}
}
@@ -2895,7 +2901,7 @@
}
} else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
- mDeviceMode = DEVICE_MODE_UNSCALED;
+ mDeviceMode = DEVICE_MODE_NAVIGATION;
} else {
mSource = AINPUT_SOURCE_TOUCHPAD;
mDeviceMode = DEVICE_MODE_UNSCALED;
@@ -3243,8 +3249,8 @@
break;
}
- // Compute pointer gesture detection parameters.
if (mDeviceMode == DEVICE_MODE_POINTER) {
+ // Compute pointer gesture detection parameters.
float rawDiagonal = hypotf(rawWidth, rawHeight);
float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);
@@ -3269,10 +3275,14 @@
// translated into freeform gestures.
mPointerGestureMaxSwipeWidth =
mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
- }
- // Abort current pointer usages because the state has changed.
- abortPointerUsage(when, 0 /*policyFlags*/);
+ // Abort current pointer usages because the state has changed.
+ abortPointerUsage(when, 0 /*policyFlags*/);
+ } else if (mDeviceMode == DEVICE_MODE_NAVIGATION) {
+ // Compute navigation parameters.
+ mNavigationAssistStartY = mSurfaceHeight * 0.9f;
+ mNavigationAssistEndY = mSurfaceHeight * 0.5f;
+ }
// Inform the dispatcher about the changes.
*outResetNeeded = true;
@@ -3611,6 +3621,7 @@
mPointerGesture.reset();
mPointerSimple.reset();
+ mNavigation.reset();
if (mPointerController != NULL) {
mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
@@ -3761,6 +3772,8 @@
mPointerController->setSpots(mCurrentCookedPointerData.pointerCoords,
mCurrentCookedPointerData.idToIndex,
mCurrentCookedPointerData.touchingIdBits);
+ } else if (mDeviceMode == DEVICE_MODE_NAVIGATION) {
+ dispatchNavigationAssist(when, policyFlags);
}
dispatchHoverExit(when, policyFlags);
@@ -5482,6 +5495,44 @@
dispatchPointerSimple(when, policyFlags, false, false);
}
+void TouchInputMapper::dispatchNavigationAssist(nsecs_t when, uint32_t policyFlags) {
+ if (mCurrentCookedPointerData.touchingIdBits.count() == 1) {
+ if (mLastCookedPointerData.touchingIdBits.isEmpty()) {
+ // First pointer down.
+ uint32_t id = mCurrentCookedPointerData.touchingIdBits.firstMarkedBit();
+ const PointerCoords& coords = mCurrentCookedPointerData.pointerCoordsForId(id);
+ if (coords.getY() >= mNavigationAssistStartY) {
+ // Start tracking the possible assist swipe.
+ mNavigation.activeAssistId = id;
+ return;
+ }
+ } else if (mNavigation.activeAssistId >= 0
+ && mCurrentCookedPointerData.touchingIdBits.hasBit(mNavigation.activeAssistId)) {
+ const PointerCoords& coords = mCurrentCookedPointerData.pointerCoordsForId(
+ mNavigation.activeAssistId);
+ if (coords.getY() > mNavigationAssistEndY) {
+ // Swipe is still in progress.
+ return;
+ }
+
+ // Detected assist swipe.
+ int32_t metaState = mContext->getGlobalMetaState();
+ NotifyKeyArgs downArgs(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
+ policyFlags | POLICY_FLAG_VIRTUAL,
+ AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_ASSIST, 0, metaState, when);
+ getListener()->notifyKey(&downArgs);
+
+ NotifyKeyArgs upArgs(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
+ policyFlags | POLICY_FLAG_VIRTUAL,
+ AKEY_EVENT_ACTION_UP, 0, AKEYCODE_ASSIST, 0, metaState, when);
+ getListener()->notifyKey(&upArgs);
+ }
+ }
+
+ // Cancel the assist swipe.
+ mNavigation.activeAssistId = -1;
+}
+
void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
const PointerProperties* properties, const PointerCoords* coords,
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 8a52c06..312f19b 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -791,6 +791,10 @@
void clear();
void copyFrom(const CookedPointerData& other);
+ inline const PointerCoords& pointerCoordsForId(uint32_t id) const {
+ return pointerCoords[idToIndex[id]];
+ }
+
inline bool isHovering(uint32_t pointerIndex) {
return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id);
}
@@ -1180,6 +1184,7 @@
DEVICE_MODE_DISABLED, // input is disabled
DEVICE_MODE_DIRECT, // direct mapping (touchscreen)
DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad)
+ DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
DEVICE_MODE_POINTER, // pointer mapping (pointer)
};
DeviceMode mDeviceMode;
@@ -1432,6 +1437,10 @@
// The maximum swipe width.
float mPointerGestureMaxSwipeWidth;
+ // The start and end Y thresholds for invoking the assist navigation swipe.
+ float mNavigationAssistStartY;
+ float mNavigationAssistEndY;
+
struct PointerDistanceHeapElement {
uint32_t currentPointerIndex : 8;
uint32_t lastPointerIndex : 8;
@@ -1606,6 +1615,15 @@
}
} mPointerSimple;
+ struct Navigation {
+ // The id of a pointer that is tracking a possible assist swipe.
+ int32_t activeAssistId; // -1 if none
+
+ void reset() {
+ activeAssistId = -1;
+ }
+ } mNavigation;
+
// The pointer and scroll velocity controls.
VelocityControl mPointerVelocityControl;
VelocityControl mWheelXVelocityControl;
@@ -1641,6 +1659,8 @@
bool down, bool hovering);
void abortPointerSimple(nsecs_t when, uint32_t policyFlags);
+ void dispatchNavigationAssist(nsecs_t when, uint32_t policyFlags);
+
// Dispatches a motion event.
// If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
// method will take care of setting the index and transmuting the action to DOWN or UP
diff --git a/services/input/tests/Android.mk b/services/input/tests/Android.mk
index 8f8c34b..211e64b 100644
--- a/services/input/tests/Android.mk
+++ b/services/input/tests/Android.mk
@@ -9,6 +9,7 @@
shared_libraries := \
libcutils \
+ liblog \
libandroidfw \
libutils \
libhardware \
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 9e06db8..ffc3672 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -321,12 +321,11 @@
// track the current default http proxy - tell the world if we get a new one (real change)
private ProxyProperties mDefaultProxy = null;
- private Object mDefaultProxyLock = new Object();
+ private Object mProxyLock = new Object();
private boolean mDefaultProxyDisabled = false;
// track the global proxy.
private ProxyProperties mGlobalProxy = null;
- private final Object mGlobalProxyLock = new Object();
private SettingsObserver mSettingsObserver;
@@ -3039,14 +3038,15 @@
// so this API change wouldn't have a benifit. It also breaks the passing
// of proxy info to all the JVMs.
// enforceAccessPermission();
- synchronized (mDefaultProxyLock) {
- return mDefaultProxyDisabled ? null : mDefaultProxy;
+ synchronized (mProxyLock) {
+ if (mGlobalProxy != null) return mGlobalProxy;
+ return (mDefaultProxyDisabled ? null : mDefaultProxy);
}
}
public void setGlobalProxy(ProxyProperties proxyProperties) {
enforceChangePermission();
- synchronized (mGlobalProxyLock) {
+ synchronized (mProxyLock) {
if (proxyProperties == mGlobalProxy) return;
if (proxyProperties != null && proxyProperties.equals(mGlobalProxy)) return;
if (mGlobalProxy != null && mGlobalProxy.equals(proxyProperties)) return;
@@ -3072,7 +3072,7 @@
if (mGlobalProxy == null) {
proxyProperties = mDefaultProxy;
}
- //sendProxyBroadcast(proxyProperties);
+ sendProxyBroadcast(proxyProperties);
}
private void loadGlobalProxy() {
@@ -3083,7 +3083,7 @@
Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
if (!TextUtils.isEmpty(host)) {
ProxyProperties proxyProperties = new ProxyProperties(host, port, exclList);
- synchronized (mGlobalProxyLock) {
+ synchronized (mProxyLock) {
mGlobalProxy = proxyProperties;
}
}
@@ -3094,7 +3094,7 @@
// so this API change wouldn't have a benifit. It also breaks the passing
// of proxy info to all the JVMs.
// enforceAccessPermission();
- synchronized (mGlobalProxyLock) {
+ synchronized (mProxyLock) {
return mGlobalProxy;
}
}
@@ -3103,11 +3103,12 @@
if (proxy != null && TextUtils.isEmpty(proxy.getHost())) {
proxy = null;
}
- synchronized (mDefaultProxyLock) {
+ synchronized (mProxyLock) {
if (mDefaultProxy != null && mDefaultProxy.equals(proxy)) return;
- if (mDefaultProxy == proxy) return;
+ if (mDefaultProxy == proxy) return; // catches repeated nulls
mDefaultProxy = proxy;
+ if (mGlobalProxy != null) return;
if (!mDefaultProxyDisabled) {
sendProxyBroadcast(proxy);
}
@@ -3350,10 +3351,10 @@
mDnsOverridden = true;
}
- // Temporarily disable the default proxy.
- synchronized (mDefaultProxyLock) {
+ // Temporarily disable the default proxy (not global).
+ synchronized (mProxyLock) {
mDefaultProxyDisabled = true;
- if (mDefaultProxy != null) {
+ if (mGlobalProxy == null && mDefaultProxy != null) {
sendProxyBroadcast(null);
}
}
@@ -3368,9 +3369,9 @@
mHandler.sendEmptyMessage(EVENT_RESTORE_DNS);
}
}
- synchronized (mDefaultProxyLock) {
+ synchronized (mProxyLock) {
mDefaultProxyDisabled = false;
- if (mDefaultProxy != null) {
+ if (mGlobalProxy == null && mDefaultProxy != null) {
sendProxyBroadcast(mDefaultProxy);
}
}
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
index 297324b..6293dc6 100644
--- a/services/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -4,6 +4,7 @@
import android.graphics.Matrix;
import android.util.Slog;
+import android.util.TimeUtils;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -30,6 +31,11 @@
// Protect with mAnimator.
boolean freezingScreen;
+ /**
+ * How long we last kept the screen frozen.
+ */
+ int lastFreezeDuration;
+
// Offset to the window of all layers in the token, for use by
// AppWindowToken animations.
int animLayerAdjustment;
@@ -287,6 +293,10 @@
pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen);
pw.print(" allDrawn="); pw.print(allDrawn);
pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment);
+ if (lastFreezeDuration != 0) {
+ pw.print(prefix); pw.print("lastFreezeDuration=");
+ TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println();
+ }
if (animating || animation != null) {
pw.print(prefix); pw.print("animating="); pw.println(animating);
pw.print(prefix); pw.print("animation="); pw.println(animation);
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 3964782..054a075 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -70,6 +70,7 @@
int mAboveUniverseLayer = 0;
int mBulkUpdateParams = 0;
+ Object mLastWindowFreezeSource;
SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators =
new SparseArray<WindowAnimator.DisplayContentsAnimator>();
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index af603fd..1d1fda5 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -43,6 +43,7 @@
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import android.app.AppOpsManager;
+import android.util.TimeUtils;
import android.view.IWindowId;
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
@@ -464,6 +465,9 @@
boolean mTraversalScheduled = false;
boolean mDisplayFrozen = false;
+ long mDisplayFreezeTime = 0;
+ int mLastDisplayFreezeDuration = 0;
+ Object mLastFinishedFreezeSource = null;
boolean mWaitingForConfig = false;
boolean mWindowsFreezingScreen = false;
boolean mClientFreezingScreen = false;
@@ -582,6 +586,7 @@
boolean mWallpaperForceHidingChanged = false;
boolean mWallpaperMayChange = false;
boolean mOrientationChangeComplete = true;
+ Object mLastWindowFreezeSource = null;
private Session mHoldScreen = null;
private boolean mObscured = false;
boolean mDimming = false;
@@ -3590,7 +3595,10 @@
synchronized(mWindowMap) {
mCurConfiguration = new Configuration(config);
- mWaitingForConfig = false;
+ if (mWaitingForConfig) {
+ mWaitingForConfig = false;
+ mLastFinishedFreezeSource = "new-config";
+ }
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -4209,6 +4217,7 @@
w.mOrientationChanging = true;
mInnerFields.mOrientationChangeComplete = false;
}
+ w.mLastFreezeDuration = 0;
unfrozeWindows = true;
w.mDisplayContent.layoutNeeded = true;
}
@@ -4216,7 +4225,10 @@
if (force || unfrozeWindows) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "No longer freezing: " + wtoken);
wtoken.mAppAnimator.freezingScreen = false;
+ wtoken.mAppAnimator.lastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mDisplayFreezeTime);
mAppsFreezingScreen--;
+ mLastFinishedFreezeSource = wtoken;
}
if (unfreezeSurfaceNow) {
if (unfrozeWindows) {
@@ -4242,6 +4254,7 @@
if (!wtoken.hiddenRequested) {
if (!wtoken.mAppAnimator.freezingScreen) {
wtoken.mAppAnimator.freezingScreen = true;
+ wtoken.mAppAnimator.lastFreezeDuration = 0;
mAppsFreezingScreen++;
if (mAppsFreezingScreen == 1) {
startFreezingDisplayLocked(false, 0, 0);
@@ -4750,6 +4763,7 @@
synchronized(mWindowMap) {
if (mClientFreezingScreen) {
mClientFreezingScreen = false;
+ mLastFinishedFreezeSource = "client";
final long origId = Binder.clearCallingIdentity();
try {
stopFreezingDisplayLocked();
@@ -5742,6 +5756,7 @@
w.mOrientationChanging = true;
mInnerFields.mOrientationChangeComplete = false;
}
+ w.mLastFreezeDuration = 0;
}
for (int i=mRotationWatchers.size()-1; i>=0; i--) {
@@ -6240,6 +6255,7 @@
if (config == null && mWaitingForConfig) {
// Nothing changed but we are waiting for something... stop that!
mWaitingForConfig = false;
+ mLastFinishedFreezeSource = "new-config";
performLayoutAndPlaceSurfacesLocked();
}
return config;
@@ -7036,6 +7052,8 @@
WindowState w = windows.get(i);
if (w.mOrientationChanging) {
w.mOrientationChanging = false;
+ w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mDisplayFreezeTime);
Slog.w(TAG, "Force clearing orientation change: " + w);
}
}
@@ -7112,6 +7130,7 @@
synchronized (mWindowMap) {
if (mClientFreezingScreen) {
mClientFreezingScreen = false;
+ mLastFinishedFreezeSource = "client-timeout";
stopFreezingDisplayLocked();
}
}
@@ -8029,6 +8048,7 @@
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Changing surface while display frozen: " + w);
w.mOrientationChanging = true;
+ w.mLastFreezeDuration = 0;
mInnerFields.mOrientationChangeComplete = false;
if (!mWindowsFreezingScreen) {
mWindowsFreezingScreen = true;
@@ -8417,6 +8437,8 @@
"Orientation not waiting for draw in "
+ w + ", surface " + winAnimator.mSurfaceControl);
w.mOrientationChanging = false;
+ w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mDisplayFreezeTime);
}
}
}
@@ -8930,6 +8952,8 @@
winAnimator.mSurfaceResized = false;
} catch (RemoteException e) {
win.mOrientationChanging = false;
+ win.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+ - mDisplayFreezeTime);
}
mResizingWindows.remove(i);
}
@@ -8940,6 +8964,7 @@
if (mInnerFields.mOrientationChangeComplete) {
if (mWindowsFreezingScreen) {
mWindowsFreezingScreen = false;
+ mLastFinishedFreezeSource = mInnerFields.mLastWindowFreezeSource;
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
}
stopFreezingDisplayLocked();
@@ -9226,6 +9251,7 @@
mInnerFields.mOrientationChangeComplete = false;
} else {
mInnerFields.mOrientationChangeComplete = true;
+ mInnerFields.mLastWindowFreezeSource = mAnimator.mLastWindowFreezeSource;
if (mWindowsFreezingScreen) {
doRequest = true;
}
@@ -9498,6 +9524,8 @@
mScreenFrozenLock.acquire();
mDisplayFrozen = true;
+ mDisplayFreezeTime = SystemClock.elapsedRealtime();
+ mLastFinishedFreezeSource = null;
mInputMonitor.freezeInputDispatchingLw();
@@ -9552,6 +9580,15 @@
}
mDisplayFrozen = false;
+ mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime);
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Screen frozen for ");
+ TimeUtils.formatDuration(mLastDisplayFreezeDuration, sb);
+ if (mLastFinishedFreezeSource != null) {
+ sb.append(" due to ");
+ sb.append(mLastFinishedFreezeSource);
+ }
+ Slog.i(TAG, sb.toString());
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
if (PROFILE_ORIENTATION) {
@@ -10076,6 +10113,13 @@
}
pw.print(" mInTouchMode="); pw.print(mInTouchMode);
pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
+ pw.print(" mLastDisplayFreezeDuration=");
+ TimeUtils.formatDuration(mLastDisplayFreezeDuration, pw);
+ if ( mLastFinishedFreezeSource != null) {
+ pw.print(" due to ");
+ pw.print(mLastFinishedFreezeSource);
+ }
+ pw.println();
if (dumpAll) {
pw.print(" mSystemDecorRect="); pw.print(mSystemDecorRect.toShortString());
pw.print(" mSystemDecorLayer="); pw.print(mSystemDecorLayer);
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 506fcec..ca060f4 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -26,6 +26,7 @@
import android.app.AppOpsManager;
import android.os.RemoteCallbackList;
+import android.util.TimeUtils;
import android.view.IWindowFocusObserver;
import android.view.IWindowId;
import com.android.server.input.InputWindowHandle;
@@ -266,6 +267,11 @@
*/
boolean mOrientationChanging;
+ /**
+ * How long we last kept the screen frozen.
+ */
+ int mLastFreezeDuration;
+
/** Is this window now (or just being) removed? */
boolean mRemoved;
@@ -1387,6 +1393,10 @@
pw.print(" mAppFreezing="); pw.print(mAppFreezing);
pw.print(" mTurnOnScreen="); pw.println(mTurnOnScreen);
}
+ if (mLastFreezeDuration != 0) {
+ pw.print(prefix); pw.print("mLastFreezeDuration=");
+ TimeUtils.formatDuration(mLastFreezeDuration, pw); pw.println();
+ }
if (mHScale != 1 || mVScale != 1) {
pw.print(prefix); pw.print("mHScale="); pw.print(mHScale);
pw.print(" mVScale="); pw.println(mVScale);
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 3a9f7cb..452f76d 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -1307,6 +1307,7 @@
if (w.mOrientationChanging) {
if (!w.isDrawnLw()) {
mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
+ mAnimator.mLastWindowFreezeSource = w;
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation continue waiting for draw in " + w);
} else {
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index d097a93..b313d48 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -32,6 +32,7 @@
libandroid_runtime \
libandroidfw \
libcutils \
+ liblog \
libhardware \
libhardware_legacy \
libnativehelper \
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 6e2a70d..31e01c0 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -162,13 +162,13 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("CdmaCellIdentitiy:");
- sb.append(super.toString());
+ StringBuilder sb = new StringBuilder("CellIdentitiyCdma:{");
sb.append(" mNetworkId="); sb.append(mNetworkId);
sb.append(" mSystemId="); sb.append(mSystemId);
sb.append(" mBasestationId="); sb.append(mBasestationId);
sb.append(" mLongitude="); sb.append(mLongitude);
sb.append(" mLatitude="); sb.append(mLatitude);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index bda96be..98113e7 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -147,13 +147,13 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("GsmCellIdentitiy:");
- sb.append(super.toString());
+ StringBuilder sb = new StringBuilder("CellIdentitiyGsm:{");
sb.append(" mMcc=").append(mMcc);
- sb.append(" mMnc=").append(mMcc);
+ sb.append(" mMnc=").append(mMnc);
sb.append(" mLac=").append(mLac);
sb.append(" mCid=").append(mCid);
sb.append(" mPsc=").append(mPsc);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index f72d583..86924bd 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -142,13 +142,13 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("LteCellIdentitiy:");
- sb.append(super.toString());
+ StringBuilder sb = new StringBuilder("CellIdentitiyLte:{");
sb.append(" mMcc="); sb.append(mMcc);
sb.append(" mMnc="); sb.append(mMnc);
sb.append(" mCi="); sb.append(mCi);
sb.append(" mPci="); sb.append(mPci);
sb.append(" mTac="); sb.append(mTac);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index f367f99..fe3c68b 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -149,7 +149,7 @@
StringBuffer sb = new StringBuffer();
String timeStampType;
- sb.append(" mRegistered=").append(mRegistered ? "YES" : "NO");
+ sb.append("mRegistered=").append(mRegistered ? "YES" : "NO");
timeStampType = timeStampTypeToString(mTimeStampType);
sb.append(" mTimeStampType=").append(timeStampType);
sb.append(" mTimeStamp=").append(mTimeStamp).append("ns");
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index a5d6e9c..6f2f1f6 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -87,10 +87,11 @@
public String toString() {
StringBuffer sb = new StringBuffer();
- sb.append("CellInfoCdma:");
+ sb.append("CellInfoCdma:{");
sb.append(super.toString());
- sb.append(", ").append(mCellIdentityCdma);
- sb.append(", ").append(mCellSignalStrengthCdma);
+ sb.append(" ").append(mCellIdentityCdma);
+ sb.append(" ").append(mCellSignalStrengthCdma);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index bf0eca8..1bedddb 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -87,10 +87,11 @@
public String toString() {
StringBuffer sb = new StringBuffer();
- sb.append("CellInfoGsm:");
+ sb.append("CellInfoGsm:{");
sb.append(super.toString());
- sb.append(", ").append(mCellIdentityGsm);
- sb.append(", ").append(mCellSignalStrengthGsm);
+ sb.append(" ").append(mCellIdentityGsm);
+ sb.append(" ").append(mCellSignalStrengthGsm);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index d7a58b6..287c9f0 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -91,10 +91,11 @@
public String toString() {
StringBuffer sb = new StringBuffer();
- sb.append("CellInfoLte:");
+ sb.append("CellInfoLte:{");
sb.append(super.toString());
- sb.append(", ").append(mCellIdentityLte);
- sb.append(", ").append(mCellSignalStrengthLte);
+ sb.append(" ").append(mCellIdentityLte);
+ sb.append(" ").append(mCellSignalStrengthLte);
+ sb.append("}");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4aee902..6400e68 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1341,7 +1341,8 @@
}
/**
- * Returns all observed cell information of the device.
+ * Returns all observed cell information of the device. This does
+ * not cause or change the rate of PhoneStateListner#onCellInfoChanged.
*
* @return List of CellInfo or null if info unavailable.
*
@@ -1357,4 +1358,24 @@
return null;
}
}
+
+ /**
+ * Sets the minimum time in milli-seconds between {@link PhoneStateListener#onCellInfoChanged
+ * PhoneStateListener.onCellInfoChanged} will be invoked.
+ *
+ * The default, 0, means invoke onCellInfoChanged when any of the reported
+ * information changes. Setting the value to INT_MAX(0x7fffffff) means never issue
+ * A onCellInfoChanged.
+ *
+ * @param rateInMillis the rate
+ *
+ * @hide
+ */
+ public void setCellInfoListRate(int rateInMillis) {
+ try {
+ getITelephony().setCellInfoListRate(rateInMillis);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 1449ab1..b78f589 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -294,5 +294,10 @@
* Returns the all observed cell information of the device.
*/
List<CellInfo> getAllCellInfo();
+
+ /**
+ * Sets minimum time in milli-seconds between onCellInfoChanged
+ */
+ void setCellInfoListRate(int rateInMillis);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 077ad68..9650b99 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -260,6 +260,8 @@
int RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU = 106;
int RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS = 107;
int RIL_REQUEST_VOICE_RADIO_TECH = 108;
+ int RIL_REQUEST_GET_CELL_INFO_LIST = 109;
+ int RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE = 110;
int RIL_UNSOL_RESPONSE_BASE = 1000;
int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001;
@@ -297,4 +299,5 @@
int RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE = 1033;
int RIL_UNSOL_RIL_CONNECTED = 1034;
int RIL_UNSOL_VOICE_RADIO_TECH_CHANGED = 1035;
+ int RIL_UNSOL_CELL_INFO_LIST = 1036;
}
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 5b88669..9b1658a 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -39,14 +39,14 @@
LOCAL_C_INCLUDES += external/zlib
LOCAL_C_INCLUDES += build/libs/host/include
-#LOCAL_WHOLE_STATIC_LIBRARIES :=
LOCAL_STATIC_LIBRARIES := \
libhost \
libandroidfw \
libutils \
libcutils \
libexpat \
- libpng
+ libpng \
+ liblog
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -lrt -ldl -lpthread
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
index dd57ae6..ad8de69 100644
--- a/tools/obbtool/Android.mk
+++ b/tools/obbtool/Android.mk
@@ -20,7 +20,8 @@
LOCAL_STATIC_LIBRARIES := \
libutils \
libandroidfw \
- libcutils
+ libcutils \
+ liblog
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -ldl -lpthread
diff --git a/tools/validatekeymaps/Android.mk b/tools/validatekeymaps/Android.mk
index fce2e93..90fbc08 100644
--- a/tools/validatekeymaps/Android.mk
+++ b/tools/validatekeymaps/Android.mk
@@ -20,7 +20,8 @@
LOCAL_STATIC_LIBRARIES := \
libandroidfw \
libutils \
- libcutils
+ libcutils \
+ liblog
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -ldl -lpthread
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 47f1fbf..7b1a71f 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -1196,7 +1196,7 @@
WifiConfiguration newConfig) {
boolean ipChanged = false;
boolean proxyChanged = false;
- LinkProperties linkProperties = new LinkProperties();
+ LinkProperties linkProperties = null;
switch (newConfig.ipAssignment) {
case STATIC:
@@ -1262,10 +1262,10 @@
}
if (!ipChanged) {
- addIpSettingsFromConfig(linkProperties, currentConfig);
+ linkProperties = copyIpSettingsFromConfig(currentConfig);
} else {
currentConfig.ipAssignment = newConfig.ipAssignment;
- addIpSettingsFromConfig(linkProperties, newConfig);
+ linkProperties = copyIpSettingsFromConfig(newConfig);
log("IP config changed SSID = " + currentConfig.SSID + " linkProperties: " +
linkProperties.toString());
}
@@ -1291,8 +1291,9 @@
return new NetworkUpdateResult(ipChanged, proxyChanged);
}
- private void addIpSettingsFromConfig(LinkProperties linkProperties,
- WifiConfiguration config) {
+ private LinkProperties copyIpSettingsFromConfig(WifiConfiguration config) {
+ LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(config.linkProperties.getInterfaceName());
for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) {
linkProperties.addLinkAddress(linkAddr);
}
@@ -1302,6 +1303,7 @@
for (InetAddress dns : config.linkProperties.getDnses()) {
linkProperties.addDns(dns);
}
+ return linkProperties;
}
/**