Android系统屏保功能开发
1 AndroidManifest.xml 文件配置
需要先配置为系统应用
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.xxx.xxx"
android:sharedUserId="android.uid.system">
<uses-permission android:name="android.permission.BIND_DREAM_SERVICE" />
...
<!--屏保服务类-->
<service
android:name=".display.xxx.MyDream"
android:enabled="true"
android:exported="true"
android:icon="@drawable/bg_screensaver_clock"
android:label="@string/display_dream_clock"
android:permission="android.permission.BIND_DREAM_SERVICE">
<intent-filter>
<action android:name="android.service.dreams.DreamService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</manifest>
实现 DreamService
在这里,我实现了一个基类,方便扩展多种屏保。
public abstract class BaseDream extends DreamService {
public static final String TAG = BaseDream.class.getName();
public boolean mLowProfile;
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
wakeUp();
return true;
}
@Override
public void onWakeUp() {
super.onWakeUp();
wakeupDream();
}
protected void wakeupDream() {
PowerManager pm = getSystemService(PowerManager.class);
pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
"com.android.systemui:NODOZE");
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
init();
hideBar();
}
@Override
public void onDreamingStarted() {
super.onDreamingStarted();
}
@Override
public void onDreamingStopped() {
super.onDreamingStopped();
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
release();
}
@Override
public void onDestroy() {
super.onDestroy();
}
protected abstract void init();
protected abstract void release();
private void hideBar() {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LOW_PROFILE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
public void setLowProfile(boolean lowProfile) {
if (mLowProfile != lowProfile) {
mLowProfile = lowProfile;
int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE;
applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag);
}
}
private void applySystemUiVisibilityFlags(int flags, int mask) {
View v = getWindow() == null ? null : getWindow().getDecorView();
if (v != null) {
v.setSystemUiVisibility(applyFlags(v.getSystemUiVisibility(), flags, mask));
}
}
private int applyFlags(int oldFlags, int flags, int mask) {
return (oldFlags&~mask) | (flags&mask);
}
}
实现时钟屏保类,不外乎就是在这个服务里自定义自己的UI。
/**
* 系统默认屏保:时钟屏保
* 详情见 device\rockchip\rk3399\PRODUCT\overlay\frameworks\base\core\res\res\values\config.xml
* <string name="config_dreamsDefaultComponent"
* translatable="false">com.XXX.XXX/com.XXX.XXX.display.screensaver.dream.MyDream</string>
*/
public class ClockDream extends BaseDream {
public static final String TAG = ClockDream.class.getSimpleName();
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
refreshLayoutParams();
}
private void refreshLayoutParams() {
}
@Override
protected void init() {
setInteractive(false);
setFullscreen(true);
setLowProfile(true);
// 设置布局文件
setContentView(R.layout.view_dream_screensaver);
...
}
@Override
protected void release() {
...
}
...
}
到这里开发基本完成,那怎么才能把我们定义好的屏保设置为当前屏保呢,其实在系统启动的时候,会加载 AndroidManifest.xml 清单文件,解析并将配置为屏保的服务存储起来,通过系统服务来向应用层提供屏保列表的遍历、设置接口。
2 PowerManagerService 分析
屏保从何时启动,在系统SystemServer startOtherServices 服务启动阶段, PowerManagerService 系统服务准备就绪时,调用 systemReady() 方法,注册了更新屏保的系统广播,通过接收 Intent.ACTION_DREAMING_STARTED、Intent.ACTION_DREAMING_STOPPED 广播来对外提供控制屏保的启动与停止, 另一种方式就是通过接口来控制,上述两种方式最后调用的接口最后都会调用到 DeamManagerService 服务的接口。
//frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
public final class PowerManagerService extends SystemService
implements Watchdog.Monitor {
// Message: 当发生用户活动超时(界面无操作)以更新电源状态时发送.
private static final int MSG_USER_ACTIVITY_TIMEOUT = 1;
// Message: 当设备进入或退出dreaming或dozing状态时发送
private static final int MSG_SANDMAN = 2;
// Message: 当屏幕亮度提升到期时发送
private static final int MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 3;
// Message: 轮询以查找长期持有的唤醒锁.
private static final int MSG_CHECK_FOR_LONG_WAKELOCKS = 4;
public void systemReady(IAppOpsService appOps) {
mSystemReady = true;
mAppOps = appOps;
mDreamManager = getLocalService(DreamManagerInternal.class);
mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
mPolicy = getLocalService(WindowManagerPolicy.class);
//省略代码...
ilter = new IntentFilter();
filter.addAction(Intent.ACTION_DREAMING_STARTED);
filter.addAction(Intent.ACTION_DREAMING_STOPPED);
mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler);
}
private final class DreamReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
scheduleSandmanLocked();
}
}
}
// 广播调用、updateDreamLocked() 调用
private void scheduleSandmanLocked() {
if (!mSandmanScheduled) {
mSandmanScheduled = true;
// 发送消息 MSG_SANDMAN
Message msg = mHandler.obtainMessage(MSG_SANDMAN);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
private final class PowerManagerHandler extends Handler {
public PowerManagerHandler(Looper looper) {
super(looper, null, true /*async*/);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_USER_ACTIVITY_TIMEOUT:
handleUserActivityTimeout();
break;
case MSG_SANDMAN:
// 进入屏保的入口,后面分析
handleSandman();
break;
case MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT:
handleScreenBrightnessBoostTimeout();
break;
case MSG_CHECK_FOR_LONG_WAKELOCKS:
checkForLongWakeLocks();
break;
}
}
}
// 这个方法是在电源状态改变的时候回去调用,逻辑比较复杂,我们分析到这里先不详细说明
private void updatePowerStateLocked() {
if (!mSystemReady || mDirty == 0) {
return;
}
if (!Thread.holdsLock(mLock)) {
Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked");
}
Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState");
try {
// Phase 0: Basic state updates.
updateIsPoweredLocked(mDirty);
updateStayOnLocked(mDirty);
updateScreenBrightnessBoostLocked(mDirty);
// Phase 1: Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
final long now = SystemClock.uptimeMillis();
int dirtyPhase2 = 0;
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;
updateWakeLockSummaryLocked(dirtyPhase1);
updateUserActivitySummaryLocked(now, dirtyPhase1);
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
}
// Phase 2: Lock profiles that became inactive/not kept awake.
updateProfilesLocked(now);
// Phase 3: Update display power state.
final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
// Phase 4: Update dream state (depends on display ready signal).
updateDreamLocked(dirtyPhase2, displayBecameReady); // 入口
// Phase 5: Send notifications, if needed.
finishWakefulnessChangeIfNeededLocked();
// Phase 6: Update suspend blocker.
// Because we might release the last suspend blocker here, we need to make sure
// we finished everything else first!
updateSuspendBlockerLocked();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
}
我们这里直接分析 handleSandman 方法
private void handleSandman() { // runs on handler thread
// Handle preconditions.
final boolean startDreaming;
final int wakefulness;
synchronized (mLock) {
mSandmanScheduled = false;
wakefulness = mWakefulness;
if (mSandmanSummoned && mDisplayReady) {
// modified by wugm
startDreaming = canDreamLocked()|| canDozeLocked();
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*/);
// 通过 DreamManager 直接调用屏保
mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);
}
isDreaming = mDreamManager.isDreaming();
} else {
isDreaming = false;
}
// At this point, we either attempted to start the dream or no attempt will be made,
// so stop holding the display suspend blocker for Doze.
mDozeStartInProgress = 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) {
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(),
PowerManager.WAKE_REASON_UNKNOWN,
"android.server.power:DREAM_FINISHED", Process.SYSTEM_UID,
mContext.getOpPackageName(), Process.SYSTEM_UID);
updatePowerStateLocked();
}
} else if (wakefulness == WAKEFULNESS_DOZING) {
if (isDreaming) {
return; // continue dozing
}
// Doze has ended or will be stopped. Update the power state.
reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
updatePowerStateLocked();
}
}
// Stop dream.
if (isDreaming) {
mDreamManager.stopDream(false /*immediate*/);
}
}
3 DeamManagerService 解析
追踪 startDream 方法
// frameworks/base/services/core/java/com/android/server/dreams/DreamManagerService.java
private final class LocalService extends DreamManagerInternal {
@Override
public void startDream(boolean doze) {
// modified by wugm
Slog.i(TAG, "startDreamInternal doze:"+ doze);
startDreamInternal(doze);
}
@Override
public void stopDream(boolean immediate) {
stopDreamInternal(immediate);
}
@Override
public boolean isDreaming() {
return isDreamingInternal();
}
}
private void startDreamInternal(boolean doze) {
final int userId = ActivityManager.getCurrentUser();
// 1.获取当前屏保
final ComponentName dream = chooseDreamForUser(doze, userId);
Slog.i(TAG, "startDreamInternal dream: " + dream);
if (dream != null) {
synchronized (mLock) {
// 2.开始启动屏保
startDreamLocked(dream, false /*isTest*/, doze, userId);
}
}
}
3.1 获取当前屏保
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;
}
private ComponentName[] getDreamComponentsForUser(int userId) {
String names = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Settings.Secure.SCREENSAVER_COMPONENTS,
userId);
ComponentName[] components = componentsFromString(names);
// first, ensure components point to valid services
List<ComponentName> validComponents = new ArrayList<ComponentName>();
if (components != null) {
for (ComponentName component : components) {
if (validateDream(component)) {
validComponents.add(component);
}
}
}
// modified by ian4u
boolean screensaverEnabled = Settings.Secure.getInt(
mContext.getContentResolver(),
Settings.Secure.SCREENSAVER_ENABLED,
1) == 1;
// fallback to the default dream component if necessary
if (validComponents.isEmpty() && screensaverEnabled) {
ComponentName defaultDream = getDefaultDreamComponentForUser(userId);
if (defaultDream != null) {
Slog.w(TAG, "Falling back to default dream " + defaultDream);
validComponents.add(defaultDream);
}
}
return validComponents.toArray(new ComponentName[validComponents.size()]);
}
private ComponentName getDefaultDreamComponentForUser(int userId) {
String name = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
userId);
return name == null ? null : ComponentName.unflattenFromString(name);
}
3.2 开始启动屏保
private void startDreamLocked(final ComponentName name,
final boolean isTest, final boolean canDoze, final int userId) {
if (Objects.equals(mCurrentDreamName, name)
&& mCurrentDreamIsTest == isTest
&& mCurrentDreamCanDoze == canDoze
&& mCurrentDreamUserId == userId) {
Slog.i(TAG, "Already in target dream.");
return;
}
stopDreamLocked(true /*immediate*/);
Slog.i(TAG, "Entering dreamland.");
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");
mHandler.post(wakeLock.wrap(
// 通过 DreamController 启动
() -> mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock)));
}
DreamManagerService 也是在SystemServer启动startOtherServices服务阶段启动的
public final class DreamManagerService extends SystemService {
public DreamManagerService(Context context) {
super(context);
mContext = context;
mHandler = new DreamHandler(FgThread.get().getLooper());
// 实例化 DreamController
mController = new DreamController(context, mHandler, mControllerListener);
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG);
mDozeConfig = new AmbientDisplayConfiguration(mContext);
}
}
4 DreamController 分析
// frameworks/base/services/core/java/com/android/server/dreams/DreamController.java
final class DreamController {
public DreamController(Context context, Handler handler, Listener listener) {
mContext = context;
mHandler = handler;
mListener = listener;
mIWindowManager = WindowManagerGlobal.getWindowManagerService();
mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mCloseNotificationShadeIntent.putExtra("reason", "dream");
}
// 主要分析
public void startDream(Binder token, ComponentName name,
boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
stopDream(true /*immediate*/);
Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream");
try {
// Close the notification shade. No need to send to all, but better to be explicit.
mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
Slog.i(TAG, "Starting dream: name=" + name
+ ", isTest=" + isTest + ", canDoze=" + canDoze
+ ", userId=" + userId);
mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId, wakeLock);
mDreamStartTime = SystemClock.elapsedRealtime();
MetricsLogger.visible(mContext,
mCurrentDream.mCanDoze ? MetricsEvent.DOZING : MetricsEvent.DREAMING);
try {
// 为屏保服务添加专用的窗口 TYPE_DREAM
// WindowManager public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
// Android 12上 屏保服务更改成了启动 DreamActivity 的形式显示屏保
mIWindowManager.addWindowToken(token, TYPE_DREAM, DEFAULT_DISPLAY);
} catch (RemoteException ex) {
Slog.e(TAG, "Unable to add window token for dream.", ex);
stopDream(true /*immediate*/);
return;
}
// DreamService.SERVICE_INTERFACE 为 android.service.dreams.DreamService
// 通过这个action来找到我们再用中自定义的屏保服务
Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
intent.setComponent(name);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
try {
if (!mContext.bindServiceAsUser(intent, mCurrentDream,
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);
}
}
// frameworks/base/services/core/java/com/android/server/dreams/DreamController.java
private final class DreamRecord implements DeathRecipient, ServiceConnection {
public final Binder mToken;
public final ComponentName mName;
public final boolean mIsTest;
public final boolean mCanDoze;
public final int mUserId;
public PowerManager.WakeLock mWakeLock;
public boolean mBound;
public boolean mConnected;
public IDreamService mService;
public boolean mSentStartBroadcast;
public boolean mWakingGently;
public DreamRecord(Binder token, ComponentName name,
boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
mToken = token;
mName = name;
mIsTest = isTest;
mCanDoze = canDoze;
mUserId = userId;
mWakeLock = wakeLock;
// Hold the lock while we're waiting for the service to connect and start dreaming.
// Released after the service has started dreaming, we stop dreaming, or it timed out.
mWakeLock.acquire();
mHandler.postDelayed(mReleaseWakeLockIfNeeded, 10000);
}
// May be called on any thread.
@Override
public void binderDied() {
mHandler.post(new Runnable() {
@Override
public void run() {
mService = null;
if (mCurrentDream == DreamRecord.this) {
stopDream(true /*immediate*/);
}
}
});
}
// 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 入口
attach(IDreamService.Stub.asInterface(service));
// Wake lock will be released once dreaming starts.
} else {
releaseWakeLockIfNeeded();
}
}
});
}
// May be called on any thread.
@Override
public void onServiceDisconnected(ComponentName name) {
mHandler.post(new Runnable() {
@Override
public void run() {
mService = null;
if (mCurrentDream == DreamRecord.this) {
stopDream(true /*immediate*/);
}
}
});
}
void releaseWakeLockIfNeeded() {
if (mWakeLock != null) {
mWakeLock.release();
mWakeLock = null;
mHandler.removeCallbacks(mReleaseWakeLockIfNeeded);
}
}
final Runnable mReleaseWakeLockIfNeeded = this::releaseWakeLockIfNeeded;
final IRemoteCallback mDreamingStartedCallback = new IRemoteCallback.Stub() {
// May be called on any thread.
@Override
public void sendResult(Bundle data) throws RemoteException {
mHandler.post(mReleaseWakeLockIfNeeded);
}
};
}
private void attach(IDreamService service) {
try {
service.asBinder().linkToDeath(mCurrentDream, 0);
// binder调用代理对象IDreamService的 attach 方法,
// 实际上调用到 DreamService的attach方法, 继续DreamService方法往分析
// 传递参数比较关键 mCurrentDream.mToken
service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze,
mCurrentDream.mDreamingStartedCallback);
} catch (RemoteException ex) {
Slog.e(TAG, "The dream service died unexpectedly.", ex);
stopDream(true /*immediate*/);
return;
}
mCurrentDream.mService = service;
if (!mCurrentDream.mIsTest) {
// 发送广播 Intent.ACTION_DREAMING_STARTED
mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL);
mCurrentDream.mSentStartBroadcast = true;
}
}
}
5 DreamService 分析
这里通过 action 来绑定我们在应用中定义好的的 DreamService, 我们来看一下里面的具体实现
// frameworks/base/core/java/android/service/dreams/DreamService.java
public class DreamService extends Service implements Window.Callback {
//该服务实现了 Window.Callback 接口,说明他能响应正常Window的用户操作。
private final IDreamManager mSandman;
private final Handler mHandler = new Handler();
private IBinder mWindowToken;
private Window mWindow;
private boolean mInteractive;
private boolean mLowProfile = true;
private boolean mFullscreen;
private boolean mScreenBright = true;
private boolean mStarted;
private boolean mWaking;
private boolean mFinished;
private boolean mCanDoze;
private boolean mDozing;
private boolean mWindowless;
private int mDozeScreenState = Display.STATE_UNKNOWN;
private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
public DreamService() {
mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
}
@Override
public final IBinder onBind(Intent intent) {
if (mDebug) Slog.v(TAG, "onBind() intent = " + intent);
// 通过 DreamServiceWrapper 包装 IDreamService.Stub 来为DreamController提供代理对象。
return new DreamServiceWrapper();
}
private final class DreamServiceWrapper extends IDreamService.Stub {
@Override
public void attach(final IBinder windowToken, final boolean canDoze,
IRemoteCallback started) {
mHandler.post(new Runnable() {
@Override
public void run() {
DreamService.this.attach(windowToken, canDoze, started);
}
});
}
@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*/);
}
});
}
}
}
真正启用一个屏保窗口的代码位置:
private final void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started) {
if (mWindowToken != null) {
Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken);
return;
}
if (mFinished || mWaking) {
Slog.w(TAG, "attach() called after dream already finished");
try {
mSandman.finishSelf(windowToken, true /*immediate*/);
} catch (RemoteException ex) {
// system server died
}
return;
}
// 保存之前创建好的窗口 windowToken
mWindowToken = windowToken;
mCanDoze = canDoze;
if (mWindowless && !mCanDoze) {
throw new IllegalStateException("Only doze dreams can be windowless");
}
if (!mWindowless) {
// 创建了 PhoneWindow
mWindow = new PhoneWindow(this);
// 设置 callback
mWindow.setCallback(this);
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000));
mWindow.setFormat(PixelFormat.OPAQUE);
if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s",
windowToken, WindowManager.LayoutParams.TYPE_DREAM));
// 设置窗口布局
WindowManager.LayoutParams lp = mWindow.getAttributes();
lp.type = WindowManager.LayoutParams.TYPE_DREAM;
lp.token = windowToken;
lp.windowAnimations = com.android.internal.R.style.Animation_Dream;
lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
| (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
| (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
);
mWindow.setAttributes(lp);
// Workaround: Currently low-profile and in-window system bar backgrounds don't go
// along well. Dreams usually don't need such bars anyways, so disable them by default.
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
mWindow.setWindowManager(null, windowToken, "dream", true);
applySystemUiVisibilityFlags(
(mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
View.SYSTEM_UI_FLAG_LOW_PROFILE);
try {
// 将窗口视图添加到 WindowManager 中,渲染界面
getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes());
} catch (WindowManager.BadTokenException ex) {
// This can happen because the dream manager service will remove the token
// immediately without necessarily waiting for the dream to start.
// We should receive a finish message soon.
Slog.i(TAG, "attach() called after window token already removed, dream will "
+ "finish soon");
mWindow = null;
return;
}
}
// 将 onDreamingStarted 的调用推迟到 onWindowAttached 之后,它由 addView 发布到处理程序
mHandler.post(new Runnable() {
@Override
public void run() {
if (mWindow != null || mWindowless) {
if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()");
mStarted = true;
try {
onDreamingStarted();
} finally {
try {
started.sendResult(null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
}
});
}
走到在这里的流程,我们发现我们并未设置过相关的布局文件, 那它又是怎样进行初始化的呢?
这就需要用户在实现 DreamSercie时,自行为当前 window 设置一个 contentView了,那又应该在什么时候调用呢?
根据DreamService中相关提示
/**
* Sets a view to be the content view for this Dream.
* Behaves similarly to
* {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}
* in an activity.
*
* <p>Note: This requires a window, so you should usually call it during
* {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it
* during {@link #onCreate}).</p>
*java
* @param view The desired content to display.
* @param params Layout parameters for the view.
*
* @see #setContentView(android.view.View)
* @see #setContentView(int)
*/
public void setContentView(View view, ViewGroup.LayoutParams params) {
getWindow().setContentView(view, params);
}
在onAttachedToWindow() 之后,不要在之前
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor(); //创建 DecorView
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
// 创建相关布局
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
本文详细介绍了Android10系统屏保功能的开发过程,包括AndroidManifest.xml的配置,PowerManagerService和DreamManagerService的分析,以及DreamService的实现。在AndroidManifest.xml中配置系统应用和DreamService,通过PowerManagerService监听屏保状态,DeamManagerService负责启动屏保,并在DreamService中自定义UI。文章深入探讨了屏保启动、设置和UI初始化的关键步骤。
1752

被折叠的 条评论
为什么被折叠?



