Android R DreamManagerService分析

3,061 阅读15分钟

DreamManagerService(下面就简称DreamMS)是继承于SystemService的系统服务,运行于system_server进程内,负责Dream和Doze功能的进入、退出等,Dream是指手机屏保,现在这个功能已经几乎不使用了,被AOD取而代之,Doze则是表示一种屏幕状态,在Doze状态时,屏幕会处于一种低耗电状态,支持系统提供显示内容,但也允许CPU挂起,AOD/FOD等就是在Doze下实现的。从代码来看DreamMS只有短短600行,应该是碰到过最小的SystemService了,当然,整个Dream或Doze的实现,并非仅仅由它一个类完成,这篇文章分析总结下DreamMS模块的整体框架,以及Dream/Doze的实现流程,由于屏保功能已几乎不使用,所以以Doze为主,这里统称为Dreamland。

1.实现原理

当系统灭屏后,PMS会通过DreamMS去启动一个指定的DreamService服务,当该服务启动成功后,DreamMS中会获取一个DOZE_WAKE_LOCK类型的wakelock锁,此时屏幕将处于Doze状态,而DreamService所属组件,将自由添加显示内容。

2.模块架构

整个Dreamland相关类图如下:

dreamland_class.jpg

  • IDreamManager: 通过IDreamManager.aidl生成的Binder接口,用来和Client进程交互;
  • DreamManagerInternal: DreamManagerService用来和system_server内部线程的交互;
  • DreamManagerService:继承于SystemService,由SystemServer启动,内部实现了IDreamService.Stub和DisplayManagerInternal用于和其他进程/线程进行交互;
  • DreamController: Dreamland功能的控制层,DreamManagerService不会直接和Client交互,而是通过该类和Client交互;
  • DreamRecord: 代表一个Dreamland(梦境),实现了ServiceConnection接口,用来绑定Client端的DreamService;
  • DreamService: FW层定义的一个Service,Client端必须是它的子类。
  • IDreamService:通过IDreamService.aidl生成的Binder接口,bindService时作为客户端返回给DreamMS的IBinder对象;
  • AmbientDisplayConfiguration: 该类中会读取配置相关信息;

3.启动流程

DreamMS的启动在SystemServer中开始:

mSystemServiceManager.startService(DreamManagerService.class);

由于是SystemService,因此之后将依次调用其构造方法、onStart()、onPhase(),直接看这三个方法:

    public DreamManagerService(Context context) {
        super(context);
        mContext = context;
        // 所以Dream相关的操作是在android.fg线程执行的
        mHandler = new DreamHandler(FgThread.get().getLooper());
        // 实例化DreamController实例,并接受其callback
        mController = new DreamController(context, mHandler, mControllerListener);

        mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
        // 实例化一个Doze WakeLock
        mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG);
        // 该对象用来获取doze相关配置
        mDozeConfig = new AmbientDisplayConfiguration(mContext);
    }

构造方法中进行了一些初始化操作,先是使用android.fg线程的Looper对象创建了一个DreamHandler,所以此Handler中的逻辑,运行在android.fg线程中,然后实例化了DreamController对象,顾名思义,这个对象就是用来控制Dream/Doze的。最后实例化了AmbientDisplayConfiguration对象,可以通过该对象获取Doze的一些配置属性,内部就是通过这个传入的mContext操作的。 这里也顺便看看DreamController的构造方法:

    public DreamController(Context context, Handler handler, Listener listener) {
        mContext = context;
        mHandler = handler;
        mListener = listener;
        // 获取WMS对象
        mIWindowManager = WindowManagerGlobal.getWindowManagerService();
        // 创建一个Intent,在startDream的时候会发送该广播
        mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        mCloseNotificationShadeIntent.putExtra("reason", "dream");
    }

构造方法执行完毕后,执行onStart():

    @Override
    public void onStart() {
        publishBinderService(DreamService.DREAM_SERVICE, new BinderService());
        publishLocalService(DreamManagerInternal.class, new LocalService());
    }

