GVKun编程网logo

DBMS_XPLAN : Display Oracle Execution Plans

1

关于DBMS_XPLAN:DisplayOracleExecutionPlans的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于(OK)Androidgraphic(12)—display

关于DBMS_XPLAN : Display Oracle Execution Plans的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于(OK) Android graphic (12)—display 上层相关概念、关系 - mLogicalDisplays.get (Display.DEFAULT_DISPLAY)、5、Azure Devops 之 Azure Test Plans 篇、AWS plans multimillion-dollar investment in Brazil、Biggest evening dip in NBN speeds seen on 100Mbps plans等相关知识的信息别忘了在本站进行查找喔。

本文目录一览:

DBMS_XPLAN : Display Oracle Execution Plans

DBMS_XPLAN : Display Oracle Execution Plans

    The DBMS_XPLAN package is used to format the output of an explain plan. It was introduced in Oracle 9i as a replacement for the "utlxpls.sql" script or custom queries of the plan table. Subsequent database versions have increased the functionality of the package.


    DBMS_XPLAN包是用来格式化执行计划输出的,其最初在9i的时候引入,用于替代用户执行utlxpls.sql脚本和查询计划表;在随后的ORACLE版本中增强了这个包的功能



Setup

If it is not already present create the SCott schema.

conn sys/password as sysdba
@$ORACLE_HOME/rdbms/admin/utlsampl.sql

Create a PLAN_TABLE if it does not already exist.

conn sys/password as sysdba
@$ORACLE_HOME/rdbms/admin/utlxplan.sql
CREATE PUBLIC SYNONYM plan_table FOR sys.plan_table;
GRANT ALL ON sys.plan_table TO public;

disPLAY Function

The disPLAY function allows us to display the execution plan stored in the plan table. First we explain a sql statement.

CONN scott/tiger

EXPLAIN PLAN FOR
SELECT *
FROM   emp e, dept d
WHERE  e.deptno = d.deptno
AND    e.ename  = ''SMITH'';

Next we use the DBMS_XPLAN.disPLAY function to display the execution plan.

SET LInesIZE 130
SET PAGESIZE 0
SELECT * 
FROM   TABLE(DBMS_XPLAN.disPLAY);

----------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Rows  | Bytes | Cost (%cpu)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |     1 |    58 |     4   (0)| 00:00:01 |
|   1 |  nesTED LOOPS                |         |       |       |            |          |
|   2 |   nesTED LOOPS               |         |     1 |    58 |     4   (0)| 00:00:01 |
|*  3 |    TABLE ACCESS FULL         | EMP     |     1 |    38 |     3   (0)| 00:00:01 |
|*  4 |    INDEX UNIQUE SCAN         | PK_DEPT |     1 |       |     0   (0)| 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     1 |    20 |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------

Predicate information (identified by operation id):
---------------------------------------------------

   3 - filter("E"."ENAME"=''SMITH'')
   4 - access("E"."DEPTNO"="D"."DEPTNO")

18 rows selected.

sql>

The DBMS_XPLAN.disPLAY function can accept 3 optional parameters:

  • table_name - Name of the PLAN_TABLE, default value ''PLAN_TABLE''.

  • statement_id - Statement id of the plan to be displayed. The default value is NULL, which displays the most recent execution plan in the PLAN_TABLE.

  • format - Controls the level of detail displayed, default value ''TYPICAL''. Other values include ''BASIC'', ''ALL'', ''SERIAL''. There is also an undocumented ''ADVANCED'' setting.



         table_name:指定计划表的名字,默认为PLAN_TABLE

         statement_id:指定要显示的执行计划的statement_id;默认为null,意味着显示计划表中最新的执行计划

         format:格式化定制输出执行计划,默认值为format;还有basic,all,serial以及阿斗advanced值

     


Note. From Oracle 10g Release 2 onwards the format of the output can be tailored by using the standard list of formats along with keywords that represent columns to including or excluding (prefixed with ''-''). As a result, the format column can Now be a space or comma delimited list. The list of available columns varies depending on the database version and function being called. Check the documentation for your version.

EXPLAIN PLAN SET STATEMENT_ID=''TSH'' FOR
SELECT *
FROM   emp e, dept d
WHERE  e.deptno = d.deptno
AND    e.ename  = ''SMITH'';

SET LInesIZE 130
SELECT * 
FROM   TABLE(DBMS_XPLAN.disPLAY(''PLAN_TABLE'',''TSH'',''BASIC''));

Plan hash value: 3625962092

------------------------------------------------
| Id  | Operation                    | Name    |
------------------------------------------------
|   0 | SELECT STATEMENT             |         |
|   1 |  nesTED LOOPS                |         |
|   2 |   nesTED LOOPS               |         |
|   3 |    TABLE ACCESS FULL         | EMP     |
|   4 |    INDEX UNIQUE SCAN         | PK_DEPT |
|   5 |   TABLE ACCESS BY INDEX ROWID| DEPT    |
------------------------------------------------

12 rows selected.

sql>



disPLAY_CURSOR Function

In Oracle 10g Release 1 Oracle introduced the disPLAY_CURSOR function. Rather than displaying an execution plan from the PLAN_TABLE, it displays the actual execution plan used to run a query stored in the cursor cache. This information is gathered from the V$sql_PLAN_STATISTICS_ALLV$sql and V$sql_PLAN views, so the user must have access to these.

    ORACLE10.1之后的版本引入了一个新的display_cursor函数,这个函数用于显示存储在library cahce池中的真实的执行计划;而不是像display函数那样显示一个从plan_table评估出的执行计划。

    display_cursor函数从动态视图v$sql_plan_statistics_all和v$sql_plan中获取信息,所以用户要具有这两个视图的执行权限

 It accepts three optional parameters:

  • sql_id - The sql_ID of the statement in the cursor cache. The sql_ID as available from the V$sql and V$sqlAREA views, or from the V$SESSION view using the PREV_sql_ID column. If omitted, the last cursor executed by the session is displayed.

  • child_number - The child number of the cursor specified by the sql_ID parameter. If not specified, all cursors for the specified sql_ID are diaplyed.

  • format - In addition to the setting available for the disPLAY function, this function also has ''RUNSTATS_LAST'' and ''RUNSTATS_TOT'' to display the last and total runtime statistics respectively. These additional format options require "STATISTICS_LEVEL=ALL".

      sql_id:游标池中的sql_id(其实就是父游标号),sql_id可以从v$sql和v$sqlarea视图中获取,也可以使用prev_sal_id列从v$session视图中获取;默认会取出会话最近执行的语句的游标信息

      child_number:子游标号;如果没有指定,将会展示父游标下的所有子游标的执行计划

      format:允许使用display函数的所有的format参数,还可以设置runstats-last和runstats_tot来获取最近一次的和全部的运行时统计信息;需要设置为statistics_level=all状态


