Merge "Dump xt_qtaguid stats to using GID instead of su."
diff --git a/api/current.txt b/api/current.txt
index 2a7e2f7..bf71f67 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9083,14 +9083,17 @@
ctor public GradientDrawable(android.graphics.drawable.GradientDrawable.Orientation, int[]);
method public void draw(android.graphics.Canvas);
method public int getOpacity();
+ method public android.graphics.drawable.GradientDrawable.Orientation getOrientation();
method public void setAlpha(int);
method public void setColor(int);
method public void setColorFilter(android.graphics.ColorFilter);
+ method public void setColors(int[]);
method public void setCornerRadii(float[]);
method public void setCornerRadius(float);
method public void setGradientCenter(float, float);
method public void setGradientRadius(float);
method public void setGradientType(int);
+ method public void setOrientation(android.graphics.drawable.GradientDrawable.Orientation);
method public void setShape(int);
method public void setSize(int, int);
method public void setStroke(int, int);
@@ -23828,6 +23831,9 @@
method public android.view.ViewPropertyAnimator translationXBy(float);
method public android.view.ViewPropertyAnimator translationY(float);
method public android.view.ViewPropertyAnimator translationYBy(float);
+ method public android.view.ViewPropertyAnimator withEndAction(java.lang.Runnable);
+ method public android.view.ViewPropertyAnimator withLayer();
+ method public android.view.ViewPropertyAnimator withStartAction(java.lang.Runnable);
method public android.view.ViewPropertyAnimator x(float);
method public android.view.ViewPropertyAnimator xBy(float);
method public android.view.ViewPropertyAnimator y(float);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 9807b89..3c5f53a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4413,7 +4413,7 @@
});
}
- public static final ActivityThread systemMain() {
+ public static ActivityThread systemMain() {
HardwareRenderer.disable(true);
ActivityThread thread = new ActivityThread();
thread.attach(true);
@@ -4454,6 +4454,8 @@
ActivityThread thread = new ActivityThread();
thread.attach(false);
+ AsyncTask.init();
+
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 5e9abb7..fd6bed7 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -135,6 +135,8 @@
* <p>There are a few threading rules that must be followed for this class to
* work properly:</p>
* <ul>
+ * <li>The AsyncTask class must be loaded on the UI thread. This is done
+ * automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li>
* <li>The task instance must be created on the UI thread.</li>
* <li>{@link #execute} must be invoked on the UI thread.</li>
* <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index c08a4024..fa4dd25 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -189,7 +189,7 @@
}
private static native int nGetMaximumTextureWidth();
- private static native int nGetMaximumTextureHeight();
+ private static native int nGetMaximumTextureHeight();
///////////////////////////////////////////////////////////////////////////
// Setup
@@ -268,6 +268,24 @@
private static native void nFinish(int renderer);
+ /**
+ * Returns the size of the stencil buffer required by the underlying
+ * implementation.
+ *
+ * @return The minimum number of bits the stencil buffer must. Always >= 0.
+ *
+ * @hide
+ */
+ public static int getStencilSize() {
+ return nGetStencilSize();
+ }
+
+ private static native int nGetStencilSize();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Functor
+ ///////////////////////////////////////////////////////////////////////////
+
@Override
public boolean callDrawGLFunction(int drawGLFunction) {
return nCallDrawGLFunction(mRenderer, drawGLFunction);
@@ -275,7 +293,6 @@
private static native boolean nCallDrawGLFunction(int renderer, int drawGLFunction);
-
///////////////////////////////////////////////////////////////////////////
// Memory
///////////////////////////////////////////////////////////////////////////
@@ -361,6 +378,12 @@
private static native int nGetDisplayListSize(int displayList);
+ static void setDisplayListName(int displayList, String name) {
+ nSetDisplayListName(displayList, name);
+ }
+
+ private static native void nSetDisplayListName(int displayList, String name);
+
@Override
public boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty) {
return nDrawDisplayList(mRenderer,
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index 0cb9449..969c9ab 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -31,10 +31,17 @@
private GLES20RecordingCanvas mCanvas;
private boolean mValid;
+ // Used for debugging
+ private final String mName;
+
// The native display list will be destroyed when this object dies.
// DO NOT overwrite this reference once it is set.
private DisplayListFinalizer mFinalizer;
+ GLES20DisplayList(String name) {
+ mName = name;
+ }
+
int getNativeDisplayList() {
if (!mValid || mFinalizer == null) {
throw new IllegalStateException("The display list is not valid.");
@@ -75,6 +82,7 @@
mCanvas.end(mFinalizer.mNativeDisplayList);
} else {
mFinalizer = new DisplayListFinalizer(mCanvas.end(0));
+ GLES20Canvas.setDisplayListName(mFinalizer.mNativeDisplayList, mName);
}
mCanvas.recycle();
mCanvas = null;
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 1c9cbbf..9e8a228 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -283,9 +283,12 @@
* Creates a new display list that can be used to record batches of
* drawing operations.
*
+ * @param name The name of the display list, used for debugging purpose.
+ * May be null
+ *
* @return A new display list.
*/
- public abstract DisplayList createDisplayList();
+ public abstract DisplayList createDisplayList(String name);
/**
* Creates a new hardware layer. A hardware layer built by calling this
@@ -1047,7 +1050,7 @@
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 0,
- EGL_STENCIL_SIZE, 0,
+ EGL_STENCIL_SIZE, GLES20Canvas.getStencilSize(),
EGL_SURFACE_TYPE, EGL_WINDOW_BIT |
(dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
EGL_NONE
@@ -1094,8 +1097,8 @@
}
@Override
- public DisplayList createDisplayList() {
- return new GLES20DisplayList();
+ public DisplayList createDisplayList(String name) {
+ return new GLES20DisplayList(name);
}
@Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 39f603d..8597017 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3770,6 +3770,14 @@
}
if ((mPrivateFlags & FOCUSED) != 0) {
+ // If this is the first focusable do not clear focus since the we
+ // try to give it focus every time a view clears its focus. Hence,
+ // the view that would gain focus already has it.
+ View firstFocusable = getFirstFocusable();
+ if (firstFocusable == this) {
+ return;
+ }
+
mPrivateFlags &= ~FOCUSED;
if (mParent != null) {
@@ -3778,9 +3786,24 @@
onFocusChanged(false, 0, null);
refreshDrawableState();
+
+ // The view cleared focus and invoked the callbacks, so now is the
+ // time to give focus to the the first focusable to ensure that the
+ // gain focus is announced after clear focus.
+ if (firstFocusable != null) {
+ firstFocusable.requestFocus(FOCUS_FORWARD);
+ }
}
}
+ private View getFirstFocusable() {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ return viewRoot.focusSearch(null, FOCUS_FORWARD);
+ }
+ return null;
+ }
+
/**
* Called to clear the focus of a view that is about to be removed.
* Doesn't call clearChildFocus, which prevents this view from taking
@@ -10374,7 +10397,8 @@
// we copy in child display lists into ours in drawChild()
mRecreateDisplayList = true;
if (mDisplayList == null) {
- mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList();
+ final String name = getClass().getSimpleName();
+ mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList(name);
// If we're creating a new display list, make sure our parent gets invalidated
// since they will need to recreate their display list to account for this
// new child display list.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index d906a16..7559862 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -675,11 +675,14 @@
*/
@Override
public void clearFocus() {
- super.clearFocus();
-
- // clear any child focus if it exists
- if (mFocused != null) {
+ if (DBG) {
+ System.out.println(this + " clearFocus()");
+ }
+ if (mFocused == null) {
+ super.clearFocus();
+ } else {
mFocused.clearFocus();
+ mFocused = null;
}
}
@@ -691,12 +694,12 @@
if (DBG) {
System.out.println(this + " unFocus()");
}
-
- super.unFocus();
- if (mFocused != null) {
+ if (mFocused == null) {
+ super.unFocus();
+ } else {
mFocused.unFocus();
+ mFocused = null;
}
- mFocused = null;
}
/**
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 89a1ef2..0fdcd0f 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -113,6 +113,10 @@
* on that list are added to the list of properties associated with that animator.
*/
ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>();
+ private Runnable mPendingSetupAction;
+ private Runnable mPendingCleanupAction;
+ private Runnable mPendingOnStartAction;
+ private Runnable mPendingOnEndAction;
/**
* Constants used to associate a property being requested and the mechanism used to set
@@ -199,6 +203,10 @@
*/
private HashMap<Animator, PropertyBundle> mAnimatorMap =
new HashMap<Animator, PropertyBundle>();
+ private HashMap<Animator, Runnable> mAnimatorSetupMap;
+ private HashMap<Animator, Runnable> mAnimatorCleanupMap;
+ private HashMap<Animator, Runnable> mAnimatorOnStartMap;
+ private HashMap<Animator, Runnable> mAnimatorOnEndMap;
/**
* This is the information we need to set each property during the animation.
@@ -614,6 +622,93 @@
}
/**
+ * The View associated with this ViewPropertyAnimator will have its
+ * {@link View#setLayerType(int, android.graphics.Paint) layer type} set to
+ * {@link View#LAYER_TYPE_HARDWARE} for the duration of the next animation. This state
+ * is not persistent, either on the View or on this ViewPropertyAnimator: the layer type
+ * of the View will be restored when the animation ends to what it was when this method was
+ * called, and this setting on ViewPropertyAnimator is only valid for the next animation.
+ * Note that calling this method and then independently setting the layer type of the View
+ * (by a direct call to {@link View#setLayerType(int, android.graphics.Paint)}) will result
+ * in some inconsistency, including having the layer type restored to its pre-withLayer()
+ * value when the animation ends.
+ *
+ * @see View#setLayerType(int, android.graphics.Paint)
+ * @return This object, allowing calls to methods in this class to be chained.
+ */
+ public ViewPropertyAnimator withLayer() {
+ mPendingSetupAction= new Runnable() {
+ @Override
+ public void run() {
+ mView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ }
+ };
+ final int currentLayerType = mView.getLayerType();
+ mPendingCleanupAction = new Runnable() {
+ @Override
+ public void run() {
+ mView.setLayerType(currentLayerType, null);
+ }
+ };
+ if (mAnimatorSetupMap == null) {
+ mAnimatorSetupMap = new HashMap<Animator, Runnable>();
+ }
+ if (mAnimatorCleanupMap == null) {
+ mAnimatorCleanupMap = new HashMap<Animator, Runnable>();
+ }
+
+ return this;
+ }
+
+ /**
+ * Specifies an action to take place when the next animation runs. If there is a
+ * {@link #setStartDelay(long) startDelay} set on this ViewPropertyAnimator, then the
+ * action will run after that startDelay expires, when the actual animation begins.
+ * This method, along with {@link #withEndAction(Runnable)}, is intended to help facilitate
+ * choreographing ViewPropertyAnimator animations with other animations or actions
+ * in the application.
+ *
+ * @param runnable The action to run when the next animation starts.
+ * @return This object, allowing calls to methods in this class to be chained.
+ */
+ public ViewPropertyAnimator withStartAction(Runnable runnable) {
+ mPendingOnStartAction = runnable;
+ if (runnable != null && mAnimatorOnStartMap == null) {
+ mAnimatorOnStartMap = new HashMap<Animator, Runnable>();
+ }
+ return this;
+ }
+
+ /**
+ * Specifies an action to take place when the next animation ends. The action is only
+ * run if the animation ends normally; if the ViewPropertyAnimator is canceled during
+ * that animation, the runnable will not run.
+ * This method, along with {@link #withStartAction(Runnable)}, is intended to help facilitate
+ * choreographing ViewPropertyAnimator animations with other animations or actions
+ * in the application.
+ *
+ * <p>For example, the following code animates a view to x=200 and then back to 0:</p>
+ * <pre>
+ * Runnable endAction = new Runnable() {
+ * public void run() {
+ * view.animate().x(0);
+ * }
+ * };
+ * view.animate().x(200).onEnd(endAction);
+ * </pre>
+ *
+ * @param runnable The action to run when the next animation ends.
+ * @return This object, allowing calls to methods in this class to be chained.
+ */
+ public ViewPropertyAnimator withEndAction(Runnable runnable) {
+ mPendingOnEndAction = runnable;
+ if (runnable != null && mAnimatorOnEndMap == null) {
+ mAnimatorOnEndMap = new HashMap<Animator, Runnable>();
+ }
+ return this;
+ }
+
+ /**
* Starts the underlying Animator for a set of properties. We use a single animator that
* simply runs from 0 to 1, and then use that fractional value to set each property
* value accordingly.
@@ -630,6 +725,22 @@
propertyMask |= nameValuesHolder.mNameConstant;
}
mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
+ if (mPendingSetupAction != null) {
+ mAnimatorSetupMap.put(animator, mPendingSetupAction);
+ mPendingSetupAction = null;
+ }
+ if (mPendingCleanupAction != null) {
+ mAnimatorCleanupMap.put(animator, mPendingCleanupAction);
+ mPendingCleanupAction = null;
+ }
+ if (mPendingOnStartAction != null) {
+ mAnimatorOnStartMap.put(animator, mPendingOnStartAction);
+ mPendingOnStartAction = null;
+ }
+ if (mPendingOnEndAction != null) {
+ mAnimatorOnEndMap.put(animator, mPendingOnEndAction);
+ mPendingOnEndAction = null;
+ }
animator.addUpdateListener(mAnimatorEventListener);
animator.addListener(mAnimatorEventListener);
if (mStartDelaySet) {
@@ -800,6 +911,20 @@
implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
@Override
public void onAnimationStart(Animator animation) {
+ if (mAnimatorSetupMap != null) {
+ Runnable r = mAnimatorSetupMap.get(animation);
+ if (r != null) {
+ r.run();
+ }
+ mAnimatorSetupMap.remove(animation);
+ }
+ if (mAnimatorOnStartMap != null) {
+ Runnable r = mAnimatorOnStartMap.get(animation);
+ if (r != null) {
+ r.run();
+ }
+ mAnimatorOnStartMap.remove(animation);
+ }
if (mListener != null) {
mListener.onAnimationStart(animation);
}
@@ -810,6 +935,9 @@
if (mListener != null) {
mListener.onAnimationCancel(animation);
}
+ if (mAnimatorOnEndMap != null) {
+ mAnimatorOnEndMap.remove(animation);
+ }
}
@Override
@@ -824,6 +952,20 @@
if (mListener != null) {
mListener.onAnimationEnd(animation);
}
+ if (mAnimatorOnEndMap != null) {
+ Runnable r = mAnimatorOnEndMap.get(animation);
+ if (r != null) {
+ r.run();
+ }
+ mAnimatorOnEndMap.remove(animation);
+ }
+ if (mAnimatorCleanupMap != null) {
+ Runnable r = mAnimatorCleanupMap.get(animation);
+ if (r != null) {
+ r.run();
+ }
+ mAnimatorCleanupMap.remove(animation);
+ }
mAnimatorMap.remove(animation);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1a4bdf4..2ef843b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -168,6 +168,7 @@
View mView;
View mFocusedView;
View mRealFocusedView; // this is not set to null in touch mode
+ View mOldFocusedView;
int mViewVisibility;
boolean mAppVisible = true;
int mOrigWindowType = -1;
@@ -2226,32 +2227,33 @@
public void requestChildFocus(View child, View focused) {
checkThread();
- if (mFocusedView != focused) {
- mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused);
- scheduleTraversals();
+
+ if (DEBUG_INPUT_RESIZE) {
+ Log.v(TAG, "Request child focus: focus now " + focused);
}
+
+ mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, focused);
+ scheduleTraversals();
+
mFocusedView = mRealFocusedView = focused;
- if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now "
- + mFocusedView);
}
public void clearChildFocus(View child) {
checkThread();
- View oldFocus = mFocusedView;
-
- if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus");
- mFocusedView = mRealFocusedView = null;
- if (mView != null && !mView.hasFocus()) {
- // If a view gets the focus, the listener will be invoked from requestChildFocus()
- if (!mView.requestFocus(View.FOCUS_FORWARD)) {
- mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
- }
- } else if (oldFocus != null) {
- mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
+ if (DEBUG_INPUT_RESIZE) {
+ Log.v(TAG, "Clearing child focus");
}
- }
+ mOldFocusedView = mFocusedView;
+
+ // Invoke the listener only if there is no view to take focus
+ if (focusSearch(null, View.FOCUS_FORWARD) == null) {
+ mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, null);
+ }
+
+ mFocusedView = mRealFocusedView = null;
+ }
public void focusableViewAvailable(View v) {
checkThread();
@@ -2724,6 +2726,7 @@
mView.unFocus();
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
mFocusedView = null;
+ mOldFocusedView = null;
return true;
}
}
diff --git a/core/java/android/webkit/WebCoreThreadWatchdog.java b/core/java/android/webkit/WebCoreThreadWatchdog.java
index d100260..0541d5d 100644
--- a/core/java/android/webkit/WebCoreThreadWatchdog.java
+++ b/core/java/android/webkit/WebCoreThreadWatchdog.java
@@ -40,9 +40,6 @@
// WebCore thread unresponsive.
private static final int TIMED_OUT = 101;
- // Message to tell the Watchdog thread to terminate.
- private static final int QUIT = 102;
-
// Wait 10s after hearing back from the WebCore thread before checking it's still alive.
private static final int HEARTBEAT_PERIOD = 10 * 1000;
@@ -57,7 +54,6 @@
private Handler mWebCoreThreadHandler;
private Handler mHandler;
private boolean mPaused;
- private boolean mPendingQuit;
private static WebCoreThreadWatchdog sInstance;
@@ -88,12 +84,6 @@
}
}
- public synchronized static void quit() {
- if (sInstance != null) {
- sInstance.quitWatchdog();
- }
- }
-
private void setContext(Context context) {
mContext = context;
}
@@ -103,19 +93,6 @@
mWebCoreThreadHandler = webCoreThreadHandler;
}
- private void quitWatchdog() {
- if (mHandler == null) {
- // The thread hasn't started yet, so set a flag to stop it starting.
- mPendingQuit = true;
- return;
- }
- // Clear any pending messages, and then post a quit to the WatchDog handler.
- mHandler.removeMessages(TIMED_OUT);
- mHandler.removeMessages(IS_ALIVE);
- mWebCoreThreadHandler.removeMessages(EventHub.HEARTBEAT);
- mHandler.obtainMessage(QUIT).sendToTarget();
- }
-
private void pauseWatchdog() {
mPaused = true;
@@ -146,12 +123,8 @@
mHandler.sendMessageDelayed(mHandler.obtainMessage(TIMED_OUT), TIMEOUT_PERIOD);
}
- private boolean createHandler() {
+ private void createHandler() {
synchronized (WebCoreThreadWatchdog.class) {
- if (mPendingQuit) {
- return false;
- }
-
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -206,15 +179,9 @@
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
break;
-
- case QUIT:
- Looper.myLooper().quit();
- break;
}
}
};
-
- return true;
}
}
@@ -222,9 +189,7 @@
public void run() {
Looper.prepare();
- if (!createHandler()) {
- return;
- }
+ createHandler();
// Send the initial control to WebViewCore and start the timeout timer as long as we aren't
// paused.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index cc8eef2..2c2cf64 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -789,12 +789,18 @@
// know to handle Shift and arrows natively first
private boolean mAccessibilityScriptInjected;
- static final boolean USE_JAVA_TEXT_SELECTION = true;
- static final boolean DEBUG_TEXT_HANDLES = false;
- private Region mTextSelectionRegion = new Region();
- private Paint mTextSelectionPaint;
private Drawable mSelectHandleLeft;
private Drawable mSelectHandleRight;
+ private Rect mSelectCursorBase = new Rect();
+ private int mSelectCursorBaseLayerId;
+ private Rect mSelectCursorExtent = new Rect();
+ private int mSelectCursorExtentLayerId;
+ private Rect mSelectDraggingCursor;
+ private Point mSelectDraggingOffset = new Point();
+ static final int HANDLE_ID_START = 0;
+ static final int HANDLE_ID_END = 1;
+ static final int HANDLE_ID_BASE = 2;
+ static final int HANDLE_ID_EXTENT = 3;
static boolean sDisableNavcache = false;
// the color used to highlight the touch rectangles
@@ -2656,12 +2662,6 @@
return mZoomManager.getScale();
}
- // Called by JNI. Returns the scale to apply to the text selection handles
- /* package */ float getTextHandleScale() {
- float density = mContext.getResources().getDisplayMetrics().density;
- return density / getScale();
- }
-
/**
* Compute the reading level scale of the WebView
* @param scale The current scale.
@@ -3852,6 +3852,16 @@
if (x == mScrollingLayerRect.left && y == mScrollingLayerRect.top) {
return;
}
+ if (mSelectingText) {
+ int dx = mScrollingLayerRect.left - x;
+ int dy = mScrollingLayerRect.top - y;
+ if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) {
+ mSelectCursorBase.offset(dx, dy);
+ }
+ if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) {
+ mSelectCursorExtent.offset(dx, dy);
+ }
+ }
nativeScrollLayer(mCurrentScrollingLayerId, x, y);
mScrollingLayerRect.left = x;
mScrollingLayerRect.top = y;
@@ -4624,12 +4634,7 @@
* Select the word at the indicated content coordinates.
*/
boolean selectText(int x, int y) {
- if (!setUpSelect(true, x, y)) {
- return false;
- }
- nativeSetExtendSelection();
- mDrawSelectionPointer = false;
- mTouchMode = TOUCH_DRAG_MODE;
+ mWebViewCore.sendMessage(EventHub.SELECT_WORD_AT, x, y);
return true;
}
@@ -4830,11 +4835,8 @@
int extras = DRAW_EXTRAS_NONE;
if (mFindIsUp) {
extras = DRAW_EXTRAS_FIND;
- } else if (mSelectingText && (!USE_JAVA_TEXT_SELECTION || DEBUG_TEXT_HANDLES)) {
+ } else if (mSelectingText) {
extras = DRAW_EXTRAS_SELECTION;
- nativeSetSelectionPointer(mNativeClass,
- mDrawSelectionPointer,
- mZoomManager.getInvScale(), mSelectX, mSelectY - getTitleHeight());
} else if (drawCursorRing) {
extras = DRAW_EXTRAS_CURSOR_RING;
}
@@ -4879,7 +4881,7 @@
}
canvas.restoreToCount(saveCount);
- if (mSelectingText && USE_JAVA_TEXT_SELECTION) {
+ if (mSelectingText) {
drawTextSelectionHandles(canvas);
}
@@ -4901,30 +4903,12 @@
}
private void drawTextSelectionHandles(Canvas canvas) {
- if (mTextSelectionPaint == null) {
- mTextSelectionPaint = new Paint();
- mTextSelectionPaint.setColor(HIGHLIGHT_COLOR);
- }
- mTextSelectionRegion.setEmpty();
- nativeGetTextSelectionRegion(mNativeClass, mTextSelectionRegion);
- Rect r = new Rect();
- RegionIterator iter = new RegionIterator(mTextSelectionRegion);
- Rect clip = canvas.getClipBounds();
- while (iter.next(r)) {
- r.set(contentToViewDimension(r.left),
- contentToViewDimension(r.top),
- contentToViewDimension(r.right),
- contentToViewDimension(r.bottom));
- if (r.intersect(clip)) {
- canvas.drawRect(r, mTextSelectionPaint);
- }
- }
if (mSelectHandleLeft == null) {
mSelectHandleLeft = mContext.getResources().getDrawable(
com.android.internal.R.drawable.text_select_handle_left);
}
int[] handles = new int[4];
- nativeGetSelectionHandles(mNativeClass, handles);
+ getSelectionHandles(handles);
int start_x = contentToViewDimension(handles[0]);
int start_y = contentToViewDimension(handles[1]);
int end_x = contentToViewDimension(handles[2]);
@@ -4942,14 +4926,31 @@
mSelectHandleRight.setBounds(end_x, end_y,
end_x + mSelectHandleRight.getIntrinsicWidth(),
end_y + mSelectHandleRight.getIntrinsicHeight());
- if (DEBUG_TEXT_HANDLES) {
- mSelectHandleLeft.setAlpha(125);
- mSelectHandleRight.setAlpha(125);
- }
mSelectHandleLeft.draw(canvas);
mSelectHandleRight.draw(canvas);
}
+ /**
+ * Takes an int[4] array as an output param with the values being
+ * startX, startY, endX, endY
+ */
+ private void getSelectionHandles(int[] handles) {
+ handles[0] = mSelectCursorBase.right;
+ handles[1] = mSelectCursorBase.bottom -
+ (mSelectCursorBase.height() / 4);
+ handles[2] = mSelectCursorExtent.left;
+ handles[3] = mSelectCursorExtent.bottom
+ - (mSelectCursorExtent.height() / 4);
+ if (!nativeIsBaseFirst(mNativeClass)) {
+ int swap = handles[0];
+ handles[0] = handles[2];
+ handles[2] = swap;
+ swap = handles[1];
+ handles[1] = handles[3];
+ handles[3] = swap;
+ }
+ }
+
// draw history
private boolean mDrawHistory = false;
private Picture mHistoryPicture = null;
@@ -5009,7 +5010,7 @@
/* package */ void deleteSelection(int start, int end) {
mTextGeneration++;
WebViewCore.TextSelectionData data
- = new WebViewCore.TextSelectionData(start, end);
+ = new WebViewCore.TextSelectionData(start, end, 0);
mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
data);
}
@@ -5462,15 +5463,6 @@
return pinScrollTo(mContentWidth, mScrollY, true, 0);
}
}
- if (mSelectingText) {
- int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT
- ? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0;
- int yRate = keyCode == KeyEvent.KEYCODE_DPAD_UP ?
- -1 : keyCode == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : 0;
- int multiplier = event.getRepeatCount() + 1;
- moveSelection(xRate * multiplier, yRate * multiplier);
- return true;
- }
if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
playSoundEffect(keyCodeToSoundsEffect(keyCode));
return true;
@@ -5623,14 +5615,8 @@
mGotCenterDown = false;
if (mSelectingText) {
- if (mExtendSelection) {
- copySelection();
- selectionDone();
- } else {
- mExtendSelection = true;
- nativeSetExtendSelection();
- invalidate(); // draw the i-beam instead of the arrow
- }
+ copySelection();
+ selectionDone();
return true; // discard press if copy in progress
}
@@ -5676,21 +5662,7 @@
return false;
}
- /*
- * Enter selecting text mode, and see if CAB should be shown.
- * Returns true if the WebView is now in
- * selecting text mode (including if it was already in that mode, and this
- * method did nothing).
- */
- private boolean setUpSelect(boolean selectWord, int x, int y) {
- if (0 == mNativeClass) return false; // client isn't initialized
- if (inFullScreenMode()) return false;
- if (mSelectingText) return true;
- nativeResetSelection();
- if (selectWord && !nativeWordSelection(x, y)) {
- selectionDone();
- return false;
- }
+ private boolean startSelectActionMode() {
mSelectCallback = new SelectActionModeCallback();
mSelectCallback.setWebView(this);
if (startActionMode(mSelectCallback) == null) {
@@ -5699,54 +5671,43 @@
selectionDone();
return false;
}
- mExtendSelection = false;
- mSelectingText = mDrawSelectionPointer = true;
- if (DEBUG_TEXT_HANDLES) {
- // Debugging text handles requires running in software mode
- setLayerType(LAYER_TYPE_SOFTWARE, null);
- }
- // don't let the picture change during text selection
- WebViewCore.pauseUpdatePicture(mWebViewCore);
- if (nativeHasCursorNode()) {
- Rect rect = nativeCursorNodeBounds();
- mSelectX = contentToViewX(rect.left);
- mSelectY = contentToViewY(rect.top);
- } else if (mLastTouchY > getVisibleTitleHeightImpl()) {
- mSelectX = mScrollX + mLastTouchX;
- mSelectY = mScrollY + mLastTouchY;
- } else {
- mSelectX = mScrollX + getViewWidth() / 2;
- mSelectY = mScrollY + getViewHeightWithTitle() / 2;
- }
- nativeHideCursor();
- mMinAutoScrollX = 0;
- mMaxAutoScrollX = getViewWidth();
- mMinAutoScrollY = 0;
- mMaxAutoScrollY = getViewHeightWithTitle();
- mCurrentScrollingLayerId = nativeScrollableLayer(viewToContentX(mSelectX),
- viewToContentY(mSelectY), mScrollingLayerRect,
- mScrollingLayerBounds);
- if (mCurrentScrollingLayerId != 0) {
- if (mScrollingLayerRect.left != mScrollingLayerRect.right) {
- mMinAutoScrollX = Math.max(mMinAutoScrollX,
- contentToViewX(mScrollingLayerBounds.left));
- mMaxAutoScrollX = Math.min(mMaxAutoScrollX,
- contentToViewX(mScrollingLayerBounds.right));
- }
- if (mScrollingLayerRect.top != mScrollingLayerRect.bottom) {
- mMinAutoScrollY = Math.max(mMinAutoScrollY,
- contentToViewY(mScrollingLayerBounds.top));
- mMaxAutoScrollY = Math.min(mMaxAutoScrollY,
- contentToViewY(mScrollingLayerBounds.bottom));
- }
- }
- mMinAutoScrollX += SELECT_SCROLL;
- mMaxAutoScrollX -= SELECT_SCROLL;
- mMinAutoScrollY += SELECT_SCROLL;
- mMaxAutoScrollY -= SELECT_SCROLL;
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
return true;
}
+ private void syncSelectionCursors() {
+ mSelectCursorBaseLayerId =
+ nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE, mSelectCursorBase);
+ mSelectCursorExtentLayerId =
+ nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT, mSelectCursorExtent);
+ }
+
+ private boolean setupWebkitSelect() {
+ syncSelectionCursors();
+ if (!startSelectActionMode()) {
+ selectionDone();
+ return false;
+ }
+ mSelectingText = true;
+ mTouchMode = TOUCH_DRAG_MODE;
+ return true;
+ }
+
+ private void updateWebkitSelection() {
+ int[] handles = null;
+ if (mSelectingText) {
+ handles = new int[4];
+ handles[0] = mSelectCursorBase.centerX();
+ handles[1] = mSelectCursorBase.centerY();
+ handles[2] = mSelectCursorExtent.centerX();
+ handles[3] = mSelectCursorExtent.centerY();
+ } else {
+ nativeSetTextSelection(mNativeClass, 0);
+ }
+ mWebViewCore.removeMessages(EventHub.SELECT_TEXT);
+ mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT, handles);
+ }
+
/**
* Use this method to put the WebView into text selection mode.
* Do not rely on this functionality; it will be deprecated in the future.
@@ -5755,7 +5716,6 @@
@Deprecated
public void emulateShiftHeld() {
checkThread();
- setUpSelect(false, 0, 0);
}
/**
@@ -5764,17 +5724,7 @@
* @hide This is an implementation detail.
*/
public void selectAll() {
- if (0 == mNativeClass) return; // client isn't initialized
- if (inFullScreenMode()) return;
- if (!mSelectingText) {
- // retrieve a point somewhere within the text
- Point select = nativeSelectableText();
- if (!selectText(select.x, select.y)) return;
- }
- nativeSelectAll();
- mDrawSelectionPointer = false;
- mExtendSelection = true;
- invalidate();
+ mWebViewCore.sendMessage(EventHub.SELECT_ALL);
}
/**
@@ -5783,17 +5733,11 @@
void selectionDone() {
if (mSelectingText) {
mSelectingText = false;
- if (DEBUG_TEXT_HANDLES) {
- // Debugging text handles required running in software mode, set
- // back to default now
- setLayerType(LAYER_TYPE_NONE, null);
- }
// finish is idempotent, so this is fine even if selectionDone was
// called by mSelectCallback.onDestroyActionMode
mSelectCallback.finish();
mSelectCallback = null;
- WebViewCore.resumePriority();
- WebViewCore.resumeUpdatePicture(mWebViewCore);
+ updateWebkitSelection();
invalidate(); // redraw without selection
mAutoScrollX = 0;
mAutoScrollY = 0;
@@ -5821,7 +5765,7 @@
.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setText(selection);
int[] handles = new int[4];
- nativeGetSelectionHandles(mNativeClass, handles);
+ getSelectionHandles(handles);
mWebViewCore.sendMessage(EventHub.COPY_TEXT, handles);
}
invalidate(); // remove selection region and pointer
@@ -5836,7 +5780,7 @@
public void cutSelection() {
copySelection();
int[] handles = new int[4];
- nativeGetSelectionHandles(mNativeClass, handles);
+ getSelectionHandles(handles);
mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles);
}
@@ -5854,7 +5798,7 @@
CharSequence pasteText = clipItem.getText();
if (pasteText != null) {
int[] handles = new int[4];
- nativeGetSelectionHandles(mNativeClass, handles);
+ getSelectionHandles(handles);
mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles);
mWebViewCore.sendMessage(EventHub.INSERT_TEXT,
pasteText.toString());
@@ -6407,12 +6351,26 @@
(eventTime - mLastTouchUpTime), eventTime);
}
if (mSelectingText) {
- mDrawSelectionPointer = false;
- mSelectionStarted = nativeStartSelection(contentX, contentY);
+ mSelectionStarted = false;
+ int shiftedY = y - getTitleHeight() + mScrollY;
+ int shiftedX = x + mScrollX;
+ if (mSelectHandleLeft.getBounds()
+ .contains(shiftedX, shiftedY)) {
+ mSelectionStarted = true;
+ mSelectDraggingCursor = mSelectCursorBase;
+ } else if (mSelectHandleRight.getBounds()
+ .contains(shiftedX, shiftedY)) {
+ mSelectionStarted = true;
+ mSelectDraggingCursor = mSelectCursorExtent;
+ }
+ if (mSelectDraggingCursor != null) {
+ mSelectDraggingOffset.set(
+ mSelectDraggingCursor.left - contentX,
+ mSelectDraggingCursor.top - contentY);
+ }
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "select=" + contentX + "," + contentY);
}
- invalidate();
}
}
// Trigger the link
@@ -6478,6 +6436,26 @@
removeTouchHighlight();
}
}
+ if (mSelectingText && mSelectionStarted) {
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "extend=" + contentX + "," + contentY);
+ }
+ ViewParent parent = getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
+ if (deltaX != 0 || deltaY != 0) {
+ mSelectDraggingCursor.offsetTo(
+ contentX + mSelectDraggingOffset.x,
+ contentY + mSelectDraggingOffset.y);
+ updateWebkitSelection();
+ mLastTouchX = x;
+ mLastTouchY = y;
+ invalidate();
+ }
+ break;
+ }
+
// pass the touch events from UI thread to WebCore thread
if (shouldForwardTouchEvent() && mConfirmMove && (firstMove
|| eventTime - mLastSentTouchTime > mCurrentTouchInterval)) {
@@ -6520,30 +6498,6 @@
} else {
mVelocityTracker.addMovement(ev);
}
- if (mSelectingText && mSelectionStarted) {
- if (DebugFlags.WEB_VIEW) {
- Log.v(LOGTAG, "extend=" + contentX + "," + contentY);
- }
- ViewParent parent = getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
- mAutoScrollX = x <= mMinAutoScrollX ? -SELECT_SCROLL
- : x >= mMaxAutoScrollX ? SELECT_SCROLL : 0;
- mAutoScrollY = y <= mMinAutoScrollY ? -SELECT_SCROLL
- : y >= mMaxAutoScrollY ? SELECT_SCROLL : 0;
- if ((mAutoScrollX != 0 || mAutoScrollY != 0)
- && !mSentAutoScrollMessage) {
- mSentAutoScrollMessage = true;
- mPrivateHandler.sendEmptyMessageDelayed(
- SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL);
- }
- if (deltaX != 0 || deltaY != 0) {
- nativeExtendSelection(contentX, contentY);
- invalidate();
- }
- break;
- }
if (mTouchMode != TOUCH_DRAG_MODE &&
mTouchMode != TOUCH_DRAG_LAYER_MODE) {
@@ -6758,7 +6712,7 @@
} else {
if (mSelectingText) {
// tapping on selection or controls does nothing
- if (!nativeHitSelection(contentX, contentY)) {
+ if (!mSelectionStarted) {
selectionDone();
}
break;
@@ -7055,6 +7009,12 @@
if (mOverScrollGlow != null) {
mOverScrollGlow.releaseAll();
}
+
+ if (mSelectingText) {
+ mSelectionStarted = false;
+ syncSelectionCursors();
+ invalidate();
+ }
}
private void cancelTouch() {
@@ -7119,8 +7079,6 @@
private int mTrackballYMove = 0;
private boolean mSelectingText = false;
private boolean mSelectionStarted = false;
- private boolean mExtendSelection = false;
- private boolean mDrawSelectionPointer = false;
private static final int TRACKBALL_KEY_TIMEOUT = 1000;
private static final int TRACKBALL_TIMEOUT = 200;
private static final int TRACKBALL_WAIT = 100;
@@ -7189,14 +7147,8 @@
mTrackballDown = false;
mTrackballUpTime = time;
if (mSelectingText) {
- if (mExtendSelection) {
- copySelection();
- selectionDone();
- } else {
- mExtendSelection = true;
- nativeSetExtendSelection();
- invalidate(); // draw the i-beam instead of the arrow
- }
+ copySelection();
+ selectionDone();
return true; // discard press if copy in progress
}
if (DebugFlags.WEB_VIEW) {
@@ -7239,42 +7191,6 @@
return true;
}
- void moveSelection(float xRate, float yRate) {
- if (mNativeClass == 0)
- return;
- int width = getViewWidth();
- int height = getViewHeight();
- mSelectX += xRate;
- mSelectY += yRate;
- int maxX = width + mScrollX;
- int maxY = height + mScrollY;
- mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET
- , mSelectX));
- mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
- , mSelectY));
- if (DebugFlags.WEB_VIEW) {
- Log.v(LOGTAG, "moveSelection"
- + " mSelectX=" + mSelectX
- + " mSelectY=" + mSelectY
- + " mScrollX=" + mScrollX
- + " mScrollY=" + mScrollY
- + " xRate=" + xRate
- + " yRate=" + yRate
- );
- }
- nativeMoveSelection(viewToContentX(mSelectX), viewToContentY(mSelectY));
- int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
- : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
- : 0;
- int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
- : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
- : 0;
- pinScrollBy(scrollX, scrollY, true, 0);
- Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
- requestRectangleOnScreen(select);
- invalidate();
- }
-
private int scaleTrackballX(float xRate, int width) {
int xMove = (int) (xRate / TRACKBALL_SCALE * width);
int nextXMove = xMove;
@@ -7328,21 +7244,6 @@
float yRate = mTrackballRemainsY * 1000 / elapsed;
int viewWidth = getViewWidth();
int viewHeight = getViewHeight();
- if (mSelectingText) {
- if (!mDrawSelectionPointer) {
- // The last selection was made by touch, disabling drawing the
- // selection pointer. Allow the trackball to adjust the
- // position of the touch control.
- mSelectX = contentToViewX(nativeSelectionX());
- mSelectY = contentToViewY(nativeSelectionY());
- mDrawSelectionPointer = mExtendSelection = true;
- nativeSetExtendSelection();
- }
- moveSelection(scaleTrackballX(xRate, viewWidth),
- scaleTrackballY(yRate, viewHeight));
- mTrackballRemainsX = mTrackballRemainsY = 0;
- return;
- }
float ax = Math.abs(xRate);
float ay = Math.abs(yRate);
float maxA = Math.max(ax, ay);
@@ -9204,6 +9105,18 @@
mInputConnection.setSelection(data.mStart, data.mEnd);
}
}
+
+ nativeSetTextSelection(mNativeClass, data.mSelectTextPtr);
+ if (data.mSelectTextPtr != 0) {
+ if (!mSelectingText) {
+ setupWebkitSelect();
+ } else if (!mSelectionStarted) {
+ syncSelectionCursors();
+ }
+ } else {
+ selectionDone();
+ }
+ invalidate();
}
// Class used to use a dropdown for a <select> element
@@ -9952,7 +9865,6 @@
private native boolean nativeMoveCursor(int keyCode, int count,
boolean noScroll);
private native int nativeMoveGeneration();
- private native void nativeMoveSelection(int x, int y);
/**
* @return true if the page should get the shift and arrow keys, rather
* than select text/navigation.
@@ -9962,15 +9874,8 @@
*/
private native boolean nativePageShouldHandleShiftAndArrows();
private native boolean nativePointInNavCache(int x, int y, int slop);
- // Like many other of our native methods, you must make sure that
- // mNativeClass is not null before calling this method.
- private native void nativeResetSelection();
- private native Point nativeSelectableText();
- private native void nativeSelectAll();
private native void nativeSelectBestAt(Rect rect);
private native void nativeSelectAt(int x, int y);
- private native int nativeSelectionX();
- private native int nativeSelectionY();
private native int nativeFindIndex();
private native void nativeSetExtendSelection();
private native void nativeSetFindIsEmpty();
@@ -10026,12 +9931,14 @@
private native int nativeGetBackgroundColor();
native boolean nativeSetProperty(String key, String value);
native String nativeGetProperty(String key);
- private native void nativeGetTextSelectionRegion(int instance, Region region);
- private native void nativeGetSelectionHandles(int instance, int[] handles);
/**
* See {@link ComponentCallbacks2} for the trim levels and descriptions
*/
private static native void nativeOnTrimMemory(int level);
private static native void nativeSetPauseDrawing(int instance, boolean pause);
private static native boolean nativeDisableNavcache();
+ private static native void nativeSetTextSelection(int instance, int selection);
+ private static native int nativeGetHandleLayerId(int instance, int handle,
+ Rect cursorLocation);
+ private static native boolean nativeIsBaseFirst(int instance);
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index fe51581..395a638 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -835,12 +835,14 @@
}
static class TextSelectionData {
- public TextSelectionData(int start, int end) {
+ public TextSelectionData(int start, int end, int selectTextPtr) {
mStart = start;
mEnd = end;
+ mSelectTextPtr = selectTextPtr;
}
int mStart;
int mEnd;
+ int mSelectTextPtr;
}
static class TouchUpData {
@@ -1118,6 +1120,9 @@
static final int COPY_TEXT = 210;
static final int DELETE_TEXT = 211;
static final int INSERT_TEXT = 212;
+ static final int SELECT_TEXT = 213;
+ static final int SELECT_WORD_AT = 214;
+ static final int SELECT_ALL = 215;
// Private handler for WebCore messages.
private Handler mHandler;
@@ -1194,7 +1199,6 @@
mSettings.onDestroyed();
mNativeClass = 0;
mWebView = null;
- WebCoreThreadWatchdog.quit();
}
break;
@@ -1747,6 +1751,25 @@
case INSERT_TEXT:
nativeInsertText(mNativeClass, (String) msg.obj);
break;
+ case SELECT_TEXT: {
+ int[] args = (int[]) msg.obj;
+ if (args == null) {
+ nativeClearTextSelection(mNativeClass);
+ } else {
+ nativeSelectText(mNativeClass, args[0],
+ args[1], args[2], args[3]);
+ }
+ break;
+ }
+ case SELECT_WORD_AT: {
+ int x = msg.arg1;
+ int y = msg.arg2;
+ nativeSelectWordAt(mNativeClass, x, y);
+ break;
+ }
+ case SELECT_ALL:
+ nativeSelectAll(mNativeClass);
+ break;
}
}
};
@@ -2700,11 +2723,11 @@
// called by JNI
private void updateTextSelection(int pointer, int start, int end,
- int textGeneration) {
+ int textGeneration, int selectionPtr) {
if (mWebView != null) {
Message.obtain(mWebView.mPrivateHandler,
WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
- new TextSelectionData(start, end)).sendToTarget();
+ new TextSelectionData(start, end, selectionPtr)).sendToTarget();
}
}
@@ -2771,7 +2794,7 @@
if (mWebView != null) {
Message.obtain(mWebView.mPrivateHandler,
WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
- textGeneration, new TextSelectionData(selStart, selEnd))
+ textGeneration, new TextSelectionData(selStart, selEnd, 0))
.sendToTarget();
}
}
@@ -3026,4 +3049,9 @@
*/
private native String nativeGetText(int nativeClass,
int startX, int startY, int endX, int endY);
+ private native void nativeSelectText(int nativeClass,
+ int startX, int startY, int endX, int endY);
+ private native void nativeClearTextSelection(int nativeClass);
+ private native void nativeSelectWordAt(int nativeClass, int x, int y);
+ private native void nativeSelectAll(int nativeClass);
}
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index d395fb2..84e86af 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -56,13 +56,13 @@
/**
* A widget that enables the user to select a number form a predefined range.
* The widget presents an input field and up and down buttons for selecting the
- * current value. Pressing/long pressing the up and down buttons increments and
+ * current value. Pressing/long-pressing the up and down buttons increments and
* decrements the current value respectively. Touching the input field shows a
- * scroll wheel, tapping on which while shown and not moving allows direct edit
- * of the current value. Sliding motions up or down hide the buttons and the
- * input field, show the scroll wheel, and rotate the latter. Flinging is
+ * scroll wheel, which when touched allows direct edit
+ * of the current value. Sliding gestures up or down hide the buttons and the
+ * input filed, show and rotate the scroll wheel. Flinging is
* also supported. The widget enables mapping from positions to strings such
- * that instead the position index the corresponding string is displayed.
+ * that, instead of the position index, the corresponding string is displayed.
* <p>
* For an example of using this widget, see {@link android.widget.TimePicker}.
* </p>
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 40d8a77..164bc64 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -256,10 +256,7 @@
private float mShadowRadius, mShadowDx, mShadowDy;
- private static final int PREDRAW_NOT_REGISTERED = 0;
- private static final int PREDRAW_PENDING = 1;
- private static final int PREDRAW_DONE = 2;
- private int mPreDrawState = PREDRAW_NOT_REGISTERED;
+ private boolean mPreDrawRegistered;
private TextUtils.TruncateAt mEllipsize = null;
@@ -365,8 +362,6 @@
private SpellChecker mSpellChecker;
- private boolean mSoftInputShownOnFocus = true;
-
// The alignment to pass to Layout, or null if not resolved.
private Layout.Alignment mLayoutAlignment;
@@ -2384,29 +2379,6 @@
}
/**
- * Sets whether the soft input method will be made visible when this
- * TextView gets focused. The default is true.
- *
- * @attr ref android.R.styleable#TextView_softInputShownOnFocus
- * @hide
- */
- @android.view.RemotableViewMethod
- public final void setSoftInputShownOnFocus(boolean show) {
- mSoftInputShownOnFocus = show;
- }
-
- /**
- * Returns whether the soft input method will be made visible when this
- * TextView gets focused. The default is true.
- *
- * @attr ref android.R.styleable#TextView_softInputShownOnFocus
- * @hide
- */
- public final boolean getSoftInputShownOnFocus() {
- return mSoftInputShownOnFocus;
- }
-
- /**
* Returns the list of URLSpans attached to the text
* (by {@link Linkify} or otherwise) if any. You can call
* {@link URLSpan#getURL} on them to find where they link to
@@ -4387,26 +4359,16 @@
}
private void registerForPreDraw() {
- final ViewTreeObserver observer = getViewTreeObserver();
-
- if (mPreDrawState == PREDRAW_NOT_REGISTERED) {
- observer.addOnPreDrawListener(this);
- mPreDrawState = PREDRAW_PENDING;
- } else if (mPreDrawState == PREDRAW_DONE) {
- mPreDrawState = PREDRAW_PENDING;
+ if (!mPreDrawRegistered) {
+ getViewTreeObserver().addOnPreDrawListener(this);
+ mPreDrawRegistered = true;
}
-
- // else state is PREDRAW_PENDING, so keep waiting.
}
/**
* {@inheritDoc}
*/
public boolean onPreDraw() {
- if (mPreDrawState != PREDRAW_PENDING) {
- return true;
- }
-
if (mLayout == null) {
assumeLayout();
}
@@ -4457,7 +4419,9 @@
startSelectionActionMode();
}
- mPreDrawState = PREDRAW_DONE;
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ mPreDrawRegistered = false;
+
return !changed;
}
@@ -4492,10 +4456,9 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- final ViewTreeObserver observer = getViewTreeObserver();
- if (mPreDrawState != PREDRAW_NOT_REGISTERED) {
- observer.removeOnPreDrawListener(this);
- mPreDrawState = PREDRAW_NOT_REGISTERED;
+ if (mPreDrawRegistered) {
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ mPreDrawRegistered = false;
}
if (mError != null) {
@@ -4768,12 +4731,6 @@
@Override
protected void onDraw(Canvas canvas) {
- if (mPreDrawState == PREDRAW_DONE) {
- final ViewTreeObserver observer = getViewTreeObserver();
- observer.removeOnPreDrawListener(this);
- mPreDrawState = PREDRAW_NOT_REGISTERED;
- }
-
if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return;
restartMarqueeIfNeeded();
@@ -5040,7 +4997,7 @@
if (mTextDisplayList == null || !mTextDisplayList.isValid() ||
!mTextDisplayListIsValid) {
if (mTextDisplayList == null) {
- mTextDisplayList = getHardwareRenderer().createDisplayList();
+ mTextDisplayList = getHardwareRenderer().createDisplayList("Text");
}
final HardwareCanvas hardwareCanvas = mTextDisplayList.start();
@@ -5539,7 +5496,7 @@
&& mLayout != null && onCheckIsTextEditor()) {
InputMethodManager imm = InputMethodManager.peekInstance();
viewClicked(imm);
- if (imm != null && mSoftInputShownOnFocus) {
+ if (imm != null) {
imm.showSoftInput(this, 0);
}
}
@@ -8395,7 +8352,7 @@
// Show the IME, except when selecting in read-only text.
final InputMethodManager imm = InputMethodManager.peekInstance();
viewClicked(imm);
- if (!mTextIsSelectable && mSoftInputShownOnFocus) {
+ if (!mTextIsSelectable) {
handled |= imm != null && imm.showSoftInput(this, 0);
}
@@ -10190,7 +10147,7 @@
}
final boolean selectionStarted = mSelectionActionMode != null || willExtract;
- if (selectionStarted && !mTextIsSelectable && mSoftInputShownOnFocus) {
+ if (selectionStarted && !mTextIsSelectable) {
// Show the IME to be able to replace text, except when selecting non editable text.
final InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
diff --git a/core/java/com/android/internal/util/FastMath.java b/core/java/com/android/internal/util/FastMath.java
index efd0871..88a17e6 100644
--- a/core/java/com/android/internal/util/FastMath.java
+++ b/core/java/com/android/internal/util/FastMath.java
@@ -26,8 +26,8 @@
* thought it may return slightly different results. It does not try to
* handle (in any meaningful way) NaN or infinities.
*/
- public static int round(float x) {
- long lx = (long)(x * (65536 * 256f));
- return (int)((lx + 0x800000) >> 24);
+ public static int round(float value) {
+ long lx = (long) (value * (65536 * 256f));
+ return (int) ((lx + 0x800000) >> 24);
}
}
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 6a533c0..e19bb38 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -188,6 +188,14 @@
renderer->finish();
}
+static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz) {
+ return OpenGLRenderer::getStencilSize();
+}
+
+// ----------------------------------------------------------------------------
+// Functor
+// ----------------------------------------------------------------------------
+
static bool android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, Functor *functor) {
android::uirenderer::Rect dirty;
@@ -618,6 +626,15 @@
return displayList->getSize();
}
+static void android_view_GLES20Canvas_setDisplayListName(JNIEnv* env,
+ jobject clazz, DisplayList* displayList, jstring name) {
+ if (name != NULL) {
+ const char* textArray = env->GetStringUTFChars(name, NULL);
+ displayList->setName(textArray);
+ env->ReleaseStringUTFChars(name, textArray);
+ }
+}
+
static OpenGLRenderer* android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env,
jobject clazz) {
return new DisplayListRenderer;
@@ -808,6 +825,8 @@
{ "nPrepareDirty", "(IIIIIZ)V", (void*) android_view_GLES20Canvas_prepareDirty },
{ "nFinish", "(I)V", (void*) android_view_GLES20Canvas_finish },
+ { "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize },
+
{ "nCallDrawGLFunction", "(II)Z",
(void*) android_view_GLES20Canvas_callDrawGLFunction },
@@ -880,10 +899,14 @@
{ "nGetDisplayList", "(II)I", (void*) android_view_GLES20Canvas_getDisplayList },
{ "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20Canvas_destroyDisplayList },
{ "nGetDisplayListSize", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListSize },
- { "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer },
- { "nResetDisplayListRenderer", "(I)V", (void*) android_view_GLES20Canvas_resetDisplayListRenderer },
+ { "nSetDisplayListName", "(ILjava/lang/String;)V",
+ (void*) android_view_GLES20Canvas_setDisplayListName },
{ "nDrawDisplayList", "(IIIILandroid/graphics/Rect;)Z",
(void*) android_view_GLES20Canvas_drawDisplayList },
+
+ { "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer },
+ { "nResetDisplayListRenderer", "(I)V", (void*) android_view_GLES20Canvas_resetDisplayListRenderer },
+
{ "nOutputDisplayList", "(II)V", (void*) android_view_GLES20Canvas_outputDisplayList },
{ "nInterrupt", "(I)V", (void*) android_view_GLES20Canvas_interrupt },
{ "nResume", "(I)V", (void*) android_view_GLES20Canvas_resume },
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 030a6b3..1175cbb 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -881,7 +881,7 @@
<string name="replace" msgid="5781686059063148930">"Erstat..."</string>
<string name="delete" msgid="6098684844021697789">"Slet"</string>
<string name="copyUrl" msgid="2538211579596067402">"Kopier webadresse"</string>
- <string name="selectTextMode" msgid="1018691815143165326">"Marker tekst"</string>
+ <string name="selectTextMode" msgid="1018691815143165326">"Markér tekst"</string>
<string name="textSelectionCABTitle" msgid="5236850394370820357">"Tekstmarkering"</string>
<string name="addToDictionary" msgid="9090375111134433012">"føj til ordbog"</string>
<string name="deleteText" msgid="7070985395199629156">"slet"</string>
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocus.java b/core/tests/coretests/src/android/widget/focus/RequestFocus.java
index af9ee17..21d762a 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocus.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocus.java
@@ -21,9 +21,7 @@
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
-import android.widget.LinearLayout;
import android.widget.Button;
-import android.view.View;
/**
* Exercises cases where elements of the UI are requestFocus()ed.
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
index a78b0c9..baf587e 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
@@ -16,21 +16,27 @@
package android.widget.focus;
-import android.widget.focus.RequestFocus;
-import com.android.frameworks.coretests.R;
-
import android.os.Handler;
-import android.test.ActivityInstrumentationTestCase;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
-import android.widget.Button;
import android.util.AndroidRuntimeException;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
+import android.widget.Button;
+
+import com.android.frameworks.coretests.R;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* {@link RequestFocusTest} is set up to exercise cases where the views that
* have focus become invisible or GONE.
*/
-public class RequestFocusTest extends ActivityInstrumentationTestCase<RequestFocus> {
+public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFocus> {
private Button mTopLeftButton;
private Button mBottomLeftButton;
@@ -39,7 +45,7 @@
private Handler mHandler;
public RequestFocusTest() {
- super("com.android.frameworks.coretests", RequestFocus.class);
+ super(RequestFocus.class);
}
@Override
@@ -94,4 +100,145 @@
e.getClass().getName());
}
}
+
+ /**
+ * This tests checks the case in which the first focusable View clears focus.
+ * In such a case the framework tries to give the focus to another View starting
+ * from the top. Hence, the framework will try to give focus to the view that
+ * wants to clear its focus. From a client perspective, the view does not loose
+ * focus after the call, therefore no callback for focus change should be invoked.
+ *
+ * @throws Exception If an error occurs.
+ */
+ @UiThreadTest
+ public void testOnFocusChangeNotCalledIfFocusDoesNotMove() throws Exception {
+ // Get the first focusable.
+ Button button = mTopLeftButton;
+
+ // Make sure that the button is the first focusable and focus it.
+ button.getRootView().requestFocus(View.FOCUS_DOWN);
+ assertTrue(button.hasFocus());
+
+ // Attach on focus change listener that should not be called.
+ button.setOnFocusChangeListener(new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ throw new IllegalStateException("Unexpeced call to"
+ + "OnFocusChangeListener#onFocusChange");
+ }
+ });
+
+ // Attach on global focus change listener that should not be called.
+ button.getViewTreeObserver().addOnGlobalFocusChangeListener(
+ new OnGlobalFocusChangeListener() {
+ @Override
+ public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+ throw new IllegalStateException("Unexpeced call to"
+ + "OnFocusChangeListener#onFocusChange");
+ }
+ });
+
+ // Try to clear focus.
+ button.clearFocus();
+ }
+
+ /**
+ * This tests check whether the on focus change callbacks are invoked in
+ * the proper order when a View loses focus and the framework gives it to
+ * the fist focusable one.
+ *
+ * @throws Exception
+ */
+ @UiThreadTest
+ public void testOnFocusChangeCallbackOrder() throws Exception {
+ // Get the first focusable.
+ Button clearingFocusButton = mTopRightButton;
+ Button gainingFocusButton = mTopLeftButton;
+
+ // Make sure that the clearing focus is not the first focusable.
+ View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null,
+ View.FOCUS_FORWARD);
+ assertNotSame("The clearing focus button is not the first focusable.",
+ clearingFocusButton, focusCandidate);
+ assertSame("The gaining focus button is the first focusable.",
+ gainingFocusButton, focusCandidate);
+
+ // Focus the clearing focus button.
+ clearingFocusButton.requestFocus();
+ assertTrue(clearingFocusButton.hasFocus());
+
+ // Register the invocation order checker.
+ CallbackOrderChecker checker = new CallbackOrderChecker(clearingFocusButton,
+ gainingFocusButton);
+ clearingFocusButton.setOnFocusChangeListener(checker);
+ gainingFocusButton.setOnFocusChangeListener(checker);
+ clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(checker);
+
+ // Try to clear focus.
+ clearingFocusButton.clearFocus();
+
+ // Check that no callback was invoked since focus did not move.
+ checker.verify();
+ }
+
+ /**
+ * This class check whether the focus change callback are invoked in order.
+ */
+ private class CallbackOrderChecker implements OnFocusChangeListener,
+ OnGlobalFocusChangeListener {
+
+ private class CallbackInvocation {
+ final String mMethodName;
+ final Object[] mArguments;
+
+ CallbackInvocation(String methodName, Object[] arguments) {
+ mMethodName = methodName;
+ mArguments = arguments;
+ }
+ }
+
+ private final View mClearingFocusView;
+ private final View mGainingFocusView;
+
+ private final List<CallbackInvocation> mInvocations = new ArrayList<CallbackInvocation>();
+
+ public CallbackOrderChecker(View clearingFocusView, View gainingFocusView) {
+ mClearingFocusView = clearingFocusView;
+ mGainingFocusView = gainingFocusView;
+ }
+
+ @Override
+ public void onFocusChange(View view, boolean hasFocus) {
+ CallbackInvocation invocation = new CallbackInvocation(
+ "OnFocusChangeListener#onFocusChange", new Object[] {view, hasFocus});
+ mInvocations.add(invocation);
+ }
+
+ @Override
+ public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+ CallbackInvocation invocation = new CallbackInvocation(
+ "OnFocusChangeListener#onFocusChange", new Object[] {oldFocus, newFocus});
+ mInvocations.add(invocation);
+ }
+
+ public void verify() {
+ assertSame("All focus change callback should be invoked.", 3, mInvocations.size());
+ assertInvioked("Callback for View clearing focus explected.", 0,
+ "OnFocusChangeListener#onFocusChange",
+ new Object[] {mClearingFocusView, false});
+ assertInvioked("Callback for View global focus change explected.", 1,
+ "OnFocusChangeListener#onFocusChange", new Object[] {mClearingFocusView,
+ mGainingFocusView});
+ assertInvioked("Callback for View gaining focus explected.", 2,
+ "OnFocusChangeListener#onFocusChange", new Object[] {mGainingFocusView, true});
+ }
+
+ private void assertInvioked(String message, int order, String methodName,
+ Object[] arguments) {
+ CallbackInvocation invocation = mInvocations.get(order);
+ assertEquals(message, methodName, invocation.mMethodName);
+ assertEquals(message, arguments[0], invocation.mArguments[0]);
+ assertEquals(message, arguments[1], invocation.mArguments[1]);
+ }
+ }
}
diff --git a/docs/html/guide/appendix/market-filters.jd b/docs/html/guide/appendix/market-filters.jd
index 07b9370..d9b2155 100644
--- a/docs/html/guide/appendix/market-filters.jd
+++ b/docs/html/guide/appendix/market-filters.jd
@@ -165,10 +165,10 @@
<p><strong>Example 1</strong><br />
The manifest declares <code><uses-sdk android:minSdkVersion="3"></code>
- and does not does not include a <code><supports-screens></code> element.
- <strong>Result</strong>: Android Market will not show the app to a user of a
- small-screen device, but will show it to users of normal and large-screen
- devices, users, unless other filters apply. </p>
+ and does not include a <code><supports-screens></code> element.
+ <strong>Result</strong>: Android Market does not show the app to a user of a
+ small-screen device, but does show it to users of normal and large-screen
+ devices, unless other filters also exclude those devices. </p>
<p><strong>Example 2<br />
</strong>The manifest declares <code><uses-sdk android:minSdkVersion="3"
android:targetSdkVersion="4"></code> and does not include a
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 4e5badd..6c01d44 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -8,15 +8,16 @@
<ul>
<li>
- <h2><span class="en">Android Basics</span>
- <span class="de" style="display:none">Einführung in Android</span>
- <span class="es" style="display:none">Información básica sobre Android</span>
- <span class="fr" style="display:none">Présentation d'Android</span>
- <span class="it" style="display:none">Nozioni di base su Android</span>
- <span class="ja" style="display:none">Android の基本</span>
- <span class="zh-CN" style="display:none">Android 基础知识</span>
- <span class="zh-TW" style="display:none">Android 簡介</span>
- </h2>
+ <span class="heading">
+ <span class="en">Android Basics</span>
+ <span class="de" style="display:none">Einführung in Android</span>
+ <span class="es" style="display:none">Información básica sobre Android</span>
+ <span class="fr" style="display:none">Présentation d'Android</span>
+ <span class="it" style="display:none">Nozioni di base su Android</span>
+ <span class="ja" style="display:none">Android の基本</span>
+ <span class="zh-CN" style="display:none">Android 基础知识</span>
+ <span class="zh-TW" style="display:none">Android 簡介</span>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>guide/basics/what-is-android.html">
<span class="en">What Is Android?</span>
@@ -46,7 +47,7 @@
</li>
<li>
- <h2>
+ <span class="heading">
<span class="en">Framework Topics</span>
<span class="de" style="display:none">Framework-Themen</span>
<span class="es" style="display:none">Temas sobre el framework</span>
@@ -55,7 +56,7 @@
<span class="ja" style="display:none">フレームワーク トピック</span>
<span class="zh-CN" style="display:none">框架主题</span>
<span class="zh-TW" style="display:none">架構主題</span>
- </h2>
+ </span>
<ul>
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/fundamentals/activities.html">
@@ -443,9 +444,9 @@
</li>
<li>
- <h2>
+ <span class="heading">
<span class="en">Android Market Topics</span>
- </h2>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>guide/publishing/publishing.html">
<span class="en">Publishing on Android Market</span>
@@ -489,15 +490,16 @@
<li>
- <h2><span class="en">Developing</span>
- <span class="de" style="display:none">Entwicklung</span>
- <span class="es" style="display:none">Desarrollo</span>
- <span class="fr" style="display:none">Développement</span>
- <span class="it" style="display:none">Sviluppo</span>
- <span class="ja" style="display:none">開発</span>
- <span class="zh-CN" style="display:none">开发</span>
- <span class="zh-TW" style="display:none">開發</span>
- </h2>
+ <span class="heading">
+ <span class="en">Developing</span>
+ <span class="de" style="display:none">Entwicklung</span>
+ <span class="es" style="display:none">Desarrollo</span>
+ <span class="fr" style="display:none">Développement</span>
+ <span class="it" style="display:none">Sviluppo</span>
+ <span class="ja" style="display:none">開発</span>
+ <span class="zh-CN" style="display:none">开发</span>
+ <span class="zh-TW" style="display:none">開發</span>
+ </span>
<ul>
<!--<li><a href="">Developing for Android</a></li>
signing, upgrading, selecting a package name, select device profile, touch, trackball, dpad available, etc. -->
@@ -683,15 +685,16 @@
</li>
<li>
- <h2><span class="en">Publishing</span>
- <span class="de" style="display:none">Veröffentlichung</span>
- <span class="es" style="display:none">Publicación</span>
- <span class="fr" style="display:none">Publication</span>
- <span class="it" style="display:none">Pubblicazione</span>
- <span class="ja" style="display:none">公開</span>
- <span class="zh-CN" style="display:none">发布</span>
- <span class="zh-TW" style="display:none">發佈</span>
- </h2>
+ <span class="heading">
+ <span class="en">Publishing</span>
+ <span class="de" style="display:none">Veröffentlichung</span>
+ <span class="es" style="display:none">Publicación</span>
+ <span class="fr" style="display:none">Publication</span>
+ <span class="it" style="display:none">Pubblicazione</span>
+ <span class="ja" style="display:none">公開</span>
+ <span class="zh-CN" style="display:none">发布</span>
+ <span class="zh-TW" style="display:none">發佈</span>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>guide/publishing/publishing_overview.html">
<span class="en">Publishing Overview</span>
@@ -733,15 +736,16 @@
</li>
<li>
- <h2><span class="en">Best Practices</span>
- <span class="de" style="display:none">Bewährte Verfahren</span>
- <span class="es" style="display:none">Prácticas recomendadas</span>
- <span class="fr" style="display:none">Meilleures pratiques</span>
- <span class="it" style="display:none">Best practice</span>
- <span class="ja" style="display:none">ベスト プラクティス</span>
- <span class="zh-CN" style="display:none">最佳实践</span>
- <span class="zh-TW" style="display:none">最佳實務</span>
- </h2>
+ <span class="heading">
+ <span class="en">Best Practices</span>
+ <span class="de" style="display:none">Bewährte Verfahren</span>
+ <span class="es" style="display:none">Prácticas recomendadas</span>
+ <span class="fr" style="display:none">Meilleures pratiques</span>
+ <span class="it" style="display:none">Best practice</span>
+ <span class="ja" style="display:none">ベスト プラクティス</span>
+ <span class="zh-CN" style="display:none">最佳实践</span>
+ <span class="zh-TW" style="display:none">最佳實務</span>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>guide/practices/compatibility.html">
<span class="en">Compatibility</span>
@@ -837,8 +841,9 @@
</li>
<li>
- <h2><span class="en">Web Applications</span>
- </h2>
+ <span class="heading">
+ <span class="en">Web Applications</span>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>guide/webapps/index.html">
<span class="en">Web Apps Overview</span>
@@ -859,15 +864,16 @@
</li>
<li>
- <h2><span class="en">Appendix</span>
- <span class="de" style="display:none">Anhang</span>
- <span class="es" style="display:none">Apéndice</span>
- <span class="fr" style="display:none">Annexes</span>
- <span class="it" style="display:none">Appendice</span>
- <span class="ja" style="display:none">付録</span>
- <span class="zh-CN" style="display:none">附录</span>
- <span class="zh-TW" style="display:none">附錄</span>
- </h2>
+ <span class="heading">
+ <span class="en">Appendix</span>
+ <span class="de" style="display:none">Anhang</span>
+ <span class="es" style="display:none">Apéndice</span>
+ <span class="fr" style="display:none">Annexes</span>
+ <span class="it" style="display:none">Appendice</span>
+ <span class="ja" style="display:none">付録</span>
+ <span class="zh-CN" style="display:none">附录</span>
+ <span class="zh-TW" style="display:none">附錄</span>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>guide/appendix/api-levels.html">
<span class="en">Android API Levels</span>
diff --git a/docs/html/guide/topics/ui/how-android-draws.jd b/docs/html/guide/topics/ui/how-android-draws.jd
index 3a57afa..6a8cd86 100644
--- a/docs/html/guide/topics/ui/how-android-draws.jd
+++ b/docs/html/guide/topics/ui/how-android-draws.jd
@@ -62,7 +62,7 @@
<p>
The measure pass uses two classes to communicate dimensions. The
- {@link android.view.View.MeasureSpec} class is used by Views to tell their parents how they
+ {@link android.view.ViewGroup.LayoutParams} class is used by Views to tell their parents how they
want to be measured and positioned. The base LayoutParams class just
describes how big the View wants to be for both width and height. For each
dimension, it can specify one of:</p>
diff --git a/docs/html/guide/topics/ui/notifiers/notifications.jd b/docs/html/guide/topics/ui/notifiers/notifications.jd
index 33b0fec..bef9671 100644
--- a/docs/html/guide/topics/ui/notifiers/notifications.jd
+++ b/docs/html/guide/topics/ui/notifiers/notifications.jd
@@ -245,31 +245,27 @@
activity like this can cause it to be mixed with your normal application back stack
in undesired ways. To make it behave correctly, in the manifest declaration
for the activity the attributes
-<code>android:launchMode="singleInstance"</code> and
+<code>android:launchMode="singleTask"</code>,
+<code>android:taskAffinity=""</code> and
<code>android:excludeFromRecents="true"</code>
must be set. The full activity declaration for this sample is:</p>
{@sample development/samples/ApiDemos/AndroidManifest.xml interstitial_affinity}
-<p>Because of the use of <code>singleInstance</code>, you must be careful about launching
-any other activities from this one. These activities will be launched
-in their own task, and care must be taken to make sure this interacts
-well with the current state of your application's task. This is essentially
+<p>You must be careful when launching other activities from this initial activity,
+because this is not a top-level part of the application, does not appear in
+recents, and needs to be relaunched at any point from the notification with new data
+to show. This best approach is to make sure any activity launched from it is
+launched in its own task. When doing this care must be taken to make sure this
+new task interacts well with the current state of your exiting application's
+task. This is essentially
the same as switching to the main application as described for the Email style
notification shown before. Given the <code>makeMessageIntentStack()</code>
-method previously shown, handling a click here would look something like this:</p>
+method previously shown, handling a click then would look something like this:</p>
{@sample development/samples/ApiDemos/src/com/example/android/apis/app/IncomingMessageInterstitial.java
app_launch}
-<p>If you don't want to use the <code>singleInstance</code> launch mode for
-this activity, an alternative approach is to use <code>android:taskAffinity=""</code>.
-This tells Android that the activity should not be treated as part of the
-main application flow, so it will not get mixed together with that. All of the
-other issues discussed here do still apply, though this would allow you to start
-additional activities that are part of this notification task instead of switching
-to and replacing the main application task.</p>
-
<h2 id="ManageYourNotifications">Managing your Notifications</h2>
<p>The {@link android.app.NotificationManager} is a system service that manages all
diff --git a/docs/html/index.jd b/docs/html/index.jd
index c14c0ae..fdf860a 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -1,4 +1,5 @@
home=true
+metaDescription=The official site for Android developers. Provides the Android SDK and documentation for app developers and designers.
@jd:body
@@ -153,7 +154,7 @@
+ "href='https://plus.google.com/108967384991768947849'>+Android Developers</a>. "
+ "We'll use it to host Hangouts for developers, talk about the latest releases, "
+ "development and design tips, and much more.</p>"
-+ "<div style='margin-top:.7em'><g:plus href='https://plus.google.com/108967384991768947849' "
++ "<div style='margin:.7em 0 0 -1.2em'><g:plus href='https://plus.google.com/108967384991768947849' "
+ "size=\"smallbadge\" width=\"275\"></g:plus></div>"
},
@@ -213,6 +214,3 @@
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(po, s);
})();</script>
-<style type="text/css">
- #___plus_0, #___plus_0 iframe, #___plus_0 { width: 275px !important; }
-</style>
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index 628df9e..8483037 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -1,7 +1,8 @@
<ul>
<li>
- <h2><span class="en">Android Training</span>
- </h2>
+ <span class="heading">
+ <span class="en">Android Training</span>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>training/index.html">
@@ -237,8 +238,9 @@
<li>
- <h2><span class="en">Technical Resources</span>
- </h2>
+ <span class="heading">
+ <span class="en">Technical Resources</span>
+ </span>
<ul>
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>resources/browser.html?tag=sample">
@@ -288,15 +290,16 @@
</ul>
</li>
<li>
- <h2><span class="en">Community</span>
- <span style="display:none" class="de"></span>
- <span style="display:none" class="es">Comunidad</span>
- <span style="display:none" class="fr">Communauté</span>
- <span style="display:none" class="it"></span>
- <span style="display:none" class="ja">コミュニティ</span>
- <span style="display:none" class="zh-CN">社区</span>
- <span style="display:none" class="zh-TW">社群</span>
- </h2>
+ <span class="heading">
+ <span class="en">Community</span>
+ <span style="display:none" class="de"></span>
+ <span style="display:none" class="es">Comunidad</span>
+ <span style="display:none" class="fr">Communauté</span>
+ <span style="display:none" class="it"></span>
+ <span style="display:none" class="ja">コミュニティ</span>
+ <span style="display:none" class="zh-CN">社区</span>
+ <span style="display:none" class="zh-TW">社群</span>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>resources/community-groups.html">
<span class="en">Developer Forums</span>
@@ -309,8 +312,9 @@
<?cs
if:android.whichdoc == "online" ?>
<li>
- <h2><span class="en">Device Dashboard</span>
- </h2>
+ <span class="heading">
+ <span class="en">Device Dashboard</span>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>resources/dashboard/platform-versions.html">
<span class="en">Platform Versions</span>
@@ -327,7 +331,9 @@
?>
<li>
- <h2><span class="en">More</span></h2>
+ <span class="heading">
+ <span class="en">More</span>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>resources/faq/commontasks.html">
<span class="en">Common Tasks </span>
diff --git a/docs/html/sdk/android-2.3.3.jd b/docs/html/sdk/android-2.3.3.jd
index 7a5b044..023e2e4 100644
--- a/docs/html/sdk/android-2.3.3.jd
+++ b/docs/html/sdk/android-2.3.3.jd
@@ -336,7 +336,7 @@
<li>English, New Zealand (en_NZ)</li>
<li>English, Singapore(en_SG)</li>
<li>English, US (en_US)</li>
-<li>English, Zimbabwe (en_ZA)</li>
+<li>English, South Africa (en_ZA)</li>
<li>Spanish (es_ES)</li>
<li>Spanish, US (es_US)</li>
<li>Finnish, Finland (fi_FI)</li>
diff --git a/docs/html/sdk/android-2.3.4.jd b/docs/html/sdk/android-2.3.4.jd
index 4cb44b9..eeaa69a 100644
--- a/docs/html/sdk/android-2.3.4.jd
+++ b/docs/html/sdk/android-2.3.4.jd
@@ -296,7 +296,7 @@
<li>English, New Zealand (en_NZ)</li>
<li>English, Singapore(en_SG)</li>
<li>English, US (en_US)</li>
-<li>English, Zimbabwe (en_ZA)</li>
+<li>English, South Africa (en_ZA)</li>
<li>Spanish (es_ES)</li>
<li>Spanish, US (es_US)</li>
<li>Finnish, Finland (fi_FI)</li>
diff --git a/docs/html/sdk/android-2.3.jd b/docs/html/sdk/android-2.3.jd
index e7aa0fa..fc4f5aa 100644
--- a/docs/html/sdk/android-2.3.jd
+++ b/docs/html/sdk/android-2.3.jd
@@ -859,7 +859,7 @@
<li>English, New Zealand (en_NZ)</li>
<li>English, Singapore(en_SG)</li>
<li>English, US (en_US)</li>
-<li>English, Zimbabwe (en_ZA)</li>
+<li>English, South Africa (en_ZA)</li>
<li>Spanish (es_ES)</li>
<li>Spanish, US (es_US)</li>
<li>Finnish, Finland (fi_FI)</li>
diff --git a/docs/html/sdk/android-3.0.jd b/docs/html/sdk/android-3.0.jd
index 96b92d9..49fefee 100644
--- a/docs/html/sdk/android-3.0.jd
+++ b/docs/html/sdk/android-3.0.jd
@@ -1119,7 +1119,7 @@
<li>English, New Zealand (en_NZ)</li>
<li>English, Singapore(en_SG)</li>
<li>English, US (en_US)</li>
-<li>English, Zimbabwe (en_ZA)</li>
+<li>English, South Africa (en_ZA)</li>
<li>Spanish (es_ES)</li>
<li>Spanish, US (es_US)</li>
<li>Finnish, Finland (fi_FI)</li>
diff --git a/docs/html/sdk/android-3.1.jd b/docs/html/sdk/android-3.1.jd
index 78f265d..b9cf969 100644
--- a/docs/html/sdk/android-3.1.jd
+++ b/docs/html/sdk/android-3.1.jd
@@ -1040,7 +1040,7 @@
<li>English, New Zealand (en_NZ)</li>
<li>English, Singapore(en_SG)</li>
<li>English, US (en_US)</li>
-<li>English, Zimbabwe (en_ZA)</li>
+<li>English, South Africa (en_ZA)</li>
<li>Spanish (es_ES)</li>
<li>Spanish, US (es_US)</li>
<li>Finnish, Finland (fi_FI)</li>
diff --git a/docs/html/sdk/android-4.0.3.jd b/docs/html/sdk/android-4.0.3.jd
index 1fca8df..809c83c 100644
--- a/docs/html/sdk/android-4.0.3.jd
+++ b/docs/html/sdk/android-4.0.3.jd
@@ -435,7 +435,7 @@
<li>English, New Zealand (en_NZ)</li>
<li>English, Singapore(en_SG)</li>
<li>English, US (en_US)</li>
-<li>English, Zimbabwe (en_ZA)</li>
+<li>English, South Africa (en_ZA)</li>
<li>Spanish (es_ES)</li>
<li>Spanish, US (es_US)</li>
<li>Finnish, Finland (fi_FI)</li>
diff --git a/docs/html/sdk/android-4.0.jd b/docs/html/sdk/android-4.0.jd
index 5f55947..2cad86b 100644
--- a/docs/html/sdk/android-4.0.jd
+++ b/docs/html/sdk/android-4.0.jd
@@ -1963,7 +1963,7 @@
<li>English, New Zealand (en_NZ)</li>
<li>English, Singapore(en_SG)</li>
<li>English, US (en_US)</li>
-<li>English, Zimbabwe (en_ZA)</li>
+<li>English, South Africa (en_ZA)</li>
<li>Spanish (es_ES)</li>
<li>Spanish, US (es_US)</li>
<li>Finnish, Finland (fi_FI)</li>
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index f15da78..30825ee 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -42,7 +42,7 @@
<p>Developing in Eclipse with ADT is highly recommended and is the fastest way
to get started. With the guided project setup it provides, as well as tools
-integration, custom XML editors, and debug ouput pane, ADT gives you an
+integration, custom XML editors, and debug output pane, ADT gives you an
incredible boost in developing Android applications. </p>
<p>This document provides step-by-step instructions on how to download the ADT
@@ -1104,7 +1104,7 @@
<h3 id="downloading">Downloading the ADT Plugin</h3>
<p>Use the Update Manager feature of your Eclipse installation to install the latest
-revision of ADT on your development computer.<>
+revision of ADT on your development computer.</p>
<p>Assuming that you have a compatible version of the Eclipse IDE installed, as
described in <a href="#preparing">Preparing for Installation</a>, above, follow
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 65a1f46..5cf05e0 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -1,4 +1,5 @@
page.title=Android SDK
+page.metaDescription=Download the official Android SDK to develop apps for Android-powered devices.
sdk.redirect=0
sdk.win_installer=installer_r16-windows.exe
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 5f6a57f..0de477a 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -1,7 +1,7 @@
<?cs if:!sdk.redirect ?>
<ul>
<li>
- <h2>
+ <span class="heading">
<span class="en">Android SDK Starter Package</span>
<span style="display:none" class="de">Aktuelle SDK-Version</span>
<span style="display:none" class="es">Versión actual del SDK</span>
@@ -10,7 +10,7 @@
<span style="display:none" class="ja">現在リリースされている SDK</span>
<span style="display:none" class="zh-CN">当前的 SDK 版本</span>
<span style="display:none" class="zh-TW">目前 SDK 發行版本</span>
- </h2>
+ </span>
<ul><?cs
if:android.whichdoc == "online" ?>
<li><a href="<?cs var:toroot ?>sdk/index.html">
@@ -37,7 +37,8 @@
</ul>
</li><?cs
if:sdk.preview ?>
- <li><h2>Android 3.0 Preview SDK</h2>
+ <li>
+ <span class="heading">Android 3.0 Preview SDK</span>
<ul>
<li><a href="<?cs var:toroot ?>sdk/preview/start.html">Getting Started</a> <span
class="new">new!</span></li>
@@ -46,13 +47,14 @@
/if ?>
<?cs
if:sdk.preview ?>
- <li><h2>Android x.x Preview</h2>
+ <li>
+ <span class="heading">Android x.x Preview</span>
<ul>
</ul>
</li><?cs
/if ?>
<li>
- <h2>
+ <span class="heading">
<span class="en">Downloadable SDK Components</span>
<span style="display:none" class="de"></span>
<span style="display:none" class="es"></span>
@@ -61,7 +63,7 @@
<span style="display:none" class="ja"></span>
<span style="display:none" class="zh-CN"></span>
<span style="display:none" class="zh-TW"></span>
- </h2>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>sdk/adding-components.html">
<span class="en">Adding SDK Components</span>
@@ -158,7 +160,7 @@
</ul>
</li>
<li>
- <h2>
+ <span class="heading">
<span class="en">ADT Plugin for Eclipse</span>
<span style="display:none" class="de"></span>
<span style="display:none" class="es"></span>
@@ -167,7 +169,7 @@
<span style="display:none" class="ja"></span>
<span style="display:none" class="zh-CN"></span>
<span style="display:none" class="zh-TW"></span>
- </h2>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 16.0.1
<span style="display:none" class="de"></span>
@@ -182,7 +184,8 @@
</ul>
</li>
<li>
- <h2><span class="en">Native Development Tools</span>
+ <span class="heading">
+ <span class="en">Native Development Tools</span>
<span style="display:none" class="de"></span>
<span style="display:none" class="es"></span>
<span style="display:none" class="fr"></span>
@@ -190,7 +193,7 @@
<span style="display:none" class="ja"></span>
<span style="display:none" class="zh-CN"></span>
<span style="display:none" class="zh-TW"></span>
- </h2>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r7</a>
<span class="new">new!</span>
@@ -200,7 +203,7 @@
</li>
<li>
- <h2>
+ <span class="heading">
<span class="en">More Information</span>
<span style="display:none" class="de"></span>
<span style="display:none" class="es"></span>
@@ -209,7 +212,7 @@
<span style="display:none" class="ja"></span>
<span style="display:none" class="zh-CN"></span>
<span style="display:none" class="zh-TW"></span>
- </h2>
+ </span>
<ul>
<li><a href="<?cs var:toroot ?>sdk/oem-usb.html">
<span class="en">OEM USB Drivers</span>
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 82ed199..96a71e3 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -28,8 +28,8 @@
the the colors are distributed evenly along the gradient line.
@param tile The Shader tiling mode
*/
- public LinearGradient(float x0, float y0, float x1, float y1,
- int colors[], float positions[], TileMode tile) {
+ public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
+ TileMode tile) {
if (colors.length < 2) {
throw new IllegalArgumentException("needs >= 2 number of colors");
}
@@ -50,8 +50,8 @@
@param color1 The color at the end of the gradient line.
@param tile The Shader tiling mode
*/
- public LinearGradient(float x0, float y0, float x1, float y1,
- int color0, int color1, TileMode tile) {
+ public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,
+ TileMode tile) {
native_instance = nativeCreate2(x0, y0, x1, y1, color0, color1, tile.nativeInt);
native_shader = nativePostCreate2(native_instance, x0, y0, x1, y1, color0, color1,
tile.nativeInt);
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 7830224..0ada1fb 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -76,13 +76,21 @@
}
@Override
- public boolean equals(Object obj) {
- Rect r = (Rect) obj;
- if (r != null) {
- return left == r.left && top == r.top && right == r.right
- && bottom == r.bottom;
- }
- return false;
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Rect r = (Rect) o;
+ return left == r.left && top == r.top && right == r.right && bottom == r.bottom;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = left;
+ result = 31 * result + top;
+ result = 31 * result + right;
+ result = 31 * result + bottom;
+ return result;
}
@Override
diff --git a/graphics/java/android/graphics/RectF.java b/graphics/java/android/graphics/RectF.java
index 00e9609..293dfcc 100644
--- a/graphics/java/android/graphics/RectF.java
+++ b/graphics/java/android/graphics/RectF.java
@@ -79,6 +79,24 @@
bottom = r.bottom;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Rect r = (Rect) o;
+ return left == r.left && top == r.top && right == r.right && bottom == r.bottom;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (left != +0.0f ? Float.floatToIntBits(left) : 0);
+ result = 31 * result + (top != +0.0f ? Float.floatToIntBits(top) : 0);
+ result = 31 * result + (right != +0.0f ? Float.floatToIntBits(right) : 0);
+ result = 31 * result + (bottom != +0.0f ? Float.floatToIntBits(bottom) : 0);
+ return result;
+ }
+
public String toString() {
return "RectF(" + left + ", " + top + ", "
+ right + ", " + bottom + ")";
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 50964d5..5b50beb 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -173,9 +173,20 @@
}
/**
- * Specify radii for each of the 4 corners. For each corner, the array
- * contains 2 values, [X_radius, Y_radius]. The corners are ordered
- * top-left, top-right, bottom-right, bottom-left
+ * <p>Specify radii for each of the 4 corners. For each corner, the array
+ * contains 2 values, <code>[X_radius, Y_radius]</code>. The corners are ordered
+ * top-left, top-right, bottom-right, bottom-left. This property
+ * is honored only when the shape is of type {@link #RECTANGLE}.</p>
+ * <p><strong>Note</strong>: changing this property will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing this property.</p>
+ *
+ * @param radii 4 pairs of X and Y radius for each corner, specified in pixels.
+ * The length of this array must be >= 8
+ *
+ * @see #mutate()
+ * @see #setCornerRadii(float[])
+ * @see #setShape(int)
*/
public void setCornerRadii(float[] radii) {
mGradientState.setCornerRadii(radii);
@@ -184,23 +195,57 @@
}
/**
- * Specify radius for the corners of the gradient. If this is > 0, then the
- * drawable is drawn in a round-rectangle, rather than a rectangle.
+ * <p>Specify radius for the corners of the gradient. If this is > 0, then the
+ * drawable is drawn in a round-rectangle, rather than a rectangle. This property
+ * is honored only when the shape is of type {@link #RECTANGLE}.</p>
+ * <p><strong>Note</strong>: changing this property will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing this property.</p>
+ *
+ * @param radius The radius in pixels of the corners of the rectangle shape
+ *
+ * @see #mutate()
+ * @see #setCornerRadii(float[])
+ * @see #setShape(int)
*/
public void setCornerRadius(float radius) {
mGradientState.setCornerRadius(radius);
mPathIsDirty = true;
invalidateSelf();
}
-
+
/**
- * Set the stroke width and color for the drawable. If width is zero,
- * then no stroke is drawn.
+ * <p>Set the stroke width and color for the drawable. If width is zero,
+ * then no stroke is drawn.</p>
+ * <p><strong>Note</strong>: changing this property will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing this property.</p>
+ *
+ * @param width The width in pixels of the stroke
+ * @param color The color of the stroke
+ *
+ * @see #mutate()
+ * @see #setStroke(int, int, float, float)
*/
public void setStroke(int width, int color) {
setStroke(width, color, 0, 0);
}
-
+
+ /**
+ * <p>Set the stroke width and color for the drawable. If width is zero,
+ * then no stroke is drawn. This method can also be used to dash the stroke.</p>
+ * <p><strong>Note</strong>: changing this property will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing this property.</p>
+ *
+ * @param width The width in pixels of the stroke
+ * @param color The color of the stroke
+ * @param dashWidth The length in pixels of the dashes, set to 0 to disable dashes
+ * @param dashGap The gap in pixels between dashes
+ *
+ * @see #mutate()
+ * @see #setStroke(int, int)
+ */
public void setStroke(int width, int color, float dashWidth, float dashGap) {
mGradientState.setStroke(width, color, dashWidth, dashGap);
@@ -218,13 +263,37 @@
mStrokePaint.setPathEffect(e);
invalidateSelf();
}
-
+
+
+ /**
+ * <p>Sets the size of the shape drawn by this drawable.</p>
+ * <p><strong>Note</strong>: changing this property will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing this property.</p>
+ *
+ * @param width The width of the shape used by this drawable
+ * @param height The height of the shape used by this drawable
+ *
+ * @see #mutate()
+ * @see #setGradientType(int)
+ */
public void setSize(int width, int height) {
mGradientState.setSize(width, height);
mPathIsDirty = true;
invalidateSelf();
}
-
+
+ /**
+ * <p>Sets the type of shape used to draw the gradient.</p>
+ * <p><strong>Note</strong>: changing this property will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing this property.</p>
+ *
+ * @param shape The desired shape for this drawable: {@link #LINE},
+ * {@link #OVAL}, {@link #RECTANGLE} or {@link #RING}
+ *
+ * @see #mutate()
+ */
public void setShape(int shape) {
mRingPath = null;
mPathIsDirty = true;
@@ -232,24 +301,73 @@
invalidateSelf();
}
+ /**
+ * <p>Sets the type of gradient used by this drawable..</p>
+ * <p><strong>Note</strong>: changing this property will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing this property.</p>
+ *
+ * @param gradient The type of the gradient: {@link #LINEAR_GRADIENT},
+ * {@link #RADIAL_GRADIENT} or {@link #SWEEP_GRADIENT}
+ *
+ * @see #mutate()
+ */
public void setGradientType(int gradient) {
mGradientState.setGradientType(gradient);
mRectIsDirty = true;
invalidateSelf();
}
+ /**
+ * <p>Sets the center location of the gradient. The radius is honored only when
+ * the gradient type is set to {@link #RADIAL_GRADIENT} or {@link #SWEEP_GRADIENT}.</p>
+ * <p><strong>Note</strong>: changing this property will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing this property.</p>
+ *
+ * @param x The x coordinate of the gradient's center
+ * @param y The y coordinate of the gradient's center
+ *
+ * @see #mutate()
+ * @see #setGradientType(int)
+ */
public void setGradientCenter(float x, float y) {
mGradientState.setGradientCenter(x, y);
mRectIsDirty = true;
invalidateSelf();
}
+ /**
+ * <p>Sets the radius of the gradient. The radius is honored only when the
+ * gradient type is set to {@link #RADIAL_GRADIENT}.</p>
+ * <p><strong>Note</strong>: changing this property will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing this property.</p>
+ *
+ * @param gradientRadius The radius of the gradient in pixels
+ *
+ * @see #mutate()
+ * @see #setGradientType(int)
+ */
public void setGradientRadius(float gradientRadius) {
mGradientState.setGradientRadius(gradientRadius);
mRectIsDirty = true;
invalidateSelf();
}
+ /**
+ * <p>Sets whether or not this drawable will honor its <code>level</code>
+ * property.</p>
+ * <p><strong>Note</strong>: changing this property will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing this property.</p>
+ *
+ * @param useLevel True if this drawable should honor its level, false otherwise
+ *
+ * @see #mutate()
+ * @see #setLevel(int)
+ * @see #getLevel()
+ */
public void setUseLevel(boolean useLevel) {
mGradientState.mUseLevel = useLevel;
mRectIsDirty = true;
@@ -261,6 +379,47 @@
return alpha * scale >> 8;
}
+ /**
+ * Returns the orientation of the gradient defined in this drawable.
+ */
+ public Orientation getOrientation() {
+ return mGradientState.mOrientation;
+ }
+
+ /**
+ * <p>Changes the orientation of the gradient defined in this drawable.</p>
+ * <p><strong>Note</strong>: changing orientation will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing the orientation.</p>
+ *
+ * @param orientation The desired orientation (angle) of the gradient
+ *
+ * @see #mutate()
+ */
+ public void setOrientation(Orientation orientation) {
+ mGradientState.mOrientation = orientation;
+ mRectIsDirty = true;
+ invalidateSelf();
+ }
+
+ /**
+ * <p>Sets the colors used to draw the gradient. Each color is specified as an
+ * ARGB integer and the array must contain at least 2 colors.</p>
+ * <p><strong>Note</strong>: changing orientation will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing the orientation.</p>
+ *
+ * @param colors 2 or more ARGB colors
+ *
+ * @see #mutate()
+ * @see #setColor(int)
+ */
+ public void setColors(int[] colors) {
+ mGradientState.setColors(colors);
+ mRectIsDirty = true;
+ invalidateSelf();
+ }
+
@Override
public void draw(Canvas canvas) {
if (!ensureValidRect()) {
@@ -442,6 +601,17 @@
return ringPath;
}
+ /**
+ * <p>Changes this drawbale to use a single color instead of a gradient.</p>
+ * <p><strong>Note</strong>: changing orientation will affect all instances
+ * of a drawable loaded from a resource. It is recommended to invoke
+ * {@link #mutate()} before changing the orientation.</p>
+ *
+ * @param argb The color used to fill the shape
+ *
+ * @see #mutate()
+ * @see #setColors(int[])
+ */
public void setColor(int argb) {
mGradientState.setSolidColor(argb);
mFillPaint.setColor(argb);
@@ -450,10 +620,9 @@
@Override
public int getChangingConfigurations() {
- return super.getChangingConfigurations()
- | mGradientState.mChangingConfigurations;
+ return super.getChangingConfigurations() | mGradientState.mChangingConfigurations;
}
-
+
@Override
public void setAlpha(int alpha) {
if (alpha != mAlpha) {
@@ -480,7 +649,6 @@
@Override
public int getOpacity() {
- // XXX need to figure out the actual opacity...
return PixelFormat.TRANSLUCENT;
}
@@ -911,11 +1079,6 @@
private float mGradientRadius = 0.5f;
private boolean mUseLevel;
private boolean mUseLevelForShape;
-
-
- GradientState() {
- mOrientation = Orientation.TOP_BOTTOM;
- }
GradientState(Orientation orientation, int[] colors) {
mOrientation = orientation;
@@ -987,6 +1150,11 @@
mCenterY = y;
}
+ public void setColors(int[] colors) {
+ mHasSolidColor = false;
+ mColors = colors;
+ }
+
public void setSolidColor(int argb) {
mHasSolidColor = true;
mSolidColor = argb;
@@ -1055,4 +1223,3 @@
}
}
}
-
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index bc1db4d..07bf7bf 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2011-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.
@@ -567,7 +567,7 @@
jint len = _env->GetArrayLength(data);
LOG_API("nAllocationElementData1D, con(%p), alloc(%p), offset(%i), comp(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, compIdx, len, sizeBytes);
jbyte *ptr = _env->GetByteArrayElements(data, NULL);
- rsAllocation1DElementData(con, (RsAllocation)alloc, offset, lod, ptr, compIdx, sizeBytes);
+ rsAllocation1DElementData(con, (RsAllocation)alloc, offset, lod, ptr, sizeBytes, compIdx);
_env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
}
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index c4cc947..a295e9a 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -42,10 +42,10 @@
typedef void *buffer_id;
typedef void *node_id;
- // Given the calling process' pid, returns true iff
+ // Given a node_id and the calling process' pid, returns true iff
// the implementation of the OMX interface lives in the same
// process.
- virtual bool livesLocally(pid_t pid) = 0;
+ virtual bool livesLocally(node_id node, pid_t pid) = 0;
struct ComponentInfo {
String8 mName;
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index d1af1a3..0ef8469 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -55,6 +55,16 @@
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+ if (extensions.hasDebugMarker()) {
+ eventMark = glInsertEventMarkerEXT;
+ startMark = glPushGroupMarkerEXT;
+ endMark = glPopGroupMarkerEXT;
+ } else {
+ eventMark = eventMarkNull;
+ startMark = startMarkNull;
+ endMark = endMarkNull;
+ }
+
init();
mDebugLevel = readDebugLevel();
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 409584f..f8c7bcc 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -240,7 +240,15 @@
GammaFontRenderer fontRenderer;
ResourceCache resourceCache;
+ PFNGLINSERTEVENTMARKEREXTPROC eventMark;
+ PFNGLPUSHGROUPMARKEREXTPROC startMark;
+ PFNGLPOPGROUPMARKEREXTPROC endMark;
+
private:
+ static void eventMarkNull(GLsizei length, const GLchar *marker) { }
+ static void startMarkNull(GLsizei length, const GLchar *marker) { }
+ static void endMarkNull() { }
+
GLuint mCurrentBuffer;
GLuint mCurrentIndicesBuffer;
void* mCurrentPositionPointer;
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index ee935e4..1a11fbc 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -19,9 +19,10 @@
#include "DisplayListLogBuffer.h"
#include "DisplayListRenderer.h"
-#include <utils/String8.h>
#include "Caches.h"
+#include <utils/String8.h>
+
namespace android {
namespace uirenderer {
@@ -217,7 +218,7 @@
indent[i] = ' ';
}
indent[count] = '\0';
- ALOGD("%sStart display list (%p)", (char*) indent + 2, this);
+ ALOGD("%sStart display list (%p, %s)", (char*) indent + 2, this, mName.string());
int saveCount = renderer.getSaveCount() - 1;
@@ -562,9 +563,11 @@
indent[i] = ' ';
}
indent[count] = '\0';
- DISPLAY_LIST_LOGD("%sStart display list (%p)", (char*) indent + 2, this);
+ DISPLAY_LIST_LOGD("%sStart display list (%p, %s)", (char*) indent + 2, this, mName.string());
#endif
+ renderer.startMark(mName.string());
+
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
int saveCount = renderer.getSaveCount() - 1;
while (!mReader.eof()) {
@@ -575,7 +578,9 @@
case DrawGLFunction: {
Functor *functor = (Functor *) getInt();
DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], functor);
+ renderer.startMark("GL functor");
needsInvalidate |= renderer.callDrawGLFunction(functor, dirty);
+ renderer.endMark();
}
break;
case Save: {
@@ -934,6 +939,8 @@
}
}
+ renderer.endMark();
+
DISPLAY_LIST_LOGD("%sDone, returning %d", (char*) indent + 2, needsInvalidate);
return needsInvalidate;
}
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 6fe4205..46506e4 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -28,6 +28,8 @@
#include <cutils/compiler.h>
+#include <utils/String8.h>
+
#include "DisplayListLogBuffer.h"
#include "OpenGLRenderer.h"
#include "utils/Functor.h"
@@ -128,6 +130,12 @@
return mIsRenderable;
}
+ void setName(const char* name) {
+ if (name) {
+ mName.setTo(name);
+ }
+ }
+
private:
void init();
@@ -224,6 +232,8 @@
size_t mSize;
bool mIsRenderable;
+
+ String8 mName;
};
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 1069e3f..f11fecc 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -67,6 +67,7 @@
mHasNPot = hasExtension("GL_OES_texture_npot");
mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");
mHasDiscardFramebuffer = hasExtension("GL_EXT_discard_framebuffer");
+ mHasDebugMarker = hasExtension("GL_EXT_debug_marker");
const char* vendor = (const char*) glGetString(GL_VENDOR);
EXT_LOGD("Vendor: %s", vendor);
@@ -82,6 +83,7 @@
inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
inline bool needsHighpTexCoords() const { return mNeedsHighpTexCoords; }
inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; }
+ inline bool hasDebugMarker() const { return mHasDebugMarker; }
bool hasExtension(const char* extension) const {
const String8 s(extension);
@@ -101,6 +103,7 @@
bool mNeedsHighpTexCoords;
bool mHasFramebufferFetch;
bool mHasDiscardFramebuffer;
+ bool mHasDebugMarker;
}; // class Extensions
}; // namespace uirenderer
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index cc0e05e..bd213d5 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -124,9 +124,25 @@
}
///////////////////////////////////////////////////////////////////////////////
+// Debug
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::startMark(const char* name) const {
+ mCaches.startMark(0, name);
+}
+
+void OpenGLRenderer::endMark() const {
+ mCaches.endMark();
+}
+
+///////////////////////////////////////////////////////////////////////////////
// Setup
///////////////////////////////////////////////////////////////////////////////
+uint32_t OpenGLRenderer::getStencilSize() {
+ return STENCIL_BUFFER_SIZE;
+}
+
void OpenGLRenderer::setViewport(int width, int height) {
mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index ae355bb..3c2d09e 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -141,6 +141,11 @@
SkPaint* filterPaint(SkPaint* paint);
+ ANDROID_API static uint32_t getStencilSize();
+
+ void startMark(const char* name) const;
+ void endMark() const;
+
protected:
/**
* Compose the layer defined in the current snapshot with the layer
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 2eae0f1..71fb8da 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -37,6 +37,11 @@
// Textures used by layers must have dimensions multiples of this number
#define LAYER_SIZE 64
+// Defines the size in bits of the stencil buffer
+// Note: We only want 1 bit, but in practice we'll get 8 bits on all GPUs
+// for the foreseeable future
+#define STENCIL_BUFFER_SIZE 0
+
/**
* Debug level for app developers.
*/
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 20b1f52..6887b22 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -162,7 +162,7 @@
param uint32_t x
param uint32_t lod
param const void *data
- param uint32_t comp_offset
+ param size_t comp_offset
}
Allocation2DData {
@@ -183,7 +183,7 @@
param uint32_t lod
param RsAllocationCubemapFace face
param const void *data
- param uint32_t element_offset
+ param size_t element_offset
}
AllocationGenerateMipmaps {
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 2773d5c..972e3d6 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2009-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.
@@ -71,11 +71,11 @@
}
void Allocation::data(Context *rsc, uint32_t xoff, uint32_t lod,
- uint32_t count, const void *data, uint32_t sizeBytes) {
- const uint32_t eSize = mHal.state.type->getElementSizeBytes();
+ uint32_t count, const void *data, size_t sizeBytes) {
+ const size_t eSize = mHal.state.type->getElementSizeBytes();
if ((count * eSize) != sizeBytes) {
- ALOGE("Allocation::subData called with mismatched size expected %i, got %i",
+ ALOGE("Allocation::subData called with mismatched size expected %zu, got %zu",
(count * eSize), sizeBytes);
mHal.state.type->dumpLOGV("type info");
return;
@@ -86,14 +86,14 @@
}
void Allocation::data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face,
- uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes) {
- const uint32_t eSize = mHal.state.elementSizeBytes;
- const uint32_t lineSize = eSize * w;
+ uint32_t w, uint32_t h, const void *data, size_t sizeBytes) {
+ const size_t eSize = mHal.state.elementSizeBytes;
+ const size_t lineSize = eSize * w;
//ALOGE("data2d %p, %i %i %i %i %i %i %p %i", this, xoff, yoff, lod, face, w, h, data, sizeBytes);
if ((lineSize * h) != sizeBytes) {
- ALOGE("Allocation size mismatch, expected %i, got %i", (lineSize * h), sizeBytes);
+ ALOGE("Allocation size mismatch, expected %zu, got %zu", (lineSize * h), sizeBytes);
rsAssert(!"Allocation::subData called with mismatched size");
return;
}
@@ -104,12 +104,12 @@
void Allocation::data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t zoff,
uint32_t lod, RsAllocationCubemapFace face,
- uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes) {
+ uint32_t w, uint32_t h, uint32_t d, const void *data, size_t sizeBytes) {
}
void Allocation::elementData(Context *rsc, uint32_t x, const void *data,
- uint32_t cIdx, uint32_t sizeBytes) {
- uint32_t eSize = mHal.state.elementSizeBytes;
+ uint32_t cIdx, size_t sizeBytes) {
+ size_t eSize = mHal.state.elementSizeBytes;
if (cIdx >= mHal.state.type->getElement()->getFieldCount()) {
ALOGE("Error Allocation::subElementData component %i out of range.", cIdx);
@@ -125,7 +125,7 @@
const Element * e = mHal.state.type->getElement()->getField(cIdx);
if (sizeBytes != e->getSizeBytes()) {
- ALOGE("Error Allocation::subElementData data size %i does not match field size %zu.", sizeBytes, e->getSizeBytes());
+ ALOGE("Error Allocation::subElementData data size %zu does not match field size %zu.", sizeBytes, e->getSizeBytes());
rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size.");
return;
}
@@ -135,8 +135,8 @@
}
void Allocation::elementData(Context *rsc, uint32_t x, uint32_t y,
- const void *data, uint32_t cIdx, uint32_t sizeBytes) {
- uint32_t eSize = mHal.state.elementSizeBytes;
+ const void *data, uint32_t cIdx, size_t sizeBytes) {
+ size_t eSize = mHal.state.elementSizeBytes;
if (x >= mHal.state.dimensionX) {
ALOGE("Error Allocation::subElementData X offset %i out of range.", x);
@@ -159,7 +159,7 @@
const Element * e = mHal.state.type->getElement()->getField(cIdx);
if (sizeBytes != e->getSizeBytes()) {
- ALOGE("Error Allocation::subElementData data size %i does not match field size %zu.", sizeBytes, e->getSizeBytes());
+ ALOGE("Error Allocation::subElementData data size %zu does not match field size %zu.", sizeBytes, e->getSizeBytes());
rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size.");
return;
}
@@ -249,7 +249,7 @@
delete[] sizeUnpadded;
}
-void Allocation::unpackVec3Allocation(const void *data, uint32_t dataSize) {
+void Allocation::unpackVec3Allocation(const void *data, size_t dataSize) {
const uint8_t *src = (const uint8_t*)data;
uint8_t *dst = (uint8_t*)getPtr();
@@ -519,13 +519,13 @@
}
void rsi_Allocation2DElementData(Context *rsc, RsAllocation va, uint32_t x, uint32_t y, uint32_t lod, RsAllocationCubemapFace face,
- const void *data, size_t eoff, uint32_t sizeBytes) { // TODO: this seems wrong, eoff and sizeBytes may be swapped
+ const void *data, size_t sizeBytes, size_t eoff) {
Allocation *a = static_cast<Allocation *>(va);
a->elementData(rsc, x, y, data, eoff, sizeBytes);
}
void rsi_Allocation1DElementData(Context *rsc, RsAllocation va, uint32_t x, uint32_t lod,
- const void *data, size_t eoff, uint32_t sizeBytes) { // TODO: this seems wrong, eoff and sizeBytes may be swapped
+ const void *data, size_t sizeBytes, size_t eoff) {
Allocation *a = static_cast<Allocation *>(va);
a->elementData(rsc, x, data, eoff, sizeBytes);
}
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
index 4ce863a..20201ca 100644
--- a/libs/rs/rsAllocation.h
+++ b/libs/rs/rsAllocation.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2009-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.
@@ -80,16 +80,16 @@
void resize1D(Context *rsc, uint32_t dimX);
void resize2D(Context *rsc, uint32_t dimX, uint32_t dimY);
- void data(Context *rsc, uint32_t xoff, uint32_t lod, uint32_t count, const void *data, uint32_t sizeBytes);
+ void data(Context *rsc, uint32_t xoff, uint32_t lod, uint32_t count, const void *data, size_t sizeBytes);
void data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face,
- uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes);
+ uint32_t w, uint32_t h, const void *data, size_t sizeBytes);
void data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t lod, RsAllocationCubemapFace face,
- uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes);
+ uint32_t w, uint32_t h, uint32_t d, const void *data, size_t sizeBytes);
void elementData(Context *rsc, uint32_t x,
- const void *data, uint32_t elementOff, uint32_t sizeBytes);
+ const void *data, uint32_t elementOff, size_t sizeBytes);
void elementData(Context *rsc, uint32_t x, uint32_t y,
- const void *data, uint32_t elementOff, uint32_t sizeBytes);
+ const void *data, uint32_t elementOff, size_t sizeBytes);
void read(void *data);
@@ -138,7 +138,7 @@
uint32_t getPackedSize() const;
static void writePackedData(const Type *type, uint8_t *dst, const uint8_t *src, bool dstPadded);
- void unpackVec3Allocation(const void *data, uint32_t dataSize);
+ void unpackVec3Allocation(const void *data, size_t dataSize);
void packVec3Allocation(OStream *stream) const;
};
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index d2f5f71..27c7e03 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -59,9 +59,10 @@
: BpInterface<IOMX>(impl) {
}
- virtual bool livesLocally(pid_t pid) {
+ virtual bool livesLocally(node_id node, pid_t pid) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
data.writeInt32(pid);
remote()->transact(LIVES_LOCALLY, data, &reply);
@@ -417,7 +418,9 @@
case LIVES_LOCALLY:
{
CHECK_INTERFACE(IOMX, data, reply);
- reply->writeInt32(livesLocally((pid_t)data.readInt32()));
+ node_id node = (void *)data.readIntPtr();
+ pid_t pid = (pid_t)data.readInt32();
+ reply->writeInt32(livesLocally(node, pid));
return OK;
}
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 4d61067..a452ad5 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -74,6 +74,7 @@
libcrypto \
libssl \
libgui \
+ libstagefright_omx \
LOCAL_STATIC_LIBRARIES := \
libstagefright_color_conversion \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 8480b6d..8073af8 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -30,7 +30,7 @@
#include "include/MPEG2TSExtractor.h"
#include "include/WVMExtractor.h"
-#include "timedtext/TimedTextPlayer.h"
+#include "timedtext/TimedTextDriver.h"
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -192,7 +192,7 @@
mVideoBuffer(NULL),
mDecryptHandle(NULL),
mLastVideoTimeUs(-1),
- mTextPlayer(NULL) {
+ mTextDriver(NULL) {
CHECK_EQ(mClient.connect(), (status_t)OK);
DataSource::RegisterDefaultSniffers();
@@ -530,9 +530,9 @@
delete mAudioPlayer;
mAudioPlayer = NULL;
- if (mTextPlayer != NULL) {
- delete mTextPlayer;
- mTextPlayer = NULL;
+ if (mTextDriver != NULL) {
+ delete mTextDriver;
+ mTextDriver = NULL;
}
mVideoRenderer.clear();
@@ -1118,7 +1118,7 @@
}
if (mFlags & TEXTPLAYER_STARTED) {
- mTextPlayer->pause();
+ mTextDriver->pause();
modifyFlags(TEXT_RUNNING, CLEAR);
}
@@ -1272,9 +1272,9 @@
}
status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) {
- if (mTextPlayer != NULL) {
+ if (mTextDriver != NULL) {
if (index >= 0) { // to turn on a text track
- status_t err = mTextPlayer->setTimedTextTrackIndex(index);
+ status_t err = mTextDriver->setTimedTextTrackIndex(index);
if (err != OK) {
return err;
}
@@ -1290,7 +1290,7 @@
modifyFlags(TEXTPLAYER_STARTED, CLEAR);
}
- return mTextPlayer->setTimedTextTrackIndex(index);
+ return mTextDriver->setTimedTextTrackIndex(index);
}
} else {
return INVALID_OPERATION;
@@ -1319,7 +1319,7 @@
seekAudioIfNecessary_l();
if (mFlags & TEXTPLAYER_STARTED) {
- mTextPlayer->seekTo(mSeekTimeUs);
+ mTextDriver->seekToAsync(mSeekTimeUs);
}
if (!(mFlags & PLAYING)) {
@@ -1364,11 +1364,11 @@
Mutex::Autolock autoLock(mTimedTextLock);
CHECK(source != NULL);
- if (mTextPlayer == NULL) {
- mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue);
+ if (mTextDriver == NULL) {
+ mTextDriver = new TimedTextDriver(mListener);
}
- mTextPlayer->addTextSource(source);
+ mTextDriver->addInBandTextSource(source);
}
status_t AwesomePlayer::initAudioDecoder() {
@@ -1695,7 +1695,7 @@
}
if ((mFlags & TEXTPLAYER_STARTED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
- mTextPlayer->resume();
+ mTextDriver->resume();
modifyFlags(TEXT_RUNNING, SET);
}
@@ -2241,11 +2241,11 @@
case KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE:
{
Mutex::Autolock autoLock(mTimedTextLock);
- if (mTextPlayer == NULL) {
- mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue);
+ if (mTextDriver == NULL) {
+ mTextDriver = new TimedTextDriver(mListener);
}
- return mTextPlayer->setParameter(key, request);
+ return mTextDriver->addOutOfBandTextSource(request);
}
case KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS:
{
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index bc88015..6c95d4e5 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -20,7 +20,6 @@
#include "include/MPEG4Extractor.h"
#include "include/SampleTable.h"
#include "include/ESDS.h"
-#include "timedtext/TimedTextPlayer.h"
#include <arpa/inet.h>
@@ -2430,4 +2429,3 @@
}
} // namespace android
-
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 9de873e..7a805aa 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -20,11 +20,299 @@
#include <binder/IServiceManager.h>
#include <media/IMediaPlayerService.h>
-#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/OMXClient.h>
+#include <utils/KeyedVector.h>
+
+#include "include/OMX.h"
namespace android {
+struct MuxOMX : public IOMX {
+ MuxOMX(const sp<IOMX> &remoteOMX);
+ virtual ~MuxOMX();
+
+ virtual IBinder *onAsBinder() { return NULL; }
+
+ virtual bool livesLocally(node_id node, pid_t pid);
+
+ virtual status_t listNodes(List<ComponentInfo> *list);
+
+ virtual status_t allocateNode(
+ const char *name, const sp<IOMXObserver> &observer,
+ node_id *node);
+
+ virtual status_t freeNode(node_id node);
+
+ virtual status_t sendCommand(
+ node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param);
+
+ virtual status_t getParameter(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size);
+
+ virtual status_t setParameter(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size);
+
+ virtual status_t getConfig(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size);
+
+ virtual status_t setConfig(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size);
+
+ virtual status_t getState(
+ node_id node, OMX_STATETYPE* state);
+
+ virtual status_t storeMetaDataInBuffers(
+ node_id node, OMX_U32 port_index, OMX_BOOL enable);
+
+ virtual status_t enableGraphicBuffers(
+ node_id node, OMX_U32 port_index, OMX_BOOL enable);
+
+ virtual status_t getGraphicBufferUsage(
+ node_id node, OMX_U32 port_index, OMX_U32* usage);
+
+ virtual status_t useBuffer(
+ node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
+ buffer_id *buffer);
+
+ virtual status_t useGraphicBuffer(
+ node_id node, OMX_U32 port_index,
+ const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer);
+
+ virtual status_t allocateBuffer(
+ node_id node, OMX_U32 port_index, size_t size,
+ buffer_id *buffer, void **buffer_data);
+
+ virtual status_t allocateBufferWithBackup(
+ node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
+ buffer_id *buffer);
+
+ virtual status_t freeBuffer(
+ node_id node, OMX_U32 port_index, buffer_id buffer);
+
+ virtual status_t fillBuffer(node_id node, buffer_id buffer);
+
+ virtual status_t emptyBuffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp);
+
+ virtual status_t getExtensionIndex(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index);
+
+private:
+ mutable Mutex mLock;
+
+ sp<IOMX> mRemoteOMX;
+ sp<IOMX> mLocalOMX;
+
+ KeyedVector<node_id, bool> mIsLocalNode;
+
+ bool isLocalNode(node_id node) const;
+ bool isLocalNode_l(node_id node) const;
+ const sp<IOMX> &getOMX(node_id node) const;
+ const sp<IOMX> &getOMX_l(node_id node) const;
+
+ static bool IsSoftwareComponent(const char *name);
+
+ DISALLOW_EVIL_CONSTRUCTORS(MuxOMX);
+};
+
+MuxOMX::MuxOMX(const sp<IOMX> &remoteOMX)
+ : mRemoteOMX(remoteOMX) {
+}
+
+MuxOMX::~MuxOMX() {
+}
+
+bool MuxOMX::isLocalNode(node_id node) const {
+ Mutex::Autolock autoLock(mLock);
+
+ return isLocalNode_l(node);
+}
+
+bool MuxOMX::isLocalNode_l(node_id node) const {
+ return mIsLocalNode.indexOfKey(node) >= 0;
+}
+
+// static
+bool MuxOMX::IsSoftwareComponent(const char *name) {
+ return !strncasecmp(name, "OMX.google.", 11);
+}
+
+const sp<IOMX> &MuxOMX::getOMX(node_id node) const {
+ return isLocalNode(node) ? mLocalOMX : mRemoteOMX;
+}
+
+const sp<IOMX> &MuxOMX::getOMX_l(node_id node) const {
+ return isLocalNode_l(node) ? mLocalOMX : mRemoteOMX;
+}
+
+bool MuxOMX::livesLocally(node_id node, pid_t pid) {
+ return getOMX(node)->livesLocally(node, pid);
+}
+
+status_t MuxOMX::listNodes(List<ComponentInfo> *list) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mLocalOMX == NULL) {
+ mLocalOMX = new OMX;
+ }
+
+ return mLocalOMX->listNodes(list);
+}
+
+status_t MuxOMX::allocateNode(
+ const char *name, const sp<IOMXObserver> &observer,
+ node_id *node) {
+ Mutex::Autolock autoLock(mLock);
+
+ sp<IOMX> omx;
+
+ if (IsSoftwareComponent(name)) {
+ if (mLocalOMX == NULL) {
+ mLocalOMX = new OMX;
+ }
+ omx = mLocalOMX;
+ } else {
+ omx = mRemoteOMX;
+ }
+
+ status_t err = omx->allocateNode(name, observer, node);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (omx == mLocalOMX) {
+ mIsLocalNode.add(*node, true);
+ }
+
+ return OK;
+}
+
+status_t MuxOMX::freeNode(node_id node) {
+ Mutex::Autolock autoLock(mLock);
+
+ status_t err = getOMX_l(node)->freeNode(node);
+
+ if (err != OK) {
+ return err;
+ }
+
+ mIsLocalNode.removeItem(node);
+
+ return OK;
+}
+
+status_t MuxOMX::sendCommand(
+ node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
+ return getOMX(node)->sendCommand(node, cmd, param);
+}
+
+status_t MuxOMX::getParameter(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) {
+ return getOMX(node)->getParameter(node, index, params, size);
+}
+
+status_t MuxOMX::setParameter(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) {
+ return getOMX(node)->setParameter(node, index, params, size);
+}
+
+status_t MuxOMX::getConfig(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) {
+ return getOMX(node)->getConfig(node, index, params, size);
+}
+
+status_t MuxOMX::setConfig(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) {
+ return getOMX(node)->setConfig(node, index, params, size);
+}
+
+status_t MuxOMX::getState(
+ node_id node, OMX_STATETYPE* state) {
+ return getOMX(node)->getState(node, state);
+}
+
+status_t MuxOMX::storeMetaDataInBuffers(
+ node_id node, OMX_U32 port_index, OMX_BOOL enable) {
+ return getOMX(node)->storeMetaDataInBuffers(node, port_index, enable);
+}
+
+status_t MuxOMX::enableGraphicBuffers(
+ node_id node, OMX_U32 port_index, OMX_BOOL enable) {
+ return getOMX(node)->enableGraphicBuffers(node, port_index, enable);
+}
+
+status_t MuxOMX::getGraphicBufferUsage(
+ node_id node, OMX_U32 port_index, OMX_U32* usage) {
+ return getOMX(node)->getGraphicBufferUsage(node, port_index, usage);
+}
+
+status_t MuxOMX::useBuffer(
+ node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
+ buffer_id *buffer) {
+ return getOMX(node)->useBuffer(node, port_index, params, buffer);
+}
+
+status_t MuxOMX::useGraphicBuffer(
+ node_id node, OMX_U32 port_index,
+ const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) {
+ return getOMX(node)->useGraphicBuffer(
+ node, port_index, graphicBuffer, buffer);
+}
+
+status_t MuxOMX::allocateBuffer(
+ node_id node, OMX_U32 port_index, size_t size,
+ buffer_id *buffer, void **buffer_data) {
+ return getOMX(node)->allocateBuffer(
+ node, port_index, size, buffer, buffer_data);
+}
+
+status_t MuxOMX::allocateBufferWithBackup(
+ node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms,
+ buffer_id *buffer) {
+ return getOMX(node)->allocateBufferWithBackup(
+ node, port_index, params, buffer);
+}
+
+status_t MuxOMX::freeBuffer(
+ node_id node, OMX_U32 port_index, buffer_id buffer) {
+ return getOMX(node)->freeBuffer(node, port_index, buffer);
+}
+
+status_t MuxOMX::fillBuffer(node_id node, buffer_id buffer) {
+ return getOMX(node)->fillBuffer(node, buffer);
+}
+
+status_t MuxOMX::emptyBuffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp) {
+ return getOMX(node)->emptyBuffer(
+ node, buffer, range_offset, range_length, flags, timestamp);
+}
+
+status_t MuxOMX::getExtensionIndex(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index) {
+ return getOMX(node)->getExtensionIndex(node, parameter_name, index);
+}
+
OMXClient::OMXClient() {
}
@@ -38,6 +326,11 @@
mOMX = service->getOMX();
CHECK(mOMX.get() != NULL);
+ if (!mOMX->livesLocally(NULL /* node */, getpid())) {
+ ALOGI("Using client-side OMX mux.");
+ mOMX = new MuxOMX(mOMX);
+ }
+
return OK;
}
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 7597f64..1f26cbe 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1479,7 +1479,7 @@
const sp<MediaSource> &source,
const sp<ANativeWindow> &nativeWindow)
: mOMX(omx),
- mOMXLivesLocally(omx->livesLocally(getpid())),
+ mOMXLivesLocally(omx->livesLocally(node, getpid())),
mNode(node),
mQuirks(quirks),
mFlags(flags),
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
index da9d280..ea6c360 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
@@ -218,6 +218,18 @@
return OMX_ErrorNone;
}
+ case OMX_IndexParamAudioPcm:
+ {
+ const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
+ (OMX_AUDIO_PARAM_PCMMODETYPE *)params;
+
+ if (pcmParams->nPortIndex != 1) {
+ return OMX_ErrorUndefined;
+ }
+
+ return OMX_ErrorNone;
+ }
+
default:
return SimpleSoftOMXComponent::internalSetParameter(index, params);
}
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 82c6476..a7a3d47 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -41,7 +41,7 @@
class DrmManagerClinet;
class DecryptHandle;
-class TimedTextPlayer;
+class TimedTextDriver;
struct WVMExtractor;
struct AwesomeRenderer : public RefBase {
@@ -232,7 +232,7 @@
sp<DecryptHandle> mDecryptHandle;
int64_t mLastVideoTimeUs;
- TimedTextPlayer *mTextPlayer;
+ TimedTextDriver *mTextDriver;
mutable Mutex mTimedTextLock;
sp<WVMExtractor> mWVMExtractor;
@@ -326,4 +326,3 @@
} // namespace android
#endif // AWESOME_PLAYER_H_
-
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index 53e764f..2c87b34 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -31,7 +31,7 @@
public:
OMX();
- virtual bool livesLocally(pid_t pid);
+ virtual bool livesLocally(node_id node, pid_t pid);
virtual status_t listNodes(List<ComponentInfo> *list);
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 694b12d8..ace883c 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -185,7 +185,7 @@
instance->onObserverDied(mMaster);
}
-bool OMX::livesLocally(pid_t pid) {
+bool OMX::livesLocally(node_id node, pid_t pid) {
return pid == getpid();
}
diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk
index 59d0e15..8b23dee 100644
--- a/media/libstagefright/timedtext/Android.mk
+++ b/media/libstagefright/timedtext/Android.mk
@@ -3,7 +3,10 @@
LOCAL_SRC_FILES:= \
TextDescriptions.cpp \
- TimedTextParser.cpp \
+ TimedTextDriver.cpp \
+ TimedTextInBandSource.cpp \
+ TimedTextSource.cpp \
+ TimedTextSRTSource.cpp \
TimedTextPlayer.cpp
LOCAL_CFLAGS += -Wno-multichar
diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp
new file mode 100644
index 0000000..9ec9415
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextDriver.cpp
@@ -0,0 +1,223 @@
+ /*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TimedTextDriver"
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+
+#include "TimedTextDriver.h"
+
+#include "TextDescriptions.h"
+#include "TimedTextPlayer.h"
+#include "TimedTextSource.h"
+
+namespace android {
+
+TimedTextDriver::TimedTextDriver(
+ const wp<MediaPlayerBase> &listener)
+ : mLooper(new ALooper),
+ mListener(listener),
+ mState(UNINITIALIZED) {
+ mLooper->setName("TimedTextDriver");
+ mLooper->start();
+ mPlayer = new TimedTextPlayer(listener);
+ mLooper->registerHandler(mPlayer);
+}
+
+TimedTextDriver::~TimedTextDriver() {
+ mTextInBandVector.clear();
+ mTextOutOfBandVector.clear();
+ mLooper->stop();
+}
+
+status_t TimedTextDriver::setTimedTextTrackIndex_l(int32_t index) {
+ if (index >=
+ (int)(mTextInBandVector.size() + mTextOutOfBandVector.size())) {
+ return BAD_VALUE;
+ }
+
+ sp<TimedTextSource> source;
+ if (index < mTextInBandVector.size()) {
+ source = mTextInBandVector.itemAt(index);
+ } else {
+ source = mTextOutOfBandVector.itemAt(index - mTextInBandVector.size());
+ }
+ mPlayer->setDataSource(source);
+ return OK;
+}
+
+status_t TimedTextDriver::start() {
+ Mutex::Autolock autoLock(mLock);
+ switch (mState) {
+ case UNINITIALIZED:
+ return INVALID_OPERATION;
+ case STOPPED:
+ mPlayer->start();
+ break;
+ case PLAYING:
+ return OK;
+ case PAUSED:
+ mPlayer->resume();
+ break;
+ default:
+ TRESPASS();
+ }
+ mState = PLAYING;
+ return OK;
+}
+
+status_t TimedTextDriver::stop() {
+ return pause();
+}
+
+// TODO: Test if pause() works properly.
+// Scenario 1: start - pause - resume
+// Scenario 2: start - seek
+// Scenario 3: start - pause - seek - resume
+status_t TimedTextDriver::pause() {
+ Mutex::Autolock autoLock(mLock);
+ switch (mState) {
+ case UNINITIALIZED:
+ return INVALID_OPERATION;
+ case STOPPED:
+ return OK;
+ case PLAYING:
+ mPlayer->pause();
+ break;
+ case PAUSED:
+ return OK;
+ default:
+ TRESPASS();
+ }
+ mState = PAUSED;
+ return OK;
+}
+
+status_t TimedTextDriver::resume() {
+ return start();
+}
+
+status_t TimedTextDriver::seekToAsync(int64_t timeUs) {
+ mPlayer->seekToAsync(timeUs);
+ return OK;
+}
+
+status_t TimedTextDriver::setTimedTextTrackIndex(int32_t index) {
+ // TODO: This is current implementation for MediaPlayer::disableTimedText().
+ // Find better way for readability.
+ if (index < 0) {
+ mPlayer->pause();
+ return OK;
+ }
+
+ status_t ret = OK;
+ Mutex::Autolock autoLock(mLock);
+ switch (mState) {
+ case UNINITIALIZED:
+ ret = INVALID_OPERATION;
+ break;
+ case PAUSED:
+ ret = setTimedTextTrackIndex_l(index);
+ break;
+ case PLAYING:
+ mPlayer->pause();
+ ret = setTimedTextTrackIndex_l(index);
+ if (ret != OK) {
+ break;
+ }
+ mPlayer->start();
+ break;
+ case STOPPED:
+ // TODO: The only difference between STOPPED and PAUSED is this
+ // part. Revise the flow from "MediaPlayer::enableTimedText()" and
+ // remove one of the status, PAUSED and STOPPED, if possible.
+ ret = setTimedTextTrackIndex_l(index);
+ if (ret != OK) {
+ break;
+ }
+ mPlayer->start();
+ break;
+ defaut:
+ TRESPASS();
+ }
+ return ret;
+}
+
+status_t TimedTextDriver::addInBandTextSource(
+ const sp<MediaSource>& mediaSource) {
+ sp<TimedTextSource> source =
+ TimedTextSource::CreateTimedTextSource(mediaSource);
+ if (source == NULL) {
+ return ERROR_UNSUPPORTED;
+ }
+ Mutex::Autolock autoLock(mLock);
+ mTextInBandVector.add(source);
+ if (mState == UNINITIALIZED) {
+ mState = STOPPED;
+ }
+ return OK;
+}
+
+status_t TimedTextDriver::addOutOfBandTextSource(
+ const Parcel &request) {
+ // TODO: Define "TimedTextSource::CreateFromURI(uri)"
+ // and move below lines there..?
+
+ // String values written in Parcel are UTF-16 values.
+ const String16 uri16 = request.readString16();
+ String8 uri = String8(request.readString16());
+
+ uri.toLower();
+ // To support local subtitle file only for now
+ if (strncasecmp("file://", uri.string(), 7)) {
+ return ERROR_UNSUPPORTED;
+ }
+ sp<DataSource> dataSource =
+ DataSource::CreateFromURI(uri);
+ if (dataSource == NULL) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ sp<TimedTextSource> source;
+ if (uri.getPathExtension() == String8(".srt")) {
+ source = TimedTextSource::CreateTimedTextSource(
+ dataSource, TimedTextSource::OUT_OF_BAND_FILE_SRT);
+ }
+
+ if (source == NULL) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+
+ mTextOutOfBandVector.add(source);
+ if (mState == UNINITIALIZED) {
+ mState = STOPPED;
+ }
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextDriver.h b/media/libstagefright/timedtext/TimedTextDriver.h
new file mode 100644
index 0000000..efedb6e
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextDriver.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef TIMED_TEXT_DRIVER_H_
+#define TIMED_TEXT_DRIVER_H_
+
+#include <media/stagefright/foundation/ABase.h> // for DISALLOW_* macro
+#include <utils/Errors.h> // for status_t
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class ALooper;
+class MediaPlayerBase;
+class MediaSource;
+class Parcel;
+class TimedTextPlayer;
+class TimedTextSource;
+
+class TimedTextDriver {
+public:
+ TimedTextDriver(const wp<MediaPlayerBase> &listener);
+
+ ~TimedTextDriver();
+
+ // TODO: pause-resume pair seems equivalent to stop-start pair.
+ // Check if it is replaceable with stop-start.
+ status_t start();
+ status_t stop();
+ status_t pause();
+ status_t resume();
+
+ status_t seekToAsync(int64_t timeUs);
+
+ status_t addInBandTextSource(const sp<MediaSource>& source);
+ status_t addOutOfBandTextSource(const Parcel &request);
+
+ status_t setTimedTextTrackIndex(int32_t index);
+
+private:
+ Mutex mLock;
+
+ enum State {
+ UNINITIALIZED,
+ STOPPED,
+ PLAYING,
+ PAUSED,
+ };
+
+ sp<ALooper> mLooper;
+ sp<TimedTextPlayer> mPlayer;
+ wp<MediaPlayerBase> mListener;
+
+ // Variables to be guarded by mLock.
+ State mState;
+ Vector<sp<TimedTextSource> > mTextInBandVector;
+ Vector<sp<TimedTextSource> > mTextOutOfBandVector;
+ // -- End of variables to be guarded by mLock
+
+ status_t setTimedTextTrackIndex_l(int32_t index);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TimedTextDriver);
+};
+
+} // namespace android
+
+#endif // TIMED_TEXT_DRIVER_H_
diff --git a/media/libstagefright/timedtext/TimedTextInBandSource.cpp b/media/libstagefright/timedtext/TimedTextInBandSource.cpp
new file mode 100644
index 0000000..f2c4d54
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextInBandSource.cpp
@@ -0,0 +1,118 @@
+ /*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TimedTextInBandSource"
+#include <utils/Log.h>
+
+#include <binder/Parcel.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDebug.h> // CHECK_XX macro
+#include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+
+#include "TimedTextInBandSource.h"
+#include "TextDescriptions.h"
+
+namespace android {
+
+TimedTextInBandSource::TimedTextInBandSource(const sp<MediaSource>& mediaSource)
+ : mSource(mediaSource) {
+}
+
+TimedTextInBandSource::~TimedTextInBandSource() {
+}
+
+status_t TimedTextInBandSource::read(
+ int64_t *timeUs, Parcel *parcel, const MediaSource::ReadOptions *options) {
+ MediaBuffer *textBuffer = NULL;
+ status_t err = mSource->read(&textBuffer, options);
+ if (err != OK) {
+ return err;
+ }
+ CHECK(textBuffer != NULL);
+ textBuffer->meta_data()->findInt64(kKeyTime, timeUs);
+ // TODO: this is legacy code. when 'timeUs' can be <= 0?
+ if (*timeUs > 0) {
+ extractAndAppendLocalDescriptions(*timeUs, textBuffer, parcel);
+ }
+ textBuffer->release();
+ return OK;
+}
+
+// Each text sample consists of a string of text, optionally with sample
+// modifier description. The modifier description could specify a new
+// text style for the string of text. These descriptions are present only
+// if they are needed. This method is used to extract the modifier
+// description and append it at the end of the text.
+status_t TimedTextInBandSource::extractAndAppendLocalDescriptions(
+ int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel) {
+ const void *data;
+ size_t size = 0;
+ int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS;
+
+ const char *mime;
+ CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+
+ if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) {
+ data = textBuffer->data();
+ size = textBuffer->size();
+
+ if (size > 0) {
+ parcel->freeData();
+ flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
+ return TextDescriptions::getParcelOfDescriptions(
+ (const uint8_t *)data, size, flag, timeUs / 1000, parcel);
+ }
+ return OK;
+ }
+ return ERROR_UNSUPPORTED;
+}
+
+// To extract and send the global text descriptions for all the text samples
+// in the text track or text file.
+// TODO: send error message to application via notifyListener()...?
+status_t TimedTextInBandSource::extractGlobalDescriptions(Parcel *parcel) {
+ const void *data;
+ size_t size = 0;
+ int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS;
+
+ const char *mime;
+ CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+
+ // support 3GPP only for now
+ if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) {
+ uint32_t type;
+ // get the 'tx3g' box content. This box contains the text descriptions
+ // used to render the text track
+ if (!mSource->getFormat()->findData(
+ kKeyTextFormatData, &type, &data, &size)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (size > 0) {
+ flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
+ return TextDescriptions::getParcelOfDescriptions(
+ (const uint8_t *)data, size, flag, 0, parcel);
+ }
+ return OK;
+ }
+ return ERROR_UNSUPPORTED;
+}
+
+} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextInBandSource.h b/media/libstagefright/timedtext/TimedTextInBandSource.h
new file mode 100644
index 0000000..26e5737
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextInBandSource.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef TIMED_TEXT_IN_BAND_SOURCE_H_
+#define TIMED_TEXT_IN_BAND_SOURCE_H_
+
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+
+#include "TimedTextSource.h"
+
+namespace android {
+
+class MediaBuffer;
+class Parcel;
+
+class TimedTextInBandSource : public TimedTextSource {
+ public:
+ TimedTextInBandSource(const sp<MediaSource>& mediaSource);
+ virtual status_t start() { return mSource->start(); }
+ virtual status_t stop() { return mSource->stop(); }
+ virtual status_t read(
+ int64_t *timeUs,
+ Parcel *parcel,
+ const MediaSource::ReadOptions *options = NULL);
+ virtual status_t extractGlobalDescriptions(Parcel *parcel);
+
+ protected:
+ virtual ~TimedTextInBandSource();
+
+ private:
+ sp<MediaSource> mSource;
+
+ status_t extractAndAppendLocalDescriptions(
+ int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TimedTextInBandSource);
+};
+
+} // namespace android
+
+#endif // TIMED_TEXT_IN_BAND_SOURCE_H_
diff --git a/media/libstagefright/timedtext/TimedTextParser.cpp b/media/libstagefright/timedtext/TimedTextParser.cpp
deleted file mode 100644
index caea0a4..0000000
--- a/media/libstagefright/timedtext/TimedTextParser.cpp
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "TimedTextParser.h"
-#include <media/stagefright/DataSource.h>
-
-namespace android {
-
-TimedTextParser::TimedTextParser()
- : mDataSource(NULL),
- mOffset(0),
- mIndex(0) {
-}
-
-TimedTextParser::~TimedTextParser() {
- reset();
-}
-
-status_t TimedTextParser::init(
- const sp<DataSource> &dataSource, FileType fileType) {
- mDataSource = dataSource;
- mFileType = fileType;
-
- status_t err;
- if ((err = scanFile()) != OK) {
- reset();
- return err;
- }
-
- return OK;
-}
-
-void TimedTextParser::reset() {
- mDataSource.clear();
- mTextVector.clear();
- mOffset = 0;
- mIndex = 0;
-}
-
-// scan the text file to get start/stop time and the
-// offset of each piece of text content
-status_t TimedTextParser::scanFile() {
- if (mFileType != OUT_OF_BAND_FILE_SRT) {
- return ERROR_UNSUPPORTED;
- }
-
- off64_t offset = 0;
- int64_t startTimeUs;
- bool endOfFile = false;
-
- while (!endOfFile) {
- TextInfo info;
- status_t err = getNextInSrtFileFormat(&offset, &startTimeUs, &info);
-
- if (err != OK) {
- if (err == ERROR_END_OF_STREAM) {
- endOfFile = true;
- } else {
- return err;
- }
- } else {
- mTextVector.add(startTimeUs, info);
- }
- }
-
- if (mTextVector.isEmpty()) {
- return ERROR_MALFORMED;
- }
- return OK;
-}
-
-// read one line started from *offset and store it into data.
-status_t TimedTextParser::readNextLine(off64_t *offset, AString *data) {
- char character;
-
- data->clear();
-
- while (true) {
- ssize_t err;
- if ((err = mDataSource->readAt(*offset, &character, 1)) < 1) {
- if (err == 0) {
- return ERROR_END_OF_STREAM;
- }
- return ERROR_IO;
- }
-
- (*offset) ++;
-
- // a line could end with CR, LF or CR + LF
- if (character == 10) {
- break;
- } else if (character == 13) {
- if ((err = mDataSource->readAt(*offset, &character, 1)) < 1) {
- if (err == 0) { // end of the stream
- return OK;
- }
- return ERROR_IO;
- }
-
- (*offset) ++;
-
- if (character != 10) {
- (*offset) --;
- }
- break;
- }
-
- data->append(character);
- }
-
- return OK;
-}
-
-/* SRT format:
- * Subtitle number
- * Start time --> End time
- * Text of subtitle (one or more lines)
- * Blank lines
- *
- * .srt file example:
- * 1
- * 00:00:20,000 --> 00:00:24,400
- * Altocumulus clouds occur between six thousand
- *
- * 2
- * 00:00:24,600 --> 00:00:27,800
- * and twenty thousand feet above ground level.
- */
-status_t TimedTextParser::getNextInSrtFileFormat(
- off64_t *offset, int64_t *startTimeUs, TextInfo *info) {
- AString data;
- status_t err;
-
- // To skip blank lines.
- do {
- if ((err = readNextLine(offset, &data)) != OK) {
- return err;
- }
- data.trim();
- } while(data.empty());
-
- // Just ignore the first non-blank line which is subtitle sequence number.
-
- if ((err = readNextLine(offset, &data)) != OK) {
- return err;
- }
- int hour1, hour2, min1, min2, sec1, sec2, msec1, msec2;
- // the start time format is: hours:minutes:seconds,milliseconds
- // 00:00:24,600 --> 00:00:27,800
- if (sscanf(data.c_str(), "%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d",
- &hour1, &min1, &sec1, &msec1, &hour2, &min2, &sec2, &msec2) != 8) {
- return ERROR_MALFORMED;
- }
-
- *startTimeUs = ((hour1 * 3600 + min1 * 60 + sec1) * 1000 + msec1) * 1000ll;
- info->endTimeUs = ((hour2 * 3600 + min2 * 60 + sec2) * 1000 + msec2) * 1000ll;
- if (info->endTimeUs <= *startTimeUs) {
- return ERROR_MALFORMED;
- }
-
- info->offset = *offset;
-
- bool needMoreData = true;
- while (needMoreData) {
- if ((err = readNextLine(offset, &data)) != OK) {
- if (err == ERROR_END_OF_STREAM) {
- needMoreData = false;
- } else {
- return err;
- }
- }
-
- if (needMoreData) {
- data.trim();
- if (data.empty()) {
- // it's an empty line used to separate two subtitles
- needMoreData = false;
- }
- }
- }
-
- info->textLen = *offset - info->offset;
-
- return OK;
-}
-
-status_t TimedTextParser::getText(
- AString *text, int64_t *startTimeUs, int64_t *endTimeUs,
- const MediaSource::ReadOptions *options) {
- Mutex::Autolock autoLock(mLock);
-
- text->clear();
-
- int64_t seekTimeUs;
- MediaSource::ReadOptions::SeekMode mode;
- if (options && options->getSeekTo(&seekTimeUs, &mode)) {
- int64_t lastEndTimeUs = mTextVector.valueAt(mTextVector.size() - 1).endTimeUs;
- int64_t firstStartTimeUs = mTextVector.keyAt(0);
-
- if (seekTimeUs < 0 || seekTimeUs > lastEndTimeUs) {
- return ERROR_OUT_OF_RANGE;
- } else if (seekTimeUs < firstStartTimeUs) {
- mIndex = 0;
- } else {
- // binary search
- ssize_t low = 0;
- ssize_t high = mTextVector.size() - 1;
- ssize_t mid = 0;
- int64_t currTimeUs;
-
- while (low <= high) {
- mid = low + (high - low)/2;
- currTimeUs = mTextVector.keyAt(mid);
- const int diff = currTimeUs - seekTimeUs;
-
- if (diff == 0) {
- break;
- } else if (diff < 0) {
- low = mid + 1;
- } else {
- if ((high == mid + 1)
- && (seekTimeUs < mTextVector.keyAt(high))) {
- break;
- }
- high = mid - 1;
- }
- }
-
- mIndex = mid;
- }
- }
-
- TextInfo info = mTextVector.valueAt(mIndex);
- *startTimeUs = mTextVector.keyAt(mIndex);
- *endTimeUs = info.endTimeUs;
- mIndex ++;
-
- char *str = new char[info.textLen];
- if (mDataSource->readAt(info.offset, str, info.textLen) < info.textLen) {
- delete[] str;
- return ERROR_IO;
- }
-
- text->append(str, info.textLen);
- delete[] str;
- return OK;
-}
-
-} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextParser.h b/media/libstagefright/timedtext/TimedTextParser.h
deleted file mode 100644
index 44774c2..0000000
--- a/media/libstagefright/timedtext/TimedTextParser.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TIMED_TEXT_PARSER_H_
-
-#define TIMED_TEXT_PARSER_H_
-
-#include <media/MediaPlayerInterface.h>
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AString.h>
-#include <media/stagefright/MediaSource.h>
-
-namespace android {
-
-class DataSource;
-
-class TimedTextParser : public RefBase {
-public:
- TimedTextParser();
- virtual ~TimedTextParser();
-
- enum FileType {
- OUT_OF_BAND_FILE_SRT = 1,
- };
-
- status_t getText(AString *text, int64_t *startTimeUs, int64_t *endTimeUs,
- const MediaSource::ReadOptions *options = NULL);
- status_t init(const sp<DataSource> &dataSource, FileType fileType);
- void reset();
-
-private:
- Mutex mLock;
-
- sp<DataSource> mDataSource;
- off64_t mOffset;
-
- struct TextInfo {
- int64_t endTimeUs;
- // the offset of the text in the original file
- off64_t offset;
- int textLen;
- };
-
- int mIndex;
- FileType mFileType;
-
- // the key indicated the start time of the text
- KeyedVector<int64_t, TextInfo> mTextVector;
-
- status_t getNextInSrtFileFormat(
- off64_t *offset, int64_t *startTimeUs, TextInfo *info);
- status_t readNextLine(off64_t *offset, AString *data);
-
- status_t scanFile();
-
- DISALLOW_EVIL_CONSTRUCTORS(TimedTextParser);
-};
-
-} // namespace android
-
-#endif // TIMED_TEXT_PARSER_H_
-
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
index 3014b0b..8c2df88 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.cpp
+++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -18,399 +18,164 @@
#define LOG_TAG "TimedTextPlayer"
#include <utils/Log.h>
-#include <binder/IPCThreadState.h>
-
+#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaDebug.h>
-#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/MetaData.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/FileSource.h>
-#include <media/stagefright/Utils.h>
+#include <media/MediaPlayerInterface.h>
-#include "include/AwesomePlayer.h"
#include "TimedTextPlayer.h"
-#include "TimedTextParser.h"
-#include "TextDescriptions.h"
+
+#include "TimedTextDriver.h"
+#include "TimedTextSource.h"
namespace android {
-struct TimedTextEvent : public TimedEventQueue::Event {
- TimedTextEvent(
- TimedTextPlayer *player,
- void (TimedTextPlayer::*method)())
- : mPlayer(player),
- mMethod(method) {
- }
+static const int64_t kAdjustmentProcessingTimeUs = 100000ll;
-protected:
- virtual ~TimedTextEvent() {}
-
- virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
- (mPlayer->*mMethod)();
- }
-
-private:
- TimedTextPlayer *mPlayer;
- void (TimedTextPlayer::*mMethod)();
-
- TimedTextEvent(const TimedTextEvent &);
- TimedTextEvent &operator=(const TimedTextEvent &);
-};
-
-TimedTextPlayer::TimedTextPlayer(
- AwesomePlayer *observer,
- const wp<MediaPlayerBase> &listener,
- TimedEventQueue *queue)
- : mSource(NULL),
- mOutOfBandSource(NULL),
- mSeekTimeUs(0),
- mStarted(false),
- mTextEventPending(false),
- mQueue(queue),
- mListener(listener),
- mObserver(observer),
- mTextBuffer(NULL),
- mTextParser(NULL),
- mTextType(kNoText) {
- mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent);
+TimedTextPlayer::TimedTextPlayer(const wp<MediaPlayerBase> &listener)
+ : mListener(listener),
+ mSource(NULL),
+ mSendSubtitleGeneration(0) {
}
TimedTextPlayer::~TimedTextPlayer() {
- if (mStarted) {
- reset();
+ if (mSource != NULL) {
+ mSource->stop();
+ mSource.clear();
+ mSource = NULL;
}
-
- mTextTrackVector.clear();
- mTextOutOfBandVector.clear();
}
-status_t TimedTextPlayer::start(uint8_t index) {
- CHECK(!mStarted);
-
- if (index >=
- mTextTrackVector.size() + mTextOutOfBandVector.size()) {
- ALOGE("Incorrect text track index: %d", index);
- return BAD_VALUE;
- }
-
- status_t err;
- if (index < mTextTrackVector.size()) { // start an in-band text
- mSource = mTextTrackVector.itemAt(index);
-
- err = mSource->start();
-
- if (err != OK) {
- return err;
- }
- mTextType = kInBandText;
- } else { // start an out-of-band text
- OutOfBandText text =
- mTextOutOfBandVector.itemAt(index - mTextTrackVector.size());
-
- mOutOfBandSource = text.source;
- TimedTextParser::FileType fileType = text.type;
-
- if (mTextParser == NULL) {
- mTextParser = new TimedTextParser();
- }
-
- if ((err = mTextParser->init(mOutOfBandSource, fileType)) != OK) {
- return err;
- }
- mTextType = kOutOfBandText;
- }
-
- // send sample description format
- if ((err = extractAndSendGlobalDescriptions()) != OK) {
- return err;
- }
-
- int64_t positionUs;
- mObserver->getPosition(&positionUs);
- seekTo(positionUs);
-
- postTextEvent();
-
- mStarted = true;
-
- return OK;
+void TimedTextPlayer::start() {
+ sp<AMessage> msg = new AMessage(kWhatSeek, id());
+ msg->setInt64("seekTimeUs", -1);
+ msg->post();
}
void TimedTextPlayer::pause() {
- CHECK(mStarted);
-
- cancelTextEvent();
+ (new AMessage(kWhatPause, id()))->post();
}
void TimedTextPlayer::resume() {
- CHECK(mStarted);
-
- postTextEvent();
+ start();
}
-void TimedTextPlayer::reset() {
- CHECK(mStarted);
+void TimedTextPlayer::seekToAsync(int64_t timeUs) {
+ sp<AMessage> msg = new AMessage(kWhatSeek, id());
+ msg->setInt64("seekTimeUs", timeUs);
+ msg->post();
+}
- // send an empty text to clear the screen
- notifyListener(MEDIA_TIMED_TEXT);
+void TimedTextPlayer::setDataSource(sp<TimedTextSource> source) {
+ sp<AMessage> msg = new AMessage(kWhatSetSource, id());
+ msg->setObject("source", source);
+ msg->post();
+}
- cancelTextEvent();
-
- mSeeking = false;
- mStarted = false;
-
- if (mTextType == kInBandText) {
- if (mTextBuffer != NULL) {
- mTextBuffer->release();
- mTextBuffer = NULL;
+void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatPause: {
+ mSendSubtitleGeneration++;
+ break;
}
-
- if (mSource != NULL) {
- mSource->stop();
- mSource.clear();
- mSource = NULL;
+ case kWhatSeek: {
+ int64_t seekTimeUs = 0;
+ msg->findInt64("seekTimeUs", &seekTimeUs);
+ if (seekTimeUs < 0) {
+ sp<MediaPlayerBase> listener = mListener.promote();
+ if (listener != NULL) {
+ int32_t positionMs = 0;
+ listener->getCurrentPosition(&positionMs);
+ seekTimeUs = positionMs * 1000ll;
+ }
+ }
+ doSeekAndRead(seekTimeUs);
+ break;
}
- } else {
- if (mTextParser != NULL) {
- mTextParser.clear();
- mTextParser = NULL;
+ case kWhatSendSubtitle: {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation != mSendSubtitleGeneration) {
+ // Drop obsolete msg.
+ break;
+ }
+ sp<RefBase> obj;
+ msg->findObject("subtitle", &obj);
+ if (obj != NULL) {
+ sp<ParcelEvent> parcelEvent;
+ parcelEvent = static_cast<ParcelEvent*>(obj.get());
+ notifyListener(MEDIA_TIMED_TEXT, &(parcelEvent->parcel));
+ } else {
+ notifyListener(MEDIA_TIMED_TEXT);
+ }
+ doRead();
+ break;
}
- if (mOutOfBandSource != NULL) {
- mOutOfBandSource.clear();
- mOutOfBandSource = NULL;
+ case kWhatSetSource: {
+ sp<RefBase> obj;
+ msg->findObject("source", &obj);
+ if (obj == NULL) break;
+ if (mSource != NULL) {
+ mSource->stop();
+ }
+ mSource = static_cast<TimedTextSource*>(obj.get());
+ mSource->start();
+ Parcel parcel;
+ if (mSource->extractGlobalDescriptions(&parcel) == OK &&
+ parcel.dataSize() > 0) {
+ notifyListener(MEDIA_TIMED_TEXT, &parcel);
+ } else {
+ notifyListener(MEDIA_TIMED_TEXT);
+ }
+ break;
}
}
}
-status_t TimedTextPlayer::seekTo(int64_t time_us) {
- Mutex::Autolock autoLock(mLock);
-
- mSeeking = true;
- mSeekTimeUs = time_us;
-
- postTextEvent();
-
- return OK;
-}
-
-status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) {
- if (index >=
- (int)(mTextTrackVector.size() + mTextOutOfBandVector.size())) {
- return BAD_VALUE;
- }
-
- if (mStarted) {
- reset();
- }
-
- if (index >= 0) {
- return start(index);
- }
- return OK;
-}
-
-void TimedTextPlayer::onTextEvent() {
- Mutex::Autolock autoLock(mLock);
-
- if (!mTextEventPending) {
- return;
- }
- mTextEventPending = false;
-
- if (mData.dataSize() > 0) {
- notifyListener(MEDIA_TIMED_TEXT, &mData);
- mData.freeData();
- }
-
+void TimedTextPlayer::doSeekAndRead(int64_t seekTimeUs) {
MediaSource::ReadOptions options;
- if (mSeeking) {
- options.setSeekTo(mSeekTimeUs,
- MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
- mSeeking = false;
-
- notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen
- }
-
- int64_t positionUs, timeUs;
- mObserver->getPosition(&positionUs);
-
- if (mTextType == kInBandText) {
- if (mSource->read(&mTextBuffer, &options) != OK) {
- return;
- }
-
- mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs);
- } else {
- int64_t endTimeUs;
- if (mTextParser->getText(
- &mText, &timeUs, &endTimeUs, &options) != OK) {
- return;
- }
- }
-
- if (timeUs > 0) {
- extractAndAppendLocalDescriptions(timeUs);
- }
-
- if (mTextType == kInBandText) {
- if (mTextBuffer != NULL) {
- mTextBuffer->release();
- mTextBuffer = NULL;
- }
- } else {
- mText.clear();
- }
-
- //send the text now
- if (timeUs <= positionUs + 100000ll) {
- postTextEvent();
- } else {
- postTextEvent(timeUs - positionUs - 100000ll);
- }
+ options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
+ doRead(&options);
}
-void TimedTextPlayer::postTextEvent(int64_t delayUs) {
- if (mTextEventPending) {
- return;
- }
-
- mTextEventPending = true;
- mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs);
+void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) {
+ int64_t timeUs = 0;
+ sp<ParcelEvent> parcelEvent = new ParcelEvent();
+ mSource->read(&timeUs, &(parcelEvent->parcel), options);
+ postTextEvent(parcelEvent, timeUs);
}
-void TimedTextPlayer::cancelTextEvent() {
- mQueue->cancelEvent(mTextEvent->eventID());
- mTextEventPending = false;
-}
+void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) {
+ sp<MediaPlayerBase> listener = mListener.promote();
+ if (listener != NULL) {
+ int64_t positionUs, delayUs;
+ int32_t positionMs = 0;
+ listener->getCurrentPosition(&positionMs);
+ positionUs = positionMs * 1000;
-void TimedTextPlayer::addTextSource(sp<MediaSource> source) {
- Mutex::Autolock autoLock(mLock);
- mTextTrackVector.add(source);
-}
-
-status_t TimedTextPlayer::setParameter(int key, const Parcel &request) {
- Mutex::Autolock autoLock(mLock);
-
- if (key == KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE) {
- const String16 uri16 = request.readString16();
- String8 uri = String8(uri16);
- KeyedVector<String8, String8> headers;
-
- // To support local subtitle file only for now
- if (strncasecmp("file://", uri.string(), 7)) {
- return INVALID_OPERATION;
- }
- sp<DataSource> dataSource =
- DataSource::CreateFromURI(uri, &headers);
- status_t err = dataSource->initCheck();
-
- if (err != OK) {
- return err;
- }
-
- OutOfBandText text;
- text.source = dataSource;
- if (uri.getPathExtension() == String8(".srt")) {
- text.type = TimedTextParser::OUT_OF_BAND_FILE_SRT;
+ if (timeUs <= positionUs + kAdjustmentProcessingTimeUs) {
+ delayUs = 0;
} else {
- return ERROR_UNSUPPORTED;
+ delayUs = timeUs - positionUs - kAdjustmentProcessingTimeUs;
}
-
- mTextOutOfBandVector.add(text);
-
- return OK;
+ sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id());
+ msg->setInt32("generation", mSendSubtitleGeneration);
+ if (parcel != NULL) {
+ msg->setObject("subtitle", parcel);
+ }
+ msg->post(delayUs);
}
- return INVALID_OPERATION;
}
void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) {
- if (mListener != NULL) {
- sp<MediaPlayerBase> listener = mListener.promote();
-
- if (listener != NULL) {
- if (parcel && (parcel->dataSize() > 0)) {
- listener->sendEvent(msg, 0, 0, parcel);
- } else { // send an empty timed text to clear the screen
- listener->sendEvent(msg);
- }
+ sp<MediaPlayerBase> listener = mListener.promote();
+ if (listener != NULL) {
+ if (parcel != NULL && (parcel->dataSize() > 0)) {
+ listener->sendEvent(msg, 0, 0, parcel);
+ } else { // send an empty timed text to clear the screen
+ listener->sendEvent(msg);
}
}
}
-// Each text sample consists of a string of text, optionally with sample
-// modifier description. The modifier description could specify a new
-// text style for the string of text. These descriptions are present only
-// if they are needed. This method is used to extract the modifier
-// description and append it at the end of the text.
-status_t TimedTextPlayer::extractAndAppendLocalDescriptions(int64_t timeUs) {
- const void *data;
- size_t size = 0;
- int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS;
-
- if (mTextType == kInBandText) {
- const char *mime;
- CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
-
- if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
- flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
- data = mTextBuffer->data();
- size = mTextBuffer->size();
- } else {
- // support 3GPP only for now
- return ERROR_UNSUPPORTED;
- }
- } else {
- data = mText.c_str();
- size = mText.size();
- flag |= TextDescriptions::OUT_OF_BAND_TEXT_SRT;
- }
-
- if ((size > 0) && (flag != TextDescriptions::LOCAL_DESCRIPTIONS)) {
- mData.freeData();
- return TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, timeUs / 1000, &mData);
- }
-
- return OK;
-}
-
-// To extract and send the global text descriptions for all the text samples
-// in the text track or text file.
-status_t TimedTextPlayer::extractAndSendGlobalDescriptions() {
- const void *data;
- size_t size = 0;
- int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS;
-
- if (mTextType == kInBandText) {
- const char *mime;
- CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
-
- // support 3GPP only for now
- if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
- uint32_t type;
- // get the 'tx3g' box content. This box contains the text descriptions
- // used to render the text track
- if (!mSource->getFormat()->findData(
- kKeyTextFormatData, &type, &data, &size)) {
- return ERROR_MALFORMED;
- }
-
- flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
- }
- }
-
- if ((size > 0) && (flag != TextDescriptions::GLOBAL_DESCRIPTIONS)) {
- Parcel parcel;
- if (TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, 0, &parcel) == OK) {
- if (parcel.dataSize() > 0) {
- notifyListener(MEDIA_TIMED_TEXT, &parcel);
- }
- }
- }
-
- return OK;
-}
-}
+} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.h b/media/libstagefright/timedtext/TimedTextPlayer.h
index a744db5..837beeb 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.h
+++ b/media/libstagefright/timedtext/TimedTextPlayer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -15,99 +15,61 @@
*/
#ifndef TIMEDTEXT_PLAYER_H_
-
#define TIMEDTEXT_PLAYER_H_
-#include <media/MediaPlayerInterface.h>
+#include <binder/Parcel.h>
#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/RefBase.h>
-#include "include/TimedEventQueue.h"
-#include "TimedTextParser.h"
+#include "TimedTextSource.h"
namespace android {
-class MediaSource;
-class AwesomePlayer;
-class MediaBuffer;
+class AMessage;
+class MediaPlayerBase;
+class TimedTextDriver;
+class TimedTextSource;
-class TimedTextPlayer {
+class TimedTextPlayer : public AHandler {
public:
- TimedTextPlayer(AwesomePlayer *observer,
- const wp<MediaPlayerBase> &listener,
- TimedEventQueue *queue);
+ TimedTextPlayer(const wp<MediaPlayerBase> &listener);
virtual ~TimedTextPlayer();
- // index: the index of the text track which will
- // be turned on
- status_t start(uint8_t index);
-
+ void start();
void pause();
-
void resume();
+ void seekToAsync(int64_t timeUs);
+ void setDataSource(sp<TimedTextSource> source);
- status_t seekTo(int64_t time_us);
-
- void addTextSource(sp<MediaSource> source);
-
- status_t setTimedTextTrackIndex(int32_t index);
- status_t setParameter(int key, const Parcel &request);
+protected:
+ virtual void onMessageReceived(const sp<AMessage> &msg);
private:
- enum TextType {
- kNoText = 0,
- kInBandText = 1,
- kOutOfBandText = 2,
+ enum {
+ kWhatPause = 'paus',
+ kWhatSeek = 'seek',
+ kWhatSendSubtitle = 'send',
+ kWhatSetSource = 'ssrc',
};
- Mutex mLock;
-
- sp<MediaSource> mSource;
- sp<DataSource> mOutOfBandSource;
-
- bool mSeeking;
- int64_t mSeekTimeUs;
-
- bool mStarted;
-
- sp<TimedEventQueue::Event> mTextEvent;
- bool mTextEventPending;
-
- TimedEventQueue *mQueue;
+ // To add Parcel into an AMessage as an object, it should be 'RefBase'.
+ struct ParcelEvent : public RefBase {
+ Parcel parcel;
+ };
wp<MediaPlayerBase> mListener;
- AwesomePlayer *mObserver;
+ sp<TimedTextSource> mSource;
+ int32_t mSendSubtitleGeneration;
- MediaBuffer *mTextBuffer;
- Parcel mData;
-
- // for in-band timed text
- Vector<sp<MediaSource> > mTextTrackVector;
-
- // for out-of-band timed text
- struct OutOfBandText {
- TimedTextParser::FileType type;
- sp<DataSource> source;
- };
- Vector<OutOfBandText > mTextOutOfBandVector;
-
- sp<TimedTextParser> mTextParser;
- AString mText;
-
- TextType mTextType;
-
- void reset();
-
+ void doSeekAndRead(int64_t seekTimeUs);
+ void doRead(MediaSource::ReadOptions* options = NULL);
void onTextEvent();
- void postTextEvent(int64_t delayUs = -1);
- void cancelTextEvent();
-
+ void postTextEvent(const sp<ParcelEvent>& parcel = NULL, int64_t timeUs = -1);
void notifyListener(int msg, const Parcel *parcel = NULL);
- status_t extractAndAppendLocalDescriptions(int64_t timeUs);
- status_t extractAndSendGlobalDescriptions();
-
DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer);
};
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.cpp b/media/libstagefright/timedtext/TimedTextSRTSource.cpp
new file mode 100644
index 0000000..3752d34
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextSRTSource.cpp
@@ -0,0 +1,275 @@
+ /*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TimedTextSRTSource"
+#include <utils/Log.h>
+
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+
+#include "TimedTextSRTSource.h"
+#include "TextDescriptions.h"
+
+namespace android {
+
+TimedTextSRTSource::TimedTextSRTSource(const sp<DataSource>& dataSource)
+ : mSource(dataSource),
+ mIndex(0) {
+}
+
+TimedTextSRTSource::~TimedTextSRTSource() {
+}
+
+status_t TimedTextSRTSource::start() {
+ status_t err = scanFile();
+ if (err != OK) {
+ reset();
+ }
+ return err;
+}
+
+void TimedTextSRTSource::reset() {
+ mTextVector.clear();
+ mIndex = 0;
+}
+
+status_t TimedTextSRTSource::stop() {
+ reset();
+ return OK;
+}
+
+status_t TimedTextSRTSource::read(
+ int64_t *timeUs,
+ Parcel *parcel,
+ const MediaSource::ReadOptions *options) {
+ int64_t endTimeUs;
+ AString text;
+ status_t err = getText(options, &text, timeUs, &endTimeUs);
+ if (err != OK) {
+ return err;
+ }
+
+ if (*timeUs > 0) {
+ extractAndAppendLocalDescriptions(*timeUs, text, parcel);
+ }
+ return OK;
+}
+
+status_t TimedTextSRTSource::scanFile() {
+ off64_t offset = 0;
+ int64_t startTimeUs;
+ bool endOfFile = false;
+
+ while (!endOfFile) {
+ TextInfo info;
+ status_t err = getNextSubtitleInfo(&offset, &startTimeUs, &info);
+ switch (err) {
+ case OK:
+ mTextVector.add(startTimeUs, info);
+ break;
+ case ERROR_END_OF_STREAM:
+ endOfFile = true;
+ break;
+ default:
+ return err;
+ }
+ }
+ if (mTextVector.isEmpty()) {
+ return ERROR_MALFORMED;
+ }
+ return OK;
+}
+
+/* SRT format:
+ * Subtitle number
+ * Start time --> End time
+ * Text of subtitle (one or more lines)
+ * Blank lines
+ *
+ * .srt file example:
+ * 1
+ * 00:00:20,000 --> 00:00:24,400
+ * Altocumulus clouds occr between six thousand
+ *
+ * 2
+ * 00:00:24,600 --> 00:00:27,800
+ * and twenty thousand feet above ground level.
+ */
+status_t TimedTextSRTSource::getNextSubtitleInfo(
+ off64_t *offset, int64_t *startTimeUs, TextInfo *info) {
+ AString data;
+ status_t err;
+
+ // To skip blank lines.
+ do {
+ if ((err = readNextLine(offset, &data)) != OK) {
+ return err;
+ }
+ data.trim();
+ } while (data.empty());
+
+ // Just ignore the first non-blank line which is subtitle sequence number.
+ if ((err = readNextLine(offset, &data)) != OK) {
+ return err;
+ }
+ int hour1, hour2, min1, min2, sec1, sec2, msec1, msec2;
+ // the start time format is: hours:minutes:seconds,milliseconds
+ // 00:00:24,600 --> 00:00:27,800
+ if (sscanf(data.c_str(), "%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d",
+ &hour1, &min1, &sec1, &msec1, &hour2, &min2, &sec2, &msec2) != 8) {
+ return ERROR_MALFORMED;
+ }
+
+ *startTimeUs = ((hour1 * 3600 + min1 * 60 + sec1) * 1000 + msec1) * 1000ll;
+ info->endTimeUs = ((hour2 * 3600 + min2 * 60 + sec2) * 1000 + msec2) * 1000ll;
+ if (info->endTimeUs <= *startTimeUs) {
+ return ERROR_MALFORMED;
+ }
+
+ info->offset = *offset;
+ bool needMoreData = true;
+ while (needMoreData) {
+ if ((err = readNextLine(offset, &data)) != OK) {
+ if (err == ERROR_END_OF_STREAM) {
+ needMoreData = false;
+ } else {
+ return err;
+ }
+ }
+
+ if (needMoreData) {
+ data.trim();
+ if (data.empty()) {
+ // it's an empty line used to separate two subtitles
+ needMoreData = false;
+ }
+ }
+ }
+ info->textLen = *offset - info->offset;
+ return OK;
+}
+
+status_t TimedTextSRTSource::readNextLine(off64_t *offset, AString *data) {
+ data->clear();
+ while (true) {
+ ssize_t readSize;
+ char character;
+ if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) {
+ if (readSize == 0) {
+ return ERROR_END_OF_STREAM;
+ }
+ return ERROR_IO;
+ }
+
+ (*offset)++;
+
+ // a line could end with CR, LF or CR + LF
+ if (character == 10) {
+ break;
+ } else if (character == 13) {
+ if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) {
+ if (readSize == 0) { // end of the stream
+ return OK;
+ }
+ return ERROR_IO;
+ }
+
+ (*offset)++;
+ if (character != 10) {
+ (*offset)--;
+ }
+ break;
+ }
+ data->append(character);
+ }
+ return OK;
+}
+
+status_t TimedTextSRTSource::getText(
+ const MediaSource::ReadOptions *options,
+ AString *text, int64_t *startTimeUs, int64_t *endTimeUs) {
+ text->clear();
+ int64_t seekTimeUs;
+ MediaSource::ReadOptions::SeekMode mode;
+ if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
+ int64_t lastEndTimeUs =
+ mTextVector.valueAt(mTextVector.size() - 1).endTimeUs;
+ int64_t firstStartTimeUs = mTextVector.keyAt(0);
+ if (seekTimeUs < 0 || seekTimeUs > lastEndTimeUs) {
+ return ERROR_OUT_OF_RANGE;
+ } else if (seekTimeUs < firstStartTimeUs) {
+ mIndex = 0;
+ } else {
+ // binary search
+ ssize_t low = 0;
+ ssize_t high = mTextVector.size() - 1;
+ ssize_t mid = 0;
+ int64_t currTimeUs;
+
+ while (low <= high) {
+ mid = low + (high - low)/2;
+ currTimeUs = mTextVector.keyAt(mid);
+ const int diff = currTimeUs - seekTimeUs;
+
+ if (diff == 0) {
+ break;
+ } else if (diff < 0) {
+ low = mid + 1;
+ } else {
+ if ((high == mid + 1)
+ && (seekTimeUs < mTextVector.keyAt(high))) {
+ break;
+ }
+ high = mid - 1;
+ }
+ }
+ mIndex = mid;
+ }
+ }
+ const TextInfo &info = mTextVector.valueAt(mIndex);
+ *startTimeUs = mTextVector.keyAt(mIndex);
+ *endTimeUs = info.endTimeUs;
+ mIndex++;
+
+ char *str = new char[info.textLen];
+ if (mSource->readAt(info.offset, str, info.textLen) < info.textLen) {
+ delete[] str;
+ return ERROR_IO;
+ }
+ text->append(str, info.textLen);
+ delete[] str;
+ return OK;
+}
+
+status_t TimedTextSRTSource::extractAndAppendLocalDescriptions(
+ int64_t timeUs, const AString &text, Parcel *parcel) {
+ const void *data = text.c_str();
+ size_t size = text.size();
+ int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS |
+ TextDescriptions::OUT_OF_BAND_TEXT_SRT;
+
+ if (size > 0) {
+ return TextDescriptions::getParcelOfDescriptions(
+ (const uint8_t *)data, size, flag, timeUs / 1000, parcel);
+ }
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.h b/media/libstagefright/timedtext/TimedTextSRTSource.h
new file mode 100644
index 0000000..a0734d9
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextSRTSource.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#ifndef TIMED_TEXT_SRT_SOURCE_H_
+#define TIMED_TEXT_SRT_SOURCE_H_
+
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/Compat.h> // off64_t
+
+#include "TimedTextSource.h"
+
+namespace android {
+
+class AString;
+class DataSource;
+class MediaBuffer;
+class Parcel;
+
+class TimedTextSRTSource : public TimedTextSource {
+ public:
+ TimedTextSRTSource(const sp<DataSource>& dataSource);
+ virtual status_t start();
+ virtual status_t stop();
+ virtual status_t read(
+ int64_t *timeUs,
+ Parcel *parcel,
+ const MediaSource::ReadOptions *options = NULL);
+
+ protected:
+ virtual ~TimedTextSRTSource();
+
+ private:
+ sp<DataSource> mSource;
+
+ struct TextInfo {
+ int64_t endTimeUs;
+ // The offset of the text in the original file.
+ off64_t offset;
+ int textLen;
+ };
+
+ int mIndex;
+ KeyedVector<int64_t, TextInfo> mTextVector;
+
+ void reset();
+ status_t scanFile();
+ status_t getNextSubtitleInfo(
+ off64_t *offset, int64_t *startTimeUs, TextInfo *info);
+ status_t readNextLine(off64_t *offset, AString *data);
+ status_t getText(
+ const MediaSource::ReadOptions *options,
+ AString *text, int64_t *startTimeUs, int64_t *endTimeUs);
+ status_t extractAndAppendLocalDescriptions(
+ int64_t timeUs, const AString &text, Parcel *parcel);
+
+ DISALLOW_EVIL_CONSTRUCTORS(TimedTextSRTSource);
+};
+
+} // namespace android
+
+#endif // TIMED_TEXT_SRT_SOURCE_H_
diff --git a/media/libstagefright/timedtext/TimedTextSource.cpp b/media/libstagefright/timedtext/TimedTextSource.cpp
new file mode 100644
index 0000000..9efe67c
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextSource.cpp
@@ -0,0 +1,53 @@
+ /*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TimedTextSource"
+#include <utils/Log.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaSource.h>
+
+#include "TimedTextSource.h"
+
+#include "TimedTextInBandSource.h"
+#include "TimedTextSRTSource.h"
+
+namespace android {
+
+// static
+sp<TimedTextSource> TimedTextSource::CreateTimedTextSource(
+ const sp<MediaSource>& mediaSource) {
+ return new TimedTextInBandSource(mediaSource);
+}
+
+// static
+sp<TimedTextSource> TimedTextSource::CreateTimedTextSource(
+ const sp<DataSource>& dataSource, FileType filetype) {
+ switch(filetype) {
+ case OUT_OF_BAND_FILE_SRT:
+ return new TimedTextSRTSource(dataSource);
+ case OUT_OF_BAND_FILE_SMI:
+ // TODO: Implement for SMI.
+ ALOGE("Supporting SMI is not implemented yet");
+ break;
+ default:
+ ALOGE("Undefined subtitle format. : %d", filetype);
+ }
+ return NULL;
+}
+
+} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextSource.h b/media/libstagefright/timedtext/TimedTextSource.h
new file mode 100644
index 0000000..06bae71
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextSource.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef TIMED_TEXT_SOURCE_H_
+#define TIMED_TEXT_SOURCE_H_
+
+#include <media/stagefright/foundation/ABase.h> // for DISALLOW_XXX macro.
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h> // for MediaSource::ReadOptions
+#include <utils/RefBase.h>
+
+namespace android {
+
+class DataSource;
+class Parcel;
+
+class TimedTextSource : public RefBase {
+ public:
+ enum FileType {
+ OUT_OF_BAND_FILE_SRT = 1,
+ OUT_OF_BAND_FILE_SMI = 2,
+ };
+ static sp<TimedTextSource> CreateTimedTextSource(
+ const sp<MediaSource>& source);
+ static sp<TimedTextSource> CreateTimedTextSource(
+ const sp<DataSource>& source, FileType filetype);
+ TimedTextSource() {}
+ virtual status_t start() = 0;
+ virtual status_t stop() = 0;
+ // Returns subtitle parcel and its start time.
+ virtual status_t read(
+ int64_t *timeUs,
+ Parcel *parcel,
+ const MediaSource::ReadOptions *options = NULL) = 0;
+ virtual status_t extractGlobalDescriptions(Parcel *parcel) {
+ return INVALID_OPERATION;
+ }
+
+ protected:
+ virtual ~TimedTextSource() { }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(TimedTextSource);
+};
+
+} // namespace android
+
+#endif // TIMED_TEXT_SOURCE_H_
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 2fc6125..06be2ef 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -253,6 +253,19 @@
if (f == NULL) {
//ALOGD("%s", name);
f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented;
+
+ /*
+ * GL_EXT_debug_label is special, we always report it as
+ * supported, it's handled by GLES_trace. If GLES_trace is not
+ * enabled, then these are no-ops.
+ */
+ if (!strcmp(name, "glInsertEventMarkerEXT")) {
+ f = (__eglMustCastToProperFunctionPointerType)gl_noop;
+ } else if (!strcmp(name, "glPushGroupMarkerEXT")) {
+ f = (__eglMustCastToProperFunctionPointerType)gl_noop;
+ } else if (!strcmp(name, "glPopGroupMarkerEXT")) {
+ f = (__eglMustCastToProperFunctionPointerType)gl_noop;
+ }
}
*curr++ = f;
api++;
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index e053589..83933e5 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -233,6 +233,26 @@
// ----------------------------------------------------------------------------
+const GLubyte * egl_get_string_for_current_context(GLenum name) {
+ // NOTE: returning NULL here will fall-back to the default
+ // implementation.
+
+ EGLContext context = egl_tls_t::getContext();
+ if (context == EGL_NO_CONTEXT)
+ return NULL;
+
+ egl_context_t const * const c = get_context(context);
+ if (c == NULL) // this should never happen, by construction
+ return NULL;
+
+ if (name != GL_EXTENSIONS)
+ return NULL;
+
+ return (const GLubyte *)c->gl_extensions.string();
+}
+
+// ----------------------------------------------------------------------------
+
// this mutex protects:
// d->disp[]
// egl_init_drivers_locked()
@@ -290,6 +310,9 @@
ALOGE("called unimplemented OpenGL ES API");
}
+void gl_noop() {
+}
+
// ----------------------------------------------------------------------------
#if USE_FAST_TLS_KEY
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 8b37da5..73aab26 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -566,27 +566,6 @@
return result;
}
-static void loseCurrent(egl_context_t * cur_c)
-{
- if (cur_c) {
- egl_surface_t * cur_r = get_surface(cur_c->read);
- egl_surface_t * cur_d = get_surface(cur_c->draw);
-
- // by construction, these are either 0 or valid (possibly terminated)
- // it should be impossible for these to be invalid
- ContextRef _cur_c(cur_c);
- SurfaceRef _cur_r(cur_r);
- SurfaceRef _cur_d(cur_d);
-
- cur_c->read = NULL;
- cur_c->draw = NULL;
-
- _cur_c.release();
- _cur_r.release();
- _cur_d.release();
- }
-}
-
EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
EGLSurface read, EGLContext ctx)
{
@@ -663,21 +642,13 @@
impl_read = r->surface;
}
- EGLBoolean result;
- if (c) {
- result = c->cnx->egl.eglMakeCurrent(
- dp->disp[c->impl].dpy, impl_draw, impl_read, impl_ctx);
- } else {
- result = cur_c->cnx->egl.eglMakeCurrent(
- dp->disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx);
- }
+ EGLBoolean result = const_cast<egl_display_t*>(dp)->makeCurrent(c, cur_c,
+ draw, read, ctx,
+ impl_draw, impl_read, impl_ctx);
if (result == EGL_TRUE) {
-
- loseCurrent(cur_c);
-
- if (ctx != EGL_NO_CONTEXT) {
+ if (c) {
setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
egl_tls_t::setContext(ctx);
#if EGL_TRACE
@@ -687,8 +658,6 @@
_c.acquire();
_r.acquire();
_d.acquire();
- c->read = read;
- c->draw = draw;
} else {
setGLHooksThreadSpecific(&gHooksNoContext);
egl_tls_t::setContext(EGL_NO_CONTEXT);
@@ -924,7 +893,8 @@
cnx->hooks[GLESv1_INDEX]->ext.extensions[slot] =
cnx->hooks[GLESv2_INDEX]->ext.extensions[slot] =
#if EGL_TRACE
- debugHooks->ext.extensions[slot] = gHooksTrace.ext.extensions[slot] =
+ debugHooks->ext.extensions[slot] =
+ gHooksTrace.ext.extensions[slot] =
#endif
cnx->egl.eglGetProcAddress(procname);
}
@@ -1180,7 +1150,7 @@
clearError();
// If there is context bound to the thread, release it
- loseCurrent(get_context(getContext()));
+ egl_display_t::loseCurrent(get_context(getContext()));
for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 53eaf9a..5cf5236 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -342,6 +342,47 @@
return res;
}
+void egl_display_t::loseCurrent(egl_context_t * cur_c)
+{
+ if (cur_c) {
+ egl_surface_t * cur_r = get_surface(cur_c->read);
+ egl_surface_t * cur_d = get_surface(cur_c->draw);
+
+ // by construction, these are either 0 or valid (possibly terminated)
+ // it should be impossible for these to be invalid
+ ContextRef _cur_c(cur_c);
+ SurfaceRef _cur_r(cur_r);
+ SurfaceRef _cur_d(cur_d);
+
+ cur_c->onLooseCurrent();
+
+ _cur_c.release();
+ _cur_r.release();
+ _cur_d.release();
+ }
+}
+
+EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c,
+ EGLSurface draw, EGLSurface read, EGLContext ctx,
+ EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx)
+{
+ Mutex::Autolock _l(lock);
+ EGLBoolean result;
+ if (c) {
+ result = c->cnx->egl.eglMakeCurrent(
+ disp[c->impl].dpy, impl_draw, impl_read, impl_ctx);
+ } else {
+ result = cur_c->cnx->egl.eglMakeCurrent(
+ disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx);
+ }
+ if (result == EGL_TRUE) {
+ loseCurrent(cur_c);
+ if (c) {
+ c->onMakeCurrent(draw, read);
+ }
+ }
+ return result;
+}
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 042ae07..4479e00 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -39,6 +39,7 @@
// ----------------------------------------------------------------------------
class egl_object_t;
+class egl_context_t;
class egl_connection_t;
// ----------------------------------------------------------------------------
@@ -84,10 +85,14 @@
// add reference to this object. returns true if this is a valid object.
bool getObject(egl_object_t* object) const;
-
static egl_display_t* get(EGLDisplay dpy);
static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp);
+ EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c,
+ EGLSurface draw, EGLSurface read, EGLContext ctx,
+ EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx);
+ static void loseCurrent(egl_context_t * cur_c);
+
inline bool isReady() const { return (refs > 0); }
inline bool isValid() const { return magic == '_dpy'; }
inline bool isAlive() const { return isValid(); }
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 26e8c3e..b660c53 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -62,5 +62,41 @@
}
// ----------------------------------------------------------------------------
+
+egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
+ int impl, egl_connection_t const* cnx, int version) :
+ egl_object_t(get_display(dpy)), dpy(dpy), context(context),
+ config(config), read(0), draw(0), impl(impl), cnx(cnx),
+ version(version)
+{
+}
+
+void egl_context_t::onLooseCurrent() {
+ read = NULL;
+ draw = NULL;
+}
+
+void egl_context_t::onMakeCurrent(EGLSurface draw, EGLSurface read) {
+ this->read = read;
+ this->draw = draw;
+
+ /*
+ * Here we cache the GL_EXTENSIONS string for this context and we
+ * add the extensions always handled by the wrapper
+ */
+
+ if (gl_extensions.isEmpty()) {
+ // call the implementation's glGetString(GL_EXTENSIONS)
+ const char* exts = (const char *)gEGLImpl[impl].hooks[version]->gl.glGetString(GL_EXTENSIONS);
+ gl_extensions.setTo(exts);
+ if (gl_extensions.find("GL_EXT_debug_marker") < 0) {
+ String8 temp("GL_EXT_debug_marker ");
+ temp.append(gl_extensions);
+ gl_extensions.setTo(temp);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
}; // namespace android
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 7106fa5..abd4cbb 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -28,6 +28,7 @@
#include <GLES/glext.h>
#include <utils/threads.h>
+#include <utils/String8.h>
#include <system/window.h>
@@ -158,11 +159,11 @@
typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
- int impl, egl_connection_t const* cnx, int version) :
- egl_object_t(get_display(dpy)), dpy(dpy), context(context),
- config(config), read(0), draw(0), impl(impl), cnx(cnx),
- version(version) {
- }
+ int impl, egl_connection_t const* cnx, int version);
+
+ void onLooseCurrent();
+ void onMakeCurrent(EGLSurface draw, EGLSurface read);
+
EGLDisplay dpy;
EGLContext context;
EGLConfig config;
@@ -171,6 +172,7 @@
int impl;
egl_connection_t const* cnx;
int version;
+ String8 gl_extensions;
};
class egl_image_t: public egl_object_t {
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 107acd9..ff20957 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -58,6 +58,7 @@
extern gl_hooks_t gHooksNoContext;
extern pthread_key_t gGLWrapperKey;
extern "C" void gl_unimplemented();
+extern "C" void gl_noop();
extern char const * const gl_names[];
extern char const * const egl_names[];
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index f89c865..8dcf38d 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -82,23 +82,41 @@
#endif
+
#define GL_EXTENSION_LIST(name) \
- name(0) name(1) name(2) name(3) \
- name(4) name(5) name(6) name(7) \
- name(8) name(9) name(10) name(11) \
- name(12) name(13) name(14) name(15) \
- name(16) name(17) name(18) name(19) \
- name(20) name(21) name(22) name(23) \
- name(24) name(25) name(26) name(27) \
- name(28) name(29) name(30) name(31) \
- name(32) name(33) name(34) name(35) \
- name(36) name(37) name(38) name(39) \
- name(40) name(41) name(42) name(43) \
- name(44) name(45) name(46) name(47) \
- name(48) name(49) name(50) name(51) \
- name(52) name(53) name(54) name(55) \
- name(56) name(57) name(58) name(59) \
- name(60) name(61) name(62) name(63)
+ name(0) name(1) name(2) name(3) name(4) name(5) name(6) name(7) \
+ name(8) name(9) name(10) name(11) name(12) name(13) name(14) name(15) \
+ name(16) name(17) name(18) name(19) name(20) name(21) name(22) name(23) \
+ name(24) name(25) name(26) name(27) name(28) name(29) name(30) name(31) \
+ name(32) name(33) name(34) name(35) name(36) name(37) name(38) name(39) \
+ name(40) name(41) name(42) name(43) name(44) name(45) name(46) name(47) \
+ name(48) name(49) name(50) name(51) name(52) name(53) name(54) name(55) \
+ name(56) name(57) name(58) name(59) name(60) name(61) name(62) name(63) \
+ name(64) name(65) name(66) name(67) name(68) name(69) name(70) name(71) \
+ name(72) name(73) name(74) name(75) name(76) name(77) name(78) name(79) \
+ name(80) name(81) name(82) name(83) name(84) name(85) name(86) name(87) \
+ name(88) name(89) name(90) name(91) name(92) name(93) name(94) name(95) \
+ name(96) name(97) name(98) name(99) \
+ name(100) name(101) name(102) name(103) name(104) name(105) name(106) name(107) \
+ name(108) name(109) name(110) name(111) name(112) name(113) name(114) name(115) \
+ name(116) name(117) name(118) name(119) name(120) name(121) name(122) name(123) \
+ name(124) name(125) name(126) name(127) name(128) name(129) name(130) name(131) \
+ name(132) name(133) name(134) name(135) name(136) name(137) name(138) name(139) \
+ name(140) name(141) name(142) name(143) name(144) name(145) name(146) name(147) \
+ name(148) name(149) name(150) name(151) name(152) name(153) name(154) name(155) \
+ name(156) name(157) name(158) name(159) name(160) name(161) name(162) name(163) \
+ name(164) name(165) name(166) name(167) name(168) name(169) name(170) name(171) \
+ name(172) name(173) name(174) name(175) name(176) name(177) name(178) name(179) \
+ name(180) name(181) name(182) name(183) name(184) name(185) name(186) name(187) \
+ name(188) name(189) name(190) name(191) name(192) name(193) name(194) name(195) \
+ name(196) name(197) name(198) name(199) \
+ name(200) name(201) name(202) name(203) name(204) name(205) name(206) name(207) \
+ name(208) name(209) name(210) name(211) name(212) name(213) name(214) name(215) \
+ name(216) name(217) name(218) name(219) name(220) name(221) name(222) name(223) \
+ name(224) name(225) name(226) name(227) name(228) name(229) name(230) name(231) \
+ name(232) name(233) name(234) name(235) name(236) name(237) name(238) name(239) \
+ name(240) name(241) name(242) name(243) name(244) name(245) name(246) name(247) \
+ name(248) name(249) name(250) name(251) name(252) name(253) name(254) name(255)
GL_EXTENSION_LIST( GL_EXTENSION )
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index df22b96..79aa3cd 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -110,6 +110,20 @@
#undef CALL_GL_API
#undef CALL_GL_API_RETURN
+/*
+ * glGetString() is special because we expose some extensions in the wrapper
+ */
+
+extern "C" const GLubyte * __glGetString(GLenum name);
+
+const GLubyte * glGetString(GLenum name)
+{
+ const GLubyte * ret = egl_get_string_for_current_context(name);
+ if (ret == NULL) {
+ ret = __glGetString(name);
+ }
+ return ret;
+}
/*
* These GL calls are special because they need to EGL to retrieve some
diff --git a/opengl/libs/GLES2/gl2_api.in b/opengl/libs/GLES2/gl2_api.in
index 5164450..9a89a52 100644
--- a/opengl/libs/GLES2/gl2_api.in
+++ b/opengl/libs/GLES2/gl2_api.in
@@ -211,7 +211,7 @@
void API_ENTRY(glGetShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source) {
CALL_GL_API(glGetShaderSource, shader, bufsize, length, source);
}
-const GLubyte* API_ENTRY(glGetString)(GLenum name) {
+const GLubyte* API_ENTRY(__glGetString)(GLenum name) {
CALL_GL_API_RETURN(glGetString, name);
}
void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat* params) {
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index 2d31a35..adeaa5b 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -165,6 +165,20 @@
#undef CALL_GL_API
#undef CALL_GL_API_RETURN
+/*
+ * glGetString() is special because we expose some extensions in the wrapper
+ */
+
+extern "C" const GLubyte * __glGetString(GLenum name);
+
+const GLubyte * glGetString(GLenum name)
+{
+ const GLubyte * ret = egl_get_string_for_current_context(name);
+ if (ret == NULL) {
+ ret = __glGetString(name);
+ }
+ return ret;
+}
/*
* These GL calls are special because they need to EGL to retrieve some
diff --git a/opengl/libs/GLES_CM/gl_api.in b/opengl/libs/GLES_CM/gl_api.in
index 7f20c4f..c8f6b0c 100644
--- a/opengl/libs/GLES_CM/gl_api.in
+++ b/opengl/libs/GLES_CM/gl_api.in
@@ -262,7 +262,7 @@
void API_ENTRY(glGetPointerv)(GLenum pname, GLvoid **params) {
CALL_GL_API(glGetPointerv, pname, params);
}
-const GLubyte * API_ENTRY(glGetString)(GLenum name) {
+const GLubyte * API_ENTRY(__glGetString)(GLenum name) {
CALL_GL_API_RETURN(glGetString, name);
}
void API_ENTRY(glGetTexEnviv)(GLenum env, GLenum pname, GLint *params) {
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index a809316..8ff51ec 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -29,6 +29,7 @@
namespace android {
// ----------------------------------------------------------------------------
+EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name);
EGLAPI EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image);
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/tools/glapigen b/opengl/libs/tools/glapigen
index bd8dda3..9be40cf 100755
--- a/opengl/libs/tools/glapigen
+++ b/opengl/libs/tools/glapigen
@@ -43,6 +43,9 @@
if ($name eq "glEGLImageTargetRenderbufferStorageOES") {
$prefix = "__";
}
+ if ($name eq "glGetString") {
+ $prefix = "__";
+ }
printf("%s API_ENTRY(%s%s)(%s)", $type, $prefix, $name, $args);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index d0f72a4..d787e10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -588,8 +588,12 @@
}
}
- if ((isCdma() && isCdmaEri()) || mPhone.isNetworkRoaming()) {
- mDataTypeIconId = R.drawable.stat_sys_data_connected_roam;
+ if (isCdma()) {
+ if (isCdmaEri()) {
+ mDataTypeIconId = R.drawable.stat_sys_data_connected_roam;
+ }
+ } else if (mPhone.isNetworkRoaming()) {
+ mDataTypeIconId = R.drawable.stat_sys_data_connected_roam;
}
}
@@ -1019,10 +1023,13 @@
mContentDescriptionCombinedSignal = mHasMobileDataFeature
? mContentDescriptionDataType : mContentDescriptionWifi;
- if ((isCdma() && isCdmaEri()) || mPhone.isNetworkRoaming()) {
+ mDataTypeIconId = 0;
+ if (isCdma()) {
+ if (isCdmaEri()) {
+ mDataTypeIconId = R.drawable.stat_sys_data_connected_roam;
+ }
+ } else if (mPhone.isNetworkRoaming()) {
mDataTypeIconId = R.drawable.stat_sys_data_connected_roam;
- } else {
- mDataTypeIconId = 0;
}
}
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 1f8cf63..296c95e 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -831,7 +831,7 @@
ALOGV("Opening device: %s", devicePath);
- int fd = open(devicePath, O_RDWR);
+ int fd = open(devicePath, O_RDWR | O_CLOEXEC);
if(fd < 0) {
ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
return -1;
@@ -1063,14 +1063,20 @@
return -1;
}
+ // Enable wake-lock behavior on kernels that support it.
+ // TODO: Only need this for devices that can really wake the system.
+ bool usingSuspendBlock = ioctl(fd, EVIOCSSUSPENDBLOCK, 1) == 0;
+
ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
- "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s",
+ "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, "
+ "usingSuspendBlock=%s",
deviceId, fd, devicePath, device->identifier.name.string(),
device->classes,
device->configurationFile.string(),
device->keyMap.keyLayoutFile.string(),
device->keyMap.keyCharacterMapFile.string(),
- toString(mBuiltInKeyboardId == deviceId));
+ toString(mBuiltInKeyboardId == deviceId),
+ toString(usingSuspendBlock));
mDevices.add(deviceId, device);
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 95d651a..732af53 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -23,18 +23,22 @@
LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+ifeq ($(TARGET_HAS_WAITFORVSYNC), true)
+ LOCAL_CFLAGS += -DHAS_WAITFORVSYNC
+endif
+
ifeq ($(TARGET_BOARD_PLATFORM), omap3)
LOCAL_CFLAGS += -DNO_RGBX_8888
endif
ifeq ($(TARGET_BOARD_PLATFORM), omap4)
LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
+ LOCAL_CFLAGS += -DUSE_TRIPLE_BUFFERING=1
endif
ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE
LOCAL_CFLAGS += -DREFRESH_RATE=56
endif
-
LOCAL_SHARED_LIBRARIES := \
libcutils \
libhardware \
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index 6796d7d..92d4266 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -36,6 +36,7 @@
EventThread::EventThread(const sp<SurfaceFlinger>& flinger)
: mFlinger(flinger),
mHw(flinger->graphicPlane(0).displayHardware()),
+ mLastVSyncTimestamp(0),
mDeliveredEvents(0)
{
}
@@ -44,6 +45,20 @@
run("EventThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
}
+sp<DisplayEventConnection> EventThread::createEventConnection() const {
+ return new DisplayEventConnection(const_cast<EventThread*>(this));
+}
+
+nsecs_t EventThread::getLastVSyncTimestamp() const {
+ Mutex::Autolock _l(mLock);
+ return mLastVSyncTimestamp;
+}
+
+nsecs_t EventThread::getVSyncPeriod() const {
+ return mHw.getRefreshPeriod();
+
+}
+
status_t EventThread::registerDisplayEventConnection(
const sp<DisplayEventConnection>& connection) {
Mutex::Autolock _l(mLock);
@@ -80,8 +95,11 @@
Mutex::Autolock _l(mLock);
ConnectionInfo* info = getConnectionInfoLocked(connection);
if (info) {
- info->count = (count == 0) ? -1 : count;
- mCondition.signal();
+ const int32_t new_count = (count == 0) ? -1 : count;
+ if (info->count != new_count) {
+ info->count = new_count;
+ mCondition.signal();
+ }
}
}
}
@@ -90,10 +108,8 @@
const wp<DisplayEventConnection>& connection) {
Mutex::Autolock _l(mLock);
ConnectionInfo* info = getConnectionInfoLocked(connection);
- if (info) {
- if (info->count < 0) {
- info->count = 0;
- }
+ if (info && info->count < 0) {
+ info->count = 0;
mCondition.signal();
}
}
@@ -132,6 +148,7 @@
timestamp = mHw.waitForRefresh();
mLock.lock();
mDeliveredEvents++;
+ mLastVSyncTimestamp = timestamp;
// now see if we still need to report this VSYNC event
bool reportVsync = false;
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 35bd299..3a3071e 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -36,6 +36,7 @@
class SurfaceFlinger;
class DisplayHardware;
+class DisplayEventConnection;
// ---------------------------------------------------------------------------
@@ -45,6 +46,8 @@
public:
EventThread(const sp<SurfaceFlinger>& flinger);
+ sp<DisplayEventConnection> createEventConnection() const;
+
status_t registerDisplayEventConnection(
const sp<DisplayEventConnection>& connection);
@@ -56,6 +59,10 @@
void requestNextVsync(const wp<DisplayEventConnection>& connection);
+ nsecs_t getLastVSyncTimestamp() const;
+
+ nsecs_t getVSyncPeriod() const;
+
void dump(String8& result, char* buffer, size_t SIZE) const;
private:
@@ -88,6 +95,7 @@
// protected by mLock
KeyedVector< wp<DisplayEventConnection>, ConnectionInfo > mDisplayEventConnections;
+ nsecs_t mLastVSyncTimestamp;
// main thread only
size_t mDeliveredEvents;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8e87b88..9c04d59 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -97,7 +97,12 @@
mSurfaceTexture = new SurfaceTextureLayer(mTextureName, this);
mSurfaceTexture->setFrameAvailableListener(new FrameQueuedListener(this));
mSurfaceTexture->setSynchronousMode(true);
+#ifdef USE_TRIPLE_BUFFERING
+#warning "using triple buffering"
+ mSurfaceTexture->setBufferCountServer(3);
+#else
mSurfaceTexture->setBufferCountServer(2);
+#endif
}
Layer::~Layer()
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index cbd530c..70711e7 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -18,12 +18,17 @@
#include <errno.h>
#include <sys/types.h>
+#include <binder/IPCThreadState.h>
+
#include <utils/threads.h>
#include <utils/Timers.h>
#include <utils/Log.h>
-#include <binder/IPCThreadState.h>
+
+#include <gui/IDisplayEventConnection.h>
+#include <gui/BitTube.h>
#include "MessageQueue.h"
+#include "EventThread.h"
namespace android {
@@ -51,6 +56,15 @@
MessageQueue::~MessageQueue() {
}
+void MessageQueue::setEventThread(const sp<EventThread>& eventThread)
+{
+ mEventThread = eventThread;
+ mEvents = eventThread->createEventConnection();
+ mEventTube = mEvents->getDataChannel();
+ mLooper->addFd(mEventTube->getFd(), 0, ALOOPER_EVENT_INPUT,
+ MessageQueue::cb_eventReceiver, this);
+}
+
void MessageQueue::waitMessage() {
do {
IPCThreadState::self()->flushCommands();
@@ -93,13 +107,54 @@
return NO_ERROR;
}
-status_t MessageQueue::invalidate() {
+void MessageQueue::scheduleWorkASAP() {
if (android_atomic_or(1, &mWorkPending) == 0) {
mLooper->wake();
- }
+ }
+}
+
+status_t MessageQueue::invalidate() {
+ mEvents->requestNextVsync();
return NO_ERROR;
}
+int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
+ MessageQueue* queue = reinterpret_cast<MessageQueue *>(data);
+ return queue->eventReceiver(fd, events);
+}
+
+int MessageQueue::eventReceiver(int fd, int events) {
+ ssize_t n;
+ DisplayEventReceiver::Event buffer[8];
+ while ((n = getEvents(buffer, 8)) > 0) {
+ for (int i=0 ; i<n ; i++) {
+ if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ scheduleWorkASAP();
+ break;
+ }
+ }
+ }
+ return 1;
+}
+
+ssize_t MessageQueue::getEvents(
+ DisplayEventReceiver::Event* events, size_t count)
+{
+ ssize_t size = mEventTube->read(events, sizeof(events[0])*count);
+ ALOGE_IF(size<0, "MessageQueue::getEvents error (%s)", strerror(-size));
+ if (size >= 0) {
+ // Note: if (size % sizeof(events[0])) != 0, we've got a
+ // partial read. This can happen if the queue filed up (ie: if we
+ // didn't pull from it fast enough).
+ // We discard the partial event and rely on the sender to
+ // re-send the event if appropriate (some events, like VSYNC
+ // can be lost forever).
+ // returns number of events read
+ size /= sizeof(events[0]);
+ }
+ return size;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
index 2317d81..5ea197d 100644
--- a/services/surfaceflinger/MessageQueue.h
+++ b/services/surfaceflinger/MessageQueue.h
@@ -25,10 +25,15 @@
#include <utils/Timers.h>
#include <utils/Looper.h>
+#include <gui/DisplayEventReceiver.h>
+
#include "Barrier.h"
namespace android {
+class IDisplayEventConnection;
+class EventThread;
+
// ---------------------------------------------------------------------------
class MessageBase : public MessageHandler
@@ -55,11 +60,20 @@
class MessageQueue {
sp<Looper> mLooper;
- volatile int32_t mWorkPending;
+ sp<EventThread> mEventThread;
+ sp<IDisplayEventConnection> mEvents;
+ sp<BitTube> mEventTube;
+ int32_t mWorkPending;
+
+ static int cb_eventReceiver(int fd, int events, void* data);
+ int eventReceiver(int fd, int events);
+ ssize_t getEvents(DisplayEventReceiver::Event* events, size_t count);
+ void scheduleWorkASAP();
public:
MessageQueue();
~MessageQueue();
+ void setEventThread(const sp<EventThread>& events);
void waitMessage();
status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime=0);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 887aee7..ff70ec3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -296,6 +296,7 @@
// start the EventThread
mEventThread = new EventThread(this);
+ mEventQueue.setEventThread(mEventThread);
/*
* We're now ready to accept clients...
@@ -383,8 +384,7 @@
// ----------------------------------------------------------------------------
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
- sp<DisplayEventConnection> result(new DisplayEventConnection(mEventThread));
- return result;
+ return mEventThread->createEventConnection();
}
// ----------------------------------------------------------------------------
@@ -432,7 +432,6 @@
} else {
// pretend we did the post
hw.compositionComplete();
- hw.waitForRefresh();
}
return true;
}
diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp
index 259b937..49e8e63 100644
--- a/services/surfaceflinger/SurfaceTextureLayer.cpp
+++ b/services/surfaceflinger/SurfaceTextureLayer.cpp
@@ -94,6 +94,10 @@
*outTransform = orientation;
}
switch(api) {
+ case NATIVE_WINDOW_API_CPU:
+ // SurfaceTextureClient supports only 2 buffers for CPU connections
+ this->setBufferCountServer(2);
+ break;
case NATIVE_WINDOW_API_MEDIA:
case NATIVE_WINDOW_API_CAMERA:
// Camera preview and videos are rate-limited on the producer
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 05e198f..1a4b574 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -47,6 +47,9 @@
"none", "poor", "moderate", "good", "great"
};
+ /** @hide */
+ public static final int INVALID_SNR = 0x7FFFFFFF;
+
private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
private int mGsmBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
private int mCdmaDbm; // This value is the RSSI value
@@ -96,7 +99,7 @@
mLteSignalStrength = -1;
mLteRsrp = -1;
mLteRsrq = -1;
- mLteRssnr = -1;
+ mLteRssnr = INVALID_SNR;
mLteCqi = -1;
isGsm = true;
}
@@ -136,7 +139,8 @@
int evdoDbm, int evdoEcio, int evdoSnr,
boolean gsm) {
this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
- evdoDbm, evdoEcio, evdoSnr, -1, -1, -1, -1, -1, gsm);
+ evdoDbm, evdoEcio, evdoSnr, -1, -1,
+ -1, INVALID_SNR, -1, gsm);
}
/**
@@ -292,7 +296,7 @@
if ((mLteSignalStrength == -1)
&& (mLteRsrp == -1)
&& (mLteRsrq == -1)
- && (mLteRssnr == -1)
+ && (mLteRssnr == INVALID_SNR)
&& (mLteCqi == -1)) {
level = getGsmLevel();
} else {
@@ -327,7 +331,7 @@
if ((mLteSignalStrength == -1)
&& (mLteRsrp == -1)
&& (mLteRsrq == -1)
- && (mLteRssnr == -1)
+ && (mLteRssnr == INVALID_SNR)
&& (mLteCqi == -1)) {
asuLevel = getGsmAsuLevel();
} else {
@@ -363,7 +367,7 @@
if ((mLteSignalStrength == -1)
&& (mLteRsrp == -1)
&& (mLteRsrq == -1)
- && (mLteRssnr == -1)
+ && (mLteRssnr == INVALID_SNR)
&& (mLteCqi == -1)) {
dBm = getGsmDbm();
} else {
@@ -566,6 +570,7 @@
*/
public int getLteLevel() {
int levelLteRsrp = 0;
+ int levelLteRssnr = 0;
if (mLteRsrp == -1) levelLteRsrp = 0;
else if (mLteRsrp >= -95) levelLteRsrp = SIGNAL_STRENGTH_GREAT;
@@ -573,8 +578,23 @@
else if (mLteRsrp >= -115) levelLteRsrp = SIGNAL_STRENGTH_MODERATE;
else levelLteRsrp = SIGNAL_STRENGTH_POOR;
- if (DBG) log("Lte level: "+levelLteRsrp);
- return levelLteRsrp;
+ if (mLteRssnr == INVALID_SNR) levelLteRssnr = 0;
+ else if (mLteRssnr >= 45) levelLteRssnr = SIGNAL_STRENGTH_GREAT;
+ else if (mLteRssnr >= 10) levelLteRssnr = SIGNAL_STRENGTH_GOOD;
+ else if (mLteRssnr >= -30) levelLteRssnr = SIGNAL_STRENGTH_MODERATE;
+ else levelLteRssnr = SIGNAL_STRENGTH_POOR;
+
+ int level;
+ if (mLteRsrp == -1)
+ level = levelLteRssnr;
+ else if (mLteRssnr == INVALID_SNR)
+ level = levelLteRsrp;
+ else
+ level = (levelLteRssnr < levelLteRsrp) ? levelLteRssnr : levelLteRsrp;
+
+ if (DBG) log("Lte rsrp level: "+levelLteRsrp
+ + " snr level: " + levelLteRssnr + " level: " + level);
+ return level;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index 6b73cc5..7da23d0 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -124,7 +124,9 @@
@Override
protected void setSignalStrengthDefaultValues() {
- mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, false);
+ // TODO Make a constructor only has boolean gsm as parameter
+ mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, SignalStrength.INVALID_SNR, -1, false);
}
@Override
@@ -429,8 +431,13 @@
setSignalStrengthDefaultValues();
} else {
int[] ints = (int[])ar.result;
- int lteCqi = 99, lteRsrp = -1;
- int lteRssi = 99;
+
+ int lteRssi = -1;
+ int lteRsrp = -1;
+ int lteRsrq = -1;
+ int lteRssnr = SignalStrength.INVALID_SNR;
+ int lteCqi = -1;
+
int offset = 2;
int cdmaDbm = (ints[offset] > 0) ? -ints[offset] : -120;
int cdmaEcio = (ints[offset + 1] > 0) ? -ints[offset + 1] : -160;
@@ -438,10 +445,13 @@
int evdoEcio = (ints[offset + 3] > 0) ? -ints[offset + 3] : -1;
int evdoSnr = ((ints[offset + 4] > 0) && (ints[offset + 4] <= 8)) ? ints[offset + 4]
: -1;
+
if (networkType == ServiceState.RADIO_TECHNOLOGY_LTE) {
- lteRssi = (ints[offset + 5] >= 0) ? ints[offset + 5] : 99;
- lteRsrp = (ints[offset + 6] < 0) ? ints[offset + 6] : -1;
- lteCqi = (ints[offset + 7] >= 0) ? ints[offset + 7] : 99;
+ lteRssi = ints[offset+5];
+ lteRsrp = ints[offset+6];
+ lteRsrq = ints[offset+7];
+ lteRssnr = ints[offset+8];
+ lteCqi = ints[offset+9];
}
if (networkType != ServiceState.RADIO_TECHNOLOGY_LTE) {
@@ -449,7 +459,7 @@
evdoSnr, false);
} else {
mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio,
- evdoSnr, lteRssi, lteRsrp, -1, -1, lteCqi, true);
+ evdoSnr, lteRssi, lteRsrp, lteRsrq, lteRssnr, lteCqi, true);
}
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 92e16ce..0ebeabe 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -675,7 +675,9 @@
}
private void setSignalStrengthDefaultValues() {
- mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, true);
+ // TODO Make a constructor only has boolean gsm as parameter
+ mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, SignalStrength.INVALID_SNR, -1, true);
}
/**
@@ -1013,7 +1015,7 @@
int lteSignalStrength = -1;
int lteRsrp = -1;
int lteRsrq = -1;
- int lteRssnr = -1;
+ int lteRssnr = SignalStrength.INVALID_SNR;
int lteCqi = -1;
if (ar.exception != null) {
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index e2d2686..5b76272 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -512,7 +512,8 @@
public void testFormatNumber() {
assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "US"));
assertEquals("223-4567", PhoneNumberUtils.formatNumber("2234567", "US"));
- assertEquals("(800) 466-4114", PhoneNumberUtils.formatNumber("800-GOOG-114", "US"));
+ // formatNumber doesn't format alpha numbers, but keep them as they are.
+ assertEquals("800-GOOG-114", PhoneNumberUtils.formatNumber("800-GOOG-114", "US"));
}
@SmallTest
@@ -592,9 +593,12 @@
// addressing that, they are also classified as "potential" emergency numbers in the US.
assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("91112345", "US"));
assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("11212345", "US"));
+
// A valid mobile phone number from Singapore shouldn't be classified as an emergency number
// in Singapore, as 911 is not an emergency number there.
- assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91121234", "SG"));
+ // This test fails on devices that have ecclist property preloaded with 911.
+ // assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91121234", "SG"));
+
// A valid fixed-line phone number from Brazil shouldn't be classified as an emergency number
// in Brazil, as 112 is not an emergency number there.
assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("1121234567", "BR"));