该方法中,将BinderService对象发布给了ServiceManager,供其他模块进行IPC操作,BinderService继承于IDreamManager.Stub,当其他进程需要调用DreamMS中的方法时,可以通过这种方式进行:

        mDreamManager = IDreamManager.Stub.asInterface(
                ServiceManager.getService(DreamService.DREAM_SERVICE));

然后将LocalService对象注册给了LocalServices,供system_server进程内的其他模块调用,LocalService继承于DreamManagerInternal,比如在PMS中:

mDreamManager = getLocalService(DreamManagerInternal.class);

接下来调用的方法是onPhase():

    @Override
    public void onBootPhase(int phase) {
        // 此阶段表示可以启动或bind三方app了
        if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
            // 注册用户切换广播
            mContext.registerReceiver(new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    writePulseGestureEnabled();
                    synchronized (mLock) {
                        stopDreamLocked(false /*immediate*/);
                    }
                }
            }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
            // 注册
            mContext.getContentResolver().registerContentObserver(
                    Settings.Secure.getUriFor(Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP), false,
                    mDozeEnabledObserver, UserHandle.USER_ALL);
            writePulseGestureEnabled();
        }
    }

在SystemServer中该方法会被调用多次,以表示不同的启动阶段,很显然DreamMS中只处理一个阶段。在这个阶段,注册了Intent.ACTION_USER_SWITCHED广播以及Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP监听。然后调用了writePulseGestureEnabled()方法:

    private void writePulseGestureEnabled() {
        ComponentName name = getDozeComponent();
        boolean dozeEnabled = validateDream(name);
        LocalServices.getService(InputManagerInternal.class).setPulseGestureEnabled(dozeEnabled);
    }

从代码中看,这个方法就是用来通知InputMangerService,是否应该在Doze/Dream时,传递pulse gesture事件的(从代码来看,这个功能并没有使用)。

到这里,SystemServer对DreamMS的启动完成。以上这个过程时序图如下:

DreamMS_start.jpg

4.进入Dreamland过程

Doze的启动,还需要从PMS的流程流程开始说起,我们知道,在灭屏时,PMS中首先会将mWakefulness设置为WAKEFULNESS_DOZING,如果不支持Dream/Doze,才会设置为WAKEFULNESS_ASLEEP,表示设备进入休眠状态。在将WAKEFULNESS_DOZING后,将开始进入Dream/Doze的流程。这里关于灭屏的流程不详细分析,只调进入Dream/Doze的handleSandman()方法中的两个重要调用:

    private void handleSandman() { // runs on handler thread
        ......
        final boolean isDreaming;
        if (mDreamManager != null) {
            // 是否需要Dream/Doze
            if (startDreaming) {
                // 通过DreamMS启动Dream/Doze
                mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);
            }
        } 
        ......
        // 停止Dream/Doze了
        if (isDreaming) {
            mDreamManager.stopDream(false /*immediate*/);
        }
    }

由于Dream已几乎不使用了,所以关于Dream的流程在此都略过。

可以看到,通过DreamManagerInternal.startDream()将进入Doze状态:

    @Override
    public void startDream(boolean doze) {
        startDreamInternal(doze);
    }

    private void startDreamInternal(boolean doze) {
        final int userId = ActivityManager.getCurrentUser();
        // 选择客户端Dream/Doze组件
        final ComponentName dream = chooseDreamForUser(doze, userId);
        if (dream != null) {
            synchronized (mLock) {
                startDreamLocked(dream, false /*isTest*/, doze, userId);
            }
        }
    }

在LocalService中收到PMS调用后,立即调用了startDreamInternal(),在这个方法中,首先是选择一个客户端Doze组件,然后进一步执行启动流程。

