1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.providers.calendar; 18 19 import android.content.Context; 20 import android.content.SharedPreferences; 21 import android.os.SystemClock; 22 import android.os.UserManager; 23 import android.provider.Settings; 24 import android.provider.Settings.Global; 25 import android.text.format.DateUtils; 26 import android.util.Log; 27 import android.util.Slog; 28 29 import com.android.internal.annotations.GuardedBy; 30 import com.android.internal.annotations.VisibleForTesting; 31 32 /** 33 * We call {@link #checkLastCheckTime} at the provider public entry points to make sure 34 * {@link CalendarAlarmManager#scheduleNextAlarmLocked} has been called recently enough. 35 * 36 * atest tests/src/com/android/providers/calendar/CalendarConfidenceCheckerTest.java 37 */ 38 public class CalendarConfidenceChecker { 39 private static final String TAG = "CalendarConfidenceChecker"; 40 41 private static final boolean DEBUG = false; 42 43 private static final long MAX_ALLOWED_CHECK_INTERVAL_MS = 44 CalendarAlarmManager.NEXT_ALARM_CHECK_TIME_MS; 45 46 /** 47 * If updateLastCheckTime isn't called after user unlock within this time, 48 * we call scheduleNextAlarmCheckRightNow. 49 */ 50 private static final long MAX_ALLOWED_REAL_TIME_AFTER_UNLOCK_MS = 51 15 * DateUtils.MINUTE_IN_MILLIS; 52 53 /** 54 * Minimum interval between WTFs. 55 */ 56 private static final long WTF_INTERVAL_MS = 60 * DateUtils.MINUTE_IN_MILLIS; 57 58 private static final String PREF_NAME = "sanity"; 59 private static final String LAST_CHECK_REALTIME_PREF_KEY = "last_check_realtime"; 60 private static final String LAST_CHECK_BOOT_COUNT_PREF_KEY = "last_check_boot_count"; 61 private static final String LAST_WTF_REALTIME_PREF_KEY = "last_wtf_realtime"; 62 63 private final Context mContext; 64 65 private final Object mLock = new Object(); 66 67 @GuardedBy("mLock") 68 @VisibleForTesting 69 final SharedPreferences mPrefs; 70 CalendarConfidenceChecker(Context context)71 public CalendarConfidenceChecker(Context context) { 72 mContext = context; 73 mPrefs = mContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); 74 } 75 76 @VisibleForTesting getRealtimeMillis()77 protected long getRealtimeMillis() { 78 return SystemClock.elapsedRealtime(); 79 } 80 81 @VisibleForTesting getBootCount()82 protected long getBootCount() { 83 return Settings.Global.getLong(mContext.getContentResolver(), Global.BOOT_COUNT, 0); 84 } 85 86 @VisibleForTesting getUserUnlockTime()87 protected long getUserUnlockTime() { 88 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 89 final long startTime = um.getUserStartRealtime(); 90 final long unlockTime = um.getUserUnlockRealtime(); 91 if (DEBUG) { 92 Log.d(TAG, String.format("User start/unlock time=%d/%d", startTime, unlockTime)); 93 } 94 return unlockTime; 95 } 96 97 /** 98 * Called by {@link CalendarAlarmManager#scheduleNextAlarmLocked} 99 */ updateLastCheckTime()100 public final void updateLastCheckTime() { 101 final long now = getRealtimeMillis(); 102 if (DEBUG) { 103 Log.d(TAG, "updateLastCheckTime: now=" + now); 104 } 105 synchronized (mLock) { 106 mPrefs.edit() 107 .putLong(LAST_CHECK_REALTIME_PREF_KEY, now) 108 .putLong(LAST_CHECK_BOOT_COUNT_PREF_KEY, getBootCount()) 109 .apply(); 110 } 111 } 112 113 /** 114 * Call this at public entry points. This will check if the last check time was recent enough, 115 * and otherwise it'll call {@link CalendarAlarmManager#checkNextAlarmCheckRightNow}. 116 */ checkLastCheckTime()117 public final boolean checkLastCheckTime() { 118 final long lastBootCount; 119 final long lastCheckTime; 120 final long lastWtfTime; 121 122 synchronized (mLock) { 123 lastBootCount = mPrefs.getLong(LAST_CHECK_BOOT_COUNT_PREF_KEY, -1); 124 lastCheckTime = mPrefs.getLong(LAST_CHECK_REALTIME_PREF_KEY, -1); 125 lastWtfTime = mPrefs.getLong(LAST_WTF_REALTIME_PREF_KEY, 0); 126 127 final long nowBootCount = getBootCount(); 128 final long nowRealtime = getRealtimeMillis(); 129 130 final long unlockTime = getUserUnlockTime(); 131 132 if (DEBUG) { 133 Log.d(TAG, String.format("isStateValid: %d/%d %d/%d unlocked=%d lastWtf=%d", 134 lastBootCount, nowBootCount, lastCheckTime, nowRealtime, unlockTime, 135 lastWtfTime)); 136 } 137 138 if (lastBootCount != nowBootCount) { 139 // This branch means updateLastCheckTime() hasn't been called since boot. 140 141 debug("checkLastCheckTime: Last check time not set."); 142 143 if (unlockTime == 0) { 144 debug("checkLastCheckTime: unlockTime=0."); // This shouldn't happen though. 145 return true; 146 } 147 148 if ((nowRealtime - unlockTime) <= MAX_ALLOWED_REAL_TIME_AFTER_UNLOCK_MS) { 149 debug("checkLastCheckTime: nowRealtime okay."); 150 return true; 151 } 152 debug("checkLastCheckTime: nowRealtime too old"); 153 } else { 154 // This branch means updateLastCheckTime() has been called since boot. 155 156 if ((nowRealtime - lastWtfTime) <= WTF_INTERVAL_MS) { 157 debug("checkLastCheckTime: Last WTF recent, skipping check."); 158 return true; 159 } 160 161 if ((nowRealtime - lastCheckTime) <= MAX_ALLOWED_CHECK_INTERVAL_MS) { 162 debug("checkLastCheckTime: Last check was recent, okay."); 163 return true; 164 } 165 } 166 Slog.wtf(TAG, String.format("Last check time %d was too old. now=%d (boot count=%d/%d)", 167 lastCheckTime, nowRealtime, lastBootCount, nowBootCount)); 168 169 mPrefs.edit() 170 .putLong(LAST_CHECK_REALTIME_PREF_KEY, 0) 171 .putLong(LAST_WTF_REALTIME_PREF_KEY, nowRealtime) 172 .putLong(LAST_CHECK_BOOT_COUNT_PREF_KEY, getBootCount()) 173 .apply(); 174 175 // Note mCalendarProvider2 really shouldn't be null. 176 CalendarAlarmManager.checkNextAlarmCheckRightNow(mContext); 177 } 178 return false; 179 } 180 debug(String message)181 void debug(String message) { 182 if (DEBUG) { 183 Log.d(TAG, message); 184 } 185 } 186 } 187