本篇是接上一篇Android R WindowManagerService 添加window过程分析 (一), 分析addWindow part3 , 这部分的主要工作有:
- 创建 WindowState 对象, 用于管理窗口的各种信息
- displayPolicy调整客户端传过来的params参数, 使用策略执行诸如确保特定类型的窗口无法获得输入焦点之类的操作
- displayPolicy 检查窗口是否能被添加到系统. 如对于某一个Display,只能添加一个StatusBar
- 判断是否要创建 InputChannel , 这个跟window的input事件息息相关
- win.attach() 回调 session的 windowAddedLocked , 创建SurfaceSession
- WindowState的保存及处理 app op 状态等
下面依次来分析这些细节:
创建 WindowState
构造WindowState有很多重要参数:
- client 客服端IWindow对象的binder bp对象, 用于通知window相关改变
- token 窗口对应的WindowToken
- session 与客户端对应的session的服务端
- appOp[0] appOp 操作
- seq 与客户端同步的sequence
下面看WindowState创建
// @WMS#addWindow
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
WindowState 的构造方法如下,大致做了如下几件事:
- 成员初始化
- 注册客户端IWindow的死亡监听
- 分配 window 的层级
- 创建WindowStateAnimator 用于窗口的动画
- 创建InputWindowHandle, 用于接收input事件
如果存在父窗口, 添加到父窗口建立与其的联系
// @frameworks/base/services/core/java/com/android/server/wm/WindowState.java
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, int ownerId, int showUserId,
boolean ownerCanAddInternalSystemWindow, PowerManagerWrapper powerManagerWrapper) {
super(service);
// 初始化成员
mSession = s;
mClient = c;
mAppOp = appOp;
mToken = token;
mActivityRecord = mToken.asActivityRecord(); // 如果token是应用类型的,此处返回一个ActivityRecord,否则为null
mOwnerUid = ownerId;
mShowUserId = showUserId;
mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
mWindowId = new WindowId(this);
mAttrs.copyFrom(a);
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
mViewVisibility = viewVisibility;
mPolicy = mWmService.mPolicy;
mContext = mWmService.mContext;
DeathRecipient deathRecipient = new DeathRecipient();
mSeq = seq;
mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
if (DEBUG) {
Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
}
try { // 注册客户端IWindow的死亡监听
c.asBinder().linkToDeath(deathRecipient, 0);
} catch (RemoteException e) { // 发送异常表示客户端已经不存在了
// 清除相关成员变量
mDeathRecipient = null;
...
return;
}
mDeathRecipient = deathRecipient;
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) { // 处理子窗口
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
// 计算层级值 , TYPE_LAYER_MULTIPLIER = 10000 , TYPE_LAYER_OFFSET = 1000
// mBaseLayer = 父窗口type对应的层级值 * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type); // 通过type获取一个子窗口的层级值
mIsChildWindow = true;
mLayoutAttached = mAttrs.type !=
WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
mIsImWindow = parentWindow.mAttrs.type == TYPE_INPUT_METHOD
|| parentWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
mIsWallpaper = parentWindow.mAttrs.type == TYPE_WALLPAPER;
} else {
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
// 此窗口type对应的层级值 * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = 0;
mIsChildWindow = false;
mLayoutAttached = false;
mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
|| mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
}
mIsFloatingLayer = mIsImWindow || mIsWallpaper;
if (mActivityRecord != null && mActivityRecord.mShowForAllUsers) {
// Windows for apps that can show for all users should also show when the device is
// locked.
mAttrs.flags |= FLAG_SHOW_WHEN_LOCKED;
}
mWinAnimator = new WindowStateAnimator(this); // 创建animator,用于此窗口的动画
mWinAnimator.mAlpha = a.alpha;
mRequestedWidth = 0;
mRequestedHeight = 0;
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
mLayer = 0;
mInputWindowHandle = new InputWindowHandle( // 创建InputWindowHandle, 用于接收input事件
mActivityRecord != null ? mActivityRecord.mInputApplicationHandle : null,
getDisplayId());
// Make sure we initial all fields before adding to parentWindow, to prevent exception
// during onDisplayChanged.
if (mIsChildWindow) { // 将此子window添加到父窗口
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", this, parentWindow);
parentWindow.addChild(this, sWindowSubLayerComparator);
}
// System process or invalid process cannot register to display config change.
mWpcForDisplayConfigChanges = (s.mPid == MY_PID || s.mPid < 0)
? null
: service.mAtmService.getProcessController(s.mPid, s.mUid);
}
getWindowLayerLw
下面看看是如何通过WindowState获取layer的:
// @frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java
/**
* Returns the layer assignment for the window state. Allows you to control how different
* kinds of windows are ordered on-screen.
*
* @param win The window state
* @return int An arbitrary integer used to order windows, with lower numbers below higher ones.
*/
default int getWindowLayerLw(WindowState win) {
// 获取WindowState对应的type. 然后获取
return getWindowLayerFromTypeLw(win.getBaseType(), win.canAddInternalSystemWindow());
}
// 通过window对应的type获取层级值
default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { // 应用类型的窗口
return APPLICATION_LAYER; // APPLICATION_LAYER = 2
}
// 对于某些系统type, 有对应的固定层级值
switch (type) {
case TYPE_WALLPAPER:
// wallpaper is at the bottom, though the window manager may move it.
return 1;
case TYPE_PRESENTATION:
case TYPE_PRIVATE_PRESENTATION:
return APPLICATION_LAYER;
case TYPE_DOCK_DIVIDER:
return APPLICATION_LAYER;
case TYPE_QS_DIALOG:
return APPLICATION_LAYER;
case TYPE_PHONE:
return 3;
case TYPE_SEARCH_BAR:
case TYPE_VOICE_INTERACTION_STARTING:
return 4;
case TYPE_VOICE_INTERACTION:
// voice interaction layer is almost immediately above apps.
return 5;
case TYPE_INPUT_CONSUMER:
return 6;
case TYPE_SYSTEM_DIALOG:
return 7;
case TYPE_TOAST:
// toasts and the plugged-in battery thing
return 8;
case TYPE_PRIORITY_PHONE:
// SIM errors and unlock. Not sure if this really should be in a high layer.
return 9;
case TYPE_SYSTEM_ALERT:
// like the ANR / app crashed dialogs
// Type is deprecated for non-system apps. For system apps, this type should be
// in a higher layer than TYPE_APPLICATION_OVERLAY.
return canAddInternalSystemWindow ? 13 : 10;
case TYPE_APPLICATION_OVERLAY:
case TYPE_TRUSTED_APPLICATION_OVERLAY:
return 12;
case TYPE_INPUT_METHOD:
// on-screen keyboards and other such input method user interfaces go here.
return 15;
case TYPE_INPUT_METHOD_DIALOG:
// on-screen keyboards and other such input method user interfaces go here.
return 16;
case TYPE_STATUS_BAR:
return 17;
case TYPE_STATUS_BAR_ADDITIONAL:
return 18;
case TYPE_NOTIFICATION_SHADE:
return 19;
case TYPE_STATUS_BAR_SUB_PANEL:
return 20;
case TYPE_KEYGUARD_DIALOG:
return 21;
case TYPE_VOLUME_OVERLAY:
// the on-screen volume indicator and controller shown when the user
// changes the device volume
return 22;
case TYPE_SYSTEM_OVERLAY:
// the on-screen volume indicator and controller shown when the user
// changes the device volume
return canAddInternalSystemWindow ? 23 : 11;
case TYPE_NAVIGATION_BAR:
// the navigation bar, if available, shows atop most things
return 24;
case TYPE_NAVIGATION_BAR_PANEL:
// some panels (e.g. search) need to show on top of the navigation bar
return 25;
case TYPE_SCREENSHOT:
// screenshot selection layer shouldn't go above system error, but it should cover
// navigation bars at the very least.
return 26;
case TYPE_SYSTEM_ERROR:
// system-level error dialogs
return canAddInternalSystemWindow ? 27 : 10;
case TYPE_MAGNIFICATION_OVERLAY:
// used to highlight the magnified portion of a display
return 28;
case TYPE_DISPLAY_OVERLAY:
// used to simulate secondary display devices
return 29;
case TYPE_DRAG:
// the drag layer: input for drag-and-drop is associated with this window,
// which sits above all other focusable windows
return 30;
case TYPE_ACCESSIBILITY_OVERLAY:
// overlay put by accessibility services to intercept user interaction
return 31;
case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
return 32;
case TYPE_SECURE_SYSTEM_OVERLAY:
return 33;
case TYPE_BOOT_PROGRESS:
return 34;
case TYPE_POINTER:
// the (mouse) pointer layer
return 35;
default:
Slog.e("WindowManager", "Unknown window type: " + type);
return APPLICATION_LAYER;
}
}
getSubWindowLayerFromTypeLw
对于子窗口的层级值:
// @frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java
/**
* Return how to Z-order sub-windows in relation to the window they are attached to.
* Return positive to have them ordered in front, negative for behind.
*
* @param type The sub-window type code.
*
* @return int Layer in relation to the attached window, where positive is
* above and negative is below.
*/
default int getSubWindowLayerFromTypeLw(int type) {
switch (type) {
case TYPE_APPLICATION_PANEL:
case TYPE_APPLICATION_ATTACHED_DIALOG:
return APPLICATION_PANEL_SUBLAYER;
case TYPE_APPLICATION_MEDIA:
return APPLICATION_MEDIA_SUBLAYER;
case TYPE_APPLICATION_MEDIA_OVERLAY:
return APPLICATION_MEDIA_OVERLAY_SUBLAYER;
case TYPE_APPLICATION_SUB_PANEL:
return APPLICATION_SUB_PANEL_SUBLAYER;
case TYPE_APPLICATION_ABOVE_SUB_PANEL:
return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;
}
Slog.e("WindowManager", "Unknown sub-window type: " + type);
return 0; // 默认值是0
}
DisplayPolicy#adjustWindowParamsLw
这个方法是处理来自client的参数, 如处理input focus , 确保特定的type的window不能获取焦点, 以下type的窗口不能获取input focus
- TYPE_SYSTEM_OVERLAY:
- TYPE_SECURE_SYSTEM_OVERLAY:
- TYPE_TOAST:
如下代码
// @frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
/**
* Sanitize the layout parameters coming from a client. Allows the policy
* to do things like ensure that windows of a specific type can't take
* input focus.
*
* @param attrs The window layout parameters to be modified. These values
* are modified in-place.
*/
public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
int callingPid, int callingUid) {
// 判断是不是PRIVATE_FLAG_IS_SCREEN_DECOR, 这个FLAG 表示装饰窗口 screen decoration similar to the nav bar and status bar
final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
if (mScreenDecorWindows.contains(win)) {
if (!isScreenDecor) {
// No longer has the flag set, so remove from the set.
mScreenDecorWindows.remove(win);
}
} else if (isScreenDecor && hasStatusBarServicePermission(callingPid, callingUid)) {
mScreenDecorWindows.add(win);
}
switch (attrs.type) {
case TYPE_SYSTEM_OVERLAY:
case TYPE_SECURE_SYSTEM_OVERLAY:
// These types of windows can't receive input events.
attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
break;
case TYPE_WALLPAPER:
// Dreams and wallpapers don't have an app window token and can thus not be
// letterboxed. Hence always let them extend under the cutout.
attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
break;
case TYPE_NOTIFICATION_SHADE:
// If the Keyguard is in a hidden state (occluded by another window), we force to
// remove the wallpaper and keyguard flag so that any change in-flight after setting
// the keyguard as occluded wouldn't set these flags again.
// See {@link #processKeyguardSetHiddenResultLw}.
if (mService.mPolicy.isKeyguardOccluded()) {
attrs.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
}
break;
case TYPE_TOAST:
// While apps should use the dedicated toast APIs to add such windows
// it possible legacy apps to add the window directly. Therefore, we
// make windows added directly by the app behave as a toast as much
// as possible in terms of timeout and animation.
if (attrs.hideTimeoutMilliseconds < 0
|| attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) {
attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT;
}
// Accessibility users may need longer timeout duration. This api compares
// original timeout with user's preference and return longer one. It returns
// original timeout if there's no preference.
attrs.hideTimeoutMilliseconds = mAccessibilityManager.getRecommendedTimeoutMillis(
(int) attrs.hideTimeoutMilliseconds,
AccessibilityManager.FLAG_CONTENT_TEXT);
attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
// Toast can show with below conditions when the screen is locked.
if (canToastShowWhenLocked(callingPid)) {
attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
}
// Toasts can't be clickable
attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
break;
case TYPE_BASE_APPLICATION:
// A non-translucent main app window isn't allowed to fit insets, as it would create
// a hole on the display!
if (attrs.isFullscreen() && win.mActivityRecord != null
&& win.mActivityRecord.fillsParent()
&& (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0
&& attrs.getFitInsetsTypes() != 0) {
throw new RuntimeException("Illegal attributes: Main activity window that isn't"
+ " translucent trying to fit insets: "
+ attrs.getFitInsetsTypes()
+ " attrs=" + attrs);
}
break;
}
}
DisplayPolicy.validateAddingWindowLw
每个Display有一个displayId,对应着一个DisplayContent, 每个DisplayContent会创建一个DisplayPolicy, 因此在DisplayPolicy中确保WindowState的唯一性.可以保证此window在display的唯一性
此方法会检查添加相关type的窗口所需要的权限, 另外对于以下特殊的type在每个display禁止重复添加:
- TYPE_STATUS_BAR
- TYPE_NOTIFICATION_SHADE
- TYPE_NAVIGATION_BAR
代码如下
// @frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
/**
* Check if a window can be added to the system.
*
* Currently enforces that two window types are singletons per display:
* <ul>
* <li>{@link WindowManager.LayoutParams#TYPE_STATUS_BAR}</li>
* <li>{@link WindowManager.LayoutParams#TYPE_NOTIFICATION_SHADE}</li>
* <li>{@link WindowManager.LayoutParams#TYPE_NAVIGATION_BAR}</li>
* </ul>
*
* @param attrs Information about the window to be added.
*
* @return If ok, WindowManagerImpl.ADD_OKAY. If too many singletons,
* WindowManagerImpl.ADD_MULTIPLE_SINGLETON
*/
int validateAddingWindowLw(WindowManager.LayoutParams attrs, int callingPid, int callingUid) {
if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) {
mContext.enforcePermission(
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
}
switch (attrs.type) {
case TYPE_STATUS_BAR:
mContext.enforcePermission(
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
if (mStatusBar != null) {
if (mStatusBar.isAlive()) {
return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
}
break;
case TYPE_NOTIFICATION_SHADE:
mContext.enforcePermission(
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
if (mNotificationShade != null) {
if (mNotificationShade.isAlive()) {
return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
}
break;
case TYPE_NAVIGATION_BAR:
mContext.enforcePermission(
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
if (mNavigationBar != null) {
if (mNavigationBar.isAlive()) {
return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
}
break;
case TYPE_NAVIGATION_BAR_PANEL:
// Check for permission if the caller is not the recents component.
if (!mService.mAtmInternal.isCallerRecents(callingUid)) {
mContext.enforcePermission(
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
}
break;
case TYPE_STATUS_BAR_ADDITIONAL:
case TYPE_STATUS_BAR_SUB_PANEL:
case TYPE_VOICE_INTERACTION_STARTING:
mContext.enforcePermission(
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
break;
case TYPE_TRUSTED_APPLICATION_OVERLAY:
mContext.enforcePermission(
android.Manifest.permission.INTERNAL_SYSTEM_WINDOW, callingPid, callingUid,
"DisplayPolicy");
break;
case TYPE_STATUS_BAR_PANEL:
return WindowManagerGlobal.ADD_INVALID_TYPE;
}
if (attrs.providesInsetsTypes != null) {
mContext.enforcePermission(
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes);
}
return ADD_OKAY;
}
创建 InputChannel
在WMS的addWindow方法方法会根据客户端传递过来的参数判断是否创建input channel, 要创建需满足以下条件:
- outInputChannel 不为空 , 用于返回给客户端
- INPUT_FEATURE_NO_INPUT_CHANNEL 没有设置
代码如下
// @WMS#addWindow
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
WindowState#openInputChannel
这个方法用于创建输入事件的管道
// @frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) { // 不能为此WindowState重复创建input channel
throw new IllegalStateException("Window already has an input channel.");
}
String name = getName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); // 创建一个input channel 对
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
mWmService.mInputManager.registerInputChannel(mInputChannel); // 将0端的InputChannel注册到IMS
mInputWindowHandle.token = mInputChannel.getToken();
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
mClientChannel = null;
} else {
// If the window died visible, we setup a dummy input channel, so that taps
// can still detected by input monitor channel, and we can relaunch the app.
// Create dummy event receiver that simply reports all events as handled.
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
},
这个方法首先通过openInputChannelPair 创建了一个socket pair , 然后通过registerInputChannel 将socketfd[0]注册到了IMS, 将socketfd[1]端通过transferTo转换到了outInputChannel, 之后这个outInputChannel返回给客户端, 这样就使得IMS与客户端建立了事件分发与反馈的通道.
在客户端的ViewRootImpl通过此channel创建一个WindowInputEventReceiver, 通过此receiver可以接收到来自IMS端分发的事件, 也可以向其反馈事件处理完毕. 这部分内容比较多,在另外一篇文章进行叙述
下面引用深入理解Android 卷3 一张图来描述这个通信过程
WindowState#attach
接下来看WindowState的attach方法
void attach() {
if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
mSession.windowAddedLocked(mAttrs.packageName); // 调用session的windowAddedLocked方法
}
Session#windowAddedLocked
void windowAddedLocked(String packageName) {
mPackageName = packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
if (mSurfaceSession == null) { // 没有SurfaceSession, 则创建一个
if (DEBUG) {
Slog.v(TAG_WM, "First window added to " + this + ", creating SurfaceSession");
}
mSurfaceSession = new SurfaceSession(); // 关键类, 用于与SurfaceFlinger通信
ProtoLog.i(WM_SHOW_TRANSACTIONS, " NEW SURFACE SESSION %s", mSurfaceSession);
mService.mSessions.add(this); // 将此session对象添加到
if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
mService.dispatchNewAnimatorScaleLocked(this);
}
}
mNumWindow++; // window 数量递增
}
创建SurfaceSession
首先看看源文件的描述, 它代表了与 SurfaceFlinger之间的一个连接, 通过它来创建一个或多个Surface , 然后在这些Surface上进行绘制, 最后将这些Surface效果混合显示到屏幕
/**
* An instance of this class represents a connection to the surface
* flinger, from which you can create one or more Surface instances that will
* be composited to the screen.
* {@hide}
*/
public final class SurfaceSession {
// Note: This field is accessed by native code.
@UnsupportedAppUsage
private long mNativeClient; // SurfaceComposerClient*
SurfaceSession 构造方法
/** Create a new connection with the surface flinger. */
@UnsupportedAppUsage
public SurfaceSession() {
mNativeClient = nativeCreate(); // 通过native创建cpp SurfaceComposerClient对象
}
nativeCreate
native方法对应的jni函数在android_view_SurfaceSession 中, 名称相同.
// @frameworks/base/core/jni/android_view_SurfaceSession.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz) {
SurfaceComposerClient* client = new SurfaceComposerClient(); // 创建SurfaceComposerClient
client->incStrong((void*)nativeCreate); // 调用一次 incStrong
return reinterpret_cast<jlong>(client); // 返回此client的地址
}
SurfaceComposerClient 构造
SurfaceComposerClient继承自RefBase. 构造方法仅仅初始话了mStatus , 重点是 incStrong调用, 它会触发onFirstRef方法的调用
/// @frameworks/native/libs/gui/SurfaceComposerClient.cpp
SurfaceComposerClient::SurfaceComposerClient()
: mStatus(NO_INIT)
{
}
SurfaceComposerClient::onFirstRef方法
void SurfaceComposerClient::onFirstRef() {
sp<ISurfaceComposer> sf(ComposerService::getComposerService()); // 获取 SurfaceFlinger
if (sf != nullptr && mStatus == NO_INIT) {
sp<ISurfaceComposerClient> conn;
conn = sf->createConnection(); // 创建 connection
if (conn != nullptr) {
mClient = conn;
mStatus = NO_ERROR;
}
}
}
获取 SurfaceFlinger 服务
/// @frameworks/native/libs/gui/SurfaceComposerClient.cpp
/*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() {
ComposerService& instance = ComposerService::getInstance(); // 获取单例
Mutex::Autolock _l(instance.mLock);
if (instance.mComposerService == nullptr) {
ComposerService::getInstance().connectLocked(); // 连接ComposerService
assert(instance.mComposerService != nullptr);
ALOGD("ComposerService reconnected");
}
return instance.mComposerService;
}
构造方法自动连接sf
ComposerService::ComposerService()
: Singleton<ComposerService>() {
Mutex::Autolock _l(mLock);
connectLocked();
}
connectLocked 实际上获取的是 SurfaceFlinger 服务 , 同时会注册sf的死亡监听
void ComposerService::connectLocked() {
const String16 name("SurfaceFlinger");
while (getService(name, &mComposerService) != NO_ERROR) {
usleep(250000);
}
assert(mComposerService != nullptr);
// Create the death listener.
class DeathObserver : public IBinder::DeathRecipient {
ComposerService& mComposerService;
virtual void binderDied(const wp<IBinder>& who) {
ALOGW("ComposerService remote (surfaceflinger) died [%p]",
who.unsafe_get());
mComposerService.composerServiceDied();
}
public:
explicit DeathObserver(ComposerService& mgr) : mComposerService(mgr) { }
};
mDeathObserver = new DeathObserver(*const_cast<ComposerService*>(this));
IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
}
// 从sm获取service
template<typename INTERFACE>
status_t getService(const String16& name, sp<INTERFACE>* outService)
{
const sp<IServiceManager> sm = defaultServiceManager();
if (sm != nullptr) {
*outService = interface_cast<INTERFACE>(sm->getService(name));
if ((*outService) != nullptr) return NO_ERROR;
}
return NAME_NOT_FOUND;
}
SurfaceFlinger::createConnection
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
const sp<Client> client = new Client(this); // 创建并返回一个 Client
return client->initCheck() == NO_ERROR ? client : nullptr;
}
Client 类继承自 BnSurfaceComposerClient , 按binder的通信架构来看, 它是属于通信的服务端, 返回给WMS的实际上是它的bp端. 通过此Client, WMS就可以与SurfaceFlinger进行通信了, 完成类似createSurface 的操作. 为什么要需要多添加一个Client, 直接与SurfaceFlinger交互不行吗? 理论上是可行的. 在我看来,使用Client有2个好处. 一是使用Client可以更好的管理和组织Session - SurfaceSession - Client 这样一对一的关系,使结构层次更清晰. 另外, 将任务首先交给Client处理, 可以减轻sf的工作压力.
class Client : public BnSurfaceComposerClient
{
public:
explicit Client(const sp<SurfaceFlinger>& flinger);
~Client() = default;
status_t initCheck() const;
// protected by SurfaceFlinger::mStateLock
void attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer);
void detachLayer(const Layer* layer);
sp<Layer> getLayerUser(const sp<IBinder>& handle) const;
private:
// ISurfaceComposerClient interface
virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags, const sp<IBinder>& parent,
LayerMetadata metadata, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp,
uint32_t* outTransformHint = nullptr);
...
}
小结
经过以上流程, 客服端对应了WMS唯一的Session, 而Session持有了独一份的SurfaceSession , SurfaceSession 进一步创建SurfaceComposerClient. 在SurfaceComposerClient中通过SurfaceFinger.createConnection创建与sf的连接,即Client. 通过此Client来完成layer/surface相关操作.
WMS与SurfaceFlinger的通信架构图:
WindowState保存与initOp
/** Mapping from an IWindow IBinder to the server's Window object. */
final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();
WMS@addWindow{...
mWindowMap.put(client.asBinder(), win);
win.initAppOpsState();
//...
}
WindowState都保存到mWindowMap, 以客户端传递过来的IWindow为键, WindowState对象为值
WindowState#initAppOpsState
这个方法主要处理窗口的显示状态. 成员mAppOpVisibility 默认为true. 成员mAppOp 是在WindowState的构造方法中赋值, 是在创建WindowState时传入的appOp[0]. 而这个appOp[0] 是在PhoneWindowManager#checkAddPermission 方法中初始化的. appOp[0] 有如下几种取值:
- AppOpsManager.OP_NONE 默认值
- AppOpsManager.OP_TOAST_WINDOW 针对type是TYPE_TOAST
- AppOpsManager.OP_SYSTEM_ALERT_WINDOW 针对type是alert window类型的
因此检查的操作主要是 OP_TOAST_WINDOW 和 OP_SYSTEM_ALERT_WINDOW . 若用户修改了相关应用显示这些窗口的权限, 将导致窗口不会显示出来.
void initAppOpsState() {
if (mAppOp == OP_NONE || !mAppOpVisibility) {
return;
}
// If the app op was MODE_DEFAULT we would have checked the permission
// and add the window only if the permission was granted. Therefore, if
// the mode is MODE_DEFAULT we want the op to succeed as the window is
// shown.
final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, getOwningUid(),
getOwningPackage(), true /* startIfModeDefault */, null /* featureId */,
"init-default-visibility"); // 检查操作是否被允许
if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
setAppOpVisibilityLw(false); // 设置可见性为false
}
}
WindowState#setAppOpVisibilityLw
更新窗口的可见性
private void setAppOpVisibilityLw(boolean state) {
if (mAppOpVisibility != state) {
mAppOpVisibility = state; // 改变mAppOpVisibility
if (state) {
// If the policy visibility had last been to hide, then this
// will incorrectly show at this point since we lost that
// information. Not a big deal -- for the windows that have app
// ops modifies they should only be hidden by policy due to the
// lock screen, and the user won't be changing this if locked.
// Plus it will quickly be fixed the next time we do a layout.
showLw(true, true); // 显示
} else {
hideLw(true, true); // 隐藏
}
}
}
总结
这篇主要设计到WindowState相关的部分, 包含但不限于创建WindowState, 调整窗口显示的参数, 检查窗口重复添加性, 窗口对应的InputChannel的创建, SurfaceSession的创建, 以及一些其他初始化操作. 这些准备为后面的窗口的管理,显示,动画,事件等打下了基础.
WindowState中几个比较重要的初始化操作:
- 创建与注册 InputChannel. 这部分关系到了窗口的输入事件
- 创建SurfaceSession. 这部分关系到了窗口的Surface管理
- 创建WindowStateAnimator. 这部分关系到了窗口动画
本篇部分细节过长, 留待后续进行分析.