【WMS】窗口添加

28 阅读10分钟

APP窗口基础

核心类

image.png

  • Activity负责生命周期和事件处理以及具体的业务逻辑;

  • Window控制视图,一个Activity至少包含一个Window,如果Activity没有Window,那就相当于Service。

  • Window是一个抽象类,具体的实现类为PhoneWindow,它对View进行管理;从用户的角度来讲,它是一个“交互界面”;从SurfaceFlinger的角度来看,它是一个Layer,承载着和窗口有关的数据和属性;从WMS角度来看,它是一个WindowState,用于管理和窗口有关的状态。

  • WindowManager是一个接口类,继承自接口ViewManager它是用来管理Window的,它的实现类为WindowManagerImpl,然后WindowManagerImpl的功能具体实现都委托给了WindowManagerGlobal,WindowManagerGlobal是进程单例的,它管理所有的ViewRootImpl,实际执行addView。

  • DecorView是FrameLayout的子类,是窗口View树的根节点,包含了ActionBar和ContentParent,在onCreate时setContentView()installDecor()时创建。

  • ViewRootImpl是View系统的核心类,核心功能如下

    • 通过WindowSession和IWindow和WMS通信,负责窗口的添加和删除、处理WMS下发的Resize/FocusChanged等信息,
    • 负责View的三大流程Measure/Layout/Draw,并负责管理应用的Surface和创建BBQ,和RenderThread一起完成应用的绘制;
    • 也是输入事件的中转站,持有InputChannel,接收Input下发的事件并转发给Activity和View处理。
  • 每个Window都属于一个具体的类型,例如应用窗口、子窗口和系统窗口;Activity对应的Window是应用窗口;PopupWindow,OptionMenu是常用的子窗口;像状态栏和导航栏就是系统窗口。

image.png

窗口添加流程

APP构建窗口

从handleLaunchActivity到generateDecor

总结:Activity的创建和PhoneWindow的窗口都是在Activity Launch的时候(即Create、Start的时候)

handleLaunchActivity

activity启动的核心函数

  1. 调用Instrumentation.newActivity方法, 利用反射创建activity
  2. 执行attach函数,为activity创建PhoneWindow(绑定Window)
  3. 执行onCreate生命周期回调,并设置activity的状态为on_create,会执行到自定义Activity的onCreate()方法。
//android.app.ActivityThread#performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    //1. 利用反射创建activity
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
    } catch (Exception e) {
        ....
    }
    //2. 创建Application
    try {
        Application  app  = r.packageInfo.makeApplication( false , mInstrumentation);

            // 3. 执行attach函数,创建Window
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback,
                    r.assistToken, r.shareableActivityToken);

            // 4. 执行生命周期的回调, wm_on_create_called log打印的地方
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }

        }
        //5. 将activity的状态设置为on_create
        r.setState(ON_CREATE);
    }
    return activity;
}

Activity#attach

  1. 新建一个PhoneWindow作为当前activity的Window
  2. 设置回调,设置主线程,设置WindowManager
//android.app.Activity#attach
final  void attach(Context context, ActivityThread aThread,...) {
    //1. 新建一个PhoneWindow作为当前activity的Window
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
 
    //2. 设置主线程
    mUiThread = Thread.currentThread();
    mMainThread = aThread;
    mInstrumentation = instr;
    
    //3. 为当前Window设置WindowManager
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    mWindowManager = mWindow.getWindowManager();
}

handleResumeActivity

窗口添加的系统侧是在activity resume的时候

  1. 获取当前window的DecorView 作为添加到WMS的View
  2. 获取当前window的属性,作为布局参数添加到WMS
  3. 调用WindowManagerImpl的addView方法将View和布局参数添加到WMS
