blob: 90559079ef0a59b05bfc5cc77fa0b2125c83cdb2 [file] [log] [blame]
Jon Miranda16ea1b12017-12-12 14:52:48 -08001/*
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 */
16package com.android.wallpaper.module;
17
18import android.content.BroadcastReceiver;
19import android.content.Context;
20import android.content.Intent;
21import android.os.PowerManager;
22import android.os.PowerManager.WakeLock;
Jon Miranda16ea1b12017-12-12 14:52:48 -080023import android.util.Log;
24
25import com.android.wallpaper.model.WallpaperMetadata;
26import com.android.wallpaper.module.WallpaperPreferences.PresentationMode;
27import com.android.wallpaper.module.WallpaperRefresher.RefreshListener;
28import com.android.wallpaper.util.DiskBasedLogger;
29
30import java.util.Calendar;
31
Sunny Goyal8600a3f2018-08-15 12:48:01 -070032import androidx.annotation.Nullable;
33
Jon Miranda16ea1b12017-12-12 14:52:48 -080034/**
35 * Performs daily logging operations when alarm is received.
36 */
37public class DailyLoggingAlarmReceiver extends BroadcastReceiver {
38
39 private static final String TAG = "DailyLoggingAlarm";
40
41 /**
42 * Releases the provided WakeLock if and only if it's currently held as to avoid throwing a
43 * "WakeLock under-locked" RuntimeException.
44 */
45 private static void releaseWakeLock(WakeLock wakeLock) {
46 if (wakeLock.isHeld()) {
47 wakeLock.release();
48 }
49 }
50
51 @Override
52 public void onReceive(Context context, Intent intent) {
53 Context appContext = context.getApplicationContext();
54 Injector injector = InjectorProvider.getInjector();
55 UserEventLogger logger = injector.getUserEventLogger(appContext);
56 WallpaperPreferences preferences = injector.getPreferences(appContext);
57
58 logger.logNumDailyWallpaperRotationsInLastWeek();
59 logger.logNumDailyWallpaperRotationsPreviousDay();
60 logger.logWallpaperPresentationMode();
61 logDailyActive(appContext);
62
63 preferences.setLastDailyLogTimestamp(System.currentTimeMillis());
64
65 logDailyWallpaperRotationStatus(appContext);
66
67 // Clear disk-based logs older than 7 days if they exist.
68 DiskBasedLogger.clearOldLogs(appContext);
69 }
70
71 private void logDailyActive(Context appContext) {
72 Injector injector = InjectorProvider.getInjector();
73 UserEventLogger logger = injector.getUserEventLogger(appContext);
74 WallpaperPreferences preferences = injector.getPreferences(appContext);
75
76 long lastAppLaunchTimestamp = preferences.getLastAppActiveTimestamp();
77
78 Calendar calendar = Calendar.getInstance();
79 // Subtract 28 days first to get a date 28 days ago to assess 28 day active, then add back
80 // enough days to calculate 14 day active, 7 day active, and 1 day active.
81 calendar.add(Calendar.DAY_OF_YEAR, -28);
82 long twentyEightDaysAgo = calendar.getTimeInMillis();
83
84 calendar.add(Calendar.DAY_OF_YEAR, 14);
85 long fourteenDaysAgo = calendar.getTimeInMillis();
86
87 calendar.add(Calendar.DAY_OF_YEAR, 7);
88 long sevenDaysAgo = calendar.getTimeInMillis();
89
90 calendar.add(Calendar.DAY_OF_YEAR, 6);
91 long oneDayAgo = calendar.getTimeInMillis();
92
93 if (lastAppLaunchTimestamp >= oneDayAgo) {
94 logger.log1DayActive();
95 }
96 if (lastAppLaunchTimestamp >= sevenDaysAgo) {
97 logger.log7DayActive();
98 }
99 if (lastAppLaunchTimestamp >= fourteenDaysAgo) {
100 logger.log14DayActive();
101 }
102 if (lastAppLaunchTimestamp >= twentyEightDaysAgo) {
103 logger.log28DayActive();
104 }
105 }
106
107 /**
108 * If daily wallpapers are currently in effect and were enabled more than 24 hours ago, then log
109 * the last-known rotation status as reported by the periodic background rotation components
110 * (BackdropAlarmReceiver and BackdropRotationTask), or if there wasn't any status update in the
111 * last 24 hours then log a "not attempted" status to the UserEventLogger.
112 */
113 private void logDailyWallpaperRotationStatus(Context appContext) {
114 // Acquire a partial wakelock because logging the daily rotation requires doing some work on
115 // another thread (via AsyncTask) after #onReceive returns, after which the kernel may power
116 // down and prevent our daily rotation log from being sent.
117 PowerManager powerManager = (PowerManager) appContext.getSystemService(Context.POWER_SERVICE);
118 final WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
119 wakeLock.acquire(10000 /* timeout */);
120
121 final Injector injector = InjectorProvider.getInjector();
122
123 // First check if rotation is still in effect.
124 injector.getWallpaperRefresher(appContext).refresh(new RefreshListener() {
125 @Override
126 public void onRefreshed(WallpaperMetadata homeWallpaperMetadata,
127 @Nullable WallpaperMetadata lockWallpaperMetadata,
128 @PresentationMode int presentationMode) {
129 // Don't log or do anything else if presentation mode is not rotating.
130 if (presentationMode != WallpaperPreferences.PRESENTATION_MODE_ROTATING) {
131 releaseWakeLock(wakeLock);
132 return;
133 }
134
135 WallpaperPreferences preferences = injector.getPreferences(appContext);
136
137 long dailyWallpaperEnabledTimestamp = preferences.getDailyWallpaperEnabledTimestamp();
138 // Validate the daily wallpaper enabled timestamp.
139 if (dailyWallpaperEnabledTimestamp < 0) {
140 Log.e(TAG, "There's no valid daily wallpaper enabled timestamp");
141 releaseWakeLock(wakeLock);
142 return;
143 }
144
145 Calendar midnightYesterday = Calendar.getInstance();
146 midnightYesterday.add(Calendar.DAY_OF_MONTH, -1);
147 midnightYesterday.set(Calendar.HOUR_OF_DAY, 0);
148 midnightYesterday.set(Calendar.MINUTE, 0);
149
150 // Exclude rotations that were put into affect later than midnight yesterday because the
151 // background task may not have had a chance to execute yet.
152 if (dailyWallpaperEnabledTimestamp > midnightYesterday.getTimeInMillis()) {
153 releaseWakeLock(wakeLock);
154 return;
155 }
156
157 try {
158 long lastRotationStatusTimestamp =
159 preferences.getDailyWallpaperLastRotationStatusTimestamp();
160
161 UserEventLogger logger = injector.getUserEventLogger(appContext);
162
163 // If a rotation status was reported more recently than midnight yesterday, then log it.
164 // Otherwise, log a "not attempted" rotation status.
165 if (lastRotationStatusTimestamp > midnightYesterday.getTimeInMillis()) {
166 int lastDailyWallpaperRotationStatus =
167 preferences.getDailyWallpaperLastRotationStatus();
168
169 logger.logDailyWallpaperRotationStatus(lastDailyWallpaperRotationStatus);
170
171 // If the daily rotation status is "failed", increment the num days failed in
172 // SharedPreferences and log it, otherwise reset the counter in SharedPreferences to 0.
173 if (UserEventLogger.ROTATION_STATUS_FAILED == lastDailyWallpaperRotationStatus) {
174 preferences.incrementNumDaysDailyRotationFailed();
175 logger.logNumDaysDailyRotationFailed(preferences.getNumDaysDailyRotationFailed());
176 } else {
177 preferences.resetNumDaysDailyRotationFailed();
178 }
179
180 // If there was a valid rotation status reported since midnight yesterday, then reset
181 // the counter for consecutive days of "not attempted".
182 preferences.resetNumDaysDailyRotationNotAttempted();
183 } else {
184 logger.logDailyWallpaperRotationStatus(UserEventLogger.ROTATION_STATUS_NOT_ATTEMPTED);
185
186 // Increment and log the consecutive # days in a row that daily rotation was not
187 // attempted.
188 preferences.incrementNumDaysDailyRotationNotAttempted();
189 logger.logNumDaysDailyRotationNotAttempted(
190 preferences.getNumDaysDailyRotationNotAttempted());
191
192 // Reset the disk-based counter for number of consecutive days daily rotation failed
193 // because if rotation was not attempted but restarts tomorrow after a boot and fails
194 // then, we want to report that as 1 day of failure instead of 3 consecutive days.
195 preferences.resetNumDaysDailyRotationFailed();
196 }
197 } finally {
198 releaseWakeLock(wakeLock);
199 }
200 }
201 });
202 }
203}