第 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
权限强制校验 通过精细 / 粗略 / 后台权限校验管控每一条数据流 LocationPermissions

33.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.javaonSystemThirdPartyAppsCanStart() 第 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 带来三处关键变更:

  1. GNSS 提供者支持代理覆盖层。当config_useGnssHardwareProvider为 false 时,LMS 首先通过ProxyLocationProvider.create(..., ACTION_GNSS_PROVIDER, config_enableGnssLocationOverlay, ...)绑定外部 GNSS 提供者;若无覆盖层,则回退使用进程内GnssLocationProvider。如果存在覆盖层,原始 HAL 仍然独立通过GPS_HARDWARE_PROVIDER对外暴露(受LOCATION_HARDWARE权限保护),因为 GNSS HAL 仅支持单个客户端。
  2. 人口密度功能不再受功能标志控制。ProxyPopulationDensityProvider无条件注册;曾经用于开关该功能的population_density_providerdensity_based_coarse_locations标志已被清理,唯一约束条件是设备上是否存在对应提供者服务(见 33.5.8 节)。
  3. 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定义(setRequeststartstopflushsendExtraCommand);AbstractLocationProvider通过内部Controller实现该接口,向LocationProviderManager暴露自身状态。

来源:frameworks/base/services/core/java/com/android/server/location/provider/AbstractLocationProvider.javaDelegateLocationProvider.javaStationaryThrottlingLocationProvider.javaMockableLocationProvider.javaLocationProviderController.java)。

33.2 LocationManagerService

LocationManagerService(简称 LMS)是所有LocationManager调用背后唯一的系统服务。它实现ILocationManager.Stub Binder 接口,应用通过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 CopyOnWriteArrayList<LocationProviderManager> mProviderManagers;
}

mProviderManagers列表中,每一个注册的提供者对应一个LocationProviderManager实例。CopyOnWriteArrayList模式保证高频getLocationProviderManager(name)查找时读路径无锁。

33.2.2 依赖注入模式

LMS 使用Injector接口,解除与 Android 系统服务之间的强耦合。使得无需完整system_server环境即可完成单元测试。

每一个 Helper 都有对应的System*具体实现(例如SystemSettingsHelperSystemEmergencyHelper),封装真实系统 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_STATS
  • ADAS 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 执行:

  1. 使LocationManager本地缓存失效
  2. 记录事件日志
  3. 广播LocationManager.MODE_CHANGED_ACTION
  4. 刷新对应用户的 AppOps 限制

33.2.8 LocationProviderManager

LocationProviderManager(简称 LPM)是请求多路复用逻辑的核心,共 3123 行,是位置包内最大的单个类。每个实例管理一个命名定位提供者。

主要职责:

职责 实现机制
将 N 个应用请求合并为 1 条提供者请求 mergeRegistrations()
维护每个注册项状态(活跃、权限) 内部类Registration
向匹配的注册项分发位置结果 deliverToListeners()
对 COARSE 权限客户端做位置模糊处理 LocationFudger
支持测试用模拟提供者 setMockProvider()
提供诊断用的提供者请求监听器 addProviderRequestListener()

来源:frameworks/base/services/core/java/com/android/server/location/provider/LocationProviderManager.java

33.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 正常工作
息屏关闭 GPS LOCATION_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 支持三类注册:

  1. Listener 注册:ILocationListener Binder 回调,接收持续位置更新。
  2. PendingIntent 注册:触发PendingIntent,Intent 携带位置附加数据。
  3. 当前位置请求:一次性定位,返回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.java

33.2.13 位置模糊处理细节

向仅拥有粗略权限的客户端分发位置时,LocationFudger混淆真实坐标,算法逻辑:

  1. 将真实坐标对齐到网格,网格单元宽度为mAccuracyM。该数值从Settings.Secure通过SettingsHelper.getCoarseLocationAccuracyM()读取,默认DEFAULT_COARSE_LOCATION_ACCURACY_M = 2000.0f(2 千米),下限MIN_ACCURACY_M = 200.0f
  2. 添加缓慢漂移的随机偏移量。对象构造时通过SecureRandom生成种子(设备每次开机重新生成),每OFFSET_UPDATE
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值