所谓的客户端Doze组件,就是一个继承于DozeService的服务,而进入Doze,实际也就是DreamMS中通过bind方式启动这个服务,之后的事情将由这个服务来处理,比如AOD的显示等。当选择好客户端Doze组件后,将通过bind的方式启动该DreamService。 选择DreamService组件:


    private ComponentName chooseDreamForUser(boolean doze, int userId) {
        if (doze) {
            ComponentName dozeComponent = getDozeComponent(userId);
            return validateDream(dozeComponent) ? dozeComponent : null;
        }
        ........
    }

    private ComponentName getDozeComponent(int userId) {
        if (mDozeConfig.enabled(userId)) {
            // 从AmbientDisplayConfiguration中获取
            return ComponentName.unflattenFromString(mDozeConfig.ambientDisplayComponent());
        } else {
            return null;
        }

    }

上面可以看到,最终是通过AmbientDisplayConfiguration获取了一个ComponentName对象,而AmbientDisplayConfiguration中的这个方法也很简单:

    public String ambientDisplayComponent() {
        return mContext.getResources().getString(R.string.config_dozeComponent);
    }

因此,进Doze后启动哪个DreamService,是在config.xml中配置的。 得到客户端DreamService的ComponentName对象后,来看接下来的流程将会进入到DreamController中:

    private void startDreamLocked(final ComponentName name,
            final boolean isTest, final boolean canDoze, final int userId) {
        // 做一个退出Dreamland操作
        stopDreamLocked(true /*immediate*/);

        final Binder newToken = new Binder();
        mCurrentDreamToken = newToken;
        mCurrentDreamName = name;
        mCurrentDreamIsTest = isTest;
        mCurrentDreamCanDoze = canDoze;
        mCurrentDreamUserId = userId;

        PowerManager.WakeLock wakeLock = mPowerManager
                .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream");
        // 进入到android.fgi线程,该wakelock锁在执行完毕后自动释放
        mHandler.post(wakeLock.wrap(
                () -> mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock)));
    }

在这里,执行流程进入到了android.fg线程,并调用进入DreamController中:

    public void startDream(Binder token, ComponentName name,
            boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
        // start前先做一次stop
        stopDream(true /*immediate*/);

        try {
            // 发送Intent.ACTION_CLOSE_SYSTEM_DIALOGS广播,关闭系统Dialog
            mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
            // 参数封装给了DreamRecode.
            mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId, wakeLock);

            mDreamStartTime = SystemClock.elapsedRealtime();
            try {
                // 添加TYPE_DREAM类型的WindowToken
                mIWindowManager.addWindowToken(token, TYPE_DREAM, DEFAULT_DISPLAY);
            } catch (RemoteException ex) {
                stopDream(true /*immediate*/);
                return;
            }

            Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
            intent.setComponent(name);
            intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
            try {
                // bind service
                if (!mContext.bindServiceAsUser(intent, mCurrentDream,
                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
                        new UserHandle(userId))) {
                    stopDream(true /*immediate*/);
                    return;
                }
            } catch (SecurityException ex) {
                stopDream(true /*immediate*/);
                return;
            }

            mCurrentDream.mBound = true;  // 表示bind Doze组件成功
            // 如果5s没有bind service成功,则stop dream恢复到原始状态
            mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT);
        } finally {
        }
    }

在进入startDream()后,首先将创建一个DreamRecord对象,startDream()方法中的参数作为属性封装在了DreamRecord中,因此,每进入一次Dreamland都会创建一个新的DreamRecord对象:

        public DreamRecord(Binder token, ComponentName name,
                boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
            mToken = token;  // Binder类型Token
            mName = name;    // doze组件
            mIsTest = isTest;  // 是否用于测试
            mCanDoze = canDoze;  // 是否是Doze
            mUserId  = userId;   // 当前user id
            mWakeLock = wakeLock;  // WakeLock对象
            mWakeLock.acquire();
            mHandler.postDelayed(mReleaseWakeLockIfNeeded, 10000);
        }