The following example show the advanced output from a query on the SCott schema.

CONN / AS SYSDBA
GRANT SELECT ON v_$session TO scott;
GRANT SELECT ON v_$sql TO scott;
GRANT SELECT ON v_$sql_plan TO scott;
GRANT SELECT ON v_$sql_plan_statistics_all TO scott;

CONN scott/tiger

SELECT *
FROM   emp e, dept d
WHERE  e.deptno = d.deptno
AND    e.ename  = ''SMITH'';

SET LInesIZE 130
SELECT * 
FROM   TABLE(DBMS_XPLAN.disPLAY_CURSOR(format => ''ADVANCED''));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------
sql_ID  gu62pbk51ubc3, child number 0
-------------------------------------
SELECT * FROM   emp e, dept d WHERE  e.deptno = d.deptno AND    e.ename
 = ''SMITH''

Plan hash value: 3625962092

----------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Rows  | Bytes | Cost (%cpu)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |       |       |     4 (100)|          |
|   1 |  nesTED LOOPS                |         |       |       |            |          |
|   2 |   nesTED LOOPS               |         |     1 |    58 |     4   (0)| 00:00:01 |
|*  3 |    TABLE ACCESS FULL         | EMP     |     1 |    38 |     3   (0)| 00:00:01 |
|*  4 |    INDEX UNIQUE SCAN         | PK_DEPT |     1 |       |     0   (0)|          |
|   5 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     1 |    20 |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1
   3 - SEL$1 / E@SEL$1
   4 - SEL$1 / D@SEL$1
   5 - SEL$1 / D@SEL$1

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGnorE_OPTIM_EMbedDED_HINTS
      OPTIMIZER_FEATURES_ENABLE(''11.2.0.2'')
      DB_VERSION(''11.2.0.2'')
      ALL_ROWS
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "E"@"SEL$1")
      INDEX(@"SEL$1" "D"@"SEL$1" ("DEPT"."DEPTNO"))
      LEADING(@"SEL$1" "E"@"SEL$1" "D"@"SEL$1")
      USE_NL(@"SEL$1" "D"@"SEL$1")
      NLJ_BATCHING(@"SEL$1" "D"@"SEL$1")
      END_OUTLINE_DATA
  */

Predicate information (identified by operation id):
---------------------------------------------------

   3 - filter("E"."ENAME"=''SMITH'')
   4 - access("E"."DEPTNO"="D"."DEPTNO")

Column Projection information (identified by operation id):
-----------------------------------------------------------

   1 - "E"."EMPNO"[NUMBER,22], "E"."ENAME"[VARCHAR2,10], "E"."JOB"[VARCHAR2,9],
       "E"."MGR"[NUMBER,22], "E"."HIREDATE"[DATE,7], "E"."SAL"[NUMBER,22],
       "E"."COMM"[NUMBER,22], "E"."DEPTNO"[NUMBER,22], "D"."DEPTNO"[NUMBER,22],
       "D"."DNAME"[VARCHAR2,14], "D"."LOC"[VARCHAR2,13]
   2 - "E"."EMPNO"[NUMBER,22], "E"."ENAME"[VARCHAR2,10], "E"."JOB"[VARCHAR2,9],
       "E"."MGR"[NUMBER,22], "E"."HIREDATE"[DATE,7], "E"."SAL"[NUMBER,22],
       "E"."COMM"[NUMBER,22], "E"."DEPTNO"[NUMBER,22], "D".ROWID[ROWID,10],
       "D"."DEPTNO"[NUMBER,22]
   3 - "E"."EMPNO"[NUMBER,22], "E"."ENAME"[VARCHAR2,10], "E"."JOB"[VARCHAR2,9],
       "E"."MGR"[NUMBER,22], "E"."HIREDATE"[DATE,7], "E"."SAL"[NUMBER,22],
       "E"."COMM"[NUMBER,22], "E"."DEPTNO"[NUMBER,22]
   4 - "D".ROWID[ROWID,10], "D"."DEPTNO"[NUMBER,22]
   5 - "D"."DNAME"[VARCHAR2,14], "D"."LOC"[VARCHAR2,13]

67 rows selected.

sql>

Other Functions

There are some other useful functions in the package, but I don''t find myself using them very often, so they are summarized below. If you need more information, follow the links at the bottom of the article for the appropriate database version.

  • disPLAY_AWR - Introduced in Oracle 10g Release 1, this function displays an execution plan stored in the Advanced Workload Repository (AWR).

  • disPLAY_sqlSET - Introduced in Oracle 10g Release 2, this function displays the execution plan of a given statement stored in a sql tuning set.

  • disPLAY_sql_PLAN_BASELINE - Introduced in Oracle 11g Release 1, this function displays one or more execution plans for the specified sql handle of a sql plan baseline.

  • disPLAY_PLAN - Introduced in Oracle 11g Release 2, this function displays the contents of the plan table in a variety of formats.



文章原文:http://oracle-base.com/articles/9i/dbms_xplan.PHP




















(OK) Android graphic (12)—display 上层相关概念、关系 - mLogicalDisplays.get (Display.DEFAULT_DISPLAY)

(OK) Android graphic (12)—display 上层相关概念、关系 - mLogicalDisplays.get (Display.DEFAULT_DISPLAY)


http://blog.csdn.net/lewif/article/details/50827430


涉及的 java 类

DisplayManagerService

Manages attached displays.
The DisplayManagerService manages the global lifecycle of displays,
decides how to configure logical displays based on the physical display devices currently
attached, sends notifications to the system and to applications when the state
changes, and so on.
The display manager service relies on a collection of DisplayAdapter components,
for discovering and configuring physical display devices attached to the system.
There are separate display adapters for each manner that devices are attached:
one display adapter for built-in local displays, one for simulated non-functional
displays when the system is headless, one for simulated overlay displays used for
development, one for wifi displays, etc.
Display adapters are only weakly coupled to the display manager service.
Display adapters communicate changes in display device state to the display manager
service asynchronously via a DisplayAdapter.Listener registered
by the display manager service.  This separation of concerns is important for
two main reasons.  First, it neatly encapsulates the responsibilities of these
two classes: display adapters handle individual display devices whereas
the display manager service handles the global state.  Second, it eliminates
the potential for deadlocks resulting from asynchronous display device discovery.

DisplayAdapter

A display adapter makes zero or more display devices available to the system
and provides facilities for discovering when displays are connected or disconnected.
For now, all display adapters are registered in the system server but
in principle it could be done from other processes.

