android6.0 PowerManagerService dream分析

13868 ワード

前回のブログではPowerManagerServiceの様々な状態を分析し、dream関連について詳しく説明しませんでした.ここでもう一度分析します.

一、Dreamクローズ


まず、印刷されたlogと結びつけて、handleSandman関数を見てみましょう.logは次の関数に直接加算されます.もちろん私たちが今言っている状況はmWakefulnessがdozing状態です.
    private void handleSandman() { // runs on handler thread
        // Handle preconditions.
        final boolean startDreaming;
        final int wakefulness;
        synchronized (mLock) {
            mSandmanScheduled = false;
            wakefulness = mWakefulness;
            if (mSandmanSummoned && mDisplayReady) {
                startDreaming = canDreamLocked() || canDozeLocked();
                Slog.d(TAG, "handleSandman startDreaming:" + startDreaming);// log true
                mSandmanSummoned = false;
            } else {
                startDreaming = false;
            }
        }

        // Start dreaming if needed.
        // We only control the dream on the handler thread, so we don't need to worry about
        // concurrent attempts to start or stop the dream.
        final boolean isDreaming;
        if (mDreamManager != null) {
            // Restart the dream whenever the sandman is summoned.
            if (startDreaming) {
                mDreamManager.stopDream(false /*immediate*/);
                mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);// startStream
            }
            
            isDreaming = mDreamManager.isDreaming();
            Slog.d(TAG, "handleSandman isDreaming:" + isDreaming);// false
        } else {
        Slog.d(TAG, "handleSandman mDreamManager null");
            isDreaming = false;
        }

        // Update dream state.
        synchronized (mLock) {
            // Remember the initial battery level when the dream started.
            if (startDreaming && isDreaming) {
                mBatteryLevelWhenDreamStarted = mBatteryLevel;
                if (wakefulness == WAKEFULNESS_DOZING) {
                    Slog.i(TAG, "Dozing...");
                } else {
                    Slog.i(TAG, "Dreaming...");
                }
            }

            // If preconditions changed, wait for the next iteration to determine
            // whether the dream should continue (or be restarted).
            if (mSandmanSummoned || mWakefulness != wakefulness) {
                return; // wait for next cycle
            }

            // Determine whether the dream should continue.
            if (wakefulness == WAKEFULNESS_DREAMING) {
                Slog.d(TAG, "handleSandman WAKEFULNESS_DREAMING");
                if (isDreaming && canDreamLocked()) {
                    if (mDreamsBatteryLevelDrainCutoffConfig >= 0
                            && mBatteryLevel < mBatteryLevelWhenDreamStarted
                                    - mDreamsBatteryLevelDrainCutoffConfig
                            && !isBeingKeptAwakeLocked()) {
                        // If the user activity timeout expired and the battery appears
                        // to be draining faster than it is charging then stop dreaming
                        // and go to sleep.
                        Slog.i(TAG, "Stopping dream because the battery appears to "
                                + "be draining faster than it is charging.  "
                                + "Battery level when dream started: "
                                + mBatteryLevelWhenDreamStarted + "%.  "
                                + "Battery level now: " + mBatteryLevel + "%.");
                    } else {
                        return; // continue dreaming
                    }
                }

                // Dream has ended or will be stopped.  Update the power state.
                if (isItBedTimeYetLocked()) {
                    goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
                            PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
                    updatePowerStateLocked();
                } else {
                    wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), "android.server.power:DREAM",
                            Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID);
                    updatePowerStateLocked();
                }
            } else if (wakefulness == WAKEFULNESS_DOZING) {
                if (isDreaming) {// false
                    Slog.d(TAG, "handleSandman isDreaming = true");
                    return; // continue dozing
                }

                // Doze has ended or will be stopped.  Update the power state.
                Slog.d(TAG, "handleSandman mwakefulness:" + mWakefulness);//dozing 
                reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
                Slog.d(TAG, "handleSandman mwakefulness:" + mWakefulness);//sleep 
                updatePowerStateLocked();
            }
        }

        Slog.d(TAG, "handleSandman wakefulness:" + mWakefulness);

        // Stop dream.
        if (isDreaming) {
            mDreamManager.stopDream(false /*immediate*/);
        }
    }

上のコメントを見て明らかにstartdreamを呼び出しましたが、isDreamingはfalseを返します.明らかにdreamをサポートしていない.そのためパワーも直接sleep状態に入ります.
では、このdreamを見てみましょう.次はDreamManagerServiceのLocalServiceです.
    private final class LocalService extends DreamManagerInternal {
        @Override
        public void startDream(boolean doze) {
            startDreamInternal(doze);
        }

        @Override
        public void stopDream(boolean immediate) {
            stopDreamInternal(immediate);
        }

        @Override
        public boolean isDreaming() {
            return isDreamingInternal();
        }
    }
startDreamInternal関数を見てみましょう
    private void startDreamInternal(boolean doze) {
        Slog.d(TAG, "startDreamInternal");
        final int userId = ActivityManager.getCurrentUser();
        final ComponentName dream = chooseDreamForUser(doze, userId);
        
        if (dream != null) {
            synchronized (mLock) {
                startDreamLocked(dream, false /*isTest*/, doze, userId);
            }
        } else {
            Slog.d(TAG, "dream is null");//  log
        }
    }

