gt:         if (mScreenOnOffBroadcastReceiver != null) {
context: /*
   * Copyright (C) 2015 Google Inc. All Rights Reserved.
   *
   * 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 jp.sonymusicstudio.cast.castcompanionlibrary.cast.reconnection;
  
  import android.app.Service;
  import android.content.BroadcastReceiver;
  import android.content.Context;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.net.NetworkInfo;
  import android.net.wifi.WifiManager;
  import android.os.IBinder;
  import android.os.SystemClock;
  
  import java.util.Timer;
  import java.util.TimerTask;
  
  import jp.sonymusicstudio.cast.castcompanionlibrary.cast.BaseCastManager;
  import jp.sonymusicstudio.cast.castcompanionlibrary.cast.VideoCastManager;
  import jp.sonymusicstudio.cast.castcompanionlibrary.cast.exceptions.NoConnectionException;
  import jp.sonymusicstudio.cast.castcompanionlibrary.cast.exceptions.TransientNetworkDisconnectionException;
  import jp.sonymusicstudio.cast.castcompanionlibrary.utils.LogUtils;
  import jp.sonymusicstudio.cast.castcompanionlibrary.utils.Utils;
  
  import static jp.sonymusicstudio.cast.castcompanionlibrary.utils.LogUtils.LOGD;
  import static jp.sonymusicstudio.cast.castcompanionlibrary.utils.LogUtils.LOGE;
  
  /**
   * A service to run in the background when the playback of a media starts, to help with reconnection
   * if needed. Due to various reasons, connectivity to the cast device can be lost; for example wifi
   * radio may turn off when device goes to sleep or user may step outside of the wifi range, etc.
   * This service helps with recovering the connectivity when circumstances are right, for example
   * when user steps back within the wifi range, etc. In order to avoid ending up with a background
   * service that lingers around longer than it is needed, this implementation uses certain heuristics
   * to stop itself when needed.
   */
  public class ReconnectionService extends Service
  {
      private static final String TAG = LogUtils.makeLogTag(ReconnectionService.class);
  
      private boolean mWifiConnectivity = true;
      private BroadcastReceiver mScreenOnOffBroadcastReceiver;
      private BroadcastReceiver mWifiBroadcastReceiver;
      private static final int RECONNECTION_ATTEMPT_PERIOD_S = 15;
      private static final long EPSILON_MS = 500;
      private Timer mEndTimer;
      private TimerTask mEndTimerTask;
      private VideoCastManager mCastManager;
  
  
      @Override
      public int onStartCommand(Intent intent, int flags, int startId)
      {
          LOGD(TAG, "onStartCommand() is called");
          setUpEndTimer();
          return Service.START_STICKY;
      }
  
      private void setUpEndTimer()
      {
          LOGD(TAG, "setUpEndTimer(): setting up a timer for the end of current media");
          long timeLeft = getMediaRemainingTime();
          if (timeLeft <= 0) {
              stopSelf();
              return;
          }
          clearEndTimer();
          mEndTimer = new Timer();
          mEndTimerTask = new TimerTask()
          {
              @Override
              public void run()
              {
                  LOGD(TAG, "setUpEndTimer(): stopping ReconnectionService since reached the end of allotted time");
                  handleTermination();
              }
          };
          mEndTimer.schedule(mEndTimerTask, timeLeft);
      }
  
  
      private long getMediaRemainingTime()
      {
          long endTime = mCastManager.getPreferenceAccessor().getLongFromPreference(BaseCastManager.PREFS_KEY_MEDIA_END, 0);
          return endTime - SystemClock.elapsedRealtime();
      }
  
  
      private void clearEndTimer()
      {
          if (mEndTimerTask != null) {
              mEndTimerTask.cancel();
              mEndTimerTask = null;
          }
  
          if (mEndTimer != null) {
              mEndTimer.cancel();
              mEndTimer = null;
          }
      }
  
  
      private void handleTermination()
      {
          if (! mCastManager.isConnected()) {
              mCastManager.clearMediaSession();
              mCastManager.clearPersistedConnectionInfo(BaseCastManager.CLEAR_ALL);
              stopSelf();
          } else {
              // since we are connected and our timer has gone off, lets update the time remaining
              // on the media (since media may have been paused) and reset teh time left
              long timeLeft = 0;
              try {
                  timeLeft = mCastManager.isRemoteStreamLive()
                           ? 0
                           : mCastManager.getMediaTimeRemaining();
  
              } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
                  LOGE(TAG, "Failed to calculate the time left for media due to lack of connectivity", e);
              }
              if (timeLeft < EPSILON_MS) {
                  // no time left
                  stopSelf();
              } else {
                  // lets reset the counter
                  mCastManager.getPreferenceAccessor().saveLongToPreference(
                            BaseCastManager.PREFS_KEY_MEDIA_END
                          , timeLeft + SystemClock.elapsedRealtime()
                  );
                  LOGD(TAG, "handleTermination(): resetting the timer");
                  setUpEndTimer();
              }
          }
      }
  
  
      @Override
      public void onCreate()
      {
          LOGD(TAG, "onCreate() is called");
          mCastManager = VideoCastManager.getInstance();
          if (! mCastManager.isConnected() && ! mCastManager.isConnecting()) {
              mCastManager.reconnectSessionIfPossible();
          }
          // register a broadcast receiver to be notified when screen goes on or off
          IntentFilter screenOnOffIntentFilter = new IntentFilter(Intent.ACTION_SCREEN_ON);
          screenOnOffIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
          mScreenOnOffBroadcastReceiver = new BroadcastReceiver()
          {
              @Override
              public void onReceive(Context context, Intent intent)
              {
                  LOGD(TAG, "ScreenOnOffBroadcastReceiver: onReceive(): " + intent.getAction());
                  long timeLeft = getMediaRemainingTime();
                  if (timeLeft < EPSILON_MS) {
                      handleTermination();
                  }
              }
          };
          registerReceiver(mScreenOnOffBroadcastReceiver, screenOnOffIntentFilter);
  
          // register a wifi receiver that would be notified when the network state changes
          IntentFilter networkIntentFilter = new IntentFilter();
          networkIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
          mWifiBroadcastReceiver = new BroadcastReceiver()
          {
              @Override
              public void onReceive(Context context, Intent intent)
              {
                  final String action = intent.getAction();
                  if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                      NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                      boolean connected = info.isConnected();
                      String networkSsid = connected ? Utils.getWifiSsid(context) : null;
                      ReconnectionService.this.onWifiConnectivityChanged(connected, networkSsid);
                  }
              }
          };
          registerReceiver(mWifiBroadcastReceiver, networkIntentFilter);
          super.onCreate();
      }
  
  
      /**
       * Since framework calls this method twice when a change happens, we are guarding against that
       * by caching the state the first time and avoiding the second call if it is the same status.
       */
      public void onWifiConnectivityChanged(boolean connected, String networkSsid)
      {
          LOGD(TAG, "WIFI connectivity changed to " + (connected ? "enabled" : "disabled"));
          if (connected && !mWifiConnectivity) {
              mWifiConnectivity = true;
              if (mCastManager.isFeatureEnabled(BaseCastManager.FEATURE_WIFI_RECONNECT)) {
                  mCastManager.startCastDiscovery();
                  mCastManager.reconnectSessionIfPossible(RECONNECTION_ATTEMPT_PERIOD_S, networkSsid);
              }
          } else {
              mWifiConnectivity = connected;
          }
      }
  
  
      @Override
      public void onDestroy()
      {
          LOGD(TAG, "onDestroy()");
  