上一篇我們看到了ViewRootImpl的setView函數,ViewRootImpl的setView函數,主要流程分爲兩步:
1.向WMS請求添加窗口並建立SurfaceFlinger鏈接
2.APP端請求獲取Surface,WMS建立SurfaceControl等等。
本篇代碼基於原生Android Q
一.顯示過程概覽
老規矩,先上圖:
二. 核心代碼流程
1.ViewRootImpl.setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
//......
// 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();//請求surface,建立SurfaceControl,獲取surfaceFlinger的gbp對象
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//向WMS申請添加窗口,建立WindowsState,SurfaceSession,SurfaceComposerClient等核心對象
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
setFrame(mTmpFrame);
}
//......
}
從執行流程來說,本篇先介紹addToDisplay方法
2.Session裏面會調用WMS的addWindow方法,該方法中有以下幾處關鍵的邏輯:
2.1 校驗窗口是否重複添加,校驗窗口的類型,校驗窗口token的有效性
if (mWindowMap.containsKey(client.asBinder())) { //校驗重複添加
Slog.w(TAG_WM, "Window " + client + " is already added");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { //校驗子窗口類型
parentWindow = windowForClientLocked(null, attrs.token, false);
if (parentWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display. Aborting.");
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
}
AppWindowToken atoken = null;
final boolean hasParent = parentWindow != null;
// Use existing parent window token for child windows since they go in the same token
// as there parent window so we can apply the same policy on them.
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
// If this is a child window, we want to apply the same type checking rules as the
// parent window type.
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
if (token == null) { //校驗token=null的時候的窗口類型,返回錯誤信息
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
parentWindow)) {
Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
final boolean isRoundedCornerOverlay =
(attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
atoken = token.asAppWindowToken();
if (atoken == null) {
Slog.w(TAG_WM, "Attempted to add window with non-application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
} else if (atoken.removed) {
Slog.w(TAG_WM, "Attempted to add window with exiting application token "
+ token + ". Aborting.");
return WindowManagerGlobal.ADD_APP_EXITING;
} else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {
Slog.w(TAG_WM, "Attempted to add starting window to token with already existing"
+ " starting window");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
} else if (rootType == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
Slog.w(TAG_WM, "Attempted to add input method window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_VOICE_INTERACTION) {
if (token.windowType != TYPE_VOICE_INTERACTION) {
Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_WALLPAPER) {
if (token.windowType != TYPE_WALLPAPER) {
Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_DREAM) {
if (token.windowType != TYPE_DREAM) {
Slog.w(TAG_WM, "Attempted to add Dream window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
callingUid, parentWindow);
if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
Slog.w(TAG_WM, "Attempted to add a toast window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_QS_DIALOG) {
if (token.windowType != TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (token.asAppWindowToken() != null) {
Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);
// It is not valid to use an app token with other system types; we will
// instead make a new token for it (as if null had been passed in for the token).
attrs.token = null;
token = new WindowToken(this, client.asBinder(), type, false, displayContent,
session.mCanAddInternalSystemWindow);
}
2.2 建立WindowState對象,作爲WMS端的窗口實體
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
2.3 通過WindowState.attach進行與SurfaceFlinger的鏈接
win.attach();
mWindowMap.put(client.asBinder(), win);
3. win.attach
void attach() {
if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
mSession.windowAddedLocked(mAttrs.packageName);
}
4. Session裏面
void windowAddedLocked(String packageName) {
mPackageName = packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
if (mSurfaceSession == null) {
if (WindowManagerService.localLOGV) Slog.v(
TAG_WM, "First window added to " + this + ", creating SurfaceSession");
mSurfaceSession = new SurfaceSession(); //App第一次新建Window會走到這邊
if (SHOW_TRANSACTIONS) Slog.i(
TAG_WM, " NEW SURFACE SESSION " + mSurfaceSession);
mService.mSessions.add(this);
if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
mService.dispatchNewAnimatorScaleLocked(this);
}
}
mNumWindow++;
}
5. new SurfaceSession
public SurfaceSession() {
mNativeClient = nativeCreate();
}
6. 走到SurfaceSession的JNI方法
static jlong nativeCreate(JNIEnv* env, jclass clazz) {
SurfaceComposerClient* client = new SurfaceComposerClient();
client->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(client);
}
7.看看怎麼初始化SurfaceComposerClient,此類繼承於RefBase類,首次調用onFirstRef方法:
void SurfaceComposerClient::onFirstRef() {
sp<ISurfaceComposer> sf(ComposerService::getComposerService()); //通過ComposerService獲得Bp對象
if (sf != nullptr && mStatus == NO_INIT) {
sp<ISurfaceComposerClient> conn;
conn = sf->createConnection(); //通過sf對象創建ISurfaceComposerClient的Bp對象
if (conn != nullptr) {
mClient = conn;
mStatus = NO_ERROR;
}
}
}
8.繼續往下看:
/*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() {
ComposerService& instance = ComposerService::getInstance(); //獲得ComposerService的單例
Mutex::Autolock _l(instance.mLock);
if (instance.mComposerService == nullptr) {
ComposerService::getInstance().connectLocked(); //調用單例方法
assert(instance.mComposerService != nullptr);
ALOGD("ComposerService reconnected");
}
return instance.mComposerService;
}
9.看看connectLocked:
void ComposerService::connectLocked() {
const String16 name("SurfaceFlinger");
while (getService(name, &mComposerService) != NO_ERROR) { //通過ServiceManager獲取SurfaceFlinger的代理服務
usleep(250000);
}
assert(mComposerService != nullptr);
// Create the death listener.
......
}
10.看看IServiceManager.h
status_t getService(const String16& name, sp<INTERFACE>* outService)
{
const sp<IServiceManager> sm = defaultServiceManager(); //獲得ServiceManager的代理對象
if (sm != nullptr) {
*outService = interface_cast<INTERFACE>(sm->getService(name)); //獲取SurfaceFlinger的代理服務
if ((*outService) != nullptr) return NO_ERROR;
}
return NAME_NOT_FOUND;
}
11.回到onFirstRef的createConnection方法,肯定是調用BpSurfaceComposer的方法:
class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
{
public:
explicit BpSurfaceComposer(const sp<IBinder>& impl)
: BpInterface<ISurfaceComposer>(impl)
{
}
virtual ~BpSurfaceComposer();
virtual sp<ISurfaceComposerClient> createConnection()
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
//遠程調用BnSurfaceComposer的方法
remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply);
return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
12.看看BnSurfaceComposer如何接收:
status_t BnSurfaceComposer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case CREATE_CONNECTION: { //根據code類型會走到這裏
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> b = IInterface::asBinder(createConnection()); //調用createConnection方法
reply->writeStrongBinder(b);
return NO_ERROR;
}
上面是一次典型的binder調用過程。
13.回調SurfaceFlinger的方法:
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
return initClient(new Client(this)); //返回Client對象 class Client : public BnSurfaceComposerClient
}
到這裏,SurfaceComposerClient的mClient對象就得到值了,當然了,肯定是BpSurfaceComposerClient對象。
附上一張Native核心binder架構圖:
三.總結
本篇重點關注了WindowState,SurfaceSession,SurfaceComposerClient等核心對象的創建和傳遞,這裏便是APP與SurfaceFlinger建立連接的主要邏輯。