package com.isa.navi.jni.hmi;
import android.content.Context;
import android.os.Message;
import android.os.StatFs;
import android.util.Log;
import androidx.annotation.NonNull;
import com.isa.navi.jni.hmi.bean.UpdateList;
import com.isa.navi.jni.hmi.update.USBListener;
import com.isa.navi.library.map.UpdateEnvironment;
import com.isa.navi.library.map.UpdateInfoBean;
import com.isa.navi.manager.CallBackManager;
import com.isa.navi.manager.CarManager;
import com.isa.navi.manager.base.BaseHandlerManager;
import com.isa.navi.receiver.USBReceiver;
import com.isa.navi.utils.GsonUtils;
import com.isa.navi.utils.LogUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.time.Month;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class HmiJNIImpl extends BaseHandlerManager implements IHmiJNI {
private static final String TAG = "UPDATE";
private static final int MSG_DOWNLOAD_STATUS_CHANGE = 1;
private final HmiProxyJNI mHmiProxyJNI;
private final Format mFormat;
private static final String PASSWORD_PREFIX = "DfCs";
private static final String PASSWORD_SUFFIX = "#@";
private static final int LAST_CHARS_LENGTH = 4;
private static String updateUSBpath = "";
/**
* 车辆管理实例,用于管理车辆相关的功能。
*/
private CarManager mCarManager;
private volatile static HmiJNIImpl mInstance;
private boolean is_run = false;
private final Set<String> USBPathSet = Collections.synchronizedSet(new HashSet<>());
public static HmiJNIImpl getInstance() {
if (mInstance == null) {
synchronized (HmiJNIImpl.class) {
if (mInstance == null) {
mInstance = new HmiJNIImpl();
}
}
}
return mInstance;
}
private HmiJNIImpl() {
super(true);
mHmiProxyJNI = new HmiProxyJNI();
mFormat = Format.getInstance();
mCarManager = CarManager.getInstance();
}
@Override
public void initialize() {
super.initialize();
LogUtils.d(TAG, "initialize : ");
DfCert.getInstance().getService();
CallBackManager.getInstance().registerUSBEventChangeListener(new USBListener() {
@Override
public void path(String mountPath) {
USBPathSet.add(mountPath);
LogUtils.i(TAG,"USBPathSet:" + mountPath);
}
});
// 初始化C++模块
mHmiProxyJNI.InitializeCpp();
InstanceEngine();
//----------------TEST-START-zwx
UpdateTest();
}
public void UpdateTest() {
Thread UpdateTest = new Thread(new Runnable(){
@Override
public void run() {
LogUtils.i(TAG, "UpdateTest start!");
setUSBPath("/data/maptest/KVM_EU_202502_T1.0_S31N.zip");
updateMap();
}
});
UpdateTest.setName("ISA_NAVI_UpdateTest");
UpdateTest.start();
}
//----------------TEST-END-zwx
private int[] getCurrentYearAndQuarter() {
int[] result = new int[2];
LocalDate currentDate = LocalDate.now();
// 获取当前年份
int currentYear = currentDate.getYear();
// 获取当前月份
Month currentMonth = currentDate.getMonth();
// 根据月份确定当前季度
int currentQuarter;
switch (currentMonth) {
case JANUARY: case FEBRUARY: case MARCH:
currentQuarter = 1;
break;
case APRIL: case MAY: case JUNE:
currentQuarter = 2;
break;
case JULY: case AUGUST: case SEPTEMBER:
currentQuarter = 3;
break;
case OCTOBER: case NOVEMBER: case DECEMBER:
currentQuarter = 4;
break;
default:
currentQuarter = 0;
}
result[0] = currentYear;
result[1] = currentQuarter;
return result;
}
private int[] parseMapInfo(String mapInfo) {
int[] result = new int[2];
// 检查输入字符串的长度是否为6(4位年份 + 2位季度)
if (mapInfo == null || mapInfo.length() != 6) {
LogUtils.e(TAG, "Input string must be in the format YYYYMM where YYYY is the year and MM is the quarter as a two-digit number.");
return result;
}
// 提取年份(前4位)
String yearStr = mapInfo.substring(0, 4);
result[0] = Integer.parseInt(yearStr);
// 提取季度(后2位),并验证它在1到4的范围内
String quarterStr = mapInfo.substring(4, 6);
int quarter = Integer.parseInt(quarterStr);
if (quarter < 1 || quarter > 4) {
LogUtils.e(TAG, "Quarter must be between 1 and 4.");
return result;
}
result[1] = quarter;
return result;
}
public void dataVersionCheck() {
Thread dataVersionCheckThread = new Thread(new Runnable() {
@Override
public void run() {
try {
UpdateInfoBean bean = new UpdateInfoBean();
String localVersion = mHmiProxyJNI.getMapInfo();
if(localVersion.isEmpty()) {
bean.setUpdateStatus(4);
handleDownloadStatusChange(bean);
LogUtils.i(TAG, "Detected no updates for a long time");
return;
}
int[] currentYearAndQuarter = getCurrentYearAndQuarter();
int[] localYearAndQuarter = parseMapInfo(localVersion);
int yearDifference = currentYearAndQuarter[0] - localYearAndQuarter[0];
int QuarterDifference = currentYearAndQuarter[1] - localYearAndQuarter[1];
if(yearDifference * 4 + QuarterDifference > 3) {
bean.setUpdateStatus(4);
handleDownloadStatusChange(bean);
LogUtils.i(TAG, "Detected no updates for a long time");
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
// 设置线程名称
dataVersionCheckThread.setName("ISA_NAVI_dataVersionCheck");
// 启动线程
dataVersionCheckThread.start();
}
public void setContext(Context context) {
if(mContext == null) {
mContext = context;
}
language.getInstance().setContext(mContext);
}
@Override
protected void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DOWNLOAD_STATUS_CHANGE:
handleDownloadStatusChange((String) msg.obj);
break;
default:
break;
}
}
private List<String> JsonArrayToListExample(JSONArray jsonArray) {
List<String> stringList = new ArrayList<>();
try {
// 遍历JSONArray
for (int i = 0; i < jsonArray.length(); i++) {
// 由于我们假设JSONArray只包含字符串,所以我们可以直接getString
String item = jsonArray.getString(i);
// 将字符串添加到列表中
stringList.add(item);
}
} catch (JSONException e) {
e.printStackTrace();
}
return stringList;
}
private UpdateInfoBean jsonToUpdateInfoBean(String json) {
UpdateInfoBean result = null;
try {
// 解析整个JSON字符串为一个JSONObject
JSONObject rootObject = new JSONObject(json);
// 解析mUpdateMessage对象
JSONObject mUpdateMessage = rootObject.getJSONObject("mUpdateMessage");
String country = mUpdateMessage.getString("country");
double downloadSize = mUpdateMessage.getDouble("downloadSize");
boolean isCurrentCountry = mUpdateMessage.getBoolean("isCurrentCountry");
double progress = mUpdateMessage.getDouble("progress");
double totalSize = mUpdateMessage.getDouble("totalSize");
int updateStatus = mUpdateMessage.getInt("updateStatus");
String version = mUpdateMessage.getString("version");
// 解析mUpdateNotification对象
JSONObject mUpdateNotification = rootObject.getJSONObject("mUpdateNotification");
JSONArray updateException = mUpdateNotification.getJSONArray("UpdateException");
JSONArray updateFailedList = mUpdateNotification.getJSONArray("UpdateFailedList");
boolean updateResult = mUpdateNotification.getBoolean("UpdateResult");
JSONArray updateSuccessfulList = mUpdateNotification.getJSONArray("UpdateSuccessfulList");
// 解析mapStatus
int mapStatus = rootObject.getInt("mapStatus");
UpdateInfoBean.UpdateNotification temp1 = new UpdateInfoBean.UpdateNotification(updateResult,JsonArrayToListExample(updateException),JsonArrayToListExample(updateSuccessfulList),JsonArrayToListExample(updateFailedList));
UpdateInfoBean.UpdateMessage temp2 = new UpdateInfoBean.UpdateMessage(country, version, totalSize, (int) progress, isCurrentCountry);
temp2.setUpdateStatus(updateStatus);
temp2.setDownloadSize(downloadSize);
result = new UpdateInfoBean(mapStatus, temp1, temp2);
} catch (Exception e) {
LogUtils.e(TAG, "JSON parsing failed:" + e.toString());
}
return result;
}
private void handleDownloadStatusChange(String json) {
UpdateInfoBean bean = jsonToUpdateInfoBean(json);
LogUtils.i(TAG, "handleDownloadStatusChange : " + bean);
if (bean != null) {
// 仅当status=8时检查is_run,其他状态直接放行update
if(bean.getUpdateStatus() != 8 || is_run) {
List<UpdateInfoBean> list = new ArrayList<>();
list.add(bean);
CallBackManager.getInstance().notifyUpdateStatus(list);
}
}
}
private void handleDownloadStatusChange(UpdateInfoBean bean) {
LogUtils.i(TAG, "handleDownloadStatusChange : " + bean);
if (bean != null) {
// 仅当status=8时检查is_run,其他状态直接放行
if(bean.getUpdateStatus() != 8 || is_run) {
List<UpdateInfoBean> list = new ArrayList<>();
list.add(bean);
CallBackManager.getInstance().notifyUpdateStatus(list);
}
} else {
LogUtils.e(TAG, "UpdateInfoBean is null!");
}
}
public void sendUpdateMessage(String json) {
LogUtils.d(TAG, "sendUpdateMessage : " + json);
Message message = Message.obtain();
message.what = MSG_DOWNLOAD_STATUS_CHANGE;
message.obj = json;
mHandler.sendMessage(message);
}
@Override
public void updateMap() {
LogUtils.d(TAG, "activeUpdateStart : ");
// 检查更新过程是否已经在运行,防止重复创建线程
if(is_run) {
LogUtils.i(TAG, "更新程序已经在进行");
return;
}else {
is_run = true;
}
// 创建一个新线程来执行主动更新
Thread updateMapThread = new Thread(new Runnable() {
@Override
public void run() {
try {
// 调用JNI方法开始主动更新
mHmiProxyJNI.updateMap();
LogUtils.d(TAG, "activeUpdateStart end: ");
} catch (Exception e) {
e.printStackTrace();
}
}
});
// 设置线程名称
updateMapThread.setName("ISA_NAVI_UpdateMapThread");
// 启动线程
updateMapThread.start();
}
public void updateMapend() {
is_run = false;
}
@Override
public void suspendUpdate(boolean suspend) {
mHmiProxyJNI.suspendUpdate(suspend);
}
@Override
public void cancelUpdate() {
mHmiProxyJNI.cancelUpdate();
}
/**
* 启动USB检测线程,用于检测插入的U盘并判断是否有可用的升级文件。
*/
public void startUSBDetection() {
LogUtils.i(TAG, "startUSBDetection START");
// setUSBPath("/data/KVM_EU_202404_T1.2.zip");
Thread USBDetectionThread = new Thread(new Runnable(){
@Override
public void run() {
UpdateInfoBean bean = new UpdateInfoBean();
boolean isHaveUSBData = false;
//遍历检测到的U盘路径
if(USBPathSet.isEmpty()) {
//未检测到U盘,触发提示弹窗
LogUtils.e(TAG, "No USB drive detected!");
bean.setUpdateStatus(2);
handleDownloadStatusChange(bean);
return;
}
int set_num = 0;
for(String usbPath : USBPathSet ) {
//判断U盘路径是否真实存在
set_num ++;
if(USBReceiver.isMounted(usbPath)) {
File usbFile = new File(usbPath);
File[] listFiles = usbFile.listFiles();
if(listFiles == null) {
continue;
}
File newfile = null;
for(File file : listFiles) {
//判断U盘内是否含有升级文件
if(!file.isDirectory() && mFormat.isUpgradeFileNamingFormat(file.getName())) {
isHaveUSBData = true;
if(newfile == null) {
newfile = file;
} else {
if(Format.getInstance().isNewerVersion(file.getName().replaceAll("\\.\\w+$", ""), newfile.getName().replaceAll("\\.\\w+$", ""))) {
newfile = file;
}
}
}
}
//未检测到U盘指定路径下是否有数据,触发提示弹窗
if(!isHaveUSBData || newfile == null) {
LogUtils.e(TAG, "The USB drive does not contain an upgrade package!");
bean.setUpdateStatus(3);
handleDownloadStatusChange(bean);
return;
}else{
// 从文件名中提取版本号
String localDataPath = Format.getInstance().extractVersion(getCountryDbPath());
LogUtils.i(TAG, "file name:" + newfile.getName());
LogUtils.i(TAG, "localDataPath:" + localDataPath);
if(localDataPath.isEmpty()) {
String mapinfo = getMapInfo();
if(!mapinfo.isEmpty()) {
String[] newVersionParts = new String[2];
Format.getInstance().extractVersion(newfile.getName().replaceAll("\\.\\w+$", ""), newVersionParts);
String newUpdateVersion = newVersionParts[0];
String newReleasesVersion = newVersionParts[1];
LogUtils.i(TAG, "localDataPath.isEmpty() newUpdateVersion: " + newUpdateVersion);
LogUtils.i(TAG, "localDataPath.isEmpty() newReleasesVersion: " + newReleasesVersion);
if(Integer.parseInt(newUpdateVersion) > Integer.parseInt(mapinfo) || (Integer.parseInt(newUpdateVersion) == Integer.parseInt(mapinfo) && Double.parseDouble(newReleasesVersion) > 1)) {
LogUtils.i(TAG,"Trigger USB drive update!");
bean.setUpdateStatus(0);
handleDownloadStatusChange(bean);
//设置U盘更新路径
updateUSBpath = usbPath;
setUSBPath(newfile.getAbsolutePath());
LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath());
} else {
LogUtils.e(TAG, "The upgrade package version on the USB drive is less than or equal to the vehicle's version!");
bean.setUpdateStatus(1);
handleDownloadStatusChange(bean);
}
} else {
LogUtils.i(TAG,"Trigger USB drive update!");
bean.setUpdateStatus(0);
handleDownloadStatusChange(bean);
//设置U盘更新路径
updateUSBpath = usbPath;
setUSBPath(newfile.getAbsolutePath());
LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath());
}
return;
}
// 判断U盘内数据版本是否大于车机数据版本
if(Format.getInstance().isNewerVersion(newfile.getName().replaceAll("\\.\\w+$", ""), localDataPath)) {
LogUtils.i(TAG,"Trigger USB drive update!");
bean.setUpdateStatus(0);
handleDownloadStatusChange(bean);
//设置U盘更新路径
updateUSBpath = usbPath;
setUSBPath(newfile.getAbsolutePath());
LogUtils.i(TAG, "filePath:" + newfile.getAbsolutePath());
} else {
LogUtils.e(TAG, "The upgrade package version on the USB drive is less than or equal to the vehicle's version!");
bean.setUpdateStatus(1);
handleDownloadStatusChange(bean);
}
return;
}
} else {
if(set_num == USBPathSet.size()) {
//未检测到U盘,触发提示弹窗
LogUtils.e(TAG, "No USB drive detected!");
bean.setUpdateStatus(2);
handleDownloadStatusChange(bean);
return;
}
}
}
}
});
USBDetectionThread.setName("ISA_NAVI_USBDetectionThread");
USBDetectionThread.start();
}
public String getMapInfo() {
String result = mHmiProxyJNI.getMapInfo();
LogUtils.i(TAG,"MapInfo:" + result);
return result;
}
public String getUpdateVersion() {
String result = mHmiProxyJNI.getUpdateVersion();
LogUtils.i(TAG,"UpdateVersion:" + result);
return result;
}
public UpdateEnvironment checkUpdateEnvironment() {
String json = mHmiProxyJNI.checkUpdateEnvironment();
LogUtils.d(TAG,"checkUpdateEnvironment JSON:" + json);
UpdateEnvironment result = GsonUtils.fromJson(json, UpdateEnvironment.class);
if(result == null) {
LogUtils.e(TAG, "GsonUtils.fromJson(json, UpdateEnvironment.class) Fail!");
return null;
}
if (!result.getResult()) {
List<String> temp_list = new ArrayList<>();
for (int i : result.getInt_reason()) {
String str = language.getInstance().getEnvironment(i);
if(str != null && !str.isEmpty()) {
temp_list.add(str);
}
}
// 更新temp对象中的reason字段
result.setReason(temp_list);
}
LogUtils.i(TAG,"checkUpdateEnvironment :" + result);
return result;
}
public String getUpdatePurpose() {
return language.getInstance().getUpdatePurpose();
}
public String getUpdateContent() {
return language.getInstance().getUpdateContent();
}
public String getUserManual() {
return language.getInstance().getUserManual();
}
public String getUpdatedContentDescription() {
return language.getInstance().getUpdatedContentDescription();
}
public int getEstimatedTime() {
return 30;
}
private void InstanceEngine() {
Thread InstanceEngine = new Thread(new Runnable(){
@Override
public void run() {
LogUtils.d("ISA_NAVI", "InstanceEngine start!");
mHmiProxyJNI.instanceEngine();
}
});
InstanceEngine.setName("ISA_NAVI_InstanceEngine");
InstanceEngine.start();
}
// 获取后四位字符
@NonNull
private static String getLastFourChars(String mapInfo) {
if (mapInfo == null || mapInfo.isEmpty()) {
LogUtils.e(TAG, "mapInfo cannot be null or empty");
return "0000";
}
return mapInfo.length() >= LAST_CHARS_LENGTH
? mapInfo.substring(mapInfo.length() - LAST_CHARS_LENGTH)
: "0000";
}
// 生成密码
@NonNull
private String generatePassword() {
String mapInfo = getMapInfo();
String lastFourChars = getLastFourChars(mapInfo);
return PASSWORD_PREFIX + lastFourChars + PASSWORD_SUFFIX;
}
// 校验密码
public boolean validatePassword(String inputPassword) {
if (inputPassword == null) {
return false;
}
String generatedPassword = generatePassword();
LogUtils.i(TAG, "generatedPassword:" + generatedPassword);
LogUtils.i(TAG, "inputPassword:" + inputPassword);
return inputPassword.equals(generatedPassword);
}
public void setUSBPath(String USBPath) {
mHmiProxyJNI.setUSBPath(USBPath);
}
public boolean unzipFile(String zipFilePath, String destinationDirPath) {
return ZipUtils.getInstance().unzip(zipFilePath, destinationDirPath);
}
public long getFileSize(String filePath) {
File file = new File(filePath);
if(!file.isFile() || !file.exists()) {
return 0;
}
LogUtils.d(TAG, "getFileSize:" + file.length());
return file.length();
}
public long getAvailableSpace(String filePath) {
StatFs stat = new StatFs(filePath);
long availableBlocks = stat.getAvailableBlocksLong();
long blockSize = stat.getBlockSizeLong();
LogUtils.i(TAG, "[AvailableSpace]:" + availableBlocks * blockSize + "字节");
return availableBlocks * blockSize;
}
public String getFail(int i) {
return language.getInstance().getFail(i);
}
public boolean decryptFile(String encryptedFilePath, String decryptedFilePath) {
return new DecryptUtil().decryptFile(encryptedFilePath, decryptedFilePath, DfCert.getInstance().getKEY_AES_256());
}
public boolean verifySignature(String filePath, String signFilePath) {
try {
return DecryptUtil.verifySignature(filePath, signFilePath, DfCert.getInstance().getPUBLIC_KEY());
} catch (Exception e) {
return false;
}
}
/**
* 获取高压电池电量百分比。
*
* <p>此方法通过调用`mCarManager.getHVPercent`来获取高压电池的电量百分比。如果调用成功,则返回电量百分比值;
* 如果调用失败或发生异常,则捕获异常并打印堆栈跟踪,最后返回`0`。</p>
*
* @return 一个浮点数,表示高压电池的电量百分比(0-100);如果获取失败或发生异常,则返回`0`。
*/
public float getHVPercent() {
float HVPercent = 0;
try {
HVPercent = mCarManager.getHVPercent();
if(HVPercent > 100 || HVPercent < 0) {
HVPercent = 0;
}
} catch (Exception e) {
e.printStackTrace();
}
return HVPercent;
}
boolean isMounted() {
return USBReceiver.isMounted(updateUSBpath);
}
public String getCountryDbPath() {
return mHmiProxyJNI.getCountryDbPath();
}
public boolean dataVerification(String verifyDataPath) {
LogUtils.i(TAG, "dataVerification[verifyDataPath]:" + verifyDataPath);
String signPath = "/data/data/com.isa.navi/sign/";
if(!mapControl()) {
return false;
}
//解压加密数据
LogUtils.i(TAG, "verifyDataPath[" + verifyDataPath + "] -> signPath[" + signPath + "]");
if(!unzipFile(verifyDataPath, signPath)) {
//删除解密验签中间文件
if(!deleteDirectory(signPath)) {
LogUtils.e(TAG, "无法删除解密验签中间文件!");
}
return false;
}
if(!mapControl()) {
return false;
}
//提取签名文件以及加密文件的绝对路径
String[] temp = ZipUtils.getInstance().findEncryptedZipFiles(signPath);
String aesDaraPath = temp[1];
String signDataPtah = temp[0];
LogUtils.i(TAG, "aesDaraPath:" + aesDaraPath);
LogUtils.i(TAG, "signDataPtah" + signDataPtah);
if(!mapControl()) {
return false;
}
//验证签名
if(!verifySignature(aesDaraPath, signDataPtah)) {
LogUtils.e(TAG, "verifySignature fail!");
//删除解密验签中间文件
if(!deleteDirectory(signPath)) {
LogUtils.e(TAG, "无法删除解密验签中间文件!");
}
return false;
}
if(!mapControl()) {
return false;
}
//对称解密Zip
if(!decryptFile(aesDaraPath, verifyDataPath)) {
LogUtils.e(TAG, "decryptFile fail!");
//删除解密验签中间文件
if(!deleteDirectory(signPath)) {
LogUtils.e(TAG, "无法删除解密验签中间文件!");
}
return false;
}
//删除解密验签中间文件
if(!deleteDirectory(signPath)) {
LogUtils.e(TAG, "无法删除解密验签中间文件!");
}
LogUtils.i(TAG, "dataVerification success");
return true;
}
/**
* 删除指定目录及其所有内容
*
* @param directory 指定的目录路径
* @return 如果删除成功返回 true,否则返回 false
*/
public boolean deleteDirectory(String directory) {
File dir = new File(directory);
if (!dir.exists()) {
LogUtils.e("目录不存在: " + directory);
return false; // 目录不存在,视为删除失败
}
if (!dir.isDirectory()) {
LogUtils.e("指定路径不是一个目录: " + directory);
return false; // 路径不是目录,无法删除
}
return deleteRecursive(dir); // 递归删除目录
}
/**
* 递归删除文件或目录
*
* @param file 文件或目录
* @return 如果删除成功返回 true,否则返回 false
*/
private boolean deleteRecursive(File file) {
if (file.isDirectory()) {
// 如果是目录,递归删除其所有子文件和子目录
File[] files = file.listFiles();
if (files != null) {
for (File subFile : files) {
if (!deleteRecursive(subFile)) {
return false; // 如果某个子文件或子目录删除失败,则整体失败
}
}
}
}
// 删除当前文件或空目录
if (!file.delete()) {
LogUtils.e("无法删除文件或目录: " + file.getAbsolutePath());
return false; // 删除失败
}
LogUtils.i("已成功删除: " + file.getAbsolutePath());
return true; // 删除成功
}
public boolean mapControl() {
LogUtils.d(TAG, "mapControl JAVA start");
return mHmiProxyJNI.mapControl();
}
public boolean zipDbFiles(String sourceDir, String outputZipPath) {
return ZipUtils.getInstance().zipDbFiles(sourceDir, outputZipPath);
}
public boolean zip(String sourcePath, String outputZipPath) {
return ZipUtils.getInstance().zip(sourcePath, outputZipPath, true);
}
// Used to load the 'native-lib' library on application startup.
static {
LogUtils.d(TAG, "loadLibrary IsaEngineJni " + System.currentTimeMillis());
System.loadLibrary("IsaEngineJni");
}
}
此代码的调用顺序和层次
最新发布