Doze 模式下 Alram 无法定时唤醒的解决方案

1. adb 命令模拟进入doze模式

  1. 设置未充电状态

方便连接logcat查看实时日志,正常情况下如果连接 USB 是无法进入doze模式,这个步骤是欺骗系统当前没有连接USB,虽然实际连接得好好的

adb shell dumpsys battery unplug
  1. 设置开启 alarm 日志

并不是每台机器都开启 alarm 的日志,所以我们可以命令强制开启

adb shell dumpsys alarm log on
  1. 强制进入Doze模块

要调试就进入深度休眠模式,即 deep idle mode

adb shell dumpsys deviceidle force-idle deep

2. 复现现象

2.1 Alarm的参考代码
    starAlarmTaskByService(this, 5);


    public static void starAlarmTaskByService(Context context, int intervalMinute) {
        AlarmManager mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        long triggerAtMillis = System.currentTimeMillis() + (intervalMinute * 60 * 1000);

        Log.d(TAG, "starAlarmTaskByService action = " + ACTION_RTC_WAKEUP_ALRTM_TYPE_0_SERVIE  + ", 下次唤醒时刻 = " + DateTimeUtil.getSysDate(triggerAtMillis));

        Intent intent = new Intent(context, MyService.class);
        intent.setAction(ACTION_RTC_WAKEUP_ALRTM_TYPE_0_SERVIE);

        PendingIntent operation = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, operation);
        } else {
            mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerAtMillis, operation);
        }
    }
2.2 异常日志

注意这里的 flags

2018-08-23 14:25:07.989 934-24435/? V/AlarmManager: set(PendingIntent{c4c0513: PendingIntentRecord{59c2350 test.demo.alarm.zui.com.alarmtest startService}}) : type=0 triggerAtTime=1535005807985 win=0 tElapsed=70204635 maxElapsed=70204635 interval=0 flags=0x1(非DeviceIdleUserWhitelist白名单)
2.3 正常日志

注意这里的 flags

2018-08-23 14:18:29.063 934-945/? V/AlarmManager: set(PendingIntent{c6e22ba: PendingIntentRecord{f55a56b test.demo.alarm.zui.com.alarmtest startService}}) : type=0 triggerAtTime=1535005409060 win=0 tElapsed=69805710 maxElapsed=69805710 interval=0 flags=0x9(DeviceIdleUserWhitelist白名单正常)
2.4 查看对应doze模式白名单

使用命令 adb shell dumpsys deviceidle whitelist 查看

2.4.1 异常现象白名单 1

这个是在系统源码路径 frameworks\base\data\etc\platform.xml,可以手机系统用 adb shell cat “/etc/permissions/platform.xml”查看

D:\AndroidStudioProjects>adb shell dumpsys deviceidle whitelist
system-excidle,com.android.providers.downloads,10020
system-excidle,com.android.shell,2000
system,com.android.providers.downloads,10020
system,com.android.shell,2000
system,test.demo.alarm.zui.com.alarmtest,10090
2.4.2 异常现象白名单 2

这里是没有任何设置白名单

D:\AndroidStudioProjects>adb shell dumpsys deviceidle whitelist
system-excidle,com.android.providers.downloads,10020
system-excidle,com.android.shell,2000
system,com.android.providers.downloads,10020
system,com.android.shell,2000
2.4.3 正常现象的白名单

这里是使用软件接口进行配置 mDeviceIdleService.addPowerSaveWhitelistApp(pkg);

D:\AndroidStudioProjects>adb shell dumpsys deviceidle whitelist
system-excidle,com.android.providers.downloads,10020
system-excidle,com.android.shell,2000
system,com.android.providers.downloads,10020
system,com.android.shell,2000
user,test.demo.alarm.zui.com.alarmtest,10090

3. 相关源码

根据 mDeviceIdleUserWhitelist 进行对应的 flags 的重新设置,这个mDeviceIdleUserWhitelist是使用接口进行加入

    private final IBinder mService = new IAlarmManager.Stub() {
        @Override
        public void set(String callingPackage,
                int type, long triggerAtTime, long windowLength, long interval, int flags,
                PendingIntent operation, IAlarmListener directReceiver, String listenerTag,
                WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock) {

            ...省略

            // If the caller is a core system component or on the user's whitelist, and not calling
            // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.
            // This means we will allow these alarms to go off as normal even while idle, with no
            // timing restrictions.
            } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
                    || callingUid == mSystemUiUid
                    || Arrays.binarySearch(mDeviceIdleUserWhitelist,
                            UserHandle.getAppId(callingUid)) >= 0)) {
                flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
                flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
            }

            setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
                    listenerTag, flags, workSource, alarmClock, callingUid, callingPackage);
        }

4.解决方案

使用软件接口设置doze模式白名单,解决即可,这样就可以开心的准时接收消息了

  • 核心接口 mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.sufadi.powersave.util;

import android.os.IDeviceIdleController;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArraySet;
import android.util.Log;


/**
 * Handles getting/changing the whitelist for the exceptions to battery saving features.
 */
public class PowerWhitelistBackend {

    private static final String TAG = "LavaPowerWhitelistBackend";

    private static final String DEVICE_IDLE_SERVICE = "deviceidle";

    private static final PowerWhitelistBackend INSTANCE = new PowerWhitelistBackend();

    private final IDeviceIdleController mDeviceIdleService;
    private final ArraySet<String> mWhitelistedApps = new ArraySet<>();
    private final ArraySet<String> mSysWhitelistedApps = new ArraySet<>();

    public PowerWhitelistBackend() {
        mDeviceIdleService = IDeviceIdleController.Stub.asInterface(
                ServiceManager.getService(DEVICE_IDLE_SERVICE));
        refreshList();
    }

    public int getWhitelistSize() {
        return mWhitelistedApps.size();
    }

    public boolean isSysWhitelisted(String pkg) {
        return mSysWhitelistedApps.contains(pkg);
    }

    public boolean isWhitelisted(String pkg) {
        return mWhitelistedApps.contains(pkg);
    }

    public void addApp(String pkg) {
        try {
            mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
            mWhitelistedApps.add(pkg);
        } catch (RemoteException e) {
            Log.w(TAG, "Unable to reach IDeviceIdleController", e);
        }
    }

    public void removeApp(String pkg) {
        try {
            mDeviceIdleService.removePowerSaveWhitelistApp(pkg);
            mWhitelistedApps.remove(pkg);
        } catch (RemoteException e) {
            Log.w(TAG, "Unable to reach IDeviceIdleController", e);
        }
    }

    private void refreshList() {
        mSysWhitelistedApps.clear();
        mWhitelistedApps.clear();
        try {
            String[] whitelistedApps = mDeviceIdleService.getFullPowerWhitelist();
            for (String app : whitelistedApps) {
                mWhitelistedApps.add(app);
            }
            String[] sysWhitelistedApps = mDeviceIdleService.getSystemPowerWhitelist();
            for (String app : sysWhitelistedApps) {
                mSysWhitelistedApps.add(app);
            }
        } catch (RemoteException e) {
            Log.w(TAG, "Unable to reach IDeviceIdleController", e);
        }
    }

    public static PowerWhitelistBackend getInstance() {
        return INSTANCE;
    }

}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

法迪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值