Android8.0充电图标不显示

1.充电图标不显示,问题分析步骤
a)找到充电图标的UI,frameworks/base/packages/SystemUI/res/layout/system_icons.xml
中找到

1
2
3
4
<com.android.systemui.BatteryMeterView android:id="@+id/battery"
android:layout_height="match_parent"
android:layout_width="wrap_content"
/>

确定充电图标UI文件,BatteryMeterView.java
frameworks/base/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
中充电图标绘制在BatteryMeterDrawableBase.java,查看
frameworks/base/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java可以看到充电的闪电图标的绘制过程。
在BatteryMeterView.java中可以看到充电图标的显示与否是由下面决定的:

1
2
3
4
5
6
7
8
9
10
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
mDrawable.setBatteryLevel(level);
mDrawable.setCharging(pluggedIn);//pluggedIn为true,显示闪电图标,为false则不显示
mLevel = level;
updatePercentText();
setContentDescription(
getContext().getString(charging ? R.string.accessibility_battery_level_charging
: R.string.accessibility_battery_level, level));
}

可以看到是由pluggedIn来决定是否显示,这里有个疑问,为何不用charging的值来判断,而要用pluggedIn的值来判断。原因在下面这个代码,
onBatteryLevelChanged这个函数,是在
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java里面addCallback回调过去的。
mCharging的值是由 mCharged和 BATTERY_STATUS_CHARGING两个值一起判断的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public void onReceive(final Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
if (mTestmode && !intent.getBooleanExtra("testmode", false)) return;
mHasReceivedBattery = true;
mLevel = (int)(100f
* intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
/ intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;

final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;

fireBatteryLevelChanged();
}

为了查看手机中当前的pluggedIn和charging这两个值的状态,从
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java文件中看到了一个Dump 的log打印,刚好打印了这几个值的状态,如下:

1
2
3
4
5
6
7
8
9
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("BatteryController state:");
pw.print(" mLevel="); pw.println(mLevel);
pw.print(" mPluggedIn="); pw.println(mPluggedIn);
pw.print(" mCharging="); pw.println(mCharging);
pw.print(" mCharged="); pw.println(mCharged);
pw.print(" mPowerSave="); pw.println(mPowerSave);
}

所以,不需要另外打任何的log,直接连接手机,使用adb shell bugreport > bugreport.txt
抓出所有的dump log即可。

BatteryController state:
  mLevel=86
  mPluggedIn=false
  mCharging=true
  mCharged=false
  mPowerSave=false

01-01 10:23:19.490  1000  1098  1110 D BatteryService: Sending ACTION_BATTERY_CHANGED.  level:71, scale:100, status:3, health:2, present:true, voltage: 3962, temperature: 375, technology: Li-ion, AC powered:false, USB powered:false, Wireless powered:false, icon:17303429, invalid charger:0, maxChargingCurrent:0, maxChargingVoltage:0, chargeCounter:1714883

-------------------------------------------------------------------------------
DUMP OF SERVICE batteryproperties:
ac: 0 usb: 0 wireless: 0 current_max: 0 voltage_max: 0
status: 2 health: 2 present: 1
level: 86 voltage: 4234 temp: 297
current now: -77209
charge counter: 2089873
current now: -376
Full charge: 2444000
--------- 0.003s was the duration of dumpsys batteryproperties, ending at: 2018-06-15 16:17:57


这里有个问题,adb shell bugreport的log在User版本和Userdebug的版本里面抓出来的结果不一样,具体原因,可能是日志的级别限制的,需要查证一下。

2.mPluggedIn的值是在收到ACTION_BATTERY_CHANGED广播里面带的参数得到的,那么这个广播是哪里发出来的呢?
通过使用http://androidxref.com 进行全局搜索,找到是在
/frameworks/base/services/core/java/com/android/server/BatteryService.java的sendIntentLocked()函数里面发出来的。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private void sendIntentLocked() {
// Pack up the values and broadcast them to everyone
final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);

int icon = getIconLocked(mBatteryProps.batteryLevel);

intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);
intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);
intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);
intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);
intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);
intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);

EXTRA_PLUGGED的值是由mPlugType得到的,而mPlugType的值在BatteryService.java中得到方式如下:
private void processValuesLocked(boolean force) {
boolean logOutlier = false;
long dischargeDuration = 0;

mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);
if (mBatteryProps.chargerAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
} else if (mBatteryProps.chargerUsbOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
} else if (mBatteryProps.chargerWirelessOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
} else {
mPlugType = BATTERY_PLUGGED_NONE;
}

