sdm660-common: introduce my simple battery management system

* settings page located in Settings -> Battery
* soc-based step charger, jeita thermal charger switch (also done something in kernel)
* add a mode for user always connected with chagrging cable to limit battery around 40% - 60%
* add a switch for user to limit max charge at around 80%

Signed-off-by: pix106 <sbordenave@gmail.com>
This commit is contained in:
Alcatraz323 2023-04-16 23:44:15 +08:00 committed by pix106
parent 5b660f6800
commit 9ab5292b0b
24 changed files with 774 additions and 1 deletions

View file

@ -10,12 +10,42 @@ android_app {
srcs: ["src/**/*.java"], srcs: ["src/**/*.java"],
resource_dirs: ["res"], resource_dirs: ["res"],
certificate: "platform", certificate: "platform",
init_rc: ["cloverparts.rc"],
platform_apis: true, platform_apis: true,
system_ext_specific: true, system_ext_specific: true,
privileged: true, privileged: true,
static_libs: [
"org.pixelexperience.settings.resources",
"androidx.cardview_cardview",
"androidx.preference_preference",
"androidx.appcompat_appcompat",
"androidx.core_core",
"SettingsLib"
],
optimize: { optimize: {
proguard_flags_files: ["proguard.flags"], proguard_flags_files: ["proguard.flags"],
}, },
required: [
"privapp-permissions_io.alcatraz.cloverparts",
"config-io.alcatraz.cloverparts",
],
}
prebuilt_etc {
name: "privapp-permissions_io.alcatraz.cloverparts",
sub_dir: "permissions",
src: "privapp-permissions_io.alcatraz.cloverparts.xml",
system_ext_specific: true,
filename_from_src: true,
}
prebuilt_etc {
name: "config-io.alcatraz.cloverparts",
sub_dir: "sysconfig",
src: "config-io.alcatraz.cloverparts.xml",
system_ext_specific: true,
filename_from_src: true,
} }

View file

@ -19,14 +19,32 @@
package="io.alcatraz.cloverparts" package="io.alcatraz.cloverparts"
android:sharedUserId="android.uid.system"> android:sharedUserId="android.uid.system">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
<application <application
android:directBootAware="true" android:directBootAware="true"
android:label="@string/app_name"> android:label="@string/app_name">
<activity
android:exported="true"
android:name=".BMSActivity"
android:label="@string/pref_battery_summary"
android:theme="@style/Theme.SubSettingsBase">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="com.android.settings.action.EXTRA_SETTINGS" />
</intent-filter>
<meta-data android:name="com.android.settings.category"
android:value="com.android.settings.category.ia.battery" />
</activity>
<service <service
android:enabled="true" android:enabled="true"
android:exported="true" android:exported="true"
android:name="io.alcatraz.cloverparts.VolumePanelTileService" android:name=".VolumePanelTileService"
android:icon="@drawable/ic_speaker_cleaner_icon" android:icon="@drawable/ic_speaker_cleaner_icon"
android:label="@string/volume_panel_trigger_title" android:label="@string/volume_panel_trigger_title"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
@ -36,5 +54,15 @@
</intent-filter> </intent-filter>
</service> </service>
<service
android:name=".BMSService" />
<receiver android:name=".BootReceiver"
android:exported="true">
<intent-filter android:priority="999">
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
</application> </application>
</manifest> </manifest>

View file

@ -0,0 +1,24 @@
#
# Copyright (c) 2023, Alcatraz323 <alcatraz32323@gmail.com>
#
# 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.
#
on boot
chown system system /sys/class/power_supply/battery/step_charging_enabled
chown system system /sys/class/power_supply/battery/sw_jeita_enabled
chown system system /sys/class/power_supply/battery/input_suspend
chmod 0660 /sys/class/power_supply/battery/step_charging_enabled
chmod 0660 /sys/class/power_supply/battery/sw_jeita_enabled
chmod 0660 /sys/class/power_supply/battery/input_suspend

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!--
/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-->
<config>
<allow-in-power-save package="io.alcatraz.cloverparts" />
<hidden-api-whitelisted-app package="io.alcatraz.cloverparts" />
</config>

View file

