Android车载系统时间同步方案具体实现
下面我将详细介绍各种时间同步方案的具体实现代码,包括完整的实现逻辑和关键代码片段。
一、NTP时间同步实现
完整实现类
java
public class NtpTimeSync {
private static final String TAG = "NtpTimeSync";
private static final String NTP_SERVER = "pool.ntp.org";
private static final int TIMEOUT_MS = 10_000;
public interface TimeSyncCallback {
void onSuccess(long newTime);
void onFailure(String error);
}
public static void syncSystemTime(Context context, TimeSyncCallback callback) {
if (!isNetworkAvailable(context)) {
callback.onFailure("Network not available");
return;
}
new AsyncTask<Void, Void, Long>() {
@Override
protected Long doInBackground(Void... voids) {
SntpClient client = new SntpClient();
try {
if (client.requestTime(NTP_SERVER, TIMEOUT_MS)) {
long now = client.getNtpTime() + SystemClock.elapsedRealtime() -
client.getNtpTimeReference();
return now;
}
} catch (Exception e) {
Log.e(TAG, "NTP sync failed", e);
}
return null;
}
@Override
protected void onPostExecute(Long result) {
if (result != null) {
if (hasSetTimePermission(context)) {
SystemClock.setCurrentTimeMillis(result);
callback.onSuccess(result);
} else {
callback.onFailure("No SET_TIME permission");
}
} else {
callback.onFailure("NTP sync failed");
}
}
}.execute();
}
private static boolean isNetworkAvailable(Context context) {
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
return netInfo != null && netInfo.isConnected();
}
private static boolean hasSetTimePermission(Context context) {
return context.checkSelfPermission(android.Manifest.permission.SET_TIME) ==
PackageManager.PERMISSION_GRANTED;
}
}
使用示例
java
NtpTimeSync.syncSystemTime(context, new NtpTimeSync.TimeSyncCallback() {
@Override
public void onSuccess(long newTime) {
Log.d(TAG, "Time synchronized successfully: " + new Date(newTime));
}
@Override
public void onFailure(String error) {
Log.e(TAG, "Time sync failed: " + error);
}
});
二、GPS时间同步实现
完整实现类
java
public class GpsTimeSync {
private static final String TAG = "GpsTimeSync";
private LocationManager locationManager;
private GpsTimeListener listener;
private Context context;
public interface GpsTimeListener {
void onGpsTimeReceived(long gpsTime);
void onGpsStatusChanged(boolean available);
}
public GpsTimeSync(Context context, GpsTimeListener listener) {
this.context = context.getApplicationContext();
this.listener = listener;
this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
}
public void startListening() {
if (hasLocationPermission()) {
try {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
0,
0,
locationListener
);
// 检查GPS状态变化
locationManager.addGpsStatusListener(gpsStatusListener);
} catch (Exception e) {
Log.e(TAG, "Failed to request location updates", e);
}
} else {
Log.w(TAG, "Location permission not granted");
}
}
public void stopListening() {
locationManager.removeUpdates(locationListener);
locationManager.removeGpsStatusListener(gpsStatusListener);
}
private final LocationListener locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
if (location != null) {
long gpsTime = location.getTime();
listener.onGpsTimeReceived(gpsTime);
}
}
@Override public void onStatusChanged(String provider, int status, Bundle extras) {}
@Override public void onProviderEnabled(String provider) {}
@Override public void onProviderDisabled(String provider) {}
};
private final GpsStatus.Listener gpsStatusListener = new GpsStatus.Listener() {
@Override
public void onGpsStatusChanged(int event) {
boolean gpsFixed = false;
if (event == GpsStatus.GPS_EVENT_SATELLITE_STATUS) {
GpsStatus status = locationManager.getGpsStatus(null);
Iterable<GpsSatellite> satellites = status.getSatellites();
int satellitesCount = 0;
for (GpsSatellite satellite : satellites) {
if (satellite.usedInFix()) {
satellitesCount++;
}
}
gpsFixed = satellitesCount >= 3;
}
listener.onGpsStatusChanged(gpsFixed);
}
};
private boolean hasLocationPermission() {
return ContextCompat.checkSelfPermission(context,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
}
}
使用示例
java
GpsTimeSync gpsTimeSync = new GpsTimeSync(context, new GpsTimeSync.GpsTimeListener() {
@Override
public void onGpsTimeReceived(long gpsTime) {
// 同步系统时间
if (hasSetTimePermission()) {
SystemClock.setCurrentTimeMillis(gpsTime);
}
}
@Override
public void onGpsStatusChanged(boolean available) {
Log.d(TAG, "GPS availability changed: " + available);
}
});
// 开始监听
gpsTimeSync.startListening();
// 停止监听(在适当的时候调用)
// gpsTimeSync.stopListening();
三、CAN总线时间同步实现
JNI部分实现 (C代码)
c
#include <jni.h>
#include <android/log.h>
#include <canbus/can.h>
#define TAG "CANTimeSync"
// CAN接收回调
void can_receive_callback(can_frame_t *frame) {
if (frame->can_id == 0x123) { // 假设0x123是时间信息帧ID
uint64_t vehicle_time = 0;
memcpy(&vehicle_time, frame->data, sizeof(uint64_t));
// 调用Java层方法
JNIEnv *env;
(*g_vm)->AttachCurrentThread(g_vm, &env, NULL);
jclass cls = (*env)->GetObjectClass(env, g_java_obj);
jmethodID method = (*env)->GetMethodID(env, cls, "onCanTimeReceived", "(J)V");
(*env)->CallVoidMethod(env, g_java_obj, method, (jlong)vehicle_time);
(*g_vm)->DetachCurrentThread(g_vm);
}
}
// JNI初始化
JNIEXPORT void JNICALL
Java_com_example_CanTimeSync_initCanBus(JNIEnv *env, jobject instance) {
// 保存Java对象和VM引用
g_java_obj = (*env)->NewGlobalRef(env, instance);
(*env)->GetJavaVM(env, &g_vm);
// 初始化CAN总线
if (can_init() != 0) {
__android_log_write(ANDROID_LOG_ERROR, TAG, "CAN init failed");
return;
}
// 设置接收回调
can_set_receive_callback(can_receive_callback);
}
// JNI关闭
JNIEXPORT void JNICALL
Java_com_example_CanTimeSync_closeCanBus(JNIEnv *env, jobject instance) {
can_close();
(*env)->DeleteGlobalRef(env, g_java_obj);
}
Java层实现
java
public class CanTimeSync {
private static final String TAG = "CanTimeSync";
static {
System.loadLibrary("cantimesync");
}
public interface CanTimeListener {
void onCanTimeReceived(long vehicleTime);
void onCanError(String error);
}
private CanTimeListener listener;
public CanTimeSync(CanTimeListener listener) {
this.listener = listener;
}
public void start() {
try {
initCanBus();
} catch (Exception e) {
listener.onCanError("CAN init failed: " + e.getMessage());
}
}
public void stop() {
closeCanBus();
}
// 由JNI调用的方法
private void onCanTimeReceived(long vehicleTime) {
listener.onCanTimeReceived(vehicleTime);
}
// Native方法
private native void initCanBus();
private native void closeCanBus();
}
使用示例
java
CanTimeSync canTimeSync = new CanTimeSync(new CanTimeSync.CanTimeListener() {
@Override
public void onCanTimeReceived(long vehicleTime) {
// 转换时间格式并同步
long systemTime = convertCanTimeToSystemTime(vehicleTime);
if (hasSetTimePermission()) {
SystemClock.setCurrentTimeMillis(systemTime);
}
}
@Override
public void onCanError(String error) {
Log.e(TAG, "CAN error: " + error);
}
});
// 启动CAN时间同步
canTimeSync.start();
// 停止同步(在适当的时候调用)
// canTimeSync.stop();
四、PTP时间同步实现
PTP客户端实现
java
public class PtpTimeSync {
private static final String TAG = "PtpTimeSync";
private static final String PTP_IFACE = "eth0";
public interface PtpSyncListener {
void onPtpSyncComplete(long offsetNanos);
void onPtpSyncError(String error);
}
public static void syncWithPtp(PtpSyncListener listener) {
new Thread(() -> {
try {
Process process = Runtime.getRuntime().exec("ptp4l -i " + PTP_IFACE + " -m");
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("offset")) {
// 解析偏移量: master offset -1234 s2 freq
String[] parts = line.split("\\s+");
if (parts.length >= 4) {
long offset = Long.parseLong(parts[3]);
listener.onPtpSyncComplete(offset);
}
}
}
int exitCode = process.waitFor();
if (exitCode != 0) {
listener.onPtpSyncError("PTP process exited with code " + exitCode);
}
} catch (Exception e) {
listener.onPtpSyncError("PTP sync failed: " + e.getMessage());
}
}).start();
}
public static void adjustSystemClock(long offsetNanos) {
long millis = offsetNanos / 1_000_000;
if (millis != 0) {
long current = System.currentTimeMillis();
SystemClock.setCurrentTimeMillis(current + millis);
}
}
}
使用示例
java
PtpTimeSync.syncWithPtp(new PtpTimeSync.PtpSyncListener() {
@Override
public void onPtpSyncComplete(long offsetNanos) {
Log.d(TAG, "PTP offset: " + offsetNanos + " ns");
if (Math.abs(offsetNanos) > 100_000) { // 如果偏移大于100μs
PtpTimeSync.adjustSystemClock(offsetNanos);
}
}
@Override
public void onPtpSyncError(String error) {
Log.e(TAG, "PTP error: " + error);
}
});
五、混合时间同步策略实现
综合时间同步管理器
java
public class TimeSyncManager {
private static final String TAG = "TimeSyncManager";
private static final long SYNC_INTERVAL = 60_000; // 1分钟
private Context context;
private Handler handler;
private NtpTimeSync.TimeSyncCallback ntpCallback;
private GpsTimeSync gpsTimeSync;
private CanTimeSync canTimeSync;
private long lastSyncTime;
private TimeSource currentSource = TimeSource.LOCAL;
public enum TimeSource {
GPS, NTP, CAN, PTP, LOCAL
}
public interface SyncStatusListener {
void onSyncStatusChanged(TimeSource source, long time, long offset);
}
public TimeSyncManager(Context context) {
this.context = context.getApplicationContext();
this.handler = new Handler(Looper.getMainLooper());
initCallbacks();
initGpsSync();
initCanSync();
}
private void initCallbacks() {
ntpCallback = new NtpTimeSync.TimeSyncCallback() {
@Override
public void onSuccess(long newTime) {
long offset = newTime - System.currentTimeMillis();
updateTime(TimeSource.NTP, newTime, offset);
}
@Override
public void onFailure(String error) {
Log.w(TAG, "NTP sync failed: " + error);
}
};
}
private void initGpsSync() {
gpsTimeSync = new GpsTimeSync(context, new GpsTimeSync.GpsTimeListener() {
@Override
public void onGpsTimeReceived(long gpsTime) {
long offset = gpsTime - System.currentTimeMillis();
updateTime(TimeSource.GPS, gpsTime, offset);
}
@Override
public void onGpsStatusChanged(boolean available) {
if (available) {
Log.d(TAG, "GPS available, preferring GPS time");
}
}
});
gpsTimeSync.startListening();
}
private void initCanSync() {
canTimeSync = new CanTimeSync(new CanTimeSync.CanTimeListener() {
@Override
public void onCanTimeReceived(long vehicleTime) {
long systemTime = convertCanTimeToSystemTime(vehicleTime);
long offset = systemTime - System.currentTimeMillis();
updateTime(TimeSource.CAN, systemTime, offset);
}
@Override
public void onCanError(String error) {
Log.w(TAG, "CAN sync error: " + error);
}
});
canTimeSync.start();
}
public void startPeriodicSync() {
handler.postDelayed(syncRunnable, SYNC_INTERVAL);
}
public void stopPeriodicSync() {
handler.removeCallbacks(syncRunnable);
gpsTimeSync.stopListening();
canTimeSync.stop();
}
private Runnable syncRunnable = new Runnable() {
@Override
public void run() {
syncTime();
handler.postDelayed(this, SYNC_INTERVAL);
}
};
private void syncTime() {
// 根据优先级尝试各种同步方法
if (isGpsAvailable()) {
// GPS时间已经通过回调处理
} else if (isNetworkAvailable()) {
NtpTimeSync.syncSystemTime(context, ntpCallback);
} else if (isCanAvailable()) {
// CAN时间已经通过回调处理
} else {
Log.w(TAG, "No time source available");
}
lastSyncTime = System.currentTimeMillis();
}
private void updateTime(TimeSource source, long newTime, long offset) {
// 只接受合理的时间偏移
if (Math.abs(offset) > 10_000) { // 10秒以上偏移
Log.w(TAG, "Large time offset detected: " + offset + "ms");
// 渐进式调整
long adjustedTime = System.currentTimeMillis() + (offset / 2);
if (hasSetTimePermission()) {
SystemClock.setCurrentTimeMillis(adjustedTime);
}
}
currentSource = source;
// 通知监听器
for (SyncStatusListener listener : listeners) {
listener.onSyncStatusChanged(source, newTime, offset);
}
}
// 工具方法
private boolean isGpsAvailable() {
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
return lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
private boolean isNetworkAvailable() {
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
return netInfo != null && netInfo.isConnected();
}
private boolean isCanAvailable() {
// 实现检查CAN总线可用性的逻辑
return true;
}
private boolean hasSetTimePermission() {
return context.checkSelfPermission(Manifest.permission.SET_TIME) ==
PackageManager.PERMISSION_GRANTED;
}
private long convertCanTimeToSystemTime(long canTime) {
// 实现CAN时间到系统时间的转换逻辑
return canTime;
}
// 监听器管理
private List<SyncStatusListener> listeners = new ArrayList<>();
public void addSyncStatusListener(SyncStatusListener listener) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
public void removeSyncStatusListener(SyncStatusListener listener) {
listeners.remove(listener);
}
}
使用示例
java
TimeSyncManager timeSyncManager = new TimeSyncManager(context);
timeSyncManager.addSyncStatusListener(new TimeSyncManager.SyncStatusListener() {
@Override
public void onSyncStatusChanged(TimeSyncManager.TimeSource source,
long time, long offset) {
Log.d(TAG, "Time source: " + source +
", offset: " + offset + "ms");
}
});
// 开始周期性同步
timeSyncManager.startPeriodicSync();
// 停止同步(在适当的时候调用)
// timeSyncManager.stopPeriodicSync();
六、权限配置
所有时间同步方案都需要在AndroidManifest.xml中添加相应权限:
xml
<uses-permission android:name="android.permission.SET_TIME" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
对于Android 6.0+,还需要在运行时请求危险权限:
java
private static final int PERMISSION_REQUEST_CODE = 100;
private String[] requiredPermissions = {
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.SET_TIME
};
private void checkPermissions() {
List<String> missingPermissions = new ArrayList<>();
for (String perm : requiredPermissions) {
if (ContextCompat.checkSelfPermission(this, perm)
!= PackageManager.PERMISSION_GRANTED) {
missingPermissions.add(perm);
}
}
if (!missingPermissions.isEmpty()) {
ActivityCompat.requestPermissions(this,
missingPermissions.toArray(new String[0]),
PERMISSION_REQUEST_CODE);
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions,
int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_CODE) {
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "Permission denied: " + permissions[i]);
}
}
}
}
以上代码提供了Android车载系统各种时间同步方案的完整实现,您可以根据实际需求进行修改和扩展。