//android.app.ActivityThread#handleResumeActivity
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
        boolean isForward, String reason) {

    // 执行生命周期的回调, wm_on_resume_called log打印的地方 设置ON_RESUME
    if (!performResumeActivity(r, finalStateRequest, reason)) {
        return;
    }

    final Activity a = r.activity;
    if (r.window == null && !a.mFinished && willBeVisible) {
        r.window = r.activity.getWindow();
        //获取当前window的DecorView 作为添加到WMS的View
        View decor = r.window.getDecorView();
        ViewManager wm = a.getWindowManager();
        //获取当前window的属性,作为布局参数添加到WMS
        WindowManager.LayoutParams l = r.window.getAttributes();
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                //调用WindowManagerImpl的addView方法将View和布局参数添加到WMS
                wm.addView(decor, l);
            }
        }
    }
}

WindowManagerGlobal#addView

  1. 先判断是否存在父Window,如果存在则会将新创建的Window的参数和父Window的携带调整为一致
  2. 创建ViewRootImpl
  3. 记录view, 记录ViewRootImpl,记录布局参数
//android.view.WindowManagerGlobal#addView
public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow, int userId) {

    synchronized (mLock) {
        // 创建ViewRootImpl, 并构建IWindowSession
        if (windowlessSession == null) {
            root = new ViewRootImpl(view.getContext(), display);
        } else {
            root = new ViewRootImpl(view.getContext(), display,
                    windowlessSession);
        }
        
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        //如果是子Window即parentWindow != null,则会调整SubWindow的参数
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        }
        
        view.setLayoutParams(wparams);
        //记录view, 记录ViewRootImpl,记录布局参数
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        // do this last because it fires off messages to start doing things
        try {
            //将View, params, 和userId添加到WMS
            root.setView(view, wparams, panelParentView, userId);
        } 
    }
}

adjustLayoutParamsForSubWindow的主要逻辑,

android.view.Window#adjustLayoutParamsForSubWindow
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
    CharSequence curTitle = wp.getTitle();
    //如果是subWindow,则将token设置为decorView的token,即主Window的Token
    if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
            wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
        if (wp.token == null) {
            View decor = peekDecorView();
            if (decor != null) {
                wp.token = decor.getWindowToken();
            }
        }
    } else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
            wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
        //处理系统窗口
        if (curTitle == null || curTitle.length() == 0) {
            final StringBuilder title = new StringBuilder(32);
            title.append("Sys").append(wp.type);
            if (mAppName != null) {
                title.append(":").append(mAppName);
            }
            wp.setTitle(title);
        }
    } else {
        //其他情况设置为Window的mAppToken
        if (wp.token == null) {
            wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
        }
        if ((curTitle == null || curTitle.length() == 0)
                && mAppName != null) {
            wp.setTitle(mAppName);
        }
    }
    if (wp.packageName == null) {
        wp.packageName = mContext.getPackageName();
    }
}

ViewRootImpl#setView

ViewRootImpl是一个Window中View树的根,因此与Activity、Window、WindowState等一一对应。 通过Session和Window对象,实现Client端和WMS端的交互。

主要负责:

  1. Client->WMS: Window的添加和删除:WindowSession#addToDisplay,WindowSession#remove
  2. WMS-> Client: 处理WMS下发的一些Winodw变化:resized, moved, windowFocusChanged
  3. 核心功能:View的三大流程,测量、布局和绘制:measure, layout, draw
  4. Input事件:输入事件的中转站,处理keyevent, motion event等
  5. 窗口关键信息管理:View.AttachInfo, 管理Surface
  1. mWindowSession.addToDisplayAsUser 通过mWindowSession将窗口,布局参数等其他属性添加进WMS,其中mWindowSession类型是IWindowSession,在构造方法赋值,通过WindowManagerGlobal.getWindowSession()获取。
  2. 在mWindowLayout.computeFrames处计算窗口的大小
  3. setFrame(mTmpFrames.frame); 更新窗口的大小
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
        int userId) {
    synchronized (this) {
        if (mView == null) {
            mView = view;

            // 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.
         //在将窗口添加到系统侧之前,先进行一次布局,这样可以保证在收到系统事件前做了relayout
            requestLayout();
            try {
                //将当前窗口添加到系统侧
                res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), userId,
                        mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
                        mTempControls, attachedFrame, compatScale);
            }

            //计算当前窗口的大小
            mWindowLayout.computeFrames(mWindowAttributes, state,
                    displayCutoutSafe, winConfig.getBounds(), winConfig.getWindowingMode(),
                    UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH,
                    mInsetsController.getRequestedVisibilities(), 1f /* compactScale */,
                    mTmpFrames);
            //更新当前窗口的大小        
            setFrame(mTmpFrames.frame);
        }
    }
}

