diff options
| -rw-r--r-- | core/java/android/os/Debug.java | 54 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 57 | ||||
| -rw-r--r-- | core/java/android/view/ViewDebug.java | 64 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 60 | ||||
| -rw-r--r-- | core/java/android/view/ViewRoot.java | 52 | ||||
| -rw-r--r-- | core/java/android/widget/AbsListView.java | 59 | ||||
| -rw-r--r-- | services/java/com/android/server/status/IconMerger.java | 8 |
7 files changed, 317 insertions, 37 deletions
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 9cf7a222c186..cd52bf3de7ea 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -30,6 +30,10 @@ import java.io.PrintWriter; import java.io.Reader; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; @@ -857,6 +861,15 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** + * Equivalent to <code>setFieldsOn(cl, false)</code>. + * + * @see #setFieldsOn(Class, boolean) + */ + public static void setFieldsOn(Class<?> cl) { + setFieldsOn(cl, false); + } + + /** * Reflectively sets static fields of a class based on internal debugging * properties. This method is a no-op if android.util.Config.DEBUG is * false. @@ -868,7 +881,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * Class setup: define a class whose only fields are non-final, static * primitive types (except for "char") or Strings. In a static block * after the field definitions/initializations, pass the class to - * this method, Debug.setFieldsOn(). Example: + * this method, Debug.setFieldsOn(). Example: * <pre> * package com.example; * @@ -880,12 +893,18 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * public static String ns = null; * public static boolean b = false; * public static int i = 5; + * @Debug.DebugProperty * public static float f = 0.1f; + * @@Debug.DebugProperty * public static double d = 0.5d; * * // This MUST appear AFTER all fields are defined and initialized! * static { + * // Sets all the fields * Debug.setFieldsOn(MyDebugVars.class); + * + * // Sets only the fields annotated with @Debug.DebugProperty + * // Debug.setFieldsOn(MyDebugVars.class, true); * } * } * </pre> @@ -898,25 +917,31 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * {@hide} * * @param cl The class to (possibly) modify + * @param partial If false, sets all static fields, otherwise, only set + * fields with the {@link android.os.Debug.DebugProperty} + * annotation * @throws IllegalArgumentException if any fields are final or non-static, * or if the type of the field does not match the type of * the internal debugging property value. */ - public static void setFieldsOn(Class<?> cl) { + public static void setFieldsOn(Class<?> cl, boolean partial) { if (Config.DEBUG) { if (debugProperties != null) { /* Only look for fields declared directly by the class, * so we don't mysteriously change static fields in superclasses. */ for (Field field : cl.getDeclaredFields()) { - final String propertyName = cl.getName() + "." + field.getName(); - boolean isStatic = Modifier.isStatic(field.getModifiers()); - boolean isFinal = Modifier.isFinal(field.getModifiers()); - if (!isStatic || isFinal) { - throw new IllegalArgumentException(propertyName + - " must be static and non-final"); + if (!partial || field.getAnnotation(DebugProperty.class) != null) { + final String propertyName = cl.getName() + "." + field.getName(); + boolean isStatic = Modifier.isStatic(field.getModifiers()); + boolean isFinal = Modifier.isFinal(field.getModifiers()); + + if (!isStatic || isFinal) { + throw new IllegalArgumentException(propertyName + + " must be static and non-final"); + } + modifyFieldIfSet(field, debugProperties, propertyName); } - modifyFieldIfSet(field, debugProperties, propertyName); } } } else { @@ -925,4 +950,15 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo ") called in non-DEBUG build"); } } + + /** + * Annotation to put on fields you want to set with + * {@link Debug#setFieldsOn(Class, boolean)}. + * + * @hide + */ + @Target({ ElementType.FIELD }) + @Retention(RetentionPolicy.RUNTIME) + public @interface DebugProperty { + } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 335b43c7c6e4..af5dca6ee817 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -49,6 +49,7 @@ import android.util.Poolable; import android.util.Pool; import android.util.Pools; import android.util.PoolableManager; +import android.util.Config; import android.view.ContextMenu.ContextMenuInfo; import android.view.animation.Animation; import android.view.inputmethod.InputConnection; @@ -5641,7 +5642,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE); } - if (ViewRoot.PROFILE_DRAWING) { + if (Config.DEBUG && ViewDebug.profileDrawing) { EventLog.writeEvent(60002, hashCode()); } @@ -7166,6 +7167,60 @@ public class View implements Drawable.Callback, KeyEvent.Callback { } /** + * @param consistency The type of consistency. See ViewDebug for more information. + * + * @hide + */ + protected boolean dispatchConsistencyCheck(int consistency) { + return onConsistencyCheck(consistency); + } + + /** + * Method that subclasses should implement to check their consistency. The type of + * consistency check is indicated by the bit field passed as a parameter. + * + * @param consistency The type of consistency. See ViewDebug for more information. + * + * @throws IllegalStateException if the view is in an inconsistent state. + * + * @hide + */ + protected boolean onConsistencyCheck(int consistency) { + boolean result = true; + + final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0; + final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0; + + if (checkLayout) { + if (getParent() == null) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, + "View " + this + " does not have a parent."); + } + + if (mAttachInfo == null) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, + "View " + this + " is not attached to a window."); + } + } + + if (checkDrawing) { + // Do not check the DIRTY/DRAWN flags because views can call invalidate() + // from their draw() method + + if ((mPrivateFlags & DRAWN) != DRAWN && + (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, + "View " + this + " was invalidated but its drawing cache is valid."); + } + } + + return result; + } + + /** * Prints information about this view in the log output, with the tag * {@link #VIEW_LOG_TAG}. * diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 367c9a22d953..4436f4b6e62c 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -54,6 +54,27 @@ import java.lang.reflect.AccessibleObject; */ public class ViewDebug { /** + * Log tag used to log errors related to the consistency of the view hierarchy. + * + * @hide + */ + public static final String CONSISTENCY_LOG_TAG = "ViewConsistency"; + + /** + * Flag indicating the consistency check should check layout-related properties. + * + * @hide + */ + public static final int CONSISTENCY_LAYOUT = 0x1; + + /** + * Flag indicating the consistency check should check drawing-related properties. + * + * @hide + */ + public static final int CONSISTENCY_DRAWING = 0x2; + + /** * Enables or disables view hierarchy tracing. Any invoker of * {@link #trace(View, android.view.ViewDebug.HierarchyTraceType)} should first * check that this value is set to true as not to affect performance. @@ -80,6 +101,49 @@ public class ViewDebug { static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent"; /** + * Profiles drawing times in the events log. + * + * @hide + */ + @Debug.DebugProperty + public static boolean profileDrawing = false; + + /** + * Profiles layout times in the events log. + * + * @hide + */ + @Debug.DebugProperty + public static boolean profileLayout = false; + + /** + * Profiles real fps (times between draws) and displays the result. + * + * @hide + */ + @Debug.DebugProperty + public static boolean showFps = false; + + /** + * <p>Enables or disables views consistency check. Even when this property is enabled, + * view consistency checks happen only if {@link android.util.Config#DEBUG} is set + * to true. The value of this property can be configured externally in one of the + * following files:</p> + * <ul> + * <li>/system/debug.prop</li> + * <li>/debug.prop</li> + * <li>/data/debug.prop</li> + * </ul> + * @hide + */ + @Debug.DebugProperty + public static boolean consistencyCheckEnabled = false; + + static { + Debug.setFieldsOn(ViewDebug.class, true); + } + + /** * This annotation can be used to mark fields and methods to be dumped by * the view server. Only non-void methods with no arguments can be annotated * by this annotation. diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 31159d722d75..26fe776ba350 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -32,6 +32,7 @@ import android.util.AttributeSet; import android.util.EventLog; import android.util.Log; import android.util.SparseArray; +import android.util.Config; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.LayoutAnimationController; @@ -1404,7 +1405,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Clear the flag as early as possible to allow draw() implementations // to call invalidate() successfully when doing animations - child.mPrivateFlags |= DRAWN; + child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN; if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) && (child.mPrivateFlags & DRAW_ANIMATION) == 0) { @@ -1494,7 +1495,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager cachePaint.setAlpha(255); mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE; } - if (ViewRoot.PROFILE_DRAWING) { + if (Config.DEBUG && ViewDebug.profileDrawing) { EventLog.writeEvent(60003, hashCode()); } canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint); @@ -2750,6 +2751,61 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * @hide + */ + @Override + protected boolean dispatchConsistencyCheck(int consistency) { + boolean result = super.dispatchConsistencyCheck(consistency); + + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + if (!children[i].dispatchConsistencyCheck(consistency)) result = false; + } + + return result; + } + + /** + * @hide + */ + @Override + protected boolean onConsistencyCheck(int consistency) { + boolean result = super.onConsistencyCheck(consistency); + + final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0; + final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0; + + if (checkLayout) { + final int count = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < count; i++) { + if (children[i].getParent() != this) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, + "View " + children[i] + " has no parent/a parent that is not " + this); + } + } + } + + if (checkDrawing) { + // If this group is dirty, check that the parent is dirty as well + if ((mPrivateFlags & DIRTY_MASK) != 0) { + final ViewParent parent = getParent(); + if (parent != null && !(parent instanceof ViewRoot)) { + if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, + "ViewGroup " + this + " is dirty but its parent is not: " + this); + } + } + } + } + + return result; + } + + /** * {@inheritDoc} */ @Override diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 8494d5e0c2c4..90453ba5d542 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -75,13 +75,6 @@ public final class ViewRoot extends Handler implements ViewParent, private static final boolean DEBUG_IMF = false || LOCAL_LOGV; private static final boolean WATCH_POINTER = false; - static final boolean PROFILE_DRAWING = false; - private static final boolean PROFILE_LAYOUT = false; - // profiles real fps (times between draws) and displays the result - private static final boolean SHOW_FPS = false; - // used by SHOW_FPS - private static int sDrawTime; - /** * Maximum time we allow the user to roll the trackball enough to generate * a key event, before resetting the counters. @@ -97,6 +90,8 @@ public final class ViewRoot extends Handler implements ViewParent, static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>(); + private static int sDrawTime; + long mLastTrackballTime = 0; final TrackballAxis mTrackballAxisX = new TrackballAxis(); final TrackballAxis mTrackballAxisY = new TrackballAxis(); @@ -796,7 +791,7 @@ public final class ViewRoot extends Handler implements ViewParent, final Rect frame = mWinFrame; boolean initialized = false; boolean contentInsetsChanged = false; - boolean visibleInsetsChanged = false; + boolean visibleInsetsChanged; try { boolean hadSurface = mSurface.isValid(); int fl = 0; @@ -937,14 +932,22 @@ public final class ViewRoot extends Handler implements ViewParent, if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v( "ViewRoot", "Laying out " + host + " to (" + host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")"); - long startTime; - if (PROFILE_LAYOUT) { + long startTime = 0L; + if (Config.DEBUG && ViewDebug.profileLayout) { startTime = SystemClock.elapsedRealtime(); } host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight); - if (PROFILE_LAYOUT) { + if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { + if (!host.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_LAYOUT)) { + throw new IllegalStateException("The view hierarchy is an inconsistent state," + + "please refer to the logs with the tag " + + ViewDebug.CONSISTENCY_LOG_TAG + " for more infomation."); + } + } + + if (Config.DEBUG && ViewDebug.profileLayout) { EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime); } @@ -960,10 +963,11 @@ public final class ViewRoot extends Handler implements ViewParent, mTmpLocation[1] + host.mBottom - host.mTop); host.gatherTransparentRegion(mTransparentRegion); - if (mAppScale != 1.0f) { - mTransparentRegion.scale(mAppScale); - } + // TODO: scale the region, like: + // Region uses native methods. We probabl should have ScalableRegion class. + + // Region does not have equals method ? if (!mTransparentRegion.equals(mPreviousTransparentRegion)) { mPreviousTransparentRegion.set(mTransparentRegion); // reconfigure window manager @@ -1168,6 +1172,9 @@ public final class ViewRoot extends Handler implements ViewParent, canvas.scale(scale, scale); } mView.draw(canvas); + if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { + mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING); + } } finally { canvas.restoreToCount(saveCount); } @@ -1175,7 +1182,7 @@ public final class ViewRoot extends Handler implements ViewParent, mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); checkEglErrors(); - if (SHOW_FPS) { + if (Config.DEBUG && ViewDebug.showFps) { int now = (int)SystemClock.elapsedRealtime(); if (sDrawTime != 0) { nativeShowFPS(canvas, now - sDrawTime); @@ -1216,7 +1223,7 @@ public final class ViewRoot extends Handler implements ViewParent, try { if (!dirty.isEmpty() || mIsAnimating) { - long startTime; + long startTime = 0L; if (DEBUG_ORIENTATION || DEBUG_DRAW) { Log.v("ViewRoot", "Surface " + surface + " drawing to bitmap w=" @@ -1224,7 +1231,7 @@ public final class ViewRoot extends Handler implements ViewParent, //canvas.drawARGB(255, 255, 0, 0); } - if (PROFILE_DRAWING) { + if (Config.DEBUG && ViewDebug.profileDrawing) { startTime = SystemClock.elapsedRealtime(); } @@ -1259,11 +1266,15 @@ public final class ViewRoot extends Handler implements ViewParent, canvas.scale(scale, scale); } mView.draw(canvas); + + if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { + mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING); + } } finally { canvas.restoreToCount(saveCount); } - if (SHOW_FPS) { + if (Config.DEBUG && ViewDebug.showFps) { int now = (int)SystemClock.elapsedRealtime(); if (sDrawTime != 0) { nativeShowFPS(canvas, now - sDrawTime); @@ -1271,7 +1282,7 @@ public final class ViewRoot extends Handler implements ViewParent, sDrawTime = now; } - if (PROFILE_DRAWING) { + if (Config.DEBUG && ViewDebug.profileDrawing) { EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime); } } @@ -1878,6 +1889,9 @@ public final class ViewRoot extends Handler implements ViewParent, } else { didFinish = false; } + if (event != null) { + event.scale(mAppScaleInverted); + } if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event); diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 4f503b488460..1ca59b2430c7 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3190,7 +3190,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // Reclaim views on screen for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - AbsListView.LayoutParams lp = (AbsListView.LayoutParams)child.getLayoutParams(); + AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams(); // Don't reclaim header or footer views, or views that should be ignored if (lp != null && mRecycler.shouldRecycleViewType(lp.viewType)) { views.add(child); @@ -3205,6 +3205,63 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** + * @hide + */ + @Override + protected boolean onConsistencyCheck(int consistency) { + boolean result = super.onConsistencyCheck(consistency); + + final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0; + + if (checkLayout) { + // The active recycler must be empty + final View[] activeViews = mRecycler.mActiveViews; + int count = activeViews.length; + for (int i = 0; i < count; i++) { + if (activeViews[i] != null) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, + "AbsListView " + this + " has a view in its active recycler: " + + activeViews[i]); + } + } + + // All views in the recycler must NOT be on screen and must NOT have a parent + final ArrayList<View> scrap = mRecycler.mCurrentScrap; + if (!checkScrap(scrap)) result = false; + final ArrayList<View>[] scraps = mRecycler.mScrapViews; + count = scraps.length; + for (int i = 0; i < count; i++) { + if (!checkScrap(scraps[i])) result = false; + } + } + + return result; + } + + private boolean checkScrap(ArrayList<View> scrap) { + if (scrap == null) return true; + boolean result = true; + + final int count = scrap.size(); + for (int i = 0; i < count; i++) { + final View view = scrap.get(i); + if (view.getParent() != null) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this + + " has a view in its scrap heap still attached to a parent: " + view); + } + if (indexOfChild(view) >= 0) { + result = false; + android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this + + " has a view in its scrap heap that is also a direct child: " + view); + } + } + + return result; + } + + /** * Sets the recycler listener to be notified whenever a View is set aside in * the recycler for later reuse. This listener can be used to free resources * associated to the View. diff --git a/services/java/com/android/server/status/IconMerger.java b/services/java/com/android/server/status/IconMerger.java index 37fdbfbbb329..5b806383d80b 100644 --- a/services/java/com/android/server/status/IconMerger.java +++ b/services/java/com/android/server/status/IconMerger.java @@ -8,8 +8,6 @@ import android.widget.LinearLayout; public class IconMerger extends LinearLayout { - private static final boolean SPEW = false; - StatusBarService service; StatusBarIcon moreIcon; @@ -29,7 +27,7 @@ public class IconMerger extends LinearLayout { int fitRight = -1; for (i=N-1; i>=0; i--) { final View child = getChildAt(i); - if (child != null && child.getVisibility() != GONE) { + if (child.getVisibility() != GONE) { fitRight = child.getRight(); break; } @@ -45,7 +43,7 @@ public class IconMerger extends LinearLayout { moreView = child; startIndex = i+1; } - else if (child != null && child.getVisibility() != GONE) { + else if (child.getVisibility() != GONE) { fitLeft = child.getLeft(); break; } @@ -71,7 +69,7 @@ public class IconMerger extends LinearLayout { int number = 0; for (i=startIndex; i<N; i++) { final View child = getChildAt(i); - if (child != null && child.getVisibility() != GONE) { + if (child.getVisibility() != GONE) { int childLeft = child.getLeft(); int childRight = child.getRight(); if (childLeft < breakingPoint) { |