从中可以看出是根据mBatteryProps.chargerAcOnline或者mBatteryProps.chargerUsbOnline或者mBatteryProps.chargerWirelessOnline的值来判断,在
frameworks/base/core/java/android/os/BatteryProperties.java中,可以看到这三个值是通过序列化的方式得到:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
private BatteryProperties(Parcel p) {
chargerAcOnline = p.readInt() == 1 ? true : false;
chargerUsbOnline = p.readInt() == 1 ? true : false;
chargerWirelessOnline = p.readInt() == 1 ? true : false;
maxChargingCurrent = p.readInt();
maxChargingVoltage = p.readInt();
batteryStatus = p.readInt();
batteryHealth = p.readInt();
batteryPresent = p.readInt() == 1 ? true : false;
batteryLevel = p.readInt();
batteryVoltage = p.readInt();
batteryTemperature = p.readInt();
batteryFullCharge = p.readInt();
batteryChargeCounter = p.readInt();
batteryTechnology = p.readString();
}

public void writeToParcel(Parcel p, int flags) {
p.writeInt(chargerAcOnline ? 1 : 0);
p.writeInt(chargerUsbOnline ? 1 : 0);
p.writeInt(chargerWirelessOnline ? 1 : 0);
p.writeInt(maxChargingCurrent);
p.writeInt(maxChargingVoltage);
p.writeInt(batteryStatus);
p.writeInt(batteryHealth);
p.writeInt(batteryPresent ? 1 : 0);
p.writeInt(batteryLevel);
p.writeInt(batteryVoltage);
p.writeInt(batteryTemperature);
p.writeInt(batteryFullCharge);
p.writeInt(batteryChargeCounter);
p.writeString(batteryTechnology);
}

