串口在系统framework 中的应用


一)场景需求

提示: 如果是手机方案,标准的电池电量一套,比如大家最常见的手机方案,采用的就是最标准的一套架构实现。

Android电池系统架构 电池 安卓

二)问题

如果是标准方案,按照标准流程,电池系统从驱动到应用层一层层传递,但是 如果电池厂商特别是定制的电池方案,电量和充电状态并不是

比如各种大型机器,汽车、摩托车、电车等,用的绝对不是手机电池方案了,终究原因 电池电压不够。 传统谷歌那套就不合适了,只是流程差不多而已。

三)本文内容讲解

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>&quot;scale&quot; - int, the maximum value for the charge level</p>
 * <p>&quot;level&quot; - int, charge level, from 0 through &quot;scale&quot; inclusive</p>
 * <p>&quot;status&quot; - String, the current charging status.<br />
 * <p>&quot;health&quot; - String, the current battery health.<br />
 * <p>&quot;present&quot; - boolean, true if the battery is present<br />
 * <p>&quot;icon-small&quot; - int, suggested small icon to use for this state</p>
 * <p>&quot;plugged&quot; - 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>&quot;voltage&quot; - int, current battery voltage in millivolts</p>
 * <p>&quot;temperature&quot; - int, current battery temperature in tenths of
 * a degree Centigrade</p>
 * <p>&quot;technology&quot; - 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值