【Android surface 】二:源码分析App的surface创建过程

文章目录

  • 画布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 addedmAdded = 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*///第一个参数是decorviewpublic 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.javapublic 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@Overridepublic IWindowSession openSession(IWindowSessionCallback callback) {return new Session(this, callback);}
frameworks/base/services/java/com/android/server/WindowManagerService.javapublic 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绘制

 @Overridepublic 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 canvascanvas = 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);//传递了surfaceif (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.javapublic 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 screenwin.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 shuffledboolean 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:WindowStateSurface 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 valuesif (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继承自CanvasmCanvas = 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,//传入SurfaceSessionint 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);//SurfaceControlsp<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,返回的是是SurfaceControlsurface = 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对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/306857.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文心一言

文章目录 前言一、首页二、使用总结 前言 今天给大家带来百度的文心一言,它基于百度的文心大模型,是一种全新的生成式人工智能工具。 一、首页 首先要登录才能使用,左侧可以看到以前的聊天历史 3.5的目前免费用,但是4.0的就需要vip了 二、使用 首先在最下方文本框输入你想要搜…

Harmony鸿蒙南向驱动开发-SDIO接口使用

功能简介 SDIO是安全数字输入输出接口&#xff08;Secure Digital Input and Output&#xff09;的缩写&#xff0c;是从SD内存卡接口的基础上演化出来的一种外设接口。SDIO接口兼容以前的SD卡&#xff0c;并且可以连接支持SDIO接口的其他设备。 SDIO接口定义了操作SDIO的通用…

总分410+专业130+国防科技大学831信号与系统考研经验国防科大电子信息与通信工程,真题,大纲,参考书。

好几个学弟催着&#xff0c;总结一下我自己的复习经历&#xff0c;希望大家复习少走弯路&#xff0c;投入的复习正比换回分数。我专业课831信号与系统130&#xff08;感觉比估分要低&#xff0c;后面找Jenny老师讨论了自己拿不准的地方也没有错误&#xff0c;心里最近也这经常回…

记账本|基于SSM的家庭记账本小程序设计与实现(源码+数据库+文档)

家庭记账本小程序目录 基于SSM的家庭记账本小程序设计与实现 一、前言 二、系统设计 三、系统功能设计 1、小程序端&#xff1a; 2、后台 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大…

【opencv】示例-imagelist_creator.cpp 从命令行参数中创建一个图像文件列表(yaml格式)...

/* 这个程序可以创建一个命令行参数列表的yaml或xml文件列表 */ // 包含必要的OpenCV头文件 #include "opencv2/core.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" #include <string> #include <iostream>…

视频秒播优化实践

本文字数&#xff1a;2259字 预计阅读时间&#xff1a;10分钟 视频起播时间&#xff0c;即首帧时间&#xff0c;是视频类应用的一个重要核心指标&#xff0c;也是影响用户观看体验的核心因素之一。如果视频要加载很久才能开始播放&#xff0c;用户放弃播放甚至离开 App 的概率都…

飞书api增加权限

1&#xff0c;进入飞书开发者后台&#xff1a;飞书开放平台 给应用增加权限 2&#xff0c;进入飞书管理后台 https://fw5slkpbyb3.feishu.cn/admin/appCenter/audit 审核最新发布的版本 如果还是不行&#xff0c;则需要修改数据权限&#xff0c;修改为全部成员可修改。 改完…

大数据架构之关系型数据仓库——解读大数据架构(二)

文章目录 前言什么是关系型数仓对数仓的错误认识与使用自上而下的方法关系型数仓的优点关系型数仓的缺点数据加载加载数据的频率如何确定变更数据 关系型数仓会消失吗总结 前言 本文对关系型数据仓库&#xff08;RDW&#xff09;进行了简要的介绍说明&#xff0c;包括什么是关…

课时93:流程控制_函数进阶_综合练习

1.1.3 综合练习 学习目标 这一节&#xff0c;我们从 案例解读、脚本实践、小结 三个方面来学习。 案例解读 案例需求 使用shell脚本绘制一个杨辉三角案例解读 1、每行数字左右对称&#xff0c;从1开始变大&#xff0c;然后变小为1。    2、第n行的数字个数为n个&#xf…

Bug及异常:unity场景角色移动卡墙壁的问题

场景是一个小的杠铃形状封闭空间&#xff0c;美术没有给包围盒&#xff0c;我自己用blender做了一个&#xff08;属于兴趣爱好&#xff09;&#xff0c;如下&#xff1a; 导入场景中使用meshcollider做成空气墙&#xff0c;发现角色移动到角落继续行走会卡角落处&#x…

