【问题标题】:isPowerSaveMode() always returns false for Huawei devices对于华为设备,isPowerSaveMode() 始终返回 false
【发布时间】:2018-01-26 08:42:56
【问题描述】:

我目前正在实施一项功能,要求用户忽略应用程序的电池优化。这样做的原因是,不幸的是,应用程序的主要功能受到省电模式的严重影响。

为了实现我的目标,我通过创建Intent 并将Action 设置为ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 来提示用户。

虽然在触发Intent之前,我都会检查isPowerSaveMode()isIgnoringBatteryOptimizations(),以确保在未启用省电模式时不会提示用户;这是该功能的要求。我这样做的方式是:

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
boolean isPowerSaveMode = pm.isPowerSaveMode(); // always returns false for Huawei devices

这适用于大多数设备,但对于华为设备,isPowerSaveMode() 始终返回 false。因此,由于先决条件失败,因此永远不会显示提示。

有没有其他人可能遇到过这个问题?如果有,你是怎么解决的?

请注意,Xamarin.Android SDK 中也存在同样的问题。

【问题讨论】:

  • 有一个变通方法,已经描述here
  • @Ch4t4r 感谢您的链接。它虽然不能解决我遇到的问题。
  • 确定与手机安装的安卓版本无关?如果是的话,你能试试这段代码吗? PowerManager powerManager = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && powerManager.isPowerSaveMode()) { }
  • @hugorgor 我确信它与操作系统版本无关。我尝试了几款华为设备,包括运行 Android 6 和 7 的设备。在这两种情况下,即使启用了isPowerSaveMode(),设备也会在请求isPowerSaveMode() 时返回false。我的想法是,与其他制造商(例如三星)相比,该标志的暴露方式不同(工作正常)。但这只是一个理论。
  • 也许你应该试试 Xamarin 测试云

标签: android xamarin.android powermanager


【解决方案1】:

部分中文ROM如HuaweiXiaomi没有实现标准的省电模式查询API。但是像其他系统设置一样,当用户打开/关闭省电模式时,状态标志将保存到数据库中。

所以我们可以利用这个状态标志来解决兼容性问题。另外,在切换省电模式时,系统会发送一个特定的意图,我们可以监听这个意图动作来监控省电模式的变化。

下面是HuaweiXiaomi 设备的详细 kotlin 代码实现。

object PowerManagerCompat {

    private const val TAG = "PowerManagerCompat"

    interface PowerSaveModeChangeListener {
        /**
         * will be called when power save mode change, new state can be query via [PowerManagerCompat.isPowerSaveMode]
         */
        fun onPowerSaveModeChanged()
    }

    private val POWER_SAVE_MODE_VALUES = mapOf(
            "HUAWEI" to 4,
            "XIAOMI" to 1
    )

    private val POWER_SAVE_MODE_SETTING_NAMES = arrayOf(
            "SmartModeStatus", // huawei setting name
            "POWER_SAVE_MODE_OPEN" // xiaomi setting name
    )

    private val POWER_SAVE_MODE_CHANGE_ACTIONS = arrayOf(
            "huawei.intent.action.POWER_MODE_CHANGED_ACTION",
            "miui.intent.action.POWER_SAVE_MODE_CHANGED"
    )

    private const val monitorViaBroadcast = true

    /**
     * Monitor power save mode change, only support following devices
     * * Xiaomi
     * * Huawei
     */
    fun monitorPowerSaveModeChange(context: Context, powerSaveModeChangeListener: PowerSaveModeChangeListener) {
        if (Build.MANUFACTURER.toUpperCase(Locale.getDefault()) !in POWER_SAVE_MODE_VALUES.keys) {
            Log.w(TAG, "monitorPowerSaveModeChange: doesn't know how to monitor power save mode change for ${Build.MANUFACTURER}")
        }
        if (monitorViaBroadcast) {
            context.registerReceiver(object : BroadcastReceiver() {
                override fun onReceive(context: Context?, intent: Intent?) {
                    powerSaveModeChangeListener.onPowerSaveModeChanged()
                }
            }, IntentFilter().also {
                for (a in POWER_SAVE_MODE_CHANGE_ACTIONS) {
                    it.addAction(a)
                }
            })
        } else {
            val contentObserver = object : ContentObserver(null) {
                override fun onChange(selfChange: Boolean) {
                    super.onChange(selfChange)
                    powerSaveModeChangeListener.onPowerSaveModeChanged()
                }
            }
            for (name in POWER_SAVE_MODE_SETTING_NAMES) {
                context.contentResolver.registerContentObserver(
                        Uri.parse("content://settings/system/${name}"), false, contentObserver)
            }
        }
    }