WMS添加窗口

WindowToken创建和挂载

窗口添加时,核心问题是WindowToken挂到层级树的哪个节点。入口在DisplayContent.addWindowToken()

// DisplayContent.java
final DisplayArea.Tokens da = findAreaForToken(token).asTokens();
da.addChild(token);

findAreaForWindowType()根据窗口类型做三路分派

// DisplayContent.java
if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) {
    //Type是application的,则返回TaskDisplayArea
    return mDisplayAreaPolicy.getTaskDisplayArea(options);  // → TaskDisplayArea
}
if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) {
    return mImeContainer;  // → ImeContainer
}
return mDisplayAreaPolicy.findAreaForWindowType(...);  // → 系统窗口对应的DisplayArea.Tokens

对于系统窗口,最终调用RootDisplayArea.findAreaForWindowTypeInLayer()

// RootDisplayArea.java
int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(
        windowType, ownerCanManageAppTokens, roundedCornerOverlay);
return mAreaForLayer[windowLayerFromType];  // 通过数组直接索引

WindowToken的层级

这些层级数字对应mAreaForLayer[]数组的索引,每个索引指向一个预构建的DisplayArea.Tokens容器。

WindowManagerPolicy.getWindowLayerFromTypeLw()将窗口type映射为数字层级(1-36):

层级窗口类型说明
1TYPE_WALLPAPER壁纸
2APPLICATION_LAYER应用窗口(特殊,走TaskDisplayArea)
3TYPE_PHONE / TYPE_PRESENTATION电话/演示窗口
7/27TYPE_TOAST普通Toast=7,系统Toast=27
9/12TYPE_SYSTEM_ALERT普通=9,系统=12
11TYPE_APPLICATION_OVERLAY应用悬浮窗
13TYPE_INPUT_METHOD输入法
15TYPE_STATUS_BAR状态栏
17TYPE_NOTIFICATION_SHADE通知面板
24TYPE_NAVIGATION_BAR导航栏
26TYPE_SCREENSHOT截屏
31TYPE_ACCESSIBILITY_OVERLAY无障碍覆盖
35TYPE_POINTER指针
36圆角覆盖层最高层级
RootDisplayArea
  ├─ DisplayArea.Tokens [layer=1]    ← 壁纸
  ├─ ...
  ├─ TaskDisplayArea [layer=2]       ← 应用窗口
  │   └─ Task → ActivityRecord → WindowState
  ├─ ...
  ├─ ImeContainer [layer=13-14]     ← 输入法
  ├─ DisplayArea.Tokens [layer=15]   ← 状态栏
  ├─ DisplayArea.Tokens [layer=24]   ← 导航栏
  ├─ ...
  └─ DisplayArea.Tokens [layer=35]   ← 指针

DisplayAreaPolicyBuilder在Display创建时构建这棵树,每个层级对应一个DisplayArea.Tokens叶节点。

WindowState创建和添加

创建WidowState,SurfaceSession,并将WindowState添加到ActivityRecord的过程

  1. 创建WindowState,并创建WindowStateAnimator管理Surface的状态,mDrawState默认为NO_SURFACE = 0
  2. 建立与SF的链接,并创建Layer,后续流程见Surface创建流程
  3. 添加到mWindowMap, 由WMS统一管理
  4. 添加到WindowToken中,把属于同一WindowToken的Window组织在一起
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
        int displayId, ...) {
    //获取 displayContent
    final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);        
    
    //如果有父Window则用父Window的Token,如果是子Window则用子Window的token。    
    final boolean hasParent = parentWindow != null;
    WindowToken token = displayContent.getWindowToken(
            hasParent ? parentWindow.mAttrs.token : attrs.token);
           
    //创建 WindowState 并创建WindowStateAnimator
    final WindowState win = new WindowState(this, session, client, token, parentWindow,
        appOp[0], attrs, viewVisibility, session.mUid, userId,
        session.mCanAddInternalSystemWindow); 

    // 创建SurfaceSession,建立与SF的链接
    win.attach(); 
    
    //添加到mWindowMap, 由WMS统一管理
    mWindowMap.put(client.asBinder(), win);
            
    //添加到WindowToken中,把属于同一WindowToken的Window组织在一起
    win.mToken.addWindow(win);
}

