merge in jb-mr2-release history after reset to jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index f4407b5..f2fcbd6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24953,6 +24953,14 @@
field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
}
+ public abstract interface Overlay {
+ method public abstract void add(android.graphics.drawable.Drawable);
+ method public abstract void add(android.view.View);
+ method public abstract void clear();
+ method public abstract void remove(android.graphics.drawable.Drawable);
+ method public abstract void remove(android.view.View);
+ }
+
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
method public float getCurrentSpan();
@@ -25257,6 +25265,7 @@
method public int getNextFocusUpId();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
method public int getOverScrollMode();
+ method public android.view.Overlay getOverlay();
method public int getPaddingBottom();
method public int getPaddingEnd();
method public int getPaddingLeft();
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index 58105fa..da8586c 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -36,12 +36,18 @@
return shortHelp() + "\n"
+ "\n"
+ "usage: svc power stayon [true|false|usb|ac|wireless]\n"
- + " Set the 'keep awake while plugged in' setting.\n";
+ + " Set the 'keep awake while plugged in' setting.\n"
+ + " svc power reboot [reason]\n"
+ + " Perform a runtime shutdown and reboot device with specified reason.\n"
+ + " svc power shutdown\n"
+ + " Perform a runtime shutdown and power off the device.\n";
}
public void run(String[] args) {
fail: {
if (args.length >= 2) {
+ IPowerManager pm = IPowerManager.Stub.asInterface(
+ ServiceManager.getService(Context.POWER_SERVICE));
if ("stayon".equals(args[1]) && args.length == 3) {
int val;
if ("true".equals(args[2])) {
@@ -60,8 +66,6 @@
} else {
break fail;
}
- IPowerManager pm
- = IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE));
try {
if (val != 0) {
// if the request is not to set it to false, wake up the screen so that
@@ -74,6 +78,26 @@
System.err.println("Faild to set setting: " + e);
}
return;
+ } else if ("reboot".equals(args[1])) {
+ String mode = null;
+ if (args.length == 3) {
+ mode = args[2];
+ }
+ try {
+ // no confirm, wait till device is rebooted
+ pm.reboot(false, mode, true);
+ } catch (RemoteException e) {
+ System.err.println("Failed to reboot.");
+ }
+ return;
+ } else if ("shutdown".equals(args[1])) {
+ try {
+ // no confirm, wait till device is off
+ pm.shutdown(false, true);
+ } catch (RemoteException e) {
+ System.err.println("Failed to shutdown.");
+ }
+ return;
}
}
}
diff --git a/core/java/android/view/Overlay.java b/core/java/android/view/Overlay.java
new file mode 100644
index 0000000..f15d4d2
--- /dev/null
+++ b/core/java/android/view/Overlay.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * An overlay is an extra layer that sits on top of a View (the "host view") which is drawn after
+ * all other content in that view (including children, if the view is a ViewGroup). Interaction
+ * with the overlay layer is done in terms of adding/removing views and drawables. Invalidation and
+ * redrawing of the overlay layer (and its host view) is handled differently for views versus
+ * drawables in the overlay. Views invalidate themselves as usual, causing appropriate redrawing
+ * to occur automatically. Drawables, on the other hand, do not manage invalidation, so changes to
+ * drawable objects should be accompanied by appropriate calls to invalidate() on the host view.
+ *
+ * @see android.view.View#getOverlay()
+ */
+public interface Overlay {
+
+ /**
+ * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
+ * the host view. Any drawable added to the overlay should be removed when it is no longer
+ * needed or no longer visible. There is no automatic invalidation of the host view; changes to
+ * the drawable should be accompanied by appropriate invalidation calls to the host view
+ * to cause the proper area of the view, and the overlay, to be redrawn.
+ *
+ * @param drawable The Drawable to be added to the overlay. This drawable will be
+ * drawn when the view redraws its overlay.
+ * @see #remove(android.graphics.drawable.Drawable)
+ * @see #add(View)
+ */
+ void add(Drawable drawable);
+
+ /**
+ * Removes the specified Drawable from the overlay.
+ *
+ * @param drawable The Drawable to be removed from the overlay.
+ * @see #add(android.graphics.drawable.Drawable)
+ */
+ void remove(Drawable drawable);
+
+ /**
+ * Adds a View to the overlay. The bounds of the added view should be relative to
+ * the host view. Any view added to the overlay should be removed when it is no longer
+ * needed or no longer visible. The view must not be parented elsewhere when it is added
+ * to the overlay.
+ *
+ * @param view The View to be added to the overlay. The added view will be
+ * drawn when the overlay is drawn.
+ * @see #remove(View)
+ * @see #add(android.graphics.drawable.Drawable)
+ */
+ void add(View view);
+
+ /**
+ * Removes the specified View from the overlay.
+ *
+ * @param view The View to be removed from the overlay.
+ * @see #add(View)
+ */
+ void remove(View view);
+
+ /**
+ * Removes all views and drawables from the overlay.
+ */
+ void clear();
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index df07dcd..e869d09 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -574,7 +574,8 @@
* @param maxLayer The highest (top-most Z order) surface layer to
* include in the screenshot.
* @return Returns a Bitmap containing the screen contents, or null
- * if an error occurs.
+ * if an error occurs. Make sure to call Bitmap.recycle() as soon as
+ * possible, once its content is not needed anymore.
*/
public static Bitmap screenshot(int width, int height, int minLayer, int maxLayer) {
// TODO: should take the display as a parameter
@@ -586,6 +587,14 @@
/**
* Like {@link SurfaceControl#screenshot(int, int, int, int)} but includes all
* Surfaces in the screenshot.
+ *
+ * @param width The desired width of the returned bitmap; the raw
+ * screen will be scaled down to this size.
+ * @param height The desired height of the returned bitmap; the raw
+ * screen will be scaled down to this size.
+ * @return Returns a Bitmap containing the screen contents, or null
+ * if an error occurs. Make sure to call Bitmap.recycle() as soon as
+ * possible, once its content is not needed anymore.
*/
public static Bitmap screenshot(int width, int height) {
// TODO: should take the display as a parameter
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d412d82..2fa9484 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3223,6 +3223,12 @@
AccessibilityDelegate mAccessibilityDelegate;
/**
+ * The view's overlay layer. Developers get a reference to the overlay via getOverlay()
+ * and add/remove objects to/from the overlay directly through the Overlay methods.
+ */
+ ViewOverlay mOverlay;
+
+ /**
* Consistency verifier for debugging purposes.
* @hide
*/
@@ -6069,8 +6075,7 @@
mTransientStateCount = 0;
Log.e(VIEW_LOG_TAG, "hasTransientState decremented below 0: " +
"unmatched pair of setHasTransientState calls");
- }
- if ((hasTransientState && mTransientStateCount == 1) ||
+ } else if ((hasTransientState && mTransientStateCount == 1) ||
(!hasTransientState && mTransientStateCount == 0)) {
// update flag if we've just incremented up from 0 or decremented down to 0
mPrivateFlags2 = (mPrivateFlags2 & ~PFLAG2_HAS_TRANSIENT_STATE) |
@@ -9590,7 +9595,7 @@
mDisplayList.setTop(mTop);
}
- onSizeChanged(width, mBottom - mTop, width, oldHeight);
+ sizeChange(width, mBottom - mTop, width, oldHeight);
if (!matrixIsIdentity) {
if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
@@ -9663,7 +9668,7 @@
mDisplayList.setBottom(mBottom);
}
- onSizeChanged(width, mBottom - mTop, width, oldHeight);
+ sizeChange(width, mBottom - mTop, width, oldHeight);
if (!matrixIsIdentity) {
if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
@@ -9730,7 +9735,7 @@
mDisplayList.setLeft(left);
}
- onSizeChanged(mRight - mLeft, height, oldWidth, height);
+ sizeChange(mRight - mLeft, height, oldWidth, height);
if (!matrixIsIdentity) {
if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
@@ -9794,7 +9799,7 @@
mDisplayList.setRight(mRight);
}
- onSizeChanged(mRight - mLeft, height, oldWidth, height);
+ sizeChange(mRight - mLeft, height, oldWidth, height);
if (!matrixIsIdentity) {
if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) {
@@ -12092,6 +12097,9 @@
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
//System.out.println("Attached! " + this);
mAttachInfo = info;
+ if (mOverlay != null) {
+ mOverlay.mAttachInfo = info;
+ }
mWindowAttachCount++;
// We will need to evaluate the drawable state at least once.
mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
@@ -12160,6 +12168,9 @@
}
mAttachInfo = null;
+ if (mOverlay != null) {
+ mOverlay.mAttachInfo = null;
+ }
}
/**
@@ -12815,6 +12826,9 @@
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
+ if (mOverlay != null && !mOverlay.isEmpty()) {
+ mOverlay.draw(canvas);
+ }
} else {
draw(canvas);
}
@@ -13128,6 +13142,9 @@
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
+ if (mOverlay != null && !mOverlay.isEmpty()) {
+ mOverlay.draw(canvas);
+ }
} else {
draw(canvas);
}
@@ -13883,6 +13900,10 @@
// Step 6, draw decorations (scrollbars)
onDrawScrollBars(canvas);
+ if (mOverlay != null && !mOverlay.isEmpty()) {
+ mOverlay.dispatchDraw(canvas);
+ }
+
// we're done...
return;
}
@@ -14022,6 +14043,37 @@
// Step 6, draw decorations (scrollbars)
onDrawScrollBars(canvas);
+
+ if (mOverlay != null && !mOverlay.isEmpty()) {
+ mOverlay.dispatchDraw(canvas);
+ }
+ }
+
+ /**
+ * Called by the addToOverlay() methods to create, attach, and size the overlay as necessary
+ */
+ private void setupOverlay() {
+ if (mOverlay == null) {
+ mOverlay = new ViewOverlay(mContext, this);
+ mOverlay.mAttachInfo = mAttachInfo;
+ mOverlay.setRight(mRight);
+ mOverlay.setBottom(mBottom);
+ }
+ }
+
+ /**
+ * Returns the overlay for this view, creating it if it does not yet exist. Adding drawables
+ * and/or views to the overlay will cause them to be displayed whenever the view itself is
+ * redrawn. Objects in the overlay should be actively managed: remove them when they should
+ * not be displayed anymore and invalidate this view appropriately when overlay drawables
+ * change. The overlay will always have the same size as its host view.
+ *
+ * @return The Overlay object for this view.
+ * @see Overlay
+ */
+ public Overlay getOverlay() {
+ setupOverlay();
+ return mOverlay;
}
/**
@@ -14277,7 +14329,7 @@
mTransformationInfo.mMatrixDirty = true;
}
}
- onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
+ sizeChange(newWidth, newHeight, oldWidth, oldHeight);
}
if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
@@ -14301,6 +14353,14 @@
return changed;
}
+ private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) {
+ onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
+ if (mOverlay != null) {
+ mOverlay.setRight(mRight);
+ mOverlay.setBottom(mBottom);
+ }
+ }
+
/**
* Finalize inflating a view from XML. This is called as the last phase
* of inflation, after all child views have been added.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 442dfdb..d63f7bc 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1869,7 +1869,7 @@
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
- if (childrenCount != 0) {
+ if (childrenCount != 0 || mOverlay != null) {
// Find a child that can receive the event.
// Scan children from front to back.
final View[] children = mChildren;
@@ -1906,6 +1906,27 @@
break;
}
}
+ if (mOverlay != null && newTouchTarget == null) {
+ // Check to see whether the overlay can handle the event
+ final View child = mOverlay;
+ if (canViewReceivePointerEvents(child) &&
+ isTransformedTouchPointInView(x, y, child, null)) {
+ newTouchTarget = getTouchTarget(child);
+ if (newTouchTarget != null) {
+ newTouchTarget.pointerIdBits |= idBitsToAssign;
+ } else {
+ resetCancelNextUpFlag(child);
+ if (dispatchTransformedTouchEvent(ev, false, child,
+ idBitsToAssign)) {
+ mLastTouchDownTime = ev.getDownTime();
+ mLastTouchDownX = ev.getX();
+ mLastTouchDownY = ev.getY();
+ newTouchTarget = addTouchTarget(child, idBitsToAssign);
+ alreadyDispatchedToNewTouchTarget = true;
+ }
+ }
+ }
+ }
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
@@ -3022,6 +3043,13 @@
child.mRecreateDisplayList = false;
}
}
+ if (mOverlay != null) {
+ mOverlay.mRecreateDisplayList = (mOverlay.mPrivateFlags & PFLAG_INVALIDATED)
+ == PFLAG_INVALIDATED;
+ mOverlay.mPrivateFlags &= ~PFLAG_INVALIDATED;
+ mOverlay.getDisplayList();
+ mOverlay.mRecreateDisplayList = false;
+ }
}
/**
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
new file mode 100644
index 0000000..8c2ab9d
--- /dev/null
+++ b/core/java/android/view/ViewOverlay.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+import java.util.ArrayList;
+
+/**
+ * ViewOverlay is a container that View uses to host all objects (views and drawables) that
+ * are added to its "overlay", gotten through {@link View#getOverlay()}. Views and drawables are
+ * added to the overlay via the add/remove methods in this class. These views and drawables are
+ * then drawn whenever the view itself is drawn, after which it will draw its overlay (if it
+ * exists).
+ *
+ * Besides managing and drawing the list of drawables, this class serves two purposes:
+ * (1) it noops layout calls because children are absolutely positioned and
+ * (2) it forwards all invalidation calls to its host view. The invalidation redirect is
+ * necessary because the overlay is not a child of the host view and invalidation cannot
+ * therefore follow the normal path up through the parent hierarchy.
+ *
+ * @hide
+ */
+class ViewOverlay extends ViewGroup implements Overlay {
+
+ /**
+ * The View for which this is an overlay. Invalidations of the overlay are redirected to
+ * this host view.
+ */
+ View mHostView;
+
+ /**
+ * The set of drawables to draw when the overlay is rendered.
+ */
+ ArrayList<Drawable> mDrawables = null;
+
+ ViewOverlay(Context context, View host) {
+ super(context);
+ mHostView = host;
+ mParent = mHostView.getParent();
+ }
+
+ @Override
+ public void add(Drawable drawable) {
+ if (mDrawables == null) {
+ mDrawables = new ArrayList<Drawable>();
+ }
+ if (!mDrawables.contains(drawable)) {
+ // Make each drawable unique in the overlay; can't add it more than once
+ mDrawables.add(drawable);
+ invalidate(drawable.getBounds());
+ }
+ }
+
+ @Override
+ public void remove(Drawable drawable) {
+ if (mDrawables != null) {
+ mDrawables.remove(drawable);
+ invalidate(drawable.getBounds());
+ }
+ }
+
+ @Override
+ public void add(View child) {
+ super.addView(child);
+ }
+
+ @Override
+ public void remove(View view) {
+ super.removeView(view);
+ }
+
+ @Override
+ public void clear() {
+ removeAllViews();
+ mDrawables.clear();
+ }
+
+ boolean isEmpty() {
+ if (getChildCount() == 0 && (mDrawables == null || mDrawables.size() == 0)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ final int numDrawables = (mDrawables == null) ? 0 : mDrawables.size();
+ for (int i = 0; i < numDrawables; ++i) {
+ mDrawables.get(i).draw(canvas);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // Noop: children are positioned absolutely
+ }
+
+ /*
+ The following invalidation overrides exist for the purpose of redirecting invalidation to
+ the host view. The overlay is not parented to the host view (since a View cannot be a parent),
+ so the invalidation cannot proceed through the normal parent hierarchy.
+ There is a built-in assumption that the overlay exactly covers the host view, therefore
+ the invalidation rectangles received do not need to be adjusted when forwarded to
+ the host view.
+ */
+
+ @Override
+ public void invalidate(Rect dirty) {
+ super.invalidate(dirty);
+ if (mHostView != null) {
+ dirty.offset(getLeft(), getTop());
+ mHostView.invalidate(dirty);
+ }
+ }
+
+ @Override
+ public void invalidate(int l, int t, int r, int b) {
+ super.invalidate(l, t, r, b);
+ if (mHostView != null) {
+ mHostView.invalidate(l, t, r, b);
+ }
+ }
+
+ @Override
+ public void invalidate() {
+ super.invalidate();
+ if (mHostView != null) {
+ mHostView.invalidate();
+ }
+ }
+
+ @Override
+ void invalidate(boolean invalidateCache) {
+ super.invalidate(invalidateCache);
+ if (mHostView != null) {
+ mHostView.invalidate(invalidateCache);
+ }
+ }
+
+ @Override
+ void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
+ super.invalidateViewProperty(invalidateParent, forceRedraw);
+ if (mHostView != null) {
+ mHostView.invalidateViewProperty(invalidateParent, forceRedraw);
+ }
+ }
+
+ @Override
+ protected void invalidateParentCaches() {
+ super.invalidateParentCaches();
+ if (mHostView != null) {
+ mHostView.invalidateParentCaches();
+ }
+ }
+
+ @Override
+ protected void invalidateParentIfNeeded() {
+ super.invalidateParentIfNeeded();
+ if (mHostView != null) {
+ mHostView.invalidateParentIfNeeded();
+ }
+ }
+
+ public void invalidateChildFast(View child, final Rect dirty) {
+ if (mHostView != null) {
+ // Note: This is not a "fast" invalidation. Would be nice to instead invalidate using DL
+ // properties and a dirty rect instead of causing a real invalidation of the host view
+ int left = child.mLeft;
+ int top = child.mTop;
+ if (!child.getMatrix().isIdentity()) {
+ child.transformRect(dirty);
+ }
+ dirty.offset(left, top);
+ mHostView.invalidate(dirty);
+ }
+ }
+
+ @Override
+ public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
+ if (mHostView != null) {
+ mHostView.invalidate(dirty);
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 396fd68..d659110 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -6167,6 +6167,7 @@
private ArrayList<View> mSkippedScrap;
private SparseArray<View> mTransientStateViews;
+ private LongSparseArray<View> mTransientStateViewsById;
public void setViewTypeCount(int viewTypeCount) {
if (viewTypeCount < 1) {
@@ -6205,6 +6206,12 @@
mTransientStateViews.valueAt(i).forceLayout();
}
}
+ if (mTransientStateViewsById != null) {
+ final int count = mTransientStateViewsById.size();
+ for (int i = 0; i < count; i++) {
+ mTransientStateViewsById.valueAt(i).forceLayout();
+ }
+ }
}
public boolean shouldRecycleViewType(int viewType) {
@@ -6234,6 +6241,9 @@
if (mTransientStateViews != null) {
mTransientStateViews.clear();
}
+ if (mTransientStateViewsById != null) {
+ mTransientStateViewsById.clear();
+ }
}
/**
@@ -6281,16 +6291,21 @@
}
View getTransientStateView(int position) {
- if (mTransientStateViews == null) {
- return null;
+ if (mAdapter != null && mAdapterHasStableIds && mTransientStateViewsById != null) {
+ long id = mAdapter.getItemId(position);
+ View result = mTransientStateViewsById.get(id);
+ mTransientStateViewsById.remove(id);
+ return result;
}
- final int index = mTransientStateViews.indexOfKey(position);
- if (index < 0) {
- return null;
+ if (mTransientStateViews != null) {
+ final int index = mTransientStateViews.indexOfKey(position);
+ if (index >= 0) {
+ View result = mTransientStateViews.valueAt(index);
+ mTransientStateViews.removeAt(index);
+ return result;
+ }
}
- final View result = mTransientStateViews.valueAt(index);
- mTransientStateViews.removeAt(index);
- return result;
+ return null;
}
/**
@@ -6300,6 +6315,9 @@
if (mTransientStateViews != null) {
mTransientStateViews.clear();
}
+ if (mTransientStateViewsById != null) {
+ mTransientStateViewsById.clear();
+ }
}
/**
@@ -6342,11 +6360,18 @@
mSkippedScrap.add(scrap);
}
if (scrapHasTransientState) {
- if (mTransientStateViews == null) {
- mTransientStateViews = new SparseArray<View>();
- }
scrap.dispatchStartTemporaryDetach();
- mTransientStateViews.put(position, scrap);
+ if (mAdapter != null && mAdapterHasStableIds) {
+ if (mTransientStateViewsById == null) {
+ mTransientStateViewsById = new LongSparseArray<View>();
+ }
+ mTransientStateViewsById.put(lp.itemId, scrap);
+ } else {
+ if (mTransientStateViews == null) {
+ mTransientStateViews = new SparseArray<View>();
+ }
+ mTransientStateViews.put(position, scrap);
+ }
}
return;
}
@@ -6405,10 +6430,18 @@
removeDetachedView(victim, false);
}
if (scrapHasTransientState) {
- if (mTransientStateViews == null) {
- mTransientStateViews = new SparseArray<View>();
+ if (mAdapter != null && mAdapterHasStableIds) {
+ if (mTransientStateViewsById == null) {
+ mTransientStateViewsById = new LongSparseArray<View>();
+ }
+ long id = mAdapter.getItemId(mFirstActivePosition + i);
+ mTransientStateViewsById.put(id, victim);
+ } else {
+ if (mTransientStateViews == null) {
+ mTransientStateViews = new SparseArray<View>();
+ }
+ mTransientStateViews.put(mFirstActivePosition + i, victim);
}
- mTransientStateViews.put(mFirstActivePosition + i, victim);
}
continue;
}
@@ -6457,6 +6490,15 @@
}
}
}
+ if (mTransientStateViewsById != null) {
+ for (int i = 0; i < mTransientStateViewsById.size(); i++) {
+ final View v = mTransientStateViewsById.valueAt(i);
+ if (!v.hasTransientState()) {
+ mTransientStateViewsById.removeAt(i);
+ i--;
+ }
+ }
+ }
}
/**
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 69e3177..7c40a64 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1550,6 +1550,32 @@
setSelectedPositionInt(mNextSelectedPosition);
+ // Remember which child, if any, had accessibility focus. This must
+ // occur before recycling any views, since that will clear
+ // accessibility focus.
+ final ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ final View accessFocusedView = viewRootImpl.getAccessibilityFocusedHost();
+ if (accessFocusedView != null) {
+ final View accessFocusedChild = findAccessibilityFocusedChild(
+ accessFocusedView);
+ if (accessFocusedChild != null) {
+ if (!dataChanged || isDirectChildHeaderOrFooter(accessFocusedChild)) {
+ // If the views won't be changing, try to maintain
+ // focus on the current view host and (if
+ // applicable) its virtual view.
+ accessibilityFocusLayoutRestoreView = accessFocusedView;
+ accessibilityFocusLayoutRestoreNode = viewRootImpl
+ .getAccessibilityFocusedVirtualView();
+ } else {
+ // Otherwise, try to maintain focus at the same
+ // position.
+ accessibilityFocusPosition = getPositionForView(accessFocusedChild);
+ }
+ }
+ }
+ }
+
// Pull all children into the RecycleBin.
// These views will be reused if possible
final int firstPosition = mFirstPosition;
@@ -1590,30 +1616,6 @@
requestFocus();
}
- // Remember which child, if any, had accessibility focus.
- final ViewRootImpl viewRootImpl = getViewRootImpl();
- if (viewRootImpl != null) {
- final View accessFocusedView = viewRootImpl.getAccessibilityFocusedHost();
- if (accessFocusedView != null) {
- final View accessFocusedChild = findAccessibilityFocusedChild(
- accessFocusedView);
- if (accessFocusedChild != null) {
- if (!dataChanged || isDirectChildHeaderOrFooter(accessFocusedChild)) {
- // If the views won't be changing, try to maintain
- // focus on the current view host and (if
- // applicable) its virtual view.
- accessibilityFocusLayoutRestoreView = accessFocusedView;
- accessibilityFocusLayoutRestoreNode = viewRootImpl
- .getAccessibilityFocusedVirtualView();
- } else {
- // Otherwise, try to maintain focus at the same
- // position.
- accessibilityFocusPosition = getPositionForView(accessFocusedChild);
- }
- }
- }
- }
-
// Clear out old views
detachAllViewsFromParent();
recycleBin.removeSkippedScrap();
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index aef7bdc..102815f 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -885,7 +885,7 @@
<string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Umožňuje aplikaci číst historii všech adres URL navštívených v Prohlížeči a všechny záložky v Prohlížeči. Poznámka: Pro prohlížeče třetí strany a jiné aplikace umožňující procházení webu toto oprávnění platit nemusí."</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"psaní webových záložek a historie"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Umožňuje aplikaci upravit historii prohlížeče nebo záložky uložené v tabletu. Aplikace s tímto oprávněním může vymazat či pozměnit data prohlížeče. Poznámka: Pro prohlížeče třetí strany a jiné aplikace umožňující procházení webu toto oprávnění platit nemusí."</string>
- <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Umožňuje aplikaci upravit historii prohlížeče nebo záložky uložené v telefonu. Aplikace s tímto oprávněním může vymazat či pozměnit data prohlížeče .Poznámka: Pro prohlížeče třetí strany a jiné aplikace umožňující procházení webu toto oprávnění platit nemusí."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Umožňuje aplikaci upravit historii prohlížeče nebo záložky uložené v telefonu. Aplikace s tímto oprávněním může vymazat či pozměnit data prohlížeče. Poznámka: Pro prohlížeče třetí strany a jiné aplikace umožňující procházení webu toto oprávnění platit nemusí."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"nastavení budíku"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Umožňuje aplikaci nastavit budík v nainstalované aplikaci budík. Některé aplikace budík tuto funkci nemusí obsahovat."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"přidat hlasovou zprávu"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 1fc59b6..eeb6878 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -329,7 +329,7 @@
<string name="permdesc_confirm_full_backup" msgid="1748762171637699562">"Permite que la aplicación inicie la IU de confirmación de copia de seguridad completa. No todas las aplicaciones pueden utilizar este permiso."</string>
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"mostrar ventanas no autorizadas"</string>
<string name="permdesc_internalSystemWindow" msgid="7458387759461466397">"Permite que la aplicación cree ventanas para la interfaz de usuario interna del sistema. Las aplicaciones normales no deben usar este permiso."</string>
- <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"destacar sobre otras aplicaciones"</string>
+ <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"mostrar sobre otras aplicaciones"</string>
<string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Permite a la aplicación escribir por encima de otras aplicaciones o partes de la interfaz de usuario que pueden interferir en tu uso de la interfaz en cualquier aplicación o cambiar lo que crees que ves en otras aplicaciones."</string>
<string name="permlab_setAnimationScale" msgid="2805103241153907174">"modificar la velocidad de la animación global"</string>
<string name="permdesc_setAnimationScale" msgid="7690063428924343571">"Permite que la aplicación cambie la velocidad de animación global (animaciones más rápidas o más lentas) en cualquier momento."</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 98d7477..8aedc15 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1188,7 +1188,7 @@
<string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"Po włączeniu nośnika USB niektóre używane aplikacje zostaną zatrzymane i mogą być niedostępne do chwili jego wyłączenia."</string>
<string name="dlg_error_title" msgid="7323658469626514207">"Operacja USB zakończona niepowodzeniem"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_mtp_notification_title" msgid="3699913097391550394">"Podłączono jako urządzenie multimedialne."</string>
+ <string name="usb_mtp_notification_title" msgid="3699913097391550394">"Podłączono urządzenie multimedialne"</string>
<string name="usb_ptp_notification_title" msgid="1960817192216064833">"Podłączono jako aparat."</string>
<string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"Podłączono jako nośnik instalacyjny."</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"Podłączono akcesorium USB"</string>
@@ -1320,7 +1320,7 @@
<string name="sync_really_delete" msgid="2572600103122596243">"Usuń elementy."</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"Cofnij usunięcia"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"Nie wykonuj teraz żadnych czynności."</string>
- <string name="choose_account_label" msgid="5655203089746423927">"Wybierz konto."</string>
+ <string name="choose_account_label" msgid="5655203089746423927">"Wybierz konto"</string>
<string name="add_account_label" msgid="2935267344849993553">"Dodaj konto"</string>
<string name="add_account_button_label" msgid="3611982894853435874">"Dodaj konto"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"Zwiększ"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 88c56bc..4fb58b8 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -40,7 +40,7 @@
<string name="serviceEnabledFor" msgid="6856228140453471041">"Serviciul a fost activat pentru:"</string>
<string name="serviceDisabled" msgid="1937553226592516411">"Serviciul a fost dezactivat."</string>
<string name="serviceRegistered" msgid="6275019082598102493">"Înregistrarea a reuşit."</string>
- <string name="serviceErased" msgid="1288584695297200972">"Ştergerea a reuşit."</string>
+ <string name="serviceErased" msgid="1288584695297200972">"Ștergerea a reuşit."</string>
<string name="passwordIncorrect" msgid="7612208839450128715">"Parolă incorectă."</string>
<string name="mmiComplete" msgid="8232527495411698359">"MMI finalizat."</string>
<string name="badPin" msgid="9015277645546710014">"Codul PIN vechi introdus nu este corect."</string>
@@ -129,8 +129,8 @@
<string name="contentServiceSync" msgid="8353523060269335667">"Sincronizare"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronizare"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Prea multe ştergeri <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
- <string name="low_memory" product="tablet" msgid="6494019234102154896">"Stocarea pe tabletă este plină. Ştergeţi câteva fişiere pentru a elibera spaţiu."</string>
- <string name="low_memory" product="default" msgid="3475999286680000541">"Stocarea pe telefon este plină. Ştergeţi câteva fişiere pentru a elibera spaţiu."</string>
+ <string name="low_memory" product="tablet" msgid="6494019234102154896">"Stocarea pe tabletă este plină. Ștergeţi câteva fişiere pentru a elibera spaţiu."</string>
+ <string name="low_memory" product="default" msgid="3475999286680000541">"Stocarea pe telefon este plină. Ștergeţi câteva fişiere pentru a elibera spaţiu."</string>
<string name="me" msgid="6545696007631404292">"Eu"</string>
<string name="power_dialog" product="tablet" msgid="8545351420865202853">"Opţiuni tablet PC"</string>
<string name="power_dialog" product="default" msgid="1319919075463988638">"Opţiuni telefon"</string>
@@ -628,9 +628,9 @@
<string name="policydesc_resetPassword" msgid="605963962301904458">"Editaţi parola de deblocare a ecranului."</string>
<string name="policylab_forceLock" msgid="2274085384704248431">"Blocaţi ecranul"</string>
<string name="policydesc_forceLock" msgid="1141797588403827138">"Stabiliţi modul şi timpul în care se blochează ecranul."</string>
- <string name="policylab_wipeData" msgid="3910545446758639713">"Ştergere integrală date"</string>
- <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Ştergeţi datele de pe tabletă fără avertisment, efectuând resetarea configurării din fabrică."</string>
- <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Ştergeţi datele din telefon fără avertisment, efectuând resetarea configurării din fabrică."</string>
+ <string name="policylab_wipeData" msgid="3910545446758639713">"Ștergere integrală date"</string>
+ <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Ștergeţi datele de pe tabletă fără avertisment, efectuând resetarea configurării din fabrică."</string>
+ <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Ștergeţi datele din telefon fără avertisment, efectuând resetarea configurării din fabrică."</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Setaţi serverul proxy global pentru dispozitiv"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Setaţi serverul proxy global pentru dispozitiv care să fie utilizat cât timp politica este activă. Numai primul administrator al dispozitivului poate seta serverul proxy global activ."</string>
<string name="policylab_expirePassword" msgid="885279151847254056">"Expirare parolă blocare ecran"</string>
@@ -916,7 +916,7 @@
<string name="search_go" msgid="8298016669822141719">"Căutaţi"</string>
<string name="searchview_description_search" msgid="6749826639098512120">"Căutaţi"</string>
<string name="searchview_description_query" msgid="5911778593125355124">"Interogare de căutare"</string>
- <string name="searchview_description_clear" msgid="1330281990951833033">"Ştergeţi interogarea"</string>
+ <string name="searchview_description_clear" msgid="1330281990951833033">"Ștergeţi interogarea"</string>
<string name="searchview_description_submit" msgid="2688450133297983542">"Trimiteţi interogarea"</string>
<string name="searchview_description_voice" msgid="2453203695674994440">"Căutare vocală"</string>
<string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"Activaţi Exploraţi prin atingere?"</string>
@@ -1036,12 +1036,12 @@
<string name="copy" msgid="2681946229533511987">"Copiaţi"</string>
<string name="paste" msgid="5629880836805036433">"Inseraţi"</string>
<string name="replace" msgid="5781686059063148930">"Înlocuiţi..."</string>
- <string name="delete" msgid="6098684844021697789">"Ştergeţi"</string>
+ <string name="delete" msgid="6098684844021697789">"Ștergeţi"</string>
<string name="copyUrl" msgid="2538211579596067402">"Copiaţi adresa URL"</string>
<string name="selectTextMode" msgid="1018691815143165326">"Selectaţi text"</string>
<string name="textSelectionCABTitle" msgid="5236850394370820357">"Selectare text"</string>
<string name="addToDictionary" msgid="4352161534510057874">"Adăugaţi în dicţionar"</string>
- <string name="deleteText" msgid="6979668428458199034">"Ştergeţi"</string>
+ <string name="deleteText" msgid="6979668428458199034">"Ștergeţi"</string>
<string name="inputMethod" msgid="1653630062304567879">"Metodă de intrare"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Acţiuni pentru text"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spaţiul de stocare aproape ocupat"</string>
@@ -1056,7 +1056,7 @@
<string name="capital_off" msgid="6815870386972805832">"DEZACTIVAT"</string>
<string name="whichApplication" msgid="4533185947064773386">"Finalizare acţiune utilizând"</string>
<string name="alwaysUse" msgid="4583018368000610438">"Se utilizează în mod prestabilit pentru această acţiune."</string>
- <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Ştergeţi setările prestabilite din Setări de sistem > Aplicaţii > Descărcate."</string>
+ <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Ștergeţi setările prestabilite din Setări de sistem > Aplicaţii > Descărcate."</string>
<string name="chooseActivity" msgid="7486876147751803333">"Alegeţi o acţiune"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"Alegeţi o aplicaţie pentru dispozitivul USB"</string>
<string name="noApplications" msgid="2991814273936504689">"Această acţiune nu poate fi efectuată de nicio aplicaţie."</string>
@@ -1201,7 +1201,7 @@
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depanarea USB este conectată"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Atingeţi pentru a dezactiva depanarea USB."</string>
<string name="select_input_method" msgid="4653387336791222978">"Alegeți metoda de introducere de text"</string>
- <string name="configure_input_methods" msgid="9091652157722495116">"Configurați metode introducere"</string>
+ <string name="configure_input_methods" msgid="9091652157722495116">"Setați metode introducere text"</string>
<string name="use_physical_keyboard" msgid="6203112478095117625">"Tastatură fizică"</string>
<string name="hardware" msgid="7517821086888990278">"Hardware"</string>
<string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Selectaţi aspectul tastaturii"</string>
@@ -1317,7 +1317,7 @@
<string name="gpsVerifNo" msgid="1146564937346454865">"Nu"</string>
<string name="sync_too_many_deletes" msgid="5296321850662746890">"Limita pentru ştergere a fost depăşită"</string>
<string name="sync_too_many_deletes_desc" msgid="496551671008694245">"Există <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> (de) elemente şterse pentru <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, contul <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. Ce doriţi să faceţi?"</string>
- <string name="sync_really_delete" msgid="2572600103122596243">"Ştergeţi elementele"</string>
+ <string name="sync_really_delete" msgid="2572600103122596243">"Ștergeţi elementele"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"Anulaţi aceste ştergeri"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"Nu trebuie să luaţi nicio măsură deocamdată"</string>
<string name="choose_account_label" msgid="5655203089746423927">"Alegeţi un cont"</string>
@@ -1341,7 +1341,7 @@
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduceţi valoarea pentru an"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Anulaţi"</string>
- <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ştergeţi"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ștergeţi"</string>
<string name="keyboardview_keycode_done" msgid="1992571118466679775">"Terminat"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Schimbarea modului"</string>
<string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1618110..06e658d 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -34,7 +34,6 @@
ProgramCache.cpp \
RenderBufferCache.cpp \
ResourceCache.cpp \
- ShapeCache.cpp \
SkiaColorFilter.cpp \
SkiaShader.cpp \
Snapshot.cpp \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 4642a4f..dc3a4e2 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -224,16 +224,6 @@
gradientCache.getSize(), gradientCache.getMaxSize());
log.appendFormat(" PathCache %8d / %8d\n",
pathCache.getSize(), pathCache.getMaxSize());
- log.appendFormat(" CircleShapeCache %8d / %8d\n",
- circleShapeCache.getSize(), circleShapeCache.getMaxSize());
- log.appendFormat(" OvalShapeCache %8d / %8d\n",
- ovalShapeCache.getSize(), ovalShapeCache.getMaxSize());
- log.appendFormat(" RoundRectShapeCache %8d / %8d\n",
- roundRectShapeCache.getSize(), roundRectShapeCache.getMaxSize());
- log.appendFormat(" RectShapeCache %8d / %8d\n",
- rectShapeCache.getSize(), rectShapeCache.getMaxSize());
- log.appendFormat(" ArcShapeCache %8d / %8d\n",
- arcShapeCache.getSize(), arcShapeCache.getMaxSize());
log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(),
dropShadowCache.getMaxSize());
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
@@ -253,11 +243,6 @@
total += gradientCache.getSize();
total += pathCache.getSize();
total += dropShadowCache.getSize();
- total += roundRectShapeCache.getSize();
- total += circleShapeCache.getSize();
- total += ovalShapeCache.getSize();
- total += rectShapeCache.getSize();
- total += arcShapeCache.getSize();
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
total += fontRenderer->getFontRendererSize(i);
}
@@ -325,11 +310,6 @@
fontRenderer->flush();
textureCache.flush();
pathCache.clear();
- roundRectShapeCache.clear();
- circleShapeCache.clear();
- ovalShapeCache.clear();
- rectShapeCache.clear();
- arcShapeCache.clear();
// fall through
case kFlushMode_Layers:
layerCache.clear();
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index dc32a7e..63836c1 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -36,7 +36,6 @@
#include "GradientCache.h"
#include "PatchCache.h"
#include "ProgramCache.h"
-#include "ShapeCache.h"
#include "PathCache.h"
#include "TextDropShadowCache.h"
#include "FboCache.h"
@@ -269,11 +268,6 @@
GradientCache gradientCache;
ProgramCache programCache;
PathCache pathCache;
- RoundRectShapeCache roundRectShapeCache;
- CircleShapeCache circleShapeCache;
- OvalShapeCache ovalShapeCache;
- RectShapeCache rectShapeCache;
- ArcShapeCache arcShapeCache;
PatchCache patchCache;
TextDropShadowCache dropShadowCache;
FboCache fboCache;
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 773fe82..46beb94 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -64,7 +64,7 @@
#define DEBUG_PATCHES_EMPTY_VERTICES 0
// Turn on to display debug info about shapes
-#define DEBUG_SHAPES 0
+#define DEBUG_PATHS 0
// Turn on to display debug info about textures
#define DEBUG_TEXTURES 0
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 8455545..5c5bf45 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "OpenGLRenderer"
#define ATRACE_TAG ATRACE_TAG_VIEW
+#include <SkCanvas.h>
+
#include <utils/Trace.h>
#include "Debug.h"
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 4743f58..4944fe8 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <SkCanvas.h>
+
#include "Debug.h"
#include "DisplayList.h"
#include "DisplayListOp.h"
@@ -121,9 +123,7 @@
}
for (size_t i = 0; i < mPaths.size(); i++) {
- SkPath* path = mPaths.itemAt(i);
- caches.pathCache.remove(path);
- delete path;
+ delete mPaths.itemAt(i);
}
for (size_t i = 0; i < mMatrices.size(); i++) {
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 11a655e..8b5b54c 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "OpenGLRenderer"
#include <SkCamera.h>
+#include <SkCanvas.h>
#include <private/hwui/DrawGlInfo.h>
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 66681e0..e31f6f6 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2257,9 +2257,11 @@
// TODO: try clipping large paths to viewport
PathTessellator::tessellatePath(path, paint, mSnapshot->transform, vertexBuffer);
- SkRect bounds = path.getBounds();
- PathTessellator::expandBoundsForStroke(bounds, paint, false);
- dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
+ if (hasLayer()) {
+ SkRect bounds = path.getBounds();
+ PathTessellator::expandBoundsForStroke(bounds, paint, false);
+ dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
+ }
return drawVertexBuffer(vertexBuffer, paint);
}
@@ -2389,7 +2391,7 @@
if (p->getPathEffect() != 0) {
mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect(
+ const PathTexture* texture = mCaches.pathCache.getRoundRect(
right - left, bottom - top, rx, ry, p);
return drawShape(left, top, texture, p);
}
@@ -2413,7 +2415,7 @@
}
if (p->getPathEffect() != 0) {
mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, p);
+ const PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
return drawShape(x - radius, y - radius, texture, p);
}
@@ -2434,7 +2436,7 @@
if (p->getPathEffect() != 0) {
mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.ovalShapeCache.getOval(right - left, bottom - top, p);
+ const PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
return drawShape(left, top, texture, p);
}
@@ -2460,7 +2462,7 @@
// TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
mCaches.activeTexture(0);
- const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top,
+ const PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
startAngle, sweepAngle, useCenter, p);
return drawShape(left, top, texture, p);
}
@@ -2495,7 +2497,7 @@
p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
mCaches.activeTexture(0);
const PathTexture* texture =
- mCaches.rectShapeCache.getRect(right - left, bottom - top, p);
+ mCaches.pathCache.getRect(right - left, bottom - top, p);
return drawShape(left, top, texture, p);
}
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index fb687cd..490c22a0 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,19 +15,301 @@
*/
#define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
-#include <utils/Mutex.h>
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkRect.h>
-#include <sys/sysinfo.h>
+#include <utils/JenkinsHash.h>
+#include <utils/Trace.h>
#include "Caches.h"
#include "PathCache.h"
-#include "Properties.h"
+
+#include "thread/Signal.h"
+#include "thread/Task.h"
+#include "thread/TaskProcessor.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
+// Cache entries
+///////////////////////////////////////////////////////////////////////////////
+
+PathDescription::PathDescription():
+ type(kShapeNone),
+ join(SkPaint::kDefault_Join),
+ cap(SkPaint::kDefault_Cap),
+ style(SkPaint::kFill_Style),
+ miter(4.0f),
+ strokeWidth(1.0f),
+ pathEffect(NULL) {
+ memset(&shape, 0, sizeof(Shape));
+}
+
+PathDescription::PathDescription(ShapeType type, SkPaint* paint):
+ type(type),
+ join(paint->getStrokeJoin()),
+ cap(paint->getStrokeCap()),
+ style(paint->getStyle()),
+ miter(paint->getStrokeMiter()),
+ strokeWidth(paint->getStrokeWidth()),
+ pathEffect(paint->getPathEffect()) {
+ memset(&shape, 0, sizeof(Shape));
+}
+
+hash_t PathDescription::hash() const {
+ uint32_t hash = JenkinsHashMix(0, type);
+ hash = JenkinsHashMix(hash, join);
+ hash = JenkinsHashMix(hash, cap);
+ hash = JenkinsHashMix(hash, style);
+ hash = JenkinsHashMix(hash, android::hash_type(miter));
+ hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
+ hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
+ hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
+ return JenkinsHashWhiten(hash);
+}
+
+int PathDescription::compare(const PathDescription& rhs) const {
+ return memcmp(this, &rhs, sizeof(PathDescription));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Utilities
+///////////////////////////////////////////////////////////////////////////////
+
+bool PathCache::canDrawAsConvexPath(SkPath* path, SkPaint* paint) {
+ // NOTE: This should only be used after PathTessellator handles joins properly
+ return paint->getPathEffect() == NULL && path->getConvexity() == SkPath::kConvex_Convexity;
+}
+
+void PathCache::computePathBounds(const SkPath* path, const SkPaint* paint,
+ float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+ const SkRect& bounds = path->getBounds();
+ PathCache::computeBounds(bounds, paint, left, top, offset, width, height);
+}
+
+void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint,
+ float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+ const float pathWidth = fmax(bounds.width(), 1.0f);
+ const float pathHeight = fmax(bounds.height(), 1.0f);
+
+ left = bounds.fLeft;
+ top = bounds.fTop;
+
+ offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
+
+ width = uint32_t(pathWidth + offset * 2.0 + 0.5);
+ height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+}
+
+static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
+ bitmap.setConfig(SkBitmap::kA8_Config, width, height);
+ bitmap.allocPixels();
+ bitmap.eraseColor(0);
+}
+
+static void initPaint(SkPaint& paint) {
+ // Make sure the paint is opaque, color, alpha, filter, etc.
+ // will be applied later when compositing the alpha8 texture
+ paint.setColor(0xff000000);
+ paint.setAlpha(255);
+ paint.setColorFilter(NULL);
+ paint.setMaskFilter(NULL);
+ paint.setShader(NULL);
+ SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
+ SkSafeUnref(paint.setXfermode(mode));
+}
+
+static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
+ float left, float top, float offset, uint32_t width, uint32_t height) {
+ initBitmap(bitmap, width, height);
+
+ SkPaint pathPaint(*paint);
+ initPaint(pathPaint);
+
+ SkCanvas canvas(bitmap);
+ canvas.translate(-left + offset, -top + offset);
+ canvas.drawPath(*path, pathPaint);
+}
+
+static PathTexture* createTexture(float left, float top, float offset,
+ uint32_t width, uint32_t height, uint32_t id) {
+ PathTexture* texture = new PathTexture();
+ texture->left = left;
+ texture->top = top;
+ texture->offset = offset;
+ texture->width = width;
+ texture->height = height;
+ texture->generation = id;
+ return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache constructor/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+PathCache::PathCache():
+ mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity),
+ mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) {
+ INIT_LOGD(" Setting %s cache size to %sMB", name, property);
+ setMaxSize(MB(atof(property)));
+ } else {
+ INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE);
+ }
+ init();
+}
+
+PathCache::~PathCache() {
+ mCache.clear();
+}
+
+void PathCache::init() {
+ mCache.setOnEntryRemovedListener(this);
+
+ GLint maxTextureSize;
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+ mMaxTextureSize = maxTextureSize;
+
+ mDebugEnabled = readDebugLevel() & kDebugCaches;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t PathCache::getSize() {
+ return mSize;
+}
+
+uint32_t PathCache::getMaxSize() {
+ return mMaxSize;
+}
+
+void PathCache::setMaxSize(uint32_t maxSize) {
+ mMaxSize = maxSize;
+ while (mSize > mMaxSize) {
+ mCache.removeOldest();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void PathCache::operator()(PathDescription& entry, PathTexture*& texture) {
+ removeTexture(texture);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void PathCache::removeTexture(PathTexture* texture) {
+ if (texture) {
+ const uint32_t size = texture->width * texture->height;
+ mSize -= size;
+
+ PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d",
+ texture->id, size, mSize);
+ if (mDebugEnabled) {
+ ALOGD("Shape deleted, size = %d", size);
+ }
+
+ if (texture->id) {
+ glDeleteTextures(1, &texture->id);
+ }
+ delete texture;
+ }
+}
+
+void PathCache::purgeCache(uint32_t width, uint32_t height) {
+ const uint32_t size = width * height;
+ // Don't even try to cache a bitmap that's bigger than the cache
+ if (size < mMaxSize) {
+ while (mSize + size > mMaxSize) {
+ mCache.removeOldest();
+ }
+ }
+}
+
+void PathCache::trim() {
+ while (mSize > mMaxSize) {
+ mCache.removeOldest();
+ }
+}
+
+PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path,
+ const SkPaint* paint) {
+ ATRACE_CALL();
+
+ float left, top, offset;
+ uint32_t width, height;
+ computePathBounds(path, paint, left, top, offset, width, height);
+
+ if (!checkTextureSize(width, height)) return NULL;
+
+ purgeCache(width, height);
+
+ SkBitmap bitmap;
+ drawPath(path, paint, bitmap, left, top, offset, width, height);
+
+ PathTexture* texture = createTexture(left, top, offset, width, height,
+ path->getGenerationID());
+ addTexture(entry, &bitmap, texture);
+
+ return texture;
+}
+
+void PathCache::addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture) {
+ generateTexture(*bitmap, texture);
+
+ uint32_t size = texture->width * texture->height;
+ if (size < mMaxSize) {
+ mSize += size;
+ PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d",
+ texture->id, size, mSize);
+ if (mDebugEnabled) {
+ ALOGD("Shape created, size = %d", size);
+ }
+ mCache.put(entry, texture);
+ } else {
+ texture->cleanup = true;
+ }
+}
+
+void PathCache::clear() {
+ mCache.clear();
+}
+
+void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
+ SkAutoLockPixels alp(bitmap);
+ if (!bitmap.readyToDraw()) {
+ ALOGE("Cannot generate texture from bitmap");
+ return;
+ }
+
+ glGenTextures(1, &texture->id);
+
+ glBindTexture(GL_TEXTURE_2D, texture->id);
+ // Textures are Alpha8
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ texture->blend = true;
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
+
+ texture->setFilter(GL_LINEAR);
+ texture->setWrap(GL_CLAMP_TO_EDGE);
+}
+
+///////////////////////////////////////////////////////////////////////////////
// Path precaching
///////////////////////////////////////////////////////////////////////////////
@@ -52,7 +334,7 @@
if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
SkBitmap* bitmap = new SkBitmap();
- PathCache::drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height);
+ drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height);
t->setResult(bitmap);
} else {
texture->width = 0;
@@ -62,23 +344,17 @@
}
///////////////////////////////////////////////////////////////////////////////
-// Path cache
+// Paths
///////////////////////////////////////////////////////////////////////////////
-PathCache::PathCache(): ShapeCache<PathCacheEntry>("path",
- PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE) {
-}
-
-PathCache::~PathCache() {
-}
-
void PathCache::remove(SkPath* path) {
- Vector<PathCacheEntry> pathsToRemove;
- LruCache<PathCacheEntry, PathTexture*>::Iterator i(mCache);
+ Vector<PathDescription> pathsToRemove;
+ LruCache<PathDescription, PathTexture*>::Iterator i(mCache);
while (i.next()) {
- const PathCacheEntry& key = i.key();
- if (key.path == path || key.path == path->getSourcePath()) {
+ const PathDescription& key = i.key();
+ if (key.type == kShapePath &&
+ (key.shape.path.mPath == path || key.shape.path.mPath == path->getSourcePath())) {
pathsToRemove.push(key);
}
}
@@ -121,7 +397,9 @@
PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
path = getSourcePath(path);
- PathCacheEntry entry(path, paint);
+ PathDescription entry(kShapePath, paint);
+ entry.shape.path.mPath = path;
+
PathTexture* texture = mCache.get(entry);
if (!texture) {
@@ -159,7 +437,9 @@
path = getSourcePath(path);
- PathCacheEntry entry(path, paint);
+ PathDescription entry(kShapePath, paint);
+ entry.shape.path.mPath = path;
+
PathTexture* texture = mCache.get(entry);
bool generate = false;
@@ -193,5 +473,130 @@
}
}
+///////////////////////////////////////////////////////////////////////////////
+// Rounded rects
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getRoundRect(float width, float height,
+ float rx, float ry, SkPaint* paint) {
+ PathDescription entry(kShapeRoundRect, paint);
+ entry.shape.roundRect.mWidth = width;
+ entry.shape.roundRect.mHeight = height;
+ entry.shape.roundRect.mRx = rx;
+ entry.shape.roundRect.mRy = ry;
+
+ PathTexture* texture = get(entry);
+
+ if (!texture) {
+ SkPath path;
+ SkRect r;
+ r.set(0.0f, 0.0f, width, height);
+ path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
+
+ texture = addTexture(entry, &path, paint);
+ }
+
+ return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Circles
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getCircle(float radius, SkPaint* paint) {
+ PathDescription entry(kShapeCircle, paint);
+ entry.shape.circle.mRadius = radius;
+
+ PathTexture* texture = get(entry);
+
+ if (!texture) {
+ SkPath path;
+ path.addCircle(radius, radius, radius, SkPath::kCW_Direction);
+
+ texture = addTexture(entry, &path, paint);
+ }
+
+ return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Ovals
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getOval(float width, float height, SkPaint* paint) {
+ PathDescription entry(kShapeOval, paint);
+ entry.shape.oval.mWidth = width;
+ entry.shape.oval.mHeight = height;
+
+ PathTexture* texture = get(entry);
+
+ if (!texture) {
+ SkPath path;
+ SkRect r;
+ r.set(0.0f, 0.0f, width, height);
+ path.addOval(r, SkPath::kCW_Direction);
+
+ texture = addTexture(entry, &path, paint);
+ }
+
+ return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Rects
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getRect(float width, float height, SkPaint* paint) {
+ PathDescription entry(kShapeRect, paint);
+ entry.shape.rect.mWidth = width;
+ entry.shape.rect.mHeight = height;
+
+ PathTexture* texture = get(entry);
+
+ if (!texture) {
+ SkPath path;
+ SkRect r;
+ r.set(0.0f, 0.0f, width, height);
+ path.addRect(r, SkPath::kCW_Direction);
+
+ texture = addTexture(entry, &path, paint);
+ }
+
+ return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Arcs
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getArc(float width, float height,
+ float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
+ PathDescription entry(kShapeArc, paint);
+ entry.shape.arc.mWidth = width;
+ entry.shape.arc.mHeight = height;
+ entry.shape.arc.mStartAngle = startAngle;
+ entry.shape.arc.mSweepAngle = sweepAngle;
+ entry.shape.arc.mUseCenter = useCenter;
+
+ PathTexture* texture = get(entry);
+
+ if (!texture) {
+ SkPath path;
+ SkRect r;
+ r.set(0.0f, 0.0f, width, height);
+ if (useCenter) {
+ path.moveTo(r.centerX(), r.centerY());
+ }
+ path.arcTo(r, startAngle, sweepAngle, !useCenter);
+ if (useCenter) {
+ path.close();
+ }
+
+ texture = addTexture(entry, &path, paint);
+ }
+
+ return texture;
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 27031a5..e6d92df 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,17 +17,21 @@
#ifndef ANDROID_HWUI_PATH_CACHE_H
#define ANDROID_HWUI_PATH_CACHE_H
-#include <utils/Thread.h>
+#include <GLES2/gl2.h>
+
+#include <utils/LruCache.h>
+#include <utils/Mutex.h>
#include <utils/Vector.h>
#include "Debug.h"
-#include "ShapeCache.h"
-#include "thread/Signal.h"
-#include "thread/Task.h"
-#include "thread/TaskProcessor.h"
+#include "Properties.h"
+#include "Texture.h"
+class SkBitmap;
+class SkCanvas;
class SkPaint;
class SkPath;
+class SkRect;
namespace android {
namespace uirenderer {
@@ -35,56 +39,181 @@
class Caches;
///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#if DEBUG_PATHS
+ #define PATH_LOGD(...) ALOGD(__VA_ARGS__)
+#else
+ #define PATH_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
// Classes
///////////////////////////////////////////////////////////////////////////////
-struct PathCacheEntry: public ShapeCacheEntry {
- PathCacheEntry(SkPath* path, SkPaint* paint):
- ShapeCacheEntry(ShapeCacheEntry::kShapePath, paint) {
- this->path = path;
+/**
+ * Alpha texture used to represent a path.
+ */
+struct PathTexture: public Texture {
+ PathTexture(): Texture() {
}
- PathCacheEntry(): ShapeCacheEntry() {
- path = NULL;
+ ~PathTexture() {
+ clearTask();
}
- hash_t hash() const {
- uint32_t hash = ShapeCacheEntry::hash();
- hash = JenkinsHashMix(hash, android::hash_type(path));
- return JenkinsHashWhiten(hash);
+ /**
+ * Left coordinate of the path bounds.
+ */
+ float left;
+ /**
+ * Top coordinate of the path bounds.
+ */
+ float top;
+ /**
+ * Offset to draw the path at the correct origin.
+ */
+ float offset;
+
+ sp<Task<SkBitmap*> > task() const {
+ return mTask;
}
- int compare(const ShapeCacheEntry& r) const {
- int deltaInt = ShapeCacheEntry::compare(r);
- if (deltaInt != 0) return deltaInt;
-
- const PathCacheEntry& rhs = (const PathCacheEntry&) r;
- return path - rhs.path;
+ void setTask(const sp<Task<SkBitmap*> >& task) {
+ mTask = task;
}
- SkPath* path;
+ void clearTask() {
+ if (mTask != NULL) {
+ mTask.clear();
+ }
+ }
-}; // PathCacheEntry
+private:
+ sp<Task<SkBitmap*> > mTask;
+}; // struct PathTexture
-inline hash_t hash_type(const PathCacheEntry& entry) {
- return entry.hash();
-}
+enum ShapeType {
+ kShapeNone,
+ kShapeRect,
+ kShapeRoundRect,
+ kShapeCircle,
+ kShapeOval,
+ kShapeArc,
+ kShapePath
+};
+
+struct PathDescription {
+ ShapeType type;
+ SkPaint::Join join;
+ SkPaint::Cap cap;
+ SkPaint::Style style;
+ float miter;
+ float strokeWidth;
+ SkPathEffect* pathEffect;
+ union Shape {
+ struct Path {
+ SkPath* mPath;
+ } path;
+ struct RoundRect {
+ float mWidth;
+ float mHeight;
+ float mRx;
+ float mRy;
+ } roundRect;
+ struct Circle {
+ float mRadius;
+ } circle;
+ struct Oval {
+ float mWidth;
+ float mHeight;
+ } oval;
+ struct Rect {
+ float mWidth;
+ float mHeight;
+ } rect;
+ struct Arc {
+ float mWidth;
+ float mHeight;
+ float mStartAngle;
+ float mSweepAngle;
+ bool mUseCenter;
+ } arc;
+ } shape;
+
+ PathDescription();
+ PathDescription(ShapeType shapeType, SkPaint* paint);
+
+ hash_t hash() const;
+
+ int compare(const PathDescription& rhs) const;
+
+ bool operator==(const PathDescription& other) const {
+ return compare(other) == 0;
+ }
+
+ bool operator!=(const PathDescription& other) const {
+ return compare(other) != 0;
+ }
+
+ friend inline int strictly_order_type(
+ const PathDescription& lhs, const PathDescription& rhs) {
+ return lhs.compare(rhs) < 0;
+ }
+
+ friend inline int compare_type(const PathDescription& lhs, const PathDescription& rhs) {
+ return lhs.compare(rhs);
+ }
+
+ friend inline hash_t hash_type(const PathDescription& entry) {
+ return entry.hash();
+ }
+};
/**
- * A simple LRU path cache. The cache has a maximum size expressed in bytes.
+ * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
* Any texture added to the cache causing the cache to grow beyond the maximum
* allowed size will also cause the oldest texture to be kicked out.
*/
-class PathCache: public ShapeCache<PathCacheEntry> {
+class PathCache: public OnEntryRemoved<PathDescription, PathTexture*> {
public:
PathCache();
~PathCache();
/**
- * Returns the texture associated with the specified path. If the texture
- * cannot be found in the cache, a new texture is generated.
+ * Used as a callback when an entry is removed from the cache.
+ * Do not invoke directly.
*/
+ void operator()(PathDescription& path, PathTexture*& texture);
+
+ /**
+ * Clears the cache. This causes all textures to be deleted.
+ */
+ void clear();
+
+ /**
+ * Sets the maximum size of the cache in bytes.
+ */
+ void setMaxSize(uint32_t maxSize);
+ /**
+ * Returns the maximum size of the cache in bytes.
+ */
+ uint32_t getMaxSize();
+ /**
+ * Returns the current size of the cache in bytes.
+ */
+ uint32_t getSize();
+
+ PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
+ PathTexture* getCircle(float radius, SkPaint* paint);
+ PathTexture* getOval(float width, float height, SkPaint* paint);
+ PathTexture* getRect(float width, float height, SkPaint* paint);
+ PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
+ bool useCenter, SkPaint* paint);
PathTexture* get(SkPath* path, SkPaint* paint);
+
/**
* Removes an entry.
*/
@@ -98,10 +227,63 @@
* Process deferred removals.
*/
void clearGarbage();
+ /**
+ * Trims the contents of the cache, removing items until it's under its
+ * specified limit.
+ *
+ * Trimming is used for caches that support pre-caching from a worker
+ * thread. During pre-caching the maximum limit of the cache can be
+ * exceeded for the duration of the frame. It is therefore required to
+ * trim the cache at the end of the frame to keep the total amount of
+ * memory used under control.
+ */
+ void trim();
+ /**
+ * Precaches the specified path using background threads.
+ */
void precache(SkPath* path, SkPaint* paint);
+ static bool canDrawAsConvexPath(SkPath* path, SkPaint* paint);
+ static void computePathBounds(const SkPath* path, const SkPaint* paint,
+ float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
+ static void computeBounds(const SkRect& bounds, const SkPaint* paint,
+ float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
+
private:
+ PathTexture* addTexture(const PathDescription& entry,
+ const SkPath *path, const SkPaint* paint);
+ PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap);
+ void addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture);
+
+ PathTexture* get(const PathDescription& entry) {
+ return mCache.get(entry);
+ }
+
+ /**
+ * Ensures there is enough space in the cache for a texture of the specified
+ * dimensions.
+ */
+ void purgeCache(uint32_t width, uint32_t height);
+
+ void removeTexture(PathTexture* texture);
+
+ bool checkTextureSize(uint32_t width, uint32_t height) {
+ if (width > mMaxTextureSize || height > mMaxTextureSize) {
+ ALOGW("Shape too large to be rendered into a texture (%dx%d, max=%dx%d)",
+ width, height, mMaxTextureSize, mMaxTextureSize);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Generates the texture from a bitmap into the specified texture structure.
+ */
+ void generateTexture(SkBitmap& bitmap, Texture* texture);
+
+ void init();
+
class PathTask: public Task<SkBitmap*> {
public:
PathTask(SkPath* path, SkPaint* paint, PathTexture* texture):
@@ -128,6 +310,13 @@
uint32_t mMaxTextureSize;
};
+ LruCache<PathDescription, PathTexture*> mCache;
+ uint32_t mSize;
+ uint32_t mMaxSize;
+ GLuint mMaxTextureSize;
+
+ bool mDebugEnabled;
+
sp<PathProcessor> mProcessor;
Vector<SkPath*> mGarbage;
mutable Mutex mLock;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index b8559bc8..5f39abf 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -115,7 +115,6 @@
#define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size"
#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
-#define PROPERTY_SHAPE_CACHE_SIZE "ro.hwui.shape_cache_size"
#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
@@ -159,8 +158,7 @@
#define DEFAULT_TEXTURE_CACHE_SIZE 24.0f
#define DEFAULT_LAYER_CACHE_SIZE 16.0f
#define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
-#define DEFAULT_PATH_CACHE_SIZE 4.0f
-#define DEFAULT_SHAPE_CACHE_SIZE 1.0f
+#define DEFAULT_PATH_CACHE_SIZE 10.0f
#define DEFAULT_PATCH_CACHE_SIZE 512
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
diff --git a/libs/hwui/ShapeCache.cpp b/libs/hwui/ShapeCache.cpp
deleted file mode 100644
index 5a23235..0000000
--- a/libs/hwui/ShapeCache.cpp
+++ /dev/null
@@ -1,168 +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.
- */
-
-#define LOG_TAG "OpenGLRenderer"
-
-#include "ShapeCache.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Rounded rects
-///////////////////////////////////////////////////////////////////////////////
-
-RoundRectShapeCache::RoundRectShapeCache(): ShapeCache<RoundRectShapeCacheEntry>(
- "round rect", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* RoundRectShapeCache::getRoundRect(float width, float height,
- float rx, float ry, SkPaint* paint) {
- RoundRectShapeCacheEntry entry(width, height, rx, ry, paint);
- PathTexture* texture = get(entry);
-
- if (!texture) {
- SkPath path;
- SkRect r;
- r.set(0.0f, 0.0f, width, height);
- path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
-
- texture = addTexture(entry, &path, paint);
- }
-
- return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Circles
-///////////////////////////////////////////////////////////////////////////////
-
-CircleShapeCache::CircleShapeCache(): ShapeCache<CircleShapeCacheEntry>(
- "circle", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* CircleShapeCache::getCircle(float radius, SkPaint* paint) {
- CircleShapeCacheEntry entry(radius, paint);
- PathTexture* texture = get(entry);
-
- if (!texture) {
- SkPath path;
- path.addCircle(radius, radius, radius, SkPath::kCW_Direction);
-
- texture = addTexture(entry, &path, paint);
- }
-
- return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Ovals
-///////////////////////////////////////////////////////////////////////////////
-
-OvalShapeCache::OvalShapeCache(): ShapeCache<OvalShapeCacheEntry>(
- "oval", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* OvalShapeCache::getOval(float width, float height, SkPaint* paint) {
- OvalShapeCacheEntry entry(width, height, paint);
- PathTexture* texture = get(entry);
-
- if (!texture) {
- SkPath path;
- SkRect r;
- r.set(0.0f, 0.0f, width, height);
- path.addOval(r, SkPath::kCW_Direction);
-
- texture = addTexture(entry, &path, paint);
- }
-
- return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Rects
-///////////////////////////////////////////////////////////////////////////////
-
-RectShapeCache::RectShapeCache(): ShapeCache<RectShapeCacheEntry>(
- "rect", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* RectShapeCache::getRect(float width, float height, SkPaint* paint) {
- RectShapeCacheEntry entry(width, height, paint);
- PathTexture* texture = get(entry);
-
- if (!texture) {
- SkRect bounds;
- bounds.set(0.0f, 0.0f, width, height);
-
- float left, top, offset;
- uint32_t rectWidth, rectHeight;
- computeBounds(bounds, paint, left, top, offset, rectWidth, rectHeight);
-
- if (!checkTextureSize(rectWidth, rectHeight)) return NULL;
-
- purgeCache(rectWidth, rectHeight);
-
- SkBitmap bitmap;
- initBitmap(bitmap, rectWidth, rectHeight);
-
- SkPaint pathPaint(*paint);
- initPaint(pathPaint);
-
- SkCanvas canvas(bitmap);
- canvas.translate(-left + offset, -top + offset);
- canvas.drawRect(bounds, pathPaint);
-
- texture = createTexture(0, 0, offset, rectWidth, rectHeight, 0);
- addTexture(entry, &bitmap, texture);
- }
-
- return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Arcs
-///////////////////////////////////////////////////////////////////////////////
-
-ArcShapeCache::ArcShapeCache(): ShapeCache<ArcShapeCacheEntry>(
- "arc", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* ArcShapeCache::getArc(float width, float height,
- float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
- ArcShapeCacheEntry entry(width, height, startAngle, sweepAngle, useCenter, paint);
- PathTexture* texture = get(entry);
-
- if (!texture) {
- SkPath path;
- SkRect r;
- r.set(0.0f, 0.0f, width, height);
- if (useCenter) {
- path.moveTo(r.centerX(), r.centerY());
- }
- path.arcTo(r, startAngle, sweepAngle, !useCenter);
- if (useCenter) {
- path.close();
- }
-
- texture = addTexture(entry, &path, paint);
- }
-
- return texture;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h
deleted file mode 100644
index 92314b0..0000000
--- a/libs/hwui/ShapeCache.h
+++ /dev/null
@@ -1,816 +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 ANDROID_HWUI_SHAPE_CACHE_H
-#define ANDROID_HWUI_SHAPE_CACHE_H
-
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
-#include <GLES2/gl2.h>
-
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkPaint.h>
-#include <SkPath.h>
-#include <SkRect.h>
-
-#include <utils/JenkinsHash.h>
-#include <utils/LruCache.h>
-#include <utils/Trace.h>
-
-#include "Debug.h"
-#include "Properties.h"
-#include "Texture.h"
-#include "thread/Task.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_SHAPES
- #define SHAPE_LOGD(...) ALOGD(__VA_ARGS__)
-#else
- #define SHAPE_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Classes
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Alpha texture used to represent a path.
- */
-struct PathTexture: public Texture {
- PathTexture(): Texture() {
- }
-
- ~PathTexture() {
- clearTask();
- }
-
- /**
- * Left coordinate of the path bounds.
- */
- float left;
- /**
- * Top coordinate of the path bounds.
- */
- float top;
- /**
- * Offset to draw the path at the correct origin.
- */
- float offset;
-
- sp<Task<SkBitmap*> > task() const {
- return mTask;
- }
-
- void setTask(const sp<Task<SkBitmap*> >& task) {
- mTask = task;
- }
-
- void clearTask() {
- if (mTask != NULL) {
- mTask.clear();
- }
- }
-
-private:
- sp<Task<SkBitmap*> > mTask;
-}; // struct PathTexture
-
-/**
- * Describe a shape in the shape cache.
- */
-struct ShapeCacheEntry {
- enum ShapeType {
- kShapeNone,
- kShapeRect,
- kShapeRoundRect,
- kShapeCircle,
- kShapeOval,
- kShapeArc,
- kShapePath
- };
-
- ShapeCacheEntry() {
- shapeType = kShapeNone;
- join = SkPaint::kDefault_Join;
- cap = SkPaint::kDefault_Cap;
- style = SkPaint::kFill_Style;
- miter = 4.0f;
- strokeWidth = 1.0f;
- pathEffect = NULL;
- }
-
- ShapeCacheEntry(ShapeType type, SkPaint* paint) {
- shapeType = type;
- join = paint->getStrokeJoin();
- cap = paint->getStrokeCap();
- miter = paint->getStrokeMiter();
- strokeWidth = paint->getStrokeWidth();
- style = paint->getStyle();
- pathEffect = paint->getPathEffect();
- }
-
- virtual ~ShapeCacheEntry() {
- }
-
- virtual hash_t hash() const {
- uint32_t hash = JenkinsHashMix(0, shapeType);
- hash = JenkinsHashMix(hash, join);
- hash = JenkinsHashMix(hash, cap);
- hash = JenkinsHashMix(hash, style);
- hash = JenkinsHashMix(hash, android::hash_type(miter));
- hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
- hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
- return JenkinsHashWhiten(hash);
- }
-
- virtual int compare(const ShapeCacheEntry& rhs) const {
- int deltaInt = shapeType - rhs.shapeType;
- if (deltaInt != 0) return deltaInt;
-
- deltaInt = join - rhs.join;
- if (deltaInt != 0) return deltaInt;
-
- deltaInt = cap - rhs.cap;
- if (deltaInt != 0) return deltaInt;
-
- deltaInt = style - rhs.style;
- if (deltaInt != 0) return deltaInt;
-
- if (miter < rhs.miter) return -1;
- if (miter > rhs.miter) return +1;
-
- if (strokeWidth < rhs.strokeWidth) return -1;
- if (strokeWidth > rhs.strokeWidth) return +1;
-
- if (pathEffect < rhs.pathEffect) return -1;
- if (pathEffect > rhs.pathEffect) return +1;
-
- return 0;
- }
-
- bool operator==(const ShapeCacheEntry& other) const {
- return compare(other) == 0;
- }
-
- bool operator!=(const ShapeCacheEntry& other) const {
- return compare(other) != 0;
- }
-
- ShapeType shapeType;
- SkPaint::Join join;
- SkPaint::Cap cap;
- SkPaint::Style style;
- float miter;
- float strokeWidth;
- SkPathEffect* pathEffect;
-}; // struct ShapeCacheEntry
-
-// Cache support
-
-inline int strictly_order_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
- return lhs.compare(rhs) < 0;
-}
-
-inline int compare_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
- return lhs.compare(rhs);
-}
-
-inline hash_t hash_type(const ShapeCacheEntry& entry) {
- return entry.hash();
-}
-
-struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
- RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
- ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
- mWidth = width;
- mHeight = height;
- mRx = rx;
- mRy = ry;
- }
-
- RoundRectShapeCacheEntry(): ShapeCacheEntry() {
- mWidth = 0;
- mHeight = 0;
- mRx = 0;
- mRy = 0;
- }
-
- hash_t hash() const {
- uint32_t hash = ShapeCacheEntry::hash();
- hash = JenkinsHashMix(hash, android::hash_type(mWidth));
- hash = JenkinsHashMix(hash, android::hash_type(mHeight));
- hash = JenkinsHashMix(hash, android::hash_type(mRx));
- hash = JenkinsHashMix(hash, android::hash_type(mRy));
- return JenkinsHashWhiten(hash);
- }
-
- int compare(const ShapeCacheEntry& r) const {
- int deltaInt = ShapeCacheEntry::compare(r);
- if (deltaInt != 0) return deltaInt;
-
- const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
-
- if (mWidth < rhs.mWidth) return -1;
- if (mWidth > rhs.mWidth) return +1;
-
- if (mHeight < rhs.mHeight) return -1;
- if (mHeight > rhs.mHeight) return +1;
-
- if (mRx < rhs.mRx) return -1;
- if (mRx > rhs.mRx) return +1;
-
- if (mRy < rhs.mRy) return -1;
- if (mRy > rhs.mRy) return +1;
-
- return 0;
- }
-
-private:
- float mWidth;
- float mHeight;
- float mRx;
- float mRy;
-}; // RoundRectShapeCacheEntry
-
-inline hash_t hash_type(const RoundRectShapeCacheEntry& entry) {
- return entry.hash();
-}
-
-struct CircleShapeCacheEntry: public ShapeCacheEntry {
- CircleShapeCacheEntry(float radius, SkPaint* paint):
- ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
- mRadius = radius;
- }
-
- CircleShapeCacheEntry(): ShapeCacheEntry() {
- mRadius = 0;
- }
-
- hash_t hash() const {
- uint32_t hash = ShapeCacheEntry::hash();
- hash = JenkinsHashMix(hash, android::hash_type(mRadius));
- return JenkinsHashWhiten(hash);
- }
-
- int compare(const ShapeCacheEntry& r) const {
- int deltaInt = ShapeCacheEntry::compare(r);
- if (deltaInt != 0) return deltaInt;
-
- const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
-
- if (mRadius < rhs.mRadius) return -1;
- if (mRadius > rhs.mRadius) return +1;
-
- return 0;
- }
-
-private:
- float mRadius;
-}; // CircleShapeCacheEntry
-
-inline hash_t hash_type(const CircleShapeCacheEntry& entry) {
- return entry.hash();
-}
-
-struct OvalShapeCacheEntry: public ShapeCacheEntry {
- OvalShapeCacheEntry(float width, float height, SkPaint* paint):
- ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) {
- mWidth = width;
- mHeight = height;
- }
-
- OvalShapeCacheEntry(): ShapeCacheEntry() {
- mWidth = mHeight = 0;
- }
-
- hash_t hash() const {
- uint32_t hash = ShapeCacheEntry::hash();
- hash = JenkinsHashMix(hash, android::hash_type(mWidth));
- hash = JenkinsHashMix(hash, android::hash_type(mHeight));
- return JenkinsHashWhiten(hash);
- }
-
- int compare(const ShapeCacheEntry& r) const {
- int deltaInt = ShapeCacheEntry::compare(r);
- if (deltaInt != 0) return deltaInt;
-
- const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r;
-
- if (mWidth < rhs.mWidth) return -1;
- if (mWidth > rhs.mWidth) return +1;
-
- if (mHeight < rhs.mHeight) return -1;
- if (mHeight > rhs.mHeight) return +1;
-
- return 0;
- }
-
-private:
- float mWidth;
- float mHeight;
-}; // OvalShapeCacheEntry
-
-inline hash_t hash_type(const OvalShapeCacheEntry& entry) {
- return entry.hash();
-}
-
-struct RectShapeCacheEntry: public ShapeCacheEntry {
- RectShapeCacheEntry(float width, float height, SkPaint* paint):
- ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) {
- mWidth = width;
- mHeight = height;
- }
-
- RectShapeCacheEntry(): ShapeCacheEntry() {
- mWidth = mHeight = 0;
- }
-
- hash_t hash() const {
- uint32_t hash = ShapeCacheEntry::hash();
- hash = JenkinsHashMix(hash, android::hash_type(mWidth));
- hash = JenkinsHashMix(hash, android::hash_type(mHeight));
- return JenkinsHashWhiten(hash);
- }
-
- int compare(const ShapeCacheEntry& r) const {
- int deltaInt = ShapeCacheEntry::compare(r);
- if (deltaInt != 0) return deltaInt;
-
- const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r;
-
- if (mWidth < rhs.mWidth) return -1;
- if (mWidth > rhs.mWidth) return +1;
-
- if (mHeight < rhs.mHeight) return -1;
- if (mHeight > rhs.mHeight) return +1;
-
- return 0;
- }
-
-private:
- float mWidth;
- float mHeight;
-}; // RectShapeCacheEntry
-
-inline hash_t hash_type(const RectShapeCacheEntry& entry) {
- return entry.hash();
-}
-
-struct ArcShapeCacheEntry: public ShapeCacheEntry {
- ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle,
- bool useCenter, SkPaint* paint):
- ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) {
- mWidth = width;
- mHeight = height;
- mStartAngle = startAngle;
- mSweepAngle = sweepAngle;
- mUseCenter = useCenter ? 1 : 0;
- }
-
- ArcShapeCacheEntry(): ShapeCacheEntry() {
- mWidth = 0;
- mHeight = 0;
- mStartAngle = 0;
- mSweepAngle = 0;
- mUseCenter = 0;
- }
-
- hash_t hash() const {
- uint32_t hash = ShapeCacheEntry::hash();
- hash = JenkinsHashMix(hash, android::hash_type(mWidth));
- hash = JenkinsHashMix(hash, android::hash_type(mHeight));
- hash = JenkinsHashMix(hash, android::hash_type(mStartAngle));
- hash = JenkinsHashMix(hash, android::hash_type(mSweepAngle));
- hash = JenkinsHashMix(hash, mUseCenter);
- return JenkinsHashWhiten(hash);
- }
-
- int compare(const ShapeCacheEntry& r) const {
- int deltaInt = ShapeCacheEntry::compare(r);
- if (deltaInt != 0) return deltaInt;
-
- const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r;
-
- if (mWidth < rhs.mWidth) return -1;
- if (mWidth > rhs.mWidth) return +1;
-
- if (mHeight < rhs.mHeight) return -1;
- if (mHeight > rhs.mHeight) return +1;
-
- if (mStartAngle < rhs.mStartAngle) return -1;
- if (mStartAngle > rhs.mStartAngle) return +1;
-
- if (mSweepAngle < rhs.mSweepAngle) return -1;
- if (mSweepAngle > rhs.mSweepAngle) return +1;
-
- return mUseCenter - rhs.mUseCenter;
- }
-
-private:
- float mWidth;
- float mHeight;
- float mStartAngle;
- float mSweepAngle;
- uint32_t mUseCenter;
-}; // ArcShapeCacheEntry
-
-inline hash_t hash_type(const ArcShapeCacheEntry& entry) {
- return entry.hash();
-}
-
-/**
- * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
- * Any texture added to the cache causing the cache to grow beyond the maximum
- * allowed size will also cause the oldest texture to be kicked out.
- */
-template<typename Entry>
-class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> {
-public:
- ShapeCache(const char* name, const char* propertyName, float defaultSize);
- ~ShapeCache();
-
- /**
- * Used as a callback when an entry is removed from the cache.
- * Do not invoke directly.
- */
- void operator()(Entry& path, PathTexture*& texture);
-
- /**
- * Clears the cache. This causes all textures to be deleted.
- */
- void clear();
-
- /**
- * Sets the maximum size of the cache in bytes.
- */
- void setMaxSize(uint32_t maxSize);
- /**
- * Returns the maximum size of the cache in bytes.
- */
- uint32_t getMaxSize();
- /**
- * Returns the current size of the cache in bytes.
- */
- uint32_t getSize();
-
- /**
- * Trims the contents of the cache, removing items until it's under its
- * specified limit.
- *
- * Trimming is used for caches that support pre-caching from a worker
- * thread. During pre-caching the maximum limit of the cache can be
- * exceeded for the duration of the frame. It is therefore required to
- * trim the cache at the end of the frame to keep the total amount of
- * memory used under control.
- *
- * Only the PathCache currently supports pre-caching.
- */
- void trim();
-
- static void computePathBounds(const SkPath* path, const SkPaint* paint,
- float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
- const SkRect& bounds = path->getBounds();
- computeBounds(bounds, paint, left, top, offset, width, height);
- }
-
- static void computeBounds(const SkRect& bounds, const SkPaint* paint,
- float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
- const float pathWidth = fmax(bounds.width(), 1.0f);
- const float pathHeight = fmax(bounds.height(), 1.0f);
-
- left = bounds.fLeft;
- top = bounds.fTop;
-
- offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
-
- width = uint32_t(pathWidth + offset * 2.0 + 0.5);
- height = uint32_t(pathHeight + offset * 2.0 + 0.5);
- }
-
- static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
- float left, float top, float offset, uint32_t width, uint32_t height) {
- initBitmap(bitmap, width, height);
-
- SkPaint pathPaint(*paint);
- initPaint(pathPaint);
-
- SkCanvas canvas(bitmap);
- canvas.translate(-left + offset, -top + offset);
- canvas.drawPath(*path, pathPaint);
- }
-
-protected:
- PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
- PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap);
- void addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture);
-
- /**
- * Ensures there is enough space in the cache for a texture of the specified
- * dimensions.
- */
- void purgeCache(uint32_t width, uint32_t height);
-
- PathTexture* get(Entry entry) {
- return mCache.get(entry);
- }
-
- void removeTexture(PathTexture* texture);
-
- bool checkTextureSize(uint32_t width, uint32_t height) {
- if (width > mMaxTextureSize || height > mMaxTextureSize) {
- ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
- mName, width, height, mMaxTextureSize, mMaxTextureSize);
- return false;
- }
- return true;
- }
-
- static PathTexture* createTexture(float left, float top, float offset,
- uint32_t width, uint32_t height, uint32_t id) {
- PathTexture* texture = new PathTexture();
- texture->left = left;
- texture->top = top;
- texture->offset = offset;
- texture->width = width;
- texture->height = height;
- texture->generation = id;
- return texture;
- }
-
- static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
- bitmap.setConfig(SkBitmap::kA8_Config, width, height);
- bitmap.allocPixels();
- bitmap.eraseColor(0);
- }
-
- static void initPaint(SkPaint& paint) {
- // Make sure the paint is opaque, color, alpha, filter, etc.
- // will be applied later when compositing the alpha8 texture
- paint.setColor(0xff000000);
- paint.setAlpha(255);
- paint.setColorFilter(NULL);
- paint.setMaskFilter(NULL);
- paint.setShader(NULL);
- SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
- SkSafeUnref(paint.setXfermode(mode));
- }
-
- LruCache<Entry, PathTexture*> mCache;
- uint32_t mSize;
- uint32_t mMaxSize;
- GLuint mMaxTextureSize;
-
- char* mName;
- bool mDebugEnabled;
-
-private:
- /**
- * Generates the texture from a bitmap into the specified texture structure.
- */
- void generateTexture(SkBitmap& bitmap, Texture* texture);
-
- void init();
-}; // class ShapeCache
-
-class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
-public:
- RoundRectShapeCache();
-
- PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
-}; // class RoundRectShapeCache
-
-class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
-public:
- CircleShapeCache();
-
- PathTexture* getCircle(float radius, SkPaint* paint);
-}; // class CircleShapeCache
-
-class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> {
-public:
- OvalShapeCache();
-
- PathTexture* getOval(float width, float height, SkPaint* paint);
-}; // class OvalShapeCache
-
-class RectShapeCache: public ShapeCache<RectShapeCacheEntry> {
-public:
- RectShapeCache();
-
- PathTexture* getRect(float width, float height, SkPaint* paint);
-}; // class RectShapeCache
-
-class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> {
-public:
- ArcShapeCache();
-
- PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
- bool useCenter, SkPaint* paint);
-}; // class ArcShapeCache
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-template<class Entry>
-ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize):
- mCache(LruCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
- mSize(0), mMaxSize(MB(defaultSize)) {
- char property[PROPERTY_VALUE_MAX];
- if (property_get(propertyName, property, NULL) > 0) {
- INIT_LOGD(" Setting %s cache size to %sMB", name, property);
- setMaxSize(MB(atof(property)));
- } else {
- INIT_LOGD(" Using default %s cache size of %.2fMB", name, defaultSize);
- }
-
- size_t len = strlen(name);
- mName = new char[len + 1];
- strcpy(mName, name);
- mName[len] = '\0';
-
- init();
-}
-
-template<class Entry>
-ShapeCache<Entry>::~ShapeCache() {
- mCache.clear();
- delete[] mName;
-}
-
-template<class Entry>
-void ShapeCache<Entry>::init() {
- mCache.setOnEntryRemovedListener(this);
-
- GLint maxTextureSize;
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
- mMaxTextureSize = maxTextureSize;
-
- mDebugEnabled = readDebugLevel() & kDebugCaches;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Size management
-///////////////////////////////////////////////////////////////////////////////
-
-template<class Entry>
-uint32_t ShapeCache<Entry>::getSize() {
- return mSize;
-}
-
-template<class Entry>
-uint32_t ShapeCache<Entry>::getMaxSize() {
- return mMaxSize;
-}
-
-template<class Entry>
-void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
- mMaxSize = maxSize;
- while (mSize > mMaxSize) {
- mCache.removeOldest();
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Callbacks
-///////////////////////////////////////////////////////////////////////////////
-
-template<class Entry>
-void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
- removeTexture(texture);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-template<class Entry>
-void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
- if (texture) {
- const uint32_t size = texture->width * texture->height;
- mSize -= size;
-
- SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d",
- mName, texture->id, size, mSize);
- if (mDebugEnabled) {
- ALOGD("Shape %s deleted, size = %d", mName, size);
- }
-
- if (texture->id) {
- glDeleteTextures(1, &texture->id);
- }
- delete texture;
- }
-}
-
-template<class Entry>
-void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) {
- const uint32_t size = width * height;
- // Don't even try to cache a bitmap that's bigger than the cache
- if (size < mMaxSize) {
- while (mSize + size > mMaxSize) {
- mCache.removeOldest();
- }
- }
-}
-
-template<class Entry>
-void ShapeCache<Entry>::trim() {
- while (mSize > mMaxSize) {
- mCache.removeOldest();
- }
-}
-
-template<class Entry>
-PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
- const SkPaint* paint) {
- ATRACE_CALL();
-
- float left, top, offset;
- uint32_t width, height;
- computePathBounds(path, paint, left, top, offset, width, height);
-
- if (!checkTextureSize(width, height)) return NULL;
-
- purgeCache(width, height);
-
- SkBitmap bitmap;
- drawPath(path, paint, bitmap, left, top, offset, width, height);
-
- PathTexture* texture = createTexture(left, top, offset, width, height,
- path->getGenerationID());
- addTexture(entry, &bitmap, texture);
-
- return texture;
-}
-
-template<class Entry>
-void ShapeCache<Entry>::addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture) {
- generateTexture(*bitmap, texture);
-
- uint32_t size = texture->width * texture->height;
- if (size < mMaxSize) {
- mSize += size;
- SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d",
- mName, texture->id, size, mSize);
- if (mDebugEnabled) {
- ALOGD("Shape %s created, size = %d", mName, size);
- }
- mCache.put(entry, texture);
- } else {
- texture->cleanup = true;
- }
-}
-
-template<class Entry>
-void ShapeCache<Entry>::clear() {
- mCache.clear();
-}
-
-template<class Entry>
-void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) {
- SkAutoLockPixels alp(bitmap);
- if (!bitmap.readyToDraw()) {
- ALOGE("Cannot generate texture from bitmap");
- return;
- }
-
- glGenTextures(1, &texture->id);
-
- glBindTexture(GL_TEXTURE_2D, texture->id);
- // Textures are Alpha8
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
- texture->blend = true;
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
-
- texture->setFilter(GL_LINEAR);
- texture->setWrap(GL_CLAMP_TO_EDGE);
-}
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_SHAPE_CACHE_H
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 9013fd5..c38eedb 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -411,8 +411,14 @@
void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
const Snapshot& snapshot, GLuint* textureUnit) {
- mFirst->setupProgram(program, modelView, snapshot, textureUnit);
- mSecond->setupProgram(program, modelView, snapshot, textureUnit);
+ // Apply this compose shader's local transform and pass it down to
+ // the child shaders. They will in turn apply their local transform
+ // to this matrix.
+ mat4 transform;
+ computeScreenSpaceMatrix(transform, modelView);
+
+ mFirst->setupProgram(program, transform, snapshot, textureUnit);
+ mSecond->setupProgram(program, transform, snapshot, textureUnit);
}
}; // namespace uirenderer
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 0b3df35..72d249a 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -96,6 +96,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.am.BatteryStatsService;
+import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.BaseNetworkObserver;
@@ -154,6 +155,8 @@
private boolean mLockdownEnabled;
private LockdownVpnTracker mLockdownTracker;
+ private Nat464Xlat mClat;
+
/** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */
private Object mRulesLock = new Object();
/** Currently active network rules by UID. */
@@ -544,9 +547,12 @@
mVpn = new Vpn(mContext, mVpnCallback, mNetd);
mVpn.startMonitoring(mContext, mTrackerHandler);
+ mClat = new Nat464Xlat(mContext, mNetd, this, mTrackerHandler);
+
try {
mNetd.registerObserver(mTethering);
mNetd.registerObserver(mDataActivityObserver);
+ mNetd.registerObserver(mClat);
} catch (RemoteException e) {
loge("Error registering observer :" + e);
}
@@ -2276,6 +2282,17 @@
}
}
+ // Update 464xlat state.
+ // TODO: Move to handleConnect()
+ NetworkStateTracker tracker = mNetTrackers[netType];
+ if (mClat.requiresClat(netType, tracker)) {
+ if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
+ mClat.startClat(tracker);
+ } else {
+ mClat.stopClat();
+ }
+ }
+
// TODO: Temporary notifying upstread change to Tethering.
// @see bug/4455071
/** Notify TetheringService if interface name has been changed. */
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 7686705..2210a18 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -233,6 +233,7 @@
try {
mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
} catch (RemoteException e) {
+ } catch (RuntimeException e) {
}
}
mObservers.finishBroadcast();
@@ -248,6 +249,7 @@
try {
mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
} catch (RemoteException e) {
+ } catch (RuntimeException e) {
}
}
mObservers.finishBroadcast();
@@ -262,6 +264,7 @@
try {
mObservers.getBroadcastItem(i).interfaceAdded(iface);
} catch (RemoteException e) {
+ } catch (RuntimeException e) {
}
}
mObservers.finishBroadcast();
@@ -281,6 +284,7 @@
try {
mObservers.getBroadcastItem(i).interfaceRemoved(iface);
} catch (RemoteException e) {
+ } catch (RuntimeException e) {
}
}
mObservers.finishBroadcast();
@@ -295,6 +299,7 @@
try {
mObservers.getBroadcastItem(i).limitReached(limitName, iface);
} catch (RemoteException e) {
+ } catch (RuntimeException e) {
}
}
mObservers.finishBroadcast();
@@ -309,6 +314,7 @@
try {
mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(label, active);
} catch (RemoteException e) {
+ } catch (RuntimeException e) {
}
}
mObservers.finishBroadcast();
diff --git a/services/java/com/android/server/connectivity/Nat464Xlat.java b/services/java/com/android/server/connectivity/Nat464Xlat.java
new file mode 100644
index 0000000..2884eaf
--- /dev/null
+++ b/services/java/com/android/server/connectivity/Nat464Xlat.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+
+import java.net.Inet4Address;
+
+import android.content.Context;
+import android.net.IConnectivityManager;
+import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkStateTracker;
+import android.net.NetworkUtils;
+import android.net.RouteInfo;
+import android.os.Handler;
+import android.os.Message;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.net.BaseNetworkObserver;
+
+/**
+ * @hide
+ *
+ * Class to manage a 464xlat CLAT daemon.
+ */
+public class Nat464Xlat extends BaseNetworkObserver {
+ private Context mContext;
+ private INetworkManagementService mNMService;
+ private IConnectivityManager mConnService;
+ private NetworkStateTracker mTracker;
+ private Handler mHandler;
+
+ // Whether we started clatd and expect it to be running.
+ private boolean mIsStarted;
+ // Whether the clatd interface exists (i.e., clatd is running).
+ private boolean mIsRunning;
+ // The LinkProperties of the clat interface.
+ private LinkProperties mLP;
+
+ // This must match the interface name in clatd.conf.
+ private static final String CLAT_INTERFACE_NAME = "clat4";
+
+ private static final String TAG = "Nat464Xlat";
+
+ public Nat464Xlat(Context context, INetworkManagementService nmService,
+ IConnectivityManager connService, Handler handler) {
+ mContext = context;
+ mNMService = nmService;
+ mConnService = connService;
+ mHandler = handler;
+
+ mIsStarted = false;
+ mIsRunning = false;
+ mLP = new LinkProperties();
+ }
+
+ /**
+ * Determines whether an interface requires clat.
+ * @param netType the network type (one of the
+ * android.net.ConnectivityManager.TYPE_* constants)
+ * @param tracker the NetworkStateTracker corresponding to the network type.
+ * @return true if the interface requires clat, false otherwise.
+ */
+ public boolean requiresClat(int netType, NetworkStateTracker tracker) {
+ LinkProperties lp = tracker.getLinkProperties();
+ // Only support clat on mobile for now.
+ Slog.d(TAG, "requiresClat: netType=" + netType + ", hasIPv4Address=" +
+ lp.hasIPv4Address());
+ return netType == TYPE_MOBILE && !lp.hasIPv4Address();
+ }
+
+ /**
+ * Starts the clat daemon.
+ * @param lp The link properties of the interface to start clatd on.
+ */
+ public void startClat(NetworkStateTracker tracker) {
+ if (mIsStarted) {
+ Slog.e(TAG, "startClat: already started");
+ return;
+ }
+ mTracker = tracker;
+ LinkProperties lp = mTracker.getLinkProperties();
+ String iface = lp.getInterfaceName();
+ Slog.i(TAG, "Starting clatd on " + iface + ", lp=" + lp);
+ try {
+ mNMService.startClatd(iface);
+ } catch(RemoteException e) {
+ Slog.e(TAG, "Error starting clat daemon: " + e);
+ }
+ mIsStarted = true;
+ }
+
+ /**
+ * Stops the clat daemon.
+ */
+ public void stopClat() {
+ if (mIsStarted) {
+ Slog.i(TAG, "Stopping clatd");
+ try {
+ mNMService.stopClatd();
+ } catch(RemoteException e) {
+ Slog.e(TAG, "Error stopping clat daemon: " + e);
+ }
+ mIsStarted = false;
+ mIsRunning = false;
+ mTracker = null;
+ mLP.clear();
+ } else {
+ Slog.e(TAG, "stopClat: already stopped");
+ }
+ }
+
+ public boolean isStarted() {
+ return mIsStarted;
+ }
+
+ public boolean isRunning() {
+ return mIsRunning;
+ }
+
+ @Override
+ public void interfaceAdded(String iface) {
+ if (iface.equals(CLAT_INTERFACE_NAME)) {
+ Slog.i(TAG, "interface " + CLAT_INTERFACE_NAME +
+ " added, mIsRunning = " + mIsRunning + " -> true");
+ mIsRunning = true;
+
+ // Get the network configuration of the clat interface, store it
+ // in our link properties, and stack it on top of the interface
+ // it's running on.
+ try {
+ InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
+ mLP.clear();
+ mLP.setInterfaceName(iface);
+ RouteInfo ipv4Default = new RouteInfo(new LinkAddress(Inet4Address.ANY, 0), null,
+ iface);
+ mLP.addRoute(ipv4Default);
+ mLP.addLinkAddress(config.getLinkAddress());
+ mTracker.addStackedLink(mLP);
+ Slog.i(TAG, "Adding stacked link. tracker LP: " +
+ mTracker.getLinkProperties());
+ } catch(RemoteException e) {
+ Slog.e(TAG, "Error getting link properties: " + e);
+ }
+
+ // Inform ConnectivityService that things have changed.
+ Message msg = mHandler.obtainMessage(
+ NetworkStateTracker.EVENT_CONFIGURATION_CHANGED,
+ mTracker.getNetworkInfo());
+ Slog.i(TAG, "sending message to ConnectivityService: " + msg);
+ msg.sendToTarget();
+ }
+ }
+
+ @Override
+ public void interfaceRemoved(String iface) {
+ if (iface == CLAT_INTERFACE_NAME) {
+ if (mIsRunning) {
+ NetworkUtils.resetConnections(
+ CLAT_INTERFACE_NAME,
+ NetworkUtils.RESET_IPV4_ADDRESSES);
+ }
+ Slog.i(TAG, "interface " + CLAT_INTERFACE_NAME +
+ " removed, mIsRunning = " + mIsRunning + " -> false");
+ mIsRunning = false;
+ mTracker.removeStackedLink(mLP);
+ mLP.clear();
+ Slog.i(TAG, "mLP = " + mLP);
+ }
+ }
+};
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index e4a7ead..32f39b7 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -35,6 +35,7 @@
import android.net.LinkProperties;
import android.net.NetworkInfo;
import android.net.NetworkUtils;
+import android.net.RouteInfo;
import android.os.Binder;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -1345,7 +1346,21 @@
linkProperties = mConnService.getLinkProperties(upType);
} catch (RemoteException e) { }
if (linkProperties != null) {
- iface = linkProperties.getInterfaceName();
+ // Find the interface with the default IPv4 route. It may be the
+ // interface described by linkProperties, or one of the interfaces
+ // stacked on top of it.
+ Log.i(TAG, "Finding IPv4 upstream interface on: " + linkProperties);
+ RouteInfo ipv4Default = RouteInfo.selectBestRoute(
+ linkProperties.getAllRoutes(), Inet4Address.ANY);
+ if (ipv4Default != null) {
+ iface = ipv4Default.getInterface();
+ Log.i(TAG, "Found interface " + ipv4Default.getInterface());
+ } else {
+ Log.i(TAG, "No IPv4 upstream interface, giving up.");
+ }
+ }
+
+ if (iface != null) {
String[] dnsServers = mDefaultDnsServers;
Collection<InetAddress> dnses = linkProperties.getDnses();
if (dnses != null) {
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 3708df2..b2fbec1 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -234,6 +234,7 @@
mSurfaceControl.setLayer(FREEZE_LAYER + 1);
mSurfaceControl.setAlpha(0);
mSurfaceControl.show();
+ sur.destroy();
} catch (SurfaceControl.OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate freeze surface", e);
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index cfefadd..56f4de5 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -1481,7 +1481,11 @@
pos++;
}
if (pos >= N) {
- // All is good!
+ // Z order is good.
+ // The IM target window may be changed, so update the mTargetAppToken.
+ if (imWin != null) {
+ imWin.mTargetAppToken = mInputMethodTarget.mAppToken;
+ }
return false;
}
}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java
index 02cb4b6..1847f43 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java
@@ -57,6 +57,7 @@
private Paint mLargePaint;
private BitmapShader mScaled2Shader;
private ColorFilter mColorFilter;
+ private final Matrix mMtx1;
ShadersView(Context c) {
super(c);
@@ -70,7 +71,7 @@
mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
Shader.TileMode.MIRROR);
Matrix m2 = new Matrix();
- m2.setScale(0.5f, 0.5f);
+ m2.setScale(0.1f, 0.1f);
mScaledShader.setLocalMatrix(m2);
mScaled2Shader = new BitmapShader(texture, Shader.TileMode.MIRROR,
@@ -81,12 +82,20 @@
mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
Color.RED, 0x7f00ff00, Shader.TileMode.CLAMP);
-
+ Matrix m4 = new Matrix();
+ m4.setScale(0.5f, 0.5f);
+ mHorGradient.setLocalMatrix(m4);
+
mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
mComposeShader = new ComposeShader(mScaledShader, mHorGradient,
PorterDuff.Mode.SRC_OVER);
+ mMtx1 = new Matrix();
+ mMtx1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f);
+ mMtx1.postRotate(45, 0, 0);
+ mComposeShader.setLocalMatrix(mMtx1);
+
mCompose2Shader = new ComposeShader(mHorGradient, mScaledShader,
PorterDuff.Mode.SRC_OUT);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java
index 97e5526..61dca78 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.RectF;
import android.os.Bundle;
import android.view.View;
@@ -34,12 +35,13 @@
}
static class ShapesView extends View {
- private Paint mNormalPaint;
- private Paint mStrokePaint;
- private Paint mFillPaint;
- private RectF mRect;
- private RectF mOval;
- private RectF mArc;
+ private final Paint mNormalPaint;
+ private final Paint mStrokePaint;
+ private final Paint mFillPaint;
+ private final RectF mRect;
+ private final RectF mOval;
+ private final RectF mArc;
+ private final Path mTriangle;
ShapesView(Context c) {
super(c);
@@ -65,6 +67,12 @@
mOval = new RectF(0.0f, 0.0f, 80.0f, 45.0f);
mArc = new RectF(0.0f, 0.0f, 100.0f, 120.0f);
+
+ mTriangle = new Path();
+ mTriangle.moveTo(0.0f, 90.0f);
+ mTriangle.lineTo(45.0f, 0.0f);
+ mTriangle.lineTo(90.0f, 90.0f);
+ mTriangle.close();
}
@Override
@@ -136,6 +144,17 @@
canvas.translate(0.0f, 110.0f);
canvas.drawArc(mArc, 30.0f, 100.0f, false, mFillPaint);
canvas.restore();
+
+ canvas.save();
+ canvas.translate(50.0f, 400.0f);
+ canvas.drawPath(mTriangle, mNormalPaint);
+
+ canvas.translate(110.0f, 0.0f);
+ canvas.drawPath(mTriangle, mStrokePaint);
+
+ canvas.translate(110.0f, 0.0f);
+ canvas.drawPath(mTriangle, mFillPaint);
+ canvas.restore();
}
}
}
diff --git a/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/SharedLibraryMain.java b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/SharedLibraryMain.java
index c1cd925..2c61b77 100644
--- a/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/SharedLibraryMain.java
+++ b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/SharedLibraryMain.java
@@ -17,16 +17,14 @@
package com.google.android.test.shared_library;
import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
+import android.app.Fragment;
+import android.app.FragmentManager;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
public class SharedLibraryMain {
- private static String LIBRARY_PACKAGE = "com.google.android.test.shared_library";
+ static String LIBRARY_PACKAGE = "com.google.android.test.shared_library";
/**
* Base version of the library.
@@ -38,6 +36,9 @@
*/
public static int VERSION_SECOND = 2;
+ /**
+ * Return the version number of the currently installed library.
+ */
public static int getVersion(Context context) {
PackageInfo pi = null;
try {
@@ -48,40 +49,34 @@
}
}
- public static void ensureVersion(Activity activity, int minVersion) {
+ /**
+ * Check that the library's version is at least the given minimum version,
+ * displaying a dialog to have the user install an update if that is not true.
+ * The dialog is displayed as a DialogFragment in your activity if a newer
+ * version is needed. If a newer version is needed, false is returned.
+ */
+ public static boolean ensureVersion(final Activity activity, int minVersion) {
+ final FragmentManager fm = activity.getFragmentManager();
+ final String dialogTag = LIBRARY_PACKAGE + ":version";
+ Fragment curDialog = fm.findFragmentByTag(dialogTag);
+
if (getVersion(activity) >= minVersion) {
- return;
+ // Library version is sufficient. Make sure any version dialog
+ // we had shown is removed before returning.
+ if (curDialog != null) {
+ fm.beginTransaction().remove(curDialog).commitAllowingStateLoss();
+ }
+ return true;
}
- // The current version of the library does not meet the required version. Show
- // a dialog to inform the user and have them update to the current version.
- // Note that updating the library will be necessity mean killing the current
- // application (so it can be re-started with the new version, so there is no
- // reason to return a result here.
- final Context context;
- try {
- context = activity.createPackageContext(LIBRARY_PACKAGE, 0);
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalStateException("Can't find my package!", e);
+ // The current version of the library does not meet the required version.
+ // If we don't already have a version dialog displayed, display it now.
+ if (curDialog == null) {
+ curDialog = new VersionDialog();
+ fm.beginTransaction().add(curDialog, dialogTag).commitAllowingStateLoss();
}
- // Display the dialog. Note that we don't need to deal with activity lifecycle
- // stuff because if the activity gets recreated, it will first call through to
- // ensureVersion(), causing us to either re-display the dialog if needed or let
- // it now proceed.
- final Resources res = context.getResources();
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setTitle(res.getText(R.string.upgrade_title));
- builder.setMessage(res.getString(R.string.upgrade_body,
- activity.getApplicationInfo().loadLabel(activity.getPackageManager()),
- context.getApplicationInfo().loadLabel(context.getPackageManager())));
- builder.setPositiveButton(res.getText(R.string.upgrade_button),
- new Dialog.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // Launch play store.
- }
- });
- builder.show();
+ // Tell the caller that the current version is not sufficient.
+ return false;
}
}
diff --git a/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/VersionDialog.java b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/VersionDialog.java
new file mode 100644
index 0000000..f457532
--- /dev/null
+++ b/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/VersionDialog.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.shared_library;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * This is the dialog we show when the library's version is older than
+ * the version the app needs.
+ */
+public class VersionDialog extends DialogFragment {
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Activity activity = getActivity();
+
+ // Need to use our library's resources for showing the dialog.
+ final Context context;
+ try {
+ context = activity.createPackageContext(SharedLibraryMain.LIBRARY_PACKAGE, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalStateException("Can't find my package!", e);
+ }
+
+ final Resources res = context.getResources();
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setTitle(res.getText(R.string.upgrade_title));
+ builder.setMessage(res.getString(R.string.upgrade_body,
+ activity.getApplicationInfo().loadLabel(activity.getPackageManager()),
+ context.getApplicationInfo().loadLabel(context.getPackageManager())));
+ builder.setPositiveButton(res.getText(R.string.upgrade_button),
+ new Dialog.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // Launch play store into the details of our app.
+ try {
+ activity.startActivity(new Intent(Intent.ACTION_VIEW,
+ Uri.parse("market://details?id="
+ + SharedLibraryMain.LIBRARY_PACKAGE)));
+ } catch (android.content.ActivityNotFoundException anfe) {
+ activity.startActivity(new Intent(Intent.ACTION_VIEW,
+ Uri.parse("http://play.google.com/store/apps/details?id="
+ + SharedLibraryMain.LIBRARY_PACKAGE)));
+ }
+ }
+ });
+ return builder.create();
+ }
+}