title: Android 17 LocationManager 架构与性能优化 chapter: '1.25' section: '1.25' status: finalized pipeline_stage: ready-to-publish applicable_versions: Android 12 (API 31) - Android 17 (API 37) last_verified: '2026-08-18' last_verified_against: AOSP android-17.0.0_r1 confidence: high sources:
- type: aosp path: frameworks/base/location/java/android/location/LocationManager.java
- type: aosp path: frameworks/base/location/java/android/location/LocationRequest.java
- type: aosp path: frameworks/base/location/java/android/location/GnssMeasurementRequest.java
- type: aosp path: frameworks/base/location/java/android/location/GnssMeasurementsEvent.java
- type: aosp path: frameworks/base/location/java/android/location/GnssStatus.java
- type: aosp path: frameworks/base/location/java/android/location/ILocationManager.aidl
- type: aosp path: frameworks/base/location/java/android/location/ILocationListener.aidl
- type: aosp path: frameworks/base/services/core/java/com/android/server/location/LocationManagerService.java
- type: aosp path: frameworks/base/services/java/com/android/server/SystemServer.java
- type: aosp path: frameworks/base/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
- type: aosp path: frameworks/base/services/core/java/com/android/server/location/provider/LocationProviderManager.java
- type: aosp path: frameworks/base/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
- type: aosp path: frameworks/base/services/core/java/com/android/server/location/fudger/LocationFudger.java
- type: aosp path: frameworks/base/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
- type: aosp path: frameworks/base/services/core/java/com/android/server/location/gnss/GnssPsdsDownloader.java
- type: aosp path: frameworks/base/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
- type: aosp path: frameworks/base/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
- type: aosp path: frameworks/base/services/core/jni/gnss/Gnss.cpp
- type: aosp path: frameworks/base/services/core/java/com/android/server/location/geofence/GeofenceManager.java
- type: aosp path: frameworks/base/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
- type: aosp path: frameworks/base/services/core/java/com/android/server/location/gnss/GnssGeofenceProxy.java
- type: aosp path: hardware/interfaces/gnss/aidl/android/hardware/gnss/IGnss.aidl
- type: aosp path: hardware/interfaces/gnss/aidl/android/hardware/gnss/IGnssPsds.aidl
- type: aosp path: hardware/interfaces/gnss/aidl/android/hardware/gnss/IGnssBatching.aidl
- type: aosp path: hardware/interfaces/gnss/aidl/android/hardware/gnss/IGnssGeofence.aidl
- type: aosp path: hardware/interfaces/gnss/aidl/android/hardware/gnss/IGnssPowerIndication.aidl
- type: official path: developer.android.com/reference/android/location/LocationManager
- type: official path: developer.android.com/reference/android/location/LocationRequest
- type: official path: developer.android.com/reference/android/location/GnssMeasurementRequest
- type: official path: developer.android.com/develop/sensors-and-location/location/permissions
- type: official path: developer.android.com/develop/sensors-and-location/location/background tags:
- location
- gps
- gnss
- system-service
- geofence
- hal
- performance
- power related_chapters:
- '1.10'
- '1.21'
- '1.19'
- '5.3'
- '8.1'
Android 17 LocationManager 架构与性能优化
LocationManager 是 Android 平台位置 API 的客户端入口。应用选定 provider,描述更新间隔、质量、批处理延迟等需求,再通过 Binder 把请求交给 system_server。这里保留 API 中的 provider 一词,它表示按名称选择的位置提供者或数据源。服务端负责权限检查、前后台限制、请求合并、Provider 调度、结果裁剪和回调投递。
范围限定在 Android 平台 API 与 AOSP 服务端。Google Play services 的 FusedLocationProviderClient、LocationCallback 和 Geofencing API 是另一套客户端 API;它们可能使用系统的 fused provider,也可能在自己的服务中增加策略,但不能用来解释 LocationManager 的公开调用链。
平台基线是 Android 17 / API 37 / android-17.0.0_r1,GNSS(全球卫星导航系统)HAL 接口以同一平台标签下的 hardware/interfaces 为准。GPS 是 GNSS 中的一套卫星系统,Android 的 gps provider 名称不表示底层只能使用 GPS 星座。Linux kernel 实现不在讨论范围内,厂商 GNSS 驱动行为不能推断为 LocationManagerService 的固定语义。
1. 系统边界:应用提出需求,Provider 提供位置
Android 17 的主链路如下:
App process
LocationManager
├─ getLastKnownLocation(provider)
├─ getCurrentLocation(provider, ..., Consumer<Location>)
└─ requestLocationUpdates(provider, ..., LocationListener/PendingIntent)
│
│ ILocationManager
▼
system_server
LocationManagerService
└─ LocationProviderManager(每个 provider 一个实例)
├─ passive → PassiveLocationProvider
├─ gps → GnssLocationProvider(默认)或配置的代理 Provider
│ └─ GnssNative → JNI GnssHal → vendor GNSS HAL
├─ network → ProxyLocationProvider → 外部系统 Provider 服务
└─ fused → ProxyLocationProvider → 外部系统 Provider 服务
LocationManagerService 在 SystemServer 中启动。Lifecycle 构造服务实例时先创建 passive provider,onStart() 发布 location Binder 服务;到 PHASE_THIRD_PARTY_APPS_CAN_START,再初始化 network、fused、GNSS 以及相关代理服务。GNSS 放在这些 Provider 之后初始化,避免早期 GNSS 回调依赖尚未就绪的系统组件。
1.1 四个常见 Provider 的含义
| Provider | Android 17 framework 实现 | 请求是否主动驱动数据源 | 需要注意的边界 |
|---|---|---|---|
gps | 默认是 GnssLocationProvider,可由配置的代理 Provider 覆盖 | 是 | 默认路径进入 JNI 和厂商 GNSS HAL;能力、功耗和射频表现取决于设备 |
network | ProxyLocationProvider | 是 | 算法位于受配置约束的系统 Provider 服务中,AOSP 不规定其一定由 GMS 实现 |
fused | ProxyLocationProvider | 是 | 系统要求存在可直接启动的系统实现;它不等同于 Play services 客户端类 |
passive | PassiveLocationProvider | 否 | 只接收其他 Provider 已产生的位置,不独立启动定位硬件 |
在纯 AOSP、GMS(Google Mobile Services)设备和 OEM 设备上,network/fused 背后的包可能不同。framework 能验证的是绑定条件、Binder 协议和 Provider 输出,无法据此断言外部服务采用 Wi-Fi 指纹、卡尔曼滤波或某个私有数据库。设备若配置 GNSS Provider override(替代实现),gps 会绑定代理实现,进程内 HAL 路径则可注册为受 LOCATION_HARDWARE 权限保护的 gps_hardware provider。
1.2 FUSED_PROVIDER 与 Play services API
这两个名称容易产生误解:
LocationManager.FUSED_PROVIDER是平台定义的 provider 名称。调用者仍通过LocationManager、LocationListener或PendingIntent使用它。FusedLocationProviderClient是 Google Play services 的客户端 API,回调类型是LocationCallback。- 平台
LocationManager没有接收LocationCallback的requestLocationUpdates重载。
排查问题时,应先看应用链接的是 android.location.* 还是 com.google.android.gms.location.*。API 入口不同,进程、日志和版本依赖也会不同。
[源码依据:SystemServer.java、LocationManagerService.Lifecycle、ProxyLocationProvider.java,android-17.0.0_r1]
2. 提供者由调用者选择,quality 不负责自动选源
平台 API 的规则是:provider 决定请求交给谁,LocationRequest.quality 只是给该 Provider 的质量提示。
例如,调用:
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
request,
executor,
listener);
服务端会查找名为 gps 的 LocationProviderManager。它不会因为 request 使用 QUALITY_LOW_POWER 就把请求迁移到 network;同理,向 network provider 提交 QUALITY_HIGH_ACCURACY 也不会自动启动 GNSS。Provider 对 quality 的解释由具体实现决定。
旧的 Criteria API 可以让 framework 从现有 Provider 中挑选一个名称,但那是调用前的 Provider 选择,不是一次请求同时驱动多个 Provider。
2.1 LocationRequest 中值得区分的字段
| 字段 | 服务端用途 |
|---|---|
intervalMillis | 期望更新周期,也是同一 Provider 合并请求时的主要调度输入 |
quality | 质量/功耗提示;数值越小表示要求越高,100 比 104 更强 |
minUpdateIntervalMillis | 对单个注册的投递限速 |
minUpdateDistanceMeters | 对单个注册按位移过滤 |
maxUpdateDelayMillis | 允许批量延迟;达到条件时 Provider 才可能 batching(先缓存多条位置,再成批交付) |
durationMillis | 注册有效时间 |
maxUpdates | 成功投递达到次数后移除注册 |
lowPower | 传给 Provider 的低功耗提示,是否支持由实现决定 |
WorkSource | 供有权限的调用者指定这项工作应归因给谁;普通应用不能随意伪造 |
公开的 Builder 形式自 API 31 起可用。maxUpdates 和 durationMillis 也不是 Android 14 才出现的字段。
下面的示例用于明确指定平台 gps provider,并允许最多 10 秒的批量延迟:
LocationRequest request = new LocationRequest.Builder(5_000L)
.setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
.setMinUpdateIntervalMillis(2_000L)
.setMaxUpdateDelayMillis(10_000L)
.build();
LocationListener listener = location -> {
// 把重计算转交给工作线程;这里保持回调短小。
};
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
request,
context.getMainExecutor(),
listener);
LocationRequest.Builder(long) 的参数是更新间隔,不是 priority(优先级)。示例中的 10_000 / 2 >= 5_000 满足 framework 接受 batching 参数的基本比例,但设备还要具备 batching 扩展,Provider 也要接受该请求;因此这段代码不构成硬件批处理保证。
[源码依据:LocationManager.java、LocationRequest.java、LocationManagerService.validateLocationRequest(),android-17.0.0_r1]
3. 三类位置读取的成本不同
3.1 最近位置:只读缓存
getLastKnownLocation(provider) 通过 ILocationManager.getLastLocation() 读取指定 Provider 的服务端缓存。它不会为了这次调用启动 Provider,返回值可以为 null,也可能已经过时。业务必须结合 Location.getElapsedRealtimeAgeMillis()、accuracy(估计精度半径)和数据完整性判断能否使用。
系统日期时间可能被修改,不能用 System.currentTimeMillis() - location.getTime() 作为唯一新鲜度依据。比较位置年龄应优先使用单调递增、不受改时钟影响的 elapsed realtime。
3.2 单次位置:先尝试缓存,再等待新结果
getCurrentLocation() 的 Android 17 服务端行为包含两个明确上限:
- 注册激活时,指定 Provider 若有不超过 30 秒的合格缓存,可以立即返回;
- 请求 duration 超过 30 秒时,
LocationProviderManager会把它截到 30 秒。
权限、位置开关、Provider 状态或 AppOps 导致注册不活跃时,回调可能很快收到 null。超时也返回 null。调用者应保留并在业务结束时触发 CancellationSignal,不要把“等待单次结果”写成无期限状态。
3.3 连续更新:注册会参与 Provider 合并
requestLocationUpdates() 会建立长期注册。Listener 适合进程存活且生命周期清晰的页面或服务;PendingIntent 适合需要跨组件交付的场景,但仍受后台位置权限、前台服务和系统节流规则约束。
使用 Listener 时,应保存同一个实例,并在生命周期结束时取消:
private final LocationListener listener = this::onLocationChanged;
@Override
protected void onStart() {
super.onStart();
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
request,
getMainExecutor(),
listener);
}
@Override
protected void onStop() {
locationManager.removeUpdates(listener);
super.onStop();
}
removeUpdates() 需要原先的 Listener 对象。及时取消既能断开 Activity/Fragment 与回调对象的引用,也能把该注册从服务端合并请求中移除,避免页面不可见后仍维持高频 Provider 请求。若产品需要后台定位,应把注册放到有明确停止条件且满足权限要求的组件,不应依赖页面对象继续存活。
4. Binder 入口之后发生了什么
以连续 Listener 注册为例,Android 17 的主要路径是:
LocationManager.requestLocationUpdates()
→ ILocationManager.registerLocationListener()
→ LocationManagerService.registerLocationListener()
→ CallerIdentity + permission level
→ validateLocationRequest()
→ getLocationProviderManager(provider)
→ LocationProviderManager.registerLocationRequest()
→ Registration 参与活跃性判断与请求合并
4.1 身份、权限和请求清洗
LocationManagerService 从 Binder 调用构造 CallerIdentity,读取 fine/coarse 权限级别,并检查 provider 是否存在。validateLocationRequest() 会处理:
- 普通调用者无权指定的
WorkSource; lowPower、忽略位置设置、ADAS(高级驾驶辅助系统)GNSS bypass(绕过部分常规限制)等受限字段;- 包名、attribution tag 与调用 UID 的一致性;
- 对特权 bypass 的权限及 allowlist(允许名单)限制。
所以,客户端对象中的隐藏字段不等于服务端会照单执行。调试系统应用时,要同时打印“客户端构造的请求”和 dumpsys location 中“服务端接受后的请求”。
4.2 注册能否生效,由动态状态共同决定
LocationProviderManager 会持续重算注册是否 active(当前能够参与请求合并并接收位置),条件包括:
- 调用者是否仍有相应位置权限;
- 用户是否可见、Provider 对该用户是否启用;
- 包是否命中位置黑名单;
- 前后台状态及后台节流;
- Battery Saver 的位置模式、屏幕状态;
- AppOps 是否允许本次使用;
- 特权 bypass 是否仍满足策略。
Binder 注册成功不代表 Provider 马上运行,也不代表每个位置都能交付。权限、前后台和省电状态可以在注册存续期间改变,服务端会随之更新 active 状态。
4.3 coarse 权限会改请求,也会改结果
Android 12 起,用户可以在应用同时请求 fine(精确)和 coarse(粗略)权限时选择 approximate location(大致位置)。Android 17 服务端对只有 coarse 权限的注册至少做两层处理:
LocationProviderManager把 quality 改为QUALITY_LOW_POWER,并把 interval 与 min interval 提高到内部的 10 分钟下限;- 位置结果经过
LocationFudger(模糊位置生成器),通过随时间变化的偏移和网格化生成 coarse 位置。
“10 分钟”是 android-17.0.0_r1 的 framework 内部调度下限,不是公开 API 对所有设备、所有版本的回调承诺。coarse 也不是简单地截断经纬度小数位。应用不应依赖固定的模糊半径或固定小数位数。
后台还有另一层动态 interval 调整。Android 官方文档将普通后台应用描述为每小时只能收到少量位置更新;具体节流值由系统配置、设备版本、进程状态和豁免条件共同决定。
[源码依据:LocationProviderManager.Registration.calculateProviderLocationRequest()、LocationFudger.java,android-17.0.0_r1]
5. 同一 Provider 的请求如何合并
每个 Provider 有自己的 LocationProviderManager,只合并发往该 Provider 且当前 active 的注册。合并过程不是“全系统挑一个最佳位置源”。
5.1 ProviderRequest 的合并规则
Android 17 的 mergeRegistrations() 对非 passive 请求执行:
- interval 取最小值;
- quality 取数值最小值,也就是更强的质量要求;
- max update delay 取最小值;
- ADAS bypass 与 ignore-settings 标志做 OR;
lowPower做 AND;- WorkSource 收集接近最短 interval、会影响底层工作量的注册。
如果 maxUpdateDelay / 2 < interval,framework 把合并后的 batching delay 置为 0。这样可以避免一个不具备批处理意义的延迟字段反复触发 Provider 重配。公开 LocationRequest 文档也把“最大延迟至少为 interval 的两倍”列为可能批处理的条件。
例如,同一 gps provider 有两个 active 请求:
A: interval=5s, quality=HIGH, maxDelay=20s, lowPower=false
B: interval=30s, quality=LOW, maxDelay=60s, lowPower=true
合并给 gps Provider:
interval=5s
quality=HIGH
maxDelay=20s
lowPower=false
这个结果只配置一次 gps Provider。A、B 各自是否收到某个位置,仍要经过各自注册的 min interval、min distance、权限、AppOps、duration 和 max updates 过滤。B 不会因为底层以 5 秒工作就必然每 5 秒收到回调。
5.2 一条高频请求会抬高该 Provider 的共同成本
同一 Provider 的活动请求共享底层工作。一条 1 秒 GPS 请求可能使 GNSS 持续工作;其他低频 GPS 注册虽然被单独限速,底层工作强度已经由频率最高、质量要求最强的请求决定。优化时应找出参与合并的最短 interval 和最强 quality,而不能只看某个业务模块自己的配置。
dumpsys location 会列出 Provider 请求和注册,是定位“哪条请求让 GPS 高频工作”的首选证据。WorkSource/BatteryStats 用于能耗归因,但合并后的归因集合不能表示各应用的实际能耗占比。
6. 位置上报与回调投递
Provider 上报 LocationResult 后,LocationProviderManager 会先更新缓存,再对注册逐一处理。连续更新的主要过滤点包括:
- 经纬度是否合法;
- min update interval,包含 framework 允许的小幅时序抖动;
- min update distance;
- duration 与 max updates;
- fine/coarse 结果选择;
- AppOps 是否允许本次交付。
回调路径可简化为:
Provider.reportLocation()
→ LocationProviderManager
→ Registration.acceptLocationChange()
→ ILocationListener.onLocationChanged()(oneway Binder)
→ App 的 LocationListenerTransport
→ 指定 Executor
→ LocationListener.onLocationChanged()
→ IRemoteCallback 通知 system_server 本次处理完成
6.1 Executor 堵塞为什么也会带来系统成本
对非 passive 的连续更新,服务端在交付前获取一个 partial wakelock(允许屏幕关闭但暂时阻止 CPU 休眠的锁),超时为 30 秒。应用侧 LocationListenerTransport 在指定 Executor 上完成回调后,通过 IRemoteCallback 通知服务端释放它。
这带来三点工程约束:
- oneway Binder(异步单向调用)只表示发送方不等待同步返回,不表示应用回调没有背压;这里的背压是接收端处理不过来后产生的队列压力;
- Executor 队列长时间拥塞,会延后 completion callback(处理完成通知),服务端 wakelock 只能等回调或 30 秒超时;
- 回调中如果需要比这更长的后台工作,应用应自行采用符合系统约束的执行与保活机制,不能把 framework 的交付 wakelock 当作业务 wakelock。
不要假设 Perfetto 中存在稳定的 LocationManagerService 或 GnssLocationProvider 自定义 track(时间轨道)。android-17.0.0_r1 的这些 Java 路径没有提供可依赖的统一 slice(带起止时间的区间事件)名称。可结合 Binder、线程调度、CPU frequency/idle、wakelock 和应用自定义 trace 分析延迟。
[源码依据:ILocationListener.aidl、LocationManager.LocationListenerTransport、LocationProviderManager.Registration.acceptLocationChange(),android-17.0.0_r1]
7. GNSS 从 framework 到 HAL
这里的 AIDL 是当前稳定的 HAL 接口技术,HIDL 是旧版接口技术;JNI 是 Java 与本地 C++ 代码之间的调用桥梁。
Android 17 的 GNSS 主路径是:
LocationProviderManager[gps]
→ GnssLocationProvider
→ GnssNative(Java)
→ JNI
→ services/core/jni/gnss/Gnss.cpp
→ AIDL IGnss 或旧版 HIDL IGnss
→ vendor GNSS service / chipset
GnssLocationProvider 运行在 system_server,主要任务是把合并后的 ProviderRequest 转换成 GNSS 引擎配置,管理启动、停止、framework 调度、batching、辅助数据和指标。GnssNative 汇集 Java/JNI 回调。射频跟踪、基带算法和大部分硬件电源状态位于厂商实现。
7.1 Android 17 仍保留 AIDL/HIDL 兼容路径
JNI GnssHal 先等待 VINTF(Vendor Interface,系统与 vendor 组件的接口声明)中列出的 AIDL 服务。AIDL interface version 大于等于 2 时,构造过程直接结束;否则还会依次探测 HIDL 2.1、2.0、1.1、1.0。Android 17 仍带着多版本兼容代码,不能预设设备只走 AIDL。分析单机问题时,要从 dumpsys 和 GnssJni 日志确认连接到的接口版本。
AIDL IGnss 的主生命周期接口是:
setCallback();start()/stop();close();setPositionMode();- 时间、位置和 aiding data(辅助定位数据)注入;
- 获取 measurement(原始卫星测量)、PSDS、batching、geofence、power indication(功耗报告)等扩展。
IGnss 没有 open()。各扩展通常使用自己的 setCallback() 或 init() 建立回调。
7.2 能力位是“设备支持什么”,不是性能数字
HAL 会上报 scheduling、measurement、geofence、low-power mode 等能力。framework 据此启用对应路径。例如,没有调度能力时,GnssLocationProvider 可以通过 alarm 在低频请求之间休眠和唤醒;具备调度能力时,可把周期直接交给 HAL。
能力位不能推导出固定精度、TTFF(Time to First Fix,首次定位时间)或电流值。天线、天空可视度、频段、热状态、厂商固件、辅助数据、移动速度和测量环境都会改变结果。通用章节不应给出“GPS 恒定 3–10 米”或“batching 恒定 1–5 mA”一类设备无关结论。
8. GNSS 调度、batching 与 full tracking
8.1 普通定位请求如何驱动 GNSS
GnssLocationProvider.updateRequirements() 从合并请求读取 interval、max update delay、low-power 提示和 WorkSource:
- 满足 batching 条件时,停止普通导航并启动硬件 batch;
- 已启动且 HAL 支持 scheduling 时,更新
setPositionMode(); - 尚未启动时,设置 position mode 后调用
start(); - HAL 不支持 scheduling 时,framework 用 alarm 辅助长间隔轮询;
- Provider request 变为 inactive 时,停止 navigation 和 batching。
这里没有“interval 大于 10 秒就必定 batching”的规则。
8.2 Hardware batching 的必要条件
Android 17 framework 至少检查:
initBatching()成功;- HAL 报告的 batch size 大于 1;
batchLength = min(maxUpdateDelay, framework 上限);batchLength / 2 >= max(interval, framework 最小 batch interval)。
满足这些条件后,GnssNative.startBatch() 才会进入 IGnssBatching.start()。如果请求的 batch 长度小于硬件缓冲区容量对应的时长,framework 还会设置精确定时器(alarm),按 batch 长度调用 flushBatch()。
batching 的收益来自减少 AP(Application Processor,应用处理器)唤醒和 Binder/Java 交付次数,但 GNSS 接收机仍可能按设定周期采样。它不能自动把射频跟踪成本降为零。评估收益要同时比较 AP idle(空闲)时间、唤醒次数、GNSS power stats(功耗统计)和厂商电源轨数据。
8.3 GNSS measurement 的 full tracking 是另一条请求
GnssMeasurementRequest.Builder.setFullTracking(true) 自 API 31 起用于原始 GNSS measurements。它要求关闭 duty cycling(周期性停机以省电),以连续跟踪更多 GNSS 信号,通常会增加功耗。多个 measurement listener 合并时,只要任一 active 请求要求 full tracking(全程跟踪),合并结果就启用 full tracking;measurement interval 也取最短值。
API 34 增加了 GnssMeasurementsEvent.isFullTracking(),调用者可以在 HAL 支持相应信息时判断事件是否处于 full-tracking 模式。
普通 LocationRequest 的 QUALITY_HIGH_ACCURACY + 短 interval 不等价于显式 GnssMeasurementRequest.setFullTracking(true)。IGnssPowerIndication 也不是 full-tracking 开关:它只提供 setCallback() 和 requestGnssPowerStats(),用途是报告 GNSS power capabilities/statistics(功耗能力与统计)。
[源码依据:GnssLocationProvider.updateRequirements()、GnssMeasurementsProvider.mergeRegistrations()、IGnssBatching.aidl、IGnssPowerIndication.aidl,android-17.0.0_r1]
9. PSDS 与 TTFF:把测量事实和经验值分开
9.1 PSDS 数据路径
PSDS(Predicted Satellite Data Service,预测卫星数据服务)向 GNSS 提供辅助数据。HAL 通过 PSDS callback 请求 framework 下载数据,Android 17 的路径是:
vendor GNSS HAL
→ IGnssPsdsCallback.downloadRequest(type)
→ GnssLocationProvider
→ GnssPsdsDownloader
→ 下载配置的厂商 URL
→ GnssNative.injectPsdsData(type, opaque bytes)
→ IGnssPsds.injectPsdsData()
framework 把 PSDS 内容视为 opaque bytes(不解析内部格式的不透明字节),不会自行拆成“星历表字段”。GnssPsdsDownloader 对单次内容设置 1 MB 上限,连接超时 30 秒、读取超时 60 秒;服务器地址来自设备配置。下载失败会按 framework 策略重试,但设备是否支持某类 PSDS、数据含义及有效期由 HAL/服务配置决定。
PSDS 有助于缩短部分启动场景的捕获时间,却不是 TTFF 的唯一变量。时间/位置注入、SUPL(Secure User Plane Location,一种通过数据网络提供定位辅助信息的协议)、网络状态、aiding data、天线、天空遮挡、干扰、频段和厂商算法都可能影响首次定位。
9.2 framework 如何记录 TTFF
startNavigating() 成功调用 HAL start() 后记录 mFixRequestTime。收到第一份含经纬度的有效 GNSS location 时:
TTFF = first valid fix elapsed realtime - mFixRequestTime
结果写入 GnssMetrics,GnssStatus.Callback.onFirstFix(ttffMillis) 也向有权限的监听者暴露相应事件。这个定义可以用于同设备、同场景的前后对比;“冷启动必为 30–60 秒”或“A-GPS 必为 1–5 秒”没有跨设备保证。
做可重复测试时,至少记录:
- 设备、build fingerprint(精确标识系统构建的字符串)、GNSS HAL 接口版本;
- 是否删除 aiding data,是否重启 GNSS;
- 网络、SUPL/PSDS 状态;
- 室外天空条件、静止/运动状态;
- 请求开始的 elapsed realtime 与首个有效定位(fix);
- 多轮分布,而非只报一次最小值。
10. 地理围栏:公开 proximity alert 与硬件围栏是两条路径
10.1 LocationManager.addProximityAlert() 的 framework 路径
平台遗留 API 的调用链是:
LocationManager.addProximityAlert()
→ ILocationManager.requestGeofence()
→ LocationManagerService
→ GeofenceManager
→ 使用 FUSED_PROVIDER 请求位置
→ system_server 中计算进入/离开
→ PendingIntent
GeofenceManager 要求 fine location 权限。proximity alert(邻近提醒)由中心点、半径和 PendingIntent 定义。它为所有 active 围栏合并一个位置请求,根据“当前位置到最近围栏边界的距离 / 假定最大速度”估算下次更新间隔,并受后台 proximity alert 节流值约束,最长不超过 2 小时。缓存位置超过 5 分钟就不用于这个估算。
收到位置后,它计算中心距离,并用:
effectiveRadius = max(geofenceRadius, locationAccuracy)
inside = distanceToCenter <= effectiveRadius
从 UNKNOWN/OUTSIDE 进入 INSIDE 时发送 entering;只有先处于 INSIDE,之后转为 OUTSIDE,才发送 exiting。该实现没有网格空间索引、loitering delay(进入后停留多久才触发)或通用 hysteresis margin(防止边界抖动的迟滞余量)。
10.2 硬件 GNSS geofence 路径
Android 17 还存在独立的硬件围栏基础设施:
配置的 Geofence Provider
↔ GeofenceProxy
↔ GeofenceHardwareService
↔ GnssGeofenceProxy
↔ GnssNative
↔ IGnssGeofence
GnssGeofenceProxy 支持 add/remove/pause/resume,并缓存已接受的条目,以便 HAL 重启后恢复。GeofenceProxy 是否创建取决于设备 overlay 与可解析的系统服务。
不能由此得出 addProximityAlert() 会自动把同一个围栏切换到 GNSS 硬件。前者在 GeofenceManager 中以位置更新做软件判断;后者服务于设备配置的硬件 geofence provider。Google Play services Geofencing API 也不属于这条公开平台调用链。
[源码依据:GeofenceManager.java、GeofenceProxy.java、GnssGeofenceProxy.java、IGnssGeofence.aidl,android-17.0.0_r1]
11. 性能问题的证据链
位置问题常同时跨越应用、system_server、Provider 服务和 vendor HAL。只看一条回调耗时,很难区分请求未激活、Provider 没有产生有效定位(fix)、结果被过滤或 Executor 堵塞。
11.1 先用 dumpsys location 建立状态快照
以下命令分别查看常规状态、详细状态和 GNSS 指标:
adb shell dumpsys location
adb shell dumpsys location -a
adb shell dumpsys location --gnssmetrics
应重点核对:
- provider 是否存在、是否启用;
- 每个 Provider 的 active request(当前生效的合并请求)与注册调用者;
- interval、quality、max delay 是否已被服务端改写;
- last location(最近缓存位置)的年龄;
- GNSS 是否启动、是否处于 batching,以及 capabilities 与 TTFF 指标;
- framework proximity alerts 和硬件 geofence 状态。
命令输出会随 build 类型和厂商扩展变化。调试量产使用的 user build 时,部分身份或 HAL 细节可能被裁剪。
11.2 分清应用记录与系统观测点
普通应用只能稳定记录自己的请求和回调,不能直接在 system_server 的注册激活或 Provider 上报处增加时间点。应用侧应使用单调时钟,例如 SystemClock.elapsedRealtimeNanos(),记录这三个事件:
A_request 调用 LocationManager API
A_callback 指定 Executor 开始执行回调
A_done 应用完成轻量回调处理
这组数据能直接计算应用看到的端到端等待时间 A_callback - A_request,以及回调工作时间 A_done - A_callback。前者包含服务端策略、Provider 产出、Binder 投递和 Executor 排队,不能仅凭应用日志继续归因。
要继续分段,需要在用于调试的 userdebug/eng 构建、定制 framework/Provider 或设备已经提供对应 Perfetto 数据源时,增加系统侧观测点:
| 观测点 | 所在层 | 可用于解释的阶段 |
|---|---|---|
S_active | system_server 注册变为 active,合并后的 ProviderRequest 更新 | 请求是否通过权限、位置开关、前后台和省电策略 |
P_report | Provider 向 framework 上报 LocationResult | Provider 或 GNSS/HAL 等待时间 |
S_deliver | system_server 决定向目标注册投递 | 单注册过滤与服务端投递时间 |
当这些事件来自同一次受控请求,并且都使用可换算到同一单调时钟域的时间戳时,才可以计算:
S_active - A_request:Binder 入口、校验与激活;P_report - S_active:Provider 等待,GNSS 场景还要结合 TTFF;S_deliver - P_report:缓存更新、单注册过滤与投递准备;A_callback - S_deliver:Binder、进程调度和 Executor 排队。
并发请求下,不能用“时间上最近的一条 location”猜测跨进程事件属于同一个注册。应使用定制关联 ID,或把实验限制为单应用、单 provider、单注册,再结合调用者身份、请求参数和 Location.getElapsedRealtimeNanos() 交叉核对。
Perfetto 可组合 sched、binder_driver、CPU frequency/idle、wakelock/power、network,以及设备提供的 vendor GNSS data source(跟踪数据源)。没有明确的 framework/HAL 事件或自定义时间戳时,不能凭模糊的 Gnss 字符串把某段 slice 当成 TTFF。
11.3 常见症状与核查点
| 症状 | 第一批证据 | 高概率方向 |
|---|---|---|
| 注册成功但无回调 | active 状态、provider 是否启用、AppOps | 权限/后台/省电限制,或 Provider 没有结果 |
| 回调频率低于请求 | 服务端改写后的 interval、单注册过滤 | coarse/后台节流、min interval、min distance、batching |
| GPS 长时间保持 active | gps 的合并请求和调用者 | 另一条更短 interval 请求、未取消注册 |
| TTFF 变长 | 多轮 TTFF、PSDS/SUPL/网络、天空条件 | 辅助数据、射频环境、HAL/固件变化 |
| 回调后系统仍持有 wakelock | App Executor 与处理完成通知的时间 | Executor 拥塞或回调耗时 |
| 围栏漏报 | 使用的平台/GMS API、位置 accuracy、软件/硬件路径 | 路径判断错误、权限/后台限制、位置本身不稳定 |
12. 设计与检查清单
应用侧:
- 明确写出使用的平台 API 还是 Play services API;
- 明确选哪个 provider,不把 quality 当作自动选源器;
- 最近位置要检查年龄与 accuracy;
- 单次请求处理
null、取消与 30 秒服务端上限; - 连续请求绑定清晰生命周期,停止时用同一 Listener 或 PendingIntent 取消;
- 回调只做轻量工作,避免堵塞指定 Executor;
- 需要 approximate location(大致位置)时按能力降级,不假设固定模糊半径;
- 后台定位同时检查后台位置权限、前台服务类型和系统节流。
系统与厂商侧:
- 以
dumpsys location中的服务端请求为准,不只看客户端对象; - 分 Provider 查最短 interval、最强 quality 和最小 max delay;
- 确认 GNSS 使用 AIDL 还是 HIDL,以及能力位(capabilities)是否匹配预期;
- 对 batching 同时验证 HAL 支持、batch size、请求比例和实际 flush;
- 把 measurement full tracking 与普通
LocationRequest、power indication 分开; - TTFF、精度和电流只报告指定设备与测试条件下的统计分布;
- 区分
GeofenceManager软件 proximity alert 与硬件 geofence provider。
13. 与其他章节的关系
| 关联章节 | 内容边界 |
|---|---|
| §1.10 | Binder oneway、线程调度与回调完成通知 |
| §1.22 | network/fused Provider 的网络依赖,但定位算法不由 ConnectivityManager 决定 |
| §5.3 | 应用定位周期、后台限制与电池预算 |
| §8.1 | Executor/主线程拥塞造成的通用响应延迟 |
android-17.0.0_r1 包含上述平台 API、服务端合并、GNSS HAL 兼容路径与两类 geofence 基础设施。厂商 Provider 算法、GNSS 芯片功耗和射频性能不属于 AOSP 可统一保证的范围,相关结论必须附具体设备与测量条件。