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