将WindowState对象添加到WindowToken中,

void addWindow(final WindowState win) {
    ProtoLog.d(WM_DEBUG_FOCUS,
            "addWindow: win=%s Callers=%s", win, Debug.getCallers(5));
    //子窗口有父窗口管理,不需要WindowToken管理
    if (win.isChildWindow()) {
        // Child windows are added to their parent windows.
        return;
    }
    //如果没有SurfaceControl,则创建SurfaceControl, 什么情况下会走这里?
    if (mSurfaceControl == null) {
        createSurfaceControl(true /* force */);

        // Layers could have been assigned before the surface was created, update them again
        reassignLayer(getSyncTransaction());
    }
    //添加到mChildren中,并设置mParent属性
    if (!mChildren.contains(win)) {
        ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);
        addChild(win, mWindowComparator);
        mWmService.mWindowsChanged = true;
    }
}

WindowState的Z-Order

WMS添加窗口小结

宏观定位:

窗口type → getWindowLayerFromTypeLw() → 层级数字 → mAreaForLayer[] → 对应的DisplayArea.Tokens容器

决定窗口在层级树中的大致位置

微观排序:

WindowState通过mBaseLayer比较器在WindowToken内部有序插入 → assignChildLayers()按children顺序递增分配最终Z-order值给SurfaceFlinger

决定窗口的精确Z-order

WindowToken的创建与传递

在ActivityRecord的构造方法中会new一个Token对象,同时调用super方法到WindowToken中,然后token通过弱引用持有ActivityRecord。

可以看到Token是一个Binder(这样方便跨进程传输);

在WindowToken的构造函数中,会将token和WindowToken添加到DisplayContent中的mTokenMap。

//com.android.server.wm.ActivityRecord#ActivityRecord
private ActivityRecord(.....) {
 super (_service.mWindowManager, new Token (),   TYPE_APPLICATION , true ,
 null  /* displayContent */ , false  /* ownerCanManageAppTokens */ );

    mAtmService = _service;
    ((Token) token).mActivityRef = new  WeakReference <>( this );
    info = aInfo;
    mUserId = UserHandle.getUserId(info.applicationInfo.uid);
    packageName = info.applicationInfo.packageName;
    intent = _intent;
}

//com.android.server.wm.ActivityRecord.Token
private static class Token extends Binder {
    @NonNull WeakReference<ActivityRecord> mActivityRef;

    @Override
    public String toString() {
        return "Token{" + Integer.toHexString(System.identityHashCode(this)) + " "
                + mActivityRef.get() + "}";
    }
}

//com.android.server.wm.WindowToken#WindowToken
protected WindowToken(WindowManagerService service, IBinder _token, int type,
        boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,
        boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
    super(service);
    token = _token;
    if (dc != null) {
        dc.addWindowToken(token, this);
    }
}

//com.android.server.wm.DisplayContent#addWindowToken
void addWindowToken(IBinder binder, WindowToken token) {
    final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
    mTokenMap.put(binder, token);
}

WindowToken的传递到Client

  1. 在realStartActivityLocked中发送LaunchActivityItem时,会将token这个Binder封装到ClientTransaction中。
