Merge "Sometimes write may be called twice in a row for the same content."
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index e53ce8b..bb09c90 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -18,6 +18,8 @@
 
 import android.graphics.Matrix;
 
+import java.util.ArrayList;
+
 /**
  * <p>A display list records a series of graphics related operations and can replay
  * them later. Display lists are usually built by recording operations on a
@@ -120,12 +122,23 @@
  *
  * @hide
  */
-public abstract class DisplayList {
+public class DisplayList {
     private boolean mDirty;
+    private ArrayList<DisplayList> mChildDisplayLists;
+
+    private GLES20RecordingCanvas mCanvas;
+    private boolean mValid;
+
+    // Used for debugging
+    private final String mName;
+
+    // The native display list will be destroyed when this object dies.
+    // DO NOT overwrite this reference once it is set.
+    private DisplayListFinalizer mFinalizer;
 
     /**
      * Flag used when calling
-     * {@link HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int)} 
+     * {@link HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int)}
      * When this flag is set, draw operations lying outside of the bounds of the
      * display list will be culled early. It is recommeneded to always set this
      * flag.
@@ -173,6 +186,10 @@
      */
     public static final int STATUS_DREW = 0x4;
 
+    private DisplayList(String name) {
+        mName = name;
+    }
+
     /**
      * Creates a new display list that can be used to record batches of
      * drawing operations.
@@ -184,7 +201,7 @@
      * @hide
      */
     public static DisplayList create(String name) {
-        return new GLES20DisplayList(name);
+        return new DisplayList(name);
     }
 
     /**
@@ -202,7 +219,21 @@
      * @see #end()
      * @see #isValid()
      */
-    public abstract HardwareCanvas start(int width, int height);
+    public HardwareCanvas start(int width, int height) {
+        if (mCanvas != null) {
+            throw new IllegalStateException("Recording has already started");
+        }
+
+        mValid = false;
+        mCanvas = GLES20RecordingCanvas.obtain(this);
+        mCanvas.start();
+
+        mCanvas.setViewport(width, height);
+        // The dirty rect should always be null for a display list
+        mCanvas.onPreDraw(null);
+
+        return mCanvas;
+    }
 
     /**
      * Ends the recording for this display list. A display list cannot be
@@ -212,7 +243,20 @@
      * @see #start(int, int)
      * @see #isValid()
      */
-    public abstract void end();
+    public void end() {
+        if (mCanvas != null) {
+            mCanvas.onPostDraw();
+            if (mFinalizer != null) {
+                mCanvas.end(mFinalizer.mNativeDisplayList);
+            } else {
+                mFinalizer = new DisplayListFinalizer(mCanvas.end(0));
+                nSetDisplayListName(mFinalizer.mNativeDisplayList, mName);
+            }
+            mCanvas.recycle();
+            mCanvas = null;
+            mValid = true;
+        }
+    }
 
     /**
      * Clears resources held onto by this display list. After calling this method
@@ -221,8 +265,26 @@
      * @see #isValid()
      * @see #reset()
      */
-    public abstract void clear();
+    public void clear() {
+        clearDirty();
 
+        if (mCanvas != null) {
+            mCanvas.recycle();
+            mCanvas = null;
+        }
+        mValid = false;
+
+        clearReferences();
+    }
+
+    void clearReferences() {
+        if (mChildDisplayLists != null) mChildDisplayLists.clear();
+    }
+
+    ArrayList<DisplayList> getChildDisplayLists() {
+        if (mChildDisplayLists == null) mChildDisplayLists = new ArrayList<DisplayList>();
+        return mChildDisplayLists;
+    }
 
     /**
      * Reset native resources. This is called when cleaning up the state of display lists
@@ -233,7 +295,12 @@
      *
      * @hide
      */
-    public abstract void reset();
+    public void reset() {
+        if (hasNativeDisplayList()) {
+            nReset(mFinalizer.mNativeDisplayList);
+        }
+        clear();
+    }
 
     /**
      * Sets the dirty flag. When a display list is dirty, {@link #clear()} should
@@ -279,16 +346,30 @@
      *
      * @return boolean true if the display list is able to be replayed, false otherwise.
      */
-    public abstract boolean isValid();
+    public boolean isValid() { return mValid; }
 
     /**
      * Return the amount of memory used by this display list.
-     * 
+     *
      * @return The size of this display list in bytes
      *
      * @hide
      */
-    public abstract int getSize();
+    public int getSize() {
+        if (mFinalizer == null) return 0;
+        return nGetDisplayListSize(mFinalizer.mNativeDisplayList);
+    }
+
+    boolean hasNativeDisplayList() {
+        return mValid && mFinalizer != null;
+    }
+
+    int getNativeDisplayList() {
+        if (!mValid || mFinalizer == null) {
+            throw new IllegalStateException("The display list is not valid.");
+        }
+        return mFinalizer.mNativeDisplayList;
+    }
 
     ///////////////////////////////////////////////////////////////////////////
     // DisplayList Property Setters
@@ -303,7 +384,11 @@
      *
      * @hide
      */
-    public abstract void setCaching(boolean caching);
+    public void setCaching(boolean caching) {
+        if (hasNativeDisplayList()) {
+            nSetCaching(mFinalizer.mNativeDisplayList, caching);
+        }
+    }
 
     /**
      * Set whether the display list should clip itself to its bounds. This property is controlled by
@@ -311,7 +396,11 @@
      *
      * @param clipToBounds true if the display list should clip to its bounds
      */
-    public abstract void setClipToBounds(boolean clipToBounds);
+    public void setClipToBounds(boolean clipToBounds) {
+        if (hasNativeDisplayList()) {
+            nSetClipToBounds(mFinalizer.mNativeDisplayList, clipToBounds);
+        }
+    }
 
     /**
      * Set the static matrix on the display list. The specified matrix is combined with other
@@ -322,7 +411,11 @@
      * @see #getMatrix(android.graphics.Matrix)
      * @see #getMatrix()
      */
-    public abstract void setMatrix(Matrix matrix);
+    public void setMatrix(Matrix matrix) {
+        if (hasNativeDisplayList()) {
+            nSetStaticMatrix(mFinalizer.mNativeDisplayList, matrix.native_instance);
+        }
+    }
 
     /**
      * Returns the static matrix set on this display list.
@@ -348,7 +441,12 @@
      * @see #getMatrix()
      * @see #setMatrix(android.graphics.Matrix)
      */
-    public abstract Matrix getMatrix(Matrix matrix);
+    public Matrix getMatrix(Matrix matrix) {
+        if (hasNativeDisplayList()) {
+            nGetMatrix(mFinalizer.mNativeDisplayList, matrix.native_instance);
+        }
+        return matrix;
+    }
 
     /**
      * Set the Animation matrix on the display list. This matrix exists if an Animation is
@@ -360,7 +458,12 @@
      *
      * @hide
      */
-    public abstract void setAnimationMatrix(Matrix matrix);
+    public void setAnimationMatrix(Matrix matrix) {
+        if (hasNativeDisplayList()) {
+            nSetAnimationMatrix(mFinalizer.mNativeDisplayList,
+                    (matrix != null) ? matrix.native_instance : 0);
+        }
+    }
 
     /**
      * Sets the translucency level for the display list.
@@ -370,7 +473,11 @@
      * @see View#setAlpha(float)
      * @see #getAlpha()
      */
-    public abstract void setAlpha(float alpha);
+    public void setAlpha(float alpha) {
+        if (hasNativeDisplayList()) {
+            nSetAlpha(mFinalizer.mNativeDisplayList, alpha);
+        }
+    }
 
     /**
      * Returns the translucency level of this display list.
@@ -379,7 +486,12 @@
      *
      * @see #setAlpha(float)
      */
-    public abstract float getAlpha();
+    public float getAlpha() {
+        if (hasNativeDisplayList()) {
+            return nGetAlpha(mFinalizer.mNativeDisplayList);
+        }
+        return 1.0f;
+    }
 
     /**
      * Sets whether the display list renders content which overlaps. Non-overlapping rendering
@@ -392,7 +504,11 @@
      * @see android.view.View#hasOverlappingRendering()
      * @see #hasOverlappingRendering()
      */
-    public abstract void setHasOverlappingRendering(boolean hasOverlappingRendering);
+    public void setHasOverlappingRendering(boolean hasOverlappingRendering) {
+        if (hasNativeDisplayList()) {
+            nSetHasOverlappingRendering(mFinalizer.mNativeDisplayList, hasOverlappingRendering);
+        }
+    }
 
     /**
      * Indicates whether the content of this display list overlaps.
@@ -401,7 +517,13 @@
      *
      * @see #setHasOverlappingRendering(boolean)
      */
-    public abstract boolean hasOverlappingRendering();
+    public boolean hasOverlappingRendering() {
+        //noinspection SimplifiableIfStatement
+        if (hasNativeDisplayList()) {
+            return nHasOverlappingRendering(mFinalizer.mNativeDisplayList);
+        }
+        return true;
+    }
 
     /**
      * Sets the translation value for the display list on the X axis
@@ -411,14 +533,23 @@
      * @see View#setTranslationX(float)
      * @see #getTranslationX()
      */
-    public abstract void setTranslationX(float translationX);
+    public void setTranslationX(float translationX) {
+        if (hasNativeDisplayList()) {
+            nSetTranslationX(mFinalizer.mNativeDisplayList, translationX);
+        }
+    }
 
     /**
      * Returns the translation value for this display list on the X axis, in pixels.
      *
      * @see #setTranslationX(float)
      */
-    public abstract float getTranslationX();
+    public float getTranslationX() {
+        if (hasNativeDisplayList()) {
+            return nGetTranslationX(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the translation value for the display list on the Y axis
@@ -428,14 +559,23 @@
      * @see View#setTranslationY(float)
      * @see #getTranslationY()
      */
-    public abstract void setTranslationY(float translationY);
+    public void setTranslationY(float translationY) {
+        if (hasNativeDisplayList()) {
+            nSetTranslationY(mFinalizer.mNativeDisplayList, translationY);
+        }
+    }
 
     /**
      * Returns the translation value for this display list on the Y axis, in pixels.
      *
      * @see #setTranslationY(float)
      */
-    public abstract float getTranslationY();
+    public float getTranslationY() {
+        if (hasNativeDisplayList()) {
+            return nGetTranslationY(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the translation value for the display list on the Z axis
@@ -443,14 +583,23 @@
      * @see View#setTranslationZ(float)
      * @see #getTranslationZ()
      */
-    public abstract void setTranslationZ(float translationZ);
+    public void setTranslationZ(float translationZ) {
+        if (hasNativeDisplayList()) {
+            nSetTranslationZ(mFinalizer.mNativeDisplayList, translationZ);
+        }
+    }
 
     /**
      * Returns the translation value for this display list on the Z axis.
      *
      * @see #setTranslationZ(float)
      */
-    public abstract float getTranslationZ();
+    public float getTranslationZ() {
+        if (hasNativeDisplayList()) {
+            return nGetTranslationZ(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the rotation value for the display list around the Z axis
@@ -460,14 +609,23 @@
      * @see View#setRotation(float)
      * @see #getRotation()
      */
-    public abstract void setRotation(float rotation);
+    public void setRotation(float rotation) {
+        if (hasNativeDisplayList()) {
+            nSetRotation(mFinalizer.mNativeDisplayList, rotation);
+        }
+    }
 
     /**
      * Returns the rotation value for this display list around the Z axis, in degrees.
      *
      * @see #setRotation(float)
      */
-    public abstract float getRotation();
+    public float getRotation() {
+        if (hasNativeDisplayList()) {
+            return nGetRotation(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the rotation value for the display list around the X axis
@@ -477,14 +635,23 @@
      * @see View#setRotationX(float)
      * @see #getRotationX()
      */
-    public abstract void setRotationX(float rotationX);
+    public void setRotationX(float rotationX) {
+        if (hasNativeDisplayList()) {
+            nSetRotationX(mFinalizer.mNativeDisplayList, rotationX);
+        }
+    }
 
     /**
      * Returns the rotation value for this display list around the X axis, in degrees.
      *
      * @see #setRotationX(float)
      */
-    public abstract float getRotationX();
+    public float getRotationX() {
+        if (hasNativeDisplayList()) {
+            return nGetRotationX(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the rotation value for the display list around the Y axis
@@ -494,14 +661,23 @@
      * @see View#setRotationY(float)
      * @see #getRotationY()
      */
-    public abstract void setRotationY(float rotationY);
+    public void setRotationY(float rotationY) {
+        if (hasNativeDisplayList()) {
+            nSetRotationY(mFinalizer.mNativeDisplayList, rotationY);
+        }
+    }
 
     /**
      * Returns the rotation value for this display list around the Y axis, in degrees.
      *
      * @see #setRotationY(float)
      */
-    public abstract float getRotationY();
+    public float getRotationY() {
+        if (hasNativeDisplayList()) {
+            return nGetRotationY(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the scale value for the display list on the X axis
@@ -511,14 +687,23 @@
      * @see View#setScaleX(float)
      * @see #getScaleX()
      */
-    public abstract void setScaleX(float scaleX);
+    public void setScaleX(float scaleX) {
+        if (hasNativeDisplayList()) {
+            nSetScaleX(mFinalizer.mNativeDisplayList, scaleX);
+        }
+    }
 
     /**
      * Returns the scale value for this display list on the X axis.
      *
      * @see #setScaleX(float)
      */
-    public abstract float getScaleX();
+    public float getScaleX() {
+        if (hasNativeDisplayList()) {
+            return nGetScaleX(mFinalizer.mNativeDisplayList);
+        }
+        return 1.0f;
+    }
 
     /**
      * Sets the scale value for the display list on the Y axis
@@ -528,14 +713,23 @@
      * @see View#setScaleY(float)
      * @see #getScaleY()
      */
-    public abstract void setScaleY(float scaleY);
+    public void setScaleY(float scaleY) {
+        if (hasNativeDisplayList()) {
+            nSetScaleY(mFinalizer.mNativeDisplayList, scaleY);
+        }
+    }
 
     /**
      * Returns the scale value for this display list on the Y axis.
      *
      * @see #setScaleY(float)
      */
-    public abstract float getScaleY();
+    public float getScaleY() {
+        if (hasNativeDisplayList()) {
+            return nGetScaleY(mFinalizer.mNativeDisplayList);
+        }
+        return 1.0f;
+    }
 
     /**
      * Sets all of the transform-related values of the display list
@@ -551,9 +745,15 @@
      *
      * @hide
      */
-    public abstract void setTransformationInfo(float alpha,
+    public void setTransformationInfo(float alpha,
             float translationX, float translationY, float translationZ,
-            float rotation, float rotationX, float rotationY, float scaleX, float scaleY);
+            float rotation, float rotationX, float rotationY, float scaleX, float scaleY) {
+        if (hasNativeDisplayList()) {
+            nSetTransformationInfo(mFinalizer.mNativeDisplayList, alpha,
+                    translationX, translationY, translationZ,
+                    rotation, rotationX, rotationY, scaleX, scaleY);
+        }
+    }
 
     /**
      * Sets the pivot value for the display list on the X axis
@@ -563,14 +763,23 @@
      * @see View#setPivotX(float)
      * @see #getPivotX()
      */
-    public abstract void setPivotX(float pivotX);
+    public void setPivotX(float pivotX) {
+        if (hasNativeDisplayList()) {
+            nSetPivotX(mFinalizer.mNativeDisplayList, pivotX);
+        }
+    }
 
     /**
      * Returns the pivot value for this display list on the X axis, in pixels.
      *
      * @see #setPivotX(float)
      */
-    public abstract float getPivotX();
+    public float getPivotX() {
+        if (hasNativeDisplayList()) {
+            return nGetPivotX(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the pivot value for the display list on the Y axis
@@ -580,14 +789,23 @@
      * @see View#setPivotY(float)
      * @see #getPivotY()
      */
-    public abstract void setPivotY(float pivotY);
+    public void setPivotY(float pivotY) {
+        if (hasNativeDisplayList()) {
+            nSetPivotY(mFinalizer.mNativeDisplayList, pivotY);
+        }
+    }
 
     /**
      * Returns the pivot value for this display list on the Y axis, in pixels.
      *
      * @see #setPivotY(float)
      */
-    public abstract float getPivotY();
+    public float getPivotY() {
+        if (hasNativeDisplayList()) {
+            return nGetPivotY(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the camera distance for the display list. Refer to
@@ -599,14 +817,23 @@
      * @see View#setCameraDistance(float)
      * @see #getCameraDistance()
      */
-    public abstract void setCameraDistance(float distance);
+    public void setCameraDistance(float distance) {
+        if (hasNativeDisplayList()) {
+            nSetCameraDistance(mFinalizer.mNativeDisplayList, distance);
+        }
+    }
 
     /**
      * Returns the distance in Z of the camera of the display list.
      *
      * @see #setCameraDistance(float)
      */
-    public abstract float getCameraDistance();
+    public float getCameraDistance() {
+        if (hasNativeDisplayList()) {
+            return nGetCameraDistance(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the left position for the display list.
@@ -616,14 +843,23 @@
      * @see View#setLeft(int)
      * @see #getLeft()
      */
-    public abstract void setLeft(int left);
+    public void setLeft(int left) {
+        if (hasNativeDisplayList()) {
+            nSetLeft(mFinalizer.mNativeDisplayList, left);
+        }
+    }
 
     /**
      * Returns the left position for the display list in pixels.
      *
      * @see #setLeft(int)
      */
-    public abstract float getLeft();
+    public float getLeft() {
+        if (hasNativeDisplayList()) {
+            return nGetLeft(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the top position for the display list.
@@ -633,14 +869,23 @@
      * @see View#setTop(int)
      * @see #getTop()
      */
-    public abstract void setTop(int top);
+    public void setTop(int top) {
+        if (hasNativeDisplayList()) {
+            nSetTop(mFinalizer.mNativeDisplayList, top);
+        }
+    }
 
     /**
      * Returns the top position for the display list in pixels.
      *
      * @see #setTop(int)
      */
-    public abstract float getTop();
+    public float getTop() {
+        if (hasNativeDisplayList()) {
+            return nGetTop(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the right position for the display list.
@@ -650,14 +895,23 @@
      * @see View#setRight(int)
      * @see #getRight()
      */
-    public abstract void setRight(int right);
+    public void setRight(int right) {
+        if (hasNativeDisplayList()) {
+            nSetRight(mFinalizer.mNativeDisplayList, right);
+        }
+    }
 
     /**
      * Returns the right position for the display list in pixels.
      *
      * @see #setRight(int)
      */
-    public abstract float getRight();
+    public float getRight() {
+        if (hasNativeDisplayList()) {
+            return nGetRight(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the bottom position for the display list.
@@ -667,14 +921,23 @@
      * @see View#setBottom(int)
      * @see #getBottom()
      */
-    public abstract void setBottom(int bottom);
+    public void setBottom(int bottom) {
+        if (hasNativeDisplayList()) {
+            nSetBottom(mFinalizer.mNativeDisplayList, bottom);
+        }
+    }
 
     /**
      * Returns the bottom position for the display list in pixels.
      *
      * @see #setBottom(int)
      */
-    public abstract float getBottom();
+    public float getBottom() {
+        if (hasNativeDisplayList()) {
+            return nGetBottom(mFinalizer.mNativeDisplayList);
+        }
+        return 0.0f;
+    }
 
     /**
      * Sets the left and top positions for the display list
@@ -689,7 +952,11 @@
      * @see View#setRight(int)
      * @see View#setBottom(int)
      */
-    public abstract void setLeftTopRightBottom(int left, int top, int right, int bottom);
+    public void setLeftTopRightBottom(int left, int top, int right, int bottom) {
+        if (hasNativeDisplayList()) {
+            nSetLeftTopRightBottom(mFinalizer.mNativeDisplayList, left, top, right, bottom);
+        }
+    }
 
     /**
      * Offsets the left and right positions for the display list
@@ -699,7 +966,11 @@
      *
      * @see View#offsetLeftAndRight(int)
      */
-    public abstract void offsetLeftAndRight(float offset);
+    public void offsetLeftAndRight(float offset) {
+        if (hasNativeDisplayList()) {
+            nOffsetLeftAndRight(mFinalizer.mNativeDisplayList, offset);
+        }
+    }
 
     /**
      * Offsets the top and bottom values for the display list
@@ -709,7 +980,11 @@
      *
      * @see View#offsetTopAndBottom(int)
      */
-    public abstract void offsetTopAndBottom(float offset);
+    public void offsetTopAndBottom(float offset) {
+        if (hasNativeDisplayList()) {
+            nOffsetTopAndBottom(mFinalizer.mNativeDisplayList, offset);
+        }
+    }
 
     /**
      * Outputs the display list to the log. This method exists for use by
@@ -717,5 +992,91 @@
      *
      * @hide
      */
-    public abstract void output();
+    public void output() {
+        if (hasNativeDisplayList()) {
+            nOutput(mFinalizer.mNativeDisplayList);
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Native methods
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static native void nDestroyDisplayList(int displayList);
+    private static native int nGetDisplayListSize(int displayList);
+    private static native void nSetDisplayListName(int displayList, String name);
+
+    // Properties
+
+    private static native void nReset(int displayList);
+    private static native void nOffsetTopAndBottom(int displayList, float offset);
+    private static native void nOffsetLeftAndRight(int displayList, float offset);
+    private static native void nSetLeftTopRightBottom(int displayList, int left, int top,
+            int right, int bottom);
+    private static native void nSetBottom(int displayList, int bottom);
+    private static native void nSetRight(int displayList, int right);
+    private static native void nSetTop(int displayList, int top);
+    private static native void nSetLeft(int displayList, int left);
+    private static native void nSetCameraDistance(int displayList, float distance);
+    private static native void nSetPivotY(int displayList, float pivotY);
+    private static native void nSetPivotX(int displayList, float pivotX);
+    private static native void nSetCaching(int displayList, boolean caching);
+    private static native void nSetClipToBounds(int displayList, boolean clipToBounds);
+    private static native void nSetAlpha(int displayList, float alpha);
+    private static native void nSetHasOverlappingRendering(int displayList,
+            boolean hasOverlappingRendering);
+    private static native void nSetTranslationX(int displayList, float translationX);
+    private static native void nSetTranslationY(int displayList, float translationY);
+    private static native void nSetTranslationZ(int displayList, float translationZ);
+    private static native void nSetRotation(int displayList, float rotation);
+    private static native void nSetRotationX(int displayList, float rotationX);
+    private static native void nSetRotationY(int displayList, float rotationY);
+    private static native void nSetScaleX(int displayList, float scaleX);
+    private static native void nSetScaleY(int displayList, float scaleY);
+    private static native void nSetTransformationInfo(int displayList, float alpha,
+            float translationX, float translationY, float translationZ,
+            float rotation, float rotationX, float rotationY, float scaleX, float scaleY);
+    private static native void nSetStaticMatrix(int displayList, int nativeMatrix);
+    private static native void nSetAnimationMatrix(int displayList, int animationMatrix);
+
+    private static native boolean nHasOverlappingRendering(int displayList);
+    private static native void nGetMatrix(int displayList, int matrix);
+    private static native float nGetAlpha(int displayList);
+    private static native float nGetLeft(int displayList);
+    private static native float nGetTop(int displayList);
+    private static native float nGetRight(int displayList);
+    private static native float nGetBottom(int displayList);
+    private static native float nGetCameraDistance(int displayList);
+    private static native float nGetScaleX(int displayList);
+    private static native float nGetScaleY(int displayList);
+    private static native float nGetTranslationX(int displayList);
+    private static native float nGetTranslationY(int displayList);
+    private static native float nGetTranslationZ(int displayList);
+    private static native float nGetRotation(int displayList);
+    private static native float nGetRotationX(int displayList);
+    private static native float nGetRotationY(int displayList);
+    private static native float nGetPivotX(int displayList);
+    private static native float nGetPivotY(int displayList);
+    private static native void nOutput(int displayList);
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Finalization
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class DisplayListFinalizer {
+        final int mNativeDisplayList;
+
+        public DisplayListFinalizer(int nativeDisplayList) {
+            mNativeDisplayList = nativeDisplayList;
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                nDestroyDisplayList(mNativeDisplayList);
+            } finally {
+                super.finalize();
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 9b48881d..584a04c 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -416,7 +416,7 @@
 
     @Override
     public int drawDisplayList(DisplayList displayList, Rect dirty, int flags) {
-        return nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).getNativeDisplayList(),
+        return nDrawDisplayList(mRenderer, displayList.getNativeDisplayList(),
                 dirty, flags);
     }
 
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
deleted file mode 100644
index 7944e66..0000000
--- a/core/java/android/view/GLES20DisplayList.java
+++ /dev/null
@@ -1,538 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.graphics.Matrix;
-
-import java.util.ArrayList;
-
-/**
- * An implementation of display list for OpenGL ES 2.0.
- */
-class GLES20DisplayList extends DisplayList {
-    private ArrayList<DisplayList> mChildDisplayLists;
-
-    private GLES20RecordingCanvas mCanvas;
-    private boolean mValid;
-
-    // Used for debugging
-    private final String mName;
-
-    // The native display list will be destroyed when this object dies.
-    // DO NOT overwrite this reference once it is set.
-    private DisplayListFinalizer mFinalizer;
-
-    GLES20DisplayList(String name) {
-        mName = name;
-    }
-
-    boolean hasNativeDisplayList() {
-        return mValid && mFinalizer != null;
-    }
-
-    int getNativeDisplayList() {
-        if (!mValid || mFinalizer == null) {
-            throw new IllegalStateException("The display list is not valid.");
-        }
-        return mFinalizer.mNativeDisplayList;
-    }
-
-    @Override
-    public HardwareCanvas start(int width, int height) {
-        if (mCanvas != null) {
-            throw new IllegalStateException("Recording has already started");
-        }
-
-        mValid = false;
-        mCanvas = GLES20RecordingCanvas.obtain(this);
-        mCanvas.start();
-
-        mCanvas.setViewport(width, height);
-        // The dirty rect should always be null for a display list
-        mCanvas.onPreDraw(null);
-
-        return mCanvas;
-    }
-    @Override
-    public void clear() {
-        clearDirty();
-
-        if (mCanvas != null) {
-            mCanvas.recycle();
-            mCanvas = null;
-        }
-        mValid = false;
-
-        clearReferences();
-    }
-
-    void clearReferences() {
-        if (mChildDisplayLists != null) mChildDisplayLists.clear();
-    }
-
-    ArrayList<DisplayList> getChildDisplayLists() {
-        if (mChildDisplayLists == null) mChildDisplayLists = new ArrayList<DisplayList>();
-        return mChildDisplayLists;
-    }
-
-    @Override
-    public void reset() {
-        if (hasNativeDisplayList()) {
-            nReset(mFinalizer.mNativeDisplayList);
-        }
-        clear();
-    }
-
-    @Override
-    public boolean isValid() {
-        return mValid;
-    }
-
-    @Override
-    public void end() {
-        if (mCanvas != null) {
-            mCanvas.onPostDraw();
-            if (mFinalizer != null) {
-                mCanvas.end(mFinalizer.mNativeDisplayList);
-            } else {
-                mFinalizer = new DisplayListFinalizer(mCanvas.end(0));
-                nSetDisplayListName(mFinalizer.mNativeDisplayList, mName);
-            }
-            mCanvas.recycle();
-            mCanvas = null;
-            mValid = true;
-        }
-    }
-
-    @Override
-    public int getSize() {
-        if (mFinalizer == null) return 0;
-        return nGetDisplayListSize(mFinalizer.mNativeDisplayList);
-    }
-
-    private static native void nDestroyDisplayList(int displayList);
-    private static native int nGetDisplayListSize(int displayList);
-    private static native void nSetDisplayListName(int displayList, String name);
-
-    ///////////////////////////////////////////////////////////////////////////
-    // Native View Properties
-    ///////////////////////////////////////////////////////////////////////////
-
-    @Override
-    public void setCaching(boolean caching) {
-        if (hasNativeDisplayList()) {
-            nSetCaching(mFinalizer.mNativeDisplayList, caching);
-        }
-    }
-
-    @Override
-    public void setClipToBounds(boolean clipToBounds) {
-        if (hasNativeDisplayList()) {
-            nSetClipToBounds(mFinalizer.mNativeDisplayList, clipToBounds);
-        }
-    }
-
-    @Override
-    public void setMatrix(Matrix matrix) {
-        if (hasNativeDisplayList()) {
-            nSetStaticMatrix(mFinalizer.mNativeDisplayList, matrix.native_instance);
-        }
-    }
-
-    @Override
-    public Matrix getMatrix(Matrix matrix) {
-        if (hasNativeDisplayList()) {
-            nGetMatrix(mFinalizer.mNativeDisplayList, matrix.native_instance);
-        }
-        return matrix;
-    }
-
-    @Override
-    public void setAnimationMatrix(Matrix matrix) {
-        if (hasNativeDisplayList()) {
-            nSetAnimationMatrix(mFinalizer.mNativeDisplayList,
-                    (matrix != null) ? matrix.native_instance : 0);
-        }
-    }
-
-    @Override
-    public void setAlpha(float alpha) {
-        if (hasNativeDisplayList()) {
-            nSetAlpha(mFinalizer.mNativeDisplayList, alpha);
-        }
-    }
-
-    @Override
-    public float getAlpha() {
-        if (hasNativeDisplayList()) {
-            return nGetAlpha(mFinalizer.mNativeDisplayList);
-        }
-        return 1.0f;
-    }
-
-    @Override
-    public void setHasOverlappingRendering(boolean hasOverlappingRendering) {
-        if (hasNativeDisplayList()) {
-            nSetHasOverlappingRendering(mFinalizer.mNativeDisplayList, hasOverlappingRendering);
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        //noinspection SimplifiableIfStatement
-        if (hasNativeDisplayList()) {
-            return nHasOverlappingRendering(mFinalizer.mNativeDisplayList);
-        }
-        return true;
-    }
-
-    @Override
-    public void setTranslationX(float translationX) {
-        if (hasNativeDisplayList()) {
-            nSetTranslationX(mFinalizer.mNativeDisplayList, translationX);
-        }
-    }
-
-    @Override
-    public float getTranslationX() {
-        if (hasNativeDisplayList()) {
-            return nGetTranslationX(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setTranslationY(float translationY) {
-        if (hasNativeDisplayList()) {
-            nSetTranslationY(mFinalizer.mNativeDisplayList, translationY);
-        }
-    }
-
-    @Override
-    public float getTranslationY() {
-        if (hasNativeDisplayList()) {
-            return nGetTranslationY(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setTranslationZ(float translationZ) {
-        if (hasNativeDisplayList()) {
-            nSetTranslationZ(mFinalizer.mNativeDisplayList, translationZ);
-        }
-    }
-
-    @Override
-    public float getTranslationZ() {
-        if (hasNativeDisplayList()) {
-            return nGetTranslationZ(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setRotation(float rotation) {
-        if (hasNativeDisplayList()) {
-            nSetRotation(mFinalizer.mNativeDisplayList, rotation);
-        }
-    }
-
-    @Override
-    public float getRotation() {
-        if (hasNativeDisplayList()) {
-            return nGetRotation(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setRotationX(float rotationX) {
-        if (hasNativeDisplayList()) {
-            nSetRotationX(mFinalizer.mNativeDisplayList, rotationX);
-        }
-    }
-
-    @Override
-    public float getRotationX() {
-        if (hasNativeDisplayList()) {
-            return nGetRotationX(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setRotationY(float rotationY) {
-        if (hasNativeDisplayList()) {
-            nSetRotationY(mFinalizer.mNativeDisplayList, rotationY);
-        }
-    }
-
-    @Override
-    public float getRotationY() {
-        if (hasNativeDisplayList()) {
-            return nGetRotationY(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setScaleX(float scaleX) {
-        if (hasNativeDisplayList()) {
-            nSetScaleX(mFinalizer.mNativeDisplayList, scaleX);
-        }
-    }
-
-    @Override
-    public float getScaleX() {
-        if (hasNativeDisplayList()) {
-            return nGetScaleX(mFinalizer.mNativeDisplayList);
-        }
-        return 1.0f;
-    }
-
-    @Override
-    public void setScaleY(float scaleY) {
-        if (hasNativeDisplayList()) {
-            nSetScaleY(mFinalizer.mNativeDisplayList, scaleY);
-        }
-    }
-
-    @Override
-    public float getScaleY() {
-        if (hasNativeDisplayList()) {
-            return nGetScaleY(mFinalizer.mNativeDisplayList);
-        }
-        return 1.0f;
-    }
-
-    @Override
-    public void setTransformationInfo(float alpha,
-            float translationX, float translationY, float translationZ,
-            float rotation, float rotationX, float rotationY, float scaleX, float scaleY) {
-        if (hasNativeDisplayList()) {
-            nSetTransformationInfo(mFinalizer.mNativeDisplayList, alpha,
-                    translationX, translationY, translationZ,
-                    rotation, rotationX, rotationY, scaleX, scaleY);
-        }
-    }
-
-    @Override
-    public void setPivotX(float pivotX) {
-        if (hasNativeDisplayList()) {
-            nSetPivotX(mFinalizer.mNativeDisplayList, pivotX);
-        }
-    }
-
-    @Override
-    public float getPivotX() {
-        if (hasNativeDisplayList()) {
-            return nGetPivotX(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setPivotY(float pivotY) {
-        if (hasNativeDisplayList()) {
-            nSetPivotY(mFinalizer.mNativeDisplayList, pivotY);
-        }
-    }
-
-    @Override
-    public float getPivotY() {
-        if (hasNativeDisplayList()) {
-            return nGetPivotY(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setCameraDistance(float distance) {
-        if (hasNativeDisplayList()) {
-            nSetCameraDistance(mFinalizer.mNativeDisplayList, distance);
-        }
-    }
-
-    @Override
-    public float getCameraDistance() {
-        if (hasNativeDisplayList()) {
-            return nGetCameraDistance(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setLeft(int left) {
-        if (hasNativeDisplayList()) {
-            nSetLeft(mFinalizer.mNativeDisplayList, left);
-        }
-    }
-
-    @Override
-    public float getLeft() {
-        if (hasNativeDisplayList()) {
-            return nGetLeft(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setTop(int top) {
-        if (hasNativeDisplayList()) {
-            nSetTop(mFinalizer.mNativeDisplayList, top);
-        }
-    }
-
-    @Override
-    public float getTop() {
-        if (hasNativeDisplayList()) {
-            return nGetTop(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setRight(int right) {
-        if (hasNativeDisplayList()) {
-            nSetRight(mFinalizer.mNativeDisplayList, right);
-        }
-    }
-
-    @Override
-    public float getRight() {
-        if (hasNativeDisplayList()) {
-            return nGetRight(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setBottom(int bottom) {
-        if (hasNativeDisplayList()) {
-            nSetBottom(mFinalizer.mNativeDisplayList, bottom);
-        }
-    }
-
-    @Override
-    public float getBottom() {
-        if (hasNativeDisplayList()) {
-            return nGetBottom(mFinalizer.mNativeDisplayList);
-        }
-        return 0.0f;
-    }
-
-    @Override
-    public void setLeftTopRightBottom(int left, int top, int right, int bottom) {
-        if (hasNativeDisplayList()) {
-            nSetLeftTopRightBottom(mFinalizer.mNativeDisplayList, left, top, right, bottom);
-        }
-    }
-
-    @Override
-    public void offsetLeftAndRight(float offset) {
-        if (hasNativeDisplayList()) {
-            nOffsetLeftAndRight(mFinalizer.mNativeDisplayList, offset);
-        }
-    }
-
-    @Override
-    public void offsetTopAndBottom(float offset) {
-        if (hasNativeDisplayList()) {
-            nOffsetTopAndBottom(mFinalizer.mNativeDisplayList, offset);
-        }
-    }
-
-    @Override
-    public void output() {
-        if (hasNativeDisplayList()) {
-            nOutput(mFinalizer.mNativeDisplayList);
-        }
-    }
-
-    private static native void nReset(int displayList);
-    private static native void nOffsetTopAndBottom(int displayList, float offset);
-    private static native void nOffsetLeftAndRight(int displayList, float offset);
-    private static native void nSetLeftTopRightBottom(int displayList, int left, int top,
-            int right, int bottom);
-    private static native void nSetBottom(int displayList, int bottom);
-    private static native void nSetRight(int displayList, int right);
-    private static native void nSetTop(int displayList, int top);
-    private static native void nSetLeft(int displayList, int left);
-    private static native void nSetCameraDistance(int displayList, float distance);
-    private static native void nSetPivotY(int displayList, float pivotY);
-    private static native void nSetPivotX(int displayList, float pivotX);
-    private static native void nSetCaching(int displayList, boolean caching);
-    private static native void nSetClipToBounds(int displayList, boolean clipToBounds);
-    private static native void nSetAlpha(int displayList, float alpha);
-    private static native void nSetHasOverlappingRendering(int displayList,
-            boolean hasOverlappingRendering);
-    private static native void nSetTranslationX(int displayList, float translationX);
-    private static native void nSetTranslationY(int displayList, float translationY);
-    private static native void nSetTranslationZ(int displayList, float translationZ);
-    private static native void nSetRotation(int displayList, float rotation);
-    private static native void nSetRotationX(int displayList, float rotationX);
-    private static native void nSetRotationY(int displayList, float rotationY);
-    private static native void nSetScaleX(int displayList, float scaleX);
-    private static native void nSetScaleY(int displayList, float scaleY);
-    private static native void nSetTransformationInfo(int displayList, float alpha,
-            float translationX, float translationY, float translationZ,
-            float rotation, float rotationX, float rotationY, float scaleX, float scaleY);
-    private static native void nSetStaticMatrix(int displayList, int nativeMatrix);
-    private static native void nSetAnimationMatrix(int displayList, int animationMatrix);
-
-    private static native boolean nHasOverlappingRendering(int displayList);
-    private static native void nGetMatrix(int displayList, int matrix);
-    private static native float nGetAlpha(int displayList);
-    private static native float nGetLeft(int displayList);
-    private static native float nGetTop(int displayList);
-    private static native float nGetRight(int displayList);
-    private static native float nGetBottom(int displayList);
-    private static native float nGetCameraDistance(int displayList);
-    private static native float nGetScaleX(int displayList);
-    private static native float nGetScaleY(int displayList);
-    private static native float nGetTranslationX(int displayList);
-    private static native float nGetTranslationY(int displayList);
-    private static native float nGetTranslationZ(int displayList);
-    private static native float nGetRotation(int displayList);
-    private static native float nGetRotationX(int displayList);
-    private static native float nGetRotationY(int displayList);
-    private static native float nGetPivotX(int displayList);
-    private static native float nGetPivotY(int displayList);
-    private static native void nOutput(int displayList);
-
-    ///////////////////////////////////////////////////////////////////////////
-    // Finalization
-    ///////////////////////////////////////////////////////////////////////////
-
-    private static class DisplayListFinalizer {
-        final int mNativeDisplayList;
-
-        public DisplayListFinalizer(int nativeDisplayList) {
-            mNativeDisplayList = nativeDisplayList;
-        }
-
-        @Override
-        protected void finalize() throws Throwable {
-            try {
-                nDestroyDisplayList(mNativeDisplayList);
-            } finally {
-                super.finalize();
-            }
-        }
-    }
-}
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index b6fc38d..97efa18 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -33,13 +33,13 @@
     private static final SynchronizedPool<GLES20RecordingCanvas> sPool =
             new SynchronizedPool<GLES20RecordingCanvas>(POOL_LIMIT);
 
-    private GLES20DisplayList mDisplayList;
+    private DisplayList mDisplayList;
 
     private GLES20RecordingCanvas() {
         super(true, true);
     }
 
-    static GLES20RecordingCanvas obtain(GLES20DisplayList displayList) {
+    static GLES20RecordingCanvas obtain(DisplayList displayList) {
         GLES20RecordingCanvas canvas = sPool.acquire();
         if (canvas == null) {
             canvas = new GLES20RecordingCanvas();
diff --git a/core/java/android/view/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java
index 68ba77c..8c97867 100644
--- a/core/java/android/view/GLES20RenderLayer.java
+++ b/core/java/android/view/GLES20RenderLayer.java
@@ -124,7 +124,7 @@
     @Override
     void redrawLater(DisplayList displayList, Rect dirtyRect) {
         GLES20Canvas.nUpdateRenderLayer(mLayer, mCanvas.getRenderer(),
-                ((GLES20DisplayList) displayList).getNativeDisplayList(),
+                displayList.getNativeDisplayList(),
                 dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
     }
 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 329331a..f26374a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -42,6 +42,7 @@
 	android_database_SQLiteDebug.cpp \
 	android_emoji_EmojiFactory.cpp \
 	android_view_DisplayEventReceiver.cpp \
+	android_view_DisplayList.cpp \
 	android_view_Surface.cpp \
 	android_view_SurfaceControl.cpp \
 	android_view_SurfaceSession.cpp \
@@ -55,7 +56,6 @@
 	android_view_KeyCharacterMap.cpp \
 	android_view_GraphicBuffer.cpp \
 	android_view_GLRenderer.cpp \
-	android_view_GLES20DisplayList.cpp \
 	android_view_GLES20Canvas.cpp \
 	android_view_ThreadedRenderer.cpp \
 	android_view_MotionEvent.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index b20dc09..89d75dc 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -119,8 +119,8 @@
 extern int register_android_graphics_Xfermode(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
 extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
+extern int register_android_view_DisplayList(JNIEnv* env);
 extern int register_android_view_GraphicBuffer(JNIEnv* env);
-extern int register_android_view_GLES20DisplayList(JNIEnv* env);
 extern int register_android_view_GLES20Canvas(JNIEnv* env);
 extern int register_android_view_GLRenderer(JNIEnv* env);
 extern int register_android_view_ThreadedRenderer(JNIEnv* env);
@@ -1121,11 +1121,11 @@
     REG_JNI(register_android_os_SystemProperties),
     REG_JNI(register_android_os_Binder),
     REG_JNI(register_android_os_Parcel),
-    REG_JNI(register_android_view_DisplayEventReceiver),
     REG_JNI(register_android_nio_utils),
     REG_JNI(register_android_graphics_Graphics),
+    REG_JNI(register_android_view_DisplayEventReceiver),
+    REG_JNI(register_android_view_DisplayList),
     REG_JNI(register_android_view_GraphicBuffer),
-    REG_JNI(register_android_view_GLES20DisplayList),
     REG_JNI(register_android_view_GLES20Canvas),
     REG_JNI(register_android_view_GLRenderer),
     REG_JNI(register_android_view_ThreadedRenderer),
diff --git a/core/jni/android_view_DisplayList.cpp b/core/jni/android_view_DisplayList.cpp
new file mode 100644
index 0000000..fc12ec4
--- /dev/null
+++ b/core/jni/android_view_DisplayList.cpp
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <EGL/egl.h>
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <DisplayList.h>
+#include <DisplayListRenderer.h>
+
+namespace android {
+
+using namespace uirenderer;
+
+/**
+ * Note: OpenGLRenderer JNI layer is generated and compiled only on supported
+ *       devices. This means all the logic must be compiled only when the
+ *       preprocessor variable USE_OPENGL_RENDERER is defined.
+ */
+#ifdef USE_OPENGL_RENDERER
+
+// ----------------------------------------------------------------------------
+// DisplayList view properties
+// ----------------------------------------------------------------------------
+
+static void android_view_DisplayList_reset(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    displayList->reset();
+}
+
+static jint android_view_DisplayList_getDisplayListSize(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getSize();
+}
+
+static void android_view_DisplayList_setDisplayListName(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, jstring name) {
+    if (name != NULL) {
+        const char* textArray = env->GetStringUTFChars(name, NULL);
+        displayList->setName(textArray);
+        env->ReleaseStringUTFChars(name, textArray);
+    }
+}
+
+static void android_view_DisplayList_output(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    displayList->output();
+}
+
+static void android_view_DisplayList_destroyDisplayList(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    DisplayList::destroyDisplayListDeferred(displayList);
+}
+
+// ----------------------------------------------------------------------------
+// DisplayList view properties
+// ----------------------------------------------------------------------------
+
+static void android_view_DisplayList_setCaching(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, jboolean caching) {
+    displayList->setCaching(caching);
+}
+
+static void android_view_DisplayList_setStaticMatrix(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, SkMatrix* matrix) {
+    displayList->setStaticMatrix(matrix);
+}
+
+static void android_view_DisplayList_setAnimationMatrix(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, SkMatrix* matrix) {
+    displayList->setAnimationMatrix(matrix);
+}
+
+static void android_view_DisplayList_setClipToBounds(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, jboolean clipToBounds) {
+    displayList->setClipToBounds(clipToBounds);
+}
+
+static void android_view_DisplayList_setAlpha(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float alpha) {
+    displayList->setAlpha(alpha);
+}
+
+static void android_view_DisplayList_setHasOverlappingRendering(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, bool hasOverlappingRendering) {
+    displayList->setHasOverlappingRendering(hasOverlappingRendering);
+}
+
+static void android_view_DisplayList_setTranslationX(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float tx) {
+    displayList->setTranslationX(tx);
+}
+
+static void android_view_DisplayList_setTranslationY(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float ty) {
+    displayList->setTranslationY(ty);
+}
+
+static void android_view_DisplayList_setTranslationZ(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float tz) {
+    displayList->setTranslationZ(tz);
+}
+
+static void android_view_DisplayList_setRotation(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float rotation) {
+    displayList->setRotation(rotation);
+}
+
+static void android_view_DisplayList_setRotationX(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float rx) {
+    displayList->setRotationX(rx);
+}
+
+static void android_view_DisplayList_setRotationY(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float ry) {
+    displayList->setRotationY(ry);
+}
+
+static void android_view_DisplayList_setScaleX(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float sx) {
+    displayList->setScaleX(sx);
+}
+
+static void android_view_DisplayList_setScaleY(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float sy) {
+    displayList->setScaleY(sy);
+}
+
+static void android_view_DisplayList_setTransformationInfo(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float alpha,
+        float translationX, float translationY, float translationZ,
+        float rotation, float rotationX, float rotationY, float scaleX, float scaleY) {
+    displayList->setAlpha(alpha);
+    displayList->setTranslationX(translationX);
+    displayList->setTranslationY(translationY);
+    displayList->setTranslationZ(translationZ);
+    displayList->setRotation(rotation);
+    displayList->setRotationX(rotationX);
+    displayList->setRotationY(rotationY);
+    displayList->setScaleX(scaleX);
+    displayList->setScaleY(scaleY);
+}
+
+static void android_view_DisplayList_setPivotX(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float px) {
+    displayList->setPivotX(px);
+}
+
+static void android_view_DisplayList_setPivotY(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float py) {
+    displayList->setPivotY(py);
+}
+
+static void android_view_DisplayList_setCameraDistance(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float distance) {
+    displayList->setCameraDistance(distance);
+}
+
+static void android_view_DisplayList_setLeft(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, int left) {
+    displayList->setLeft(left);
+}
+
+static void android_view_DisplayList_setTop(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, int top) {
+    displayList->setTop(top);
+}
+
+static void android_view_DisplayList_setRight(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, int right) {
+    displayList->setRight(right);
+}
+
+static void android_view_DisplayList_setBottom(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, int bottom) {
+    displayList->setBottom(bottom);
+}
+
+static void android_view_DisplayList_setLeftTopRightBottom(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, int left, int top,
+        int right, int bottom) {
+    displayList->setLeftTopRightBottom(left, top, right, bottom);
+}
+
+static void android_view_DisplayList_offsetLeftAndRight(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float offset) {
+    displayList->offsetLeftRight(offset);
+}
+
+static void android_view_DisplayList_offsetTopAndBottom(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, float offset) {
+    displayList->offsetTopBottom(offset);
+}
+
+static void android_view_DisplayList_getMatrix(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, SkMatrix* matrix) {
+    SkMatrix* source = displayList->getStaticMatrix();
+    if (source) {
+        matrix->setConcat(SkMatrix::I(), *source);
+    } else {
+        matrix->setIdentity();
+    }
+}
+
+static jboolean android_view_DisplayList_hasOverlappingRendering(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->hasOverlappingRendering();
+}
+
+static jfloat android_view_DisplayList_getAlpha(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getAlpha();
+}
+
+static jfloat android_view_DisplayList_getLeft(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getLeft();
+}
+
+static jfloat android_view_DisplayList_getTop(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getTop();
+}
+
+static jfloat android_view_DisplayList_getRight(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getRight();
+}
+
+static jfloat android_view_DisplayList_getBottom(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getBottom();
+}
+
+static jfloat android_view_DisplayList_getCameraDistance(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getCameraDistance();
+}
+
+static jfloat android_view_DisplayList_getScaleX(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getScaleX();
+}
+
+static jfloat android_view_DisplayList_getScaleY(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getScaleY();
+}
+
+static jfloat android_view_DisplayList_getTranslationX(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getTranslationX();
+}
+
+static jfloat android_view_DisplayList_getTranslationY(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getTranslationY();
+}
+
+static jfloat android_view_DisplayList_getRotation(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getRotation();
+}
+
+static jfloat android_view_DisplayList_getRotationX(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getRotationX();
+}
+
+static jfloat android_view_DisplayList_getRotationY(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getRotationY();
+}
+
+static jfloat android_view_DisplayList_getPivotX(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getPivotX();
+}
+
+static jfloat android_view_DisplayList_getPivotY(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    return displayList->getPivotY();
+}
+
+#endif // USE_OPENGL_RENDERER
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/view/DisplayList";
+
+static JNINativeMethod gMethods[] = {
+#ifdef USE_OPENGL_RENDERER
+    { "nDestroyDisplayList",   "(I)V",   (void*) android_view_DisplayList_destroyDisplayList },
+    { "nGetDisplayListSize",   "(I)I",   (void*) android_view_DisplayList_getDisplayListSize },
+    { "nSetDisplayListName",   "(ILjava/lang/String;)V",
+            (void*) android_view_DisplayList_setDisplayListName },
+    { "nOutput",               "(I)V",  (void*) android_view_DisplayList_output },
+
+    { "nReset",                "(I)V",   (void*) android_view_DisplayList_reset },
+    { "nSetCaching",           "(IZ)V",  (void*) android_view_DisplayList_setCaching },
+    { "nSetStaticMatrix",      "(II)V",  (void*) android_view_DisplayList_setStaticMatrix },
+    { "nSetAnimationMatrix",   "(II)V",  (void*) android_view_DisplayList_setAnimationMatrix },
+    { "nSetClipToBounds",      "(IZ)V",  (void*) android_view_DisplayList_setClipToBounds },
+    { "nSetAlpha",             "(IF)V",  (void*) android_view_DisplayList_setAlpha },
+    { "nSetHasOverlappingRendering", "(IZ)V",
+            (void*) android_view_DisplayList_setHasOverlappingRendering },
+    { "nSetTranslationX",      "(IF)V",  (void*) android_view_DisplayList_setTranslationX },
+    { "nSetTranslationY",      "(IF)V",  (void*) android_view_DisplayList_setTranslationY },
+    { "nSetTranslationZ",      "(IF)V",  (void*) android_view_DisplayList_setTranslationZ },
+    { "nSetRotation",          "(IF)V",  (void*) android_view_DisplayList_setRotation },
+    { "nSetRotationX",         "(IF)V",  (void*) android_view_DisplayList_setRotationX },
+    { "nSetRotationY",         "(IF)V",  (void*) android_view_DisplayList_setRotationY },
+    { "nSetScaleX",            "(IF)V",  (void*) android_view_DisplayList_setScaleX },
+    { "nSetScaleY",            "(IF)V",  (void*) android_view_DisplayList_setScaleY },
+    { "nSetTransformationInfo","(IFFFFFFFFF)V",
+            (void*) android_view_DisplayList_setTransformationInfo },
+    { "nSetPivotX",            "(IF)V",  (void*) android_view_DisplayList_setPivotX },
+    { "nSetPivotY",            "(IF)V",  (void*) android_view_DisplayList_setPivotY },
+    { "nSetCameraDistance",    "(IF)V",  (void*) android_view_DisplayList_setCameraDistance },
+    { "nSetLeft",              "(II)V",  (void*) android_view_DisplayList_setLeft },
+    { "nSetTop",               "(II)V",  (void*) android_view_DisplayList_setTop },
+    { "nSetRight",             "(II)V",  (void*) android_view_DisplayList_setRight },
+    { "nSetBottom",            "(II)V",  (void*) android_view_DisplayList_setBottom },
+    { "nSetLeftTopRightBottom","(IIIII)V", (void*) android_view_DisplayList_setLeftTopRightBottom },
+    { "nOffsetLeftAndRight",   "(IF)V",  (void*) android_view_DisplayList_offsetLeftAndRight },
+    { "nOffsetTopAndBottom",   "(IF)V",  (void*) android_view_DisplayList_offsetTopAndBottom },
+
+    { "nGetMatrix",               "(II)V", (void*) android_view_DisplayList_getMatrix },
+    { "nHasOverlappingRendering", "(I)Z",  (void*) android_view_DisplayList_hasOverlappingRendering },
+    { "nGetAlpha",                "(I)F",  (void*) android_view_DisplayList_getAlpha },
+    { "nGetLeft",                 "(I)F",  (void*) android_view_DisplayList_getLeft },
+    { "nGetTop",                  "(I)F",  (void*) android_view_DisplayList_getTop },
+    { "nGetRight",                "(I)F",  (void*) android_view_DisplayList_getRight },
+    { "nGetBottom",               "(I)F",  (void*) android_view_DisplayList_getBottom },
+    { "nGetCameraDistance",       "(I)F",  (void*) android_view_DisplayList_getCameraDistance },
+    { "nGetScaleX",               "(I)F",  (void*) android_view_DisplayList_getScaleX },
+    { "nGetScaleY",               "(I)F",  (void*) android_view_DisplayList_getScaleY },
+    { "nGetTranslationX",         "(I)F",  (void*) android_view_DisplayList_getTranslationX },
+    { "nGetTranslationY",         "(I)F",  (void*) android_view_DisplayList_getTranslationY },
+    { "nGetRotation",             "(I)F",  (void*) android_view_DisplayList_getRotation },
+    { "nGetRotationX",            "(I)F",  (void*) android_view_DisplayList_getRotationX },
+    { "nGetRotationY",            "(I)F",  (void*) android_view_DisplayList_getRotationY },
+    { "nGetPivotX",               "(I)F",  (void*) android_view_DisplayList_getPivotX },
+    { "nGetPivotY",               "(I)F",  (void*) android_view_DisplayList_getPivotY },
+#endif
+};
+
+#ifdef USE_OPENGL_RENDERER
+    #define FIND_CLASS(var, className) \
+            var = env->FindClass(className); \
+            LOG_FATAL_IF(! var, "Unable to find class " className);
+
+    #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+            var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+            LOG_FATAL_IF(! var, "Unable to find method " methodName);
+#else
+    #define FIND_CLASS(var, className)
+    #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor)
+#endif
+
+int register_android_view_DisplayList(JNIEnv* env) {
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+};
+
diff --git a/core/jni/android_view_GLES20DisplayList.cpp b/core/jni/android_view_GLES20DisplayList.cpp
deleted file mode 100644
index d6cddb2..0000000
--- a/core/jni/android_view_GLES20DisplayList.cpp
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "OpenGLRenderer"
-
-#include <EGL/egl.h>
-
-#include "jni.h"
-#include "GraphicsJNI.h"
-#include <nativehelper/JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-
-#include <DisplayList.h>
-#include <DisplayListRenderer.h>
-
-namespace android {
-
-using namespace uirenderer;
-
-/**
- * Note: OpenGLRenderer JNI layer is generated and compiled only on supported
- *       devices. This means all the logic must be compiled only when the
- *       preprocessor variable USE_OPENGL_RENDERER is defined.
- */
-#ifdef USE_OPENGL_RENDERER
-
-// ----------------------------------------------------------------------------
-// DisplayList view properties
-// ----------------------------------------------------------------------------
-
-static void android_view_GLES20DisplayList_reset(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    displayList->reset();
-}
-
-static jint android_view_GLES20DisplayList_getDisplayListSize(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getSize();
-}
-
-static void android_view_GLES20DisplayList_setDisplayListName(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, jstring name) {
-    if (name != NULL) {
-        const char* textArray = env->GetStringUTFChars(name, NULL);
-        displayList->setName(textArray);
-        env->ReleaseStringUTFChars(name, textArray);
-    }
-}
-
-static void android_view_GLES20DisplayList_output(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    displayList->output();
-}
-
-static void android_view_GLES20DisplayList_destroyDisplayList(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    DisplayList::destroyDisplayListDeferred(displayList);
-}
-
-// ----------------------------------------------------------------------------
-// DisplayList view properties
-// ----------------------------------------------------------------------------
-
-static void android_view_GLES20DisplayList_setCaching(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, jboolean caching) {
-    displayList->setCaching(caching);
-}
-
-static void android_view_GLES20DisplayList_setStaticMatrix(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, SkMatrix* matrix) {
-    displayList->setStaticMatrix(matrix);
-}
-
-static void android_view_GLES20DisplayList_setAnimationMatrix(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, SkMatrix* matrix) {
-    displayList->setAnimationMatrix(matrix);
-}
-
-static void android_view_GLES20DisplayList_setClipToBounds(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, jboolean clipToBounds) {
-    displayList->setClipToBounds(clipToBounds);
-}
-
-static void android_view_GLES20DisplayList_setAlpha(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float alpha) {
-    displayList->setAlpha(alpha);
-}
-
-static void android_view_GLES20DisplayList_setHasOverlappingRendering(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, bool hasOverlappingRendering) {
-    displayList->setHasOverlappingRendering(hasOverlappingRendering);
-}
-
-static void android_view_GLES20DisplayList_setTranslationX(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float tx) {
-    displayList->setTranslationX(tx);
-}
-
-static void android_view_GLES20DisplayList_setTranslationY(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float ty) {
-    displayList->setTranslationY(ty);
-}
-
-static void android_view_GLES20DisplayList_setTranslationZ(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float tz) {
-    displayList->setTranslationZ(tz);
-}
-
-static void android_view_GLES20DisplayList_setRotation(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float rotation) {
-    displayList->setRotation(rotation);
-}
-
-static void android_view_GLES20DisplayList_setRotationX(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float rx) {
-    displayList->setRotationX(rx);
-}
-
-static void android_view_GLES20DisplayList_setRotationY(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float ry) {
-    displayList->setRotationY(ry);
-}
-
-static void android_view_GLES20DisplayList_setScaleX(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float sx) {
-    displayList->setScaleX(sx);
-}
-
-static void android_view_GLES20DisplayList_setScaleY(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float sy) {
-    displayList->setScaleY(sy);
-}
-
-static void android_view_GLES20DisplayList_setTransformationInfo(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float alpha,
-        float translationX, float translationY, float translationZ,
-        float rotation, float rotationX, float rotationY, float scaleX, float scaleY) {
-    displayList->setAlpha(alpha);
-    displayList->setTranslationX(translationX);
-    displayList->setTranslationY(translationY);
-    displayList->setTranslationZ(translationZ);
-    displayList->setRotation(rotation);
-    displayList->setRotationX(rotationX);
-    displayList->setRotationY(rotationY);
-    displayList->setScaleX(scaleX);
-    displayList->setScaleY(scaleY);
-}
-
-static void android_view_GLES20DisplayList_setPivotX(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float px) {
-    displayList->setPivotX(px);
-}
-
-static void android_view_GLES20DisplayList_setPivotY(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float py) {
-    displayList->setPivotY(py);
-}
-
-static void android_view_GLES20DisplayList_setCameraDistance(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float distance) {
-    displayList->setCameraDistance(distance);
-}
-
-static void android_view_GLES20DisplayList_setLeft(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, int left) {
-    displayList->setLeft(left);
-}
-
-static void android_view_GLES20DisplayList_setTop(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, int top) {
-    displayList->setTop(top);
-}
-
-static void android_view_GLES20DisplayList_setRight(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, int right) {
-    displayList->setRight(right);
-}
-
-static void android_view_GLES20DisplayList_setBottom(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, int bottom) {
-    displayList->setBottom(bottom);
-}
-
-static void android_view_GLES20DisplayList_setLeftTopRightBottom(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, int left, int top,
-        int right, int bottom) {
-    displayList->setLeftTopRightBottom(left, top, right, bottom);
-}
-
-static void android_view_GLES20DisplayList_offsetLeftAndRight(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float offset) {
-    displayList->offsetLeftRight(offset);
-}
-
-static void android_view_GLES20DisplayList_offsetTopAndBottom(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float offset) {
-    displayList->offsetTopBottom(offset);
-}
-
-static void android_view_GLES20DisplayList_getMatrix(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, SkMatrix* matrix) {
-    SkMatrix* source = displayList->getStaticMatrix();
-    if (source) {
-        matrix->setConcat(SkMatrix::I(), *source);
-    } else {
-        matrix->setIdentity();
-    }
-}
-
-static jboolean android_view_GLES20DisplayList_hasOverlappingRendering(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->hasOverlappingRendering();
-}
-
-static jfloat android_view_GLES20DisplayList_getAlpha(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getAlpha();
-}
-
-static jfloat android_view_GLES20DisplayList_getLeft(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getLeft();
-}
-
-static jfloat android_view_GLES20DisplayList_getTop(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getTop();
-}
-
-static jfloat android_view_GLES20DisplayList_getRight(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getRight();
-}
-
-static jfloat android_view_GLES20DisplayList_getBottom(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getBottom();
-}
-
-static jfloat android_view_GLES20DisplayList_getCameraDistance(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getCameraDistance();
-}
-
-static jfloat android_view_GLES20DisplayList_getScaleX(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getScaleX();
-}
-
-static jfloat android_view_GLES20DisplayList_getScaleY(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getScaleY();
-}
-
-static jfloat android_view_GLES20DisplayList_getTranslationX(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getTranslationX();
-}
-
-static jfloat android_view_GLES20DisplayList_getTranslationY(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getTranslationY();
-}
-
-static jfloat android_view_GLES20DisplayList_getRotation(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getRotation();
-}
-
-static jfloat android_view_GLES20DisplayList_getRotationX(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getRotationX();
-}
-
-static jfloat android_view_GLES20DisplayList_getRotationY(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getRotationY();
-}
-
-static jfloat android_view_GLES20DisplayList_getPivotX(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getPivotX();
-}
-
-static jfloat android_view_GLES20DisplayList_getPivotY(JNIEnv* env,
-        jobject clazz, DisplayList* displayList) {
-    return displayList->getPivotY();
-}
-
-#endif // USE_OPENGL_RENDERER
-
-// ----------------------------------------------------------------------------
-// JNI Glue
-// ----------------------------------------------------------------------------
-
-const char* const kClassPathName = "android/view/GLES20DisplayList";
-
-static JNINativeMethod gMethods[] = {
-#ifdef USE_OPENGL_RENDERER
-    { "nDestroyDisplayList",   "(I)V",   (void*) android_view_GLES20DisplayList_destroyDisplayList },
-    { "nGetDisplayListSize",   "(I)I",   (void*) android_view_GLES20DisplayList_getDisplayListSize },
-    { "nSetDisplayListName",   "(ILjava/lang/String;)V",
-            (void*) android_view_GLES20DisplayList_setDisplayListName },
-    { "nOutput",               "(I)V",  (void*) android_view_GLES20DisplayList_output },
-
-    { "nReset",                "(I)V",   (void*) android_view_GLES20DisplayList_reset },
-    { "nSetCaching",           "(IZ)V",  (void*) android_view_GLES20DisplayList_setCaching },
-    { "nSetStaticMatrix",      "(II)V",  (void*) android_view_GLES20DisplayList_setStaticMatrix },
-    { "nSetAnimationMatrix",   "(II)V",  (void*) android_view_GLES20DisplayList_setAnimationMatrix },
-    { "nSetClipToBounds",      "(IZ)V",  (void*) android_view_GLES20DisplayList_setClipToBounds },
-    { "nSetAlpha",             "(IF)V",  (void*) android_view_GLES20DisplayList_setAlpha },
-    { "nSetHasOverlappingRendering", "(IZ)V",
-            (void*) android_view_GLES20DisplayList_setHasOverlappingRendering },
-    { "nSetTranslationX",      "(IF)V",  (void*) android_view_GLES20DisplayList_setTranslationX },
-    { "nSetTranslationY",      "(IF)V",  (void*) android_view_GLES20DisplayList_setTranslationY },
-    { "nSetTranslationZ",      "(IF)V",  (void*) android_view_GLES20DisplayList_setTranslationZ },
-    { "nSetRotation",          "(IF)V",  (void*) android_view_GLES20DisplayList_setRotation },
-    { "nSetRotationX",         "(IF)V",  (void*) android_view_GLES20DisplayList_setRotationX },
-    { "nSetRotationY",         "(IF)V",  (void*) android_view_GLES20DisplayList_setRotationY },
-    { "nSetScaleX",            "(IF)V",  (void*) android_view_GLES20DisplayList_setScaleX },
-    { "nSetScaleY",            "(IF)V",  (void*) android_view_GLES20DisplayList_setScaleY },
-    { "nSetTransformationInfo","(IFFFFFFFFF)V",
-            (void*) android_view_GLES20DisplayList_setTransformationInfo },
-    { "nSetPivotX",            "(IF)V",  (void*) android_view_GLES20DisplayList_setPivotX },
-    { "nSetPivotY",            "(IF)V",  (void*) android_view_GLES20DisplayList_setPivotY },
-    { "nSetCameraDistance",    "(IF)V",  (void*) android_view_GLES20DisplayList_setCameraDistance },
-    { "nSetLeft",              "(II)V",  (void*) android_view_GLES20DisplayList_setLeft },
-    { "nSetTop",               "(II)V",  (void*) android_view_GLES20DisplayList_setTop },
-    { "nSetRight",             "(II)V",  (void*) android_view_GLES20DisplayList_setRight },
-    { "nSetBottom",            "(II)V",  (void*) android_view_GLES20DisplayList_setBottom },
-    { "nSetLeftTopRightBottom","(IIIII)V",
-            (void*) android_view_GLES20DisplayList_setLeftTopRightBottom },
-    { "nOffsetLeftAndRight",   "(IF)V",  (void*) android_view_GLES20DisplayList_offsetLeftAndRight },
-    { "nOffsetTopAndBottom",   "(IF)V",  (void*) android_view_GLES20DisplayList_offsetTopAndBottom },
-
-
-    { "nGetMatrix",               "(II)V", (void*) android_view_GLES20DisplayList_getMatrix },
-    { "nHasOverlappingRendering", "(I)Z",  (void*) android_view_GLES20DisplayList_hasOverlappingRendering },
-    { "nGetAlpha",                "(I)F",  (void*) android_view_GLES20DisplayList_getAlpha },
-    { "nGetLeft",                 "(I)F",  (void*) android_view_GLES20DisplayList_getLeft },
-    { "nGetTop",                  "(I)F",  (void*) android_view_GLES20DisplayList_getTop },
-    { "nGetRight",                "(I)F",  (void*) android_view_GLES20DisplayList_getRight },
-    { "nGetBottom",               "(I)F",  (void*) android_view_GLES20DisplayList_getBottom },
-    { "nGetCameraDistance",       "(I)F",  (void*) android_view_GLES20DisplayList_getCameraDistance },
-    { "nGetScaleX",               "(I)F",  (void*) android_view_GLES20DisplayList_getScaleX },
-    { "nGetScaleY",               "(I)F",  (void*) android_view_GLES20DisplayList_getScaleY },
-    { "nGetTranslationX",         "(I)F",  (void*) android_view_GLES20DisplayList_getTranslationX },
-    { "nGetTranslationY",         "(I)F",  (void*) android_view_GLES20DisplayList_getTranslationY },
-    { "nGetRotation",             "(I)F",  (void*) android_view_GLES20DisplayList_getRotation },
-    { "nGetRotationX",            "(I)F",  (void*) android_view_GLES20DisplayList_getRotationX },
-    { "nGetRotationY",            "(I)F",  (void*) android_view_GLES20DisplayList_getRotationY },
-    { "nGetPivotX",               "(I)F",  (void*) android_view_GLES20DisplayList_getPivotX },
-    { "nGetPivotY",               "(I)F",  (void*) android_view_GLES20DisplayList_getPivotY },
-#endif
-};
-
-#ifdef USE_OPENGL_RENDERER
-    #define FIND_CLASS(var, className) \
-            var = env->FindClass(className); \
-            LOG_FATAL_IF(! var, "Unable to find class " className);
-
-    #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
-            var = env->GetMethodID(clazz, methodName, methodDescriptor); \
-            LOG_FATAL_IF(! var, "Unable to find method " methodName);
-#else
-    #define FIND_CLASS(var, className)
-    #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor)
-#endif
-
-int register_android_view_GLES20DisplayList(JNIEnv* env) {
-    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
-}
-
-};
-
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 20eb356..9db35fc 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -98,7 +98,7 @@
     private static SimpleDateFormat sFormatter;
 
     static {
-        System.loadLibrary("exif_jni");
+        System.loadLibrary("jhead_jni");
         sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
         sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
     }
diff --git a/preloaded-classes b/preloaded-classes
index 342126d..42412c6 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -977,14 +977,13 @@
 android.view.DisplayInfo
 android.view.DisplayInfo$1
 android.view.DisplayList
+android.view.DisplayList$DisplayListFinalizer
 android.view.FallbackEventHandler
 android.view.FocusFinder
 android.view.FocusFinder$1
 android.view.FocusFinder$SequentialFocusComparator
 android.view.GLES20Canvas
 android.view.GLES20Canvas$CanvasFinalizer
-android.view.GLES20DisplayList
-android.view.GLES20DisplayList$DisplayListFinalizer
 android.view.GLES20Layer
 android.view.GLES20Layer$Finalizer
 android.view.GLES20RecordingCanvas
diff --git a/services/Android.mk b/services/Android.mk
index 6ee7fca..8bd7629 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -7,7 +7,7 @@
 LOCAL_SRC_FILES :=
 
 # TODO: Move this to the product makefiles
-REQUIRED_SERVICES := core appwidget backup devicepolicy accessibility print
+REQUIRED_SERVICES := core accessibility appwidget backup devicepolicy print
 
 include $(patsubst %,$(LOCAL_PATH)/%/java/service.mk,$(REQUIRED_SERVICES))
 
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
index 6fd8871..3378e3d 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
@@ -38,6 +38,7 @@
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -46,21 +47,19 @@
 
 
 /**
- * Redirects calls to this service to the instance of the service for the appropriate user.
+ * SystemService that publishes an IAppWidgetService.
  */
-public class AppWidgetService extends IAppWidgetService.Stub
-{
-    private static final String TAG = "AppWidgetService";
+public class AppWidgetService extends SystemService {
+
+    static final String TAG = "AppWidgetService";
 
     Context mContext;
-    Locale mLocale;
-    PackageManager mPackageManager;
-    boolean mSafeMode;
-    private final Handler mSaveStateHandler;
+    Handler mSaveStateHandler;
 
-    private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
+    SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
 
-    public AppWidgetService(Context context) {
+    @Override
+    public void onCreate(Context context) {
         mContext = context;
 
         mSaveStateHandler = BackgroundThread.getHandler();
@@ -70,294 +69,316 @@
         mAppWidgetServices.append(0, primary);
     }
 
-    public void systemRunning(boolean safeMode) {
-        mSafeMode = safeMode;
-
-        mAppWidgetServices.get(0).systemReady(safeMode);
-
-        // Register for the boot completed broadcast, so we can send the
-        // ENABLE broacasts. If we try to send them now, they time out,
-        // because the system isn't ready to handle them yet.
-        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
-
-        // Register for configuration changes so we can update the names
-        // of the widgets when the locale changes.
-        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
-
-        // Register for broadcasts about package install, etc., so we can
-        // update the provider list.
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addDataScheme("package");
-        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                filter, null, null);
-        // Register for events related to sdcard installation.
-        IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                sdFilter, null, null);
-
-        IntentFilter userFilter = new IntentFilter();
-        userFilter.addAction(Intent.ACTION_USER_REMOVED);
-        userFilter.addAction(Intent.ACTION_USER_STOPPING);
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
-                    onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                            UserHandle.USER_NULL));
-                } else if (Intent.ACTION_USER_STOPPING.equals(intent.getAction())) {
-                    onUserStopping(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                            UserHandle.USER_NULL));
-                }
-            }
-        }, userFilter);
+    @Override
+    public void onStart() {
+        publishBinderService(Context.APPWIDGET_SERVICE, mServiceImpl);
     }
 
     @Override
-    public int allocateAppWidgetId(String packageName, int hostId, int userId)
-            throws RemoteException {
-        return getImplForUser(userId).allocateAppWidgetId(packageName, hostId);
-    }
-
-    @Override
-    public int[] getAppWidgetIdsForHost(int hostId, int userId) throws RemoteException {
-        return getImplForUser(userId).getAppWidgetIdsForHost(hostId);
-    }
-
-    @Override
-    public void deleteAppWidgetId(int appWidgetId, int userId) throws RemoteException {
-        getImplForUser(userId).deleteAppWidgetId(appWidgetId);
-    }
-
-    @Override
-    public void deleteHost(int hostId, int userId) throws RemoteException {
-        getImplForUser(userId).deleteHost(hostId);
-    }
-
-    @Override
-    public void deleteAllHosts(int userId) throws RemoteException {
-        getImplForUser(userId).deleteAllHosts();
-    }
-
-    @Override
-    public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options, int userId)
-            throws RemoteException {
-        getImplForUser(userId).bindAppWidgetId(appWidgetId, provider, options);
-    }
-
-    @Override
-    public boolean bindAppWidgetIdIfAllowed(
-            String packageName, int appWidgetId, ComponentName provider, Bundle options, int userId)
-                    throws RemoteException {
-        return getImplForUser(userId).bindAppWidgetIdIfAllowed(
-                packageName, appWidgetId, provider, options);
-    }
-
-    @Override
-    public boolean hasBindAppWidgetPermission(String packageName, int userId)
-            throws RemoteException {
-        return getImplForUser(userId).hasBindAppWidgetPermission(packageName);
-    }
-
-    @Override
-    public void setBindAppWidgetPermission(String packageName, boolean permission, int userId)
-            throws RemoteException {
-        getImplForUser(userId).setBindAppWidgetPermission(packageName, permission);
-    }
-
-    @Override
-    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection,
-            int userId) throws RemoteException {
-        getImplForUser(userId).bindRemoteViewsService(appWidgetId, intent, connection);
-    }
-
-    @Override
-    public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
-            List<RemoteViews> updatedViews, int userId) throws RemoteException {
-        return getImplForUser(userId).startListening(host, packageName, hostId, updatedViews);
-    }
-
-    public void onUserRemoved(int userId) {
-        if (userId < 1) return;
-        synchronized (mAppWidgetServices) {
-            AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
-            mAppWidgetServices.remove(userId);
-
-            if (impl == null) {
-                AppWidgetServiceImpl.getSettingsFile(userId).delete();
-            } else {
-                impl.onUserRemoved();
-            }
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+            mServiceImpl.systemRunning(isSafeMode());
         }
     }
 
-    public void onUserStopping(int userId) {
-        if (userId < 1) return;
-        synchronized (mAppWidgetServices) {
-            AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
-            if (impl != null) {
+    private final AppWidgetServiceStub mServiceImpl = new AppWidgetServiceStub();
+
+    private class AppWidgetServiceStub extends IAppWidgetService.Stub {
+
+        private boolean mSafeMode;
+        private Locale mLocale;
+        private PackageManager mPackageManager;
+
+        public void systemRunning(boolean safeMode) {
+            mSafeMode = safeMode;
+
+            mAppWidgetServices.get(0).systemReady(safeMode);
+
+            // Register for the boot completed broadcast, so we can send the
+            // ENABLE broacasts. If we try to send them now, they time out,
+            // because the system isn't ready to handle them yet.
+            mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+                    new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
+
+            // Register for configuration changes so we can update the names
+            // of the widgets when the locale changes.
+            mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+                    new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
+
+            // Register for broadcasts about package install, etc., so we can
+            // update the provider list.
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addDataScheme("package");
+            mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+                    filter, null, null);
+            // Register for events related to sdcard installation.
+            IntentFilter sdFilter = new IntentFilter();
+            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+            mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+                    sdFilter, null, null);
+
+            IntentFilter userFilter = new IntentFilter();
+            userFilter.addAction(Intent.ACTION_USER_REMOVED);
+            userFilter.addAction(Intent.ACTION_USER_STOPPING);
+            mContext.registerReceiver(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
+                        onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                                UserHandle.USER_NULL));
+                    } else if (Intent.ACTION_USER_STOPPING.equals(intent.getAction())) {
+                        onUserStopping(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                                UserHandle.USER_NULL));
+                    }
+                }
+            }, userFilter);
+        }
+
+        @Override
+        public int allocateAppWidgetId(String packageName, int hostId, int userId)
+                throws RemoteException {
+            return getImplForUser(userId).allocateAppWidgetId(packageName, hostId);
+        }
+
+        @Override
+        public int[] getAppWidgetIdsForHost(int hostId, int userId) throws RemoteException {
+            return getImplForUser(userId).getAppWidgetIdsForHost(hostId);
+        }
+
+        @Override
+        public void deleteAppWidgetId(int appWidgetId, int userId) throws RemoteException {
+            getImplForUser(userId).deleteAppWidgetId(appWidgetId);
+        }
+
+        @Override
+        public void deleteHost(int hostId, int userId) throws RemoteException {
+            getImplForUser(userId).deleteHost(hostId);
+        }
+
+        @Override
+        public void deleteAllHosts(int userId) throws RemoteException {
+            getImplForUser(userId).deleteAllHosts();
+        }
+
+        @Override
+        public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options,
+                int userId) throws RemoteException {
+            getImplForUser(userId).bindAppWidgetId(appWidgetId, provider, options);
+        }
+
+        @Override
+        public boolean bindAppWidgetIdIfAllowed(
+                String packageName, int appWidgetId, ComponentName provider, Bundle options,
+                int userId) throws RemoteException {
+            return getImplForUser(userId).bindAppWidgetIdIfAllowed(
+                    packageName, appWidgetId, provider, options);
+        }
+
+        @Override
+        public boolean hasBindAppWidgetPermission(String packageName, int userId)
+                throws RemoteException {
+            return getImplForUser(userId).hasBindAppWidgetPermission(packageName);
+        }
+
+        @Override
+        public void setBindAppWidgetPermission(String packageName, boolean permission, int userId)
+                throws RemoteException {
+            getImplForUser(userId).setBindAppWidgetPermission(packageName, permission);
+        }
+
+        @Override
+        public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection,
+                int userId) throws RemoteException {
+            getImplForUser(userId).bindRemoteViewsService(appWidgetId, intent, connection);
+        }
+
+        @Override
+        public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
+                List<RemoteViews> updatedViews, int userId) throws RemoteException {
+            return getImplForUser(userId).startListening(host, packageName, hostId, updatedViews);
+        }
+
+        public void onUserRemoved(int userId) {
+            if (userId < 1) return;
+            synchronized (mAppWidgetServices) {
+                AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
                 mAppWidgetServices.remove(userId);
-                impl.onUserStopping();
-            }
-        }
-    }
 
-    private void checkPermission(int userId) {
-        int realUserId = ActivityManager.handleIncomingUser(
-                Binder.getCallingPid(),
-                Binder.getCallingUid(),
-                userId,
-                false, /* allowAll */
-                true, /* requireFull */
-                this.getClass().getSimpleName(),
-                this.getClass().getPackage().getName());
-    }
-
-    private AppWidgetServiceImpl getImplForUser(int userId) {
-        checkPermission(userId);
-        boolean sendInitial = false;
-        AppWidgetServiceImpl service;
-        synchronized (mAppWidgetServices) {
-            service = mAppWidgetServices.get(userId);
-            if (service == null) {
-                Slog.i(TAG, "Unable to find AppWidgetServiceImpl for user " + userId + ", adding");
-                // TODO: Verify that it's a valid user
-                service = new AppWidgetServiceImpl(mContext, userId, mSaveStateHandler);
-                service.systemReady(mSafeMode);
-                // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
-                mAppWidgetServices.append(userId, service);
-                sendInitial = true;
-            }
-        }
-        if (sendInitial) {
-            service.sendInitialBroadcasts();
-        }
-        return service;
-    }
-
-    @Override
-    public int[] getAppWidgetIds(ComponentName provider, int userId) throws RemoteException {
-        return getImplForUser(userId).getAppWidgetIds(provider);
-    }
-
-    @Override
-    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId, int userId)
-            throws RemoteException {
-        return getImplForUser(userId).getAppWidgetInfo(appWidgetId);
-    }
-
-    @Override
-    public RemoteViews getAppWidgetViews(int appWidgetId, int userId) throws RemoteException {
-        return getImplForUser(userId).getAppWidgetViews(appWidgetId);
-    }
-
-    @Override
-    public void updateAppWidgetOptions(int appWidgetId, Bundle options, int userId) {
-        getImplForUser(userId).updateAppWidgetOptions(appWidgetId, options);
-    }
-
-    @Override
-    public Bundle getAppWidgetOptions(int appWidgetId, int userId) {
-        return getImplForUser(userId).getAppWidgetOptions(appWidgetId);
-    }
-
-    @Override
-    public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter, int userId)
-            throws RemoteException {
-        return getImplForUser(userId).getInstalledProviders(categoryFilter);
-    }
-
-    @Override
-    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId, int userId)
-            throws RemoteException {
-        getImplForUser(userId).notifyAppWidgetViewDataChanged(
-                appWidgetIds, viewId);
-    }
-
-    @Override
-    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views, int userId)
-            throws RemoteException {
-        getImplForUser(userId).partiallyUpdateAppWidgetIds(
-                appWidgetIds, views);
-    }
-
-    @Override
-    public void stopListening(int hostId, int userId) throws RemoteException {
-        getImplForUser(userId).stopListening(hostId);
-    }
-
-    @Override
-    public void unbindRemoteViewsService(int appWidgetId, Intent intent, int userId)
-            throws RemoteException {
-        getImplForUser(userId).unbindRemoteViewsService(
-                appWidgetId, intent);
-    }
-
-    @Override
-    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views, int userId)
-            throws RemoteException {
-        getImplForUser(userId).updateAppWidgetIds(appWidgetIds, views);
-    }
-
-    @Override
-    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views, int userId)
-            throws RemoteException {
-        getImplForUser(userId).updateAppWidgetProvider(provider, views);
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
-
-        // Dump the state of all the app widget providers
-        synchronized (mAppWidgetServices) {
-            IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
-            for (int i = 0; i < mAppWidgetServices.size(); i++) {
-                pw.println("User: " + mAppWidgetServices.keyAt(i));
-                ipw.increaseIndent();
-                AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
-                service.dump(fd, ipw, args);
-                ipw.decreaseIndent();
-            }
-        }
-    }
-
-    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            // Slog.d(TAG, "received " + action);
-            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                if (userId >= 0) {
-                    getImplForUser(userId).sendInitialBroadcasts();
+                if (impl == null) {
+                    AppWidgetServiceImpl.getSettingsFile(userId).delete();
                 } else {
-                    Slog.w(TAG, "Incorrect user handle supplied in " + intent);
+                    impl.onUserRemoved();
                 }
-            } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+            }
+        }
+
+        public void onUserStopping(int userId) {
+            if (userId < 1) return;
+            synchronized (mAppWidgetServices) {
+                AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
+                if (impl != null) {
+                    mAppWidgetServices.remove(userId);
+                    impl.onUserStopping();
+                }
+            }
+        }
+
+        private void checkPermission(int userId) {
+            int realUserId = ActivityManager.handleIncomingUser(
+                    Binder.getCallingPid(),
+                    Binder.getCallingUid(),
+                    userId,
+                    false, /* allowAll */
+                    true, /* requireFull */
+                    this.getClass().getSimpleName(),
+                    this.getClass().getPackage().getName());
+        }
+
+        private AppWidgetServiceImpl getImplForUser(int userId) {
+            checkPermission(userId);
+            boolean sendInitial = false;
+            AppWidgetServiceImpl service;
+            synchronized (mAppWidgetServices) {
+                service = mAppWidgetServices.get(userId);
+                if (service == null) {
+                    Slog.i(TAG, "Unable to find AppWidgetServiceImpl for user " + userId
+                            + ", adding");
+                    // TODO: Verify that it's a valid user
+                    service = new AppWidgetServiceImpl(mContext, userId, mSaveStateHandler);
+                    service.systemReady(mSafeMode);
+                    // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
+                    mAppWidgetServices.append(userId, service);
+                    sendInitial = true;
+                }
+            }
+            if (sendInitial) {
+                service.sendInitialBroadcasts();
+            }
+            return service;
+        }
+
+        @Override
+        public int[] getAppWidgetIds(ComponentName provider, int userId) throws RemoteException {
+            return getImplForUser(userId).getAppWidgetIds(provider);
+        }
+
+        @Override
+        public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId, int userId)
+                throws RemoteException {
+            return getImplForUser(userId).getAppWidgetInfo(appWidgetId);
+        }
+
+        @Override
+        public RemoteViews getAppWidgetViews(int appWidgetId, int userId) throws RemoteException {
+            return getImplForUser(userId).getAppWidgetViews(appWidgetId);
+        }
+
+        @Override
+        public void updateAppWidgetOptions(int appWidgetId, Bundle options, int userId) {
+            getImplForUser(userId).updateAppWidgetOptions(appWidgetId, options);
+        }
+
+        @Override
+        public Bundle getAppWidgetOptions(int appWidgetId, int userId) {
+            return getImplForUser(userId).getAppWidgetOptions(appWidgetId);
+        }
+
+        @Override
+        public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter, int userId)
+                throws RemoteException {
+            return getImplForUser(userId).getInstalledProviders(categoryFilter);
+        }
+
+        @Override
+        public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId, int userId)
+                throws RemoteException {
+            getImplForUser(userId).notifyAppWidgetViewDataChanged(
+                    appWidgetIds, viewId);
+        }
+
+        @Override
+        public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views, int userId)
+                throws RemoteException {
+            getImplForUser(userId).partiallyUpdateAppWidgetIds(
+                    appWidgetIds, views);
+        }
+
+        @Override
+        public void stopListening(int hostId, int userId) throws RemoteException {
+            getImplForUser(userId).stopListening(hostId);
+        }
+
+        @Override
+        public void unbindRemoteViewsService(int appWidgetId, Intent intent, int userId)
+                throws RemoteException {
+            getImplForUser(userId).unbindRemoteViewsService(
+                    appWidgetId, intent);
+        }
+
+        @Override
+        public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views, int userId)
+                throws RemoteException {
+            getImplForUser(userId).updateAppWidgetIds(appWidgetIds, views);
+        }
+
+        @Override
+        public void updateAppWidgetProvider(ComponentName provider, RemoteViews views, int userId)
+                throws RemoteException {
+            getImplForUser(userId).updateAppWidgetProvider(provider, views);
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+
+            // Dump the state of all the app widget providers
+            synchronized (mAppWidgetServices) {
+                IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
                 for (int i = 0; i < mAppWidgetServices.size(); i++) {
+                    pw.println("User: " + mAppWidgetServices.keyAt(i));
+                    ipw.increaseIndent();
                     AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
-                    service.onConfigurationChanged();
+                    service.dump(fd, ipw, args);
+                    ipw.decreaseIndent();
                 }
-            } else {
-                int sendingUser = getSendingUserId();
-                if (sendingUser == UserHandle.USER_ALL) {
+            }
+        }
+
+        BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                // Slog.d(TAG, "received " + action);
+                if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+                    int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+                    if (userId >= 0) {
+                        getImplForUser(userId).sendInitialBroadcasts();
+                    } else {
+                        Slog.w(TAG, "Incorrect user handle supplied in " + intent);
+                    }
+                } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
                     for (int i = 0; i < mAppWidgetServices.size(); i++) {
                         AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
-                        service.onBroadcastReceived(intent);
+                        service.onConfigurationChanged();
                     }
                 } else {
-                    AppWidgetServiceImpl service = mAppWidgetServices.get(sendingUser);
-                    if (service != null) {
-                        service.onBroadcastReceived(intent);
+                    int sendingUser = getSendingUserId();
+                    if (sendingUser == UserHandle.USER_ALL) {
+                        for (int i = 0; i < mAppWidgetServices.size(); i++) {
+                            AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+                            service.onBroadcastReceived(intent);
+                        }
+                    } else {
+                        AppWidgetServiceImpl service = mAppWidgetServices.get(sendingUser);
+                        if (service != null) {
+                            service.onBroadcastReceived(intent);
+                        }
                     }
                 }
             }
-        }
-    };
+        };
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 18c8ec4..9277cce2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -83,6 +83,7 @@
 import com.android.internal.backup.IBackupTransport;
 import com.android.internal.backup.IObbBackupService;
 import com.android.server.EventLogTags;
+import com.android.server.SystemService;
 import com.android.server.backup.PackageManagerBackupAgent.Metadata;
 
 import java.io.BufferedInputStream;
@@ -136,6 +137,7 @@
 import javax.crypto.spec.SecretKeySpec;
 
 public class BackupManagerService extends IBackupManager.Stub {
+
     private static final String TAG = "BackupManagerService";
     private static final boolean DEBUG = true;
     private static final boolean MORE_DEBUG = false;
@@ -6089,7 +6091,7 @@
                     }
                 }
 
-                // clean up the BackupManagerService side of the bookkeeping
+                // clean up the BackupManagerImpl side of the bookkeeping
                 // and cancel any pending timeout message
                 mBackupManager.clearRestoreSession(mSession);
             }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerSystemService.java b/services/backup/java/com/android/server/backup/BackupManagerSystemService.java
new file mode 100644
index 0000000..db2c94a
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/BackupManagerSystemService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.android.server.backup;
+
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+public class BackupManagerSystemService extends SystemService {
+    private BackupManagerService mBackupManagerImpl;
+
+    @Override
+    public void onCreate(Context context) {
+        mBackupManagerImpl = new BackupManagerService(context);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.BACKUP_SERVICE, mBackupManagerImpl);
+    }
+}
+
diff --git a/services/core/java/com/android/server/SystemServer.java b/services/core/java/com/android/server/SystemServer.java
index 38f4c78d..10adc46 100644
--- a/services/core/java/com/android/server/SystemServer.java
+++ b/services/core/java/com/android/server/SystemServer.java
@@ -31,6 +31,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -52,11 +53,8 @@
 import com.android.server.accounts.AccountManagerService;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.BatteryStatsService;
-import com.android.server.appwidget.AppWidgetService;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.clipboard.ClipboardService;
 import com.android.server.content.ContentService;
-import com.android.server.devicepolicy.DevicePolicyManagerService;
 import com.android.server.display.DisplayManagerService;
 import com.android.server.dreams.DreamManagerService;
 import com.android.server.input.InputManagerService;
@@ -72,7 +70,6 @@
 import com.android.server.pm.UserManagerService;
 import com.android.server.power.PowerManagerService;
 import com.android.server.power.ShutdownThread;
-import com.android.server.print.PrintManagerService;
 import com.android.server.search.SearchManagerService;
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.storage.DeviceStorageMonitorService;
@@ -98,6 +95,19 @@
 
     ContentResolver mContentResolver;
 
+    /*
+     * Implementation class names. TODO: Move them to a codegen class or load
+     * them from the build system somehow.
+     */
+    private static final String BACKUP_MANAGER_SERVICE_CLASS =
+            "com.android.server.backup.BackupManagerSystemService";
+    private static final String DEVICE_POLICY_MANAGER_SERVICE_CLASS =
+            "com.android.server.devicepolicy.DevicePolicyManagerSystemService";
+    private static final String APPWIDGET_SERVICE_CLASS =
+            "com.android.server.appwidget.AppWidgetService";
+    private static final String PRINT_MANAGER_SERVICE_CLASS =
+            "com.android.server.print.PrintManagerService";
+
     void reportWtf(String msg, Throwable e) {
         Slog.w(TAG, "***********************************************");
         Log.wtf(TAG, "BOOT FAILURE " + msg, e);
@@ -357,11 +367,9 @@
             Slog.e("System", "************ Failure starting core service", e);
         }
 
-        DevicePolicyManagerService devicePolicy = null;
         StatusBarManagerService statusBar = null;
         INotificationManager notification = null;
         InputMethodManagerService imm = null;
-        AppWidgetService appWidget = null;
         WallpaperManagerService wallpaper = null;
         LocationManagerService location = null;
         CountryDetectorService countryDetector = null;
@@ -369,7 +377,6 @@
         LockSettingsService lockSettings = null;
         DreamManagerService dreamy = null;
         AssetAtlasService atlas = null;
-        PrintManagerService printManager = null;
         MediaRouterService mediaRouter = null;
 
         // Bring up services needed for UI.
@@ -441,8 +448,7 @@
 
                 try {
                     Slog.i(TAG, "Device Policy");
-                    devicePolicy = new DevicePolicyManagerService(context);
-                    ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy);
+                    systemServiceManager.startService(DEVICE_POLICY_MANAGER_SERVICE_CLASS);
                 } catch (Throwable e) {
                     reportWtf("starting DevicePolicyService", e);
                 }
@@ -692,16 +698,14 @@
             if (!disableNonCoreServices) {
                 try {
                     Slog.i(TAG, "Backup Service");
-                    ServiceManager.addService(Context.BACKUP_SERVICE,
-                            new BackupManagerService(context));
+                    systemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
                 } catch (Throwable e) {
                     Slog.e(TAG, "Failure starting Backup Service", e);
                 }
 
                 try {
                     Slog.i(TAG, "AppWidget Service");
-                    appWidget = new AppWidgetService(context);
-                    ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget);
+                    systemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
                 } catch (Throwable e) {
                     reportWtf("starting AppWidget Service", e);
                 }
@@ -792,8 +796,7 @@
 
             try {
                 Slog.i(TAG, "Print Service");
-                printManager = new PrintManagerService(context);
-                ServiceManager.addService(Context.PRINT_SERVICE, printManager);
+                systemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
             } catch (Throwable e) {
                 reportWtf("starting Print Service", e);
             }
@@ -839,13 +842,8 @@
             }
         }
 
-        if (devicePolicy != null) {
-            try {
-                devicePolicy.systemReady();
-            } catch (Throwable e) {
-                reportWtf("making Device Policy Service ready", e);
-            }
-        }
+        // Needed by DevicePolicyManager for initialization
+        systemServiceManager.startBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);
 
         systemServiceManager.startBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
 
@@ -896,7 +894,6 @@
         final ConnectivityService connectivityF = connectivity;
         final DockObserver dockF = dock;
         final UsbService usbF = usb;
-        final AppWidgetService appWidgetF = appWidget;
         final WallpaperManagerService wallpaperF = wallpaper;
         final InputMethodManagerService immF = imm;
         final RecognitionManagerService recognitionF = recognition;
@@ -910,7 +907,6 @@
         final AssetAtlasService atlasF = atlas;
         final InputManagerService inputManagerF = inputManager;
         final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
-        final PrintManagerService printManagerF = printManager;
         final MediaRouterService mediaRouterF = mediaRouter;
 
         // We now tell the activity manager it is okay to run third party
@@ -984,11 +980,6 @@
                 systemServiceManager.startBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
 
                 try {
-                    if (appWidgetF != null) appWidgetF.systemRunning(safeMode);
-                } catch (Throwable e) {
-                    reportWtf("Notifying AppWidgetService running", e);
-                }
-                try {
                     if (wallpaperF != null) wallpaperF.systemRunning();
                 } catch (Throwable e) {
                     reportWtf("Notifying WallpaperService running", e);
@@ -1048,12 +1039,6 @@
                 }
 
                 try {
-                    if (printManagerF != null) printManagerF.systemRuning();
-                } catch (Throwable e) {
-                    reportWtf("Notifying PrintManagerService running", e);
-                }
-
-                try {
                     if (mediaRouterF != null) mediaRouterF.systemRunning();
                 } catch (Throwable e) {
                     reportWtf("Notifying MediaRouterService running", e);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerSystemService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerSystemService.java
new file mode 100644
index 0000000..160aa39
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerSystemService.java
@@ -0,0 +1,46 @@
+/*
+ * 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.android.server.devicepolicy;
+
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+/**
+ * SystemService wrapper for the DevicePolicyManager implementation. Publishes
+ * Context.DEVICE_POLICY_SERVICE.
+ */
+public final class DevicePolicyManagerSystemService extends SystemService {
+    private DevicePolicyManagerService mDevicePolicyManagerImpl;
+
+    @Override
+    public void onCreate(Context context) {
+        mDevicePolicyManagerImpl = new DevicePolicyManagerService(context);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManagerImpl);
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_LOCK_SETTINGS_READY) {
+            mDevicePolicyManagerImpl.systemReady();
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index e24fdb5..7f146a7 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -51,6 +51,8 @@
 import com.android.internal.R;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
+import com.android.server.SystemService;
+import com.android.server.devicepolicy.DevicePolicyManagerService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -58,604 +60,633 @@
 import java.util.List;
 import java.util.Set;
 
-public final class PrintManagerService extends IPrintManager.Stub {
+/**
+ * SystemService wrapper for the PrintManager implementation. Publishes
+ * Context.PRINT_SERVICE.
+ * PrintManager implementation is contained within.
+ */
 
-    private static final char COMPONENT_NAME_SEPARATOR = ':';
+public final class PrintManagerService extends SystemService {
 
-    private static final String EXTRA_PRINT_SERVICE_COMPONENT_NAME =
-            "EXTRA_PRINT_SERVICE_COMPONENT_NAME";
+    private PrintManagerImpl mPrintManagerImpl;
 
-    private final Object mLock = new Object();
-
-    private final Context mContext;
-
-    private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
-
-    private int mCurrentUserId = UserHandle.USER_OWNER;
-
-    public PrintManagerService(Context context) {
-        mContext = context;
-        registerContentObservers();
-        registerBoradcastReceivers();
+    @Override
+    public void onCreate(Context context) {
+        mPrintManagerImpl = new PrintManagerImpl(context);
+    }
+    @Override
+    public void onStart() {
+        publishBinderService(Context.PRINT_SERVICE, mPrintManagerImpl);
     }
 
-    public void systemRuning() {
-        BackgroundThread.getHandler().post(new Runnable() {
-            @Override
-            public void run() {
-                final UserState userState;
-                synchronized (mLock) {
-                    userState = getCurrentUserStateLocked();
-                    userState.updateIfNeededLocked();
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+            mPrintManagerImpl.systemRunning();
+        }
+    }
+
+    class PrintManagerImpl extends IPrintManager.Stub {
+        private static final char COMPONENT_NAME_SEPARATOR = ':';
+
+        private static final String EXTRA_PRINT_SERVICE_COMPONENT_NAME =
+                "EXTRA_PRINT_SERVICE_COMPONENT_NAME";
+
+        private final Object mLock = new Object();
+
+        private final Context mContext;
+
+        private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
+
+        private int mCurrentUserId = UserHandle.USER_OWNER;
+
+        PrintManagerImpl(Context context) {
+            mContext = context;
+            registerContentObservers();
+            registerBoradcastReceivers();
+        }
+
+        public void systemRunning() {
+            BackgroundThread.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    final UserState userState;
+                    synchronized (mLock) {
+                        userState = getCurrentUserStateLocked();
+                        userState.updateIfNeededLocked();
+                    }
+                    // This is the first time we switch to this user after boot, so
+                    // now is the time to remove obsolete print jobs since they
+                    // are from the last boot and no application would query them.
+                    userState.removeObsoletePrintJobs();
                 }
-                // This is the first time we switch to this user after boot, so
-                // now is the time to remove obsolete print jobs since they
-                // are from the last boot and no application would query them.
-                userState.removeObsoletePrintJobs();
+            });
+        }
+
+        @Override
+        public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
+                PrintAttributes attributes, String packageName, int appId, int userId) {
+            final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            String resolvedPackageName = resolveCallingPackageNameEnforcingSecurity(packageName);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
             }
-        });
-    }
-
-    @Override
-    public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
-            PrintAttributes attributes, String packageName, int appId, int userId) {
-        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        String resolvedPackageName = resolveCallingPackageNameEnforcingSecurity(packageName);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return userState.print(printJobName, adapter, attributes,
-                    resolvedPackageName, resolvedAppId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public List<PrintJobInfo> getPrintJobInfos(int appId, int userId) {
-        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return userState.getPrintJobInfos(resolvedAppId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId, int userId) {
-        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return userState.getPrintJobInfo(printJobId, resolvedAppId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) {
-        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            userState.cancelPrintJob(printJobId, resolvedAppId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void restartPrintJob(PrintJobId printJobId, int appId, int userId) {
-        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            userState.restartPrintJob(printJobId, resolvedAppId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public List<PrintServiceInfo> getEnabledPrintServices(int userId) {
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return userState.getEnabledPrintServices();
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public List<PrintServiceInfo> getInstalledPrintServices(int userId) {
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return userState.getInstalledPrintServices();
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
-            int userId) {
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            userState.createPrinterDiscoverySession(observer);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
-            int userId) {
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            userState.destroyPrinterDiscoverySession(observer);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
-            List<PrinterId> priorityList, int userId) {
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            userState.startPrinterDiscovery(observer, priorityList);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId) {
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            userState.stopPrinterDiscovery(observer);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void validatePrinters(List<PrinterId> printerIds, int userId) {
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            userState.validatePrinters(printerIds);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void startPrinterStateTracking(PrinterId printerId, int userId) {
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            userState.startPrinterStateTracking(printerId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void stopPrinterStateTracking(PrinterId printerId, int userId) {
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            userState.stopPrinterStateTracking(printerId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,
-            int appId, int userId) throws RemoteException {
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            userState.addPrintJobStateChangeListener(listener, resolvedAppId);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener,
-            int userId) {
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final UserState userState;
-        synchronized (mLock) {
-            userState = getOrCreateUserStateLocked(resolvedUserId);
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            userState.removePrintJobStateChangeListener(listener);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
-                != PackageManager.PERMISSION_GRANTED) {
-            pw.println("Permission Denial: can't dump PrintManager from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
-            return;
-        }
-
-        synchronized (mLock) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                pw.println("PRINT MANAGER STATE (dumpsys print)");
-                final int userStateCount = mUserStates.size();
-                for (int i = 0; i < userStateCount; i++) {
-                    UserState userState = mUserStates.valueAt(i);
-                    userState.dump(fd, pw, "");
-                    pw.println();
-                }
+                return userState.print(printJobName, adapter, attributes,
+                        resolvedPackageName, resolvedAppId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
         }
-    }
 
-    private void registerContentObservers() {
-        final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
-                Settings.Secure.ENABLED_PRINT_SERVICES);
-
-        ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
-            @Override
-            public void onChange(boolean selfChange, Uri uri) {
-                if (enabledPrintServicesUri.equals(uri)) {
-                    synchronized (mLock) {
-                        UserState userState = getCurrentUserStateLocked();
-                        userState.updateIfNeededLocked();
-                    }
-                }
+        @Override
+        public List<PrintJobInfo> getPrintJobInfos(int appId, int userId) {
+            final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
             }
-        };
-
-        mContext.getContentResolver().registerContentObserver(enabledPrintServicesUri,
-                false, observer, UserHandle.USER_ALL);
-    }
-
-    private void registerBoradcastReceivers() {
-        PackageMonitor monitor = new PackageMonitor() {
-            @Override
-            public void onPackageModified(String packageName) {
-                synchronized (mLock) {
-                    boolean servicesChanged = false;
-                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
-                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
-                    while (iterator.hasNext()) {
-                        ComponentName componentName = iterator.next();
-                        if (packageName.equals(componentName.getPackageName())) {
-                            servicesChanged = true;
-                        }
-                    }
-                    if (servicesChanged) {
-                        userState.updateIfNeededLocked();
-                    }
-                }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return userState.getPrintJobInfos(resolvedAppId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
-
-            @Override
-            public void onPackageRemoved(String packageName, int uid) {
-                synchronized (mLock) {
-                    boolean servicesRemoved = false;
-                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
-                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
-                    while (iterator.hasNext()) {
-                        ComponentName componentName = iterator.next();
-                        if (packageName.equals(componentName.getPackageName())) {
-                            iterator.remove();
-                            servicesRemoved = true;
-                        }
-                    }
-                    if (servicesRemoved) {
-                        persistComponentNamesToSettingLocked(
-                                Settings.Secure.ENABLED_PRINT_SERVICES,
-                                userState.getEnabledServices(), getChangingUserId());
-                        userState.updateIfNeededLocked();
-                    }
-                }
-            }
-
-            @Override
-            public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
-                    int uid, boolean doit) {
-                synchronized (mLock) {
-                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
-                    boolean stoppedSomePackages = false;
-                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
-                    while (iterator.hasNext()) {
-                        ComponentName componentName = iterator.next();
-                        String componentPackage = componentName.getPackageName();
-                        for (String stoppedPackage : stoppedPackages) {
-                            if (componentPackage.equals(stoppedPackage)) {
-                                if (!doit) {
-                                    return true;
-                                }
-                                stoppedSomePackages = true;
-                                break;
-                            }
-                        }
-                    }
-                    if (stoppedSomePackages) {
-                        userState.updateIfNeededLocked();
-                    }
-                    return false;
-                }
-            }
-
-            @Override
-            public void onPackageAdded(String packageName, int uid) {
-                Intent intent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
-                intent.setPackage(packageName);
-
-                List<ResolveInfo> installedServices = mContext.getPackageManager()
-                        .queryIntentServicesAsUser(intent, PackageManager.GET_SERVICES,
-                                getChangingUserId());
-
-                if (installedServices == null) {
-                    return;
-                }
-
-                final int installedServiceCount = installedServices.size();
-                for (int i = 0; i < installedServiceCount; i++) {
-                    ServiceInfo serviceInfo = installedServices.get(i).serviceInfo;
-                    ComponentName component = new ComponentName(serviceInfo.packageName,
-                            serviceInfo.name);
-                    String label = serviceInfo.loadLabel(mContext.getPackageManager()).toString();
-                    showEnableInstalledPrintServiceNotification(component, label,
-                            getChangingUserId());
-                }
-            }
-
-            private void persistComponentNamesToSettingLocked(String settingName,
-                    Set<ComponentName> componentNames, int userId) {
-                StringBuilder builder = new StringBuilder();
-                for (ComponentName componentName : componentNames) {
-                    if (builder.length() > 0) {
-                        builder.append(COMPONENT_NAME_SEPARATOR);
-                    }
-                    builder.append(componentName.flattenToShortString());
-                }
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        settingName, builder.toString(), userId);
-            }
-        };
-
-        // package changes
-        monitor.register(mContext, BackgroundThread.getHandler().getLooper(),
-                UserHandle.ALL, true);
-
-        // user changes
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
-        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
-
-        mContext.registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                    switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
-                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
-                }
-            }
-        }, UserHandle.ALL, intentFilter, null, BackgroundThread.getHandler());
-    }
-
-    private UserState getCurrentUserStateLocked() {
-        return getOrCreateUserStateLocked(mCurrentUserId);
-    }
-
-    private UserState getOrCreateUserStateLocked(int userId) {
-        UserState userState = mUserStates.get(userId);
-        if (userState == null) {
-            userState = new UserState(mContext, userId, mLock);
-            mUserStates.put(userId, userState);
         }
-        return userState;
-    }
 
-    private void switchUser(int newUserId) {
-        UserState userState;
-        synchronized (mLock) {
-            if (newUserId == mCurrentUserId) {
+        @Override
+        public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId, int userId) {
+            final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return userState.getPrintJobInfo(printJobId, resolvedAppId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void cancelPrintJob(PrintJobId printJobId, int appId, int userId) {
+            final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.cancelPrintJob(printJobId, resolvedAppId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void restartPrintJob(PrintJobId printJobId, int appId, int userId) {
+            final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.restartPrintJob(printJobId, resolvedAppId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public List<PrintServiceInfo> getEnabledPrintServices(int userId) {
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return userState.getEnabledPrintServices();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public List<PrintServiceInfo> getInstalledPrintServices(int userId) {
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return userState.getInstalledPrintServices();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
+                int userId) {
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.createPrinterDiscoverySession(observer);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
+                int userId) {
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.destroyPrinterDiscoverySession(observer);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
+                List<PrinterId> priorityList, int userId) {
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.startPrinterDiscovery(observer, priorityList);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId) {
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.stopPrinterDiscovery(observer);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void validatePrinters(List<PrinterId> printerIds, int userId) {
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.validatePrinters(printerIds);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void startPrinterStateTracking(PrinterId printerId, int userId) {
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.startPrinterStateTracking(printerId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void stopPrinterStateTracking(PrinterId printerId, int userId) {
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.stopPrinterStateTracking(printerId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,
+                int appId, int userId) throws RemoteException {
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.addPrintJobStateChangeListener(listener, resolvedAppId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener,
+                int userId) {
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.removePrintJobStateChangeListener(listener);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+                pw.println("Permission Denial: can't dump PrintManager from from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid());
                 return;
             }
-            mCurrentUserId = newUserId;
-            userState = mUserStates.get(mCurrentUserId);
+
+            synchronized (mLock) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    pw.println("PRINT MANAGER STATE (dumpsys print)");
+                    final int userStateCount = mUserStates.size();
+                    for (int i = 0; i < userStateCount; i++) {
+                        UserState userState = mUserStates.valueAt(i);
+                        userState.dump(fd, pw, "");
+                        pw.println();
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+
+        private void registerContentObservers() {
+            final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
+                    Settings.Secure.ENABLED_PRINT_SERVICES);
+            ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
+                @Override
+                public void onChange(boolean selfChange, Uri uri) {
+                    if (enabledPrintServicesUri.equals(uri)) {
+                        synchronized (mLock) {
+                            UserState userState = getCurrentUserStateLocked();
+                            userState.updateIfNeededLocked();
+                        }
+                    }
+                }
+            };
+
+            mContext.getContentResolver().registerContentObserver(enabledPrintServicesUri,
+                    false, observer, UserHandle.USER_ALL);
+        }
+
+        private void registerBoradcastReceivers() {
+            PackageMonitor monitor = new PackageMonitor() {
+                @Override
+                public void onPackageModified(String packageName) {
+                    synchronized (mLock) {
+                        boolean servicesChanged = false;
+                        UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+                        Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
+                        while (iterator.hasNext()) {
+                            ComponentName componentName = iterator.next();
+                            if (packageName.equals(componentName.getPackageName())) {
+                                servicesChanged = true;
+                            }
+                        }
+                        if (servicesChanged) {
+                            userState.updateIfNeededLocked();
+                        }
+                    }
+                }
+
+                @Override
+                public void onPackageRemoved(String packageName, int uid) {
+                    synchronized (mLock) {
+                        boolean servicesRemoved = false;
+                        UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+                        Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
+                        while (iterator.hasNext()) {
+                            ComponentName componentName = iterator.next();
+                            if (packageName.equals(componentName.getPackageName())) {
+                                iterator.remove();
+                                servicesRemoved = true;
+                            }
+                        }
+                        if (servicesRemoved) {
+                            persistComponentNamesToSettingLocked(
+                                    Settings.Secure.ENABLED_PRINT_SERVICES,
+                                    userState.getEnabledServices(), getChangingUserId());
+                            userState.updateIfNeededLocked();
+                        }
+                    }
+                }
+
+                @Override
+                public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
+                        int uid, boolean doit) {
+                    synchronized (mLock) {
+                        UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+                        boolean stoppedSomePackages = false;
+                        Iterator<ComponentName> iterator = userState.getEnabledServices()
+                                .iterator();
+                        while (iterator.hasNext()) {
+                            ComponentName componentName = iterator.next();
+                            String componentPackage = componentName.getPackageName();
+                            for (String stoppedPackage : stoppedPackages) {
+                                if (componentPackage.equals(stoppedPackage)) {
+                                    if (!doit) {
+                                        return true;
+                                    }
+                                    stoppedSomePackages = true;
+                                    break;
+                                }
+                            }
+                        }
+                        if (stoppedSomePackages) {
+                            userState.updateIfNeededLocked();
+                        }
+                        return false;
+                    }
+                }
+
+                @Override
+                public void onPackageAdded(String packageName, int uid) {
+                    Intent intent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
+                    intent.setPackage(packageName);
+
+                    List<ResolveInfo> installedServices = mContext.getPackageManager()
+                            .queryIntentServicesAsUser(intent, PackageManager.GET_SERVICES,
+                                    getChangingUserId());
+
+                    if (installedServices == null) {
+                        return;
+                    }
+
+                    final int installedServiceCount = installedServices.size();
+                    for (int i = 0; i < installedServiceCount; i++) {
+                        ServiceInfo serviceInfo = installedServices.get(i).serviceInfo;
+                        ComponentName component = new ComponentName(serviceInfo.packageName,
+                                serviceInfo.name);
+                        String label = serviceInfo.loadLabel(mContext.getPackageManager())
+                                .toString();
+                        showEnableInstalledPrintServiceNotification(component, label,
+                                getChangingUserId());
+                    }
+                }
+
+                private void persistComponentNamesToSettingLocked(String settingName,
+                        Set<ComponentName> componentNames, int userId) {
+                    StringBuilder builder = new StringBuilder();
+                    for (ComponentName componentName : componentNames) {
+                        if (builder.length() > 0) {
+                            builder.append(COMPONENT_NAME_SEPARATOR);
+                        }
+                        builder.append(componentName.flattenToShortString());
+                    }
+                    Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                            settingName, builder.toString(), userId);
+                }
+            };
+
+            // package changes
+            monitor.register(mContext, BackgroundThread.getHandler().getLooper(),
+                    UserHandle.ALL, true);
+
+            // user changes
+            IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+            intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+
+            mContext.registerReceiverAsUser(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    String action = intent.getAction();
+                    if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                        switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+                    } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+                        removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+                    }
+                }
+            }, UserHandle.ALL, intentFilter, null, BackgroundThread.getHandler());
+        }
+
+        private UserState getCurrentUserStateLocked() {
+            return getOrCreateUserStateLocked(mCurrentUserId);
+        }
+
+        private UserState getOrCreateUserStateLocked(int userId) {
+            UserState userState = mUserStates.get(userId);
             if (userState == null) {
-                userState = getCurrentUserStateLocked();
-                userState.updateIfNeededLocked();
-            } else {
-                userState.updateIfNeededLocked();
+                userState = new UserState(mContext, userId, mLock);
+                mUserStates.put(userId, userState);
+            }
+            return userState;
+        }
+
+        private void switchUser(int newUserId) {
+            UserState userState;
+            synchronized (mLock) {
+                if (newUserId == mCurrentUserId) {
+                    return;
+                }
+                mCurrentUserId = newUserId;
+                userState = mUserStates.get(mCurrentUserId);
+                if (userState == null) {
+                    userState = getCurrentUserStateLocked();
+                    userState.updateIfNeededLocked();
+                } else {
+                    userState.updateIfNeededLocked();
+                }
+            }
+            // This is the first time we switch to this user after boot, so
+            // now is the time to remove obsolete print jobs since they
+            // are from the last boot and no application would query them.
+            userState.removeObsoletePrintJobs();
+        }
+
+        private void removeUser(int removedUserId) {
+            synchronized (mLock) {
+                UserState userState = mUserStates.get(removedUserId);
+                if (userState != null) {
+                    userState.destroyLocked();
+                    mUserStates.remove(removedUserId);
+                }
             }
         }
-        // This is the first time we switch to this user after boot, so
-        // now is the time to remove obsolete print jobs since they
-        // are from the last boot and no application would query them.
-        userState.removeObsoletePrintJobs();
-    }
 
-    private void removeUser(int removedUserId) {
-        synchronized (mLock) {
-            UserState userState = mUserStates.get(removedUserId);
-            if (userState != null) {
-                userState.destroyLocked();
-                mUserStates.remove(removedUserId);
+        private int resolveCallingAppEnforcingPermissions(int appId) {
+            final int callingUid = Binder.getCallingUid();
+            if (callingUid == 0 || callingUid == Process.SYSTEM_UID
+                    || callingUid == Process.SHELL_UID) {
+                return appId;
             }
-        }
-    }
-
-    private int resolveCallingAppEnforcingPermissions(int appId) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid == 0 || callingUid == Process.SYSTEM_UID
-                || callingUid == Process.SHELL_UID) {
+            final int callingAppId = UserHandle.getAppId(callingUid);
+            if (appId == callingAppId) {
+                return appId;
+            }
+            if (mContext.checkCallingPermission(
+                    "com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS")
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Call from app " + callingAppId + " as app "
+                        + appId + " without com.android.printspooler.permission"
+                        + ".ACCESS_ALL_PRINT_JOBS");
+            }
             return appId;
         }
-        final int callingAppId = UserHandle.getAppId(callingUid);
-        if (appId == callingAppId) {
-            return appId;
-        }
-        if (mContext.checkCallingPermission(
-                "com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS")
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Call from app " + callingAppId + " as app "
-                    + appId + " without com.android.printspooler.permission"
-                    + ".ACCESS_ALL_PRINT_JOBS");
-        }
-        return appId;
-    }
 
-    private int resolveCallingUserEnforcingPermissions(int userId) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid == 0 || callingUid == Process.SYSTEM_UID
-                || callingUid == Process.SHELL_UID) {
-            return userId;
-        }
-        final int callingUserId = UserHandle.getUserId(callingUid);
-        if (callingUserId == userId) {
-            return userId;
-        }
-        if (mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-                != PackageManager.PERMISSION_GRANTED
-            ||  mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS)
-                != PackageManager.PERMISSION_GRANTED) {
-            if (userId == UserHandle.USER_CURRENT_OR_SELF) {
-                return callingUserId;
+        private int resolveCallingUserEnforcingPermissions(int userId) {
+            final int callingUid = Binder.getCallingUid();
+            if (callingUid == 0 || callingUid == Process.SYSTEM_UID
+                    || callingUid == Process.SHELL_UID) {
+                return userId;
             }
-            throw new SecurityException("Call from user " + callingUserId + " as user "
-                + userId + " without permission INTERACT_ACROSS_USERS or "
-                + "INTERACT_ACROSS_USERS_FULL not allowed.");
+            final int callingUserId = UserHandle.getUserId(callingUid);
+            if (callingUserId == userId) {
+                return userId;
+            }
+            if (mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                    != PackageManager.PERMISSION_GRANTED
+                ||  mContext.checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                if (userId == UserHandle.USER_CURRENT_OR_SELF) {
+                    return callingUserId;
+                }
+                throw new SecurityException("Call from user " + callingUserId + " as user "
+                    + userId + " without permission INTERACT_ACROSS_USERS or "
+                    + "INTERACT_ACROSS_USERS_FULL not allowed.");
+            }
+            if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
+                return mCurrentUserId;
+            }
+            throw new IllegalArgumentException("Calling user can be changed to only "
+                    + "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
         }
-        if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
-            return mCurrentUserId;
-        }
-        throw new IllegalArgumentException("Calling user can be changed to only "
-                + "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
-    }
 
-    private String resolveCallingPackageNameEnforcingSecurity(String packageName) {
-        if (TextUtils.isEmpty(packageName)) {
+        private String resolveCallingPackageNameEnforcingSecurity(String packageName) {
+            if (TextUtils.isEmpty(packageName)) {
+                return null;
+            }
+            String[] packages = mContext.getPackageManager().getPackagesForUid(
+                    Binder.getCallingUid());
+            final int packageCount = packages.length;
+            for (int i = 0; i < packageCount; i++) {
+                if (packageName.equals(packages[i])) {
+                    return packageName;
+                }
+            }
             return null;
         }
-        String[] packages = mContext.getPackageManager().getPackagesForUid(
-                Binder.getCallingUid());
-        final int packageCount = packages.length;
-        for (int i = 0; i < packageCount; i++) {
-            if (packageName.equals(packages[i])) {
-                return packageName;
-            }
+
+        private void showEnableInstalledPrintServiceNotification(ComponentName component,
+                String label, int userId) {
+            UserHandle userHandle = new UserHandle(userId);
+
+            Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
+            intent.putExtra(EXTRA_PRINT_SERVICE_COMPONENT_NAME, component.flattenToString());
+
+            PendingIntent pendingIntent = PendingIntent.getActivityAsUser(mContext, 0, intent,
+                    PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT, null,
+                    userHandle);
+
+            Notification.Builder builder = new Notification.Builder(mContext)
+                    .setSmallIcon(R.drawable.ic_print)
+                    .setContentTitle(mContext.getString(R.string.print_service_installed_title,
+                            label))
+                    .setContentText(mContext.getString(R.string.print_service_installed_message))
+                    .setContentIntent(pendingIntent)
+                    .setWhen(System.currentTimeMillis())
+                    .setAutoCancel(true)
+                    .setShowWhen(true);
+
+            NotificationManager notificationManager = (NotificationManager) mContext
+                    .getSystemService(Context.NOTIFICATION_SERVICE);
+
+            String notificationTag = getClass().getName() + ":" + component.flattenToString();
+            notificationManager.notifyAsUser(notificationTag, 0, builder.build(),
+                    userHandle);
         }
-        return null;
-    }
-
-    private void showEnableInstalledPrintServiceNotification(ComponentName component,
-            String label, int userId) {
-        UserHandle userHandle = new UserHandle(userId);
-
-        Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
-        intent.putExtra(EXTRA_PRINT_SERVICE_COMPONENT_NAME, component.flattenToString());
-
-        PendingIntent pendingIntent = PendingIntent.getActivityAsUser(mContext, 0, intent,
-                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT, null, userHandle);
-
-        Notification.Builder builder = new Notification.Builder(mContext)
-                .setSmallIcon(R.drawable.ic_print)
-                .setContentTitle(mContext.getString(R.string.print_service_installed_title, label))
-                .setContentText(mContext.getString(R.string.print_service_installed_message))
-                .setContentIntent(pendingIntent)
-                .setWhen(System.currentTimeMillis())
-                .setAutoCancel(true)
-                .setShowWhen(true);
-
-        NotificationManager notificationManager = (NotificationManager) mContext
-                .getSystemService(Context.NOTIFICATION_SERVICE);
-
-        String notificationTag = getClass().getName() + ":" + component.flattenToString();
-        notificationManager.notifyAsUser(notificationTag, 0, builder.build(),
-                userHandle);
     }
 }