Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3164ff66 authored by Eva Chen's avatar Eva Chen Committed by Android (Google) Code Review
Browse files

Merge changes Ife27ecd6,I1a893ea8

* changes:
  Add GnssTimeUpdateService.
  Add meta-text about gnss time update attribution.
parents af1fafe9 270ae737
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -5387,6 +5387,9 @@
         on-device data -->
    <attribution android:tag="OfflineLocationTimeZoneProviderService"
                 android:label="@string/offline_location_time_zone_detection_service_attribution"/>
    <!-- Attribution for Gnss Time Update service. -->
    <attribution android:tag="GnssTimeUpdateService"
                 android:label="@string/gnss_time_update_service"/>

    <application android:process="system"
                 android:persistent="true"
+4 −0
Original line number Diff line number Diff line
@@ -1577,6 +1577,10 @@
        <item>network</item>
    </string-array>

    <!-- Enables the GnssTimeUpdate service. This is the global switch for enabling Gnss time based
         suggestions to TimeDetector service. See also config_autoTimeSourcesPriority. -->
    <bool name="config_enableGnssTimeUpdateService">false</bool>

    <!-- Enables the TimeZoneRuleManager service. This is the global switch for the updateable time
         zone update mechanism. -->
    <bool name="config_enableUpdateableTimeZoneRules">false</bool>
+2 −0
Original line number Diff line number Diff line
@@ -450,6 +450,8 @@
         understand which sub-unit of an application is requesting permissions and using power.
         [CHAR LIMIT=NONE]-->
    <string name="offline_location_time_zone_detection_service_attribution">Time Zone Detector (No connectivity)</string>
    <!-- Attribution for Gnss Time Update service. [CHAR LIMIT=NONE]-->
    <string name="gnss_time_update_service">GNSS Time Update Service</string>

    <!-- Factory reset warning dialog strings--> <skip />
    <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
+1 −0
Original line number Diff line number Diff line
@@ -2177,6 +2177,7 @@
  <java-symbol type="string" name="config_persistentDataPackageName" />
  <java-symbol type="string" name="config_deviceConfiguratorPackageName" />
  <java-symbol type="array" name="config_autoTimeSourcesPriority" />
  <java-symbol type="bool" name="config_enableGnssTimeUpdateService" />
  <java-symbol type="bool" name="config_enableGeolocationTimeZoneDetection" />
  <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneProvider" />
  <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" />
+203 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.timedetector;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.TimeDetector;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationManagerInternal;
import android.location.LocationRequest;
import android.location.LocationTime;
import android.os.Binder;
import android.os.SystemClock;
import android.os.TimestampedValue;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.time.Duration;

/**
 * Monitors the GNSS time.
 *
 * <p>When available, the time is always suggested to the {@link
 * com.android.server.timedetector.TimeDetectorService} where it may be used to set the device
 * system clock, depending on user settings and what other signals are available.
 */
