1d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar/*
2d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar * Copyright 2018 The Android Open Source Project
3d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar *
4d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar * Licensed under the Apache License, Version 2.0 (the "License");
5d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar * you may not use this file except in compliance with the License.
6d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar * You may obtain a copy of the License at
7d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar *
8d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar *      http://www.apache.org/licenses/LICENSE-2.0
9d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar *
10d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar * Unless required by applicable law or agreed to in writing, software
11d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar * distributed under the License is distributed on an "AS IS" BASIS,
12d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar * See the License for the specific language governing permissions and
14d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar * limitations under the License.
15d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar */
16d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
17d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarpackage androidx.work.impl.background.systemalarm;
18d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
19d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarimport static android.app.AlarmManager.RTC_WAKEUP;
20d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
21d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarimport android.app.AlarmManager;
22d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarimport android.app.PendingIntent;
23d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarimport android.content.Context;
24d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarimport android.content.Intent;
25d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarimport android.os.Build;
26d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarimport android.support.annotation.NonNull;
27d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarimport android.support.annotation.RestrictTo;
28697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumarimport android.util.Log;
29d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
30d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarimport androidx.work.impl.WorkDatabase;
31d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarimport androidx.work.impl.WorkManagerImpl;
3218e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumarimport androidx.work.impl.model.SystemIdInfo;
3318e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumarimport androidx.work.impl.model.SystemIdInfoDao;
34d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarimport androidx.work.impl.utils.IdGenerator;
35d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
36d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar/**
37d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar * A common class for managing Alarms.
38d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar *
39d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar * @hide
40d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar */
41d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
42d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumarclass Alarms {
43d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
44d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar    private static final String TAG = "Alarms";
45d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
46d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar    /**
47d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar     * Sets an exact alarm after cancelling any existing alarms for the given id.
48d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar     *
49d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar     * @param context         The application {@link Context}.
50d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar     * @param workManager     The instance of {@link WorkManagerImpl}.
51d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar     * @param workSpecId      The {@link androidx.work.impl.model.WorkSpec} identifier.
52d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar     * @param triggerAtMillis Determines when to trigger the Alarm.
53d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar     */
54d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar    public static void setAlarm(
55d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            @NonNull Context context,
56d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            @NonNull WorkManagerImpl workManager,
57d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            @NonNull String workSpecId,
58d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            long triggerAtMillis) {
59d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
60d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        WorkDatabase workDatabase = workManager.getWorkDatabase();
6118e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar        SystemIdInfoDao systemIdInfoDao = workDatabase.systemIdInfoDao();
6218e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar        SystemIdInfo systemIdInfo = systemIdInfoDao.getSystemIdInfo(workSpecId);
6318e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar        if (systemIdInfo != null) {
6418e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar            cancelExactAlarm(context, workSpecId, systemIdInfo.systemId);
6518e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar            setExactAlarm(context, workSpecId, systemIdInfo.systemId, triggerAtMillis);
66d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        } else {
67d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            IdGenerator idGenerator = new IdGenerator(context);
68d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            int alarmId = idGenerator.nextAlarmManagerId();
6918e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar            SystemIdInfo newSystemIdInfo = new SystemIdInfo(workSpecId, alarmId);
7018e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar            systemIdInfoDao.insertSystemIdInfo(newSystemIdInfo);
71d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            setExactAlarm(context, workSpecId, alarmId, triggerAtMillis);
72d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        }
73d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar    }
74d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
75d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar    /**
7618e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar     * Cancels an existing alarm and removes the {@link SystemIdInfo}.
77d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar     *
78d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar     * @param context     The application {@link Context}.
79d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar     * @param workManager The instance of {@link WorkManagerImpl}.
80d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar     * @param workSpecId  The {@link androidx.work.impl.model.WorkSpec} identifier.
81d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar     */
82d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar    public static void cancelAlarm(
83d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            @NonNull Context context,
84d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            @NonNull WorkManagerImpl workManager,
85d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            @NonNull String workSpecId) {
86d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
87d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        WorkDatabase workDatabase = workManager.getWorkDatabase();
8818e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar        SystemIdInfoDao systemIdInfoDao = workDatabase.systemIdInfoDao();
8918e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar        SystemIdInfo systemIdInfo = systemIdInfoDao.getSystemIdInfo(workSpecId);
9018e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar        if (systemIdInfo != null) {
9118e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar            cancelExactAlarm(context, workSpecId, systemIdInfo.systemId);
9218e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar            Log.d(TAG, String.format("Removing SystemIdInfo for workSpecId (%s)", workSpecId));
9318e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar            systemIdInfoDao.removeSystemIdInfo(workSpecId);
94d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        }
95d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar    }
96d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
97d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar    private static void cancelExactAlarm(
98d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            @NonNull Context context,
99d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            @NonNull String workSpecId,
100d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            int alarmId) {
101d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
102d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
103d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        Intent delayMet = CommandHandler.createDelayMetIntent(context, workSpecId);
104d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        PendingIntent pendingIntent = PendingIntent.getService(
105d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar                context, alarmId, delayMet, PendingIntent.FLAG_NO_CREATE);
106d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        if (pendingIntent != null && alarmManager != null) {
107697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar            Log.d(TAG, String.format(
10818e0a7e02c1198c5b3b33295b148c1017e32f016Rahul Ravikumar                    "Cancelling existing alarm with (workSpecId, systemId) (%s, %s)",
109697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar                    workSpecId,
110697d6a4a3797bc71d0dd8685937a318e9934066bRahul Ravikumar                    alarmId));
111d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            alarmManager.cancel(pendingIntent);
112d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        }
113d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar    }
114d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
115d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar    private static void setExactAlarm(
116d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            @NonNull Context context,
117d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            @NonNull String workSpecId,
118d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            int alarmId,
119d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            long triggerAtMillis) {
120d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar
121d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
122d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        Intent delayMet = CommandHandler.createDelayMetIntent(context, workSpecId);
123d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        PendingIntent pendingIntent = PendingIntent.getService(
124d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar                context, alarmId, delayMet, PendingIntent.FLAG_ONE_SHOT);
125d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        if (alarmManager != null) {
126d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            if (Build.VERSION.SDK_INT >= 19) {
127d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar                alarmManager.setExact(RTC_WAKEUP, triggerAtMillis, pendingIntent);
128d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            } else {
129d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar                alarmManager.set(RTC_WAKEUP, triggerAtMillis, pendingIntent);
130d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar            }
131d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar        }
132d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar    }
133f97ddbc502678a00306afcd73e7a6bb3bcc4c189Sumir Kataria
134f97ddbc502678a00306afcd73e7a6bb3bcc4c189Sumir Kataria    private Alarms() {
135f97ddbc502678a00306afcd73e7a6bb3bcc4c189Sumir Kataria    }
136d70f977a2a312353a82ffefeeef67bb23941f78eRahul Ravikumar}
137