现在支持的 4 种 Adapter,分别对应不同类型的 display。

1.LocalDisplayAdapter
A display adapter for the local displays managed by Surface Flinger
2.WifiDisplayAdapter
Connects to Wifi displays that implement the Miracast protocol.
This class is responsible for connecting to Wifi displays and mediating
the interactions between Media Server, Surface Flinger and the Display Manager Service.
3.VirtualDisplayAdapter
4.OverlayDisplayAdapter

DisplayDevice

 Represents a physical display device such as the built-in display an external monitor, or a WiFi display.
WifiDisplayDevice
VirtualDisplayDevice
OverlayDisplayDevice
LocalDisplayDevice

DisplayManagerGlobal

Manager communication with the display manager service on behalf of an application process.

DisplayManager

Manages the properties of attached displays.

LogicalDisplay

Describes how a logical display is configured.
At this time, we only support logical displays that are coupled to a particular
primary display device from which the logical display derives its basic properties
such as its size, density and refresh rate.
A logical display may be mirrored onto multiple display devices in addition to its
primary display device.  Note that the contents of a logical display may not
always be visible, even on its primary display device, such as in the case where
the primary display device is currently mirroring content from a different
logical display.
Note: The display manager architecture does not actually require logical displays
to be associated with any individual display device.  Logical displays and
display devices are orthogonal concepts.  Some mapping will exist between
logical displays and display devices but it can be many-to-many and
and some might have no relation at all.

Display

Provides information about the size and density of a logical display.

DisplayContent

Utility class for keeping track of the WindowStates and other pertinent contents of a particular Display.

DisplayInfo

Describes the characteristics of a particular logical display.

类之间的关系

以添加系统 built in display 为例,下面是各个类之间的关系图,

其中,LocalDisplayDevice 中的 mPhys 是向 surface flinger 获取的 display 的硬件相关属性,而 mDisplayToken 是 surfacefinger 中为 display 创建的 new BBinder 对应的代理对象。

默认屏幕的上层初始化分析

1.
systemserver.Java

/*--------------systemserver.java---------------------------*/
    // 专门为window manager创建了一个handler thread
    // Create a handler thread just for the window manager to enjoy.
        HandlerThread wmHandlerThread = new HandlerThread("WindowManager");
        wmHandlerThread.start();
        //创建一个window manager的Handler(looper是wmHandlerThread线程的)
        Handler wmHandler = new Handler(wmHandlerThread.getLooper());
        wmHandler.post(new Runnable() {
            @Override
            public void run() {
                //Looper.myLooper().setMessageLogging(new LogPrinter(
                //        android.util.Log.DEBUG, TAG, android.util.Log.LOG_ID_SYSTEM));
                android.os.Process.setThreadPriority(
                        android.os.Process.THREAD_PRIORITY_DISPLAY);
                android.os.Process.setCanSelfBackground(false);

                // For debug builds, log event loop stalls to dropbox for analysis.
                if (StrictMode.conditionallyEnableDebugLogging()) {
                    Slog.i(TAG, "Enabled StrictMode logging for WM Looper");
                }
            }
        });
/*--------------systemserver.java---------------------------*/
    // DisplayManagerService display = null;
    // 新建DisplayManagerService服务
        Slog.i(TAG, "Display Manager");
            display = new DisplayManagerService(context, wmHandler);
            ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);
         //需要等待第一个display初始化完成后,才继续进行,否则一直循环
        if (!display.waitForDefaultDisplay()) {
                reportWtf("Timeout waiting for default display to be initialized.",
                        new Throwable());
            }
/*--------------DisplayManagerService.java---------------------------*/
    // 在mSyncRoot wait,直到mLogicalDisplays.get(Display.DEFAULT_DISPLAY)不为null
    // 即有地方添加了默认display的LogicalDisplay
    /**
     * Pauses the boot process to wait for the first display to be initialized.
     */
    public boolean waitForDefaultDisplay() {
        synchronized (mSyncRoot) {
            long timeout = SystemClock.uptimeMillis() + WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT;
            while (mLogicalDisplays.get(Display.DEFAULT_DISPLAY) == null) {
                long delay = timeout - SystemClock.uptimeMillis();
                if (delay <= 0) {
                    return false;
                }
                if (DEBUG) {
                    Slog.d(TAG, "waitForDefaultDisplay: waiting, timeout=" + delay);
                }
                try {
                    mSyncRoot.wait(delay);
                } catch (InterruptedException ex) {
                }
            }
        }
        return true;
    }
/*--------------DisplayManagerService.java---------------------------*/
   public DisplayManagerService(Context context, Handler mainHandler) {
        mContext = context;
        mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
    //新建个DisplayManagerHandler
        mHandler = new DisplayManagerHandler(mainHandler.getLooper());
        mUiHandler = UiThread.getHandler();
        //新建个DisplayAdapterListener
        mDisplayAdapterListener = new DisplayAdapterListener();
        //persist.demo.singledisplay是只创建默认display的logical display
        mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);

        mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
    }

DisplayManagerHandler 的消息处理函数,

/*--------------DisplayManagerService.java---------------------------*/
private final class DisplayManagerHandler extends Handler {
        public DisplayManagerHandler(Looper looper) {
            super(looper, null, true /*async*/);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER:
                    registerDefaultDisplayAdapter();
                    break;

                case MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS:
                    registerAdditionalDisplayAdapters();
                    break;

                case MSG_DELIVER_DISPLAY_EVENT:
                    deliverDisplayEvent(msg.arg1, msg.arg2);
                    break;

                case MSG_REQUEST_TRAVERSAL:
                    mWindowManagerFuncs.requestTraversal();
                    break;

                case MSG_UPDATE_VIEWPORT: {
                    synchronized (mSyncRoot) {
                        mTempDefaultViewport.copyFrom(mDefaultViewport);
                        mTempExternalTouchViewport.copyFrom(mExternalTouchViewport);
                    }
                    mInputManagerFuncs.setDisplayViewports(
                            mTempDefaultViewport, mTempExternalTouchViewport);
                    break;
                }
            }
        }
    }

DisplayAdapterListener 类,

/*--------------DisplayManagerService.java---------------------------*/
    private final class DisplayAdapterListener implements DisplayAdapter.Listener {
        @Override
        public void onDisplayDeviceEvent(DisplayDevice device, int event) {
            switch (event) {
                case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED:
                    handleDisplayDeviceAdded(device);
                    break;

                case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED:
                    handleDisplayDeviceChanged(device);
                    break;

                case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED:
                    handleDisplayDeviceRemoved(device);
                    break;
            }
        }

        @Override
        public void onTraversalRequested() {
            synchronized (mSyncRoot) {
                scheduleTraversalLocked(false);
            }
        }
    }
mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
/* ro.config.headless
    系统中判断ro.config.headless的总共有几个地方
    a、SurfaceControl.java在构造函数中判断如果是headless设备则抛出异常,说明headless的设备不应该构造任何显示画面
    b、在SystemUI中判断headless为true的情况下不启动WallpaperManagerService和SystemUIService
    c、在PhoneWindowManager的systemReady中判断headless为true的情况下不起动KeyguardServiceDelegate,不显示启动提示消息,屏蔽滑盖(lid)状态,屏蔽一些按键
    d、DisplayManagerService在创建默认显示设备的时候(registerDefaultDisplayAdapter)判断headless为true的情况下创建的是HeadlessDisplayAdapter而非LocalDisplayAdapter,前者并没有通过SurfaceFlinger.getBuiltInDisplay获取一个对应的DisplayDevice,也就是说上层的默认显示设备是空
    e、ActivityManagerService在startHomeActivityLocked断headless为true的情况下不启动HomeActivity,不能启动Activity,不能重启挂掉的进程(即使是persistent),不能更新屏幕配置。
*/

mHandler 的 MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER 处理函数为,

/*--------------DisplayManagerService.java---------------------------*/
    private void registerDefaultDisplayAdapter() {
        // Register default display adapter.
        synchronized (mSyncRoot) {
            if (mHeadless) {
                registerDisplayAdapterLocked(new HeadlessDisplayAdapter(
                        mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
            } else {
                //走这个分支
                registerDisplayAdapterLocked(new LocalDisplayAdapter(
                        mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
            }
        }
    }

LocalDisplayAdapter 构造函数的 mHandler 和 mDisplayAdapterListener 分别为 DisplayManagerHandler 和 DisplayAdapterListener。

/*--------------LocalDisplayAdapter.java---------------------------*/
    /* class LocalDisplayAdapter extends DisplayAdapter */
    // Called with SyncRoot lock held.
    public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
            Context context, Handler handler, Listener listener) {
        super(syncRoot, context, handler, listener, TAG);
    }
    /*--------------DisplayAdapter.java---------------------------*/
   // LocalDisplayAdapter是 DisplayAdapter的子类
   public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
            Context context, Handler handler, Listener listener, String name) {
        mSyncRoot = syncRoot;
        mContext = context;
        mHandler = handler;
        mListener = listener;
        mName = name;
    }
/*--------------DisplayManagerService.java---------------------------*/
//    private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
        private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
        mDisplayAdapters.add(adapter);
        adapter.registerLocked();
    }

LocalDisplayAdapter.registerLocked(),

/*--------------LocalDisplayAdapter.java---------------------------*/       
        private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
            SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
            SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
    };

    // LocalDisplayAdapter.registerLocked
        public void registerLocked() {
        super.registerLocked();

        mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper());
    //对默认屏幕和HDMI调用tryConnectDisplayLocked
        for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
            tryConnectDisplayLocked(builtInDisplayId);
        }
    }

调用 tryConnectDisplayLocked (),这里主要是和底层 framework 去交互,获取底层注册的 displays 的相关信息。

/*--------------LocalDisplayAdapter.java---------------------------*/ 
      // LocalDisplayAdapter.mDevices 
      // private final SparseArray<LocalDisplayDevice> mDevices =new SparseArray<LocalDisplayDevice>();
      private void tryConnectDisplayLocked(int builtInDisplayId) {
      //获取surface flinger中的new BBinder对应的client IBinder
        IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId);
        //获取display的硬件属性,新建LocalDisplayDevice
        if (displayToken != null && SurfaceControl.getDisplayInfo(displayToken, mTempPhys)) {
            LocalDisplayDevice device = mDevices.get(builtInDisplayId);
            if (device == null) {
                // Display was added.
                device = new LocalDisplayDevice(displayToken, builtInDisplayId, mTempPhys);
                mDevices.put(builtInDisplayId, device);
                //给DisplayManagerService的DisplayManagerHandler发消息
                sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
            } else if (device.updatePhysicalDisplayInfoLocked(mTempPhys)) {
                // Display properties changed.
                sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
            }
        } else {
            // The display is no longer available. Ignore the attempt to add it.
            // If it was connected but has already been disconnected, we''ll get a
            // disconnect event that will remove it from mDevices.
        }
    }
/*--------------SurfaceControl.java---------------------------*/
    //SurfaceControl.java,都是static,类函数
    //通过id获取display token
    public static IBinder getBuiltInDisplay(int builtInDisplayId) {
        return nativeGetBuiltInDisplay(builtInDisplayId);
    }
        //通过display token,获取display硬件属性
        public static boolean getDisplayInfo(IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo) {
        if (displayToken == null) {
            throw new IllegalArgumentException("displayToken must not be null");
        }
        if (outInfo == null) {
            throw new IllegalArgumentException("outInfo must not be null");
        }
        return nativeGetDisplayInfo(displayToken, outInfo);
    }

其中,nativeGetBuiltInDisplay 最终会调用 surfaceflinger 中的 getBuiltInDisplay,通过 Binder 传回代理对象。

/*--------------SurfaceFlinger.cpp---------------------------*/
sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
    if (uint32_t(id) >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
        ALOGE("getDefaultDisplay: id=%d is not a valid default display id", id);
        return NULL;
    }
    return mBuiltinDisplays[id];
}

nativeGetDisplayInfo—>SurfaceFlinger::getDisplayInfo,

