完整Android SPI CAN管理应用程序设计 HAL/Framework配合

张开发
2026/5/18 12:29:30 15 分钟阅读
完整Android SPI CAN管理应用程序设计 HAL/Framework配合
“设计过完整的SPI CAN管理应用框架核心是SocketCAN JNI Framework Service三层架构。与之前RS485不同CAN在Linux内核层已经有完整的SocketCAN协议栈应用层通过标准Socket API就能收发CAN帧。HAL层主要负责CAN接口的创建和配置Framework层封装成系统服务供App调用。”一、整体架构设计含HAL/Framework配合1.1 完整分层架构图┌─────────────────────────────────────────────────────────────────────────────┐ │ 应用层 (App) │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ CanManager (Java) │ │ │ │ getInstance() : CanManager │ │ │ │ openCan(interface, baudRate) : CanDevice │ │ │ │ sendFrame(canId, data) │ │ │ │ registerFrameCallback(callback) │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ Binder │ ├──────────────────────────────────────┼───────────────────────────────────────┤ │ Framework层 (SystemServer) │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ CanManagerService (Java) │ │ │ │ - 权限检查 (android.permission.CAN_CONTROL) │ │ │ │ - 多进程CAN帧分发 │ │ │ │ - CAN接口状态管理 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ JNI │ ├──────────────────────────────────────┼───────────────────────────────────────┤ │ Native层 (C) │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ can_jni_wrapper.cpp │ │ │ │ - socket(PF_CAN, SOCK_RAW, CAN_RAW) // 创建CAN套接字 │ │ │ │ - bind() 绑定到can0接口 │ │ │ │ - sendto() / recvfrom() 收发CAN帧 │ │ │ │ - 使用epoll管理多个CAN接口 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ Netlink │ ├──────────────────────────────────────┼───────────────────────────────────────┤ │ Kernel层 (Linux内核) │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ SocketCAN子系统 │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ CAN_RAW协议 │ │ CAN_BCM协议 │ │ CAN_GW协议 │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ │ │ │ │ ┌───────▼───────┐ │ │ │ │ │ can0网络设备 │ (net_device) │ │ │ │ └───────┬───────┘ │ │ │ │ │ │ │ │ │ ┌───────▼───────┐ │ │ │ │ │ mcp251x驱动 │ (SPI转CAN) │ │ │ │ └───────┬───────┘ │ │ │ │ │ SPI │ │ │ │ ┌───────▼───────┐ │ │ │ │ │ SPI控制器驱动 │ │ │ │ │ └───────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘二、HAL层配合模块厂商适配2.1 内核驱动层已在前一轮详细说明SPI CAN芯片适配需要在Linux内核中添加以下文件kernel/drivers/net/can/spi/ ├── mcp251x.c # MCP251x系列驱动内核已有可能需要修改 ├── mcp251xfd.c # MCP2518FD CAN FD驱动 └── Kconfig / Makefile # 编译配置关键点SocketCAN是Linux内核原生支持的子系统驱动加载后会创建can0网络接口通过ip link set can0 up即可启用。2.2 设备树配置// arch/arm64/boot/dts/qcom/your-board.dts spi0 { status okay; pinctrl-names default; pinctrl-0 spi0_default; can0 { compatible microchip,mcp2515; // 与驱动匹配 reg 0; // SPI片选0 spi-max-frequency 10000000; interrupt-parent gpio; interrupts 38 IRQ_TYPE_LEVEL_LOW; clocks can_clock; clock-frequency 16000000; }; };2.3 内核编译配置# kernel/arch/arm64/configs/your_defconfig CONFIG_CANy CONFIG_CAN_DEVy CONFIG_CAN_RAWy CONFIG_CAN_BCMy CONFIG_CAN_MCP251Xy三、Framework层配合模块系统服务3.1 AIDL接口定义// frameworks/base/core/java/android/can/ICanManager.aidl package android.can; ​ /** * CAN管理服务接口 - 供系统服务和应用调用 * hide */ interface ICanManager { /** * 获取可用的CAN接口列表 */ String[] getAvailableInterfaces(); /** * 打开CAN接口 * param interfaceName 接口名称如can0 * param baudRate 波特率(如250000) * return 文件描述符用于后续通信 */ ParcelFileDescriptor openCanInterface(String interfaceName, int baudRate); /** * 关闭CAN接口 */ void closeCanInterface(String interfaceName); /** * 发送CAN帧 * param interfaceName 接口名称 * param id CAN ID * param data 数据 * param isExtended 是否扩展帧 * param isRemote 是否远程帧 */ void sendCanFrame(String interfaceName, int id, byte[] data, boolean isExtended, boolean isRemote); /** * 注册CAN帧接收回调 */ void registerFrameCallback(String interfaceName, ICanFrameCallback callback); /** * 获取CAN接口状态 */ CanInterfaceStatus getInterfaceStatus(String interfaceName); } ​ /** * CAN帧回调接口 */ oneway interface ICanFrameCallback { void onFrameReceived(int id, byte[] data, boolean isExtended, long timestamp); void onError(int errorCode, String message); } ​ /** * CAN接口状态 */ parcelable CanInterfaceStatus { String name; boolean isUp; int baudRate; int errorCount; int rxPackets; int txPackets; }3.2 系统服务实现// frameworks/base/services/core/java/com/android/server/can/CanManagerService.java package com.android.server.can; ​ import android.can.ICanManager; import android.can.ICanFrameCallback; import android.can.CanInterfaceStatus; import android.content.Context; import android.os.Binder; import android.os.ParcelFileDescriptor; import android.util.Slog; ​ /** * CAN管理系统服务 * * design_pattern Singleton Pattern - 系统唯一实例 * design_pattern Observer Pattern - CAN帧分发 */ public class CanManagerService extends ICanManager.Stub { private static final String TAG CanManagerService; private static final String PERMISSION_CAN android.permission.CAN_CONTROL; private final Context mContext; private final MapString, CanDevice mCanDevices new ConcurrentHashMap(); private final MapString, ListICanFrameCallback mCallbacks new ConcurrentHashMap(); // Native方法 private native long nativeOpenCanInterface(String iface, int baudRate); private native void nativeCloseCanInterface(long nativePtr); private native int nativeSendCanFrame(long nativePtr, int id, byte[] data, boolean isExtended, boolean isRemote); private native CanInterfaceStatus nativeGetStatus(long nativePtr); public CanManagerService(Context context) { mContext context; // 加载Native库 System.loadLibrary(can_jni); // 启动时自动配置已注册的CAN接口 configureCanInterfaces(); } private void configureCanInterfaces() { // 从配置文件读取需要自动启动的CAN接口 String[] interfaces mContext.getResources().getStringArray( R.array.config_can_interfaces); for (String iface : interfaces) { try { openCanInterface(iface, 250000); // 默认250kbps } catch (Exception e) { Slog.e(TAG, Failed to configure iface, e); } } } Override public String[] getAvailableInterfaces() { enforceCanPermission(); return nativeGetAvailableInterfaces(); } Override public ParcelFileDescriptor openCanInterface(String interfaceName, int baudRate) { enforceCanPermission(); // 检查是否已打开 if (mCanDevices.containsKey(interfaceName)) { throw new IllegalStateException(Interface already opened: interfaceName); } // 调用Native打开 long nativePtr nativeOpenCanInterface(interfaceName, baudRate); if (nativePtr 0) { throw new IOException(Failed to open CAN interface: interfaceName); } // 创建ParcelFileDescriptor返回给调用者 // 注意实际实现中需要创建Socket的FD ParcelFileDescriptor pfd createPfdForCanInterface(nativePtr); // 保存设备信息 CanDevice device new CanDevice(interfaceName, nativePtr, baudRate); mCanDevices.put(interfaceName, device); // 启动接收线程 startReceiveThread(interfaceName, nativePtr); Slog.i(TAG, CAN interface opened: interfaceName baudRate bps); return pfd; } Override public void sendCanFrame(String interfaceName, int id, byte[] data, boolean isExtended, boolean isRemote) { enforceCanPermission(); CanDevice device mCanDevices.get(interfaceName); if (device null) { throw new IllegalStateException(Interface not opened: interfaceName); } // 数据长度检查CAN最大8字节CAN FD最大64字节 if (data.length 8 !isCanFdSupported(interfaceName)) { throw new IllegalArgumentException(Data too long for classic CAN); } int ret nativeSendCanFrame(device.nativePtr, id, data, isExtended, isRemote); if (ret 0) { throw new IOException(Failed to send CAN frame); } device.txPackets; } Override public void registerFrameCallback(String interfaceName, ICanFrameCallback callback) { enforceCanPermission(); ListICanFrameCallback list mCallbacks.get(interfaceName); if (list null) { list new CopyOnWriteArrayList(); mCallbacks.put(interfaceName, list); } list.add(callback); } /** * 接收线程 - 从Native层读取CAN帧并分发给回调 */ private void startReceiveThread(String interfaceName, long nativePtr) { new Thread(() - { while (mCanDevices.containsKey(interfaceName)) { CanFrame frame nativeReceiveCanFrame(nativePtr, 1000); // 1秒超时 if (frame ! null) { dispatchFrame(interfaceName, frame); } } }, CAN-Rx- interfaceName).start(); } private void dispatchFrame(String interfaceName, CanFrame frame) { ListICanFrameCallback list mCallbacks.get(interfaceName); if (list null) return; for (ICanFrameCallback callback : list) { try { callback.onFrameReceived(frame.id, frame.data, frame.isExtended, frame.timestamp); } catch (RemoteException e) { Slog.e(TAG, Callback failed, e); } } } private void enforceCanPermission() { if (mContext.checkCallingOrSelfPermission(PERMISSION_CAN) ! PackageManager.PERMISSION_GRANTED) { throw new SecurityException(Requires CAN_CONTROL permission); } } private static class CanDevice { final String name; final long nativePtr; final int baudRate; long rxPackets; long txPackets; CanDevice(String name, long nativePtr, int baudRate) { this.name name; this.nativePtr nativePtr; this.baudRate baudRate; } } private static class CanFrame { int id; byte[] data; boolean isExtended; long timestamp; } // Native方法声明 private native String[] nativeGetAvailableInterfaces(); private native CanFrame nativeReceiveCanFrame(long nativePtr, int timeoutMs); }3.3 系统服务注册// frameworks/base/services/java/com/android/server/SystemServer.java private void startOtherServices() { // ... 其他服务 // 启动CAN管理服务 if (SystemProperties.getBoolean(ro.config.can.enable, false)) { try { Slog.i(TAG, Starting CanManagerService); CanManagerService canService new CanManagerService(context); ServiceManager.addService(Context.CAN_SERVICE, canService); } catch (Throwable e) { Slog.e(TAG, Failure starting CanManagerService, e); } } }四、Native层JNI实现4.1 JNI封装// frameworks/base/services/core/jni/com_android_server_can_CanManagerService.cpp #include jni.h #include nativehelper/JNIHelp.h #include android_runtime/AndroidRuntime.h ​ #include stdio.h #include stdlib.h #include string.h #include unistd.h #include fcntl.h #include sys/socket.h #include sys/ioctl.h #include net/if.h #include linux/can.h #include linux/can/raw.h ​ #define LOG_TAG CanManagerServiceJNI #include utils/Log.h ​ /** * CAN设备上下文 */ struct CanContext { int sock; // CAN套接字 struct sockaddr_can addr; // 地址 struct ifreq ifr; // 接口请求 char ifname[IFNAMSIZ]; // 接口名称 bool running; }; ​ /** * 打开CAN接口 */ static jlong nativeOpenCanInterface(JNIEnv* env, jobject thiz, jstring jIface, jint baudRate) { const char* ifname env-GetStringUTFChars(jIface, NULL); CanContext* ctx new CanContext(); strcpy(ctx-ifname, ifname); // 1. 创建CAN套接字 ctx-sock socket(PF_CAN, SOCK_RAW, CAN_RAW); if (ctx-sock 0) { ALOGE(Failed to create CAN socket: %s, strerror(errno)); delete ctx; return 0; } // 2. 绑定到指定接口 strcpy(ctx-ifr.ifr_name, ifname); if (ioctl(ctx-sock, SIOCGIFINDEX, ctx-ifr) 0) { ALOGE(Failed to get interface index: %s, strerror(errno)); close(ctx-sock); delete ctx; return 0; } ctx-addr.can_family AF_CAN; ctx-addr.can_ifindex ctx-ifr.ifr_ifindex; if (bind(ctx-sock, (struct sockaddr*)ctx-addr, sizeof(ctx-addr)) 0) { ALOGE(Failed to bind CAN socket: %s, strerror(errno)); close(ctx-sock); delete ctx; return 0; } // 3. 设置CAN过滤器可选这里允许所有帧 struct can_filter filter; filter.can_id 0; filter.can_mask 0; setsockopt(ctx-sock, SOL_CAN_RAW, CAN_RAW_FILTER, filter, sizeof(filter)); // 4. 配置波特率通过ip命令或ioctl // 注意实际配置需要通过netlink或system()调用ip命令 char cmd[128]; snprintf(cmd, sizeof(cmd), ip link set %s down, ifname); system(cmd); snprintf(cmd, sizeof(cmd), ip link set %s up type can bitrate %d, ifname, baudRate); system(cmd); ALOGI(CAN interface %s opened with baudrate %d, ifname, baudRate); env-ReleaseStringUTFChars(jIface, ifname); return reinterpret_castjlong(ctx); } ​ /** * 发送CAN帧 */ static jint nativeSendCanFrame(JNIEnv* env, jobject thiz, jlong nativePtr, jint id, jbyteArray jData, jboolean isExtended, jboolean isRemote) { CanContext* ctx reinterpret_castCanContext*(nativePtr); if (!ctx || ctx-sock 0) return -1; struct can_frame frame; memset(frame, 0, sizeof(frame)); // 设置CAN ID frame.can_id id; if (isExtended) { frame.can_id | CAN_EFF_FLAG; } if (isRemote) { frame.can_id | CAN_RTR_FLAG; } // 设置数据长度和内容 jsize len env-GetArrayLength(jData); frame.can_dlc (len 8) ? 8 : len; jbyte* data env-GetByteArrayElements(jData, NULL); memcpy(frame.data, data, frame.can_dlc); env-ReleaseByteArrayElements(jData, data, JNI_ABORT); // 发送 int ret write(ctx-sock, frame, sizeof(frame)); if (ret 0) { ALOGE(Failed to send CAN frame: %s, strerror(errno)); return -1; } return 0; } ​ /** * 接收CAN帧 */ static jobject nativeReceiveCanFrame(JNIEnv* env, jobject thiz, jlong nativePtr, jint timeoutMs) { CanContext* ctx reinterpret_castCanContext*(nativePtr); if (!ctx || ctx-sock 0) return NULL; // 设置接收超时 struct timeval tv; tv.tv_sec timeoutMs / 1000; tv.tv_usec (timeoutMs % 1000) * 1000; setsockopt(ctx-sock, SOL_SOCKET, SO_RCVTIMEO, tv, sizeof(tv)); struct can_frame frame; int ret read(ctx-sock, frame, sizeof(frame)); if (ret 0) { if (errno ! EAGAIN errno ! EWOULDBLOCK) { ALOGE(Failed to receive CAN frame: %s, strerror(errno)); } return NULL; } // 构建Java CanFrame对象 jclass clazz env-FindClass(android/can/CanManagerService$CanFrame); if (!clazz) return NULL; jmethodID constructor env-GetMethodID(clazz, init, ()V); jobject obj env-NewObject(clazz, constructor); // 设置字段 jfieldID idField env-GetFieldID(clazz, id, I); env-SetIntField(obj, idField, frame.can_id CAN_EFF_MASK); jfieldID isExtendedField env-GetFieldID(clazz, isExtended, Z); env-SetBooleanField(obj, isExtendedField, (frame.can_id CAN_EFF_FLAG) ! 0); jfieldID dataField env-GetFieldID(clazz, data, [B); jbyteArray dataArr env-NewByteArray(frame.can_dlc); env-SetByteArrayRegion(dataArr, 0, frame.can_dlc, reinterpret_castjbyte*(frame.data)); env-SetObjectField(obj, dataField, dataArr); jfieldID timestampField env-GetFieldID(clazz, timestamp, J); env-SetLongField(obj, timestampField, std::chrono::duration_caststd::chrono::milliseconds( std::chrono::system_clock::now().time_since_epoch() ).count()); return obj; } ​ /** * JNI方法注册 */ static const JNINativeMethod gMethods[] { { nativeOpenCanInterface, (Ljava/lang/String;I)J, (void*)nativeOpenCanInterface }, { nativeSendCanFrame, (JI[BZZ)I, (void*)nativeSendCanFrame }, { nativeReceiveCanFrame, (JI)Landroid/can/CanManagerService$CanFrame;, (void*)nativeReceiveCanFrame }, }; ​ int register_com_android_server_can_CanManagerService(JNIEnv* env) { return jniRegisterNativeMethods(env, com/android/server/can/CanManagerService, gMethods, NELEM(gMethods)); }五、应用层完整设计5.1 CanManager类应用层门面/** * CanManager - CAN管理应用层入口 * * design_pattern Facade Pattern - 简化CAN操作 * design_pattern Singleton Pattern - 全局唯一 */ public class CanManager { private static final String TAG CanManager; private static volatile CanManager sInstance; private Context mContext; private ICanManager mService; private final MapString, CanDevice mDevices new ConcurrentHashMap(); private CanManager() {} public static CanManager getInstance() { if (sInstance null) { synchronized (CanManager.class) { if (sInstance null) { sInstance new CanManager(); } } } return sInstance; } /** * 初始化 */ public void init(Context context) { mContext context.getApplicationContext(); // 绑定系统服务 IBinder binder ServiceManager.getService(Context.CAN_SERVICE); if (binder ! null) { mService ICanManager.Stub.asInterface(binder); } else { Log.e(TAG, CAN service not available); } } /** * 获取可用CAN接口 */ public ListString getAvailableInterfaces() { try { return Arrays.asList(mService.getAvailableInterfaces()); } catch (RemoteException e) { Log.e(TAG, Failed to get interfaces, e); return Collections.emptyList(); } } /** * 打开CAN设备 */ public CanDevice openDevice(String interfaceName, int baudRate, CanFrameCallback callback) { try { // 打开CAN接口 ParcelFileDescriptor pfd mService.openCanInterface(interfaceName, baudRate); // 注册回调 mService.registerFrameCallback(interfaceName, new ICanFrameCallback.Stub() { Override public void onFrameReceived(int id, byte[] data, boolean isExtended, long timestamp) { if (callback ! null) { CanFrame frame new CanFrame(id, data, isExtended, timestamp); callback.onFrameReceived(frame); } } Override public void onError(int errorCode, String message) { if (callback ! null) { callback.onError(new IOException(message)); } } }); CanDevice device new CanDevice(interfaceName, pfd, baudRate, mService); mDevices.put(interfaceName, device); return device; } catch (Exception e) { Log.e(TAG, Failed to open CAN device, e); return null; } } /** * 关闭CAN设备 */ public void closeDevice(String interfaceName) { CanDevice device mDevices.remove(interfaceName); if (device ! null) { device.close(); } } } ​ /** * CanDevice - CAN设备封装 */ public class CanDevice implements Closeable { private final String mInterfaceName; private final ParcelFileDescriptor mPfd; private final ICanManager mService; private final int mBaudRate; private boolean mIsOpen true; // 接收线程 private Thread mReceiveThread; private volatile boolean mRunning false; public CanDevice(String name, ParcelFileDescriptor pfd, int baudRate, ICanManager service) { this.mInterfaceName name; this.mPfd pfd; this.mBaudRate baudRate; this.mService service; } /** * 发送CAN帧 */ public void send(int id, byte[] data) throws IOException { send(id, data, false, false); } /** * 发送扩展帧 */ public void sendExtended(int id, byte[] data) throws IOException { send(id, data, true, false); } /** * 发送远程帧 */ public void sendRemote(int id) throws IOException { send(id, new byte[0], false, true); } private void send(int id, byte[] data, boolean isExtended, boolean isRemote) throws IOException { if (!mIsOpen) { throw new IOException(Device already closed); } try { mService.sendCanFrame(mInterfaceName, id, data, isExtended, isRemote); } catch (RemoteException e) { throw new IOException(e); } } /** * 获取接口状态 */ public CanInterfaceStatus getStatus() { try { return mService.getInterfaceStatus(mInterfaceName); } catch (RemoteException e) { return null; } } Override public void close() { mIsOpen false; mRunning false; try { mService.closeCanInterface(mInterfaceName); mPfd.close(); } catch (Exception e) { Log.e(CanDevice, Error closing, e); } } public String getInterfaceName() { return mInterfaceName; } public int getBaudRate() { return mBaudRate; } public boolean isOpen() { return mIsOpen; } } ​ /** * CAN帧回调接口 */ public interface CanFrameCallback { void onFrameReceived(CanFrame frame); void onError(Exception e); } ​ /** * CAN帧数据模型 */ public class CanFrame { private final int id; private final byte[] data; private final boolean isExtended; private final long timestamp; public CanFrame(int id, byte[] data, boolean isExtended, long timestamp) { this.id id; this.data data; this.isExtended isExtended; this.timestamp timestamp; } public int getId() { return id; } public byte[] getData() { return data; } public boolean isExtended() { return isExtended; } public long getTimestamp() { return timestamp; } /** * 获取数据长度 */ public int getDlc() { return data.length; } /** * 将ID格式化为十六进制 */ public String getIdHex() { if (isExtended) { return String.format(%08X, id); } return String.format(%03X, id); } /** * 将数据格式化为十六进制字符串 */ public String getDataHex() { StringBuilder sb new StringBuilder(); for (byte b : data) { sb.append(String.format(%02X , b)); } return sb.toString().trim(); } }5.2 CAN测试Activity/** * CAN测试界面 * 参考野火Android综合测试应用的CanTestActivity实现 */ public class CanTestActivity extends AppCompatActivity implements CanFrameCallback { private CanManager mCanManager; private CanDevice mCanDevice; // UI组件 private Spinner mInterfaceSpinner; private EditText mBaudRateEdit; private Button mOpenButton; private EditText mCanIdEdit; private EditText mDataEdit; private Button mSendButton; private Button mReceiveButton; private TextView mReceiveArea; private TextView mStatusView; private final ListString mReceivedMessages new ArrayList(); private ArrayAdapterString mMessageAdapter; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_can_test); initViews(); // 初始化CAN管理器 mCanManager CanManager.getInstance(); mCanManager.init(this); // 加载可用CAN接口 loadCanInterfaces(); } private void initViews() { mInterfaceSpinner findViewById(R.id.spinner_can_interface); mBaudRateEdit findViewById(R.id.et_baud_rate); mOpenButton findViewById(R.id.btn_open_can); mCanIdEdit findViewById(R.id.et_can_id); mDataEdit findViewById(R.id.et_can_data); mSendButton findViewById(R.id.btn_send); mReceiveButton findViewById(R.id.btn_receive); mReceiveArea findViewById(R.id.tv_receive_area); mStatusView findViewById(R.id.tv_status); // 设置默认值 mBaudRateEdit.setText(250000); mOpenButton.setOnClickListener(v - openCanDevice()); mSendButton.setOnClickListener(v - sendCanFrame()); mReceiveButton.setOnClickListener(v - startReceiving()); // 消息列表适配器 mMessageAdapter new ArrayAdapter(this, android.R.layout.simple_list_item_1, mReceivedMessages); ((ListView) findViewById(R.id.lv_messages)).setAdapter(mMessageAdapter); } private void loadCanInterfaces() { ListString interfaces mCanManager.getAvailableInterfaces(); ArrayAdapterString adapter new ArrayAdapter(this, android.R.layout.simple_spinner_item, interfaces); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mInterfaceSpinner.setAdapter(adapter); } private void openCanDevice() { String iface (String) mInterfaceSpinner.getSelectedItem(); if (iface null) { Toast.makeText(this, 请选择CAN接口, Toast.LENGTH_SHORT).show(); return; } int baudRate; try { baudRate Integer.parseInt(mBaudRateEdit.getText().toString()); } catch (NumberFormatException e) { Toast.makeText(this, 无效的波特率, Toast.LENGTH_SHORT).show(); return; } // 关闭已有设备 if (mCanDevice ! null) { mCanManager.closeDevice(mCanDevice.getInterfaceName()); } // 打开新设备 mCanDevice mCanManager.openDevice(iface, baudRate, this); if (mCanDevice null) { Toast.makeText(this, 打开CAN设备失败, Toast.LENGTH_SHORT).show(); return; } updateStatus(); Toast.makeText(this, 已打开 iface baudRate bps, Toast.LENGTH_SHORT).show(); } private void sendCanFrame() { if (mCanDevice null || !mCanDevice.isOpen()) { Toast.makeText(this, 请先打开CAN设备, Toast.LENGTH_SHORT).show(); return; } try { // 解析CAN ID String idStr mCanIdEdit.getText().toString(); int canId parseCanId(idStr); // 解析数据 String dataStr mDataEdit.getText().toString(); byte[] data parseCanData(dataStr); // 判断是否扩展帧 boolean isExtended idStr.length() 3; // 发送 if (isExtended) { mCanDevice.sendExtended(canId, data); } else { mCanDevice.send(canId, data); } // 显示发送信息 addMessage(发送: ID idStr DATA dataStr); } catch (Exception e) { Toast.makeText(this, 发送失败: e.getMessage(), Toast.LENGTH_SHORT).show(); } } private void startReceiving() { if (mCanDevice null || !mCanDevice.isOpen()) { Toast.makeText(this, 请先打开CAN设备, Toast.LENGTH_SHORT).show(); return; } addMessage(开始接收CAN数据...); // 接收是通过回调异步进行的这里只需要启用接收即可 Toast.makeText(this, CAN接收已启动, Toast.LENGTH_SHORT).show(); } Override public void onFrameReceived(CanFrame frame) { runOnUiThread(() - { String message String.format(收到: ID%s DLC%d DATA%s, frame.getIdHex(), frame.getDlc(), frame.getDataHex()); addMessage(message); }); } Override public void onError(Exception e) { runOnUiThread(() - { addMessage(错误: e.getMessage()); Toast.makeText(this, CAN错误: e.getMessage(), Toast.LENGTH_SHORT).show(); }); } private void updateStatus() { if (mCanDevice ! null mCanDevice.isOpen()) { CanInterfaceStatus status mCanDevice.getStatus(); if (status ! null) { String text String.format(接口: %s | 状态: %s | 波特率: %d | RX: %d | TX: %d, status.name, status.isUp ? UP : DOWN, status.baudRate, status.rxPackets, status.txPackets); mStatusView.setText(text); } } } private int parseCanId(String idStr) { if (idStr.startsWith(0x) || idStr.startsWith(0X)) { return Integer.parseInt(idStr.substring(2), 16); } return Integer.parseInt(idStr, 16); } private byte[] parseCanData(String dataStr) { dataStr dataStr.replace( , ); if (dataStr.isEmpty()) return new byte[0]; byte[] data new byte[dataStr.length() / 2]; for (int i 0; i data.length; i) { String hex dataStr.substring(i * 2, i * 2 2); data[i] (byte) Integer.parseInt(hex, 16); } return data; } private void addMessage(String message) { mReceivedMessages.add(0, new SimpleDateFormat(HH:mm:ss.SSS).format(new Date()) message); if (mReceivedMessages.size() 100) { mReceivedMessages.remove(mReceivedMessages.size() - 1); } mMessageAdapter.notifyDataSetChanged(); // 自动滚动到顶部 ListView lv findViewById(R.id.lv_messages); lv.smoothScrollToPosition(0); } Override protected void onDestroy() { super.onDestroy(); if (mCanDevice ! null) { mCanManager.closeDevice(mCanDevice.getInterfaceName()); } } }六、需要添加的文件清单6.1 HAL层驱动适配文件路径说明kernel/drivers/net/can/spi/mcp251x.cMCP251x驱动已有可能需要修改kernel/arch/arm64/boot/dts/.../your-board.dts设备树配置kernel/arch/arm64/configs/your_defconfig内核配置6.2 Framework层系统服务文件路径说明frameworks/base/core/java/android/can/ICanManager.aidlAIDL接口定义frameworks/base/core/java/android/can/CanInterfaceStatus.aidl状态Parcelableframeworks/base/core/java/android/can/ICanFrameCallback.aidl回调接口frameworks/base/services/core/java/com/android/server/can/CanManagerService.java服务实现frameworks/base/services/core/jni/com_android_server_can_CanManagerService.cppJNI实现frameworks/base/services/core/jni/Android.mkJNI编译配置frameworks/base/services/java/com/android/server/SystemServer.java服务注册修改frameworks/base/core/res/res/values/config.xml添加CAN接口配置数组frameworks/base/core/res/AndroidManifest.xml添加权限定义6.3 应用层App文件路径说明packages/apps/CanTest/src/.../CanManager.java应用层管理器packages/apps/CanTest/src/.../CanDevice.javaCAN设备封装packages/apps/CanTest/src/.../CanTestActivity.java测试界面packages/apps/CanTest/src/.../CanFrame.java数据模型packages/apps/CanTest/res/layout/activity_can_test.xml布局文件packages/apps/CanTest/AndroidManifest.xml应用清单七、权限与SELinux配置7.1 权限定义!-- frameworks/base/core/res/AndroidManifest.xml -- permission android:nameandroid.permission.CAN_CONTROL android:protectionLevelsignature|privileged android:labelstring/permission_can_control android:descriptionstring/permission_can_control_desc/7.2 SELinux策略# device/vendor/device/sepolicy/can.te type can_device, dev_type; type can_socket, socket_class; ​ # 允许系统服务访问CAN allow system_server can_device:chr_file { read write open ioctl }; allow system_server can_socket:socket { create bind connect read write }; ​ # 允许CAN服务 type can_service, domain; allow can_service can_device:chr_file rw_file_perms; allow can_service self:capability { net_admin net_raw }; ​ # 允许应用通过Binder调用CAN服务 binder_call(can_service, system_server) allow can_service system_server:fifo_file write;八、设计模式总结设计模式应用位置作用单例模式CanManager全局唯一CAN管理实例门面模式CanManager简化CAN操作接口代理模式CanDevice代理底层CAN操作观察者模式ICanFrameCallbackCAN帧异步通知工厂模式CanManager.openDevice()创建CAN设备实例建造者模式CanFrameCAN帧构建总结“总结一下SPI CAN管理应用的完整架构涉及三层配合1. HAL层内核驱动实现spi_driver和net_device_ops通过register_candev注册到SocketCAN子系统设备树配置SPI总线参数和中断引脚2. Framework层系统服务AIDL接口ICanManager定义服务契约CanManagerService运行在system_server持有CAN_CONTROL权限JNI封装通过Socket CAN API与内核交互服务注册在SystemServer中添加到ServiceManager3. 应用层AppCanManager门面模式简化调用CanDevice封装设备操作回调机制异步接收CAN帧这套架构与RS485的最大区别是CAN使用Linux标准的SocketCAN子系统应用层通过Socket API通信而RS485需要手动控制方向切换。HAL层配置好can0接口后上层使用方式与UDP Socket几乎一样。”

更多文章