com.android.server.wm.ActivityTaskSupervisor#realStartActivityLocked
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
        boolean andResume, boolean checkConfig) throws RemoteException {
        ......
        // Create activity launch transaction.
        final  ClientTransaction  clientTransaction  = ClientTransaction.obtain(
proc.getThread(), r.token );
        
        final boolean isTransitionForward = r.isTransitionForward();
        final IBinder fragmentToken = r.getTaskFragment().getFragmentToken();
        clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                System.identityHashCode(r), r.info,
                // TODO: Have this take the merged configuration instead of separate global
                // and override configs.
                mergedConfiguration.getGlobalConfiguration(),
                mergedConfiguration.getOverrideConfiguration(), r.compat,
                r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
                proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
                results, newIntents, r.takeOptions(), isTransitionForward,
                proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
                r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken));      
        ......  
}

2. 在执行生命周期回调时,会从ClientTransaction中取出token这个Binder,然后执行LaunchActivityItem的execute方法;其中会构造ActivityClientRecord对象,会将此token保存到ActivityClientRecord中的token属性中。

这样就完成了token从SystemServer到Client端的传递。

//android.app.servertransaction.TransactionExecutor#executeCallbacks
public void executeCallbacks(ClientTransaction transaction) {
    final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
    if (callbacks == null || callbacks.isEmpty()) {
        // No callbacks to execute, return early.
        return;
    }
    if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callbacks in transaction");

    final  IBinder  token  = transaction.getActivityToken();
    ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
    final int size = callbacks.size();
    for (int i = 0; i < size; ++i) {
        final ClientTransactionItem item = callbacks.get(i);
        //此时的item是LaunchActivityItem
        item.execute(mTransactionHandler, token, mPendingActions);
    }
}

//android.app.servertransaction.LaunchActivityItem#execute
public void execute(ClientTransactionHandler client, IBinder token,
        PendingTransactionActions pendingActions) {
    Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
    ActivityClientRecord  r  =  new  ActivityClientRecord (token, ....);
    client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

//android.app.ActivityThread.ActivityClientRecord#ActivityClientRecord
public ActivityClientRecord(IBinder token, ....) {
    this .token = token;
    ....
    init();
}

WindowToken的传递到Window

  1. 在上一步创建ActivityClientRecord后会将执行handleLaunchActivity,进一步执行performLaunchActivity,这里会创建Activity以及执行android.app.Activity#attach方法去创建Window。接下来看下这个过程中token的传递逻辑。
//android.app.ActivityThread#performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ....
    Activity activity = null;
    java.lang.ClassLoader cl = appContext.getClassLoader();
    //通过mInstrumentation反射创建Activity
    activity = mInstrumentation.newActivity(
            cl, component.getClassName(), r.intent);
    //创建Application
    Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation);
    
     //执行Activity#attach去创建Window
    activity.attach(appContext, this, getInstrumentation(), r.token, 
        r.ident, app, r.intent, r.activityInfo, title, r.parent,
        r.embeddedID, r.lastNonConfigurationInstances, config,
        r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
        r.assistToken, r.shareableActivityToken);
        
    //将Activity赋值给ActivityClientRecord中的activity属性
    r.activity = activity;
    
    //执行onCreate生命周期
    mInstrumentation.callActivityOnCreate(activity, r.state);
    r.setState(ON_CREATE);
}

2. 在attach方法中首先创建PhoneWindow作为Activity的Window,然后将ActivityClientRecord的token赋值给Activity的mToken属性;

然后给Window设置WindowManager,同时将mToken设置为WindowManager的mAppToken。

这样就完成了token传递给Window。

//android.app.Activity#attach
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, ...) {
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);
 //创建PhoneWindow作为Activity的Window
mWindow = new  PhoneWindow ( this , window, activityConfigCallback);

    mUiThread = Thread.currentThread();

    mMainThread = aThread;
    mInstrumentation = instr;
    //将token赋值给Activity的mToken属性
    mToken = token;

    mApplication = application;
    mIntent = intent;
    mReferrer = referrer;
    mComponent = intent.getComponent();
    mActivityInfo = info;
    mTitle = title;
    mParent = parent;
    mEmbeddedID = id;

    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;
}

//android.view.Window#setWindowManager
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
        boolean hardwareAccelerated) {
    mAppToken = appToken; 
    mAppName = appName;
    mHardwareAccelerated = hardwareAccelerated;
    if (wm == null) {
        wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    }
    mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}