/*--------------SurfaceFlinger.cpp---------------------------*/
status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) {
    int32_t type = NAME_NOT_FOUND;
    for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
        if (display == mBuiltinDisplays[i]) {
            type = i;
            break;
        }
    }

    if (type < 0) {
        return type;
    }

    const HWComposer& hwc(getHwComposer());
    float xdpi = hwc.getDpiX(type);
    float ydpi = hwc.getDpiY(type);

    // TODO: Not sure if display density should handled by SF any longer
    class Density {
        static int getDensityFromProperty(char const* propName) {
            char property[PROPERTY_VALUE_MAX];
            int density = 0;
            if (property_get(propName, property, NULL) > 0) {
                density = atoi(property);
            }
            return density;
        }
    public:
        static int getEmuDensity() {
            return getDensityFromProperty("qemu.sf.lcd_density"); }
        static int getBuildDensity()  {
            return getDensityFromProperty("ro.sf.lcd_density"); }
    };

    if (type == DisplayDevice::DISPLAY_PRIMARY) {
        // The density of the device is provided by a build property
        float density = Density::getBuildDensity() / 160.0f;
        if (density == 0) {
            // the build doesn''t provide a density -- this is wrong!
            // use xdpi instead
            ALOGE("ro.sf.lcd_density must be defined as a build property");
            density = xdpi / 160.0f;
        }
        if (Density::getEmuDensity()) {
            // if "qemu.sf.lcd_density" is specified, it overrides everything
            xdpi = ydpi = density = Density::getEmuDensity();
            density /= 160.0f;
        }
        info->density = density;

        // TODO: this needs to go away (currently needed only by webkit)
        sp<const DisplayDevice> hw(getDefaultDisplayDevice());
        info->orientation = hw->getOrientation();
    } else {
        // TODO: where should this value come from?
        static const int TV_DENSITY = 213;
        info->density = TV_DENSITY / 160.0f;
        info->orientation = 0;
    }

    info->w = hwc.getWidth(type);
    info->h = hwc.getHeight(type);
    info->xdpi = xdpi;
    info->ydpi = ydpi;
    info->fps = float(1e9 / hwc.getRefreshPeriod(type));

    // All non-virtual displays are currently considered secure.
    info->secure = true;

    return NO_ERROR;
}

创建完 LocalDisplayDevice,给 DisplayManagerService 的 DisplayManagerHandler 发消息,

/*--------------DisplayAdapter.java---------------------------*/
    /**
     * Sends a display device event to the display adapter listener asynchronously.
     */
    protected final void sendDisplayDeviceEventLocked(
            final DisplayDevice device, final int event) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                mListener.onDisplayDeviceEvent(device, event);
            }
        });
    }

LocalDisplayAdapter 构造函数的 mHandler 和 mDisplayAdapterListener 分别为 DisplayManagerHandler 和 DisplayAdapterListener,进而会去调用,

/*--------------DisplayManagerService.java---------------------------*/
        private void handleDisplayDeviceAdded(DisplayDevice device) {
        synchronized (mSyncRoot) {
            handleDisplayDeviceAddedLocked(device);
        }
    }
/*--------------DisplayManagerService.java---------------------------*/
    //输入为LocalDisplayDevice
    //    private final ArrayList<DisplayDevice> mDisplayDevices = new ArrayList<DisplayDevice>();
    private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
        if (mDisplayDevices.contains(device)) {
            Slog.w(TAG, "Attempted to add already added display device: "
                    + device.getDisplayDeviceInfoLocked());
            return;
        }

        Slog.i(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
    // List of all currently connected display devices.
    //将LocalDisplayDevice添加到mDisplayDevices
        mDisplayDevices.add(device);
        //为一个物理display添加一个logical display
        addLogicalDisplayLocked(device);
        updateDisplayBlankingLocked(device);
        scheduleTraversalLocked(false);
    }

为物理屏幕创建一个 logical display,

/*--------------DisplayManagerService.java---------------------------*/
// Adds a new logical display based on the given display device.
    // Sends notifications if needed.
    private void addLogicalDisplayLocked(DisplayDevice device) {
        DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
        boolean isDefault = (deviceInfo.flags
                & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
        //主屏,同时mLogicalDisplays包含了这个device
        if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
            Slog.w(TAG, "Ignoring attempt to add a second default display: " + deviceInfo);
            isDefault = false;
        }
        //不是主屏,但是mSingleDisplayDemoMode为真,即设置了单屏模式
        //这时候不会去为这个物理display创建新的logical display,如果
        //有hdmi,这时候会mirror主屏的内容
        //看来逻辑display就是和显示的内容有很大的关系
        if (!isDefault && mSingleDisplayDemoMode) {
            Slog.i(TAG, "Not creating a logical display for a secondary display "
                    + " because single display demo mode is enabled: " + deviceInfo);
            return;
        }

        final int displayId = assignDisplayIdLocked(isDefault);
        // layerStack 就是displayId id,主屏0,hdmi 1
        final int layerStack = assignLayerStackLocked(displayId);

        //新建LogicalDisplay
        LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
        display.updateLocked(mDisplayDevices);
        if (!display.isValidLocked()) {
            // This should never happen currently.
            Slog.w(TAG, "Ignoring display device because the logical display "
                    + "created from it was not considered valid: " + deviceInfo);
            return;
        }

        mLogicalDisplays.put(displayId, display);

        //如果添加的是built-in display,需要mSyncRoot.notifyAll()
        //因为systemserver.java中调用了waitForDefaultDisplay,这时候systemserver可以继续运行了
        // Wake up waitForDefaultDisplay.
        if (isDefault) {
            mSyncRoot.notifyAll();
        }
    //给mHandler发消息去处理
        sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
    }
/*--------------LogicalDisplay.java---------------------------*/
    public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
        mDisplayId = displayId;
        mLayerStack = layerStack;
        mPrimaryDisplayDevice = primaryDisplayDevice;
    }

进而去调用 mHandler 的 deliverDisplayEvent,先不去管 mCallbacks 是在哪里注册的,进而会去调用 mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event);

/*--------------DisplayManagerService.java---------------------------*/
   private void deliverDisplayEvent(int displayId, int event) {
        if (DEBUG) {
            Slog.d(TAG, "Delivering display event: displayId="
                    + displayId + ", event=" + event);
        }

        // Grab the lock and copy the callbacks.
        final int count;
        synchronized (mSyncRoot) {
        //这里的mCallbacks是哪里注册的??
            count = mCallbacks.size();
            mTempCallbacks.clear();
            for (int i = 0; i < count; i++) {
                mTempCallbacks.add(mCallbacks.valueAt(i));
            }
        }

        // After releasing the lock, send the notifications out.
        for (int i = 0; i < count; i++) {
            mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event);
        }
        mTempCallbacks.clear();
    }

2. mCallbacks 的由来, 上面在添加了默认 display 的 LogicalDisplay 后,会去调用下面代码,使得 systemserver.java 从 waitForDefaultDisplay () 中返回。

/*--------------systemserver.java---------------------------*/
        if (isDefault) {
            mSyncRoot.notifyAll();
        }

进而,systemserver.java 的 initAndLoop () 会去注册 WindowManagerService,

/*--------------systemserver.java---------------------------*/
            wm = WindowManagerService.main(context, power, display, inputManager,
                    wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
                    !firstBoot, onlyCore);

下面分析下,WindowManagerService 的构造函数中做了什么和 display 相关的,

