第 33 章:位置服务
位置服务是 Android 中对隐私高度敏感但又不可或缺的子系统之一。它将卫星接收器、基站数据库、Wi‑Fi 指纹识别引擎以及传感器融合算法封装在一套统一的框架 API——LocationManager背后,同时实施多层权限模型,区分精细位置、粗略位置、前台访问与后台访问。本章将逐层梳理该软件栈:从公开 SDK 接口,到LocationManagerService、GNSS HAL AIDL 契约、融合定位与网络定位提供者、地理围栏、地理编码,以及将位置转换为时区标识的 GeoTZ 模块。本章覆盖 Android 17 下该子系统的现状:GNSS HAL 已升级至 AIDL 第 7 版本,结构化 GNSS 辅助接口取代旧设备上不透明的 PSDS 二进制块;大量功能标志被移除,若干曾经的实验行为(人口密度粗略位置、GNSS 辅助数据注入)成为默认代码路径。除非特别说明,所有源码路径均相对于 AOSP 根目录。33.1 位置服务架构33.1.1 三大核心支柱Android 位置子系统基于三个概念支柱构建:支柱用途关键产物提供者抽象在统一接口之下屏蔽各类定位引擎的差异AbstractLocationProvider请求多路复用将数百个应用请求合并为每个提供者的一条最优请求LocationProviderManager权限强制校验通过精细 / 粗略 / 后台权限校验管控每一条数据流LocationPermissions33.1.2 分层示意图来源:frameworks/base/location/java/android/location/LocationManager.java—— 标记@SystemService(Context.LOCATION_SERVICE)的公开 SDK 类。33.1.3 提供者名称常量LocationManagerService定义用于标记各个提供者的字符串常量,与LocationManager内常量保持一致:常量取值说明GPS_PROVIDER"gps"基于卫星(GNSS)定位NETWORK_PROVIDER"network"基站 / Wi‑Fi 定位FUSED_PROVIDER"fused"传感器融合最优定位结果PASSIVE_PROVIDER"passive"监听全部位置更新,不主动发起定位GPS_HARDWARE_PROVIDER"gps_hardware"原始 GNSS HAL,需要LOCATION_HARDWARE权限33.1.4 启动序列LocationManagerService.Lifecycle继承SystemService。其启动阶段回调定义了精确执行顺序:onStart() publishBinderService(Context.LOCATION_SERVICE, mService) onBootPhase(PHASE_SYSTEM_SERVICES_READY) SystemInjector.onSystemReady() LocationManagerService.onSystemReady() onBootPhase(PHASE_THIRD_PARTY_APPS_CAN_START) LocationManagerService.onSystemThirdPartyAppsCanStart() 1. create NetworkProvider (ProxyLocationProvider) 2. create FusedProvider (ProxyLocationProvider, direct‑boot aware) 3. create GnssNative + GnssManagerService (also registers the GNSS assistance proxy in GnssManagerService.onSystemReady()) 4. create GnssLocationProvider, or a ProxyLocationProvider GNSS overlay (ACTION_GNSS_PROVIDER); optionally also expose the raw HAL as the "gps_hardware" provider 5. bind GeocodeProvider (ProxyGeocodeProvider) 6. bind PopulationDensityProvider (ProxyPopulationDensityProvider) 7. bind HardwareActivityRecognitionProxy (unless Flags.disableHardwareAr()) 8. bind GeofenceProxy - GeofenceHardwareService来源:frameworks/base/services/core/java/com/android/server/location/LocationManagerService.java,onSystemThirdPartyAppsCanStart()第 495‑638 行。网络提供者要在 GNSS 提供者之前创建,源码注释说明原因:“network provider should always be initialized before the gps provider since the gps provider has unfortunate hard dependencies on the network provider” “网络提供者必须在 gps 提供者之前初始化,因为 gps 提供者对网络提供者存在无法规避的强依赖”Android 17 带来三处关键变更:GNSS 提供者支持代理覆盖层。当config_useGnssHardwareProvider为 false 时,LMS 首先通过ProxyLocationProvider.create(..., ACTION_GNSS_PROVIDER, config_enableGnssLocationOverlay, ...)绑定外部 GNSS 提供者;若无覆盖层,则回退使用进程内GnssLocationProvider。如果存在覆盖层,原始 HAL 仍然独立通过GPS_HARDWARE_PROVIDER对外暴露(受LOCATION_HARDWARE权限保护),因为 GNSS HAL 仅支持单个客户端。人口密度功能不再受功能标志控制。ProxyPopulationDensityProvider无条件注册;曾经用于开关该功能的population_density_provider与density_based_coarse_locations标志已被清理,唯一约束条件是设备上是否存在对应提供者服务(见 33.5.8 节)。GNSS 辅助代理移入 GNSS 子系统。不再从onSystemThirdPartyAppsCanStart()执行绑定,ProxyGnssAssistanceProvider现在在GnssManagerService.onSystemReady()内部完成注册(见 33.10.5 节)。33.1.5 源码文件总览位置子系统分布在多个目录,完整源码树映射如下:frameworks/base/ location/java/android/location/ LocationManager.java -- 公开SDK API Geocoder.java -- 正向/反向地理编码API Geofence.java -- 地理围栏定义 Location.java -- 位置数据对象 LocationRequest.java -- 定位请求参数 GnssStatus.java -- 卫星状态 GnssMeasurement.java -- 框架层原始观测值 GnssClock.java -- GNSS时钟状态 GnssCapabilities.java -- HAL能力封装 GnssAntennaInfo.java -- 天线相位中心数据 Criteria.java -- 旧版提供者选择条件 Address.java -- 地理编码得到的地址 services/core/java/com/android/server/location/ LocationManagerService.java -- 核心系统服务 LocationPermissions.java -- 权限校验逻辑 LocationShellCommand.java -- adb shell cmd location HardwareActivityRecognitionProxy.java geofence/ GeofenceManager.java -- 软件地理围栏引擎 GeofenceProxy.java -- 硬件地理围栏桥接层 gnss/ GnssManagerService.java -- GNSS子系统管理器 GnssLocationProvider.java -- GNSS定位提供者 GnssConfiguration.java -- GNSS配置管理 GnssMetrics.java -- 性能指标统计 GnssListenerMultiplexer.java GnssMeasurementsProvider.java GnssNavigationMessageProvider.java GnssStatusProvider.java GnssNmeaProvider.java GnssAntennaInfoProvider.java GnssGeofenceProxy.java GnssPositionMode.java GnssPowerStats.java GnssPsdsDownloader.java GnssSatelliteBlocklistHelper.java GnssVisibilityControl.java GnssNetworkConnectivityHandler.java ExponentialBackOff.java NetworkTimeHelper.java -- NTP时间抽象策略 NtpNetworkTimeHelper.java TimeDetectorNetworkTimeHelper.java hal/ GnssNative.java -- 对接HAL的JNI桥接层 provider/ AbstractLocationProvider.java LocationProviderController.java -- 提供者控制接口 LocationProviderManager.java PassiveLocationProvider.java PassiveLocationProviderManager.java MockLocationProvider.java MockableLocationProvider.java StationaryThrottlingLocationProvider.java DelegateLocationProvider.java proxy/ ProxyLocationProvider.java ProxyGeocodeProvider.java ProxyPopulationDensityProvider.java ProxyGnssAssistanceProvider.java injector/ Injector.java -- 依赖注入接口 (UserInfoHelper, SettingsHelper, AlarmHelper, ... and their System* concrete implementations) listeners/ ListenerMultiplexer.java, ListenerRegistration.java, BinderListenerRegistration.java, PendingIntentListenerRegistration.java settings/ LocationSettings.java LocationUserSettings.java fudger/ LocationFudger.java LocationFudgerCache.java altitude/ AltitudeService.java countrydetector/ ComprehensiveCountryDetector.java, LocationBasedCountryDetector.java contexthub/ ContextHubService.java and the Context Hub / endpoint brokers eventlog/ LocationEventLog.java hardware/interfaces/gnss/ aidl/android/hardware/gnss/ IGnss.aidl -- GNSS HAL根接口(V7) IGnssCallback.aidl -- 框架回调与能力位掩码 GnssConstellationType.aidl -- 卫星星座枚举 GnssMeasurement.aidl -- 原始观测值 GnssSignalType.aidl -- 信号类型描述符 IGnssGeofence.aidl -- 硬件地理围栏 IGnssMeasurementInterface.aidl IGnssBatching.aidl IGnssPsds.aidl IGnssConfiguration.aidl IGnssPowerIndication.aidl IGnssDebug.aidl IGnssAntennaInfo.aidl IAGnss.aidl IAGnssRil.aidl gnss_assistance/ -- 结构化辅助数据HAL(V5及以上) IGnssAssistanceInterface.aidl, GnssAssistance.aidl, and the per‑constellation ephemeris/almanac/ionospheric model parcelables measurement_corrections/ visibility_control/ packages/modules/GeoTZ/ locationtzprovider/ -- TimeZoneProviderService geotz_lookup/ -- 基于S2的时区查找 s2storage/ -- S2几何存储 output_data/ -- tzs2.dat 二进制数据33.1.6 类继承关所有提供者的控制面由包私有接口LocationProviderController定义(setRequest、start、stop、flush、sendExtraCommand);AbstractLocationProvider通过内部Controller实现该接口,向LocationProviderManager暴露自身状态。来源:frameworks/base/services/core/java/com/android/server/location/provider/(AbstractLocationProvider.java、DelegateLocationProvider.java、StationaryThrottlingLocationProvider.java、MockableLocationProvider.java、LocationProviderController.java)。33.2 LocationManagerServiceLocationManagerService(简称 LMS)是所有LocationManager调用背后唯一的系统服务。它实现ILocationManager.StubBinder 接口,应用通过Context.getSystemService(Context.LOCATION_SERVICE)发起调用。来源:frameworks/base/services/core/java/com/android/server/location/LocationManagerService.java,Android17 版本共 2073 行。33.2.1 成员与数据结构public class LocationManagerService extends ILocationManager.Stub implements LocationProviderManager.StateChangedListener { final Object mLock = new Object(); private final Context mContext; private final Injector mInjector; private final GeofenceManager mGeofenceManager; private volatile @Nullable GnssManagerService mGnssManagerService; private ProxyGeocodeProvider mGeocodeProvider; private final PassiveLocationProviderManager mPassiveManager; // CopyOnWriteArrayList: 写操作需要加锁,读操作无锁 final CopyOnWriteArrayListLocationProviderManager mProviderManagers; }mProviderManagers列表中,每一个注册的提供者对应一个LocationProviderManager实例。CopyOnWriteArrayList模式保证高频getLocationProviderManager(name)查找时读路径无锁。33.2.2 依赖注入模式LMS 使用Injector接口,解除与 Android 系统服务之间的强耦合。使得无需完整system_server环境即可完成单元测试。每一个 Helper 都有对应的System*具体实现(例如SystemSettingsHelper、SystemEmergencyHelper),封装真实系统 API 调用。来源:frameworks/base/services/core/java/com/android/server/location/injector/(20 + 文件)。33.2.3 提供者管理添加提供者void addLocationProviderManager( LocationProviderManager manager, @Nullable AbstractLocationProvider realProvider) { synchronized (mProviderManagers) { manager.startManager(this); if (realProvider != null) { int defaultStationaryThrottlingSetting = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WATCH) ? 0 : 1; boolean enableStationaryThrottling = Settings.Global.getInt( mContext.getContentResolver(), Settings.Global.LOCATION_ENABLE_STATIONARY_THROTTLE, defaultStationaryThrottlingSetting) != 0; // In Android 17 throttling is only ever applied to the GPS provider, // and only when the keep_gnss_stationary_throttling flag is on. if (!(Flags.keepGnssStationaryThrottling() enableStationaryThrottling GPS_PROVIDER.equals(manager.getName()))) { enableStationaryThrottling = false; } if (enableStationaryThrottling) { realProvider = new StationaryThrottlingLocationProvider( manager.getName(), mInjector, realProvider); } } manager.setRealProvider(realProvider); mProviderManagers.add(manager); } }装饰器StationaryThrottlingLocationProvider:当设备进入 Doze 休眠、加速度传感器判定设备静止时,降低定位频率;不再向硬件请求新定位,而是以较长时间间隔复用上一次缓存位置。Android17 对控制逻辑做了简化:旧的Flags.disableStationaryThrottling()标志被移除;节流装饰器仅作用于 GPS 提供者,同时需要开启Flags.keepGnssStationaryThrottling()并且打开Settings.Global.LOCATION_ENABLE_STATIONARY_THROTTLE配置。手机设备该配置默认为 1(开启),Wear OS 手表设备默认为 0(关闭),因为小尺寸设备静止检测可靠性较差。也就是说,网络提供者与融合提供者不再被该装饰器包裹,这与旧版本行为存在差异。移除提供者private void removeLocationProviderManager(LocationProviderManager manager) { synchronized (mProviderManagers) { mProviderManagers.remove(manager); manager.setMockProvider(null); manager.setRealProvider(null); manager.stopManager(); } }33.2.4 请求处理流程当应用调用LocationManager.requestLocationUpdates(),Binder 调用进入registerLocationListener():validateLocationRequest()校验并清洗每一个字段:WorkSource:需要UPDATE_DEVICE_STATS权限低功耗模式:需要LOCATION_HARDWARE权限对 AppOps 隐藏:需要UPDATE_APP_OPS_STATSADAS GNSS 绕过:仅车载设备、仅 GPS 提供者可用忽略位置开关设置:需要绕过权限33.2.5 获取当前位置getCurrentLocation()为一次性定位接口。LMS 将逻辑委托给LocationProviderManager.getCurrentLocation(),返回ICancellationSignal,应用可使用该对象取消未完成的定位请求。33.2.6 获取上一次已知位置getLastLocation()返回指定提供者最近缓存的位置。调用LocationProviderManager.getLastLocation(),同样执行权限校验与粗略位置模糊处理。33.2.7 位置系统设置每个用户下位置开关启用 / 禁用状态由Settings.Secure.LOCATION_MODE维护。LMS 监听配置变更:mInjector.getSettingsHelper().addOnLocationEnabledChangedListener( this::onLocationModeChanged);当模式发生变化,LMS 执行:使LocationManager本地缓存失效记录事件日志广播LocationManager.MODE_CHANGED_ACTION刷新对应用户的 AppOps 限制33.2.8 LocationProviderManagerLocationProviderManager(简称 LPM)是请求多路复用逻辑的核心,共 3123 行,是位置包内最大的单个类。每个实例管理一个命名定位提供者。主要职责:职责实现机制将 N 个应用请求合并为 1 条提供者请求mergeRegistrations()维护每个注册项状态(活跃、权限)内部类Registration向匹配的注册项分发位置结果deliverToListeners()对 COARSE 权限客户端做位置模糊处理LocationFudger支持测试用模拟提供者setMockProvider()提供诊断用的提供者请求监听器addProviderRequestListener()来源:frameworks/base/services/core/java/com/android/server/location/provider/LocationProviderManager.java33.2.9 LPM 常量与阈值LPM 定义若干控制行为的常量,排查位置分发问题必须理解这些参数:// 粗略位置客户端最小更新间隔(10分钟) private static final long MIN_COARSE_INTERVAL_MS = 10 * 60 * 1000; // 判断为高功耗请求的最大间隔(5分钟) private static final long MAX_HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000; // 判定位置为“当前有效”的最大存活时间(30秒) private static final long MAX_CURRENT_LOCATION_AGE_MS = 30 * 1000; // getCurrentLocation最大超时(30秒) private static final long MAX_GET_CURRENT_LOCATION_TIMEOUT_MS = 30 * 1000; // 最小更新间隔抖动容忍百分比(10%) private static final float FASTEST_INTERVAL_JITTER_PERCENTAGE = .10f; // 最小更新间隔绝对抖动上限(30秒) private static final int MAX_FASTEST_INTERVAL_JITTER_MS = 30 * 1000; // 延迟请求生效的最小等待时间(30秒) private static final long MIN_REQUEST_DELAY_MS = 30 * 1000; // 位置分发持有的Wakelock超时(30秒) private static final long WAKELOCK_TIMEOUT_MS = 30 * 1000; // PendingIntent客户端允许FGS启动的临时白名单时长(10秒) private static final long TEMPORARY_APP_ALLOWLIST_DURATION_MS = 10 * 1000;MIN_COARSE_INTERVAL_MS = 10分钟是粗略权限客户端硬性下限。即便应用仅拥有ACCESS_COARSE_LOCATION却请求 1 秒更新间隔,LPM 也会将实际间隔钳位到 10 分钟。该隐私防护策略防止粗略权限应用通过位置变化量反推精细位置。Android17 中这些常量未发生修改,核对源码LocationProviderManager.java第 155‑180 行确认。33.2.10 LPM 省电模式LocationProviderManager通过LocationPowerSaveModeHelper对接 Android 电池省电机制,定义四种模式:模式常量行为无变更LOCATION_MODE_NO_CHANGE正常工作息屏关闭 GPSLOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF息屏时停止 GPS息屏全部关闭LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF息屏停止所有提供者仅前台允许LOCATION_MODE_FOREGROUND_ONLY仅前台应用收到位置更新息屏节流LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF息屏降低更新频率LPM 监听屏幕亮灭状态:mScreenInteractiveHelper.addListener(mScreenInteractiveChangedListener);当处于LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF模式并且是 GPS 提供者,屏幕熄灭时合并后的请求会被抑制。33.2.11 LPM 注册类型LPM 支持三类注册:Listener 注册:ILocationListenerBinder 回调,接收持续位置更新。PendingIntent 注册:触发PendingIntent,Intent 携带位置附加数据。当前位置请求:一次性定位,返回ICancellationSignal,由getCurrentLocation()发起。每个注册项记录:LocationRequest(间隔、精度、时长等)CallerIdentity(uid、pid、包名、归因标签)@PermissionLevel(FINE / COARSE / NONE)调用进程是否处于前台注册项是否处于活跃状态(全部前置条件满足)33.2.12 LPM 位置分发流水线当提供者上报新LocationResult,LPM 经过多阶段流水线处理:AltitudeConverter(可用时)将 WGS84 椭球高度转换为平均海平面海拔,优化展示海拔的应用体验。来源:frameworks/base/services/core/java/com/android/server/location/altitude/AltitudeService.java33.2.13 位置模糊处理细节向仅拥有粗略权限的客户端分发位置时,LocationFudger混淆真实坐标,算法逻辑:将真实坐标对齐到网格,网格单元宽度为mAccuracyM。该数值从Settings.Secure通过SettingsHelper.getCoarseLocationAccuracyM()读取,默认DEFAULT_COARSE_LOCATION_ACCURACY_M = 2000.0f(2 千米),下限MIN_ACCURACY_M = 200.0f。添加缓慢漂移的随机偏移量。对象构造时通过SecureRandom生成种子(设备每次开机重新生成),每OFFSET_UPDATE