DreamRecord还有一个重要的任务,它实现了ServiceConnection对象,因此在接下来的流程中,将以bind的方式去启动客户端DreamService,启动成功后,DreamRecord中收到客户端回调:

        @Override
        public void onServiceConnected(ComponentName name, final IBinder service) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mConnected = true;  // 表示和客户端连接成功(收到callback了)
                    if (mCurrentDream == DreamRecord.this && mService == null) {
                        // 开始attach
                        attach(IDreamService.Stub.asInterface(service));
                    } else {
                        releaseWakeLockIfNeeded();
                    }
                }
            });
        }

当绑定成功后,接下来将和客户端返回的IDreamService进行attach,attach成功后,将客户端返回的IDreamService赋给DreamRecord对象的属性,然后发送Intent.ACTION_DREAMING_STARTED广播:

    private void attach(IDreamService service) {
        try {
            service.asBinder().linkToDeath(mCurrentDream, 0);
            // 调用客户端attach()
            service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze,
                    mCurrentDream.mDreamingStartedCallback);
        } catch (RemoteException ex) {
            stopDream(true /*immediate*/);
            return;
        }
        
        mCurrentDream.mService = service;

        if (!mCurrentDream.mIsTest) {
            // attach成功后,发送DreamStart广播
            mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL);
            mCurrentDream.mSentStartBroadcast = true;
        }
    }

IDreamService是一个Binder接口,其实现类为DreamService.DreamServiceWrapper,紧接着开始DreamService.attach()的执行:

// frameworks/base/core/java/android/service/dreams/DreamService.java

    private final void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started) {
        // .......
        if (!mWindowless) {
            // 创建并添加一个Window
            try {
                getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes());
            } catch (WindowManager.BadTokenException ex) {
                mWindow = null;
                return;
            }
        }
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (mWindow != null || mWindowless) {

                    mStarted = true;
                    try {
                        // 子类实现此方法
                        onDreamingStarted();
                    } finally {
                        try {
                            // callback 给DreamController
                            started.sendResult(null);
                        } catch (RemoteException e) {
                            throw e.rethrowFromSystemServer();
                        }
                    }
                }
            }
        });
    }

在DreamService#attach()中,创建了一个Window,然后将调用onDreamingStarted(),可以发现这个方法在DreamService中并没有任何逻辑,它是为子类提供的一个hook方法,也是自绑定客户端DreamService后,收到的第一个方法。

最终,通过调用DreamMS#startDozing()方法,完成屏幕DOZE状态的设置:

    private void startDozingInternal(IBinder token, int screenState,
            int screenBrightness) {
        synchronized (mLock) {
            if (mCurrentDreamToken == token && mCurrentDreamCanDoze) {
                // 更新Doze时的Display状态和亮度
                mCurrentDreamDozeScreenState = screenState;
                mCurrentDreamDozeScreenBrightness = screenBrightness;
                // 设置给PMS
                mPowerManagerInternal.setDozeOverrideFromDreamManager(
                        screenState, screenBrightness);
                if (!mCurrentDreamIsDozing) {
                    mCurrentDreamIsDozing = true;
                    // 申请DozeWakeLock
                    mDozeWakeLock.acquire();
                }
            }
        }
    }

在这里,更新了Doze状态时的Display状态以及亮度,然后传递给PMS,由PMS完成设置,最后申请了Doze WakeLock。通过Debug可以发现,每当客户端要更新Doze状态的Display状态或亮度时,都会调用startingDoze()方法进行设置。

到这里,Framework层的Dreamland切换流程完成了,至于客户端的逻辑和具体的业务,如AOD/FOD等,是由客户端进行实现。这个过程时序图如下:

start_doze.jpg

5.退出Dreamland