/*--------------WindowManagerService.java---------------------------*/
/*
class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs,
                DisplayManagerService.WindowManagerFuncs, DisplayManager.DisplayListener
*/
    //WindowManagerService实现了DisplayManager.DisplayListener接口

    private WindowManagerService(Context context, PowerManagerService pm,
            DisplayManagerService displayManager, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {

    mDisplayManagerService = displayManager;
    //新建个DisplayManager
    mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
    //由于WindowManagerService实现了DisplayManager.DisplayListener接口,
    //将WindowManagerService注册到DisplayManager中
    mDisplayManager.registerDisplayListener(this, null);    

    }
/*--------------DisplayManager.java---------------------------*/
        public DisplayManager(Context context) {
        mContext = context;
        mGlobal = DisplayManagerGlobal.getInstance();
    }
/*--------------DisplayManagerGlobal.java---------------------------*/

// class DisplayManagerService extends IDisplayManager.Stub 
   public static DisplayManagerGlobal getInstance() {
        synchronized (DisplayManagerGlobal.class) {
            if (sInstance == null) {
                IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
                if (b != null) {
                //输入参数是DisplayManagerService 的代理
                    sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
                }
            }
            return sInstance;
        }
    }

public final class DisplayManagerGlobal {

    //mDm是DisplayManagerService 的代理,用来和DisplayManagerService打交道
    private final IDisplayManager mDm;

        private DisplayManagerGlobal(IDisplayManager dm) {
        mDm = dm;
    }

}

接着将 WindowManagerService 注册到 DisplayManager 中,

/*--------------DisplayManager.java---------------------------*/
        //输入参数为WindowManagerService,null    
        public void registerDisplayListener(DisplayListener listener, Handler handler) {
        mGlobal.registerDisplayListener(listener, handler);
    }
/*--------------DisplayManagerGlobal.java---------------------------*/
        // DisplayManagerGlobal 
        public void registerDisplayListener(DisplayListener listener, Handler handler) {
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }
   // private final ArrayList<DisplayListenerDelegate> mDisplayListeners =
    //        new ArrayList<DisplayListenerDelegate>();
        synchronized (mLock) {
            int index = findDisplayListenerLocked(listener);
            if (index < 0) {
             //新建一个display listener的代理者
                mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
             //将DisplayManagerGlobal 和DisplayManagerService 联系起来
            registerCallbackIfNeededLocked();
            }
        }
    }

 public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
            super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
            mListener = listener;
        }
/*--------------DisplayManagerGlobal.java---------------------------*/
// 将    DisplayManagerGlobal 和DisplayManagerService 联系起来
        private void registerCallbackIfNeededLocked() {
        if (mCallback == null) {
            mCallback = new DisplayManagerCallback();
            try {
                //调用DisplayManagerService 的registerCallback将
                //DisplayManagerGlobal 中的DisplayManagerCallback注册到DisplayManagerService中
                mDm.registerCallback(mCallback);
            } catch (RemoteException ex) {
                Log.e(TAG, "Failed to register callback with display manager service.", ex);
                mCallback = null;
            }
        }
    }   
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
        @Override
        public void onDisplayEvent(int displayId, int event) {
            if (DEBUG) {
                Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
            }
            handleDisplayEvent(displayId, event);
        }
    }

DisplayManagerService 中调用 registerCallback,将 DisplayManagerCallback 保存到 mCallbacks 中,

/*--------------DisplayManagerService.java---------------------------*/
  @Override // Binder call
    public void registerCallback(IDisplayManagerCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("listener must not be null");
        }

        synchronized (mSyncRoot) {
            int callingPid = Binder.getCallingPid();
            if (mCallbacks.get(callingPid) != null) {
                throw new SecurityException("The calling process has already "
                        + "registered an IDisplayManagerCallback.");
            }

            CallbackRecord record = new CallbackRecord(callingPid, callback);
            try {
                IBinder binder = callback.asBinder();
                binder.linkToDeath(record, 0);
            } catch (RemoteException ex) {
                // give up
                throw new RuntimeException(ex);
            }
        //保存的是CallbackRecord 
            mCallbacks.put(callingPid, record);
        }
    }
/*--------------DisplayManagerService.java---------------------------*/
    private final class CallbackRecord implements DeathRecipient {
        public final int mPid;
        private final IDisplayManagerCallback mCallback;

        public boolean mWifiDisplayScanRequested;

        public CallbackRecord(int pid, IDisplayManagerCallback callback) {
            mPid = pid;
            mCallback = callback;
        }

        @Override
        public void binderDied() {
            if (DEBUG) {
                Slog.d(TAG, "Display listener for pid " + mPid + " died.");
            }
            onCallbackDied(this);
        }

        public void notifyDisplayEventAsync(int displayId, int event) {
            try {
                mCallback.onDisplayEvent(displayId, event);
            } catch (RemoteException ex) {
                Slog.w(TAG, "Failed to notify process "
                        + mPid + " that displays changed, assuming it died.", ex);
                binderDied();
            }
        }
    }

3. 回到最开始的 deliverDisplayEvent 函数,会调用 CallbackRecord 的 notifyDisplayEventAsync (displayId, event),进而 mCallback.onDisplayEvent(displayId, event),进而调用 DisplayManagerGlobal 的 handleDisplayEvent(displayId, event)

/*--------------DisplayManagerService.java---------------------------*/
 private void deliverDisplayEvent(int displayId, int event) {
        if (DEBUG) {
            Slog.d(TAG, "Delivering display event: displayId="
                    + displayId + ", event=" + event);
        }

        // Grab the lock and copy the callbacks.
        final int count;
        synchronized (mSyncRoot) {
        //这里的mCallbacks是哪里注册的??
            count = mCallbacks.size();
            mTempCallbacks.clear();
            for (int i = 0; i < count; i++) {
                mTempCallbacks.add(mCallbacks.valueAt(i));
            }
        }

        // After releasing the lock, send the notifications out.
        for (int i = 0; i < count; i++) {
            mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event);
        }
        mTempCallbacks.clear();
    }

handleDisplayEvent 中的 mDisplayListeners 就是前面注册的 DisplayListenerDelegate,其中的 listener 和 handler 分别为 WindowManagerService 和 null,


/*--------------DisplayManagerGlobal.java---------------------------*/
    private void handleDisplayEvent(int displayId, int event) {
        synchronized (mLock) {
            if (USE_CACHE) {
                mDisplayInfoCache.remove(displayId);

                if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
                    mDisplayIdCache = null;
                }
            }

            final int numListeners = mDisplayListeners.size();
            for (int i = 0; i < numListeners; i++) {
                mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
            }
        }
    }

调用 DisplayListenerDelegate 的 sendDisplayEvent,进而调用 mListener.onDisplayAdded(msg.arg1);,即 WindowManagerService 的 onDisplayAdded,