@ -0,0 +1,21 @@
<!--
~ Copyright (c) 2023, Alcatraz323 <alcatraz32323@gmail.com>
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
-->
<permissions>
<privapp-permissions package="io.alcatraz.cloverparts">
<permission name="android.permission.RECEIVE_BOOT_COMPLETED" />
<permission name="android.permission.WRITE_SETTINGS" />
<permission name="android.permission.WRITE_SECURE_SETTINGS" />
<permission name="android.permission.BATTERY_STATS" />
</privapp-permissions>
</permissions>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#000"
android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4zM11,20v-5.5H9L13,7v5.5h2L11,20z"/>
</vector>

View file

@ -0,0 +1,6 @@
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#000" android:pathData="M14.17,13.71l1.4,-2.42c0.09,-0.15 0.05,-0.34 -0.08,-0.45l-1.48,-1.16c0.03,-0.22 0.05,-0.45 0.05,-0.68s-0.02,-0.46 -0.05,-0.69l1.48,-1.16c0.13,-0.11 0.17,-0.3 0.08,-0.45l-1.4,-2.42c-0.09,-0.15 -0.27,-0.21 -0.43,-0.15L12,4.83c-0.36,-0.28 -0.75,-0.51 -1.18,-0.69l-0.26,-1.85C10.53,2.13 10.38,2 10.21,2h-2.8C7.24,2 7.09,2.13 7.06,2.3L6.8,4.15C6.38,4.33 5.98,4.56 5.62,4.84l-1.74,-0.7c-0.16,-0.06 -0.34,0 -0.43,0.15l-1.4,2.42C1.96,6.86 2,7.05 2.13,7.16l1.48,1.16C3.58,8.54 3.56,8.77 3.56,9s0.02,0.46 0.05,0.69l-1.48,1.16C2,10.96 1.96,11.15 2.05,11.3l1.4,2.42c0.09,0.15 0.27,0.21 0.43,0.15l1.74,-0.7c0.36,0.28 0.75,0.51 1.18,0.69l0.26,1.85C7.09,15.87 7.24,16 7.41,16h2.8c0.17,0 0.32,-0.13 0.35,-0.3l0.26,-1.85c0.42,-0.18 0.82,-0.41 1.18,-0.69l1.74,0.7C13.9,13.92 14.08,13.86 14.17,13.71zM8.81,11c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2C10.81,10.1 9.91,11 8.81,11z"/>
<path android:fillColor="#000" android:pathData="M21.92,18.67l-0.96,-0.74c0.02,-0.14 0.04,-0.29 0.04,-0.44c0,-0.15 -0.01,-0.3 -0.04,-0.44l0.95,-0.74c0.08,-0.07 0.11,-0.19 0.05,-0.29l-0.9,-1.55c-0.05,-0.1 -0.17,-0.13 -0.28,-0.1l-1.11,0.45c-0.23,-0.18 -0.48,-0.33 -0.76,-0.44l-0.17,-1.18C18.73,13.08 18.63,13 18.53,13h-1.79c-0.11,0 -0.21,0.08 -0.22,0.19l-0.17,1.18c-0.27,0.12 -0.53,0.26 -0.76,0.44l-1.11,-0.45c-0.1,-0.04 -0.22,0 -0.28,0.1l-0.9,1.55c-0.05,0.1 -0.04,0.22 0.05,0.29l0.95,0.74c-0.02,0.14 -0.03,0.29 -0.03,0.44c0,0.15 0.01,0.3 0.03,0.44l-0.95,0.74c-0.08,0.07 -0.11,0.19 -0.05,0.29l0.9,1.55c0.05,0.1 0.17,0.13 0.28,0.1l1.11,-0.45c0.23,0.18 0.48,0.33 0.76,0.44l0.17,1.18c0.02,0.11 0.11,0.19 0.22,0.19h1.79c0.11,0 0.21,-0.08 0.22,-0.19l0.17,-1.18c0.27,-0.12 0.53,-0.26 0.75,-0.44l1.12,0.45c0.1,0.04 0.22,0 0.28,-0.1l0.9,-1.55C22.03,18.86 22,18.74 21.92,18.67zM17.63,18.83c-0.74,0 -1.35,-0.6 -1.35,-1.35s0.6,-1.35 1.35,-1.35s1.35,0.6 1.35,1.35S18.37,18.83 17.63,18.83z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#000" android:pathData="M13.5,0.67s0.74,2.65 0.74,4.8c0,2.06 -1.35,3.73 -3.41,3.73 -2.07,0 -3.63,-1.67 -3.63,-3.73l0.03,-0.36C5.21,7.51 4,10.62 4,14c0,4.42 3.58,8 8,8s8,-3.58 8,-8C20,8.61 17.41,3.8 13.5,0.67zM11.71,19c-1.78,0 -3.22,-1.4 -3.22,-3.14 0,-1.62 1.05,-2.76 2.81,-3.12 1.77,-0.36 3.6,-1.21 4.62,-2.58 0.39,1.29 0.59,2.65 0.59,4.04 0,2.65 -2.15,4.8 -4.8,4.8z"/>
</vector>