谷歌pixel6/7pro等手机WiFi不能上网,显示网络连接受限

近期在项目中遇到一个机型出现的问题,先对项目代码进行排查,发现别的设备都能正常运行,就开始来排查机型的问题,特意写出来方便后续查看,也方便其它开发者来自查。 设备机型:Pixel 6a 设备安卓版本:13 该方法无需root,只需要电脑设备安装adb(即Android Debug Bridge…

分布式技术---------------消息队列中间件之 Kafka

目录 一、Kafka 概述 1.1为什么需要消息队列&#xff08;MQ&#xff09; 1.2使用消息队列的好处 1.2.1解耦 1.2.2可恢复性 1.2.3缓冲 1.2.4灵活性 & 峰值处理能力 1.2.5异步通信 1.3消息队列的两种模式 1.3.1点对点模式&#xff08;一对一&#xff0c;消费者主动…

出海企业如何从海外云手机中受益?

随着全球化的推进&#xff0c;越来越多的企业开始将目光投向海外市场。然而&#xff0c;不同国家和地区的网络环境、政策限制&#xff0c;以及语言文化的差异&#xff0c;给出海企业的市场拓展带来了诸多挑战。在这一背景下&#xff0c;海外云手机作为一种新兴解决方案&#xf…

MySQL如何定位慢查询?如何分析这条慢查询?

常见的慢查询 聚合查询&#xff08;常用的聚合函数有&#xff1a;MAX&#xff08;&#xff09;、MIN&#xff08;&#xff09;、COUNT&#xff08;&#xff09;、SUM&#xff08;&#xff09;、AVG&#xff08;&#xff09;&#xff09;。 多表查询 表数据过大查询 深度分页…

【Tomcat 文件读取/文件包含(CVE-2020-1938)漏洞复现】

文章目录 前言 一、漏洞名称 二、漏洞描述 三、受影响端口 四、受影响版本 五、漏洞验证 六、修复建议 前言 近日在做漏扫时发现提示服务器存在CVE-2020-1938漏洞&#xff0c;故文章记录一下相关内容。 一、漏洞名称 Tomcat 文件读取/文件包含漏洞(CVE-2020-1938) 二、漏洞描…

【SpringBoot】mybatis-plus实现增删改查

mapper继承BaseMapper service 继承ServiceImpl 使用方法新增 save,updateById新增和修改方法返回boolean值,或者使用saveOrUpdate方法有id执行修改操作,没有id 执行新增操作 案例 Service public class UserService extends ServiceImpl<UserMapper,User> {// Au…

送礼物动态特效直播和短视频特效源码送礼物动态连麦PK特效语音视频聊天室朋友圈十套

内附十套动画效果源码&#xff0c;可F5刷新随机显示特效预览。送礼物动态特效直播和短视频特效源码送礼物动态连麦PK特效语音视频聊天室朋友圈十套 SVGA 是一种用于嵌入式动画的矢量文件格式&#xff0c;通常用于在移动应用程序和网页中展示高质量的动画效果。相对于传统的 GIF…

办公软件巨头CCED、WPS迎来新挑战,新款办公软件已形成普及之势

办公软件巨头CCED、WPS的成长经历 CCED与WPS&#xff0c;这两者均是中国办公软件行业的佼佼者&#xff0c;为人们所熟知。 然而&#xff0c;它们的成功并非一蹴而就&#xff0c;而是经过了长时间的积累与沉淀。 CCED&#xff0c;这款中国大陆早期的文本编辑器&#xff0c;在上…

区块链安全-----区块链基础

区块链是一种全新的信息网络架构 &#xff0c;是新一代信息基础设施 &#xff0c;是新型的价值交换方式、 分布式协 同生产机制以及新型的算法经济模式的基础。 区块链技术可以集成到多个领域。 区块链的主要用途 是作为加密货币的分布式总帐。 它在银行 &#xff0c;金融 &…

软件定义车队面临网络安全的曲折之路

当以色列 REE Automotive 设计其 P7 电动汽车底盘时&#xff0c;它是从软件开始工作的&#xff1a;扁平的车辆底盘完全可配置&#xff0c;每个轮胎附近有四个独立的模块&#xff0c;用于转向、制动、悬架和动力传动系统&#xff0c;每个模块均由电子驱动控制单元&#xff08;EC…