1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.view;
18 
19 import android.graphics.Canvas;
20 import android.graphics.PixelFormat;
21 import android.graphics.Rect;
22 import android.os.SystemClock;
23 import android.util.Log;
24 import android.view.Surface;
25 import android.view.SurfaceHolder;
26 
27 import java.util.ArrayList;
28 import java.util.concurrent.locks.ReentrantLock;
29 
30 public abstract class BaseSurfaceHolder implements SurfaceHolder {
31     private static final String TAG = "BaseSurfaceHolder";
32     static final boolean DEBUG = false;
33 
34     public final ArrayList<SurfaceHolder.Callback> mCallbacks
35             = new ArrayList<SurfaceHolder.Callback>();
36     SurfaceHolder.Callback[] mGottenCallbacks;
37     boolean mHaveGottenCallbacks;
38 
39     public final ReentrantLock mSurfaceLock = new ReentrantLock();
40     public Surface mSurface = new Surface();
41 
42     int mRequestedWidth = -1;
43     int mRequestedHeight = -1;
44     /** @hide */
45     protected int mRequestedFormat = PixelFormat.OPAQUE;
46     int mRequestedType = -1;
47 
48     long mLastLockTime = 0;
49 
50     int mType = -1;
51     final Rect mSurfaceFrame = new Rect();
52     Rect mTmpDirty;
53 
onUpdateSurface()54     public abstract void onUpdateSurface();
onRelayoutContainer()55     public abstract void onRelayoutContainer();
onAllowLockCanvas()56     public abstract boolean onAllowLockCanvas();
57 
getRequestedWidth()58     public int getRequestedWidth() {
59         return mRequestedWidth;
60     }
61 
getRequestedHeight()62     public int getRequestedHeight() {
63         return mRequestedHeight;
64     }
65 
getRequestedFormat()66     public int getRequestedFormat() {
67         return mRequestedFormat;
68     }
69 
getRequestedType()70     public int getRequestedType() {
71         return mRequestedType;
72     }
73 
addCallback(Callback callback)74     public void addCallback(Callback callback) {
75         synchronized (mCallbacks) {
76             // This is a linear search, but in practice we'll
77             // have only a couple callbacks, so it doesn't matter.
78             if (mCallbacks.contains(callback) == false) {
79                 mCallbacks.add(callback);
80             }
81         }
82     }
83 
removeCallback(Callback callback)84     public void removeCallback(Callback callback) {
85         synchronized (mCallbacks) {
86             mCallbacks.remove(callback);
87         }
88     }
89 
getCallbacks()90     public SurfaceHolder.Callback[] getCallbacks() {
91         if (mHaveGottenCallbacks) {
92             return mGottenCallbacks;
93         }
94 
95         synchronized (mCallbacks) {
96             final int N = mCallbacks.size();
97             if (N > 0) {
98                 if (mGottenCallbacks == null || mGottenCallbacks.length != N) {
99                     mGottenCallbacks = new SurfaceHolder.Callback[N];
100                 }
101                 mCallbacks.toArray(mGottenCallbacks);
102             } else {
103                 mGottenCallbacks = null;
104             }
105             mHaveGottenCallbacks = true;
106         }
107 
108         return mGottenCallbacks;
109     }
110 
ungetCallbacks()111     public void ungetCallbacks() {
112         mHaveGottenCallbacks = false;
113     }
114 
setFixedSize(int width, int height)115     public void setFixedSize(int width, int height) {
116         if (mRequestedWidth != width || mRequestedHeight != height) {
117             mRequestedWidth = width;
118             mRequestedHeight = height;
119             onRelayoutContainer();
120         }
121     }
122 
setSizeFromLayout()123     public void setSizeFromLayout() {
124         if (mRequestedWidth != -1 || mRequestedHeight != -1) {
125             mRequestedWidth = mRequestedHeight = -1;
126             onRelayoutContainer();
127         }
128     }
129 
setFormat(int format)130     public void setFormat(int format) {
131         if (mRequestedFormat != format) {
132             mRequestedFormat = format;
133             onUpdateSurface();
134         }
135     }
136 
setType(int type)137     public void setType(int type) {
138         switch (type) {
139         case SURFACE_TYPE_HARDWARE:
140         case SURFACE_TYPE_GPU:
141             // these are deprecated, treat as "NORMAL"
142             type = SURFACE_TYPE_NORMAL;
143             break;
144         }
145         switch (type) {
146         case SURFACE_TYPE_NORMAL:
147         case SURFACE_TYPE_PUSH_BUFFERS:
148             if (mRequestedType != type) {
149                 mRequestedType = type;
150                 onUpdateSurface();
151             }
152             break;
153         }
154     }
155 
lockCanvas()156     public Canvas lockCanvas() {
157         return internalLockCanvas(null);
158     }
159 
lockCanvas(Rect dirty)160     public Canvas lockCanvas(Rect dirty) {
161         return internalLockCanvas(dirty);
162     }
163 
internalLockCanvas(Rect dirty)164     private final Canvas internalLockCanvas(Rect dirty) {
165         if (mType == SURFACE_TYPE_PUSH_BUFFERS) {
166             throw new BadSurfaceTypeException(
167                     "Surface type is SURFACE_TYPE_PUSH_BUFFERS");
168         }
169         mSurfaceLock.lock();
170 
171         if (DEBUG) Log.i(TAG, "Locking canvas..,");
172 
173         Canvas c = null;
174         if (onAllowLockCanvas()) {
175             if (dirty == null) {
176                 if (mTmpDirty == null) {
177                     mTmpDirty = new Rect();
178                 }
179                 mTmpDirty.set(mSurfaceFrame);
180                 dirty = mTmpDirty;
181             }
182 
183             try {
184                 c = mSurface.lockCanvas(dirty);
185             } catch (Exception e) {
186                 Log.e(TAG, "Exception locking surface", e);
187             }
188         }
189 
190         if (DEBUG) Log.i(TAG, "Returned canvas: " + c);
191         if (c != null) {
192             mLastLockTime = SystemClock.uptimeMillis();
193             return c;
194         }
195 
196         // If the Surface is not ready to be drawn, then return null,
197         // but throttle calls to this function so it isn't called more
198         // than every 100ms.
199         long now = SystemClock.uptimeMillis();
200         long nextTime = mLastLockTime + 100;
201         if (nextTime > now) {
202             try {
203                 Thread.sleep(nextTime-now);
204             } catch (InterruptedException e) {
205             }
206             now = SystemClock.uptimeMillis();
207         }
208         mLastLockTime = now;
209         mSurfaceLock.unlock();
210 
211         return null;
212     }
213 
unlockCanvasAndPost(Canvas canvas)214     public void unlockCanvasAndPost(Canvas canvas) {
215         mSurface.unlockCanvasAndPost(canvas);
216         mSurfaceLock.unlock();
217     }
218 
getSurface()219     public Surface getSurface() {
220         return mSurface;
221     }
222 
getSurfaceFrame()223     public Rect getSurfaceFrame() {
224         return mSurfaceFrame;
225     }
226 
setSurfaceFrameSize(int width, int height)227     public void setSurfaceFrameSize(int width, int height) {
228         mSurfaceFrame.top = 0;
229         mSurfaceFrame.left = 0;
230         mSurfaceFrame.right = width;
231         mSurfaceFrame.bottom = height;
232     }
233 };
234