android6.0 PowerManagerService dream分析
13868 ワード
前回のブログではPowerManagerServiceの様々な状態を分析し、dream関連について詳しく説明しませんでした.ここでもう一度分析します.
まず、印刷されたlogと結びつけて、handleSandman関数を見てみましょう.logは次の関数に直接加算されます.もちろん私たちが今言っている状況はmWakefulnessがdozing状態です.
上のコメントを見て明らかにstartdreamを呼び出しましたが、isDreamingはfalseを返します.明らかにdreamをサポートしていない.そのためパワーも直接sleep状態に入ります.
では、このdreamを見てみましょう.次はDreamManagerServiceのLocalServiceです.
この関数にlogを加えるとdreamがnullであることが分かったので,圧根美欧はstartDreamLocked関数を呼び出した.
chooseDreamForUserがnullに戻るのは、getDozeComponentがnullに戻るからです.
getDozeComponent関数を見てみましょう.
上の変数nameはnullであり、nullを返します.やはりシステムがdreamをサポートしていない理由です.
システムはサポートしていませんが、コードも見てstartDreamLocked関数を見てみましょう.
最終的にはmControllerが呼び出された.startDream関数、DreamControllerクラスのstartDream関数を見てみましょう
そしてbindServiceはDreamServiceで、このServiceのonBindインタフェースを見てみましょう.
DreamServiceWrapperは次のようにbinderインタフェースです.binder通信用
DreamRecordを見てみると、DreamControllerがbindServiceを呼び出したときにServiceConnectionとして
さらにattachという関数を見て、サービスのattach関数をリモートで呼び出し、サービスのbinderをmCurrentDreamのmServiceに保存することで、DreamServiceと通信できるようになりました
このブログでは主にPowerManagerServiceでdreamがサポートされていないことを説明していますが、DreamServiceのソースコードも簡単に紹介しています.
一、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のソースコードも簡単に紹介しています.