
摘要本文为实现RK3566-Android11连接Wi-Fi自动更改时区并更新当地时间做出相关修改和记录。为了实现该功能本文主要针对自动获取设备当前所在的时区进行更改。采用网络定位APIIP或Google API获取时区ID结合NTP的UTC时间用设备本地时区数据库计算得到本地时间。设备出厂时预设为美国时区但当在中国开机并连接Wi-Fi后它将自动同步更新到中国地区的时区以及时间而不仅仅是保持美国时区并显示一个换算后的时间。一、方法原理与可行性分析1.1实现方法与原理设备在中国连接 WiFi → 获取到中国的公网 IP → IP 定位 API 返回时区Asia/Shanghai→ 系统时区自动设置为中国时区设备寄到美国后连接 WiFi → 获取到美国的公网 IP → IP 定位 API 返回时区America/New_York→ 系统时区自动更新为美国时区作为系统的一部分这是最专业最符合Android架构的方式。将创建一个新的系统服务例如:AutoTimeZoneService创建如NetworkTimeUpdateService将其作为服务的一个工具类。1.2逻辑步骤step1获取公共IP地址设备连接wifi后会有一个公网IPstep2调用IP地理位置API使用这个IP向一个地理定位服务商发起请求返回的信息中会包含时区信息step3解析并设置时区从API响应中提取时区标识符如America/Toronto并用它来设置系统的时区。1.3实现优势1.精度足够国家/城市级别对于确定时区来说完全够用。2.适用于所有连接Wi-Fi的设备无论是否有GPS模块。3.实现相对简单。1.4放置路径及优势/frameworks/base/services/core/java/com/android/server/timezone/1.系统权限系统服务天生具有SET_TIME和SET_TIME_ZONE权限2.生命周期服务可以随系统启动而启动并在后台监听网络连接状态实现真正的“连接Wi-Fi后自动触发”。3.架构清晰符合 Android 的模块化设计与系统其他部分如网络管理、时间管理能很好地交互。二、具体实现步骤——基于AOSP源码修改2.1创建核心工具类在/frameworks/base/services/core/java/com/android/server/timezone/创建NetworkTimeZoneUpdater.javapackage com.android.server.timezone; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; import android.util.Log; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.TimeZone; public class NetworkTimeZoneUpdater { private static final String TAG NetworkTimeZoneUpdater; //private static final String IPAPI_URL https://ipapi.co/json/; private final Context mContext; private final ConnectivityManager mConnectivityManager; private final Handler mHandler; private ConnectivityManager.NetworkCallback mNetworkCallback; public NetworkTimeZoneUpdater(Context context) { mContext context; mConnectivityManager context.getSystemService(ConnectivityManager.class); mHandler new Handler(Looper.getMainLooper()); } public void startMonitoring() { Log.i(TAG, Starting network monitoring for auto timezone update); NetworkRequest request new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) .build(); mNetworkCallback new ConnectivityManager.NetworkCallback() { Override public void onAvailable(Network network) { Log.d(TAG, Network available, triggering timezone update); // 延迟执行确保网络完全就绪 mHandler.postDelayed(() - updateTimezoneFromNetwork(), 5000); } Override public void onLost(Network network) { Log.d(TAG, Network lost); } }; mConnectivityManager.registerNetworkCallback(request, mNetworkCallback); } public void stopMonitoring() { if (mNetworkCallback ! null) { mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mNetworkCallback null; } } private void updateTimezoneFromNetwork() { new Thread(() - { try { String detectedTimezone getTimezoneFromIP(); if (detectedTimezone ! null !detectedTimezone.isEmpty()) { // 获取当前系统时区 String currentTimezone TimeZone.getDefault().getID(); // 只有当检测到的时区与当前时区不同时才更新 if (!detectedTimezone.equals(currentTimezone)) { setSystemTimezone(detectedTimezone); Log.i(TAG, Timezone updated from currentTimezone to: detectedTimezone); } else { Log.d(TAG, Timezone unchanged: currentTimezone); } } } catch (Exception e) { Log.e(TAG, Failed to update timezone from network, e); } }).start(); } private String getTimezoneFromIP() { Log.d(TAG, Attempting to get timezone from IP API); // 尝试多个备用API String[] apiUrls { https://ipapi.co/json/, http://ip-api.com/json/, https://extreme-ip-lookup.com/json/ }; for (String apiUrl : apiUrls) { try { String timezone fetchTimezoneFromAPI(apiUrl); if (timezone ! null) { Log.d(TAG, Successfully got timezone from apiUrl : timezone); return timezone; } } catch (Exception e) { Log.w(TAG, API failed: apiUrl, e); } } return null; } private String fetchTimezoneFromAPI(String apiUrl) { HttpURLConnection connection null; BufferedReader reader null; try { URL url new URL(apiUrl); connection (HttpURLConnection) url.openConnection(); connection.setRequestMethod(GET); connection.setConnectTimeout(10000); connection.setReadTimeout(10000); Log.d(TAG, Connecting to: apiUrl); int responseCode connection.getResponseCode(); Log.d(TAG, HTTP Response Code: responseCode); if (responseCode HttpURLConnection.HTTP_OK) { InputStream inputStream connection.getInputStream(); reader new BufferedReader(new InputStreamReader(inputStream)); StringBuilder response new StringBuilder(); String line; while ((line reader.readLine()) ! null) { response.append(line); } String timezone parseTimezoneFromJson(response.toString()); if (timezone ! null) { Log.d(TAG, Successfully parsed timezone: timezone); } return timezone; } else { Log.e(TAG, HTTP Error: responseCode); } } catch (Exception e) { Log.e(TAG, Error getting timezone from API: apiUrl, e); } finally { if (reader ! null) { try { reader.close(); } catch (IOException e) { Log.e(TAG, Error closing reader, e); } } if (connection ! null) { connection.disconnect(); } } return null; } private String parseTimezoneFromJson(String json) { // 简化的JSON解析 if (json ! null json.contains(\timezone\:)) { try { int start json.indexOf(\timezone\:\) 12; int end json.indexOf(\, start); if (start 11 end start) { return json.substring(start, end); } } catch (Exception e) { Log.e(TAG, Error parsing timezone from JSON, e); } } else if (json ! null json.contains(\timeZone\:)) { // 处理 ip-api.com 的格式 try { int start json.indexOf(\timeZone\:\) 12; int end json.indexOf(\, start); if (start 11 end start) { return json.substring(start, end); } } catch (Exception e) { Log.e(TAG, Error parsing timeZone from JSON, e); } } Log.d(TAG, No timezone found in JSON: (json ! null ? json.substring(0, Math.min(100, json.length())) : null)); return null; } private void setSystemTimezone(String timezoneId) { try { // 方法1: 使用SystemProperties android.os.SystemProperties.set(persist.sys.timezone, timezoneId); // 方法2: 使用alarm命令需要root if (isRooted()) { Runtime.getRuntime().exec(su -c setprop persist.sys.timezone timezoneId ); } // 方法3: 发送广播通知系统时区变化 Intent intent new Intent(Intent.ACTION_TIMEZONE_CHANGED); intent.putExtra(time-zone, timezoneId); mContext.sendBroadcast(intent); Log.i(TAG, Successfully set timezone to: timezoneId); } catch (Exception e) { Log.e(TAG, Failed to set timezone: timezoneId, e); } } private boolean isRooted() { return new File(/system/bin/su).exists() || new File(/system/xbin/su).exists(); } }2.2创建系统服务基于2.1的目录创建AutoTimezoneService.javapackage com.android.server.timezone; import android.content.Context; import android.util.Log; import com.android.server.SystemService; public class AutoTimezoneService extends SystemService { private static final String TAG AutoTimezoneService; private NetworkTimeZoneUpdater mTimeZoneUpdater; public AutoTimezoneService(Context context) { super(context); mTimeZoneUpdater new NetworkTimeZoneUpdater(context); } Override public void onStart() { Log.i(TAG, Starting AutoTimezoneService); } Override public void onBootPhase(int phase) { Log.i(TAG, onBootPhase: phase); if (phase SystemService.PHASE_SYSTEM_SERVICES_READY) { Log.i(TAG, System services ready, starting timezone monitoring); mTimeZoneUpdater.startMonitoring(); } else if (phase SystemService.PHASE_BOOT_COMPLETED) { Log.i(TAG, Boot completed); } } // 移除错误的Override注解 public void onShutdown() { Log.i(TAG, Shutting down AutoTimezoneService); mTimeZoneUpdater.stopMonitoring(); } }2.3在 SystemServer 中注册服务在\frameworks\base\services\java\com\android\server/SystemServer.java的startOtherServices方法中添加// 在 SystemServer.java 的 startOtherServices() 方法中 // 先找到这些网络相关的服务启动代码 try { t.traceBegin(StartNetworkManagementService); networkManagement NetworkManagementService.create(context); ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement); t.traceEnd(); } catch (Throwable e) { reportWtf(starting NetworkManagement Service, e); } // ... 其他网络服务 ... try { t.traceBegin(StartConnectivityService); connectivity new ConnectivityService( context, networkManagement, networkStats, networkPolicy); ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity); networkStats.bindConnectivityManager(connectivity); networkPolicy.bindConnectivityManager(connectivity); t.traceEnd(); } catch (Throwable e) { reportWtf(starting Connectivity Service, e); } // 在 ConnectivityService 启动之后添加您的服务 t.traceBegin(StartAutoTimezoneService); try { mSystemServiceManager.startService(AutoTimezoneService.class); } catch (Throwable e) { reportWtf(starting AutoTimezoneService, e); } t.traceEnd();在SystemServer.java文件开头添加导入import com.android.server.timezone.AutoTimezoneService;2.4添加网络权限确保在合适的 Android.mk 或 Android.bp 中添加网络权限uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE /2.5检查编译配置确认/frameworks/base/services/core/Android.bp包含了您的服务文件filegroup { name: services.core-sources, srcs: [java/**/*.java], path: java, visibility: [//frameworks/base/services], }查看是否有这部分代码有则无需自行添加。意思是在这个/frameworks/base/services路径下面的所有.java文件都会被编译。若不含有此部分代码则需添加在这部分里面。java_library_static { name: services.core, defaults: [services.core_defaults], srcs: [ // ... 其他文件 ... java/com/android/server/timezone/AutoTimeZoneManagerService.java, java/com/android/server/timezone/NetworkTimeZoneUpdater.java, ], // ... 其他配置 ... }如果使用Android.mk编辑/frameworks/base/services/core/Android.mkLOCAL_SRC_FILES \ # ... 其他文件 ... $(LOCAL_REL_DIR)/com/android/server/timezone/AutoTimeZoneManagerService.java \ $(LOCAL_REL_DIR)/com/android/server/timezone/NetworkTimeZoneUpdater.java2.6调试方法adb shell logcat | grep -E (AutoTimezoneService|NetworkTimeZoneUpdater) # 或者查看详细日志 adb logcat -v time -s AutoTimezoneService NetworkTimeZoneUpdater三、关键技术要点3.1关键技术及难点网络状态监听使用ConnectivityManager.NetworkCallback监听网络连接事件IP 地理定位通过ipapi.co或其他商业 API 获取时区信息系统权限由于是系统服务天然具有设置时区的权限延迟触发网络连接后延迟几秒执行确保网络稳定错误处理完善的异常处理和日志记录3.2生产环境建议使用商业 API将ipapi.co替换为更稳定的商业服务如 Ipstack、MaxMind添加重试机制网络失败时自动重试频率限制避免过于频繁的 API 调用缓存策略相同 IP 段时缓存时区信息用户设置覆盖允许用户手动设置时区并禁用自动更新3.3结论通过上述方案您的设备无论从中国寄到美国还是其他国家只要连接 WiFi 就能自动切换到当地时区完全不需要 GPS。