Home | History | Annotate | Download | only in location
      1 package com.android.server.location;
      2 
      3 import android.content.Context;
      4 import android.net.ConnectivityManager;
      5 import android.net.NetworkInfo;
      6 import android.os.Handler;
      7 import android.os.Looper;
      8 import android.os.PowerManager;
      9 import android.os.PowerManager.WakeLock;
     10 import android.util.Log;
     11 import android.util.NtpTrustedTime;
     12 
     13 import com.android.internal.annotations.GuardedBy;
     14 import com.android.internal.annotations.VisibleForTesting;
     15 
     16 import java.util.Date;
     17 
     18 /**
     19  * Handles inject NTP time to GNSS.
     20  *
     21  * <p>The client is responsible to call {@link #onNetworkAvailable()} when network is available
     22  * for retrieving NTP Time.
     23  */
     24 class NtpTimeHelper {
     25 
     26     private static final String TAG = "NtpTimeHelper";
     27     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     28 
     29     // states for injecting ntp
     30     private static final int STATE_PENDING_NETWORK = 0;
     31     private static final int STATE_RETRIEVING_AND_INJECTING = 1;
     32     private static final int STATE_IDLE = 2;
     33 
     34     // how often to request NTP time, in milliseconds
     35     // current setting 24 hours
     36     @VisibleForTesting
     37     static final long NTP_INTERVAL = 24 * 60 * 60 * 1000;
     38 
     39     // how long to wait if we have a network error in NTP
     40     // the initial value of the exponential backoff
     41     // current setting - 5 minutes
     42     @VisibleForTesting
     43     static final long RETRY_INTERVAL = 5 * 60 * 1000;
     44     // how long to wait if we have a network error in NTP
     45     // the max value of the exponential backoff
     46     // current setting - 4 hours
     47     private static final long MAX_RETRY_INTERVAL = 4 * 60 * 60 * 1000;
     48 
     49     private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
     50     private static final String WAKELOCK_KEY = "NtpTimeHelper";
     51 
     52     private final ExponentialBackOff mNtpBackOff = new ExponentialBackOff(RETRY_INTERVAL,
     53             MAX_RETRY_INTERVAL);
     54 
     55     private final ConnectivityManager mConnMgr;
     56     private final NtpTrustedTime mNtpTime;
     57     private final WakeLock mWakeLock;
     58     private final Handler mHandler;
     59 
     60     @GuardedBy("this")
     61     private final InjectNtpTimeCallback mCallback;
     62 
     63     // flags to trigger NTP when network becomes available
     64     // initialized to STATE_PENDING_NETWORK so we do NTP when the network comes up after booting
     65     @GuardedBy("this")
     66     private int mInjectNtpTimeState = STATE_PENDING_NETWORK;
     67 
     68     // set to true if the GPS engine requested on-demand NTP time requests
     69     @GuardedBy("this")
     70     private boolean mOnDemandTimeInjection;
     71 
     72     interface InjectNtpTimeCallback {
     73         void injectTime(long time, long timeReference, int uncertainty);
     74     }
     75 
     76     @VisibleForTesting
     77     NtpTimeHelper(Context context, Looper looper, InjectNtpTimeCallback callback,
     78             NtpTrustedTime ntpTime) {
     79         mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
     80         mCallback = callback;
     81         mNtpTime = ntpTime;
     82         mHandler = new Handler(looper);
     83         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
     84         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
     85     }
     86 
     87     NtpTimeHelper(Context context, Looper looper, InjectNtpTimeCallback callback) {
     88         this(context, looper, callback, NtpTrustedTime.getInstance(context));
     89     }
     90 
     91     synchronized void enablePeriodicTimeInjection() {
     92         mOnDemandTimeInjection = true;
     93     }
     94 
     95     synchronized void onNetworkAvailable() {
     96         if (mInjectNtpTimeState == STATE_PENDING_NETWORK) {
     97             retrieveAndInjectNtpTime();
     98         }
     99     }
    100 
    101     /**
    102      * @return {@code true} if there is a network available for outgoing connections,
    103      * {@code false} otherwise.
    104      */
    105     private boolean isNetworkConnected() {
    106         NetworkInfo activeNetworkInfo = mConnMgr.getActiveNetworkInfo();
    107         return activeNetworkInfo != null && activeNetworkInfo.isConnected();
    108     }
    109 
    110     synchronized void retrieveAndInjectNtpTime() {
    111         if (mInjectNtpTimeState == STATE_RETRIEVING_AND_INJECTING) {
    112             // already downloading data
    113             return;
    114         }
    115         if (!isNetworkConnected()) {
    116             // try again when network is up
    117             mInjectNtpTimeState = STATE_PENDING_NETWORK;
    118             return;
    119         }
    120         mInjectNtpTimeState = STATE_RETRIEVING_AND_INJECTING;
    121 
    122         // hold wake lock while task runs
    123         mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
    124         new Thread(this::blockingGetNtpTimeAndInject).start();
    125     }
    126 
    127     /** {@link NtpTrustedTime#forceRefresh} is a blocking network operation. */
    128     private void blockingGetNtpTimeAndInject() {
    129         long delay;
    130 
    131         // force refresh NTP cache when outdated
    132         boolean refreshSuccess = true;
    133         if (mNtpTime.getCacheAge() >= NTP_INTERVAL) {
    134             // Blocking network operation.
    135             refreshSuccess = mNtpTime.forceRefresh();
    136         }
    137 
    138         synchronized (this) {
    139             mInjectNtpTimeState = STATE_IDLE;
    140 
    141             // only update when NTP time is fresh
    142             // If refreshSuccess is false, cacheAge does not drop down.
    143             if (mNtpTime.getCacheAge() < NTP_INTERVAL) {
    144                 long time = mNtpTime.getCachedNtpTime();
    145                 long timeReference = mNtpTime.getCachedNtpTimeReference();
    146                 long certainty = mNtpTime.getCacheCertainty();
    147 
    148                 if (DEBUG) {
    149                     long now = System.currentTimeMillis();
    150                     Log.d(TAG, "NTP server returned: "
    151                             + time + " (" + new Date(time)
    152                             + ") reference: " + timeReference
    153                             + " certainty: " + certainty
    154                             + " system time offset: " + (time - now));
    155                 }
    156 
    157                 // Ok to cast to int, as can't rollover in practice
    158                 mHandler.post(() -> mCallback.injectTime(time, timeReference, (int) certainty));
    159 
    160                 delay = NTP_INTERVAL;
    161                 mNtpBackOff.reset();
    162             } else {
    163                 Log.e(TAG, "requestTime failed");
    164                 delay = mNtpBackOff.nextBackoffMillis();
    165             }
    166 
    167             if (DEBUG) {
    168                 Log.d(TAG, String.format(
    169                         "onDemandTimeInjection=%s, refreshSuccess=%s, delay=%s",
    170                         mOnDemandTimeInjection,
    171                         refreshSuccess,
    172                         delay));
    173             }
    174             // TODO(b/73893222): reconcile Capabilities bit 'on demand' name vs. de facto periodic
    175             // injection.
    176             if (mOnDemandTimeInjection || !refreshSuccess) {
    177                 /* Schedule next NTP injection.
    178                  * Since this is delayed, the wake lock is released right away, and will be held
    179                  * again when the delayed task runs.
    180                  */
    181                 mHandler.postDelayed(this::retrieveAndInjectNtpTime, delay);
    182             }
    183         }
    184         try {
    185             // release wake lock held by task
    186             mWakeLock.release();
    187         } catch (Exception e) {
    188             // This happens when the WakeLock is already released.
    189         }
    190     }
    191 }
    192