/*--------------DisplayManagerGlobal.java---------------------------*/
 private static final class DisplayListenerDelegate extends Handler {
        public final DisplayListener mListener;

        public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
            super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
            mListener = listener;
        }

        public void sendDisplayEvent(int displayId, int event) {
            Message msg = obtainMessage(event, displayId, 0);
            sendMessage(msg);
        }

        public void clearEvents() {
            removeCallbacksAndMessages(null);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case EVENT_DISPLAY_ADDED:
                    mListener.onDisplayAdded(msg.arg1);
                    break;
                case EVENT_DISPLAY_CHANGED:
                    mListener.onDisplayChanged(msg.arg1);
                    break;
                case EVENT_DISPLAY_REMOVED:
                    mListener.onDisplayRemoved(msg.arg1);
                    break;
            }
        }
    }
}

转到 WindowManagerService 中,

/*--------------WindowManagerService.java---------------------------*/
    @Override
    public void onDisplayAdded(int displayId) {
        mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_ADDED, displayId, 0));
    }
/*--------------WindowManagerService.java---------------------------*/
                    case DO_DISPLAY_ADDED:
                    synchronized (mWindowMap) {
                        handleDisplayAddedLocked(msg.arg1);
                    }
                    break;

        private void handleDisplayAddedLocked(int displayId) {
        final Display display = mDisplayManager.getDisplay(displayId);
        if (display != null) {
            createDisplayContentLocked(display);
            displayReady(displayId);
        }
    }

调用 DisplayManager 的 getDisplay,

/*--------------DisplayManager.java---------------------------*/
        public Display getDisplay(int displayId) {
        synchronized (mLock) {
            return getOrCreateDisplayLocked(displayId, false /*assumeValid*/);
        }
    }
/*--------------DisplayManager.java---------------------------*/
    //  private final SparseArray<Display> mDisplays = new SparseArray<Display>();
        private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) {
        Display display = mDisplays.get(displayId);
        if (display == null) {
            display = mGlobal.getCompatibleDisplay(displayId,
                    mContext.getDisplayAdjustments(displayId));
            if (display != null) {
                mDisplays.put(displayId, display);
            }
        } else if (!assumeValid && !display.isValid()) {
            display = null;
        }
        return display;
    }
/*--------------DisplayManagerGlobal.java---------------------------*/

    public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
        DisplayInfo displayInfo = getDisplayInfo(displayId);
        if (displayInfo == null) {
            return null;
        }
        return new Display(this, displayId, displayInfo, daj);
    }
/*--------------DisplayManagerGlobal.java---------------------------*/
//mDm DisplayManagerService
     public DisplayInfo getDisplayInfo(int displayId) {
        try {
            synchronized (mLock) {
                DisplayInfo info;
                if (USE_CACHE) {
                    info = mDisplayInfoCache.get(displayId);
                    if (info != null) {
                        return info;
                    }
                }
        //调用DisplayManagerService的getDisplayInfo,获取显示器信息
                info = mDm.getDisplayInfo(displayId);
                if (info == null) {
                    return null;
                }

                if (USE_CACHE) {
                    mDisplayInfoCache.put(displayId, info);
                }
                //前面已经注册过了,mCallback不为Null
                registerCallbackIfNeededLocked();

                if (DEBUG) {
                    Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
                }
                return info;
            }
        } catch (RemoteException ex) {
            Log.e(TAG, "Could not get display information from display manager.", ex);
            return null;
        }
    }
/*--------------DisplayManagerGlobal.java---------------------------*/
    //已经注册过了,mCallback不为Null
        private void registerCallbackIfNeededLocked() {
        if (mCallback == null) {
            mCallback = new DisplayManagerCallback();
            try {
                mDm.registerCallback(mCallback);
            } catch (RemoteException ex) {
                Log.e(TAG, "Failed to register callback with display manager service.", ex);
                mCallback = null;
            }
        }
    }
/*--------------WindowManagerService.java---------------------------*/
    //Display已经创建,创建display content
    public void createDisplayContentLocked(final Display display) {
        if (display == null) {
            throw new IllegalArgumentException("getDisplayContent: display must not be null");
        }
        getDisplayContentLocked(display.getDisplayId());
    }
/** All DisplayContents in the world, kept here */
    // SparseArray<DisplayContent> mDisplayContents = new SparseArray<DisplayContent>(2);

        public DisplayContent getDisplayContentLocked(final int displayId) {
        DisplayContent displayContent = mDisplayContents.get(displayId);
        if (displayContent == null) {
            final Display display = mDisplayManager.getDisplay(displayId);
            // 走到这个分支,创建displayContent 
            if (display != null) {
                displayContent = newDisplayContentLocked(display);
            }
        }
        return displayContent;
    }

新建一个 DisplayContent,保存到 WindowManagerService 的 mDisplayContents (displayId, displayContent)。

private DisplayContent newDisplayContentLocked(final Display display) {
        DisplayContent displayContent = new DisplayContent(display, this);
        final int displayId = display.getDisplayId();
        mDisplayContents.put(displayId, displayContent);

        DisplayInfo displayInfo = displayContent.getDisplayInfo();
        final Rect rect = new Rect();
        mDisplaySettings.getOverscanLocked(displayInfo.name, rect);
        synchronized (displayContent.mDisplaySizeLock) {
            displayInfo.overscanLeft = rect.left;
            displayInfo.overscanTop = rect.top;
            displayInfo.overscanRight = rect.right;
            displayInfo.overscanBottom = rect.bottom;
            mDisplayManagerService.setDisplayInfoOverrideFromWindowManager(
                    displayId, displayInfo);
        }
        configureDisplayPolicyLocked(displayContent);

        // TODO: Create an input channel for each display with touch capability.
        if (displayId == Display.DEFAULT_DISPLAY) {
            displayContent.mTapDetector = new StackTapPointerEventListener(this, displayContent);
            registerPointerEventListener(displayContent.mTapDetector);
        }

        return displayContent;
    }

5、Azure Devops 之 Azure Test Plans 篇

5、Azure Devops 之 Azure Test Plans 篇

1、什么是 Azure Test Plans

Azure Test Plans 是提供给团队测试人员,管理测试计划、测试套件、测试用例的部件。管理测试计划、测试用例的定义,包括请求类型定义、参数定义,执行情况、实际结果的管理。

2、Test Plans (测试计划)

测试计划对一项需求或是一个大功能的测试的规划安排情况。
可以点击『New Test Plan』创建。
Name:测试计划的名字。
Area Path:路径。

可以创建测试套件和测试用例。同时,也可以从版本角度管理测试用例,比如:testPlanV1,testPlanV2。同时这两个人测试计划也可以有重叠的测试套件或测试用例。