public static final Parcelable.Creator<BatteryProperties> CREATOR
= new Parcelable.Creator<BatteryProperties>() {
public BatteryProperties createFromParcel(Parcel p) {
return new BatteryProperties(p);
}

public BatteryProperties[] newArray(int size) {
return new BatteryProperties[size];
}
};
再来看看mBatteryProps的赋值:
1
2
3
4
5
6
7
8
9
10
11
private void update(BatteryProperties props) {
synchronized (mLock) {
if (!mUpdatesStopped) {
mBatteryProps = props;
// Process the new values.
processValuesLocked(false);
} else {
mLastBatteryProps.set(props);
}
}
}
接着看update()函数的调用,如下:
1
2
3
4
5
6
7
8
9
10
private final class BatteryListener extends IBatteryPropertiesListener.Stub {
@Override public void batteryPropertiesChanged(BatteryProperties props) {
final long identity = Binder.clearCallingIdentity();
try {
BatteryService.this.update(props);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
可以看到BatteryProperties是在由batteryPropertiesChanged赋值,是通过AIDL实现对C++侧的监听,这个AIDL实现了JAVA侧与C++侧的通讯。
这一块关于电池的共有三个AIDL文件,作为客户端和服务端的媒介,具体代码,以及作用分析如下:
1
2
3
frameworks/base/core/java/android/os/BatteryProperties.aidl
package android.os;
parcelable BatteryProperties;
BatteryProperties.aidl主要用户AIDL跨进程通讯时的数据序列化和反序列化frameworks/base/core/java/android/os/BatteryProperties.java
1
2
3
4
5
6
7
8
9
10
11
frameworks/base/core/java/android/os/IBatteryPropertiesListener.aidl
package android.os;
import android.os.BatteryProperties;

/**
* {@hide}
*/

oneway interface IBatteryPropertiesListener {
void batteryPropertiesChanged(in BatteryProperties props);
}
IBatteryPropertiesListener.aidl主要用于监听
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
frameworks/base/core/java/android/os/IBatteryPropertiesRegistrar.aidl
package android.os;
import android.os.IBatteryPropertiesListener;
import android.os.BatteryProperty;

/**
* {@hide}
*/

interface IBatteryPropertiesRegistrar {
void registerListener(IBatteryPropertiesListener listener);
void unregisterListener(IBatteryPropertiesListener listener);
int getProperty(in int id, out BatteryProperty prop);
oneway void scheduleUpdate();
}
IBatteryPropertiesRegistrar.aidl主要用于注册

服务端在BatteryService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public void onStart() {
IBinder b = ServiceManager.getService("batteryproperties");
final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
IBatteryPropertiesRegistrar.Stub.asInterface(b);
try {
batteryPropertiesRegistrar.registerListener(new BatteryListener());
} catch (RemoteException e) {
// Should never happen.
}

mBinderService = new BinderService();
publishBinderService("battery", mBinderService);
publishLocalService(BatteryManagerInternal.class, new LocalService());
}
对应的native层的代码在frameworks/native/services/batteryservice/编译出libbatteryservice.so库
Java侧与C++侧通过AIDL通讯,两边都要写AIDL文件,且两边都要有Parcel序列化。
通过查找batteryPropertiesChanged函数

system/core/healthd/BatteryPropertiesRegistrar.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include "BatteryPropertiesRegistrar.h"
#include <batteryservice/BatteryService.h>
#include <batteryservice/IBatteryPropertiesListener.h>
#include <batteryservice/IBatteryPropertiesRegistrar.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
#include <private/android_filesystem_config.h>
#include <utils/Errors.h>
#include <utils/Mutex.h>
#include <utils/String16.h>

#include <healthd/healthd.h>

namespace android {

void BatteryPropertiesRegistrar::publish(
const sp<BatteryPropertiesRegistrar>& service) {
defaultServiceManager()->addService(String16("batteryproperties"), service);
}

void BatteryPropertiesRegistrar::notifyListeners(const struct BatteryProperties& props) {
Vector<sp<IBatteryPropertiesListener> > listenersCopy;

// Binder currently may service an incoming oneway transaction whenever an
// outbound oneway call is made (if there is already a pending incoming
// oneway call waiting). This is considered a bug and may change in the
// future. For now, avoid recursive mutex lock while making outbound
// calls by making a local copy of the current list of listeners.
{
Mutex::Autolock _l(mRegistrationLock);
listenersCopy = mListeners;
}
for (size_t i = 0; i < listenersCopy.size(); i++) {
listenersCopy[i]->batteryPropertiesChanged(props);
}
}

void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesListener>& listener) {
{
if (listener == NULL)
return;
Mutex::Autolock _l(mRegistrationLock);
// check whether this is a duplicate
for (size_t i = 0; i < mListeners.size(); i++) {
if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
return;
}
}

mListeners.add(listener);
IInterface::asBinder(listener)->linkToDeath(this);
}
healthd_battery_update();
}

void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) {
if (listener == NULL)
return;
Mutex::Autolock _l(mRegistrationLock);
for (size_t i = 0; i < mListeners.size(); i++) {
if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
IInterface::asBinder(mListeners[i])->unlinkToDeath(this);
mListeners.removeAt(i);
break;
}
}
}

status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) {
return healthd_get_property(id, val);
}

void BatteryPropertiesRegistrar::scheduleUpdate() {
healthd_battery_update();
}

status_t BatteryPropertiesRegistrar::dump(int fd, const Vector<String16>& /*args*/) {
IPCThreadState* self = IPCThreadState::self();
const int pid = self->getCallingPid();
const int uid = self->getCallingUid();
if ((uid != AID_SHELL) &&
!PermissionCache::checkPermission(
String16("android.permission.DUMP"), pid, uid))
return PERMISSION_DENIED;

healthd_dump_battery_state(fd);
return OK;
}

void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) {
Mutex::Autolock _l(mRegistrationLock);

for (size_t i = 0; i < mListeners.size(); i++) {
if (IInterface::asBinder(mListeners[i]) == who) {
mListeners.removeAt(i);
break;
}
}
}

} // namespace android
再查找notifyListeners函数,在 system/core/healthd/healthd_mode_android.cpp
1
2
3
4
5
6
7
void healthd_mode_android_battery_update(
struct android::BatteryProperties *props) {
if (gBatteryPropertiesRegistrar != NULL)
gBatteryPropertiesRegistrar->notifyListeners(*props);

return;
}
Healthd是一种中介模型,向下监听来自kernel层的电池事件,向上将电池数据信息传递给Framework层的BatteryService.
主要代码在 system/core/healthd 中,对Healthd模块的代码分析如下:
参考:
Healthd模块编译出来的文件有system/bin/healthd
通过在system/core/healthd里面查找chargerUsbOnline,在system/core/healthd/BatteryMonitor.cpp中update函数可以看到赋值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
bool BatteryMonitor::update(void) {
bool logthis;

initBatteryProperties(&props);

if (!mHealthdConfig->batteryPresentPath.isEmpty())
props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
else
props.batteryPresent = mBatteryDevicePresent;

props.batteryLevel = mBatteryFixedCapacity ?
mBatteryFixedCapacity :
getIntField(mHealthdConfig->batteryCapacityPath);
props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;

if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath) / 1000;

