文章目录
一)场景需求
提示: 如果是手机方案,标准的电池电量一套,比如大家最常见的手机方案,采用的就是最标准的一套架构实现。
二)问题
如果是标准方案,按照标准流程,电池系统从驱动到应用层一层层传递,但是 如果电池厂商特别是定制的电池方案,电量和充电状态并不是
比如各种大型机器,汽车、摩托车、电车等,用的绝对不是手机电池方案了,终究原因 电池电压不够。 传统谷歌那套就不合适了,只是流程差不多而已。
三)本文内容讲解
Android系统通过串口和定制电池对接实现电池电量显示和充放电状态。核心问题:系统如何集成串口来实现电池获取和相关参数显示这一套
四)应用端实现
讲解系统集成串口前,先讲解一下 应用端如何对接串口通讯,举个例子说明,应用层串口对接第三方实现
Android 谷歌提供了SerialPort 相关源码Demo,
比如如下一个 SerialPort fock 自 谷歌的demo:
串口Demo
核心思想三点
设置串口参数
mSerialPortFinder = new SerialPortFinder();
// 得到所有设备文件地址的数组
// 实际上该操作并不需要,这里只是示例打印出所有的设备信息
String[] entryValues = mSerialPortFinder.getAllDevicesPath();
try {
// 打开/dev/ttyUSB0路径设备的串口
mSerialPort = new SerialPort(new File("/dev/ttyUSB0"), 9600, 0);
} catch (IOException e) {
System.out.println("找不到该设备文件");
}
开启线程,接收串口数据
SerialPort对象中得到输入流,并开启一个子线程进行读取该设备传入的串口数据:
final InputStream inputStream = mSerialPort.getInputStream();
/* 开启一个线程进行读取 */
new Thread(new Runnable() {
@Override
public void run() {
try {
byte[] buffer = new byte[1024];
int size = inputStream.read(buffer);
byte[] readBytes = new byte[size];
System.arraycopy(buffer, 0, readBytes, 0, size);
System.out.println("received data => " + new String(readBytes));
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
发送串口数据
发送数据时,我们只需要从SerialPort对象中得到OutputStream对象,写入字节数组数据即可:
// 开启子线程进行发送数据
new Thread(new Runnable() {
@Override
public void run() {
String content = "Hello World";
byte[] bytes = content.getBytes();
OutputStream out = mSerialPort.getOutputStream();
// 写入数据
try {
out.write(bytes);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
实际应用
实际应用中,更加简单,如果自己没有封装的能力,使用第三方封装好的依赖库,实现串口连接、收、发 数据。 操作更容易 更便捷
比如: implementation ‘com.github.Acccord:AndroidSerialPort:1.5.0’
其它还有好多依赖库,可以参考使用。
五)系统端framework层对接串口
以我们的需求场景为例:通过串口获取串口电量然后显示
实现方案
添加串口工具类
\frameworks\base\services\core\java\com\android\server\SerialPortManager.java
定义串口节点;封装打开、关闭、写 等接口方法
package com.android.server;
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import android.content.Context;
import android.hardware.SerialManager;
import android.hardware.SerialPort;
import android.util.Log;
public class SerialPortManager {
private static final String SERIAL_PORT_NAME = "/dev/ttyS1";
public static final int SERIAL_PORT_SPEED = 9600;
private static SerialPort mSerialPort = null;
public int port = 0;
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE};
private static final String TAG = "SerialChat";
private static SerialPortManager ins = new SerialPortManager();
public static SerialPortManager instance() {
if (ins == null) {
ins = new SerialPortManager();
}
return ins;
}
public SerialPort getmSerialPort(){
Log.d(TAG,"getmSerialPort");
return mSerialPort;
}
public void open(Context context) {
SerialManager sm = (SerialManager) context.getSystemService(Context.SERIAL_SERVICE);
if(port == 0){
port = SERIAL_PORT_SPEED;
}
try {
mSerialPort = sm.openSerialPort(SERIAL_PORT_NAME, port);
if (mSerialPort == null) {
return;
}
Log.d(TAG,"open the is "+port);
} catch (IOException e) {
e.printStackTrace();
return;
}
}
public void writeBuffer(ByteBuffer buffer) {
int len = buffer.array().length;
try {
mSerialPort.write(buffer,len);
Log.d(TAG,"write the is "+len);
} catch (IOException e) {
e.printStackTrace();
}
}
public void close() {
try{
if (mSerialPort != null){
mSerialPort.close();
}
Log.d(TAG,"close the mSerialPort");
} catch (IOException e){
e.printStackTrace();
}
}
public static void write(String value, String filePath) {
OutputStream stream = null;
OutputStreamWriter os = null;
try {
File file = new File(filePath);
if (!file.exists()) {
file.createNewFile();
}
stream = new FileOutputStream(filePath);
os = new OutputStreamWriter(stream, "UTF-8");
BufferedWriter writer = new BufferedWriter(os);
writer.write(value);
writer.flush();
writer.close();
Log.d(TAG,"write sys succe");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
电池服务里面实现 串口逻辑
frameworks\base\services\core\java\com\android\server\BatteryService.java
打开串口 ->开启线程 -> 收数据->根据数据对接电池协议,实现业务
BatteryService.java 源代码参考如下
/*
* Copyright (C) 2006 The Android Open Source Project
*
* 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.
*/
package com.android.server;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.hardware.health.V1_0.HealthInfo;
import android.hardware.health.V2_0.IHealth;
import android.hardware.health.V2_0.Result;
import android.hardware.health.V2_1.BatteryCapacityLevel;
import android.hardware.health.V2_1.Constants;
import android.hardware.health.V2_1.IHealthInfoCallback;
import android.hidl.manager.V1_0.IServiceManager;
import android.hidl.manager.V1_0.IServiceNotification;
import android.metrics.LogMaker;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.BatteryProperty;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
import android.os.OsProtoEnums;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UEventObserver;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.battery.BatteryServiceDumpProto;
import android.sysprop.PowerProperties;
import android.util.EventLog;
import android.util.MutableInt;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
//huanghb add
import android.hardware.SerialManager;
import android.hardware.SerialPort;
import java.nio.ByteBuffer;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
/**
* <p>BatteryService monitors the charging status, and charge level of the device
* battery. When these values change this service broadcasts the new values
* to all {@link android.content.BroadcastReceiver IntentReceivers} that are
* watching the {@link android.content.Intent#ACTION_BATTERY_CHANGED
* BATTERY_CHANGED} action.</p>
* <p>The new values are stored in the Intent data and can be retrieved by
* calling {@link android.content.Intent#getExtra Intent.getExtra} with the
* following keys:</p>
* <p>"scale" - int, the maximum value for the charge level</p>
* <p>"level" - int, charge level, from 0 through "scale" inclusive</p>
* <p>"status" - String, the current charging status.<br />
* <p>"health" - String, the current battery health.<br />
* <p>"present" - boolean, true if the battery is present<br />
* <p>"icon-small" - int, suggested small icon to use for this state</p>
* <p>"plugged" - int, 0 if the device is not plugged in; 1 if plugged
* into an AC power adapter; 2 if plugged in via USB.</p>
* <p>"voltage" - int, current battery voltage in millivolts</p>
* <p>"temperature" - int, current battery temperature in tenths of
* a degree Centigrade</p>
* <p>"technology" - String, the type of battery installed, e.g. "Li-ion"</p>
*
* <p>
* The battery service may be called by the power manager while holding its locks so
* we take care to post all outcalls into the activity manager to a handler.
*
* FIXME: Ideally the power manager would perform all of its calls into the battery
* service asynchronously itself.
* </p>
*/
public final class BatteryService extends SystemService {
private static final String TAG = BatteryService.class.getSimpleName();
private static final boolean DEBUG = false;
private static final int BATTERY_SCALE = 100; // battery capacity is a percentage
private static final long HEALTH_HAL_WAIT_MS = 1000;
private static final long BATTERY_LEVEL_CHANGE_THROTTLE_MS = 60_000;
private static final int MAX_BATTERY_LEVELS_QUEUE_SIZE = 100;
// Used locally for determining when to make a last ditch effort to log
// discharge stats before the device dies.
private int mCriticalBatteryLevel;
// TODO: Current args don't work since "--unplugged" flag was purposefully removed.
private static final String[] DUMPSYS_ARGS = new String[] {
"--checkin", "--unplugged" };
private static final String DUMPSYS_DATA_PATH = "/data/system/";
// This should probably be exposed in the API, though it's not critical
private static final int BATTERY_PLUGGED_NONE = OsProtoEnums.BATTERY_PLUGGED_NONE; // = 0
private final Context mContext;
private final IBatteryStats mBatteryStats;
BinderService mBinderService;
private final Handler mHandler;
private final Object mLock = new Object();
private HealthInfo mHealthInfo;
private final HealthInfo mLastHealthInfo = new HealthInfo();
private android.hardware.health.V2_1.HealthInfo mHealthInfo2p1;
private boolean mBatteryLevelCritical;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
private boolean mLastBatteryPresent;
private int mLastBatteryLevel;
private int mLastBatteryVoltage;
private int mLastBatteryTemperature;
private boolean mLastBatteryLevelCritical;
private int mLastMaxChargingCurrent;
private int mLastMaxChargingVoltage;
private int mLastChargeCounter;
private int mSequence = 1;
private int mInvalidCharger;
private int mLastInvalidCharger;
private int mLowBatteryWarningLevel;
private int mLastLowBatteryWarningLevel;
private int mLowBatteryCloseWarningLevel;
private int mShutdownBatteryTemperature;
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
private boolean mBatteryLevelLow;
private long mDischargeStartTime;
private int mDischargeStartLevel;
private long mChargeStartTime;
private int mChargeStartLevel;
private boolean mUpdatesStopped;
private boolean mBatteryInputSuspended;
private Led mLed;
private boolean mSentLowBatteryBroadcast = false;
private ActivityManagerInternal mActivityManagerInternal;
private HealthServiceWrapper mHealthServiceWrapper;
private HealthHalCallback mHealthHalCallback;
private BatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
private ArrayDeque<Bundle> mBatteryLevelsEventQueue;
private long mLastBatteryLevelChangedSentMs;
private MetricsLogger mMetricsLogger;
public BatteryService(Context context) {
super(context);
mContext = context;
mHandler = new Handler(true /*async*/);
mLed = new Led(context, getLocalService(LightsManager.class));
mBatteryStats = BatteryStatsService.getService();
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mCriticalBatteryLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
mLowBatteryWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
mShutdownBatteryTemperature = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shutdownBatteryTemperature);
mBatteryLevelsEventQueue = new ArrayDeque