首先执行 adb shell dumpsys battery
看看
~/projects/MTK_R_6580_2/alps/frameworks$ adb shell dumpsys battery
Current Battery Service state:
AC powered: false
USB powered: true
Wireless powered: false
Max charging current: 500000
Max charging voltage: 5000000
Charge counter: 4980000
status: 5
health: 2
present: true
level: 100
scale: 100
voltage: 4377
temperature: 250
technology: Li-ion
以 Current Battery Service state
这个字符串为出发点查找文件
~/projects/MTK_R_6580_2/alps/frameworks$ grep "Current Battery Service state" ./ -r
frameworks/base/services/core/java/com/android/server/BatteryService.java: pw.println("Current Battery Service state:");
找到在frameworks/base/services/core/java/com/android/server/BatteryService.java
文件
相关代码如下
private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mLock) {
if (args == null || args.length == 0 || "-a".equals(args[0])) {
pw.println("Current Battery Service state:");
if (mUpdatesStopped) {
pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
}
pw.println(" AC powered: " + mHealthInfo.chargerAcOnline);
pw.println(" USB powered: " + mHealthInfo.chargerUsbOnline);
pw.println(" Wireless powered: " + mHealthInfo.chargerWirelessOnline);
pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrent);
pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltage);
pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounter);
pw.println(" status: " + mHealthInfo.batteryStatus);
pw.println(" health: " + mHealthInfo.batteryHealth);
pw.println(" present: " + mHealthInfo.batteryPresent);
pw.println(" level: " + mHealthInfo.batteryLevel);
pw.println(" scale: " + BATTERY_SCALE);
pw.println(" voltage: " + mHealthInfo.batteryVoltage);
pw.println(" temperature: " + mHealthInfo.batteryTemperature);
pw.println(" technology: " + mHealthInfo.batteryTechnology);
} else {
Shell shell = new Shell();
shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
}
}
}
从上面这段代码中可以看出 args 为空或 args 的第一个参数为 -a 的时候会打印电池相关信息,然后我们再看 else 部分代码
Shell shell = new Shell();
shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
Shell的代码如下
class Shell extends ShellCommand {
@Override
public int onCommand(String cmd) {
return onShellCommand(this, cmd);
}
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
dumpHelp(pw);
}
}
想必在shell执行exec方法的时候会回调到 onCommand()方法,为了验证我们的猜想,看一下 Shell 的父类 ShellCommand
frameworks/base/core/java/android/os/ShellCommand.java
public abstract class ShellCommand extends BasicShellCommandHandler {
private ShellCallback mShellCallback;
private ResultReceiver mResultReceiver;
public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
mShellCallback = callback;
mResultReceiver = resultReceiver;
final int result = super.exec(target, in, out, err, args);
if (mResultReceiver != null) {
mResultReceiver.send(result, null);
}
return result;
}
ShellCommand中没有找到 onCommand 的回调,但找到 super.exec() ,继续往父类中看
frameworks/base/core/java/android/os/BasicShellCommandHandler.java
public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args) {
String cmd;
int start;
if (args != null && args.length > 0) {
cmd = args[0];
start = 1;
} else {
cmd = null;
start = 0;
}
init(target, in, out, err, args, start);
mCmd = cmd;
if (DEBUG) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Log.d(TAG, "Starting command " + mCmd + " on " + mTarget, here);
Log.d(TAG, "Calling uid=" + Binder.getCallingUid()
+ " pid=" + Binder.getCallingPid());
}
int res = -1;
try {
res = onCommand(mCmd); // 果然在这里找到了 onCommand() 方法的回调
if (DEBUG) Log.d(TAG, "Executed command " + mCmd + " on " + mTarget);
} catch (Throwable e) {
// Unlike usual calls, in this case if an exception gets thrown
// back to us we want to print it back in to the dump data, since
// that is where the caller expects all interesting information to
// go.
PrintWriter eout = getErrPrintWriter();
eout.println();
eout.println("Exception occurred while executing '" + mCmd + "':");
e.printStackTrace(eout);
} finally {
if (DEBUG) Log.d(TAG, "Flushing output streams on " + mTarget);
if (mOutPrintWriter != null) {
mOutPrintWriter.flush();
}
if (mErrPrintWriter != null) {
mErrPrintWriter.flush();
}
if (DEBUG) Log.d(TAG, "Sending command result on " + mTarget);
}
if (DEBUG) Log.d(TAG, "Finished command " + mCmd + " on " + mTarget);
return res;
}
现在回到 Shell 类中看一下 onShellCommand(this, cmd);
方法中做了什么
int onShellCommand(Shell shell, String cmd) {
if (cmd == null) {
return shell.handleDefaultCommands(cmd);
}
PrintWriter pw = shell.getOutPrintWriter();
switch (cmd) {
case "unplug": {
int opts = parseOptions(shell);
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
if (!mUpdatesStopped) {
copy(mLastHealthInfo, mHealthInfo);
}
mHealthInfo.chargerAcOnline = false;
mHealthInfo.chargerUsbOnline = false;
mHealthInfo.chargerWirelessOnline = false;
long ident = Binder.clearCallingIdentity();
try {
mUpdatesStopped = true;
processValuesFromShellLocked(pw, opts);
} finally {
Binder.restoreCallingIdentity(ident);
}
} break;
case "set": {
int opts = parseOptions(shell);
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
final String key = shell.getNextArg();
if (key == null) {
pw.println("No property specified");
return -1;
}
final String value = shell.getNextArg();
if (value == null) {
pw.println("No value specified");
return -1;
}
try {
if (!mUpdatesStopped) {
copy(mLastHealthInfo, mHealthInfo);
}
boolean update = true;
switch (key) {
case "present":
mHealthInfo.batteryPresent = Integer.parseInt(value) != 0;
break;
case "ac":
mHealthInfo.chargerAcOnline = Integer.parseInt(value) != 0;
break;
case "usb":
mHealthInfo.chargerUsbOnline = Integer.parseInt(value) != 0;
break;
case "wireless":
mHealthInfo.chargerWirelessOnline = Integer.parseInt(value) != 0;
break;
case "status":
mHealthInfo.batteryStatus = Integer.parseInt(value);
break;
case "level":
mHealthInfo.batteryLevel = Integer.parseInt(value);
break;
case "counter":
mHealthInfo.batteryChargeCounter = Integer.parseInt(value);
break;
case "temp":
mHealthInfo.batteryTemperature = Integer.parseInt(value);
break;
case "invalid":
mInvalidCharger = Integer.parseInt(value);
break;
default:
pw.println("Unknown set option: " + key);
update = false;
break;
}
if (update) {
long ident = Binder.clearCallingIdentity();
try {
mUpdatesStopped = true;
processValuesFromShellLocked(pw, opts);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
} catch (NumberFormatException ex) {
pw.println("Bad value: " + value);
return -1;
}
} break;
case "reset": {
int opts = parseOptions(shell);
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
long ident = Binder.clearCallingIdentity();
try {
if (mUpdatesStopped) {
mUpdatesStopped = false;
copy(mHealthInfo, mLastHealthInfo);
processValuesFromShellLocked(pw, opts);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
} break;
default:
return shell.handleDefaultCommands(cmd);
}
return 0;
}
看到这段代码应该就豁然开朗了,看到有3个方法 unplug、set、reset
。
set
方法后面有可以跟present、ac、usb、wireless、status、level、counter、temp、invalid
。
我们再回到最开始的地方看一下是哪里调用 dumpInternal() 方法的?
private final class BinderService extends Binder {
@Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
if (args.length > 0 && "--proto".equals(args[0])) {
dumpProto(fd);
} else {
dumpInternal(fd, pw, args);
}
}
@Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
(new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
}
}
哦,原来是 binder,那是不是其它的binder(或者自己加的binder)也可以通过这种方法做一些功能方便我们调试呢?小伙伴们可以自己尝试一下哦!~