亮屏时,PMS中调用DreamManagerInternal#stopDream()方法,在这个方法内部调用stopDreamLocked()方法开始执行退出Dreamland流程:

    private void stopDreamLocked(final boolean immediate) {
        // startDreamLocked中创建的一个Binder Token
        if (mCurrentDreamToken != null) {
            if (immediate) { // 是否立即退出dreamland
                Slog.i(TAG, "Leaving dreamland.");
                cleanupDreamLocked();
            } else if (mCurrentDreamIsWaking) {
                return; // already waking
            } else {  // 缓缓退出dreamland
                Slog.i(TAG, "Gently waking up from dream.");
                mCurrentDreamIsWaking = true;
            }

            // 进入android.fg线程执行
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    Slog.i(TAG, "Performing gentle wake from dream.");
                    // 调用进入DreamController
                    mController.stopDream(immediate);
                }
            });
        }
    }

stopDream(boolean immediate)方法有一个参数,表示是否立即退出dreamland,还是给点时间执行一个退出过渡过程。 就先以参数为false分析,看看两个参数的流程有何区别:

    public void stopDream(boolean immediate) {
        try {
            if (!immediate) {
                // 表示已经开始做退出dreamland过渡了
                if (mCurrentDream.mWakingGently) {
                    return; // already waking gently
                }

                if (mCurrentDream.mService != null) {
                    mCurrentDream.mWakingGently = true;
                    try {
                        // 调用客户端DozeService的wakeUp()
                        mCurrentDream.mService.wakeUp();
                        // 设置一个限制时长,5s后再次执行此方法
                        mHandler.postDelayed(mStopStubbornDreamRunnable, DREAM_FINISH_TIMEOUT);
                        return;  //直接返回
                    } catch (RemoteException ex) {
                    }
                }
            }
            // ......
    }

如果immediate=false,则先是调用mService#wakeUp(),然后设置一个5s的时间,到达5s后会再次进入此方法,然后直接返回,不执行后面操作。在DreamService#wakeUp()方法中,调用finish()方法:

// frameworks/base/core/java/android/service/dreams/DreamService.java

    public final void finish() {
        if (!mFinished) {
            mFinished = true;

            if (mWindowToken == null) {
            } else {
                try {
                    // 调用DreamMS.finishSelf()
                    mSandman.finishSelf(mWindowToken, true /*immediate*/);
                } catch (RemoteException ex) {
                    // system server died
                }
            }
        }
    }

finish()方法中,又通过Binder调用到了DreamMS#finishSelf(),将再一次调用stopDreamLocked()方法,不过参数将变为true:

    private void finishSelfInternal(IBinder token, boolean immediate) {
        ......
        synchronized (mLock) {
            if (mCurrentDreamToken == token) {
                stopDreamLocked(immediate);
            }
        }
    }

再次进入DreamMS#stopDreamLocked()后,将先调用cleanupDreamLocked()方法清除当前DreamLand的所有变量信息和释放Doze wakelock:

    private void cleanupDreamLocked() {
		......
        if (mCurrentDreamIsDozing) {
            mCurrentDreamIsDozing = false;
            mDozeWakeLock.release();  //释放Doze wakelock
        }
		......
        mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
        mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
    }

然后再次进入DreamController#stopDream()方法,这次进入后,immediate为true:

    public void stopDream(boolean immediate) {
        try {
            if (!immediate) {
               // 不满足
            }

            final DreamRecord oldDream = mCurrentDream;
            mCurrentDream = null;// 置空mCurrentDream对象
            // 发送Intent.ACTION_DREAMING_STOPPED广播
            if (oldDream.mSentStartBroadcast) {
                mContext.sendBroadcastAsUser(mDreamingStoppedIntent, UserHandle.ALL);
            }

            if (oldDream.mService != null) {
                try {
                    // 拆卸关联的DreamService
                    oldDream.mService.detach();
                } catch (RemoteException ex) {
                    // we don't care; this thing is on the way out
                }
                try {
                    oldDream.mService.asBinder().unlinkToDeath(oldDream, 0);
                } catch (NoSuchElementException ex) {
                    // don't care
                }
                oldDream.mService = null; //置空绑定的DreamService
            }

            if (oldDream.mBound) {
                // 解除和客户端DreamService的绑定
                mContext.unbindService(oldDream);
            }
            oldDream.releaseWakeLockIfNeeded(); // 释放锁

            try {
                // 移除WindowToken
                mIWindowManager.removeWindowToken(oldDream.mToken, DEFAULT_DISPLAY);
            } catch (RemoteException ex) {
            }
            // 发起回调
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mListener.onDreamStopped(oldDream.mToken);
                }
            });
        } 
    }