View file

@ -0,0 +1,9 @@
<!-- drawable/information_outline.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:attr/colorControlNormal">
<path android:fillColor="#666" android:pathData="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z" />
</vector>

View file

@ -17,4 +17,19 @@
<resources> <resources>
<string name="app_name">平板扩展</string> <string name="app_name">平板扩展</string>
<string name="volume_panel_trigger_title">音量面板</string> <string name="volume_panel_trigger_title">音量面板</string>
<string name="category_battery">电源</string>
<string name="pref_battery">电池</string>
<string name="pref_battery_summary">电池管理系统</string>
<string name="bms_category_step_charging">阶梯充电</string>
<string name="bms_step_charging_switch">开启阶梯充电</string>
<string name="bms_step_charging_switch_summary">自动根据电池电压以及温度调整充电速度以保护电池</string>
<string name="bms_category_charge_limiter">充电限制器</string>
<string name="bms_charge_limiter_notice">注意</string>
<string name="bms_charge_limiter_description">如果你需要打开任何充电限制器选项,建议关闭“显示 - 连接或断开电源时唤醒”,不然你可能会在充电控制期间触发一些意外唤醒</string>
<string name="bms_always_connected_mode">持续充电模式</string>
<string name="bms_always_connected_mode_summary" formatted="false">将电量限制在40%-60%以保护电池</string>
<string name="bms_limit_to_eighty" formatted="false">限制最高电量为80%左右</string>
<string name="bms_limit_to_eighty_summary" formatted="false">电量在80%左右时停止充电以保护电池(持续模式优先)</string>
</resources> </resources>

View file