public final class GnssTimeUpdateService extends Binder {
    private static final String TAG = "GnssTimeUpdateService";
    private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);

    /**
     * Handles the lifecycle events for the GnssTimeUpdateService.
     */
    public static class Lifecycle extends SystemService {
        private GnssTimeUpdateService mService;

        public Lifecycle(@NonNull Context context) {
            super(context);
        }

        @Override
        public void onStart() {
            mService = new GnssTimeUpdateService(getContext());
            publishBinderService("gnss_time_update_service", mService);
        }

        @Override
        public void onBootPhase(int phase) {
            // Need to wait for some location providers to be enabled. If done at
            // PHASE_SYSTEM_SERVICES_READY, error where "gps" provider does not exist could occur.
            if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
                // Initiate location updates. On boot, GNSS might not be available right away.
                // Instead of polling GNSS time periodically, passive location updates are enabled.
                // Once an update is received, the gnss time will be queried and suggested to
                // TimeDetectorService.
                mService.requestGnssTimeUpdates();
            }
        }
    }

    private static final Duration GNSS_TIME_UPDATE_ALARM_INTERVAL = Duration.ofHours(4);
    private static final String ATTRIBUTION_TAG = "GnssTimeUpdateService";

    private final Context mContext;
    private final TimeDetector mTimeDetector;
    private final AlarmManager mAlarmManager;
    private final LocationManager mLocationManager;
    private final LocationManagerInternal mLocationManagerInternal;

    @Nullable private AlarmManager.OnAlarmListener mAlarmListener;
    @Nullable private LocationListener mLocationListener;
    @Nullable private TimestampedValue<Long> mLastSuggestedGnssTime;

    @VisibleForTesting
    GnssTimeUpdateService(@NonNull Context context) {
        mContext = context.createAttributionContext(ATTRIBUTION_TAG);
        mTimeDetector = mContext.getSystemService(TimeDetector.class);
        mLocationManager = mContext.getSystemService(LocationManager.class);
        mAlarmManager = mContext.getSystemService(AlarmManager.class);
        mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
    }

    /**
     * Request passive location updates. Such a request will not trigger any active locations or
     * power usage itself.
     */
    @VisibleForTesting
    void requestGnssTimeUpdates() {
        if (D) {
            Log.d(TAG, "requestGnssTimeUpdates()");
        }

        // Location Listener triggers onLocationChanged() when GNSS data is available, so
        // that the getGnssTimeMillis() function doesn't need to be continuously polled.
        mLocationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                if (D) {
                    Log.d(TAG, "onLocationChanged()");
                }

                // getGnssTimeMillis() can return null when the Master Location Switch for the
                // foreground user is disabled.
                LocationTime locationTime = mLocationManagerInternal.getGnssTimeMillis();
                if (locationTime != null) {
                    suggestGnssTime(locationTime);
                } else {
                    if (D) {
                        Log.d(TAG, "getGnssTimeMillis() returned null");
                    }
                }

                mLocationManager.removeUpdates(mLocationListener);
                mLocationListener = null;

                mAlarmListener = new AlarmManager.OnAlarmListener() {
                    @Override
                    public void onAlarm() {
                        if (D) {
                            Log.d(TAG, "onAlarm()");
                        }
                        mAlarmListener = null;
                        requestGnssTimeUpdates();
                    }
                };

                // Set next alarm to re-enable location updates.
                long next = SystemClock.elapsedRealtime()
                        + GNSS_TIME_UPDATE_ALARM_INTERVAL.toMillis();
                mAlarmManager.set(
                        AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        next,
                        TAG,
                        mAlarmListener,
                        FgThread.getHandler());
            }
        };

        mLocationManager.requestLocationUpdates(
                LocationManager.GPS_PROVIDER,
                new LocationRequest.Builder(LocationRequest.PASSIVE_INTERVAL)
                        .setMinUpdateIntervalMillis(0)
                        .build(),
                FgThread.getExecutor(),
                mLocationListener);
    }

    /**
     * Convert LocationTime to TimestampedValue. Then suggest TimestampedValue to Time Detector.
     */
    private void suggestGnssTime(LocationTime locationTime) {
        if (D) {
            Log.d(TAG, "suggestGnssTime()");
        }
        long gnssTime = locationTime.getTime();
        long elapsedRealtimeMs = locationTime.getElapsedRealtimeNanos() / 1_000_000L;

        TimestampedValue<Long> timeSignal = new TimestampedValue<>(
                elapsedRealtimeMs, gnssTime);
        mLastSuggestedGnssTime = timeSignal;

        GnssTimeSuggestion timeSuggestion = new GnssTimeSuggestion(timeSignal);
        mTimeDetector.suggestGnssTime(timeSuggestion);
    }

    @Override
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
        pw.println("mLastSuggestedGnssTime: " + mLastSuggestedGnssTime);
        pw.print("state: ");
        if (mLocationListener != null) {
            pw.println("time updates enabled");
        } else {
            pw.println("alarm enabled");
        }
    }
}
Loading