if (!mHealthdConfig->batteryFullChargePath.isEmpty())
props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath);

if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
props.batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);

if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);

props.batteryTemperature = mBatteryFixedTemperature ?
mBatteryFixedTemperature :
getIntField(mHealthdConfig->batteryTemperaturePath);

// For devices which do not have battery and are always plugged
// into power souce.
if (mAlwaysPluggedDevice) {
props.chargerAcOnline = true;
props.batteryPresent = true;
props.batteryStatus = BATTERY_STATUS_CHARGING;
props.batteryHealth = BATTERY_HEALTH_GOOD;
}

std::string buf;

if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
props.batteryStatus = getBatteryStatus(buf.c_str());

if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
props.batteryHealth = getBatteryHealth(buf.c_str());

if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
props.batteryTechnology = String8(buf.c_str());

unsigned int i;
double MaxPower = 0;

for (i = 0; i < mChargerNames.size(); i++) {
String8 path;
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
if (getIntField(path)) {
path.clear();
path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
props.chargerAcOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_USB:
props.chargerUsbOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
props.chargerWirelessOnline = true;
break;
default:
KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
mChargerNames[i].string());
}
path.clear();
path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
int ChargingCurrent =
(access(path.string(), R_OK) == 0) ? getIntField(path) : 0;

path.clear();
path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());

int ChargingVoltage =
(access(path.string(), R_OK) == 0) ? getIntField(path) :
DEFAULT_VBUS_VOLTAGE;

double power = ((double)ChargingCurrent / MILLION) *
((double)ChargingVoltage / MILLION);
if (MaxPower < power) {
props.maxChargingCurrent = ChargingCurrent;
props.maxChargingVoltage = ChargingVoltage;
MaxPower = power;
}
}
}

logthis = !healthd_board_battery_update(&props);

if (true) {
char dmesgline[256];
size_t len;
if (props.batteryPresent) {
snprintf(dmesgline, sizeof(dmesgline),
"battery l=%d v=%d t=%s%d.%d h=%d st=%d",
props.batteryLevel, props.batteryVoltage,
props.batteryTemperature < 0 ? "-" : "",
abs(props.batteryTemperature / 10),
abs(props.batteryTemperature % 10), props.batteryHealth,
props.batteryStatus);

len = strlen(dmesgline);
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
" c=%d", props.batteryCurrent);
}

if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
" fc=%d", props.batteryFullCharge);
}

if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
" cc=%d", props.batteryCycleCount);
}
} else {
len = snprintf(dmesgline, sizeof(dmesgline),
"battery none");
}

snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
props.chargerAcOnline ? "a" : "",
props.chargerUsbOnline ? "u" : "",
props.chargerWirelessOnline ? "w" : "");

KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
}

healthd_mode_ops->battery_update(&props);
return props.chargerAcOnline | props.chargerUsbOnline |
props.chargerWirelessOnline;
}

从上面代码可以看到chargerUsbOnline的值是通过读取 sys/class/power_supply文件夹下的USB文件夹下的type里面的值来判断。

adb shell进入手机后,cd sys/class/power_supply/USB,cat type发现是USB。
这下懵逼了,这个值是对的呀,为何log里面读取出来的值不对呢?而且从之前抓的dump log来看如下:的确没有正确的读取到

-------------------------------------------------------------------------------
DUMP OF SERVICE batteryproperties:
ac: 0 usb: 0 wireless: 0 current_max: 0 voltage_max: 0
status: 2 health: 2 present: 1
level: 86 voltage: 4234 temp: 297
current now: -77209
charge counter: 2089873
current now: -376
Full charge: 2444000
--------- 0.003s was the duration of dumpsys batteryproperties, ending at: 2018-06-15 16:17:57
现在怀疑是SeAndroid的AVC权限导致的。