3、Test Suite (测试套件)

测试套件是对具有相关特性的测试用例的分组管理。例如用户信息的注册、更新、禁用操作,可以定义为一个用户接口测试用例套件。

4、运行 Test Case (测试用例)

针对单个功能的测试步骤。这里同一个Test Case 可以加入多个Test suite、Test Plan。
测试用例是对某一个接口功能的具体测试步骤的管理,包括这个接口是请求类型、参数定义、值管理,执行进度、实际测试结果的管理。

在运行测试用例的时候,也是可以添加附件、备注信息的。

同时,在这里可以标记测试用例通过与否。

5、Progress Report(运行报表)

报表功能,看执行到什么情况,看这个报表就可以了。

6、Parameters(参数)

Parameters参数管理,管理着所有测试用例所需要的参数。在这里可以统一维护管理。

7、Configuration(配置)

8、Runs(运行)

Runs所有运行记录。

微信公众号:一凡码农
欢迎交流

AWS plans multimillion-dollar investment in Brazil

AWS plans multimillion-dollar investment in Brazil

http://www.zdnet.com/article/aws-plans-multimillion-dollar-investment-in-brazil/


Amazon Web Services (AWS) has pledged to invest 1 billion reais ($233 million) in the expansion of its infrastructure in the Brazilian state of São Paulo.

The investment, to be made over the next two years, was announced by São Paulo governor João Doria, after a meeting with Shannon Kellogg, director of public policy at AWS and other company executives including the country manager for Brazil, Cleber Morais.

"With this important investment by AWS, Amazon''s cloud computing company, we will generate more jobs, technology and opportunities for startups as well, placing the state of São Paulo in the global map," Doria said.

executive guide

AWS: The complete business guide to Amazon''s cloud services

A few brilliant strokes of ingenuity, combined with a large dose of capitalism, made the e-retailer into the world’s cloud services leader.

Read More

Other than this generic official statement, no additional information was disclosed around the expansion plans of the cloud computing giant. AWS set up its first Sao Paulo datacenter in 2012.

A few years later, the company announced that it would be using the customer cost-consciousness driven by economic instability to grow its business in Brazil and increase its influence in the local technology community.

Cloud computing and artificial intelligence will be the core areas of focus when it comes to investment in technology in Brazil in 2020, according to a study released last month by technology firm CI&T.

However, immaturity around cloud security is a challenge, as companies in Brazil can''t keep up with sector advances, according to a separate study by Symantec. Another challenge mentioned by the vast majority of the study participants is lack of visibility of cloud workloads.

Biggest evening dip in NBN speeds seen on 100Mbps plans

Biggest evening dip in NBN speeds seen on 100Mbps plans

http://www.zdnet.com/article/biggest-evening-dip-in-nbn-speeds-seen-on-100mbps-plans/


Image: ACCC

Users of 100/40Mbps plans on the National Broadband Network (NBN) are seeing an almost 6Mbps dip in their evening speeds according to the latest instalment of the Measuring Broadband Australia report from the Australian Competition and Consumer Commission (ACCC) covering November.

The report states that the decrease begins after 5 pm, is at its strongest at 9 pm, and is back to normal at 11 pm.

During November, 260,000 download speed tests were completed on 1,212 whiteboxes, with almost 70% of tests hitting 90% of maximum plan speeds, this represented a jump of 5 percentage points on the previous report.

The ACCC also repeated its complaints that no user could achieve more than 95.6% of maximum plan speeds, using it as justification for the ridiculous situation of getting NBN to overdimension its layer 2 plans to allow the ACCC''s layer 7 tests to hit 100%.

Broken down by telco download speeds, Optus maintained its spot at the top, followed by TPG, Extel, Aussie Broadband, iiNet, Telstra, MyRepublic, and Vocus-owned Dodo and iPrimus.

However, once the ACCC excluded underperforming connections -- which represented 11% of its survey sample, and 95% of which use fibre-to-the-node (FttN) technology -- the telco pecking order was Optus, iiNet, Telstra, TPG, Aussie Broadband, Exetel, MyRepublic, and Dodo and iPrimus.

Across NBN connection technologies, download speeds on fibre-to-the-premises (FttP), fibre-to-the-curb (FttC), and hybrid fibre-coaxial (HFC) were sitting between 90.5% to 91.6% of plan speed, but FttN was far behind at 82%. Once the underperforming connections were excluded, FttN jumped to 89.4% of plan speeds.

"Consumers with underperforming connections are encouraged to get in touch with their RSPs, and ask whether a technician may be able to fix their connection issues," ACCC chair Rod Sims said.

"Otherwise, they should be able to move to a cheaper plan with top speeds their connection can actually provide."

In terms of latency, FttC slightly beat FttP with 9.9 milliseconds versus 10.6ms, with HFC on 13ms, and FttN on 13.9ms.

For the test related to average web page loading time, Exetel claimed top spot on 2.4 seconds, with Telstra and Aussie Broadband tied at 2.6 seconds, followed by Dodo and iPrimus, iiNet, Optus, TPG, and MyRepublic.

Overnight, the Telecommunications Industry Ombudsman (TIO) released its October to December second quarter phone and internet complaints report, which showed overall complaints were up a mere 1% compared to last year.

"When looking at the half year on half year comparison it''s pleasing to see there was no significant increase in overall complaint volumes," TIO Judi Jones said.

"Fault and connection complaints about services delivered over the National Broadband Network continue their downward trend. ''Missed appointments'' has dropped out of the top 10 issues, so consumers are telling us it''s no longer the critical issue for them.

Jones added the results reflected ongoing efforts to address issues over previous years.

The Australian Communications Consumer Action Network noted that the TIO had reported an increase in small business issues, and added that business owners should ensure they are on business grade plans to minimise disruption.

Related Coverage

  • Telstra payments wipe out NBN first half EBITDA gains
  • Telstra hits out at monthly premises reporting of broadband tax
  • NBN testing short-term satellite connections for disaster recovery
  • NBN retreats from directly signing enterprise customers
  • NBN to possibly purchase existing dark fibre for enterprise connections

今天关于DBMS_XPLAN : Display Oracle Execution Plans的讲解已经结束,谢谢您的阅读,如果想了解更多关于(OK) Android graphic (12)—display 上层相关概念、关系 - mLogicalDisplays.get (Display.DEFAULT_DISPLAY)、5、Azure Devops 之 Azure Test Plans 篇、AWS plans multimillion-dollar investment in Brazil、Biggest evening dip in NBN speeds seen on 100Mbps plans的相关知识,请在本站搜索。

本文标签: