blob: dea63255594d17813c4141b6e3c3288ce9e9ef7c [file] [log] [blame]
John Spurlockd60258f2015-04-30 09:30:52 -04001/*
2 * Copyright (C) 2015 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
17package com.android.server.notification;
18
John Spurlock2f096ed2015-05-04 11:58:26 -040019import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.content.BroadcastReceiver;
John Spurlockd60258f2015-04-30 09:30:52 -040022import android.content.ComponentName;
23import android.content.Context;
John Spurlock2f096ed2015-05-04 11:58:26 -040024import android.content.Intent;
25import android.content.IntentFilter;
John Spurlockd60258f2015-04-30 09:30:52 -040026import android.net.Uri;
27import android.service.notification.Condition;
28import android.service.notification.IConditionProvider;
29import android.service.notification.ZenModeConfig;
John Spurlock2f096ed2015-05-04 11:58:26 -040030import android.service.notification.ZenModeConfig.EventInfo;
John Spurlockd60258f2015-04-30 09:30:52 -040031import android.util.ArraySet;
32import android.util.Log;
33import android.util.Slog;
34
John Spurlock2f096ed2015-05-04 11:58:26 -040035import com.android.server.notification.CalendarTracker.CheckEventResult;
John Spurlockd60258f2015-04-30 09:30:52 -040036import com.android.server.notification.NotificationManagerService.DumpFilter;
37
38import java.io.PrintWriter;
39
40/**
41 * Built-in zen condition provider for calendar event-based conditions.
42 */
43public class EventConditionProvider extends SystemConditionProviderService {
John Spurlock2f096ed2015-05-04 11:58:26 -040044 private static final String TAG = "ConditionProviders.ECP";
45 private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG);
John Spurlockd60258f2015-04-30 09:30:52 -040046
47 public static final ComponentName COMPONENT =
48 new ComponentName("android", EventConditionProvider.class.getName());
49 private static final String NOT_SHOWN = "...";
John Spurlock2f096ed2015-05-04 11:58:26 -040050 private static final String SIMPLE_NAME = EventConditionProvider.class.getSimpleName();
51 private static final String ACTION_EVALUATE = SIMPLE_NAME + ".EVALUATE";
52 private static final int REQUEST_CODE_EVALUATE = 1;
53 private static final String EXTRA_TIME = "time";
John Spurlockd60258f2015-04-30 09:30:52 -040054
John Spurlock2f096ed2015-05-04 11:58:26 -040055 private final Context mContext = this;
John Spurlockd60258f2015-04-30 09:30:52 -040056 private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>();
John Spurlock2f096ed2015-05-04 11:58:26 -040057 private final CalendarTracker mTracker = new CalendarTracker(mContext);
John Spurlockd60258f2015-04-30 09:30:52 -040058
59 private boolean mConnected;
60 private boolean mRegistered;
John Spurlock2f096ed2015-05-04 11:58:26 -040061 private boolean mBootComplete; // don't hammer the calendar provider until boot completes.
John Spurlockd60258f2015-04-30 09:30:52 -040062
63 public EventConditionProvider() {
John Spurlock2f096ed2015-05-04 11:58:26 -040064 if (DEBUG) Slog.d(TAG, "new " + SIMPLE_NAME + "()");
John Spurlockd60258f2015-04-30 09:30:52 -040065 }
66
67 @Override
68 public ComponentName getComponent() {
69 return COMPONENT;
70 }
71
72 @Override
73 public boolean isValidConditionId(Uri id) {
74 return ZenModeConfig.isValidEventConditionId(id);
75 }
76
77 @Override
78 public void dump(PrintWriter pw, DumpFilter filter) {
John Spurlock2f096ed2015-05-04 11:58:26 -040079 pw.print(" "); pw.print(SIMPLE_NAME); pw.println(":");
John Spurlockd60258f2015-04-30 09:30:52 -040080 pw.print(" mConnected="); pw.println(mConnected);
81 pw.print(" mRegistered="); pw.println(mRegistered);
John Spurlock2f096ed2015-05-04 11:58:26 -040082 pw.print(" mBootComplete="); pw.println(mBootComplete);
John Spurlockd60258f2015-04-30 09:30:52 -040083 pw.println(" mSubscriptions=");
84 for (Uri conditionId : mSubscriptions) {
85 pw.print(" ");
86 pw.println(conditionId);
87 }
John Spurlock2f096ed2015-05-04 11:58:26 -040088 pw.println(" mTracker=");
89 mTracker.dump(" ", pw);
90 }
91
92 @Override
93 public void onBootComplete() {
94 if (DEBUG) Slog.d(TAG, "onBootComplete");
95 if (mBootComplete) return;
96 mBootComplete = true;
97 evaluateSubscriptions();
John Spurlockd60258f2015-04-30 09:30:52 -040098 }
99
100 @Override
101 public void onConnected() {
102 if (DEBUG) Slog.d(TAG, "onConnected");
103 mConnected = true;
104 }
105
106 @Override
107 public void onDestroy() {
108 super.onDestroy();
109 if (DEBUG) Slog.d(TAG, "onDestroy");
110 mConnected = false;
111 }
112
113 @Override
114 public void onRequestConditions(int relevance) {
115 if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance);
116 // does not advertise conditions
117 }
118
119 @Override
120 public void onSubscribe(Uri conditionId) {
121 if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
122 if (!ZenModeConfig.isValidEventConditionId(conditionId)) {
123 notifyCondition(conditionId, Condition.STATE_FALSE, "badCondition");
124 return;
125 }
John Spurlock2f096ed2015-05-04 11:58:26 -0400126 if (mSubscriptions.add(conditionId)) {
127 evaluateSubscriptions();
128 }
John Spurlockd60258f2015-04-30 09:30:52 -0400129 }
130
131 @Override
132 public void onUnsubscribe(Uri conditionId) {
133 if (DEBUG) Slog.d(TAG, "onUnsubscribe " + conditionId);
134 if (mSubscriptions.remove(conditionId)) {
135 evaluateSubscriptions();
136 }
137 }
138
139 @Override
140 public void attachBase(Context base) {
141 attachBaseContext(base);
142 }
143
144 @Override
145 public IConditionProvider asInterface() {
146 return (IConditionProvider) onBind(null);
147 }
148
149 private void evaluateSubscriptions() {
John Spurlock2f096ed2015-05-04 11:58:26 -0400150 if (DEBUG) Log.d(TAG, "evaluateSubscriptions");
151 if (!mBootComplete) {
152 if (DEBUG) Log.d(TAG, "Skipping evaluate before boot complete");
153 return;
John Spurlockd60258f2015-04-30 09:30:52 -0400154 }
John Spurlock2f096ed2015-05-04 11:58:26 -0400155 final long now = System.currentTimeMillis();
156 mTracker.setCallback(mSubscriptions.isEmpty() ? null : mTrackerCallback);
157 setRegistered(!mSubscriptions.isEmpty());
158 long reevaluateAt = 0;
159 for (Uri conditionId : mSubscriptions) {
160 final EventInfo event = ZenModeConfig.tryParseEventConditionId(conditionId);
161 if (event == null) {
162 notifyCondition(conditionId, Condition.STATE_FALSE, "badConditionId");
163 continue;
164 }
165 final CheckEventResult result = mTracker.checkEvent(event, now);
166 if (result.recheckAt != 0 && (reevaluateAt == 0 || result.recheckAt < reevaluateAt)) {
167 reevaluateAt = result.recheckAt;
168 }
169 if (!result.inEvent) {
170 notifyCondition(conditionId, Condition.STATE_FALSE, "!inEventNow");
171 continue;
172 }
173 notifyCondition(conditionId, Condition.STATE_TRUE, "inEventNow");
174 }
175 updateAlarm(now, reevaluateAt);
176 if (DEBUG) Log.d(TAG, "evaluateSubscriptions took " + (System.currentTimeMillis() - now));
177 }
178
179 private void updateAlarm(long now, long time) {
180 final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
181 final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
182 REQUEST_CODE_EVALUATE,
183 new Intent(ACTION_EVALUATE)
184 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
185 .putExtra(EXTRA_TIME, time),
186 PendingIntent.FLAG_UPDATE_CURRENT);
187 alarms.cancel(pendingIntent);
188 if (time == 0 || time < now) {
189 if (DEBUG) Slog.d(TAG, "Not scheduling evaluate: " + (time == 0 ? "no time specified"
190 : "specified time in the past"));
191 return;
192 }
193 if (DEBUG) Slog.d(TAG, String.format("Scheduling evaluate for %s, in %s, now=%s",
194 ts(time), formatDuration(time - now), ts(now)));
195 alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
John Spurlockd60258f2015-04-30 09:30:52 -0400196 }
197
198 private void notifyCondition(Uri conditionId, int state, String reason) {
199 if (DEBUG) Slog.d(TAG, "notifyCondition " + Condition.stateToString(state)
200 + " reason=" + reason);
201 notifyCondition(createCondition(conditionId, state));
202 }
203
204 private Condition createCondition(Uri id, int state) {
205 final String summary = NOT_SHOWN;
206 final String line1 = NOT_SHOWN;
207 final String line2 = NOT_SHOWN;
208 return new Condition(id, summary, line1, line2, 0, state, Condition.FLAG_RELEVANT_ALWAYS);
209 }
210
John Spurlock2f096ed2015-05-04 11:58:26 -0400211 private void setRegistered(boolean registered) {
212 if (mRegistered == registered) return;
213 if (DEBUG) Slog.d(TAG, "setRegistered " + registered);
214 mRegistered = registered;
215 if (mRegistered) {
216 final IntentFilter filter = new IntentFilter();
217 filter.addAction(Intent.ACTION_TIME_CHANGED);
218 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
219 filter.addAction(ACTION_EVALUATE);
220 registerReceiver(mReceiver, filter);
221 } else {
222 unregisterReceiver(mReceiver);
223 }
224 }
225
226 private final CalendarTracker.Callback mTrackerCallback = new CalendarTracker.Callback() {
227 @Override
228 public void onChanged() {
229 if (DEBUG) Log.d(TAG, "mTrackerCallback.onChanged");
230 evaluateSubscriptions();
231 }
232 };
233
234 private BroadcastReceiver mReceiver = new BroadcastReceiver() {
235 @Override
236 public void onReceive(Context context, Intent intent) {
237 if (DEBUG) Slog.d(TAG, "onReceive " + intent.getAction());
238 evaluateSubscriptions();
239 }
240 };
John Spurlockd60258f2015-04-30 09:30:52 -0400241}