@ -17,4 +17,19 @@
<resources> <resources>
<string name="app_name">Clover Parts</string> <string name="app_name">Clover Parts</string>
<string name="volume_panel_trigger_title">Volume Panel</string> <string name="volume_panel_trigger_title">Volume Panel</string>
<string name="category_battery">Power Supply</string>
<string name="pref_battery">Battery</string>
<string name="pref_battery_summary">Battery management system</string>
<string name="bms_category_step_charging">Step charging</string>
<string name="bms_step_charging_switch">Enable step charging</string>
<string name="bms_step_charging_switch_summary">Automatically tune charge speed depends on battery data(voltage/temperature) to protect battery</string>
<string name="bms_category_charge_limiter">Charge limiter</string>
<string name="bms_charge_limiter_notice">Notice</string>
<string name="bms_charge_limiter_description">If you turn on any charge limiter option, you would better turn off the “Display - Wake on plug” or you may get some unexpected wakeups during the charge control</string>
<string name="bms_always_connected_mode">Always connected mode</string>
<string name="bms_always_connected_mode_summary" formatted="false">Limit the battery percent between 40% and 60% to protect battery</string>
<string name="bms_limit_to_eighty" formatted="false">Limit to around 80%</string>
<string name="bms_limit_to_eighty_summary" formatted="false">Suspend charging when the percent is around 80% to protect battery(Always connected mode will make this never happen)</string>
</resources> </resources>

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2023, Alcatraz323 <alcatraz32323@gmail.com>
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="cat_bms_step"
android:title="@string/bms_category_step_charging">
<SwitchPreference
android:icon="@drawable/ic_baseline_miscellaneous_services_24"
android:key="bms_step_charging_switch"
android:summary="@string/bms_step_charging_switch_summary"
android:title="@string/bms_step_charging_switch" />
</PreferenceCategory>
<PreferenceCategory
android:key="cat_charge_limiter"
android:title="@string/bms_category_charge_limiter">
<Preference
android:icon="@drawable/information_outline"
android:key="cat_charge_limiter_notice"
android:selectable="false"
android:summary="@string/bms_charge_limiter_description"
android:title="@string/bms_charge_limiter_notice" />
<SwitchPreference
android:icon="@drawable/ic_baseline_whatshot_24"
android:key="bms_always_connected_mode"
android:summary="@string/bms_always_connected_mode_summary"
android:title="@string/bms_always_connected_mode" />
<SwitchPreference
android:icon="@drawable/ic_baseline_battery_charging_full_24"
android:key="bms_limit_to_eighty"
android:summary="@string/bms_limit_to_eighty_summary"
android:title="@string/bms_limit_to_eighty" />
</PreferenceCategory>
</PreferenceScreen>

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2023, Alcatraz323 <alcatraz32323@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
package io.alcatraz.cloverparts;
import android.app.Fragment;
import android.os.Bundle;
import android.view.MenuItem;
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
import com.android.settingslib.widget.R;
public class BMSActivity extends CollapsingToolbarBaseActivity {
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
Fragment fragment = getFragmentManager().findFragmentById(R.id.content_frame);
BMSFragment bmsFragment;
if (fragment == null) {
bmsFragment = new BMSFragment();
getFragmentManager().beginTransaction().add(R.id.content_frame, bmsFragment).commit();
}
}
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2023, Alcatraz323 <alcatraz32323@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
package io.alcatraz.cloverparts;
import android.os.Bundle;
import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragment;
import androidx.preference.SwitchPreference;
import static io.alcatraz.cloverparts.Constants.BMS_STEP_CHG_SWITCH;
import static io.alcatraz.cloverparts.Constants.BMS_ALWAYS_CONNECTED_MODE;
import static io.alcatraz.cloverparts.Constants.BMS_LIMIT_TO_EIGHTY;
public class BMSFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener {
private SwitchPreference mStepChargingSwitch;
@Override
public void onCreatePreferences(Bundle bundle, String key) {
addPreferencesFromResource(R.xml.bms_settings);
findPreferences();
bindListeners();
updateSwitches();
}
@Override
public boolean onPreferenceChange(Preference preference, Object o) {
switch (preference.getKey()) {
case BMS_STEP_CHG_SWITCH:
boolean enabled = (boolean) o;
ShellUtils.execCommand("echo " + (enabled ? "1" : "0") + " > /sys/class/power_supply/battery/step_charging_enabled", false);
ShellUtils.execCommand("echo " + (enabled ? "1" : "0") + " > /sys/class/power_supply/battery/sw_jeita_enabled", false);
break;
}
return true;
}
private void findPreferences() {
mStepChargingSwitch = findPreference(BMS_STEP_CHG_SWITCH);
}
private void bindListeners() {
mStepChargingSwitch.setOnPreferenceChangeListener(this);
}
private void updateSwitches() {
ShellUtils.CommandResult result = ShellUtils.execCommand("cat /sys/class/power_supply/battery/step_charging_enabled", false);
mStepChargingSwitch.setChecked(result.responseMsg.contains("1"));
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2023, Alcatraz323 <alcatraz32323@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
package io.alcatraz.cloverparts;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.BatteryManager;
import android.os.Bundle;
import static io.alcatraz.cloverparts.Constants.BMS_ALWAYS_CONNECTED_MODE;
import static io.alcatraz.cloverparts.Constants.BMS_LIMIT_TO_EIGHTY;
public class BMSReceiver extends BroadcastReceiver {
boolean alwaysConnected, limitToEighty;
@Override
public void onReceive(Context context, Intent intent) {
if (context == null) {
return;
}
SharedPreferenceUtil sharedPreferenceUtil = SharedPreferenceUtil.getInstance();
alwaysConnected = (boolean) sharedPreferenceUtil.get(context, BMS_ALWAYS_CONNECTED_MODE, false);
limitToEighty = (boolean) sharedPreferenceUtil.get(context, BMS_LIMIT_TO_EIGHTY, false);
processBatteryChange(intent);
}
private void resetSuspendState() {
ShellUtils.CommandResult result = ShellUtils.execCommand("cat /sys/class/power_supply/battery/input_suspend", false);
if(result.responseMsg.contains("1"))
ShellUtils.execCommand("echo 0 > /sys/class/power_supply/battery/input_suspend", false);
}
private void suspendCharger() {
ShellUtils.CommandResult result = ShellUtils.execCommand("cat /sys/class/power_supply/battery/input_suspend", false);
if(result.responseMsg.contains("0"))
ShellUtils.execCommand("echo 1 > /sys/class/power_supply/battery/input_suspend", false);
}
private synchronized void processBatteryChange(Intent intent) {
Bundle bundle = intent.getExtras();
int current = bundle.getInt("level");
if(alwaysConnected) { // Always connected overrides limit to 80
if(current < 40)
resetSuspendState();
else if(current > 60)
suspendCharger();
} else if(limitToEighty) {
if(current < 78)
resetSuspendState();
else if(current > 80)
suspendCharger();
} else {
resetSuspendState();
}
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2021, Alcatraz323 <alcatraz32323@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
package io.alcatraz.cloverparts;
import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import androidx.annotation.Nullable;
public class BMSService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
registerReceiver(new BMSReceiver(), intentFilter);
return super.onStartCommand(intent, flags, startId);
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2023, Alcatraz323 <alcatraz32323@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
package io.alcatraz.cloverparts;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (context == null) {
return;
}
context.startService(new Intent(context, BMSService.class));
SharedPreferenceUtil sharedPreferenceUtil = SharedPreferenceUtil.getInstance();
boolean stepChargingManualOverride = (boolean) sharedPreferenceUtil.get(context, "bms_step_charging_switch",
true);
ShellUtils.execCommand("echo " + (stepChargingManualOverride ? "1" : "0") + " > /sys/class/power_supply/battery/step_charging_enabled", false);
ShellUtils.execCommand("echo " + (stepChargingManualOverride ? "1" : "0") + " > /sys/class/power_supply/battery/sw_jeita_enabled", false);
}
}

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2021, Alcatraz323 <alcatraz32323@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
package io.alcatraz.cloverparts;
public class Constants {
public static final String BMS_STEP_CHG_SWITCH = "bms_step_charging_switch";
public static final String BMS_ALWAYS_CONNECTED_MODE = "bms_always_connected_mode";
public static final String BMS_LIMIT_TO_EIGHTY = "bms_limit_to_eighty";
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2021, Alcatraz323 <alcatraz32323@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
package io.alcatraz.cloverparts;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.Nullable;
public final class SharedPreferenceUtil {
private static final String FILE_NAME = "io.alcatraz.cloverparts_preferences";
private static SharedPreferenceUtil mInstance;
public static SharedPreferenceUtil getInstance() {
if (mInstance == null) {
synchronized (SharedPreferenceUtil.class) {
if (mInstance == null) {
mInstance = new SharedPreferenceUtil();
}
}
}
return mInstance;
}
public boolean put(Context context, String key, Object value) {
String type = value.getClass().getSimpleName();
SharedPreferences sharedPreferences = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
if ("Integer".equals(type)) {
editor.putInt(key, (Integer) value);
} else if ("Boolean".equals(type)) {
editor.putBoolean(key, (Boolean) value);
} else if ("Float".equals(type)) {
editor.putFloat(key, (Float) value);
} else if ("Long".equals(type)) {
editor.putLong(key, (Long) value);
} else if ("String".equals(type)) {
editor.putString(key, (String) value);
}
editor.apply();
return false;
}
@Nullable
public Object get(Context context, String key, Object defValue) {
SharedPreferences sharedPreferences = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
String type = defValue.getClass().getSimpleName();
if ("Integer".equals(type)) {
return sharedPreferences.getInt(key, (Integer) defValue);
} else if ("Boolean".equals(type)) {
return sharedPreferences.getBoolean(key, (Boolean) defValue);
} else if ("Float".equals(type)) {
return sharedPreferences.getFloat(key, (Float) defValue);
} else if ("Long".equals(type)) {
return sharedPreferences.getLong(key, (Long) defValue);
} else if ("String".equals(type)) {
return sharedPreferences.getString(key, (String) defValue);
}
return null;
}
}

View file

@ -0,0 +1,148 @@
/*
* Copyright (c) 2021, Alcatraz323 <alcatraz32323@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*/
package io.alcatraz.cloverparts;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
public class ShellUtils {
public static final String ERR_EMPTY_COMMAND = "Command can't be null";
/**
* check whether has root permission
*/
public static boolean hasRootPermission() {
return execCommand("echo root", true, false).result == 0;
}
public static CommandResult execCommand(String command, boolean isRoot) {
return execCommand(new String[]{command}, isRoot, true);
}
public static CommandResult execCommand(String command, boolean isRoot, boolean isNeedResultMsg) {
return execCommand(new String[]{command}, isRoot, isNeedResultMsg);
}
public static CommandResult execCommand(List<String> commands, boolean isRoot, boolean isNeedResultMsg) {
return execCommand(commands == null ? null : commands.toArray(new String[]{}), isRoot, isNeedResultMsg);
}
/**
* execute shell commands
* {@link CommandResult#result} is -1, there maybe some excepiton.
*
* @param commands command array
* @param isRoot whether need to run with root
* @param needResponse whether need result msg
*/
public static CommandResult execCommand(String[] commands, boolean isRoot, boolean needResponse) {
int result = -1;
if (commands == null || commands.length == 0) {
return new CommandResult(result, null, ERR_EMPTY_COMMAND);
}
Process process = null;
BufferedReader successResult = null;
BufferedReader errorResult = null;
StringBuilder successMsg = null;
StringBuilder errorMsg = null;
DataOutputStream os = null;
try {
process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH);
os = new DataOutputStream(process.getOutputStream());
for (String command : commands) {
if (command == null) {
continue;
}
// donnot use os.writeBytes(commmand), avoid chinese charset error
os.write(command.getBytes());
os.writeBytes(COMMAND_LINE_END);
os.flush();
}
os.writeBytes(COMMAND_EXIT);
os.flush();
result = process.waitFor();
if (needResponse) {
successMsg = new StringBuilder();
errorMsg = new StringBuilder();
successResult = new BufferedReader(new InputStreamReader(process.getInputStream()));
errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String s;
while ((s = successResult.readLine()) != null) {
successMsg.append(s).append("\n");
}
while ((s = errorResult.readLine()) != null) {
errorMsg.append(s);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (errorResult != null) {
errorResult.close();
}
if (successResult != null) {
successResult.close();
}
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (process != null) {
process.destroy();
}
}
}
return new CommandResult(result, successMsg == null ? "" : successMsg.toString(), errorMsg == null ? ""
: errorMsg.toString());
}
public static class CommandResult {
public int result;
public String responseMsg;
public String errorMsg;
public CommandResult(int result) {
this.result = result;
}
public CommandResult(int result, String responseMsg, String errorMsg) {
this.result = result;
this.responseMsg = responseMsg;
this.errorMsg = errorMsg;
}
}
public static final String COMMAND_SU = "su";
public static final String COMMAND_SH = "sh";
public static final String COMMAND_EXIT = "exit\n";
public static final String COMMAND_LINE_END = "\n";
}

View file

@ -0,0 +1,24 @@
typeattribute cloverparts_app mlstrustedsubject;
app_domain(cloverparts_app)
allow cloverparts_app activity_service:service_manager find;
allow cloverparts_app activity_task_service:service_manager find;
allow cloverparts_app audio_service:service_manager find;
allow cloverparts_app autofill_service:service_manager find;
allow cloverparts_app content_capture_service:service_manager find;
allow cloverparts_app gpu_service:service_manager find;
allow cloverparts_app hint_service:service_manager find;
allow cloverparts_app surfaceflinger_service:service_manager find;
allow cloverparts_app game_service:service_manager find;
allow cloverparts_app textservices_service:service_manager find;
allow cloverparts_app netstats_service:service_manager find;
allow cloverparts_app voiceinteraction_service:service_manager find;
allow cloverparts_app batterystats_service:service_manager find;
allow cloverparts_app batteryproperties_service:service_manager find;
# ro.input.resampling, viewroot.profile_rendering
dontaudit cloverparts_app default_prop:file read;
allow cloverparts_app system_app_data_file:dir { create rw_dir_perms search open setattr };
allow cloverparts_app system_app_data_file:file { create rw_file_perms rename setattr unlink };

View file

@ -0,0 +1 @@
user=system seinfo=platform name=io.alcatraz.cloverparts domain=cloverparts_app type=system_app_data_file

View file

@ -0,0 +1 @@
type cloverparts_app, domain;

2
sepolicy/vendor/cloverparts_app.te vendored Normal file
View file

@ -0,0 +1,2 @@
allow cloverparts_app sysfs_battery_supply:file rw_file_perms;
allow cloverparts_app sysfs_battery_supply:dir search;