Android LocationManagerService启动(一)
Location服务是系统中很重要的一个服务,几乎当前所有的App都会用到这个服务。
首先看代码在Android源码的位置
Android API
frameworks/base/location
LocationManagerService
frameworks/base/services/core/java/com/android/server/location
frameworks/base/services/core/java/com/android/server/LocationManagerService.java
SystemServer
作为一个系统级别服务,启动肯定是在SystemServer中进行。
代码入口:frameworks/base/services/java/com/android/server/SystemServer.java
启动LocationManagerService的代码在startOtherServices()函数里面
执行流程如下
- 创建
LocationManagerService对象 - 将创建好的的对象加入到
ServiceManager里面 - 调用
LocationManagerService的systemRunning()函数初始化相关的Provider
private void startOtherServices() {
////////////////////
LocationManagerService location = null;
traceBeginAndSlog("StartLocationManagerService");
try {
location = new LocationManagerService(context);
ServiceManager.addService(Context.LOCATION_SERVICE, location);
} catch (Throwable e) {
reportWtf("starting Location Manager", e);
}
traceEnd();
// These are needed to propagate to the runnable below.
final LocationManagerService locationF = location;
traceBeginAndSlog("MakeLocationServiceReady");
try {
if (locationF != null) locationF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying Location Service running", e);
}
traceEnd();
////////////////////
}
LocationManagerService
从ServiceManager启动后,会调用systemRunning()方法,这个函数里面会初始化所有的可用的Providers
代码路径:frameworks/base/services/core/java/com/android/server/LocationManagerService.java
构造函数
构造函数很简单,除了初始化变量外,就给PackageManager设置了一个PackageProvider
public LocationManagerService(Context context) {
super();
mContext = context;
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
// Let the package manager query which are the default location
// providers as they get certain permissions granted by default.
PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
packageManagerInternal.setLocationPackagesProvider(
new PackageManagerInternal.PackagesProvider() {
@Override
public String[] getPackages(int userId) {
return mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationProviderPackageNames);
}
});
if (D) Log.d(TAG, "Constructed");
// most startup is deferred until systemRunning()
}
systemRunning()
作为LocationManagerService的入口函数,主要做以下几部分工作
- 初始化成员变量,主要是个各种需要用的Manager
- 初始化Provider
- 注册Observer,主要是包括Settings,UserChange等
public void systemRunning() {
synchronized (mLock) {
if (D) Log.d(TAG, "systemRunning()");
// prepare providers
loadProvidersLocked();
updateProvidersLocked();
}
// listen for settings changes
// ..... //
// listen for user change
// ..... //
}
我们主要看第二点,包括了各种Provider的初始化
loadProvidersLocked
加载顺序如下
1. PassiveProvider,必须
2. GnssLocationProvider,如果有,则加载
3. FuseProvider检查,这里比较重要,如果通不过,会跑出异常,不会进行下面的步骤(systemRunning会结束)
4. Bind NetworkProvider,第三方Provider
5. Bind Fused provider
6. Bind Geocoder provider
7. Bind Geofence provider
另外,针对每一个Provider,创建的步骤大致都是以下几个步骤
{
XxxProvider xxxProvider = new XxxProvider(this);
addProviderLocked(xxxProvider);
mEnabledProviders.add(xxxProvider.getName());
// or
mRealProviders.put(LocationManager.Xxx_PROVIDER, xxxProvider);
mXxxProvider = xxxProvider;
}
部分Provider初始化相对复杂一点,但是整体的逻辑一致。我们这里重点分析GnssProvider
private void loadProvidersLocked() {
// create a passive location provider, which is always enabled
PassiveProvider passiveProvider = new PassiveProvider(this);
addProviderLocked(passiveProvider);
mEnabledProviders.add(passiveProvider.getName());
mPassiveProvider = passiveProvider;
if (GnssLocationProvider.isSupported()) {
// 创建GnssProvider(老版本的Android上是GPS,后面整合成了GNSS,支持不同类型的导航系统)
GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext, this,
mLocationHandler.getLooper());
mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
addProviderLocked(gnssProvider);
mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProvider);
mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
mGpsGeofenceProxy = gnssProvider.getGpsGeofenceProxy();
}
/*
Load package name(s) containing location provider support.
These packages can contain services implementing location providers:
Geocoder Provider, Network Location Provider, and
Fused Location Provider. They will each be searched for
service components implementing these providers.
The location framework also has support for installation
of new location providers at run-time. The new package does not
have to be explicitly listed here, however it must have a signature
that matches the signature of at least one package on this list.
*/
Resources resources = mContext.getResources();
ArrayList<String> providerPackageNames = new ArrayList<>();
String[] pkgs = resources.getStringArray(
com.android.internal.R.array.config_locationProviderPackageNames);
if (D) {
Log.d(TAG, "certificates for location providers pulled from: " +
Arrays.toString(pkgs));
}
if (pkgs != null) providerPackageNames.addAll(Arrays.asList(pkgs));
// 注意这里如果没有找到FuseProvider,会抛出异常,结束这个初始化流程
ensureFallbackFusedProviderPresentLocked(providerPackageNames);
// bind to network provider
// bind to fused provider
// bind to geocoder provider
// bind to geofence provider
// bind to hardware activity recognition
}
updateProvidersLocked
上一个步骤仅仅只是进行了Provider的初始化操作,这里会调用Provider的enable方法,启用相关的Provider,主要调用
updateProviderListenersLocked(String provider, boolean enabled)
private void updateProvidersLocked() {
boolean changesMade = false;
// 遍历所有的Provider
for (int i = mProviders.size() - 1; i >= 0; i--) {
LocationProviderInterface p = mProviders.get(i);
boolean isEnabled = p.isEnabled();
String name = p.getName();
// 通过当前用户的Settings来判断是否需要启动这个Provider
boolean shouldBeEnabled = isAllowedByCurrentUserSettingsLocked(name);
if (isEnabled && !shouldBeEnabled) {
// 如果已经启动并且是不需要被启动,就会关闭
// 并且清除之前的记录
updateProviderListenersLocked(name, false);
// If any provider has been disabled, clear all last locations for all providers.
// This is to be on the safe side in case a provider has location derived from
// this disabled provider.
mLastLocation.clear();
mLastLocationCoarseInterval.clear();
changesMade = true;
} else if (!isEnabled && shouldBeEnabled) {
// 如果没有启动,并且需要被启动,则启动
updateProviderListenersLocked(name, true);
changesMade = true;
}
}
// 如果状态改变,就发系统广播通知
if (changesMade) {
mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
UserHandle.ALL);
mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
UserHandle.ALL);
}
}
updateProviderListenersLocked
private void updateProviderListenersLocked(String provider, boolean enabled) {
int listeners = 0;
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) return;
ArrayList<Receiver> deadReceivers = null;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records != null) {
for (UpdateRecord record : records) {
if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
// Sends a notification message to the receiver
if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<>();
}
deadReceivers.add(record.mReceiver);
}
listeners++;
}
}
}
if (deadReceivers != null) {
for (int i = deadReceivers.size() - 1; i >= 0; i--) {
removeUpdatesLocked(deadReceivers.get(i));
}
}
// 根据传入的参数,确定是开启还是关闭相关的Provider
if (enabled) {
p.enable();
if (listeners > 0) {
// 如果存在Locatoin listener,会调用下面的方法
applyRequirementsLocked(provider);
}
} else {
p.disable();
}
}
applyRequirementsLocked
这里会根据请求的Record的参数,来向具体的Provider发起位置信息请求。
private void applyRequirementsLocked(String provider) {
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) return;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
WorkSource worksource = new WorkSource();
ProviderRequest providerRequest = new ProviderRequest();
ContentResolver resolver = mContext.getContentResolver();
long backgroundThrottleInterval = Settings.Global.getLong(
resolver,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
// initialize the low power mode to true and set to false if any of the records requires
providerRequest.lowPowerMode = true;
if (records != null) {
for (UpdateRecord record : records) {
if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
if (checkLocationAccess(
record.mReceiver.mIdentity.mPid,
record.mReceiver.mIdentity.mUid,
record.mReceiver.mIdentity.mPackageName,
record.mReceiver.mAllowedResolutionLevel)) {
LocationRequest locationRequest = record.mRealRequest;
long interval = locationRequest.getInterval();
if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
if (!record.mIsForegroundUid) {
interval = Math.max(interval, backgroundThrottleInterval);
}
if (interval != locationRequest.getInterval()) {
locationRequest = new LocationRequest(locationRequest);
locationRequest.setInterval(interval);
}
}
record.mRequest = locationRequest;
providerRequest.locationRequests.add(locationRequest);
if (!locationRequest.isLowPowerMode()) {
providerRequest.lowPowerMode = false;
}
if (interval < providerRequest.interval) {
providerRequest.reportLocation = true;
providerRequest.interval = interval;
}
}
}
}
if (providerRequest.reportLocation) {
// calculate who to blame for power
// This is somewhat arbitrary. We pick a threshold interval
// that is slightly higher that the minimum interval, and
// spread the blame across all applications with a request
// under that threshold.
long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
for (UpdateRecord record : records) {
if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
LocationRequest locationRequest = record.mRequest;
// Don't assign battery blame for update records whose
// client has no permission to receive location data.
if (!providerRequest.locationRequests.contains(locationRequest)) {
continue;
}
if (locationRequest.getInterval() <= thresholdInterval) {
if (record.mReceiver.mWorkSource != null
&& isValidWorkSource(record.mReceiver.mWorkSource)) {
worksource.add(record.mReceiver.mWorkSource);
} else {
// Assign blame to caller if there's no WorkSource associated with
// the request or if it's invalid.
worksource.add(
record.mReceiver.mIdentity.mUid,
record.mReceiver.mIdentity.mPackageName);
}
}
}
}
}
}
if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
// 最终调用Provider的setRequest方法,进入到Provider的内部流程
p.setRequest(providerRequest, worksource);
}
总结
总结下LocationManagerService里面初始化的流程
systemRunning --> loadProvidersLocked
subgraph loadProvidersLockedProcess
loadProvidersLocked --> loadPassiveProvider
loadPassiveProvider --> loadGnssProvider
loadGnssProvider --> checkFusedProvider
checkFusedProvider --> bindNetworkProvider
bindNetworkProvider --> bindGeocoderProvider
bindGeocoderProvider --> bindGeofenceProvider
bindGeofenceProvider --> bindHardwareActivityRecogneition
bindHardwareActivityRecogneition --> updateProvidersLocked
end
subgraph updateProvidersLockedProcess
updateProvidersLocked --> updateProviderListenersLocked
updateProviderListenersLocked --> provider.enable
provider.enable --> applyRequirementsLocked
applyRequirementsLocked --> provider.setRequest
end
备注:
上面代码基于Android9 AOSP源码
Android LocationManagerService启动(一)的更多相关文章
- 设置Android Studio启动时可选最近打开过的工程
Android Studio启动时,默认会打开最近关闭的工程. 如果想Android Studio在启动时,打开欢迎界面(Welcome to Android Studio界面),则可以通过设置Set ...
- Android开机启动Activity或者Service方法
本文出自 “Bill_Hoo专栏” 博客,请务必保留此出处http://billhoo.blog.51cto.com/2337751/761230 这段时间在做Android的基础开发,现在有一需求是 ...
- [FMX] Android APP 启动黑屏优化补丁
使用说明 *************************************************** Android APP 启动黑屏优化补丁 作者: Swish, YangYxd 201 ...
- Windows自带Android模拟器启动失败
Windows自带Android模拟器启动失败 错误信息:[Critical] XDE Exit Code: InvalidArguments (3)XDE执行的第三个参数为设置内存值,形式为/mem ...
- Android的启动模式(上)
1. 基本介绍 大家平时只要懂一点Android知识的话,都一定会知道,一个应用的组成,往往包含了许多的activity组件,每个activity都应该围绕用户的特定动作进行跳转设计.比如说,一个电话 ...
- Android学习笔记1 android adb启动失败问题 adb server is out of date. killing...
下面是Android的学习笔记,原文地址. 我是使用adb devices出现如下红字错误, 使用第一种方法方法,结果关掉豌豆荚就可以了. android adb启动失败问题 adb server i ...
- studio_ 优化Android Studio 启动、编译和运行速度?
http://www.admin10000.com/document/6842.html: 作为一名 Android 程序员,选择一个好的 IDE 工具可以使开发变得非常高效,很多程序员喜欢使用 Go ...
- Android WIFI 启动流程(TIP^^)
前几天因为解决一堆Bug,没时间写.我不会每天都写,就是为了存档一些资料. 内容来源:工作中接触到的+高手博客+文档(Books)=自己理解 仅限参考^^ 此博客是上一个<<Android ...
- android sdk启动报错error: could not install *smartsocket* listener: cannot bind to 127.0.0.1:5037:
android sdk启动报错error: could not install *smartsocket* listener: cannot bind to 127.0.0.1:5037: 问题原因: ...
随机推荐
- Gitlab Runner的分布式缓存实战
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- 使用JMeter进行压力测试
使用JMeter进行压力测试(基础篇) 1.启动Jmeter 双击图中所示文件出现如下图所示即启动成功 2 新建测试计划 File------>new 2.1右键新建线程组 add-----&g ...
- Docker 的 2020,实 "鼠" 不易!
元旦你们出去嗨,栈长在家撸文章,惨惨惨- 没错, Docker 的 2020 年也过的不是很顺利,可以说是流年不利.命运多舛,一年发生两件大事,太折腾! 相信大家也已经看到很多相关的报道了,但同时也有 ...
- ThreadLocal解决什么问题
原创文章,转载请务必将下面这段话置于文章开头处(保留超链接).本文转发自技术世界,原文链接 http://www.jasongj.com/java/threadlocal/ ThreadLocal解决 ...
- 图解HTTP权威指南(四)| 代理
作者简介 李先生(Lemon),高级运维工程师(自称),SRE专家(目标),梦想在35岁买一辆保时捷.喜欢钻研底层技术,认为底层基础才是王道.一切新技术都离不开操作系统(CPU.内存.磁盘).网络等. ...
- 基于CefSharp开发(七)浏览器收藏夹菜单
一.Edge收藏夹菜单分析 如下图所示为Edge收藏夹菜单, 点击收藏夹菜单按钮(红框部分)弹出收藏夹菜单窗体,窗体中包含工具栏(绿框部分)和树型菜单(黄框部分) 工具栏按钮功能分别为添加当前网页到根 ...
- CTF常见编码及加解密(超全)
@ 目录 前言 常见CTF编码及加解密 补充 ASCII编码 base家族编码 MD5.SHA1.HMAC.NTLM等类似加密型 1.MD5 2.SHA1 3.HMAC 4.NTLM 5.类似加密穷举 ...
- 面试官:你真的了解Redis分布式锁吗?
什么是分布式锁 说到Redis,我们第一想到的功能就是可以缓存数据,除此之外,Redis因为单进程.性能高的特点,它还经常被用于做分布式锁. 锁我们都知道,在程序中的作用就是同步工具,保证共享资源在同 ...
- 温故而知新--day2
温故而知新--day2 类 类与对象 类是一个抽象的概念,是指对现实生活中一类具有共同特征的事物的抽象.其实列化后称为对象.类里面由类属性组成,类属性可以分为数据属性和函数属性(函数属性又称为类方法) ...
- 使用line_profiler对python代码性能进行评估优化
性能测试的意义 在做完一个python项目之后,我们经常要考虑对软件的性能进行优化.那么我们需要一个软件优化的思路,首先我们需要明确软件本身代码以及函数的瓶颈,最理想的情况就是有这样一个工具,能够将一 ...