Android系统屏保功能开发(Android10)

本文详细介绍了Android10系统屏保功能的开发过程,包括AndroidManifest.xml的配置,PowerManagerService和DreamManagerService的分析,以及DreamService的实现。在AndroidManifest.xml中配置系统应用和DreamService,通过PowerManagerService监听屏保状态,DeamManagerService负责启动屏保,并在DreamService中自定义UI。文章深入探讨了屏保启动、设置和UI初始化的关键步骤。

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;
}
Android 系统中,默认的屏幕保护程序(Screen Saver)通常被称为“休眠”或“壁纸幻灯片”,具体实现和设置方式取决于设备制造商和 Android 版本。以下是与 Android 系统默认屏幕保护程序相关的设置和修改方法。 ### Android 系统默认屏幕保护程序的设置 在 Android 系统中,屏幕保护程序通常通过系统设置中的“显示”或“安全”菜单进行配置。用户可以选择启用屏幕保护程序,并设置其启动时间、样式等。 #### 1. 启用和配置屏幕保护程序 用户可以通过以下路径在系统设置中启用和配置屏幕保护程序: - **设置 → 显示 → 屏幕保护程序**(不同设备可能路径略有不同) 在此界面中,可以进行以下操作: - 启用/禁用屏幕保护程序 - 设置屏幕保护程序的启动延迟时间 - 选择屏幕保护程序的样式(如静态壁纸、动态壁纸、幻灯片等) #### 2. 修改默认屏幕保护程序 系统默认的屏幕保护程序通常由系统框架或设备制造商预置。如果需要修改默认的屏幕保护程序,通常需要在系统源码中进行更改。以下是涉及的核心文件和路径: - **系统框架中的配置文件**: - `frameworks/base/packages/SettingsProvider/res/values/defaults.xml` - `frameworks/base/core/res/res/values/config.xml` 在这些文件中,可以找到与屏幕保护程序相关的配置项,例如默认的屏幕保护程序组件名称或样式。 例如,在 `defaults.xml` 中可能包含如下配置: ```xml <integer name="def_screen_off_timeout">60000</integer> <string name="def_screensaver_component">com.android.systemui/.wallpaper.ImageWallpaper</string> ``` 该配置项定义了默认的屏幕保护程序组件。 #### 3. 自定义屏幕保护程序 开发者可以创建自定义的屏幕保护程序,通过实现 `WallpaperService` 或使用 `Daydream`(在 Android 4.4 及以上版本中引入)来开发动态的屏幕保护程序。 以下是一个简单的 `WallpaperService` 示例: ```java public class MyWallpaperService extends WallpaperService { @Override public void onCreate() { super.onCreate(); } @Override public Engine onCreateEngine() { return new MyWallpaperEngine(); } private class MyWallpaperEngine extends Engine { // 实现自定义绘制逻辑 } } ``` 在 `AndroidManifest.xml` 中注册该服务: ```xml <service android:name=".MyWallpaperService" android:permission="android.permission.BIND_WALLPAPER"> <intent-filter> <action android:name="android.service.wallpaper.WallpaperService" /> </intent-filter> <meta-data android:name="android.service.wallpaper" android:resource="@xml/wallpaper" /> </service> ``` 此外,还需要在 `res/xml/wallpaper.xml` 中定义壁纸的配置。 ### 修改系统默认屏幕保护程序组件 如果需要修改系统默认的屏幕保护程序组件(例如更改默认的 `ImageWallpaper`),可以在系统源码中修改 `def_screensaver_component` 的值: ```xml <string name="def_screensaver_component">com.example.myapp/.MyWallpaperService</string> ``` 修改后重新编译并刷入系统镜像即可生效。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值