归筠溪 发表于 2025-7-17 17:04:23

Android车载系统时间同步方案具体实现

Android车载系统时间同步方案具体实现

下面我将详细介绍各种时间同步方案的具体实现代码,包括完整的实现逻辑和关键代码片段。
一、NTP时间同步实现

完整实现类

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;
    }
}使用示例

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时间同步实现

完整实现类

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;
    }
}使用示例

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代码)

#include <jni.h>
#include
#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层实现

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();
}使用示例

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客户端实现

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);
                            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);
      }
    }
}使用示例

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);
    }
});五、混合时间同步策略实现

综合时间同步管理器

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);
    }
}使用示例

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中添加相应权限:
对于Android 6.0+,还需要在运行时请求危险权限:
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),
            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 != PackageManager.PERMISSION_GRANTED) {
                Log.w(TAG, "Permission denied: " + permissions);
            }
      }
    }
}以上代码提供了Android车载系统各种时间同步方案的完整实现,您可以根据实际需求进行修改和扩展。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Android车载系统时间同步方案具体实现