OSDN Git Service

am 7eabd92a: am 32079484: am 4186379a: Revert "NDKr10b changes to be published to...
[android-x86/frameworks-base.git] / services / core / java / com / android / server / notification / DowntimeConditionProvider.java
1 /**
2  * Copyright (c) 2014, 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.server.notification;
18
19 import android.app.AlarmManager;
20 import android.app.PendingIntent;
21 import android.content.BroadcastReceiver;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.net.Uri;
27 import android.service.notification.Condition;
28 import android.service.notification.ConditionProviderService;
29 import android.service.notification.IConditionProvider;
30 import android.service.notification.ZenModeConfig;
31 import android.service.notification.ZenModeConfig.DowntimeInfo;
32 import android.text.format.DateFormat;
33 import android.util.ArraySet;
34 import android.util.Log;
35 import android.util.Slog;
36
37 import com.android.internal.R;
38 import com.android.server.notification.NotificationManagerService.DumpFilter;
39
40 import java.io.PrintWriter;
41 import java.text.SimpleDateFormat;
42 import java.util.Calendar;
43 import java.util.Date;
44 import java.util.Locale;
45 import java.util.Objects;
46 import java.util.TimeZone;
47
48 /** Built-in zen condition provider for managing downtime */
49 public class DowntimeConditionProvider extends ConditionProviderService {
50     private static final String TAG = "DowntimeConditions";
51     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
52
53     public static final ComponentName COMPONENT =
54             new ComponentName("android", DowntimeConditionProvider.class.getName());
55
56     private static final String ENTER_ACTION = TAG + ".enter";
57     private static final int ENTER_CODE = 100;
58     private static final String EXIT_ACTION = TAG + ".exit";
59     private static final int EXIT_CODE = 101;
60     private static final String EXTRA_TIME = "time";
61
62     private final Calendar mCalendar = Calendar.getInstance();
63     private final Context mContext = this;
64     private final ArraySet<Integer> mDays = new ArraySet<Integer>();
65
66     private boolean mConnected;
67     private boolean mInDowntime;
68     private ZenModeConfig mConfig;
69     private Callback mCallback;
70
71     public DowntimeConditionProvider() {
72         if (DEBUG) Slog.d(TAG, "new DowntimeConditionProvider()");
73     }
74
75     public void dump(PrintWriter pw, DumpFilter filter) {
76         pw.println("    DowntimeConditionProvider:");
77         pw.print("      mConnected="); pw.println(mConnected);
78         pw.print("      mInDowntime="); pw.println(mInDowntime);
79     }
80
81     public void attachBase(Context base) {
82         attachBaseContext(base);
83     }
84
85     public IConditionProvider asInterface() {
86         return (IConditionProvider) onBind(null);
87     }
88
89     public void setCallback(Callback callback) {
90         mCallback = callback;
91     }
92
93     @Override
94     public void onConnected() {
95         if (DEBUG) Slog.d(TAG, "onConnected");
96         mConnected = true;
97         final IntentFilter filter = new IntentFilter();
98         filter.addAction(ENTER_ACTION);
99         filter.addAction(EXIT_ACTION);
100         filter.addAction(Intent.ACTION_TIME_CHANGED);
101         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
102         mContext.registerReceiver(mReceiver, filter);
103         init();
104     }
105
106     @Override
107     public void onDestroy() {
108         if (DEBUG) Slog.d(TAG, "onDestroy");
109         mConnected = false;
110     }
111
112     @Override
113     public void onRequestConditions(int relevance) {
114         if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance);
115         if ((relevance & Condition.FLAG_RELEVANT_NOW) != 0) {
116             if (mInDowntime && mConfig != null) {
117                 notifyCondition(createCondition(mConfig.toDowntimeInfo(), Condition.STATE_TRUE));
118             }
119         }
120     }
121
122     @Override
123     public void onSubscribe(Uri conditionId) {
124         if (DEBUG) Slog.d(TAG, "onSubscribe conditionId=" + conditionId);
125         final DowntimeInfo downtime = ZenModeConfig.tryParseDowntimeConditionId(conditionId);
126         if (downtime != null && mConfig != null) {
127             final int state = mConfig.toDowntimeInfo().equals(downtime) && mInDowntime
128                     ? Condition.STATE_TRUE : Condition.STATE_FALSE;
129             if (DEBUG) Slog.d(TAG, "notify condition state: " + Condition.stateToString(state));
130             notifyCondition(createCondition(downtime, state));
131         }
132     }
133
134     @Override
135     public void onUnsubscribe(Uri conditionId) {
136         if (DEBUG) Slog.d(TAG, "onUnsubscribe conditionId=" + conditionId);
137     }
138
139     public void setConfig(ZenModeConfig config) {
140         if (Objects.equals(mConfig, config)) return;
141         if (DEBUG) Slog.d(TAG, "setConfig");
142         mConfig = config;
143         if (mConnected) {
144             init();
145         }
146     }
147
148     public boolean isInDowntime() {
149         return mInDowntime;
150     }
151
152     public Condition createCondition(DowntimeInfo downtime, int state) {
153         if (downtime == null) return null;
154         final Uri id = ZenModeConfig.toDowntimeConditionId(downtime);
155         final String skeleton = DateFormat.is24HourFormat(mContext) ? "Hm" : "hma";
156         final Locale locale = Locale.getDefault();
157         final String pattern = DateFormat.getBestDateTimePattern(locale, skeleton);
158         final long time = getTime(System.currentTimeMillis(), downtime.endHour, downtime.endMinute);
159         final String formatted = new SimpleDateFormat(pattern, locale).format(new Date(time));
160         final String summary = mContext.getString(R.string.downtime_condition_summary, formatted);
161         return new Condition(id, summary, "", "", 0, state, Condition.FLAG_RELEVANT_NOW);
162     }
163
164     public boolean isDowntimeCondition(Condition condition) {
165         return condition != null && ZenModeConfig.isValidDowntimeConditionId(condition.id);
166     }
167
168     private void init() {
169         updateDays();
170         reevaluateDowntime();
171         updateAlarms();
172     }
173
174     private void updateDays() {
175         mDays.clear();
176         if (mConfig != null) {
177             final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode);
178             for (int i = 0; days != null && i < days.length; i++) {
179                 mDays.add(days[i]);
180             }
181         }
182     }
183
184     private boolean isInDowntime(long time) {
185         if (mConfig == null || mDays.size() == 0) return false;
186         final long start = getTime(time, mConfig.sleepStartHour, mConfig.sleepStartMinute);
187         long end = getTime(time, mConfig.sleepEndHour, mConfig.sleepEndMinute);
188         if (start == end) return false;
189         if (end < start) {
190             end = addDays(end, 1);
191         }
192         return isInDowntime(-1, time, start, end) || isInDowntime(0, time, start, end);
193     }
194
195     private boolean isInDowntime(int daysOffset, long time, long start, long end) {
196         final int day = ((getDayOfWeek(time) + daysOffset - 1) % Calendar.SATURDAY) + 1;
197         start = addDays(start, daysOffset);
198         end = addDays(end, daysOffset);
199         return mDays.contains(day) && time >= start && time < end;
200     }
201
202     private void reevaluateDowntime() {
203         final boolean inDowntime = isInDowntime(System.currentTimeMillis());
204         if (DEBUG) Slog.d(TAG, "inDowntime=" + inDowntime);
205         if (inDowntime == mInDowntime) return;
206         Slog.i(TAG, (inDowntime ? "Entering" : "Exiting" ) + " downtime");
207         mInDowntime = inDowntime;
208         ZenLog.traceDowntime(mInDowntime, getDayOfWeek(System.currentTimeMillis()), mDays);
209         fireDowntimeChanged();
210     }
211
212     private void fireDowntimeChanged() {
213         if (mCallback != null) {
214             mCallback.onDowntimeChanged(mInDowntime);
215         }
216     }
217
218     private void updateAlarms() {
219         if (mConfig == null) return;
220         updateAlarm(ENTER_ACTION, ENTER_CODE, mConfig.sleepStartHour, mConfig.sleepStartMinute);
221         updateAlarm(EXIT_ACTION, EXIT_CODE, mConfig.sleepEndHour, mConfig.sleepEndMinute);
222     }
223
224     private int getDayOfWeek(long time) {
225         mCalendar.setTimeInMillis(time);
226         return mCalendar.get(Calendar.DAY_OF_WEEK);
227     }
228
229     private long getTime(long millis, int hour, int min) {
230         mCalendar.setTimeInMillis(millis);
231         mCalendar.set(Calendar.HOUR_OF_DAY, hour);
232         mCalendar.set(Calendar.MINUTE, min);
233         mCalendar.set(Calendar.SECOND, 0);
234         mCalendar.set(Calendar.MILLISECOND, 0);
235         return mCalendar.getTimeInMillis();
236     }
237
238     private long addDays(long time, int days) {
239         mCalendar.setTimeInMillis(time);
240         mCalendar.add(Calendar.DATE, days);
241         return mCalendar.getTimeInMillis();
242     }
243
244     private void updateAlarm(String action, int requestCode, int hr, int min) {
245         final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
246         final long now = System.currentTimeMillis();
247         mCalendar.setTimeInMillis(now);
248         mCalendar.set(Calendar.HOUR_OF_DAY, hr);
249         mCalendar.set(Calendar.MINUTE, min);
250         mCalendar.set(Calendar.SECOND, 0);
251         mCalendar.set(Calendar.MILLISECOND, 0);
252         long time = mCalendar.getTimeInMillis();
253         if (time <= now) {
254             time = addDays(time, 1);
255         }
256         final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
257                 new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT);
258         alarms.cancel(pendingIntent);
259         if (mConfig.sleepMode != null) {
260             if (DEBUG) Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s",
261                     action, ts(time), time - now, ts(now)));
262             alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
263         }
264     }
265
266     private static String ts(long time) {
267         return new Date(time) + " (" + time + ")";
268     }
269
270     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
271         @Override
272         public void onReceive(Context context, Intent intent) {
273             final String action = intent.getAction();
274             final long now = System.currentTimeMillis();
275             if (ENTER_ACTION.equals(action) || EXIT_ACTION.equals(action)) {
276                 final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
277                 if (DEBUG) Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
278                         action, ts(schTime), ts(now), now - schTime));
279             } else if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
280                 if (DEBUG) Slog.d(TAG, "timezone changed to " + TimeZone.getDefault());
281                 mCalendar.setTimeZone(TimeZone.getDefault());
282             } else {
283                 if (DEBUG) Slog.d(TAG, action + " fired at " + now);
284             }
285             reevaluateDowntime();
286             updateAlarms();
287         }
288     };
289
290     public interface Callback {
291         void onDowntimeChanged(boolean inDowntime);
292     }
293 }