    /**
     * Check the system is currently in power save mode
     * @see [PowerManager.isPowerSaveMode]
     */
    fun isPowerSaveMode(context: Context): Boolean {
        if (Build.MANUFACTURER.toUpperCase(Locale.getDefault()) in POWER_SAVE_MODE_VALUES.keys) {
            return isPowerSaveModeCompat(context)
        }
        val powerManager = context.getSystemService(Context.POWER_SERVICE) as? PowerManager
        return powerManager?.isPowerSaveMode ?: false
    }

    private fun isPowerSaveModeCompat(context: Context): Boolean {
        for (name in POWER_SAVE_MODE_SETTING_NAMES) {
            val mode = Settings.System.getInt(context.contentResolver, name, -1)
            if (mode != -1) {
                return POWER_SAVE_MODE_VALUES[Build.MANUFACTURER.toUpperCase(Locale.getDefault())] == mode
            }
        }
        return false
    }
}

【讨论】:

    【解决方案2】:

    每个 oem 都会修改 SDK 以满足他们的需求。华为设备不使用默认的节电功能,而是使用名为“受保护的应用程序”的东西。受保护的应用程序是一组即使在屏幕关闭时也允许运行的应用程序。所以这就是它总是返回 false 的原因。最好向受保护的应用程序屏幕发出意图,但无法知道您的应用程序是否已添加到受保护的应用程序列表中。 What is protected apps ?

    【讨论】:

      【解决方案3】:
      • 我找到了一种通过向IntentFilter 添加自定义操作来手动请求当前华为电源模式状态并接收更改事件的方法:

      (注意仅在Huawei P20 Lite (ANE-LX3) @ EMUI 8.0.0上测试)

      // Manually request Power Save Mode:
      public Boolean isPowerSaveMode(Context context) {
          if (Build.MANUFACTURER.equalsIgnoreCase("Huawei")) {
              return isPowerSaveModeHuawei(context);
          } else {
              return isPowerSaveModeAndroid(context);
          }
      }
      
      @TargetApi(21)
      private Boolean isPowerSaveModeAndroid(Context context) {
          boolean isPowerSaveMode = false;
          if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
              PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
              if (pm != null) isPowerSaveMode = pm.isPowerSaveMode();
          }
          return isPowerSaveMode;
      }
      
      private Boolean isPowerSaveModeHuawei(Context context) {
          try {
              int value = android.provider.Settings.System.getInt(context.getContentResolver(), "SmartModeStatus");
              return (value == 4);
          } catch (Settings.SettingNotFoundException e) {
              // Setting not found?  Return standard android mechanism and hope for the best...
              return isPowerSaveModeAndroid(context);
          }
      }
      
      // Listening for changes in Power Save Mode
      public void startMonitoringPowerSaveChanges(Context context) {
          if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
              if (mPowerSaveChangeReceiver != null) {
                  return;
              }
              // Register for PowerSaver change updates.
              mPowerSaveChangeReceiver = new PowerSaveChangeReceiver();
      
              // Registering the receiver
              IntentFilter filter = new IntentFilter();
      
              filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
              // Add custom huawei action
              filter.addAction("huawei.intent.action.POWER_MODE_CHANGED_ACTION");
      
              if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                  filter.addAction(android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
              }
              context.registerReceiver(mPowerSaveChangeReceiver, filter);
          }
      }
      
      @TargetApi(21)
      class PowerSaveChangeReceiver extends BroadcastReceiver {
          public void onReceive(Context context, Intent intent) {
              boolean isPowerSaveMode = false;
      
              // Oh, Huawei...why don't you play by the same rules as everyone else?
              if (intent.getAction().equals("huawei.intent.action.POWER_MODE_CHANGED_ACTION")) {
                  Bundle extras = intent.getExtras();
                  if ((extras != null) && extras.containsKey("state")) {
                      int state = intent.getExtras().getInt("state");
                      isPowerSaveMode = (state == 1);  // ON=1; OFF=2
                  }
              } else {
                  PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
                  isPowerSaveMode = pm.isPowerSaveMode();
              }
              Log.d("MyTag", "[powersavechange] isPowerSaveMode? " + isPowerSaveMode);
          }
      }
      

      【讨论】:

        【解决方案4】:

        在实施手持设备和可穿戴设备时,我遇到了同样的新问题。 我找到的唯一解决方案是禁用所有应用程序的省电模式。 我建议在为所有应用程序禁用此类模式后检测您的方法的结果。此错误仅出现在华为上。糟糕的供应商。

        【讨论】:

          【解决方案5】:
          private void isPowerSaveModeHuaweiXiaomi(){
            if (Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")) {
              try {
                 int value = android.provider.Settings.System.getInt(getContext().getContentResolver(), "POWER_SAVE_MODE_OPEN");
          
                      } catch (Settings.SettingNotFoundException e) {
                          Log.d("Valor modo bateria:", "Error");
                      }
                  }else if (Build.MANUFACTURER.equalsIgnoreCase("Huawei")){
                      try {
                          int value = android.provider.Settings.System.getInt(getContext().getContentResolver(), "SmartModeStatus");
          
                      } catch (Settings.SettingNotFoundException e) {
                          Log.d("Valor modo bateria:", "Error");
                      }
                  }
              }
          

          【讨论】:

            【解决方案6】:

            例如,在华为 P30 lite 等新的华为设备上,该问题的解决方案目前尚不清楚(27.12.2021)。使用键“SmartModeStatus”调用 getInt 将引发键未知异常。因此,我们能做的最好的就是以下。

            private string HuaweiPowerSaveModeSettingsName = "SmartModeStatus";
            private int HuaweiPowerSaveModeValue = 4;
            
            public bool IsBatterySaverEnabled
                => Build.Manufacturer?.ToUpper() == "HUAWEI" ? GetIsBatterySaverEnabledHuawei() : GetIsBatterySaverEnabledAllDevicesExceptHuawei();
            
            private bool GetIsBatterySaverEnabledAllDevicesExceptHuawei()
            {
                return PowerManager.FromContext(Application.Context)?.IsPowerSaveMode ?? false;
            }
            
            private bool GetIsBatterySaverEnabledHuawei()
            {
                try
                {
                    var mode = Settings.System.GetInt(Application.Context.ContentResolver, HuaweiPowerSaveModeSettingsName);
                    return HuaweiPowerSaveModeValue == mode;
                } catch (Exception e)
                {
                    return GetIsBatterySaverEnabledAllDevicesExceptHuawei();
                }
            }
            

            【讨论】:

              【解决方案7】:

              对于华为 vtr-al00,SmartModeStatus 1 可以是超级省电模式或普通模式。我已经使用反射来处理这个问题。

                  final int _HX =  Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")?2
                          :Build.MANUFACTURER.equalsIgnoreCase("Huawei")?1
                          :0;
                  // “No Kotlin”
                  private boolean isPowerSaveModeCompat(){
                      if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
                              && powerManager.isPowerSaveMode()) { // hopefully...
                          return true;
                      }
                      if (_HX==0) {
                          return false;
                      }
                      else if (_HX==1) {
                          try {
                              int value = Settings.System.getInt(getContentResolver(), "SmartModeStatus");
                              CMN.debug("isPowerSaveModeCompat::huawei::"+value);
                              // value 4==Save Mode; 1==Ultra Save Mode==Normal Mode;
                              //  ( tested on my huawei vtr-al00 )
                              if(value==4) {
                                  return true;
                              }
                              if(value==1) {
                                  // what if Ultra save mode???
                                  // https://github.com/huaweigerrit
                                  // https://github.com/SivanLiu/HwFrameWorkSource
                                  
                                  // https://stackoverflow.com/questions/2641111/where-is-android-os-systemproperties
              //                  Class sysProp= Class.forName("android.os.SystemProperties");
              //                  Method sysProp_getBool = sysProp.getMethod("getBoolean", new Class[]{String.class, boolean.class});
              //                  Object[] parms = new Object[]{"sys.super_power_save", false};
              //                  CMN.debug("huawei::UltraPowerSave::", sysProp_getBool.invoke(null, parms));
              //                  CMN.debug("huawei::UltraPowerSave::", getSystemProperty("sys.super_power_save"));
                                  return "true".equals(getSystemProperty("sys.super_power_save"));
                              }
                          } catch (Exception e) {
                              CMN.debug(e);
                          }
                      }
                      else if (_HX==2){
                          try {
                              int value = Settings.System.getInt(getContentResolver(), "POWER_SAVE_MODE_OPEN");
                              CMN.debug("isPowerSaveModeCompat::xiaomi::"+value);
                              // dont have xiaomi. not tested.
                              return value==1;
                          } catch (Exception e) {
                              CMN.debug(e);
                          }
                      }
                      // else if...
                      return false;
                  }
                  
                  // https://stackoverflow.com/questions/9937099/how-to-get-the-build-prop-values
                  public String getSystemProperty(String key) {
                      String value = null;
                      
                      try {
                          value = (String) Class.forName("android.os.SystemProperties")
                                  .getMethod("get", String.class).invoke(null, key);
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                      
                      return value;
                  }
              

              Java 只是更短的 kotlin,即使有这么多 cmets 和脏测试! :)

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-11-30
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-09-14
                相关资源
                最近更新 更多