1 /*
2  * Copyright (C) 2020 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.deskclock
18 
19 import android.annotation.SuppressLint
20 import android.content.BroadcastReceiver
21 import android.content.Context
22 import android.content.Intent
23 
24 import com.android.deskclock.AlarmAlertWakeLock.createPartialWakeLock
25 import com.android.deskclock.alarms.AlarmStateManager
26 import com.android.deskclock.controller.Controller
27 import com.android.deskclock.data.DataModel
28 
29 class AlarmInitReceiver : BroadcastReceiver() {
30     /**
31      * This receiver handles a variety of actions:
32      *
33      * <ul>
34      *     <li>Clean up backup data that was recently restored to this device on
35      *     ACTION_COMPLETE_RESTORE.</li>
36      *     <li>Reset timers and stopwatch on ACTION_BOOT_COMPLETED</li>
37      *     <li>Fix alarm states on ACTION_BOOT_COMPLETED, TIME_SET, TIMEZONE_CHANGED,
38      *     and LOCALE_CHANGED</li>
39      *     <li>Rebuild notifications on MY_PACKAGE_REPLACED</li>
40      * </ul>
41      */
onReceivenull42     override fun onReceive(context: Context, intent: Intent) {
43         val action = intent.action
44         LogUtils.i("AlarmInitReceiver $action")
45 
46         val result = goAsync()
47         val wl = createPartialWakeLock(context)
48         wl.acquire()
49 
50         // We need to increment the global id out of the async task to prevent race conditions
51         DataModel.dataModel.updateGlobalIntentId()
52 
53         // Updates stopwatch and timer data after a device reboot so they are as accurate as
54         // possible.
55         if (ACTION_BOOT_COMPLETED == action) {
56             DataModel.dataModel.updateAfterReboot()
57             // Stopwatch and timer data need to be updated on time change so the reboot
58             // functionality works as expected.
59         } else if (Intent.ACTION_TIME_CHANGED == action) {
60             DataModel.dataModel.updateAfterTimeSet()
61         }
62 
63         // Update shortcuts so they exist for the user.
64         if (Intent.ACTION_BOOT_COMPLETED == action || Intent.ACTION_LOCALE_CHANGED == action) {
65             Controller.getController().updateShortcuts()
66             NotificationUtils.updateNotificationChannels(context)
67         }
68 
69         // Notifications are canceled by the system on application upgrade. This broadcast signals
70         // that the new app is free to rebuild the notifications using the existing data.
71         // Additionally on new app installs, make sure to enable shortcuts immediately as opposed
72         // to waiting for system reboot.
73         if (Intent.ACTION_MY_PACKAGE_REPLACED == action) {
74             DataModel.dataModel.updateAllNotifications()
75             Controller.getController().updateShortcuts()
76         }
77 
78         AsyncHandler.post {
79             try {
80                 // Process restored data if any exists
81                 if (!DeskClockBackupAgent.processRestoredData(context)) {
82                     // Update all the alarm instances on time change event
83                     AlarmStateManager.fixAlarmInstances(context)
84                 }
85             } finally {
86                 result.finish()
87                 wl.release()
88                 LogUtils.v("AlarmInitReceiver finished")
89             }
90         }
91     }
92 
93     companion object {
94         /**
95          * When running on N devices, we're interested in the boot completed event that is sent
96          * while the user is still locked, so that we can schedule alarms.
97          */
98         @SuppressLint("InlinedApi")
99         private val ACTION_BOOT_COMPLETED = if (Utils.isNOrLater()) {
100             Intent.ACTION_LOCKED_BOOT_COMPLETED
101         } else {
102             Intent.ACTION_BOOT_COMPLETED
103         }
104     }
105 }