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 android.alarmmanager.cts; 18 19 import android.app.AlarmManager; 20 import android.app.PendingIntent; 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.os.SystemClock; 25 import android.util.Log; 26 27 import androidx.annotation.GuardedBy; 28 import androidx.test.InstrumentationRegistry; 29 30 import java.io.File; 31 import java.util.function.LongConsumer; 32 33 public class AlarmReceiver extends BroadcastReceiver { 34 private static final String TAG = AlarmReceiver.class.getSimpleName(); 35 private static final String ALARM_ACTION = "android.alarmmanager.cts.ALARM"; 36 private static final String EXTRA_ALARM_ID = "android.alarmmanager.cts.extra.ALARM_ID"; 37 private static final String EXTRA_QUOTAED = "android.alarmmanager.cts.extra.QUOTAED"; 38 39 private static final Context sContext = InstrumentationRegistry.getTargetContext(); 40 /** Package global history of quotaed alarms received -- useful in quota calculations */ 41 private static final PersistableEventHistory sHistory = new PersistableEventHistory( 42 new File(sContext.getFilesDir(), "alarm-history.xml")); 43 /** Listener alarms or older apps use a lower quota that is managed separately **/ 44 private static final PersistableEventHistory sCompatHistory = new PersistableEventHistory( 45 new File(sContext.getFilesDir(), "alarm-compat-history.xml")); 46 47 private static Object sWaitLock = new Object(); 48 @GuardedBy("sWaitLock") 49 private static int sLastAlarmId; 50 onAlarm(int id, LongConsumer historyRecorder)51 static void onAlarm(int id, LongConsumer historyRecorder) { 52 Log.d(TAG, "Alarm " + id + " received"); 53 54 historyRecorder.accept(SystemClock.elapsedRealtime()); 55 synchronized (sWaitLock) { 56 sLastAlarmId = id; 57 sWaitLock.notifyAll(); 58 } 59 } 60 createListener(int id, boolean quotaed)61 static AlarmManager.OnAlarmListener createListener(int id, boolean quotaed) { 62 return () -> onAlarm(id, quotaed ? sCompatHistory::recordLatestEvent : (t -> {})); 63 } 64 getAlarmSender(int id, boolean quotaed)65 static PendingIntent getAlarmSender(int id, boolean quotaed) { 66 final Intent alarmAction = new Intent(ALARM_ACTION) 67 .setClass(sContext, AlarmReceiver.class) 68 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) 69 .putExtra(EXTRA_ALARM_ID, id) 70 .putExtra(EXTRA_QUOTAED, quotaed); 71 return PendingIntent.getBroadcast(sContext, 0, alarmAction, 72 PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); 73 } 74 75 @Override onReceive(Context context, Intent intent)76 public void onReceive(Context context, Intent intent) { 77 if (ALARM_ACTION.equals(intent.getAction())) { 78 final int id = intent.getIntExtra(EXTRA_ALARM_ID, -1); 79 final boolean quotaed = intent.getBooleanExtra(EXTRA_QUOTAED, false); 80 onAlarm(id, quotaed ? sHistory::recordLatestEvent : (t -> {})); 81 } 82 } 83 getNthLastAlarmTime(int n)84 static long getNthLastAlarmTime(int n) { 85 return sHistory.getNthLastEventTime(n); 86 } 87 getNthLastCompatAlarmTime(int n)88 static long getNthLastCompatAlarmTime(int n) { 89 return sCompatHistory.getNthLastEventTime(n); 90 } 91 waitForAlarm(int alarmId, long timeOut)92 static boolean waitForAlarm(int alarmId, long timeOut) throws InterruptedException { 93 final long deadline = SystemClock.elapsedRealtime() + timeOut; 94 synchronized (sWaitLock) { 95 while (sLastAlarmId != alarmId && SystemClock.elapsedRealtime() < deadline) { 96 sWaitLock.wait(timeOut); 97 } 98 return sLastAlarmId == alarmId; 99 } 100 } 101 102 /** 103 * Used to dump debugging information when the test fails. 104 */ dumpState()105 static void dumpState() { 106 synchronized (sWaitLock) { 107 Log.i(TAG, "Last alarm id: " + sLastAlarmId); 108 } 109 sHistory.dump("History of quotaed alarms"); 110 sCompatHistory.dump("History of quotaed compat alarms"); 111 } 112 } 113