文章目录
- 画布surface
- ViewRoot的创建&setView分析
- setView
- requestLayout
- ViewRoot和WMS的关系
- activity的UI绘制
- draw
- surface
- jni层分析
- Surface无参构造
- SurfaceSession
- SurfaceSession_init
- surface的有参构造
- Surface_copyFrom
- Surface_writeToParcel
- Surface_readFromParcel
- 总结
上一篇我们分析到了Android创建了UI控件,但是作画的地方在哪里呢?
这里继续进行分析。
画布surface
Handle onto a raw buffer that is being managed by the screen compositor.
A Surface is generally created by or from a consumer of image buffers (such as a android.graphics.SurfaceTexture, android.media.MediaRecorder, or android.renderscript.Allocation), and is handed to some kind of producer (such as OpenGL, MediaPlayer, or android.hardware.camera2.CameraDevice#createCaptureSession) to draw into.
Note: A Surface acts like a weak reference to the consumer it is associated with. By itself it will not keep its parent consumer from being reclaimed.
https://developer.android.com/reference/kotlin/android/view/Surface?hl=en
从官方文档的描述,我们可以知道surface其实就是我们苦苦思索的”在哪里作画”的画布。相机、OpenGL、媒体播放器可以作为画家在上面作画。
Handle onto a raw buffer that is being managed by the screen compositor.这里说的 screen compositor其实就是SurfaceFlinger。我们可以把他们来一次具象化理解:
我们需要理解的是SurfaceFlinger和Surface的联系,这对于我们开发Android很重要。
ViewRoot的创建&setView分析
frameworks/base/core/java/android/view/ViewRoot.java
构造函数:
```java
public ViewRoot(Context context) {
super();
if (MEASURE_LATENCY && lt == null) {
lt = new LatencyTimer(100, 1000);
}
// For debug only
//++sInstanceCount;
// Initialize the statics when this class is first instantiated. This is
// done here instead of in the static block because Zygote does not
// allow the spawning of threads.
getWindowSession(context.getMainLooper());
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
mLocation.fillInStackTrace();
mWidth = -1;
mHeight = -1;
mDirty = new Rect();
mTempRect = new Rect();
mVisRect = new Rect();
mWinFrame = new Rect();
mWindow = new W(this, context);
mInputMethodCallback = new InputMethodCallback(this);
mViewVisibility = View.GONE;
mTransparentRegion = new Region();
mPreviousTransparentRegion = new Region();
mFirst = true; // true for the first time the view is added
mAdded = false;
mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
mViewConfiguration = ViewConfiguration.get(context);
mDensity = context.getResources().getDisplayMetrics().densityDpi;
}
getWindowSession:
public static IWindowSession getWindowSession(Looper mainLooper) {
synchronized (mStaticInit) {
if (!mInitialized) {
try {
InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
//binder通信的写法,很熟悉
sWindowSession = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"))
.openSession(imm.getClient(), imm.getInputContext());
mInitialized = true;
} catch (RemoteException e) {
}
}
return sWindowSession;
}
}
这个函数其实会建立Activity里面ViewRoot和WindowManagerService的关系。
我们知道,WindowManagerService由SystemServer进程启动,surfaceflinger服务也在这个进程中。所以activity的显示是需要跨进程通信的。
setView
接着分析
/**
* We have one child
*/
//第一个参数是decorview
public void setView(View view, WindowManager.LayoutParams attrs,
View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
mWindowAttributes.copyFrom(attrs);
attrs = mWindowAttributes;
Resources resources = mView.getContext().getResources();
CompatibilityInfo compatibilityInfo = resources.getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
if (mTranslator != null || !compatibilityInfo.supportsScreen()) {
mSurface.setCompatibleDisplayMetrics(resources.getDisplayMetrics(),
mTranslator);
}
boolean restore = false;
if (mTranslator != null) {
restore = true;
attrs.backup();
mTranslator.translateWindowLayout(attrs);
}
if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
if (!compatibilityInfo.supportsScreen()) {
attrs.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
}
mSoftInputMode = attrs.softInputMode;
mWindowAttributesChanged = true;
mAttachInfo.mRootView = view;
mAttachInfo.mScalingRequired = mTranslator != null;
mAttachInfo.mApplicationScale =
mTranslator == null ? 1.0f : mTranslator.applicationScale;
if (panelParentView != null) {
mAttachInfo.mPanelParentWindowToken
= panelParentView.getApplicationWindowToken();
}
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
//重点
requestLayout();
try {
//重点
res = sWindowSession.add(mWindow, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
unscheduleTraversals();
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
}
mPendingContentInsets.set(mAttachInfo.mContentInsets);
mPendingVisibleInsets.set(0, 0, 0, 0);
if (Config.LOGV) Log.v("ViewRoot", "Added window " + mWindow);
if (res < WindowManagerImpl.ADD_OKAY) {
mView = null;
mAttachInfo.mRootView = null;
mAdded = false;
unscheduleTraversals();
switch (res) {
case WindowManagerImpl.ADD_BAD_APP_TOKEN:
case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN:
throw new WindowManagerImpl.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
case WindowManagerImpl.ADD_NOT_APP_TOKEN:
throw new WindowManagerImpl.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not for an application");
case WindowManagerImpl.ADD_APP_EXITING:
throw new WindowManagerImpl.BadTokenException(
"Unable to add window -- app for token " + attrs.token
+ " is exiting");
case WindowManagerImpl.ADD_DUPLICATE_ADD:
throw new WindowManagerImpl.BadTokenException(
"Unable to add window -- window " + mWindow
+ " has already been added");
case WindowManagerImpl.ADD_STARTING_NOT_NEEDED:
// Silently ignore -- we would have just removed it
// right away, anyway.
return;
case WindowManagerImpl.ADD_MULTIPLE_SINGLETON:
throw new WindowManagerImpl.BadTokenException(
"Unable to add window " + mWindow +
" -- another window of this type already exists");
case WindowManagerImpl.ADD_PERMISSION_DENIED:
throw new WindowManagerImpl.BadTokenException(
"Unable to add window " + mWindow +
" -- permission denied for this window type");
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
view.assignParent(this);
mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0;
mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0;
}
}
}
这里做了几件事情,保存传入的decorview,调用了requestLayout,跨进程通信调用了add函数。
requestLayout
frameworks/base/core/java/android/view/ViewRoot.java
/**
* {@inheritDoc}
*/
public void requestLayout() {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
frameworks/base/core/java/android/view/ViewRoot.java
public void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
sendEmptyMessage(DO_TRAVERSAL);
}
}
ViewRoot和WMS进行了交互,ViewRoot调用openSession拿到了IWindowSession,调用它的add函数,把W类型的mWindow对象作为参数传入。
ViewRoot和WMS的关系
来看下WMS的openSession
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
return new Session(this, callback);
}
frameworks/base/services/java/com/android/server/WindowManagerService.java
public int add(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets) {
return addWindow(this, window, attrs, viewVisibility, outContentInsets);
}
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
Rect outContentInsets) {
...
//重点
win = new WindowState(session, client, token,
attachedWindow, attrs, viewVisibility);
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
// continue.
Slog.w(TAG, "Adding window client " + client.asBinder()
+ " that is dead, aborting.");
return WindowManagerImpl.ADD_APP_EXITING;
}
mPolicy.adjustWindowParamsLw(win.mAttrs);
res = mPolicy.prepareAddWindowLw(win, attrs);
if (res != WindowManagerImpl.ADD_OKAY) {
return res;
}
// From now on, no exceptions or errors allowed!
res = WindowManagerImpl.ADD_OKAY;
final long origId = Binder.clearCallingIdentity();
if (addToken) {
mTokenMap.put(attrs.token, token);
mTokenList.add(token);
}
//重点
win.attach();
mWindowMap.put(client.asBinder(), win);
...
return res;
}
WindowState的attach:
void attach() {
if (localLOGV) Slog.v(
TAG, "Attaching " + this + " token=" + mToken
+ ", list=" + mToken.windows);
mSession.windowAddedLocked();
}
void windowAddedLocked() {
if (mSurfaceSession == null) {
if (localLOGV) Slog.v(
TAG, "First window added to " + this + ", creating SurfaceSession");
//重点
mSurfaceSession = new SurfaceSession();
if (SHOW_TRANSACTIONS) Slog.i(
TAG, " NEW SURFACE SESSION " + mSurfaceSession);
mSessions.add(this);
}
mNumWindow++;
}
到这里,我们得总结一番:
activity的UI绘制
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DO_TRAVERSAL:
if (mProfile) {
Debug.startMethodTracing("ViewRoot");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
break;
}
performTraversals:
private void performTraversals() {
...
// cache mView since it is used so much below...
final View host = mView;
//重点
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
...
if (!cancelDraw && !newSurface) {
mFullRedrawNeeded = false;
//绘制
draw(fullRedrawNeeded);
...
relayoutWindow:
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
...
//重点
int relayoutResult = sWindowSession.relayout(
mWindow, params,
(int) (mView.mMeasuredWidth * appScale + 0.5f),
(int) (mView.mMeasuredHeight * appScale + 0.5f),
viewVisibility, insetsPending, mWinFrame,
mPendingContentInsets, mPendingVisibleInsets,
mPendingConfiguration, mSurface);//mSurface
...
return relayoutResult;
}
draw
frameworks/base/core/java/android/view/ViewRoot.java
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;//viewroot的成员变量
...
Rect dirty = mDirty;
if (mUseGL) {
if (!dirty.isEmpty()) {
Canvas canvas = mGlCanvas;
if (mGL != null && canvas != null) {
mGL.glDisable(GL_SCISSOR_TEST);
mGL.glClearColor(0, 0, 0, 0);
mGL.glClear(GL_COLOR_BUFFER_BIT);
mGL.glEnable(GL_SCISSOR_TEST);
mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
mAttachInfo.mIgnoreDirtyState = true;
mView.mPrivateFlags |= View.DRAWN;
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired
? DisplayMetrics.DENSITY_DEVICE : 0);
mView.draw(canvas);
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
}
} finally {
canvas.restoreToCount(saveCount);
}
mAttachInfo.mIgnoreDirtyState = false;
mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
checkEglErrors();
if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
int now = (int)SystemClock.elapsedRealtime();
if (sDrawTime != 0) {
nativeShowFPS(canvas, now - sDrawTime);
}
sDrawTime = now;
}
}
}
if (scrolling) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
return;
}
...
Canvas canvas;
try {
int left = dirty.left;
int top = dirty.top;
int right = dirty.right;
int bottom = dirty.bottom;
//lock canvas
canvas = surface.lockCanvas(dirty);
...
try {
if (!dirty.isEmpty() || mIsAnimating) {
long startTime = 0L;
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
Log.v("ViewRoot", "Surface " + surface + " drawing to bitmap w="
+ canvas.getWidth() + ", h=" + canvas.getHeight());
//canvas.drawARGB(255, 255, 0, 0);
}
if (Config.DEBUG && ViewDebug.profileDrawing) {
startTime = SystemClock.elapsedRealtime();
}
// If this bitmap's format includes an alpha channel, we
// need to clear it before drawing so that the child will
// properly re-composite its drawing on a transparent
// background. This automatically respects the clip/dirty region
// or
// If we are applying an offset, we need to clear the area
// where the offset doesn't appear to avoid having garbage
// left in the blank areas.
if (!canvas.isOpaque() || yoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
dirty.setEmpty();
mIsAnimating = false;
mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
mView.mPrivateFlags |= View.DRAWN;
if (DEBUG_DRAW) {
Context cxt = mView.getContext();
Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
", metrics=" + cxt.getResources().getDisplayMetrics() +
", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
}
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired
? DisplayMetrics.DENSITY_DEVICE : 0);
//在canvas中绘制
mView.draw(canvas);
} finally {
mAttachInfo.mIgnoreDirtyState = false;
canvas.restoreToCount(saveCount);
}
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
}
if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
int now = (int)SystemClock.elapsedRealtime();
if (sDrawTime != 0) {
nativeShowFPS(canvas, now - sDrawTime);
}
sDrawTime = now;
}
if (Config.DEBUG && ViewDebug.profileDrawing) {
EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
}
}
} finally {
//unlock画布
surface.unlockCanvasAndPost(canvas);
}
if (LOCAL_LOGV) {
Log.v("ViewRoot", "Surface " + surface + " unlockCanvasAndPost");
}
if (scrolling) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
}
总结下,ViewRoot能处理Handler的消息,Activity的显示就是由ViewRoot在它的performTraversals函数中完成的。
activity的绘制就是从mSurface中lock一块canvas,然后交给mView去画画,最后unlock并释放这块canvas。
surface
在ViewRoot构造时候,会创建一个surface,viewroot通过IWindowSession和WMS交互,WMS中调用的attach函数会构造一个SurfaceSession。而ViewRoot在performTraversal的过程会调用IWindowSession的relayout函数。
从relayout开始,relayout是一个跨进程调用,实际工作由WMS完成。
客户端的relayoutWindow:
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
float appScale = mAttachInfo.mApplicationScale;
boolean restore = false;
if (params != null && mTranslator != null) {
restore = true;
params.backup();
mTranslator.translateWindowLayout(params);
}
if (params != null) {
if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
}
mPendingConfiguration.seq = 0;
int relayoutResult = sWindowSession.relayout(
mWindow, params,
(int) (mView.mMeasuredWidth * appScale + 0.5f),
(int) (mView.mMeasuredHeight * appScale + 0.5f),
viewVisibility, insetsPending, mWinFrame,
mPendingContentInsets, mPendingVisibleInsets,
mPendingConfiguration, mSurface);//传递了surface
if (restore) {
params.restore();
}
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
}
return relayoutResult;
}
服务端的relayoutWindow
frameworks/base/services/java/com/android/server/WindowManagerService.java
public int relayoutWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, boolean insetsPending,
Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
Configuration outConfig, Surface outSurface) {
boolean displayed = false;
boolean inTouchMode;
boolean configChanged;
long origId = Binder.clearCallingIdentity();
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return 0;
}
win.mRequestedWidth = requestedWidth;
win.mRequestedHeight = requestedHeight;
if (attrs != null) {
mPolicy.adjustWindowParamsLw(attrs);
}
int attrChanges = 0;
int flagChanges = 0;
if (attrs != null) {
flagChanges = win.mAttrs.flags ^= attrs.flags;
attrChanges = win.mAttrs.copyFrom(attrs);
}
if (DEBUG_LAYOUT) Slog.v(TAG, "Relayout " + win + ": " + win.mAttrs);
if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
win.mAlpha = attrs.alpha;
}
final boolean scaledWindow =
((win.mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0);
if (scaledWindow) {
// requested{Width|Height} Surface's physical size
// attrs.{width|height} Size on screen
win.mHScale = (attrs.width != requestedWidth) ?
(attrs.width / (float)requestedWidth) : 1.0f;
win.mVScale = (attrs.height != requestedHeight) ?
(attrs.height / (float)requestedHeight) : 1.0f;
} else {
win.mHScale = win.mVScale = 1;
}
boolean imMayMove = (flagChanges&(
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0;
boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
&& (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
win.mRelayoutCalled = true;
final int oldVisibility = win.mViewVisibility;
win.mViewVisibility = viewVisibility;
if (viewVisibility == View.VISIBLE &&
(win.mAppToken == null || !win.mAppToken.clientHidden)) {
displayed = !win.isVisibleLw();
if (win.mExiting) {
win.mExiting = false;
win.mAnimation = null;
}
if (win.mDestroying) {
win.mDestroying = false;
mDestroySurface.remove(win);
}
if (oldVisibility == View.GONE) {
win.mEnterAnimationPending = true;
}
if (displayed) {
if (win.mSurface != null && !win.mDrawPending
&& !win.mCommitDrawPending && !mDisplayFrozen
&& mPolicy.isScreenOn()) {
applyEnterAnimationLocked(win);
}
if ((win.mAttrs.flags
& WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {
if (DEBUG_VISIBILITY) Slog.v(TAG,
"Relayout window turning screen on: " + win);
win.mTurnOnScreen = true;
}
int diff = 0;
if (win.mConfiguration != mCurConfiguration
&& (win.mConfiguration == null
|| (diff=mCurConfiguration.diff(win.mConfiguration)) != 0)) {
win.mConfiguration = mCurConfiguration;
if (DEBUG_CONFIGURATION) {
Slog.i(TAG, "Window " + win + " visible with new config: "
+ win.mConfiguration + " / 0x"
+ Integer.toHexString(diff));
}
outConfig.setTo(mCurConfiguration);
}
}
if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {
// To change the format, we need to re-build the surface.
win.destroySurfaceLocked();
displayed = true;
}
try {
//win就是WinStat,创建本地的surface对象
Surface surface = win.createSurfaceLocked();
if (surface != null) {
//在outSurface调用copyFrom,将本地surface拷贝到outSurface中
outSurface.copyFrom(surface);
win.mReportDestroySurface = false;
win.mSurfacePendingDestroy = false;
if (SHOW_TRANSACTIONS) Slog.i(TAG,
" OUT SURFACE " + outSurface + ": copied");
} else {
// For some reason there isn't a surface. Clear the
// caller's object so they see the same state.
outSurface.release();
}
} catch (Exception e) {
Slog.w(TAG, "Exception thrown when creating surface for client "
+ client + " (" + win.mAttrs.getTitle() + ")",
e);
Binder.restoreCallingIdentity(origId);
return 0;
}
if (displayed) {
focusMayChange = true;
}
if (win.mAttrs.type == TYPE_INPUT_METHOD
&& mInputMethodWindow == null) {
mInputMethodWindow = win;
imMayMove = true;
}
if (win.mAttrs.type == TYPE_BASE_APPLICATION
&& win.mAppToken != null
&& win.mAppToken.startingWindow != null) {
// Special handling of starting window over the base
// window of the app: propagate lock screen flags to it,
// to provide the correct semantics while starting.
final int mask =
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
WindowManager.LayoutParams sa = win.mAppToken.startingWindow.mAttrs;
sa.flags = (sa.flags&~mask) | (win.mAttrs.flags&mask);
}
} else {
win.mEnterAnimationPending = false;
if (win.mSurface != null) {
if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win
+ ": mExiting=" + win.mExiting
+ " mSurfacePendingDestroy=" + win.mSurfacePendingDestroy);
// If we are not currently running the exit animation, we
// need to see about starting one.
if (!win.mExiting || win.mSurfacePendingDestroy) {
// Try starting an animation; if there isn't one, we
// can destroy the surface right away.
int transit = WindowManagerPolicy.TRANSIT_EXIT;
if (win.getAttrs().type == TYPE_APPLICATION_STARTING) {
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
if (!win.mSurfacePendingDestroy && win.isWinVisibleLw() &&
applyAnimationLocked(win, transit, false)) {
focusMayChange = true;
win.mExiting = true;
mKeyWaiter.finishedKey(session, client, true,
KeyWaiter.RETURN_NOTHING);
} else if (win.isAnimating()) {
// Currently in a hide animation... turn this into
// an exit.
win.mExiting = true;
} else if (win == mWallpaperTarget) {
// If the wallpaper is currently behind this
// window, we need to change both of them inside
// of a transaction to avoid artifacts.
win.mExiting = true;
win.mAnimating = true;
} else {
if (mInputMethodWindow == win) {
mInputMethodWindow = null;
}
win.destroySurfaceLocked();
}
}
}
if (win.mSurface == null || (win.getAttrs().flags
& WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING) == 0
|| win.mSurfacePendingDestroy) {
// We are being called from a local process, which
// means outSurface holds its current surface. Ensure the
// surface object is cleared, but we don't want it actually
// destroyed at this point.
win.mSurfacePendingDestroy = false;
outSurface.release();
if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win);
} else if (win.mSurface != null) {
if (DEBUG_VISIBILITY) Slog.i(TAG,
"Keeping surface, will report destroy: " + win);
win.mReportDestroySurface = true;
outSurface.copyFrom(win.mSurface);
}
}
if (focusMayChange) {
//System.out.println("Focus may change: " + win.mAttrs.getTitle());
if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
imMayMove = false;
}
//System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
}
// updateFocusedWindowLocked() already assigned layers so we only need to
// reassign them at this point if the IM window state gets shuffled
boolean assignLayers = false;
if (imMayMove) {
if (moveInputMethodWindowsIfNeededLocked(false) || displayed) {
// Little hack here -- we -should- be able to rely on the
// function to return true if the IME has moved and needs
// its layer recomputed. However, if the IME was hidden
// and isn't actually moved in the list, its layer may be
// out of data so we make sure to recompute it.
assignLayers = true;
}
}
if (wallpaperMayMove) {
if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
assignLayers = true;
}
}
mLayoutNeeded = true;
win.mGivenInsetsPending = insetsPending;
if (assignLayers) {
assignLayersLocked();
}
configChanged = updateOrientationFromAppTokensLocked();
performLayoutAndPlaceSurfacesLocked();
if (displayed && win.mIsWallpaper) {
updateWallpaperOffsetLocked(win, mDisplay.getWidth(),
mDisplay.getHeight(), false);
}
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
outFrame.set(win.mFrame);
outContentInsets.set(win.mContentInsets);
outVisibleInsets.set(win.mVisibleInsets);
if (localLOGV) Slog.v(
TAG, "Relayout given client " + client.asBinder()
+ ", requestedWidth=" + requestedWidth
+ ", requestedHeight=" + requestedHeight
+ ", viewVisibility=" + viewVisibility
+ "\nRelayout returning frame=" + outFrame
+ ", surface=" + outSurface);
if (localLOGV || DEBUG_FOCUS) Slog.v(
TAG, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
inTouchMode = mInTouchMode;
}
if (configChanged) {
sendNewConfiguration();
}
Binder.restoreCallingIdentity(origId);
return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0)
| (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0);
}
createSurfaceLocked
frameworks/base/services/java/com/android/server/WindowManagerService.java:WindowState
Surface createSurfaceLocked() {
if (mSurface == null) {
mReportDestroySurface = false;
mSurfacePendingDestroy = false;
mDrawPending = true;
mCommitDrawPending = false;
mReadyToShow = false;
if (mAppToken != null) {
mAppToken.allDrawn = false;
}
int flags = 0;
if (mAttrs.memoryType == MEMORY_TYPE_PUSH_BUFFERS) {
flags |= Surface.PUSH_BUFFERS;
}
if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
flags |= Surface.SECURE;
}
if (DEBUG_VISIBILITY) Slog.v(
TAG, "Creating surface in session "
+ mSession.mSurfaceSession + " window " + this
+ " w=" + mFrame.width()
+ " h=" + mFrame.height() + " format="
+ mAttrs.format + " flags=" + flags);
int w = mFrame.width();
int h = mFrame.height();
if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
// for a scaled surface, we always want the requested
// size.
w = mRequestedWidth;
h = mRequestedHeight;
}
// Something is wrong and SurfaceFlinger will not like this,
// try to revert to sane values
if (w <= 0) w = 1;
if (h <= 0) h = 1;
mSurfaceShown = false;
mSurfaceLayer = 0;
mSurfaceAlpha = 1;
mSurfaceX = 0;
mSurfaceY = 0;
mSurfaceW = w;
mSurfaceH = h;
try {
mSurface = new Surface(
mSession.mSurfaceSession, mSession.mPid,
mAttrs.getTitle().toString(),
0, w, h, mAttrs.format, flags);
if (SHOW_TRANSACTIONS) Slog.i(TAG, " CREATE SURFACE "
+ mSurface + " IN SESSION "
+ mSession.mSurfaceSession
+ ": pid=" + mSession.mPid + " format="
+ mAttrs.format + " flags=0x"
+ Integer.toHexString(flags)
+ " / " + this);
} catch (Surface.OutOfResourcesException e) {
Slog.w(TAG, "OutOfResourcesException creating surface");
reclaimSomeSurfaceMemoryLocked(this, "create");
return null;
} catch (Exception e) {
Slog.e(TAG, "Exception creating surface", e);
return null;
}
if (localLOGV) Slog.v(
TAG, "Got surface: " + mSurface
+ ", set left=" + mFrame.left + " top=" + mFrame.top
+ ", animLayer=" + mAnimLayer);
if (SHOW_TRANSACTIONS) {
Slog.i(TAG, ">>> OPEN TRANSACTION");
if (SHOW_TRANSACTIONS) logSurface(this,
"CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" +
mFrame.width() + "x" + mFrame.height() + "), layer=" +
mAnimLayer + " HIDE", null);
}
Surface.openTransaction();//打开一个事务
try {
try {
mSurfaceX = mFrame.left + mXOffset;
mSurfaceY = mFrame.top + mYOffset;
mSurface.setPosition(mSurfaceX, mSurfaceY);
mSurfaceLayer = mAnimLayer;
mSurface.setLayer(mAnimLayer);
mSurfaceShown = false;
mSurface.hide();
if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
if (SHOW_TRANSACTIONS) logSurface(this, "DITHER", null);
mSurface.setFlags(Surface.SURFACE_DITHER,
Surface.SURFACE_DITHER);
}
} catch (RuntimeException e) {
Slog.w(TAG, "Error creating surface in " + w, e);
reclaimSomeSurfaceMemoryLocked(this, "create-init");
}
mLastHidden = true;
} finally {
if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION");
Surface.closeTransaction();//关闭事务
}
if (localLOGV) Slog.v(
TAG, "Created surface " + this);
}
return mSurface;
}
jni层分析
Surface无参构造
frameworks/base/core/java/android/view/Surface.java
/**
* Create an empty surface, which will later be filled in by
* readFromParcel().
* {@hide}
*/
public Surface() {
if (DEBUG_RELEASE) {
mCreationStack = new Exception();
}
//CompatibleCanvas继承自Canvas
mCanvas = new CompatibleCanvas();
}
canvas 根据文档可以知道,canvas相关的类有几个。
- Bitmap:存储像素,就是画布
- canvas:记载画图的工作,比如画圆形、方形
- drawing primitive:在计算机图形学中绘制基本形状的过程。基本形状通常指的是点、线和三角形。在OpenGL或其他图形渲染API中,绘制基本形状是构建复杂图形的基础。
- paint:描述绘画时候使用的颜色、风格(实线、虚线)
canvas一般会封装一块bitmap,作画就基于这块bitmap。
SurfaceSession
frameworks/base/core/java/android/view/SurfaceSession.java
/**
* An instance of this class represents a connection to the surface
* flinger, in which you can create one or more Surface instances that will
* be composited to the screen.
* {@hide}
*/
public class SurfaceSession {
/** Create a new connection with the surface flinger. */
public SurfaceSession() {
init();//native函数
}
看下这个init的实现:
SurfaceSession_init
frameworks/base/core/jni/android_view_Surface.cpp
static void SurfaceSession_init(JNIEnv* env, jobject clazz)
{
//创建了SurfaceComposerClient对象
sp<SurfaceComposerClient> client = new SurfaceComposerClient;
client->incStrong(clazz);
//在java对象中保存它
env->SetIntField(clazz, sso.client, (int)client.get());
}
surface的有参构造
frameworks/base/core/java/android/view/Surface.java
/**
* create a surface with a name
* {@hide}
*/
public Surface(SurfaceSession s,//传入SurfaceSession
int pid, String name, int display, int w, int h, int format, int flags)
throws OutOfResourcesException {
if (DEBUG_RELEASE) {
mCreationStack = new Exception();
}
mCanvas = new CompatibleCanvas();
//native函数,传递了display,w h是宽高
init(s,pid,name,display,w,h,format,flags);
mName = name;
}
看下 init(s,pid,name,display,w,h,format,flags);
frameworks/base/core/jni/android_view_Surface.cpp
static void Surface_init(
JNIEnv* env, jobject clazz,
jobject session,
jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
{
if (session == NULL) {
doThrow(env, "java/lang/NullPointerException");
return;
}
//从 SurfaceSession对象中取出之前创建的之前创建的SurfaceComposerClient对象
SurfaceComposerClient* client =
(SurfaceComposerClient*)env->GetIntField(session, sso.client);
//SurfaceControl
sp<SurfaceControl> surface;
if (jname == NULL) {
surface = client->createSurface(pid, dpy, w, h, format, flags);
} else {
const jchar* str = env->GetStringCritical(jname, 0);
const String8 name(str, env->GetStringLength(jname));
env->ReleaseStringCritical(jname, str);
//调用SurfaceComposerClient的createSurface,返回的是是SurfaceControl
surface = client->createSurface(pid, name, dpy, w, h, format, flags);
}
if (surface == 0) {
doThrow(env, OutOfResourcesException);
return;
}
//把SurfaceControl设置到java层的surface对象中
setSurfaceControl(env, clazz, surface);
}
Surface_copyFrom
static void Surface_copyFrom(
JNIEnv* env, jobject clazz, jobject other)
{
if (clazz == other)
return;
if (other == NULL) {
doThrow(env, "java/lang/NullPointerException", NULL);
return;
}
/*
* This is used by the WindowManagerService just after constructing
* a Surface and is necessary for returning the Surface reference to
* the caller. At this point, we should only have a SurfaceControl.
*/
const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz);
const sp<SurfaceControl>& rhs = getSurfaceControl(env, other);
if (!SurfaceControl::isSameSurface(surface, rhs)) {
// we reassign the surface only if it's a different one
// otherwise we would loose our client-side state.
setSurfaceControl(env, clazz, rhs);
}
}
Surface_writeToParcel
static void Surface_writeToParcel(
JNIEnv* env, jobject clazz, jobject argParcel, jint flags)
{
Parcel* parcel = (Parcel*)env->GetIntField(
argParcel, no.native_parcel);
if (parcel == NULL) {
doThrow(env, "java/lang/NullPointerException", NULL);
return;
}
const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));
SurfaceControl::writeSurfaceToParcel(control, parcel);
if (flags & PARCELABLE_WRITE_RETURN_VALUE) {
setSurfaceControl(env, clazz, 0);
}
}
Surface_readFromParcel
static void Surface_readFromParcel(
JNIEnv* env, jobject clazz, jobject argParcel)
{
Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);
if (parcel == NULL) {
doThrow(env, "java/lang/NullPointerException", NULL);
return;
}
const sp<Surface>& control(getSurface(env, clazz));
sp<Surface> rhs = new Surface(*parcel);
if (!Surface::isSameSurface(control, rhs)) {
// we reassign the surface only if it's a different one
// otherwise we would loose our client-side state.
setSurface(env, clazz, rhs);
}
}
这里面WMS的surface调用了带参的SurfaceSession构造函数,ViewRoot的surface调用了无参的构造函数。
copyFrom就是把WMS的surface信息,拷贝到ViewRoot中的surface中。这其中使用了AIDL,xxx.aidl文件可以转换为java文件.
总结
我们来回顾和总结一路过来的分析,为后续破解surfaceflinger做准备。
创建了一个SurfaeComposerClient,调用它的createSurface,拿到一个SurfaceControl对象。调用SurfaceControl的writeToParcel把信息写道parcel中。根据parcel的信息构造一个surface对象,并保存到java层的mSurface对象中。
这样viewroot得到了一个native的surface对象。