这次执行时,将依次进行以下操作:

  1. 发送Intent.ACTION_DREAMING_STOPPED广播;
  2. DreamMS和客户端DreamService进行detach操作;
  3. 调用unbindService()方法解除和客户端DreamService的绑定;
  4. 移除WindowToken;
  5. 发起mListener.onDreamStopped()回调。

和客户端DreamService进行detach操作如下:

// frameworks/base/core/java/android/service/dreams/DreamService.java
    private final void detach() {
        if (mStarted) {
            mStarted = false;
            onDreamingStopped();
        }
        // ....
    }

该方法中调用onDreamingStopped()方法,进行退出操作,和onDreamingStarted()一样,onDreamingStopped()也是一个hook方法,其具体实现交给了客户端DreamService。

detach完毕之后,通过mContext.unbindService(oldDream)方法,DreamMS和客户端DreamService进行解绑,解除绑定成功后回调onServiceDisconnected()方法:


        @Override
        public void onServiceDisconnected(ComponentName name) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mService = null;
                    if (mCurrentDream == DreamRecord.this) {
                        stopDream(true /*immediate*/);
                    }
                }
            });
        }

解绑后又会调用一次stopDream()方法,然而正常情况下,这次调用由于mCurrentDream已经被置为null了,因此不会执行。

于是,DreamMS中退出Dreamland的流程就结束了。现在回到PMS模块流程中,前面说过了,cleanupDreamLocked()的逻辑中会释放Doze WakeLock,这个操作会触发PMS中的状态更新,从而由于已经不满足Dreamland条件,因此PMS中唤醒状态将由DOZING进入ASLEEP状态,即真正意义上的灭屏:

// Doze has ended or will be stopped.  Update the power state.
reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);

就这样,退出了Dreamland......

在分析过程中,还有一个方法没有分析:DreamMS#stopDozing(),这个方法应该是提供给客户端去主动退出Dreamland的。

退出Dreamland过程时序图如下:

stop_doze.jpg

6.总结

1.经过对DreamMS模块退出和进入Dreamland的流程分析,可以发现,在交互方面,DreamMS没有直接和客户端DreamService进行交互,而是通过DreamController以Binder的方式进行交互,而客户端则可以直接通过Binder调用DreamMS。可以用下图表示这三者之间的调用关系:

relation_class.jpg

2.进入Doze后,客户端则可以自己决定Doze的亮度和Display状态,并通过startDozing()设置到FW层。如果开启DreamMSdebug,可以看到设置信息:

01-17 16:49:50.737 1721 6857 D DreamManagerService: Dream requested to start dozing: android.os.Binder@f9282a4, screenState=4, screenBrightness=4095
01-17 16:49:59.500 1721 23117 D DreamManagerService: Dream requested to start dozing: android.os.Binder@f9282a4, screenState=1, screenBrightness=4095
01-17 16:50:04.271 1721 6857 D DreamManagerService: Dream requested to start dozing: android.os.Binder@f9282a4, screenState=4, screenBrightness=4095

所以,Doze状态下客户端每次改变Doze状态下的Display状态和亮度,都会调用startDozing(IBinder token, int screenState, int screenBrightness)方法。

