当前位置: 首页 > news >正文

RK3566-OS11自动更新时区

摘要

本文为实现RK3566-Android11连接Wi-Fi自动更改时区并更新当地时间做出相关修改和记录。为了实现该功能,本文主要针对自动获取设备当前所在的时区进行更改。采用网络定位API(IP或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后,会有一个公网IP;

step2:调用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.java

package 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.java:

package 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:name="android.permission.INTERNET" /> <uses-permission android:name="android.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.mk

LOCAL_SRC_FILES += \ # ... 其他文件 ... $(LOCAL_REL_DIR)/com/android/server/timezone/AutoTimeZoneManagerService.java \ $(LOCAL_REL_DIR)/com/android/server/timezone/NetworkTimeZoneUpdater.java

2.6调试方法

adb shell logcat | grep -E "(AutoTimezoneService|NetworkTimeZoneUpdater)" # 或者查看详细日志 adb logcat -v time -s AutoTimezoneService NetworkTimeZoneUpdater

三、关键技术要点

3.1关键技术及难点

  1. 网络状态监听:使用ConnectivityManager.NetworkCallback监听网络连接事件

  2. IP 地理定位:通过ipapi.co或其他商业 API 获取时区信息

  3. 系统权限:由于是系统服务,天然具有设置时区的权限

  4. 延迟触发:网络连接后延迟几秒执行,确保网络稳定

  5. 错误处理:完善的异常处理和日志记录

3.2生产环境建议

  1. 使用商业 API:将ipapi.co替换为更稳定的商业服务(如 Ipstack、MaxMind)

  2. 添加重试机制:网络失败时自动重试

  3. 频率限制:避免过于频繁的 API 调用

  4. 缓存策略:相同 IP 段时缓存时区信息

  5. 用户设置覆盖:允许用户手动设置时区并禁用自动更新

3.3结论

通过上述方案,您的设备无论从中国寄到美国还是其他国家,只要连接 WiFi 就能自动切换到当地时区,完全不需要 GPS。

http://www.zskr.cn/news/1499739.html

相关文章:

  • 2026年 内蒙建筑膜/家具贴膜/居家隔热防晒膜TOP5推荐:专业防晒隔热与隐私保护膜品牌实力精选! - 品牌发掘
  • 2026北京公司注册代办机构专业度排行:5家主流机构实测对比(附精准选型指南) - 互联网科技品牌测评
  • 3分钟快速上手RollToolsApi:免费API接口的完整使用指南
  • 2026澳洲本地留学移民机构口碑排行 附避坑指南及FAQ - 互联网科技品牌测评
  • pinche_xcx商业变现模式分析:拼车小程序的7大盈利策略
  • 2026年6月最新版合肥第三方CMACNAS甲醛检测治理机构口碑名单:万清CMA检测中心等5家公司深度测评万清CMA检测中心TOP1推荐 - 一修哥咨询
  • 2026年 男士衬衫推荐榜:纯棉短袖衬衫/休闲商务衬衫,质感与版型俱佳之选 - 品牌发掘
  • 为什么选择Step-Audio-EditX?解析3B参数RL模型的核心技术优势
  • 2026深圳同城搬家服务推荐:当日达、响应快、不加价的5家正规搬家机构 - 从来都是英雄出少年
  • MediaManager元数据集成终极指南:3步完成TMDB与TVDB自动化配置
  • 武汉变压器回收公司排行:5家合规服务商盘点 - 起跑123
  • 2026年 护膝推荐最新榜单:广东/珠三角篮球防撞护膝,跑步护膝,羽毛球护膝,健身护膝,氨纶/蜂巢/冰丝面料专业之选 - 品牌发掘
  • 2026年广东喜糖/结婚喜糖/深圳混合喜糖/婚庆喜糖/高端喜糖/宴会喜糖推荐榜:吉佬王、爱哆哆、双囍品牌口碑与甜蜜之选 - 品牌发掘
  • 2026年6月最新版广元第三方CMACNAS甲醛检测治理机构口碑名单:万清CMA检测中心等5家公司深度测评万清CMA检测中心TOP1推荐 - 一修哥咨询
  • DexKit开发者指南:深入理解C++实现的dex解析引擎
  • I2C-SPI-I2S协议介绍
  • 3个秘诀让Continue成为你的终极AI代码审查搭档:如何实现源码可控的智能检查
  • AtCoder abc461_c Variety
  • 青岛红色合伙人防水是什么?楼长修楼官方合作资质全解析 - 青岛防水品牌推荐
  • 深度实战:用MarkItDown构建你的文档转换流水线
  • Comparative-analysis-of-hourly-load-forecasting-using-PatchTST-TFT-NHiTS-and-CatBoost源代码详解:核心组件与实现原理
  • ChatMLX核心功能全解析:多模型支持、隐私保护与39种语言能力
  • 高效跨平台阅读体验:Awaken EPUB阅读器的四大核心优势与实战指南
  • 国际EMBA含金量高吗?2026五大高含金量国际EMBA项目解析 - 品牌2026推荐
  • pinche_xcx扩展功能开发:如何添加拼车费用计算与支付功能
  • 2026年6月最新版包头第三方CMACNAS甲醛检测治理机构口碑名单:万清CMA检测中心等5家公司深度测评万清CMA检测中心TOP1推荐 - 一修哥咨询
  • CodeX Docs进阶开发:从用户到贡献者的成长之路
  • GolangBypassAV反沙箱技术:规避动态检测的关键策略
  • 2026澳洲本地留学移民机构排行 附选型避坑指南 - 互联网科技品牌测评
  • Strecs3D实战案例:悬臂梁模型的填充优化前后对比与效果分析