Android LocationManagerService啟動(一)

hlhuang發表於2021-01-30

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()函式裡面

執行流程如下

  1. 建立LocationManagerService物件
  2. 將建立好的的物件加入到ServiceManager裡面
  3. 呼叫LocationManagerServicesystemRunning()函式初始化相關的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的入口函式,主要做以下幾部分工作

  1. 初始化成員變數,主要是個各種需要用的Manager
  2. 初始化Provider
  3. 註冊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裡面初始化的流程

graph TD 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原始碼

相關文章