3.在退出Dreamland时,可以有多种方式,Powerkey亮屏时可以通过DreamManagerInternal#stopDream()退出,也可以通过客户端发起Binder调用IDreamManager#finishSelf()和awaken()退出。

4.在进入Doze后,查看LocalDisplayAdapter log时,会发现有如下DOZE和DOZE_SUSPEND不断交替的场景:

01-17 21:41:29.398 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:41:29.417 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
01-17 21:41:29.454 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:41:29.493 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
01-17 21:41:29.520 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:41:29.559 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
01-17 21:41:29.587 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:41:29.627 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
01-17 21:41:29.654 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:41:29.693 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
01-17 21:41:29.721 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:41:29.760 23692 24001 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)

这并非是客户端不断下发的Display状态,而是由于客户端不断申请释放PowerManager.DRAW_WAKE_LOCK类型的锁导致的,通过DEBUG可以看到:

01-17 21:47:17.348 29499 29514 D PowerManagerService: acquireWakeLockInternal: lock=158204803, flags=0x80, tag="gxzw", ws=null, uid=1000, pid=29873  // 申请
01-17 21:47:17.356 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)  // DOZE_SUSPEND -> DOZE
01-17 21:47:17.665 29499 30650 D PowerManagerService: releaseWakeLockInternal: lock=158204803 [gxzw], flags=0x10000  // 释放
01-17 21:47:17.672 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)  // DOZE -> DOZE_SUSPEND
01-17 21:47:18.882 29499 29514 D PowerManagerService: acquireWakeLockInternal: lock=206718903, flags=0x80, tag="Window:AOD", ws=WorkSource{1000 com.miui.aod}, uid=1000, pid=29499
01-17 21:47:18.899 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:47:19.019 29499 29499 D PowerManagerService: releaseWakeLockInternal: lock=206718903 [Window:AOD], flags=0x10000
01-17 21:47:19.023 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)
01-17 21:47:22.412 29499 30650 D PowerManagerService: acquireWakeLockInternal: lock=258767652, flags=0x80, tag="Window:gxzw_anim", ws=WorkSource{1000 com.android.systemui}, uid=1000, pid=29499
01-17 21:47:22.416 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE)
01-17 21:47:22.423 29499 30103 D PowerManagerService: acquireWakeLockInternal: lock=158204803, flags=0x80, tag="gxzw", ws=null, uid=1000, pid=29873
01-17 21:47:22.749 29499 29809 I LocalDisplayAdapter: setDisplayState(id=0, state=DOZE_SUSPEND)

对应代码如下:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
    mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
    if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0
            && !mDrawWakeLockOverrideFromSidekick) {
        if (mDisplayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
            mDisplayPowerRequest.dozeScreenState = Display.STATE_DOZE;
        }
        if (mDisplayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) {
            mDisplayPowerRequest.dozeScreenState = Display.STATE_ON;
        }
    }
    mDisplayPowerRequest.dozeScreenBrightness =
            mDozeScreenBrightnessOverrideFromDreamManager;
} 

这很好地诠释了PowerManager.DRAW_WAKE_LOCKWakeLock锁的意义,它用来让系统保持足够的awake以进行draw操作,当Display状态为Display.STATE_DOZE_SUSPEND时,表示CPU已经挂起不再更新。所以,只要有PowerManager.DRAW_WAKE_LOCK锁,Display的状态就会由Display.STATE_DOZE_SUSPEND退到Display.STATE_DOZE状态,进行轻微的绘制工作。

我们可以通过dump power 信息来查看客户端DreamService下发的状态:

$ adb shell dumpsys power | grep -Ei "mDozeScreen"
  mDozeScreenStateOverrideFromDreamManager=4
  mDozeScreenBrightnessOverrideFromDreamManager=2047

可以看到,客户端DreamService下发的Doze状态时,Display状态为Display.STATE_DOZE_SUSPEND,亮度为2047。