1 /* 2 * Copyright (C) 2021 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.permissioncontroller.permission.service 18 19 import android.content.BroadcastReceiver 20 import android.content.Context 21 import android.content.Intent 22 import android.content.SharedPreferences 23 import androidx.annotation.VisibleForTesting 24 import androidx.preference.PreferenceManager 25 import com.android.permissioncontroller.DumpableLog 26 import com.android.permissioncontroller.permission.data.PermissionEvent 27 import com.android.permissioncontroller.permission.utils.SystemTimeSource 28 import com.android.permissioncontroller.permission.utils.TimeSource 29 import kotlin.math.abs 30 import kotlinx.coroutines.Dispatchers 31 import kotlinx.coroutines.GlobalScope 32 import kotlinx.coroutines.launch 33 34 /** 35 * [BroadcastReceiver] to update the persisted timestamps when the date changes. Receives broadcasts 36 * for [Intent.ACTION_TIME_CHANGED] to know when the system time has changed. However, this action 37 * does not include metadata that allows for us to know how much the time has changed. To keep track 38 * of the delta we take a snapshot of the current time and time since boot after the 39 * [Intent.ACTION_BOOT_COMPLETED] action. 40 */ 41 class PermissionStorageTimeChangeReceiver( 42 private val storages: List<PermissionEventStorage<out PermissionEvent>> = 43 PermissionEventStorageImpls.getInstance(), 44 private val timeSource: TimeSource = SystemTimeSource() 45 ) : BroadcastReceiver() { 46 47 companion object { 48 private const val LOG_TAG = "PermissionStorageTimeChangeReceiver" 49 50 /** 51 * Key for the last known system time from the system. First initialized after boot 52 * complete. 53 */ 54 @VisibleForTesting const val PREF_KEY_SYSTEM_TIME_SNAPSHOT = "system_time_snapshot" 55 56 /** 57 * Key for the last known elapsed time since boot. First initialized after boot complete. 58 */ 59 @VisibleForTesting 60 const val PREF_KEY_ELAPSED_REALTIME_SNAPSHOT = "elapsed_realtime_snapshot" 61 62 @VisibleForTesting const val SNAPSHOT_UNINITIALIZED = -1L 63 64 /** The millisecond threshold for a time delta to be considered a time change. */ 65 private const val TIME_CHANGE_THRESHOLD_MILLIS = 60 * 1000L 66 } 67 onReceivenull68 override fun onReceive(context: Context, intent: Intent) { 69 if (storages.isEmpty()) { 70 return 71 } 72 when (val action = intent.action) { 73 Intent.ACTION_BOOT_COMPLETED -> { 74 persistTimeSnapshots( 75 context, 76 timeSource.currentTimeMillis(), 77 timeSource.elapsedRealtime() 78 ) 79 } 80 Intent.ACTION_TIME_CHANGED -> { 81 checkForTimeChanged(context) 82 } 83 else -> { 84 DumpableLog.e(LOG_TAG, "Unexpected action $action") 85 } 86 } 87 } 88 checkForTimeChangednull89 private fun checkForTimeChanged(context: Context) { 90 val systemTimeSnapshot = getSystemTimeSnapshot(context) 91 val realtimeSnapshot = getElapsedRealtimeSnapshot(context) 92 if ( 93 realtimeSnapshot == SNAPSHOT_UNINITIALIZED || 94 systemTimeSnapshot == SNAPSHOT_UNINITIALIZED 95 ) { 96 DumpableLog.e(LOG_TAG, "Snapshots not initialized") 97 return 98 } 99 val actualSystemTime = timeSource.currentTimeMillis() 100 val actualRealtime = timeSource.elapsedRealtime() 101 val expectedSystemTime = (actualRealtime - realtimeSnapshot) + systemTimeSnapshot 102 val diffSystemTime = actualSystemTime - expectedSystemTime 103 if (abs(diffSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS) { 104 DumpableLog.d(LOG_TAG, "Time changed by ${diffSystemTime / 1000} seconds") 105 onTimeChanged(diffSystemTime) 106 persistTimeSnapshots(context, actualSystemTime, actualRealtime) 107 } 108 } 109 110 @VisibleForTesting onTimeChangednull111 fun onTimeChanged(diffSystemTime: Long) { 112 GlobalScope.launch(Dispatchers.IO) { 113 for (storage in storages) { 114 storage.updateEventsBySystemTimeDelta(diffSystemTime) 115 } 116 } 117 } 118 persistTimeSnapshotsnull119 private fun persistTimeSnapshots( 120 context: Context, 121 systemTimeSnapshot: Long, 122 realtimeSnapshot: Long 123 ) { 124 DumpableLog.d(LOG_TAG, "systemTimeSnapshot set to $systemTimeSnapshot") 125 DumpableLog.d(LOG_TAG, "realtimeSnapshot set to $realtimeSnapshot") 126 context.sharedPreferences.edit().apply { 127 putLong(PREF_KEY_SYSTEM_TIME_SNAPSHOT, systemTimeSnapshot) 128 putLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, realtimeSnapshot) 129 apply() 130 } 131 } 132 getSystemTimeSnapshotnull133 private fun getSystemTimeSnapshot(context: Context): Long { 134 return context.sharedPreferences.getLong( 135 PREF_KEY_SYSTEM_TIME_SNAPSHOT, 136 SNAPSHOT_UNINITIALIZED 137 ) 138 } 139 getElapsedRealtimeSnapshotnull140 private fun getElapsedRealtimeSnapshot(context: Context): Long { 141 return context.sharedPreferences.getLong( 142 PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, 143 SNAPSHOT_UNINITIALIZED 144 ) 145 } 146 147 val Context.sharedPreferences: SharedPreferences 148 get() { 149 return PreferenceManager.getDefaultSharedPreferences(this) 150 } 151 } 152