SurfaceView.java revision 7179b8133d4a1d8e5f26cbe3da6aa978094e75c9
1/*
2 * Copyright (C) 2006 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
17package android.view;
18
19import com.android.internal.view.BaseIWindow;
20
21import android.content.Context;
22import android.content.res.Configuration;
23import android.content.res.Resources;
24import android.content.res.CompatibilityInfo.Translator;
25import android.graphics.Canvas;
26import android.graphics.PixelFormat;
27import android.graphics.PorterDuff;
28import android.graphics.Rect;
29import android.graphics.Region;
30import android.os.Handler;
31import android.os.Message;
32import android.os.RemoteException;
33import android.os.SystemClock;
34import android.os.ParcelFileDescriptor;
35import android.util.AttributeSet;
36import android.util.Config;
37import android.util.Log;
38
39import java.lang.ref.WeakReference;
40import java.util.ArrayList;
41import java.util.concurrent.locks.ReentrantLock;
42
43/**
44 * Provides a dedicated drawing surface embedded inside of a view hierarchy.
45 * You can control the format of this surface and, if you like, its size; the
46 * SurfaceView takes care of placing the surface at the correct location on the
47 * screen
48 *
49 * <p>The surface is Z ordered so that it is behind the window holding its
50 * SurfaceView; the SurfaceView punches a hole in its window to allow its
51 * surface to be displayed.  The view hierarchy will take care of correctly
52 * compositing with the Surface any siblings of the SurfaceView that would
53 * normally appear on top of it.  This can be used to place overlays such as
54 * buttons on top of the Surface, though note however that it can have an
55 * impact on performance since a full alpha-blended composite will be performed
56 * each time the Surface changes.
57 *
58 * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
59 * which can be retrieved by calling {@link #getHolder}.
60 *
61 * <p>The Surface will be created for you while the SurfaceView's window is
62 * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
63 * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
64 * Surface is created and destroyed as the window is shown and hidden.
65 *
66 * <p>One of the purposes of this class is to provide a surface in which a
67 * secondary thread can render in to the screen.  If you are going to use it
68 * this way, you need to be aware of some threading semantics:
69 *
70 * <ul>
71 * <li> All SurfaceView and
72 * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
73 * from the thread running the SurfaceView's window (typically the main thread
74 * of the application).  They thus need to correctly synchronize with any
75 * state that is also touched by the drawing thread.
76 * <li> You must ensure that the drawing thread only touches the underlying
77 * Surface while it is valid -- between
78 * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
79 * and
80 * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
81 * </ul>
82 */
83public class SurfaceView extends View {
84    static private final String TAG = "SurfaceView";
85    static private final boolean DEBUG = false;
86    static private final boolean localLOGV = DEBUG ? true : Config.LOGV;
87
88    final ArrayList<SurfaceHolder.Callback> mCallbacks
89            = new ArrayList<SurfaceHolder.Callback>();
90
91    final int[] mLocation = new int[2];
92
93    final ReentrantLock mSurfaceLock = new ReentrantLock();
94    final Surface mSurface = new Surface();
95    boolean mDrawingStopped = true;
96
97    final WindowManager.LayoutParams mLayout
98            = new WindowManager.LayoutParams();
99    IWindowSession mSession;
100    MyWindow mWindow;
101    final Rect mVisibleInsets = new Rect();
102    final Rect mWinFrame = new Rect();
103    final Rect mContentInsets = new Rect();
104    final Configuration mConfiguration = new Configuration();
105
106    static final int KEEP_SCREEN_ON_MSG = 1;
107    static final int GET_NEW_SURFACE_MSG = 2;
108
109    int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
110
111    boolean mIsCreating = false;
112
113    final Handler mHandler = new Handler() {
114        @Override
115        public void handleMessage(Message msg) {
116            switch (msg.what) {
117                case KEEP_SCREEN_ON_MSG: {
118                    setKeepScreenOn(msg.arg1 != 0);
119                } break;
120                case GET_NEW_SURFACE_MSG: {
121                    handleGetNewSurface();
122                } break;
123            }
124        }
125    };
126
127    final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
128            = new ViewTreeObserver.OnScrollChangedListener() {
129                    public void onScrollChanged() {
130                        updateWindow(false);
131                    }
132            };
133
134    boolean mRequestedVisible = false;
135    boolean mWindowVisibility = false;
136    boolean mViewVisibility = false;
137    int mRequestedWidth = -1;
138    int mRequestedHeight = -1;
139    int mRequestedFormat = PixelFormat.OPAQUE;
140    int mRequestedType = -1;
141
142    boolean mHaveFrame = false;
143    boolean mDestroyReportNeeded = false;
144    boolean mNewSurfaceNeeded = false;
145    long mLastLockTime = 0;
146
147    boolean mVisible = false;
148    int mLeft = -1;
149    int mTop = -1;
150    int mWidth = -1;
151    int mHeight = -1;
152    int mFormat = -1;
153    int mType = -1;
154    final Rect mSurfaceFrame = new Rect();
155    private Translator mTranslator;
156
157    public SurfaceView(Context context) {
158        super(context);
159        setWillNotDraw(true);
160    }
161
162    public SurfaceView(Context context, AttributeSet attrs) {
163        super(context, attrs);
164        setWillNotDraw(true);
165    }
166
167    public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
168        super(context, attrs, defStyle);
169        setWillNotDraw(true);
170    }
171
172    /**
173     * Return the SurfaceHolder providing access and control over this
174     * SurfaceView's underlying surface.
175     *
176     * @return SurfaceHolder The holder of the surface.
177     */
178    public SurfaceHolder getHolder() {
179        return mSurfaceHolder;
180    }
181
182    @Override
183    protected void onAttachedToWindow() {
184        super.onAttachedToWindow();
185        mParent.requestTransparentRegion(this);
186        mSession = getWindowSession();
187        mLayout.token = getWindowToken();
188        mLayout.setTitle("SurfaceView");
189        mViewVisibility = getVisibility() == VISIBLE;
190        getViewTreeObserver().addOnScrollChangedListener(mScrollChangedListener);
191    }
192
193    @Override
194    protected void onWindowVisibilityChanged(int visibility) {
195        super.onWindowVisibilityChanged(visibility);
196        mWindowVisibility = visibility == VISIBLE;
197        mRequestedVisible = mWindowVisibility && mViewVisibility;
198        updateWindow(false);
199    }
200
201    @Override
202    public void setVisibility(int visibility) {
203        super.setVisibility(visibility);
204        mViewVisibility = visibility == VISIBLE;
205        mRequestedVisible = mWindowVisibility && mViewVisibility;
206        updateWindow(false);
207    }
208
209    @Override
210    protected void onDetachedFromWindow() {
211        getViewTreeObserver().removeOnScrollChangedListener(mScrollChangedListener);
212        mRequestedVisible = false;
213        updateWindow(false);
214        mHaveFrame = false;
215        if (mWindow != null) {
216            try {
217                mSession.remove(mWindow);
218            } catch (RemoteException ex) {
219            }
220            mWindow = null;
221        }
222        mSession = null;
223        mLayout.token = null;
224
225        super.onDetachedFromWindow();
226    }
227
228    @Override
229    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
230        int width = getDefaultSize(mRequestedWidth, widthMeasureSpec);
231        int height = getDefaultSize(mRequestedHeight, heightMeasureSpec);
232        setMeasuredDimension(width, height);
233    }
234
235    @Override
236    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
237        super.onSizeChanged(w, h, oldw, oldh);
238        updateWindow(false);
239    }
240
241    @Override
242    public boolean gatherTransparentRegion(Region region) {
243        if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
244            return super.gatherTransparentRegion(region);
245        }
246
247        boolean opaque = true;
248        if ((mPrivateFlags & SKIP_DRAW) == 0) {
249            // this view draws, remove it from the transparent region
250            opaque = super.gatherTransparentRegion(region);
251        } else if (region != null) {
252            int w = getWidth();
253            int h = getHeight();
254            if (w>0 && h>0) {
255                getLocationInWindow(mLocation);
256                // otherwise, punch a hole in the whole hierarchy
257                int l = mLocation[0];
258                int t = mLocation[1];
259                region.op(l, t, l+w, t+h, Region.Op.UNION);
260            }
261        }
262        if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
263            opaque = false;
264        }
265        return opaque;
266    }
267
268    @Override
269    public void draw(Canvas canvas) {
270        if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
271            // draw() is not called when SKIP_DRAW is set
272            if ((mPrivateFlags & SKIP_DRAW) == 0) {
273                // punch a whole in the view-hierarchy below us
274                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
275            }
276        }
277        super.draw(canvas);
278    }
279
280    @Override
281    protected void dispatchDraw(Canvas canvas) {
282        if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
283            // if SKIP_DRAW is cleared, draw() has already punched a hole
284            if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
285                // punch a whole in the view-hierarchy below us
286                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
287            }
288        }
289        // reposition ourselves where the surface is
290        mHaveFrame = true;
291        updateWindow(false);
292        super.dispatchDraw(canvas);
293    }
294
295    /**
296     * Control whether the surface view's surface is placed on top of another
297     * regular surface view in the window (but still behind the window itself).
298     * This is typically used to place overlays on top of an underlying media
299     * surface view.
300     *
301     * <p>Note that this must be set before the surface view's containing
302     * window is attached to the window manager.
303     *
304     * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
305     */
306    public void setZOrderMediaOverlay(boolean isMediaOverlay) {
307        mWindowType = isMediaOverlay
308                ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
309                : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
310    }
311
312    /**
313     * Control whether the surface view's surface is placed on top of its
314     * window.  Normally it is placed behind the window, to allow it to
315     * (for the most part) appear to composite with the views in the
316     * hierarchy.  By setting this, you cause it to be placed above the
317     * window.  This means that none of the contents of the window this
318     * SurfaceView is in will be visible on top of its surface.
319     *
320     * <p>Note that this must be set before the surface view's containing
321     * window is attached to the window manager.
322     *
323     * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
324     */
325    public void setZOrderOnTop(boolean onTop) {
326        if (onTop) {
327            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
328            // ensures the surface is placed below the IME
329            mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
330        } else {
331            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
332            mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
333        }
334    }
335
336    /**
337     * Hack to allow special layering of windows.  The type is one of the
338     * types in WindowManager.LayoutParams.  This is a hack so:
339     * @hide
340     */
341    public void setWindowType(int type) {
342        mWindowType = type;
343    }
344
345    private void updateWindow(boolean force) {
346        if (!mHaveFrame) {
347            return;
348        }
349        ViewRoot viewRoot = (ViewRoot) getRootView().getParent();
350        if (viewRoot != null) {
351            mTranslator = viewRoot.mTranslator;
352        }
353
354        Resources res = getContext().getResources();
355        if (mTranslator != null || !res.getCompatibilityInfo().supportsScreen()) {
356            mSurface.setCompatibleDisplayMetrics(res.getDisplayMetrics(), mTranslator);
357        }
358
359        int myWidth = mRequestedWidth;
360        if (myWidth <= 0) myWidth = getWidth();
361        int myHeight = mRequestedHeight;
362        if (myHeight <= 0) myHeight = getHeight();
363
364        getLocationInWindow(mLocation);
365        final boolean creating = mWindow == null;
366        final boolean formatChanged = mFormat != mRequestedFormat;
367        final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
368        final boolean visibleChanged = mVisible != mRequestedVisible
369                || mNewSurfaceNeeded;
370        final boolean typeChanged = mType != mRequestedType;
371        if (force || creating || formatChanged || sizeChanged || visibleChanged
372            || typeChanged || mLeft != mLocation[0] || mTop != mLocation[1]) {
373
374            if (localLOGV) Log.i(TAG, "Changes: creating=" + creating
375                    + " format=" + formatChanged + " size=" + sizeChanged
376                    + " visible=" + visibleChanged
377                    + " left=" + (mLeft != mLocation[0])
378                    + " top=" + (mTop != mLocation[1]));
379
380            try {
381                final boolean visible = mVisible = mRequestedVisible;
382                mLeft = mLocation[0];
383                mTop = mLocation[1];
384                mWidth = myWidth;
385                mHeight = myHeight;
386                mFormat = mRequestedFormat;
387                mType = mRequestedType;
388
389                // Scaling/Translate window's layout here because mLayout is not used elsewhere.
390
391                // Places the window relative
392                mLayout.x = mLeft;
393                mLayout.y = mTop;
394                mLayout.width = getWidth();
395                mLayout.height = getHeight();
396                if (mTranslator != null) {
397                    mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
398                }
399
400                mLayout.format = mRequestedFormat;
401                mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
402                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
403                              | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
404                              | WindowManager.LayoutParams.FLAG_SCALED
405                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
406                              | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
407                              ;
408                if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
409                    mLayout.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
410                }
411
412                mLayout.memoryType = mRequestedType;
413
414                if (mWindow == null) {
415                    mWindow = new MyWindow(this);
416                    mLayout.type = mWindowType;
417                    mLayout.gravity = Gravity.LEFT|Gravity.TOP;
418                    mSession.add(mWindow, mLayout,
419                            mVisible ? VISIBLE : GONE, mContentInsets);
420                }
421
422                if (visibleChanged && (!visible || mNewSurfaceNeeded)) {
423                    reportSurfaceDestroyed();
424                }
425
426                mNewSurfaceNeeded = false;
427
428                mSurfaceLock.lock();
429                mDrawingStopped = !visible;
430
431                final int relayoutResult = mSession.relayout(
432                    mWindow, mLayout, mWidth, mHeight,
433                        visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
434                        mVisibleInsets, mConfiguration, mSurface);
435
436                if (localLOGV) Log.i(TAG, "New surface: " + mSurface
437                        + ", vis=" + visible + ", frame=" + mWinFrame);
438
439                mSurfaceFrame.left = 0;
440                mSurfaceFrame.top = 0;
441                if (mTranslator == null) {
442                    mSurfaceFrame.right = mWinFrame.width();
443                    mSurfaceFrame.bottom = mWinFrame.height();
444                } else {
445                    float appInvertedScale = mTranslator.applicationInvertedScale;
446                    mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
447                    mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
448                }
449                mSurfaceLock.unlock();
450
451                try {
452                    if (visible) {
453                        mDestroyReportNeeded = true;
454
455                        SurfaceHolder.Callback callbacks[];
456                        synchronized (mCallbacks) {
457                            callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
458                            mCallbacks.toArray(callbacks);
459                        }
460
461                        if (visibleChanged) {
462                            mIsCreating = true;
463                            for (SurfaceHolder.Callback c : callbacks) {
464                                c.surfaceCreated(mSurfaceHolder);
465                            }
466                        }
467                        if (creating || formatChanged || sizeChanged
468                                || visibleChanged) {
469                            for (SurfaceHolder.Callback c : callbacks) {
470                                c.surfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
471                            }
472                        }
473                    }
474                } finally {
475                    mIsCreating = false;
476                    if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
477                        mSession.finishDrawing(mWindow);
478                    }
479                }
480            } catch (RemoteException ex) {
481            }
482            if (localLOGV) Log.v(
483                TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
484                " w=" + mLayout.width + " h=" + mLayout.height +
485                ", frame=" + mSurfaceFrame);
486        }
487    }
488
489    private void reportSurfaceDestroyed() {
490        if (mDestroyReportNeeded) {
491            mDestroyReportNeeded = false;
492            SurfaceHolder.Callback callbacks[];
493            synchronized (mCallbacks) {
494                callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
495                mCallbacks.toArray(callbacks);
496            }
497            for (SurfaceHolder.Callback c : callbacks) {
498                c.surfaceDestroyed(mSurfaceHolder);
499            }
500        }
501        super.onDetachedFromWindow();
502    }
503
504    void handleGetNewSurface() {
505        mNewSurfaceNeeded = true;
506        updateWindow(false);
507    }
508
509    /**
510     * Check to see if the surface has fixed size dimensions or if the surface's
511     * dimensions are dimensions are dependent on its current layout.
512     *
513     * @return true if the surface has dimensions that are fixed in size
514     * @hide
515     */
516    public boolean isFixedSize() {
517        return (mRequestedWidth != -1 || mRequestedHeight != -1);
518    }
519
520    private static class MyWindow extends BaseIWindow {
521        private final WeakReference<SurfaceView> mSurfaceView;
522
523        public MyWindow(SurfaceView surfaceView) {
524            mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
525        }
526
527        public void resized(int w, int h, Rect coveredInsets,
528                Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
529            SurfaceView surfaceView = mSurfaceView.get();
530            if (surfaceView != null) {
531                if (localLOGV) Log.v(
532                        "SurfaceView", surfaceView + " got resized: w=" +
533                                w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight);
534                synchronized (this) {
535                    if (mCurWidth != w || mCurHeight != h) {
536                        mCurWidth = w;
537                        mCurHeight = h;
538                    }
539                    if (reportDraw) {
540                        try {
541                            surfaceView.mSession.finishDrawing(surfaceView.mWindow);
542                        } catch (RemoteException e) {
543                        }
544                    }
545                }
546            }
547        }
548
549        public void dispatchKey(KeyEvent event) {
550            SurfaceView surfaceView = mSurfaceView.get();
551            if (surfaceView != null) {
552                //Log.w("SurfaceView", "Unexpected key event in surface: " + event);
553                if (surfaceView.mSession != null && surfaceView.mSurface != null) {
554                    try {
555                        surfaceView.mSession.finishKey(surfaceView.mWindow);
556                    } catch (RemoteException ex) {
557                    }
558                }
559            }
560        }
561
562        public void dispatchPointer(MotionEvent event, long eventTime,
563                boolean callWhenDone) {
564            Log.w("SurfaceView", "Unexpected pointer event in surface: " + event);
565            //if (mSession != null && mSurface != null) {
566            //    try {
567            //        //mSession.finishKey(mWindow);
568            //    } catch (RemoteException ex) {
569            //    }
570            //}
571        }
572
573        public void dispatchTrackball(MotionEvent event, long eventTime,
574                boolean callWhenDone) {
575            Log.w("SurfaceView", "Unexpected trackball event in surface: " + event);
576            //if (mSession != null && mSurface != null) {
577            //    try {
578            //        //mSession.finishKey(mWindow);
579            //    } catch (RemoteException ex) {
580            //    }
581            //}
582        }
583
584        public void dispatchAppVisibility(boolean visible) {
585            // The point of SurfaceView is to let the app control the surface.
586        }
587
588        public void dispatchGetNewSurface() {
589            SurfaceView surfaceView = mSurfaceView.get();
590            if (surfaceView != null) {
591                Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
592                surfaceView.mHandler.sendMessage(msg);
593            }
594        }
595
596        public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
597            Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
598        }
599
600        public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
601        }
602
603        int mCurWidth = -1;
604        int mCurHeight = -1;
605    }
606
607    private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
608
609        private static final String LOG_TAG = "SurfaceHolder";
610        private int mSaveCount;
611
612        public boolean isCreating() {
613            return mIsCreating;
614        }
615
616        public void addCallback(Callback callback) {
617            synchronized (mCallbacks) {
618                // This is a linear search, but in practice we'll
619                // have only a couple callbacks, so it doesn't matter.
620                if (mCallbacks.contains(callback) == false) {
621                    mCallbacks.add(callback);
622                }
623            }
624        }
625
626        public void removeCallback(Callback callback) {
627            synchronized (mCallbacks) {
628                mCallbacks.remove(callback);
629            }
630        }
631
632        public void setFixedSize(int width, int height) {
633            if (mRequestedWidth != width || mRequestedHeight != height) {
634                mRequestedWidth = width;
635                mRequestedHeight = height;
636                requestLayout();
637            }
638        }
639
640        public void setSizeFromLayout() {
641            if (mRequestedWidth != -1 || mRequestedHeight != -1) {
642                mRequestedWidth = mRequestedHeight = -1;
643                requestLayout();
644            }
645        }
646
647        public void setFormat(int format) {
648            mRequestedFormat = format;
649            if (mWindow != null) {
650                updateWindow(false);
651            }
652        }
653
654        public void setType(int type) {
655            switch (type) {
656            case SURFACE_TYPE_HARDWARE:
657            case SURFACE_TYPE_GPU:
658                // these are deprecated, treat as "NORMAL"
659                type = SURFACE_TYPE_NORMAL;
660                break;
661            }
662            switch (type) {
663            case SURFACE_TYPE_NORMAL:
664            case SURFACE_TYPE_PUSH_BUFFERS:
665                mRequestedType = type;
666                if (mWindow != null) {
667                    updateWindow(false);
668                }
669                break;
670            }
671        }
672
673        public void setKeepScreenOn(boolean screenOn) {
674            Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
675            msg.arg1 = screenOn ? 1 : 0;
676            mHandler.sendMessage(msg);
677        }
678
679        public Canvas lockCanvas() {
680            return internalLockCanvas(null);
681        }
682
683        public Canvas lockCanvas(Rect dirty) {
684            return internalLockCanvas(dirty);
685        }
686
687        private final Canvas internalLockCanvas(Rect dirty) {
688            if (mType == SURFACE_TYPE_PUSH_BUFFERS) {
689                throw new BadSurfaceTypeException(
690                        "Surface type is SURFACE_TYPE_PUSH_BUFFERS");
691            }
692            mSurfaceLock.lock();
693
694            if (localLOGV) Log.i(TAG, "Locking canvas... stopped="
695                    + mDrawingStopped + ", win=" + mWindow);
696
697            Canvas c = null;
698            if (!mDrawingStopped && mWindow != null) {
699                Rect frame = dirty != null ? dirty : mSurfaceFrame;
700                try {
701                    c = mSurface.lockCanvas(frame);
702                } catch (Exception e) {
703                    Log.e(LOG_TAG, "Exception locking surface", e);
704                }
705            }
706
707            if (localLOGV) Log.i(TAG, "Returned canvas: " + c);
708            if (c != null) {
709                mLastLockTime = SystemClock.uptimeMillis();
710                return c;
711            }
712
713            // If the Surface is not ready to be drawn, then return null,
714            // but throttle calls to this function so it isn't called more
715            // than every 100ms.
716            long now = SystemClock.uptimeMillis();
717            long nextTime = mLastLockTime + 100;
718            if (nextTime > now) {
719                try {
720                    Thread.sleep(nextTime-now);
721                } catch (InterruptedException e) {
722                }
723                now = SystemClock.uptimeMillis();
724            }
725            mLastLockTime = now;
726            mSurfaceLock.unlock();
727
728            return null;
729        }
730
731        public void unlockCanvasAndPost(Canvas canvas) {
732            mSurface.unlockCanvasAndPost(canvas);
733            mSurfaceLock.unlock();
734        }
735
736        public Surface getSurface() {
737            return mSurface;
738        }
739
740        public Rect getSurfaceFrame() {
741            return mSurfaceFrame;
742        }
743    };
744}
745