电池--低电量关机


基于AOSP 14

模拟电池电量

framework调试过程中,涉及到电池电量的问题需要电池电量达到特定值才可以复现到问题现象,等待电池电量消耗或者现场充电并不现实,及其影响调试效率,为了使电池电量在指定的水平,可以通过adb shell命令设置电池电量显示在需要电量水平,仅仅只是更改上层电池电量的显示结果,并不会真正影响到电池的实际电量

/frameworks/base/services/core/java/com/android/server/BatteryService.java

在BatteryService.java中有一个onShellCommand()函数用于接收adb shell命令,此函数中有多个get和set指令,通过get指定的shell命令可以获取电池相关的信息,通过set指令可以设置电池相关的信息,模拟电池电量可以使用set指令设置level参数的值,来模拟电池电量
adb shell dumpsys battery set level 0

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);
            unplugBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
        } break;
        case "get": {
            final String key = shell.getNextArg();
            if (key == null) {
                pw.println("No property specified");
                return -1;
            }
            switch (key) {
                case "present":
                    pw.println(mHealthInfo.batteryPresent);
                    break;
                case "ac":
                    pw.println(mHealthInfo.chargerAcOnline);
                    break;
                case "usb":
                    pw.println(mHealthInfo.chargerUsbOnline);
                    break;
                case "wireless":
                    pw.println(mHealthInfo.chargerWirelessOnline);
                    break;
                case "dock":
                    pw.println(mHealthInfo.chargerDockOnline);
                    break;
                case "status":
                    pw.println(mHealthInfo.batteryStatus);
                    break;
                case "level":
                    pw.println(mHealthInfo.batteryLevel);
                    break;
                case "counter":
                    pw.println(mHealthInfo.batteryChargeCounterUah);
                    break;
                case "temp":
                    pw.println(mHealthInfo.batteryTemperatureTenthsCelsius);
                    break;
                case "invalid":
                    pw.println(mInvalidCharger);
                    break;
                default:
                    pw.println("Unknown get option: " + key);
                    break;
            }
        } 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) {
                    copyV1Battery(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 "dock":
                        mHealthInfo.chargerDockOnline = Integer.parseInt(value) != 0;
                        break;
                    case "status":
                        mHealthInfo.batteryStatus = Integer.parseInt(value);
                        break;
                    case "level":
                        mHealthInfo.batteryLevel = Integer.parseInt(value);
                        break;
                    case "counter":
                        mHealthInfo.batteryChargeCounterUah = Integer.parseInt(value);
                        break;
                    case "temp":
                        mHealthInfo.batteryTemperatureTenthsCelsius = Integer.parseInt(value);
                        break;
                    case "invalid":
                        mInvalidCharger = Integer.parseInt(value);
                        break;
                    default:
                        pw.println("Unknown set option: " + key);
                        update = false;
                        break;
                }
                if (update) {
                    final long ident = Binder.clearCallingIdentity();
                    try {
                        mUpdatesStopped = true;
                        processValuesLocked(
                                /* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
                    } 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);
            resetBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
        } break;
        case "suspend_input": {
            getContext().enforceCallingOrSelfPermission(
                    android.Manifest.permission.DEVICE_POWER, null);
            suspendBatteryInput();
        } break;
        default:
            return shell.handleDefaultCommands(cmd);
    }
    return 0;
}

设备电池低电量关机

监听电池信息

BatteryService.java启动动时,在onStart()函数中监听healthInfo信息,healthInfo是由底层电池计量器传递到上层的电池信息

@Override
public void onStart() {
	//注册电池helath的回调监听
    registerHealthCallback();
    ...
}
private void registerHealthCallback() {
    traceBegin("HealthInitWrapper");
    try {
    	//创建HealthServiceWrapper并调用update更新电池信息
        mHealthServiceWrapper = HealthServiceWrapper.create(this::update);
    } catch (RemoteException ex) {
        Slog.e(TAG, "health: cannot register callback. (RemoteException)");
        throw ex.rethrowFromSystemServer();
    } catch (NoSuchElementException ex) {
        Slog.e(TAG, "health: cannot register callback. (no supported health HAL service)");
        throw ex;
    } finally {
        traceEnd();
    }
    ...
}

根据电池信息更新上层显示和通知

上层在接收到电池的信息后,会根据当前电池的一些信息来更新显示或通知,比如电池电量警告,电池温度等

private void update(android.hardware.health.HealthInfo info) {
    traceBegin("HealthInfoUpdate");
    synchronized (mLock) {
        if (!mUpdatesStopped) {
        	//此处就是底层上抛的电池信息healthInfo
            mHealthInfo = info;
            // Process the new values.
            processValuesLocked(false);
            mLock.notifyAll(); // for any waiters on new info
        } else {
            copyV1Battery(mLastHealthInfo, info);
        }
    }
    traceEnd();
}

private void processValuesLocked(boolean force) {
    boolean logOutlier = false;
    long dischargeDuration = 0;
    ...
    //将电池的信息保存到BatteryStats中,以供framework使用
    try {
        mBatteryStats.setBatteryState(
                mHealthInfo.batteryStatus,
                mHealthInfo.batteryHealth,
                mPlugType,
                mHealthInfo.batteryLevel,
                mHealthInfo.batteryTemperatureTenthsCelsius,
                mHealthInfo.batteryVoltageMillivolts,
                mHealthInfo.batteryChargeCounterUah,
                mHealthInfo.batteryFullChargeUah,
                mHealthInfo.batteryChargeTimeToFullNowSeconds);
    } catch (RemoteException e) {
        // Should never happen.
    }
    //低电量关机
    shutdownIfNoPowerLocked();
    //电池温度过高时关机
    shutdownIfOverTempLocked();
    if (force
            || (mHealthInfo.batteryStatus != mLastBatteryStatus
                    || mHealthInfo.batteryHealth != mLastBatteryHealth
                    || mHealthInfo.batteryPresent != mLastBatteryPresent
                    || mHealthInfo.batteryLevel != mLastBatteryLevel
                    || mPlugType != mLastPlugType
                    || mHealthInfo.batteryVoltageMillivolts != mLastBatteryVoltage
                    || mHealthInfo.batteryTemperatureTenthsCelsius != mLastBatteryTemperature
                    || mHealthInfo.maxChargingCurrentMicroamps != mLastMaxChargingCurrent
                    || mHealthInfo.maxChargingVoltageMicrovolts != mLastMaxChargingVoltage
                    || mHealthInfo.batteryChargeCounterUah != mLastChargeCounter
                    || mInvalidCharger != mLastInvalidCharger
                    || mHealthInfo.batteryCycleCount != mLastBatteryCycleCount
                    || mHealthInfo.chargingState != mLastCharingState)) {
        ...
        // 发送广播,通知电池信息发生变化
        sendBatteryChangedIntentLocked();
        if (mLastBatteryLevel != mHealthInfo.batteryLevel || mLastPlugType != mPlugType) {
            sendBatteryLevelChangedIntentLocked();
        }
        // 电池状态的LED灯
        mLed.updateLightsLocked();
        // This needs to be done after sendIntent() so that we get the lastest battery stats.
        if (logOutlier && dischargeDuration != 0) {
            logOutlierLocked(dischargeDuration);
        }
        ...
    }
}

电池低电量关机

电池电量为0时,设备弹出提示并倒计时关机,打开ShutdownActivity.java

private boolean shouldShutdownLocked() {
	//电池电量是否与电池计量计的电量相同
    if (mHealthInfo.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
        return (mHealthInfo.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
    }
    //电池电量是否大于0
    if (mHealthInfo.batteryLevel > 0) {
        return false;
    }
    // 设备是否有电池
    if (!mHealthInfo.batteryPresent) {
        return false;
    }
    //设备不是充电状态
    return mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_CHARGING;
}

private void shutdownIfNoPowerLocked() {
    if (shouldShutdownLocked()) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (mActivityManagerInternal.isSystemReady()) {
                	//打开关机弹窗
                    Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
                    intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
                    intent.putExtra(Intent.EXTRA_REASON,
                            PowerManager.SHUTDOWN_LOW_BATTERY);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
                }
            }
        });
    }
}

准备关机–ShutdownActivity.java

/frameworks/base/core/java/com/android/internal/app/ShutdownActivity.java

在ShutdownActivity.java中,获取随intent携带来的数据打印关机原因,同时根据action和这些数据判断是需要重启还是需要关机,通过aidl调用powerManagerService的接口执行重启或关机

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    Thread thr = new Thread("ShutdownActivity") {
        @Override
        public void run() {
            IPowerManager pm = IPowerManager.Stub.asInterface(
                    ServiceManager.getService(Context.POWER_SERVICE));
            try {
                if (mReboot) {
                    pm.reboot(mConfirm, null, false);
                } else {
                    pm.shutdown(mConfirm, reason, false);
                }
            } catch (RemoteException e) {
            }
        }
    };
    thr.start();
    finish();
}

执行关机操作

/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
@Override // Binder call
public void shutdown(boolean confirm, String reason, boolean wait) {
	//关机权限
    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
    ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
    final long ident = Binder.clearCallingIdentity();
    try {
        shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
        @Nullable final String reason, boolean wait) {
        ...
        //低电量时进行回复出厂设置,设备会进行入无限重启状态
        if (RescueParty.isAttemptingFactoryReset()) {
            PowerManagerService.lowLevelReboot(reason);
        }
	//在关机线程中执行关机或重启
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            synchronized (this) {
                if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
                    ShutdownThread.rebootSafeMode(getUiContext(), confirm);
                } else if (haltMode == HALT_MODE_REBOOT) {
                    ShutdownThread.reboot(getUiContext(), reason, confirm);
                } else {
                    ShutdownThread.shutdown(getUiContext(), reason, confirm);
                }
            }
        }
    };
    ...
}

关机对话框

/frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java

在ShutdownThread中执行shutdown或者reboot操作,弹出关机对话框

public static void shutdown(final Context context, String reason, boolean confirm) {
	...
    shutdownInner(context, confirm);
}
private static void shutdownInner(final Context context, boolean confirm) {
    ...
    ShutdownCheckPoints.recordCheckPoint(/* reason= */ null);
    //长按power键关机的时间
    final int longPressBehavior = context.getResources().getInteger(
                    com.android.internal.R.integer.config_longPressOnPowerBehavior);
	//关机对话框的提示信息
    final int resourceId = mRebootSafeMode
            ? com.android.internal.R.string.reboot_safemode_confirm
            : (longPressBehavior == 2
                    ? com.android.internal.R.string.shutdown_confirm_question
                    : com.android.internal.R.string.shutdown_confirm);
    if (confirm) {
        final CloseDialogReceiver closer = new CloseDialogReceiver(context);
        if (sConfirmDialog != null) {
            sConfirmDialog.dismiss();
        }
        //构建关机对话框
        sConfirmDialog = new AlertDialog.Builder(context)
                .setTitle(mRebootSafeMode
                        ? com.android.internal.R.string.reboot_safemode_title
                        : com.android.internal.R.string.power_off)
                .setMessage(resourceId)
                .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        beginShutdownSequence(context);
                    }
                })
                .setNegativeButton(com.android.internal.R.string.no, null)
                .create();
        closer.dialog = sConfirmDialog;
        sConfirmDialog.setOnDismissListener(closer);
        sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
        sConfirmDialog.show();
    } else {
    	//关机
        beginShutdownSequence(context);
    }
}
//广播接收是否关闭关机对话框,结束关机流程
private static class CloseDialogReceiver extends BroadcastReceiver
        implements DialogInterface.OnDismissListener {
    private Context mContext;
    public Dialog dialog;
    CloseDialogReceiver(Context context) {
        mContext = context;
        IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        context.registerReceiver(this, filter, Context.RECEIVER_EXPORTED);
    }
    @Override
    public void onReceive(Context context, Intent intent) {
        dialog.cancel();
    }
    public void onDismiss(DialogInterface unused) {
        mContext.unregisterReceiver(this);
    }
}
  • 21
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值