content: /*
   * Copyright (C) 2018 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.app.AlarmManager;
  import android.content.ContentResolver;
  import android.content.Context;
  import android.content.Intent;
  import android.os.PowerManager;
  import android.os.SystemClock;
  import android.os.SystemProperties;
  import android.os.UserHandle;
  import android.provider.Settings;
  import android.util.Slog;
  
  import java.util.Objects;
  
  /**
   * The real implementation of {@link TimeDetectorStrategy.Callback} used on device.
   */
  public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrategy.Callback {
  
      private final static String TAG = "timedetector.TimeDetectorStrategyCallbackImpl";
  
      private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000;
  
      /**
       * If a newly calculated system clock time and the current system clock time differs by this or
       * more the system clock will actually be updated. Used to prevent the system clock being set
       * for only minor differences.
       */
      private final int mSystemClockUpdateThresholdMillis;
  
      @NonNull private final Context mContext;
      @NonNull private final ContentResolver mContentResolver;
      @NonNull private final PowerManager.WakeLock mWakeLock;
      @NonNull private final AlarmManager mAlarmManager;
  
      public TimeDetectorStrategyCallbackImpl(@NonNull Context context) {
          mContext = Objects.requireNonNull(context);
          mContentResolver = Objects.requireNonNull(context.getContentResolver());
  
          PowerManager powerManager = context.getSystemService(PowerManager.class);
          mWakeLock = Objects.requireNonNull(
                  powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG));
  
          mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class));
  
          mSystemClockUpdateThresholdMillis =
                  SystemProperties.getInt("ro.sys.time_detector_update_diff",
                          SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT);
      }
  
      @Override
      public int systemClockUpdateThresholdMillis() {
          return mSystemClockUpdateThresholdMillis;
      }
  
      @Override
      public boolean isTimeDetectionEnabled() {
          try {
              return Settings.Global.getInt(mContentResolver, Settings.Global.AUTO_TIME) != 0;
          } catch (Settings.SettingNotFoundException snfe) {
              return true;
          }
      }
  
      @Override
      public void acquireWakeLock() {
          if (mWakeLock.isHeld()) {
              Slog.wtf(TAG, "WakeLock " + mWakeLock + " already held");
          }
          mWakeLock.acquire();
      }
  
      @Override
      public long elapsedRealtimeMillis() {
          checkWakeLockHeld();
          return SystemClock.elapsedRealtime();
      }
  
      @Override
      public long systemClockMillis() {
          checkWakeLockHeld();
          return System.currentTimeMillis();
      }
  
      @Override
      public void setSystemClock(long newTimeMillis) {
          checkWakeLockHeld();
          mAlarmManager.setTime(newTimeMillis);
      }
  
      @Override
      public void releaseWakeLock() {
          checkWakeLockHeld();
          mWakeLock.release();
      }
  
      @Override
      public void sendStickyBroadcast(@NonNull Intent intent) {
          mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
      }
  
      private void checkWakeLockHeld() {
          if (!mWakeLock.isHeld()) {
              Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held");
          }
      }
  }
  