この関数にlogを加えるとdreamがnullであることが分かったので,圧根美欧はstartDreamLocked関数を呼び出した.
    private ComponentName chooseDreamForUser(boolean doze, int userId) {
        if (doze) {
            ComponentName dozeComponent = getDozeComponent(userId);
            return validateDream(dozeComponent) ? dozeComponent : null;
        }
        ComponentName[] dreams = getDreamComponentsForUser(userId);
        return dreams != null && dreams.length != 0 ? dreams[0] : null;
    }

chooseDreamForUserがnullに戻るのは、getDozeComponentがnullに戻るからです.
getDozeComponent関数を見てみましょう.
    private ComponentName getDozeComponent(int userId) {
        String name = Build.IS_DEBUGGABLE ? SystemProperties.get("debug.doze.component") : null;
        if (TextUtils.isEmpty(name)) {
            name = mContext.getResources().getString(
                    com.android.internal.R.string.config_dozeComponent);
        }
        boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                Settings.Secure.DOZE_ENABLED, 1, userId) != 0;
        return TextUtils.isEmpty(name) || !enabled ? null : ComponentName.unflattenFromString(name);
    }

上の変数nameはnullであり、nullを返します.やはりシステムがdreamをサポートしていない理由です.

二、DreamService


システムはサポートしていませんが、コードも見てstartDreamLocked関数を見てみましょう.
    private void startDreamLocked(final ComponentName name,
            final boolean isTest, final boolean canDoze, final int userId) {
        if (Objects.equal(mCurrentDreamName, name)
                && mCurrentDreamIsTest == isTest
                && mCurrentDreamCanDoze == canDoze
                && mCurrentDreamUserId == userId) {
            return;
        }

        stopDreamLocked(true /*immediate*/);

        Slog.i(TAG, "Entering dreamland.");

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

        mHandler.post(new Runnable() {
            @Override
            public void run() {
                mController.startDream(newToken, name, isTest, canDoze, userId);
            }
        });
    }

最終的にはmControllerが呼び出された.startDream関数、DreamControllerクラスのstartDream関数を見てみましょう
    public void startDream(Binder token, ComponentName name,
            boolean isTest, boolean canDoze, int userId) {
        stopDream(true /*immediate*/);

        Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream");
        try {
            // Close the notification shade. Don't need to send to all, but better to be explicit.
            mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);

            mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId);

            mDreamStartTime = SystemClock.elapsedRealtime();
            MetricsLogger.visible(mContext,
                    mCurrentDream.mCanDoze ? MetricsLogger.DOZING : MetricsLogger.DREAMING);

            try {
                mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM);
            } catch (RemoteException ex) {
                Slog.e(TAG, "Unable to add window token for dream.", ex);
                stopDream(true /*immediate*/);
                return;
            }

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

            mCurrentDream.mBound = true;
            mHandler.postDelayed(mStopUnconnectedDreamRunnable, DREAM_CONNECTION_TIMEOUT);// 
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_POWER);
        }
    }

そしてbindServiceはDreamServiceで、このServiceのonBindインタフェースを見てみましょう.
    public final IBinder onBind(Intent intent) {
        if (mDebug) Slog.v(TAG, "onBind() intent = " + intent);
        return new DreamServiceWrapper();
    }

DreamServiceWrapperは次のようにbinderインタフェースです.binder通信用
    private final class DreamServiceWrapper extends IDreamService.Stub {
        @Override
        public void attach(final IBinder windowToken, final boolean canDoze) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    DreamService.this.attach(windowToken, canDoze);
                }
            });
        }

        @Override
        public void detach() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    DreamService.this.detach();
                }
            });
        }

        @Override
        public void wakeUp() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    DreamService.this.wakeUp(true /*fromSystem*/);
                }
            });
        }
    }

DreamRecordを見てみると、DreamControllerがbindServiceを呼び出したときにServiceConnectionとして
    private final class DreamRecord implements DeathRecipient, ServiceConnection {
        ......
        // May be called on any thread.
        @Override
        public void onServiceConnected(ComponentName name, final IBinder service) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mConnected = true;
                    if (mCurrentDream == DreamRecord.this && mService == null) {
                        attach(IDreamService.Stub.asInterface(service));
                    }
                }
            });
        }

さらにattachという関数を見て、サービスのattach関数をリモートで呼び出し、サービスのbinderをmCurrentDreamのmServiceに保存することで、DreamServiceと通信できるようになりました
    private void attach(IDreamService service) {
        try {
            service.asBinder().linkToDeath(mCurrentDream, 0);
            service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze);
        } catch (RemoteException ex) {
            Slog.e(TAG, "The dream service died unexpectedly.", ex);
            stopDream(true /*immediate*/);
            return;
        }

        mCurrentDream.mService = service;

        if (!mCurrentDream.mIsTest) {
            mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL);
            mCurrentDream.mSentStartBroadcast = true;
        }
    }

三、まとめ


このブログでは主にPowerManagerServiceでdreamがサポートされていないことを説明していますが、DreamServiceのソースコードも簡単に紹介しています.