原子操作integer:msg.what值的产生

本文介绍了一种在Android应用中生成唯一视图ID的方法。通过使用AtomicInteger类,确保了即使在多线程环境下也能生成唯一的整数值。该方法通过递增的方式生成新的ID,并在ID超过指定范围时回滚到最小值。

1、原子操作是为了产生不重复的值:android中动态生成message中的msg.what的值


private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);



private static int generateViewId() {
for (;;) {
final int result = sNextGeneratedId.get();
// aapt-generated IDs have the high byte nonzero; clamp to the range
// under that.
int newValue = result + 1;
if (newValue > 0x00FFFFFF)
newValue = 1; // Roll over to 1, not 0.
if (sNextGeneratedId.compareAndSet(result, newValue)) {
return result;
}
}
}
private volatile boolean mLightFlag = false; private void initChildThreadHandler() { Thread childThread = new Thread(() -> { Looper.prepare(); // 子线程创建Looper Looper childLooper = Looper.myLooper(); // 获取子线程Looper if (childLooper == null){ return; } childThreadHandler = new Handler(childLooper) { @Override public void handleMessage(Message msg) { android.util.Log.d("BreathLight", "线程名:" + Thread.currentThread().getName()); if (msg.what == COLOR_CYCLE) { // 1. 计算安全索引(取模防越界) int safeIndex = mColorIndex % COLOR_ARRAY.length; int colorResult = Integer.parseInt(COLOR_ARRAY[safeIndex], 16); Slog.d(TAG, "handleColorCycle colors[index] string ==" + colorResult + " index ==" + safeIndex); // 2. 调用呼吸灯接口 if (mLogicallight != null) { //reset flash light mService.getWrapper().setLightUnchecked(mLogicallight, 0, LogicalLight.LIGHT_FLASH_HARDWARE, 0, 0, 0); mLogicallight.setFlashing(colorResult, LogicalLight.LIGHT_FLASH_HARDWARE, 0, 0); } // 3. 索引递增 mColorIndex++; // 4. 发送下一次轮询消息(循环核心) sendEmptyMessageDelayed(COLOR_CYCLE, mDuration); } else if (msg.what == STOP_LED){ turnOffLight(); } } }; Looper.loop(); // 启动子线程消息循环 android.util.Log.d("BreathLight", "子线程退出,线程名:" + Thread.currentThread().getName()); }); childThread.start(); } // 启动轮询 private void startPolling() { if (mLightFlag) return; mLightFlag = true; // 发送第一条轮询消息(触发循环) if (childThreadHandler != null) { childThreadHandler.sendEmptyMessage(COLOR_CYCLE); } } // 停止轮询 private void stopPolling() { mLightFlag = false; mColorIndex = 0; // 移除子线程Handler的所有消息(避免继续执行) if (childThreadHandler != null) { childThreadHandler.sendEmptyMessage(STOP_LED); childThreadHandler.removeMessages(COLOR_CYCLE); // Looper childlooper = childThreadHandler.getLooper(); // childlooper.quit(); } }
07-17
package com.example.demoapplication; import android.Manifest; // 导入录音权限类 import android.content.pm.PackageManager; // 导入包管理器权限类 import android.media.AudioFormat; // 导入音频格式类 import android.media.AudioManager; // 导入音频管理类 import android.media.AudioRecord; // 导入音频录制类 import android.media.MediaPlayer; // 导入媒体播放器类 import android.media.MediaRecorder; // 导入媒体录制器类 import android.os.Bundle; // 导入Android数据包类 import android.os.Handler; // 导入消息处理类 import android.os.Looper; // 导入线程循环类 import android.os.Message; // 导入消息传递类 import android.speech.tts.TextToSpeech; // 导入文本转语音接口 import android.util.Log; // 导入日志工具类 import android.widget.Button; // 导入按钮组件 import android.widget.Toast; // 导入提示框类 import androidx.annotation.NonNull; // 导入非空注解 import androidx.appcompat.app.AppCompatActivity; // 导入AppCompatActivity基类 import androidx.core.app.ActivityCompat; // 导入兼容库活动权限工具类 import androidx.core.content.ContextCompat; // 导入兼容库内容上下文工具类 import org.json.JSONObject; // 导入JSON对象类 import java.io.BufferedReader; // 导入缓冲字符读取流 import java.io.BufferedWriter; // 导入缓冲字符写入流 import java.io.File; // 导入文件操作类 import java.io.FileOutputStream; // 导入文件输出流 import java.io.IOException; // 导入输入输出异常类 import java.io.InputStreamReader; // 导入字符输入流包装器 import java.io.OutputStreamWriter; // 导入字符输出流包装器 import java.net.ServerSocket; // 导入服务器套接字 import java.net.Socket; // 导入网络连接套接字 import java.util.LinkedList; // 导入链表队列结构 import java.util.Locale; // 导入语言环境类 import java.util.Queue; // 导入队列接口 import java.util.concurrent.ExecutorService; // 导入执行服务线程池 import java.util.concurrent.Executors; // 导入线程池工厂类 import java.util.concurrent.ScheduledExecutorService; // 导入定时任务执行服务 import java.util.concurrent.TimeUnit; // 导入时间单位枚举 import java.util.concurrent.atomic.AtomicBoolean; // 导入原子布尔 public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener { private static final String TAG = "AudioRecorder"; // 日志标签 private static final int PERMISSION_REQUEST_CODE = 1; // 权限请求代码 private static final int SAMPLE_RATE = 16000; // 音频采样率 private static final int BUFFER_SIZE; // 缓冲区大小 static { // 计算最小缓冲区大小 int minBufferSize = AudioRecord.getMinBufferSize( SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, // 单声道采集 AudioFormat.ENCODING_PCM_16BIT // 16位PCM编码 ); BUFFER_SIZE = Math.max(minBufferSize, 4096); // 取最大作为最终缓冲区大小 } // UI组件声明 private Button startRecordButton, stopRecordButton; // 录音控制按钮 private Button playSoundButton, pauseSoundButton, stopSoundButton, resumeSoundButton, clearSoundsButton; // 播放控制按钮 // 音频录制相关变量 private AudioRecord audioRecord; // 音频录制对象 private ScheduledExecutorService scheduler; // 定时任务调度器 private AtomicBoolean isRecording = new AtomicBoolean(false); // 是否正在录音标志 private final Queue<byte[]> recordingQueue = new LinkedList<>(); // 录制数据队列 // 网络通信相关变量 private final ExecutorService executorService = Executors.newCachedThreadPool(); // 弹性线程池 private ServerSocket serverSocket; // 服务器端监听套接字 private volatile boolean isServerRunning = true; // 服务器运行状态标志 private volatile Socket clientSocket; // 客户端连接套接字 private volatile BufferedWriter socketWriter; // 套接字写入器 // 文本转语音相关变量 private TextToSpeech ttsEngine; // TTS引擎实例 private boolean isTtsInitialized = false; // TTS初始化状态标志 // MP3播放相关变量 private MediaPlayer mediaPlayer; // 媒体播放器实例 private final Queue<byte[]> mp3PlaybackQueue = new LinkedList<>(); // 播放队列 private final AtomicBoolean isPlaying = new AtomicBoolean(false); // 是否正在播放 private final AtomicBoolean isPaused = new AtomicBoolean(false); // 是否暂停 private volatile boolean isPlaybackThreadActive = false; // 播放线程活跃状态 // 同步锁对象 private final Object playbackQueueLock = new Object(); // 播放队列锁 private final Object recordingQueueLock = new Object(); // 录制队列锁 private final Object mediaPlayerLock = new Object(); // 媒体播放器锁 // 主线程消息处理器 private final Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(@NonNull Message msg) { switch (msg.what) { case 0x11: // 客户端连接事件 Toast.makeText(MainActivity.this, "客户端已连接", Toast.LENGTH_SHORT).show(); break; case 0x12: // 开始录音事件 Toast.makeText(MainActivity.this, "开始录音", Toast.LENGTH_SHORT).show(); sendJsonPacket("startRecorder", null); playTts("开始录音"); break; case 0x14: // 停止录音事件 Toast.makeText(MainActivity.this, "停止录音", Toast.LENGTH_SHORT).show(); sendJsonPacket("stopRecorder", null); playTts("停止录音"); break; case 0x16: // 错误消息事件 Toast.makeText(MainActivity.this, "错误: " + msg.obj, Toast.LENGTH_LONG).show(); break; case 0x17: // 播放完成事件 Toast.makeText(MainActivity.this, "播放完成", Toast.LENGTH_SHORT).show(); isPlaying.set(false); isPlaybackThreadActive = false; updatePlayButtonsState(); break; case 0x18: // 添加到播放队列 Toast.makeText(MainActivity.this, "已添加到播放队列", Toast.LENGTH_SHORT).show(); break; case 0x19: // 更新播放按钮状态 updatePlayButtonsState(); break; case 0x20: // 开始播放MP3 Toast.makeText(MainActivity.this, "开始播放MP3", Toast.LENGTH_SHORT).show(); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 设置界面布局 // 初始化文本转语音功能 ttsEngine = new TextToSpeech(this, this); // 初始化视图组件 initViews(); setupClickListeners(); // 绑定点击事件 // 检查必要权限 checkPermissions(); // 启动服务器并开始监听 startServer(30000); startSocketListener(); } private void initViews() { // 绑定UI控件 startRecordButton = findViewById(R.id.startRecordButton); stopRecordButton = findViewById(R.id.stopRecordButton); playSoundButton = findViewById(R.id.playSoundButton); pauseSoundButton = findViewById(R.id.pauseSoundButton); stopSoundButton = findViewById(R.id.stopSoundButton); resumeSoundButton = findViewById(R.id.resumeSoundButton); clearSoundsButton = findViewById(R.id.clearSoundsButton); // 设置初始按钮状态 stopRecordButton.setEnabled(false); pauseSoundButton.setEnabled(false); stopSoundButton.setEnabled(false); resumeSoundButton.setEnabled(false); } private void setupClickListeners() { // 设置录音按钮点击事件 startRecordButton.setOnClickListener(v -> startRecording()); stopRecordButton.setOnClickListener(v -> stopRecording()); // 设置播放控制按钮点击事件 playSoundButton.setOnClickListener(v -> playNextInQueue()); pauseSoundButton.setOnClickListener(v -> pausePlayback()); stopSoundButton.setOnClickListener(v -> stopPlayback()); resumeSoundButton.setOnClickListener(v -> resumePlayback()); clearSoundsButton.setOnClickListener(v -> { clearPlaybackQueue(); // 清空播放队列 handler.sendEmptyMessage(0x19); // 发送更新消息 }); } // ==================== 录音功能模块 ==================== private void startRecording() { // 检查录音权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { sendErrorMessage("没有录音权限"); return; } // 如果已经在录音则先释放资源 if (isRecording.get()) { releaseAudioResources(); } try { // 创建音频录制对象 audioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC, // 使用麦克风输入 SAMPLE_RATE, // 采样率 AudioFormat.CHANNEL_IN_MONO, // 单声道 AudioFormat.ENCODING_PCM_16BIT, // 16位PCM编码 BUFFER_SIZE // 缓冲区大小 ); // 检查初始化状态 if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) { throw new IllegalStateException("AudioRecord 初始化失败"); } // 开始录音 audioRecord.startRecording(); isRecording.set(true); startRecordButton.setEnabled(false); stopRecordButton.setEnabled(true); updatePlayButtonsState(); // 更新播放按钮状态 // 关闭旧的调度器 if (scheduler != null && !scheduler.isShutdown()) { scheduler.shutdownNow(); } // 创建新的单线程调度器 scheduler = Executors.newSingleThreadScheduledExecutor(); // 定期采集音频数据(每100ms) scheduler.scheduleAtFixedRate(this::captureAudioData, 0, 100, TimeUnit.MILLISECONDS); handler.sendEmptyMessage(0x12); // 发送开始录音通知 } catch (Exception e) { Log.e(TAG, "录音启动失败", e); sendErrorMessage("录音启动失败: " + e.getMessage()); releaseAudioResources(); } } private void stopRecording() { if (!isRecording.get()) return; isRecording.set(false); releaseAudioResources(); // 释放音频资源 stopRecordButton.setEnabled(false); startRecordButton.setEnabled(true); updatePlayButtonsState(); // 更新播放按钮状态 handler.sendEmptyMessage(0x14); // 发送停止录音通知 } private void captureAudioData() { if (!isRecording.get() || audioRecord == null) return; byte[] buffer = new byte[BUFFER_SIZE]; // 创建缓冲区 try { // 从音频设备读取数据 int bytesRead = audioRecord.read(buffer, 0, BUFFER_SIZE); if (bytesRead > 0) { synchronized (recordingQueueLock) { recordingQueue.offer(buffer.clone()); // 存储音频数据 } sendJsonPacket("recording", buffer); // 发送数据包 } } catch (Exception e) { Log.e(TAG, "音频采集失败", e); } } // ==================== MP3播放功能模块 ==================== private void addMp3ToQueue(byte[] mp3Data) { synchronized (playbackQueueLock) { mp3PlaybackQueue.offer(mp3Data); // 将MP3数据加入播放队列 } handler.sendEmptyMessage(0x18); // 发送加入队列通知 // 如果没有播放且线程不活跃,开始播放 if (!isPlaying.get() && !isPlaybackThreadActive) { playNextInQueue(); } } private void playNextInQueue() { if (isPlaybackThreadActive || mp3PlaybackQueue.isEmpty()) return; isPlaybackThreadActive = true; executorService.execute(() -> { // 在线程池中执行 try { while (!mp3PlaybackQueue.isEmpty() && !isPaused.get()) { byte[] mp3Data; synchronized (playbackQueueLock) { mp3Data = mp3PlaybackQueue.poll(); // 取出队列中的MP3数据 } if (mp3Data != null) { playMp3Data(mp3Data); // 播放MP3数据 } } if (!isPaused.get()) { isPlaying.set(false); isPlaybackThreadActive = false; handler.sendEmptyMessage(0x17); // 发送播放完成通知 } } catch (Exception e) { Log.e(TAG, "MP3播放失败", e); sendErrorMessage("MP3播放失败: " + e.getMessage()); } }); } private void playMp3Data(final byte[] mp3Data) { try { // 创建临时文件 File tempFile = File.createTempFile("audio_", ".mp3", getCacheDir()); try (FileOutputStream fos = new FileOutputStream(tempFile)) { fos.write(mp3Data); // 写入文件 } runOnUiThread(() -> { synchronized (mediaPlayerLock) { try { releaseMediaPlayer(); // 释放之前的播放器资源 mediaPlayer = new MediaPlayer(); // 创建新播放器 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置音频流类型 mediaPlayer.setDataSource(tempFile.getAbsolutePath()); // 设置数据源 // 设置准备监听器 mediaPlayer.setOnPreparedListener(mp -> { mediaPlayer.start(); // 开始播放 isPlaying.set(true); // 标记为正在播放 isPaused.set(false); // 标记为未暂停 handler.sendEmptyMessage(0x20); // 发送开始播放通知 updatePlayButtonsState(); // 更新按钮状态 }); // 设置完成监听器 mediaPlayer.setOnCompletionListener(mp -> { // 删除临时文件 tempFile.delete(); // 播放下一个文件 if (!playNextInQueueImmediately()) { isPlaying.set(false); isPlaybackThreadActive = false; handler.sendEmptyMessage(0x17); } }); // 设置错误监听器 mediaPlayer.setOnErrorListener((mp, what, extra) -> { Log.e(TAG, "MediaPlayer error: " + what + ", " + extra); sendErrorMessage("播放错误: " + what); tempFile.delete(); // 删除临时文件 return false; }); mediaPlayer.prepareAsync(); // 准备播放 } catch (IOException e) { Log.e(TAG, "MediaPlayer初始化失败", e); tempFile.delete(); // 删除临时文件 } } }); } catch (IOException e) { Log.e(TAG, "创建临时文件失败", e); } } private boolean playNextInQueueImmediately() { if (!mp3PlaybackQueue.isEmpty()) { byte[] nextData; synchronized (playbackQueueLock) { nextData = mp3PlaybackQueue.poll(); // 取出下一个播放项 } if (nextData != null) { playMp3Data(nextData); // 直接播放 return true; } } return false; } // ==================== 播放控制 ==================== private void pausePlayback() { synchronized (mediaPlayerLock) { if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); // 暂停播放 isPaused.set(true); // 设置暂停标志 updatePlayButtonsState(); // 更新按钮状态 } } } private void resumePlayback() { synchronized (mediaPlayerLock) { if (mediaPlayer != null && isPaused.get()) { mediaPlayer.start(); // 恢复播放 isPaused.set(false); // 取消暂停标志 updatePlayButtonsState(); // 更新按钮状态 } } } private void stopPlayback() { synchronized (mediaPlayerLock) { if (mediaPlayer != null) { try { mediaPlayer.stop(); // 停止播放 } catch (IllegalStateException e) { Log.e(TAG, "停止播放失败", e); } } } clearPlaybackQueue(); // 清空播放队列 isPlaying.set(false); // 设置未播放状态 isPaused.set(false); // 取消暂停状态 updatePlayButtonsState(); // 更新按钮状态 } private void clearPlaybackQueue() { synchronized (playbackQueueLock) { mp3PlaybackQueue.clear(); // 清空播放队列 } } private void releaseMediaPlayer() { synchronized (mediaPlayerLock) { if (mediaPlayer != null) { try { if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); // 停止播放 } mediaPlayer.release(); // 释放资源 } catch (Exception e) { Log.e(TAG, "释放MediaPlayer失败", e); } finally { mediaPlayer = null; // 清空引用 } } } } // ==================== 辅助方法 ==================== private void updatePlayButtonsState() { runOnUiThread(() -> { boolean hasRecordings = !mp3PlaybackQueue.isEmpty(); // 是否有录音 boolean isPlayingState = isPlaying.get() && !isPaused.get(); // 是否正在播放 boolean isPausedState = isPaused.get(); // 是否暂停 // 设置按钮启用状态 playSoundButton.setEnabled(hasRecordings && !isPlayingState); pauseSoundButton.setEnabled(isPlayingState); stopSoundButton.setEnabled(isPlaying.get() || isPausedState); resumeSoundButton.setEnabled(isPausedState); clearSoundsButton.setEnabled(hasRecordings); }); } private void playTts(String text) { if (isTtsInitialized) { ttsEngine.speak(text, TextToSpeech.QUEUE_FLUSH, null); // 播放TTS } } private void releaseAudioResources() { if (audioRecord != null) { try { // 如果正在录音则停止 if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { audioRecord.stop(); } audioRecord.release(); // 释放资源 } catch (IllegalStateException e) { Log.e(TAG, "停止录音失败", e); } finally { audioRecord = null; // 清除引用 } } if (scheduler != null) { try { scheduler.shutdownNow(); // 关闭调度器 // 等待关闭完成 if (!scheduler.awaitTermination(500, TimeUnit.MILLISECONDS)) { Log.w(TAG, "录音线程池未正常关闭"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 重新设置中断标志 } finally { scheduler = null; // 清除引用 } } } private void sendJsonPacket(String type, Object data) { if (clientSocket == null || clientSocket.isClosed() || socketWriter == null) { return; // 如果连接无效则返回 } try { JSONObject packet = new JSONObject(); // 创建JSON包 packet.put("type", type); // 设置类型 if (data != null) { packet.put("data", data); // 添加数据 } synchronized (this) { if (socketWriter != null) { socketWriter.write(packet.toString()); // 写入JSON字符串 socketWriter.write("\n\n"); // 添加分隔符 socketWriter.flush(); // 刷新缓冲区 } } } catch (Exception e) { Log.e(TAG, "发送数据包失败: " + type, e); } } private void sendErrorMessage(String message) { handler.obtainMessage(0x16, message).sendToTarget(); // 发送错误消息 } private void checkPermissions() { // 检查录音权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.RECORD_AUDIO}, PERMISSION_REQUEST_CODE ); } // 检查存储权限(用于临时文件) if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE + 1 ); } } // ==================== 网络通信 ==================== private void startServer(int port) { executorService.execute(() -> { // 在线程池中执行 try { serverSocket = new ServerSocket(port); // 创建服务器套接字 Log.i(TAG, "服务器启动: " + port); // 打印日志 while (isServerRunning) { // 服务器运行循环 try { Socket socket = serverSocket.accept(); // 接受连接 clientSocket = socket; // 保存客户端套接字 synchronized (this) { // 创建套接字写入器 socketWriter = new BufferedWriter( new OutputStreamWriter(socket.getOutputStream(), "UTF-8") ); } handler.sendEmptyMessage(0x11); // 发送连接成功通知 } catch (IOException e) { if (isServerRunning) Log.e(TAG, "接受连接失败", e); } } } catch (IOException e) { Log.e(TAG, "服务器启动失败", e); runOnUiThread(() -> Toast.makeText( this, "服务器启动失败: " + e.getMessage(), Toast.LENGTH_LONG ).show()); } finally { closeServerSocket(); // 关闭服务器套接字 } }); } private void startSocketListener() { executorService.execute(() -> { // 在线程池中执行 while (isServerRunning) { if (clientSocket != null && !clientSocket.isClosed()) { try { BufferedReader reader = new BufferedReader( new InputStreamReader(clientSocket.getInputStream(), "UTF-8") ); // 创建输入流读取器 // 先读取数据长度 String lengthLine = reader.readLine(); if (lengthLine == null) break; int dataLength = Integer.parseInt(lengthLine.trim()); // 解析数据长度 byte[] mp3Data = new byte[dataLength]; // 创建数据缓冲区 // 读取二进制数据 int bytesRead = 0; while (bytesRead < dataLength) { int count = clientSocket.getInputStream().read( mp3Data, bytesRead, dataLength - bytesRead); if (count == -1) break; bytesRead += count; } if (bytesRead == dataLength) { addMp3ToQueue(mp3Data); // 添加到播放队列 } else { Log.w(TAG, "数据不完整: " + bytesRead + "/" + dataLength); } } catch (IOException | NumberFormatException e) { Log.e(TAG, "Socket读取失败", e); closeSocket(clientSocket); // 关闭套接字 } } else { try { Thread.sleep(500); // 等待500毫秒 } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 重新设置中断标志 break; } } } }); } private void closeServerSocket() { try { if (serverSocket != null && !serverSocket.isClosed()) { serverSocket.close(); // 关闭服务器套接字 } } catch (IOException e) { Log.w(TAG, "关闭ServerSocket失败", e); } } private void closeSocket(Socket socket) { try { if (socket != null && !socket.isClosed()) { socket.close(); // 关闭套接字 } } catch (IOException e) { Log.w(TAG, "关闭Socket失败", e); } if (socket == clientSocket) { clientSocket = null; // 清除客户端套接字引用 synchronized (this) { socketWriter = null; // 清除写入器引用 } } } // ==================== 文本转语音 ==================== @Override public void onInit(int status) { if (status == TextToSpeech.SUCCESS) { int result = ttsEngine.setLanguage(Locale.CHINESE); // 设置中文语言 if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) { Log.e(TAG, "TTS语言不支持中文"); } else { isTtsInitialized = true; // 设置初始化完成标志 } } } // ==================== 生命周期 ==================== @Override protected void onDestroy() { super.onDestroy(); isServerRunning = false; // 停止服务器 // 释放TTS资源 if (ttsEngine != null) { ttsEngine.stop(); ttsEngine.shutdown(); } // 释放网络资源 closeServerSocket(); closeSocket(clientSocket); // 释放音频资源 stopRecording(); releaseMediaPlayer(); // 关闭线程池 executorService.shutdown(); try { // 等待关闭完成 if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) { executorService.shutdownNow(); // 强制关闭 } } catch (InterruptedException e) { executorService.shutdownNow(); // 强制关闭 Thread.currentThread().interrupt(); // 重新设置中断标志 } } } 我这个安卓端口是多少
07-08
package com.isa.navi.jni.hmi; import static java.lang.Thread.sleep; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.StatFs; import android.util.Log; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import androidx.annotation.NonNull; 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.InputStreamReader; import java.time.LocalDate; import java.time.Month; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; 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); } }); LogUtils.i(TAG, "Path:" + mContext.getFilesDir()); // 初始化C++模块 mHmiProxyJNI.InitializeCpp(); InstanceEngine(); //decryptUtilTest(); // UpdateTest(); } // 辅助方法:记录访问被拒绝的详细原因 private void logAccessDeniedReason(File dir) { // 记录 SELinux 拒绝日志 try { Process process = Runtime.getRuntime().exec("dmesg | grep avc"); BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream())); String line; boolean found = false; while ((line = reader.readLine()) != null) { if (line.contains("avc: denied") && line.contains(dir.getName())) { LogUtils.e(TAG, "SELinux拒绝记录: " + line); found = true; } } if (!found) { LogUtils.i(TAG, "未找到相关的SELinux拒绝记录"); } } catch (Exception e) { LogUtils.e(TAG, "获取SELinux日志失败"+e); } // 记录文件系统权限 try { Process process = Runtime.getRuntime().exec("ls -ld " + dir.getAbsolutePath()); BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream())); String line; while ((line = reader.readLine()) != null) { LogUtils.i(TAG, "详细权限信息: " + line); } } catch (Exception e) { LogUtils.e(TAG, "获取详细权限失败"+e); } // 记录当前应用权限 LogUtils.i(TAG, "应用权限上下文: " + getSELinuxContext()); } // 获取当前应用的 SELinux 安全上下文 private String getSELinuxContext() { try { Process process = Runtime.getRuntime().exec("id -Z"); BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream())); return reader.readLine(); } catch (Exception e) { return "未知: " + e.getMessage(); } } // public void upDateTest(){ // Thread UpdateTest = new Thread(new Runnable(){ // @Override // public void run() { // LogUtils.i(TAG, "upDateTest start"); // startUSBDetection(); // } // }); // // UpdateTest.setName("ISA_NAVI_upDateTest"); // UpdateTest.start(); // } public void UpdateTest() { Thread UpdateTest = new Thread(new Runnable(){ @Override public void run() { LogUtils.i(TAG, "UpdateTest start!"); setUSBPath("/data/user/0/com.isa.navi/usbpath/KVM_EU_202502_T1.0_266dd24ba609476c99293f0ef7403a9f.zip"); updateMap(); } }); UpdateTest.setName("ISA_NAVI_UpdateTest"); UpdateTest.start(); } // // public void decryptUtilTest(){ // Thread UpdateTest = new Thread(new Runnable(){ // @Override // public void run() { // try { // sleep(20); // LogUtils.i(TAG, "decryptUtilTest start"); // String encryptedZipPath = "/map/KVM_EU_202502_T1.0_266dd24ba609476c99293f0ef7403a9f.zip"; // boolean isSuccess = dataVerification(encryptedZipPath); // LogUtils.i(TAG, "验签和解密结果: " + (isSuccess ? "成功" : "失败")); // } catch (Exception e) { // LogUtils.e(TAG, "测试异常: " + e.getMessage()); // } // } // }); // // UpdateTest.setName("ISA_NAVI_decryptUtilTest"); // UpdateTest.start(); // } //读取DES秘钥文本 public String readKeyFromFile(String keyDataPath) { File keyFile = new File(keyDataPath); StringBuilder keyBuilder = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new FileReader(keyFile))) { String line; while ((line = reader.readLine()) != null) { // 移除行首尾空白字符(包括换行符) String trimmedLine = line.trim(); // 跳过空行和注释行(以#开头的行) if (trimmedLine.isEmpty() || trimmedLine.startsWith("#")) { continue; } keyBuilder.append(trimmedLine); } // 返回读取到的内容 return keyBuilder.toString(); } catch (IOException e) { // 处理文件读取异常 System.err.println("读取文件失败: " + e.getMessage()); e.printStackTrace(); return null; } } public boolean desDecrypt(String DesDataPath,String key){ LogUtils.i(TAG, "encryptedZipPath[encryptedZipPath]:" + DesDataPath); LogUtils.i(TAG, "key:" + key); String signPath = get_Global_m_data_address() +"sign/"; String downLoadPath = get_Global_m_data_address(); //解压加密数据 LogUtils.i(TAG, "DesDataPath[" + DesDataPath + "] -> signPath[" + signPath + "]"); if(!unzipFile(DesDataPath, signPath)) { //删除des解密中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除des解密中间文件!"); } return false; } String str = ZipUtils.getInstance().findFiles(signPath); LogUtils.i(TAG, " des文件[" + str + "]"); //解密des文件 if(!desDecryptFile(new File(str), downLoadPath, key)) { LogUtils.e(TAG, "DesdecryptFile fail!"); //删除des解密中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除Des解密中间文件!"); } return false; } //删除des解密中间文件 if(!deleteDirectory(signPath)) { LogUtils.e(TAG, "无法删除des解密中间文件!"); } LogUtils.i(TAG, "desDecryptUtil success"); return true; } 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,其他状态直接放行 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; String file_str = null; for(File file : listFiles) { LogUtils.i(TAG, "file:" + file.getAbsolutePath()); //判断U盘内是否含有升级文件 if(!file.isDirectory() && mFormat.isUpgradeFileNamingFormat(file.getName())) { isHaveUSBData = true; if(newfile == null) { newfile = file; file_str = newfile.getAbsolutePath(); } else { if(Format.getInstance().oldIsNewVersion(file.getName().replaceAll("\\.\\w+$", ""), newfile.getName().replaceAll("\\.\\w+$", ""))) { newfile = file; file_str = newfile.getAbsolutePath(); } } } } //未检测到U盘指定路径下是否有数据,触发提示弹窗 if(!isHaveUSBData || newfile == null) { LogUtils.e(TAG, "The USB drive does not contain an upgrade package!"); bean.setUpdateStatus(3); handleDownloadStatusChange(bean); return; }else{ // 从文件名中提取版本号-zwx // String localDataPath = Format.getInstance().extractVersion(getCountryDbPath()); String localDataPath = Format.getInstance().extractVersionFromDbPath(getCountryDbPath());//202501_T1.0 String newDataPath = Format.getInstance().extractVersionFromDbPath(file_str);//202502_T1.0 LogUtils.i(TAG, "file name:" + newDataPath); LogUtils.i(TAG, "localDataPath:" + localDataPath); if(localDataPath.isEmpty()) { String mapinfo = getMapInfo(); if(!mapinfo.isEmpty()) { String[] newVersionParts = new String[2]; newVersionParts = Format.getInstance().parseVersion(newDataPath); String newUpdateVersion = newVersionParts[0]; // 取第一部分:202502 LogUtils.i(TAG, "newUpdateVersion:" + newUpdateVersion); String newReleasesVersion = newVersionParts[1];//取第二部分:01/02 LogUtils.i(TAG, "newReleasesVersion:" + newReleasesVersion); 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(newDataPath, 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 desDecryptFile(File desDaraPath,String downLoadPath,String key){ return new DecryptUtil().desDecryptFile(desDaraPath, downLoadPath, key); } 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 String get_Global_m_data_address() { return mHmiProxyJNI.get_Global_m_data_address(); } public boolean dataVerification(String verifyDataPath) { LogUtils.i(TAG, "dataVerification[verifyDataPath]:" + verifyDataPath); String signPath = get_Global_m_data_address() + "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]; String KeyDataPath = temp [2]; LogUtils.i(TAG, "aesDaraPath:" + aesDaraPath); LogUtils.i(TAG, "signDataPtah" + signDataPtah); LogUtils.i(TAG, "KeyDataPath" + KeyDataPath); if(aesDaraPath == null || signDataPtah == null || KeyDataPath == null){ return false; } String key = readKeyFromFile(KeyDataPath); 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, "无法删除解密验签中间文件!"); } //des解密 LogUtils.i(TAG, "verifyDataPath:" + verifyDataPath); boolean isSuccess = desDecrypt(verifyDataPath,key); if(!isSuccess){ LogUtils.i(TAG, "DES解密结果: 失败" ); return false; } LogUtils.i(TAG, "DES解密结果:成功 " ); 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"); } } package com.isa.navi.jni.hmi; import android.os.Build; import android.util.Base64; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.nio.charset.StandardCharsets; import java.io.*; import java.nio.charset.StandardCharsets; import android.util.Base64; import com.isa.navi.utils.LogUtils; import java.io.IOException; import java.io.ByteArrayInputStream; import java.security.PublicKey; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.List; import org.json.JSONObject; import org.json.JSONException; import org.bouncycastle.jce.provider.BouncyCastleProvider; import java.security.NoSuchAlgorithmException; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.security.MessageDigest; import java.security.Security; import java.security.Signature; import java.util.Objects; import javax.crypto.Cipher; import javax.crypto.CipherOutputStream; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * 对称解密管理类,负责提供对称解密相关方法。 */ public class DecryptUtil { /** * 用于日志记录的标签。 * <p>这是一个静态常量,用于在日志输出中标识下载管理器的相关日志。</p> */ private static final String TAG = "DecryptUtil"; private long lastCheckTime; static { Security.addProvider(new BouncyCastleProvider()); } /** * 对称解密文件 * @param encryptedFilePath 加密文件路径 * @param decryptedFilePath 解密后文件输出路径 * @param ENCRYPTION_KEY_BASE64 密钥 * @return 解密是否成功,成功返回true,失败返回false */ public boolean decryptFile(String encryptedFilePath, String decryptedFilePath, String ENCRYPTION_KEY_BASE64) { boolean success = false; try { // 创建密钥和IV规格 SecretKeySpec keySpec = new SecretKeySpec(getKey(ENCRYPTION_KEY_BASE64), "AES/CBC/PKCS5Padding"); IvParameterSpec ivSpec = new IvParameterSpec(getIV(ENCRYPTION_KEY_BASE64)); LogUtils.i(TAG, "创建密钥和IV规格 success!"); LogUtils.i(TAG, "创建密钥和IV规格 success!"); // 创建Cipher实例并初始化 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); LogUtils.i(TAG, "创建Cipher实例并初始化 success!"); if(!HmiJNIImpl.getInstance().mapControl()) { return false; } //读取文件进行对称解密,并将解密后的文件按照指定路径输出 try (FileInputStream fis = new FileInputStream(encryptedFilePath); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream(decryptedFilePath); CipherOutputStream cos = new CipherOutputStream(fos, cipher)) { byte[] buffer = new byte[10240]; // 使用10KB的缓冲区 int bytesRead; while ((bytesRead = bis.read(buffer)) != -1) { cos.write(buffer, 0, bytesRead); // 优化后的检查逻辑(每秒最多1次) long currentTime = System.currentTimeMillis(); if (currentTime - lastCheckTime > 1000) { synchronized (this) { if (currentTime - lastCheckTime > 1000) { if (!HmiJNIImpl.getInstance().mapControl()) { return false; } lastCheckTime = currentTime; } } } } LogUtils.i(TAG, "解密并写入数据 success!"); success = true; } catch (IOException e) { LogUtils.e(TAG, "处理文件时发生错误:" + e.getMessage()); throw new IOException("文件处理失败", e); } } catch (Exception e) { // 捕获所有其他异常并记录错误 LogUtils.e(TAG, "解密文件时发生错误:" + e.getMessage()); } return success; } /** * 验签 * @param filePath 加密文件路径 * @param signFilePath 签名文件路径(.sig文件) * @param CERT_KEY 公钥 * @return 签名成功返回true,签名失败返回false */ public static boolean verifySignature(String filePath, String signFilePath, String CERT_KEY) throws Exception { if(!HmiJNIImpl.getInstance().mapControl()) { return false; } try{ // 获取加密文件的SHA256哈希 byte[] fileHash = getFileHash(filePath); // 读取base64格式签名数据 String base64Signature = readSignatureFromFile(signFilePath); LogUtils.i("base64格式签名数据: " + base64Signature); // 解码签名 byte[] signatureBytes = Base64.decode(base64Signature,Base64.DEFAULT); // 加载公钥 PublicKey publicKey = loadPublicKey(CERT_KEY); LogUtils.i("PUBLIC_KEY: " + publicKey); if(!HmiJNIImpl.getInstance().mapControl()) { return false; } // 验签 Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(publicKey); signature.update(fileHash); return signature.verify(signatureBytes); } catch (Exception e){ e.printStackTrace(); return false; } } public boolean desDecryptFile(File desDataPath, String downLoadPath, String key) { final int BUFFER_SIZE = 8 * 1024 * 1024; // 8MB缓冲区 try { // // 生成密钥(与加密时一致) // byte[] utf8Bytes = key.getBytes(StandardCharsets.UTF_8); // // // 2. Base64 编码(使用 Base64.DEFAULT,无换行) // String base64Str = Base64.encodeToString(utf8Bytes,Base64.DEFAULT); // // // 3. 取前16个字符 // String result = base64Str.substring(0, 16); // // byte[] byteStream = result.getBytes(StandardCharsets.UTF_8); // SecretKeySpec secretKeySpec = new SecretKeySpec(byteStream, "DES"); LogUtils.i(TAG,"开始解析des秘钥"); DESKeySpec desKeySpec = new DESKeySpec(Base64.encode(key.getBytes("utf-8"), Base64.DEFAULT)); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); SecretKey secretKey = keyFactory.generateSecret(desKeySpec); LogUtils.i(TAG,"des秘钥解析成功"); LogUtils.i(TAG,"初始化DES秘钥"); // 初始化DES解密Cipher Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secretKey); LogUtils.i(TAG,"秘钥初始化完成"); LogUtils.i(TAG,"开始解析文件"); // 解析文件名 String name = desDataPath.getName(); int lastDotIndex = name.lastIndexOf("."); String fileExtension = (lastDotIndex == -1) ? "" : name.substring(lastDotIndex + 1); int firstIndex = name.indexOf("_"); String fileNameContent = (firstIndex == -1) ? name : name.substring(0, firstIndex); // 创建输出文件 File outputFile = new File(downLoadPath, fileNameContent + "." + fileExtension); try (InputStream inputStream = new FileInputStream(desDataPath); BufferedInputStream bis = new BufferedInputStream(inputStream); FileOutputStream fos = new FileOutputStream(outputFile); BufferedOutputStream bos = new BufferedOutputStream(fos)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Android 8.0+ 使用流式Base64解码 LogUtils.i("Android 8.0+ 使用流式Base64解码"); java.util.Base64.Decoder base64Decoder = java.util.Base64.getMimeDecoder(); try (InputStream base64DecodeStream = base64Decoder.wrap(bis); CipherInputStream cis = new CipherInputStream(base64DecodeStream, cipher)) { byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead; while ((bytesRead = cis.read(buffer)) != -1) { bos.write(buffer, 0, bytesRead); // 优化后的检查逻辑(每秒最多1次) long currentTime = System.currentTimeMillis(); if (currentTime - lastCheckTime > 1000) { synchronized (this) { if (currentTime - lastCheckTime > 1000) { if (!HmiJNIImpl.getInstance().mapControl()) { return false; } lastCheckTime = currentTime; } } } } } } } LogUtils.i("文件解密成功,保存为: " + outputFile.getAbsolutePath()); return true; } catch (Exception e) { // 修复5:添加详细错误日志 LogUtils.e(TAG, "解密失败: " + e.getMessage() + "\n文件: " + desDataPath.getAbsolutePath() + "\n密钥: " + key.substring(0, Math.min(4, key.length())) + "***"); return false; } } // 处理Base64数据块:解码->解密->写入 private void processBase64Chunk(ByteArrayOutputStream base64Buffer, Cipher cipher, BufferedOutputStream bos) throws Exception { byte[] base64Data = base64Buffer.toByteArray(); base64Buffer.reset(); byte[] decodedData = Base64.decode(base64Data, Base64.DEFAULT); byte[] decryptedData = cipher.update(decodedData); if (decryptedData != null) { bos.write(decryptedData); } } // 从 .sig 文件读取签名 public static String readSignatureFromFile(String sigFilePath) throws IOException , JSONException{ byte[] fileBytes = new byte[(int) new File(sigFilePath).length()]; try (FileInputStream fis = new FileInputStream(sigFilePath)) { fis.read(fileBytes); } String fileContent = new String(fileBytes).trim(); // 解析 JSON,提取 signature 字 JSONObject json = new JSONObject(fileContent); return json.getString("signature"); } // 获取byte数组格式向量 private static byte[] getIV(String iv) { // 1. 清理密钥 String cleanKey = iv.replaceAll("\\?+$", ""); // 2. 提取第一个!到第二个!之间的部分 String[] parts = cleanKey.split("!"); if (parts.length < 2) { throw new IllegalArgumentException("Invalid key format"); } String prefix = parts[1]; String reversed = new StringBuilder(prefix.substring(0, 7)) .reverse() .append("!") .toString(); reversed = reversed + "abcdefg!"; return reversed.getBytes(StandardCharsets.UTF_8); } // 获取byte数组格式秘钥 private static byte[] getKey(String password) { String cleaned = password.replaceAll("[^!$a-zA-Z0-9]", ""); return cleaned.getBytes(StandardCharsets.UTF_8); } // 获取加密文件的SHA256哈希 public static byte[] getFileHash(String filePath) throws IOException { try{ MessageDigest digest = MessageDigest.getInstance("SHA-256"); File file = new File(filePath); try (FileInputStream fis = new FileInputStream(file)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { digest.update(buffer, 0, bytesRead); } } return digest.digest(); }catch (NoSuchAlgorithmException e){ e.printStackTrace(); return null; } } // 加载验签公钥 public static PublicKey loadPublicKey(String base64PublicKey) throws Exception { base64PublicKey = base64PublicKey.replaceAll("\\s+", ""); base64PublicKey = base64PublicKey.replaceAll("[^A-Za-z0-9+/=]", ""); // 1. Base64解码 byte[] certBytes = Base64.decode(base64PublicKey,Base64.DEFAULT); // 2. 解析证书链(PKCS#7/P7B格式) CertificateFactory cf = CertificateFactory.getInstance("X.509"); ByteArrayInputStream bis = new ByteArrayInputStream(certBytes); List<X509Certificate> certChain = (List<X509Certificate>) cf.generateCertificates(bis); // 3. 提取第一个证书的公钥(通常是终端实体证书) if (certChain.isEmpty()) { throw new IllegalArgumentException("证书链为空!"); } return certChain.get(0).getPublicKey(); } } 分析这两代码
最新发布
09-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值