summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorJohn Spurlock <jspurlock@google.com>2014-04-22 15:02:36 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-04-22 15:03:08 +0000
commit860107a2cced79f4a19542bfd19676367951ee49 (patch)
tree47490ab60d45d1b978b6ba85131721929d4581ee /services
parent49c4aeef081d7c9f075027cd9112858cdcab8166 (diff)
parent056c519df1dfb8fdc57daddfdf09bc0e1ffddac4 (diff)
downloadframeworks_base-860107a2cced79f4a19542bfd19676367951ee49.zip
frameworks_base-860107a2cced79f4a19542bfd19676367951ee49.tar.gz
frameworks_base-860107a2cced79f4a19542bfd19676367951ee49.tar.bz2
Merge "Do not disturb: persist user config."
Diffstat (limited to 'services')
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java227
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java312
2 files changed, 419 insertions, 120 deletions
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5b597a3..c8bdb4c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -52,6 +52,7 @@ import android.media.IRingtonePlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -64,6 +65,7 @@ import android.provider.Settings;
import android.service.notification.INotificationListener;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
+import android.service.notification.ZenModeConfig;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -78,6 +80,7 @@ import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.notification.NotificationScorer;
+import com.android.internal.util.FastXmlSerializer;
import com.android.server.EventLogTags;
import com.android.server.notification.NotificationUsageStats.SingleNotificationStats;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -87,11 +90,13 @@ import com.android.server.lights.LightsManager;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Array;
@@ -115,6 +120,7 @@ public class NotificationManagerService extends SystemService {
// message codes
static final int MESSAGE_TIMEOUT = 2;
+ static final int MESSAGE_SAVE_POLICY_FILE = 3;
static final int LONG_DELAY = 3500; // 3.5 seconds
static final int SHORT_DELAY = 2000; // 2 seconds
@@ -209,15 +215,6 @@ public class NotificationManagerService extends SystemService {
private final NotificationUsageStats mUsageStats = new NotificationUsageStats();
- private int mZenMode;
- // temporary, until we update apps to provide metadata
- private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
- "com.google.android.dialer",
- "com.android.phone"
- ));
- private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList(
- "com.google.android.deskclock"
- ));
private static final String EXTRA_INTERCEPT = "android.intercept";
// Profiles of the current user.
@@ -421,53 +418,82 @@ public class NotificationManagerService extends SystemService {
Archive mArchive = new Archive();
- private void loadBlockDb() {
- synchronized(mBlockedPackages) {
- if (mPolicyFile == null) {
- File dir = new File("/data/system");
- mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml"));
+ private void loadPolicyFile() {
+ synchronized(mPolicyFile) {
+ mBlockedPackages.clear();
- mBlockedPackages.clear();
-
- FileInputStream infile = null;
- try {
- infile = mPolicyFile.openRead();
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(infile, null);
-
- int type;
- String tag;
- int version = DB_VERSION;
- while ((type = parser.next()) != END_DOCUMENT) {
- tag = parser.getName();
- if (type == START_TAG) {
- if (TAG_BODY.equals(tag)) {
- version = Integer.parseInt(
- parser.getAttributeValue(null, ATTR_VERSION));
- } else if (TAG_BLOCKED_PKGS.equals(tag)) {
- while ((type = parser.next()) != END_DOCUMENT) {
- tag = parser.getName();
- if (TAG_PACKAGE.equals(tag)) {
- mBlockedPackages.add(
- parser.getAttributeValue(null, ATTR_NAME));
- } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
- break;
- }
+ FileInputStream infile = null;
+ try {
+ infile = mPolicyFile.openRead();
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(infile, null);
+
+ int type;
+ String tag;
+ int version = DB_VERSION;
+ while ((type = parser.next()) != END_DOCUMENT) {
+ tag = parser.getName();
+ if (type == START_TAG) {
+ if (TAG_BODY.equals(tag)) {
+ version = Integer.parseInt(
+ parser.getAttributeValue(null, ATTR_VERSION));
+ } else if (TAG_BLOCKED_PKGS.equals(tag)) {
+ while ((type = parser.next()) != END_DOCUMENT) {
+ tag = parser.getName();
+ if (TAG_PACKAGE.equals(tag)) {
+ mBlockedPackages.add(
+ parser.getAttributeValue(null, ATTR_NAME));
+ } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
+ break;
}
}
}
}
- } catch (FileNotFoundException e) {
- // No data yet
- } catch (IOException e) {
- Log.wtf(TAG, "Unable to read blocked notifications database", e);
- } catch (NumberFormatException e) {
- Log.wtf(TAG, "Unable to parse blocked notifications database", e);
- } catch (XmlPullParserException e) {
- Log.wtf(TAG, "Unable to parse blocked notifications database", e);
- } finally {
- IoUtils.closeQuietly(infile);
+ mZenModeHelper.readXml(parser);
}
+ } catch (FileNotFoundException e) {
+ // No data yet
+ } catch (IOException e) {
+ Log.wtf(TAG, "Unable to read notification policy", e);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Unable to parse notification policy", e);
+ } catch (XmlPullParserException e) {
+ Log.wtf(TAG, "Unable to parse notification policy", e);
+ } finally {
+ IoUtils.closeQuietly(infile);
+ }
+ }
+ }
+
+ public void savePolicyFile() {
+ mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
+ mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
+ }
+
+ private void handleSavePolicyFile() {
+ Slog.d(TAG, "handleSavePolicyFile");
+ synchronized (mPolicyFile) {
+ final FileOutputStream stream;
+ try {
+ stream = mPolicyFile.startWrite();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file", e);
+ return;
+ }
+
+ try {
+ final XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, TAG_BODY);
+ out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
+ mZenModeHelper.writeXml(out);
+ out.endTag(null, TAG_BODY);
+ out.endDocument();
+ mPolicyFile.finishWrite(stream);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to save policy file, restoring backup", e);
+ mPolicyFile.failWrite(stream);
}
}
}
@@ -1066,10 +1092,7 @@ public class NotificationManagerService extends SystemService {
@Override
public boolean allowDisable(int what, IBinder token, String pkg) {
- if (isCall(pkg, null)) {
- return mZenMode == Settings.Global.ZEN_MODE_OFF;
- }
- return true;
+ return mZenModeHelper.allowDisable(what, token, pkg);
}
@Override
@@ -1194,9 +1217,6 @@ public class NotificationManagerService extends SystemService {
private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
= Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
- private final Uri ZEN_MODE
- = Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
-
SettingsObserver(Handler handler) {
super(handler);
}
@@ -1207,8 +1227,6 @@ public class NotificationManagerService extends SystemService {
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
false, this, UserHandle.USER_ALL);
- resolver.registerContentObserver(ZEN_MODE,
- false, this);
update(null);
}
@@ -1229,13 +1247,11 @@ public class NotificationManagerService extends SystemService {
if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
rebindListenerServices();
}
- if (ZEN_MODE.equals(uri)) {
- updateZenMode();
- }
}
}
private SettingsObserver mSettingsObserver;
+ private ZenModeHelper mZenModeHelper;
static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
int[] ar = r.getIntArray(resid);
@@ -1261,6 +1277,15 @@ public class NotificationManagerService extends SystemService {
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
mHandler = new WorkerHandler();
+ mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
+ mZenModeHelper.setCallback(new ZenModeHelper.Callback() {
+ @Override
+ public void onConfigChanged() {
+ savePolicyFile();
+ }
+ });
+ final File systemDir = new File(Environment.getDataDirectory(), "system");
+ mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
importOldBlockDb();
@@ -1297,7 +1322,7 @@ public class NotificationManagerService extends SystemService {
Settings.Global.DEVICE_PROVISIONED, 0)) {
mDisableNotificationAlerts = true;
}
- updateZenMode();
+ mZenModeHelper.updateZenMode();
updateCurrentProfilesCache(getContext());
@@ -1350,7 +1375,7 @@ public class NotificationManagerService extends SystemService {
* Read the old XML-based app block database and import those blockages into the AppOps system.
*/
private void importOldBlockDb() {
- loadBlockDb();
+ loadPolicyFile();
PackageManager pm = getContext().getPackageManager();
for (String pkg : mBlockedPackages) {
@@ -1363,9 +1388,6 @@ public class NotificationManagerService extends SystemService {
}
}
mBlockedPackages.clear();
- if (mPolicyFile != null) {
- mPolicyFile.delete();
- }
}
@Override
@@ -1745,6 +1767,18 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public ZenModeConfig getZenModeConfig() {
+ checkCallerIsSystem();
+ return mZenModeHelper.getConfig();
+ }
+
+ @Override
+ public boolean setZenModeConfig(ZenModeConfig config) {
+ checkCallerIsSystem();
+ return mZenModeHelper.setConfig(config);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -1825,7 +1859,6 @@ public class NotificationManagerService extends SystemService {
pw.println(" mSoundNotification=" + mSoundNotification);
pw.println(" mVibrateNotification=" + mVibrateNotification);
pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts);
- pw.println(" mZenMode=" + Settings.Global.zenModeToString(mZenMode));
pw.println(" mSystemReady=" + mSystemReady);
pw.println(" mArchive=" + mArchive.toString());
Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
@@ -1841,6 +1874,8 @@ public class NotificationManagerService extends SystemService {
pw.println("\n Usage Stats:");
mUsageStats.dump(pw, " ");
+ pw.println("\n Zen Mode:");
+ mZenModeHelper.dump(pw, " ");
}
}
@@ -1973,7 +2008,7 @@ public class NotificationManagerService extends SystemService {
}
// Is this notification intercepted by zen mode?
- final boolean intercept = shouldIntercept(pkg, notification);
+ final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification);
notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
// Should this notification make noise, vibe, or use the LED?
@@ -2358,6 +2393,9 @@ public class NotificationManagerService extends SystemService {
case MESSAGE_TIMEOUT:
handleTimeout((ToastRecord)msg.obj);
break;
+ case MESSAGE_SAVE_POLICY_FILE:
+ handleSavePolicyFile();
+ break;
}
}
}
@@ -2722,42 +2760,6 @@ public class NotificationManagerService extends SystemService {
}
}
- private void updateZenMode() {
- final int mode = Settings.Global.getInt(getContext().getContentResolver(),
- Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
- if (mode != mZenMode) {
- Slog.d(TAG, String.format("updateZenMode: %s -> %s",
- Settings.Global.zenModeToString(mZenMode),
- Settings.Global.zenModeToString(mode)));
- }
- mZenMode = mode;
-
- final String[] exceptionPackages = null; // none (for now)
-
- // call restrictions
- final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
- mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
- muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
- mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
- muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
-
- // alarm restrictions
- final boolean muteAlarms = false; // TODO until we save user config
- mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
- muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
- mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
- muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
-
- // restrict vibrations with no hints
- mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
- (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
- }
-
private void updateCurrentProfilesCache(Context context) {
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
if (userManager != null) {
@@ -2788,19 +2790,4 @@ public class NotificationManagerService extends SystemService {
return mCurrentProfiles.get(userId) != null;
}
}
-
- private boolean isCall(String pkg, Notification n) {
- return CALL_PACKAGES.contains(pkg);
- }
-
- private boolean isAlarm(String pkg, Notification n) {
- return ALARM_PACKAGES.contains(pkg);
- }
-
- private boolean shouldIntercept(String pkg, Notification n) {
- if (mZenMode != Settings.Global.ZEN_MODE_OFF) {
- return !isAlarm(pkg, n);
- }
- return false;
- }
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
new file mode 100644
index 0000000..80f5b5c
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -0,0 +1,312 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.database.ContentObserver;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.provider.Settings.Global;
+import android.service.notification.ZenModeConfig;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * NotificationManagerService helper for functionality related to zen mode.
+ */
+public class ZenModeHelper {
+ private static final String TAG = "ZenModeHelper";
+
+ private static final String ACTION_ENTER_ZEN = "enter_zen";
+ private static final int REQUEST_CODE_ENTER = 100;
+ private static final String ACTION_EXIT_ZEN = "exit_zen";
+ private static final int REQUEST_CODE_EXIT = 101;
+ private static final String EXTRA_TIME = "time";
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final SettingsObserver mSettingsObserver;
+ private final AppOpsManager mAppOps;
+ private final ZenModeConfig mDefaultConfig;
+
+ private Callback mCallback;
+ private int mZenMode;
+ private ZenModeConfig mConfig;
+
+ // temporary, until we update apps to provide metadata
+ private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
+ "com.google.android.dialer",
+ "com.android.phone"
+ ));
+ private static final Set<String> MESSAGE_PACKAGES = new HashSet<String>(Arrays.asList(
+ "com.google.android.talk",
+ "com.android.mms"
+ ));
+
+ public ZenModeHelper(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mDefaultConfig = readDefaultConfig(context.getResources());
+ mConfig = mDefaultConfig;
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mSettingsObserver.observe();
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_ENTER_ZEN);
+ filter.addAction(ACTION_EXIT_ZEN);
+ mContext.registerReceiver(new ZenBroadcastReceiver(), filter);
+ }
+
+ public static ZenModeConfig readDefaultConfig(Resources resources) {
+ XmlResourceParser parser = null;
+ try {
+ parser = resources.getXml(R.xml.default_zen_mode_config);
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ final ZenModeConfig config = ZenModeConfig.readXml(parser);
+ if (config != null) return config;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Error reading default zen mode config from resource", e);
+ } finally {
+ IoUtils.closeQuietly(parser);
+ }
+ return new ZenModeConfig();
+ }
+
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ public boolean shouldIntercept(String pkg, Notification n) {
+ if (mZenMode != Global.ZEN_MODE_OFF) {
+ if (isCall(pkg, n)) {
+ return !mConfig.allowCalls;
+ }
+ if (isMessage(pkg, n)) {
+ return !mConfig.allowMessages;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public void updateZenMode() {
+ final int mode = Global.getInt(mContext.getContentResolver(),
+ Global.ZEN_MODE, Global.ZEN_MODE_OFF);
+ if (mode != mZenMode) {
+ Slog.d(TAG, String.format("updateZenMode: %s -> %s",
+ Global.zenModeToString(mZenMode),
+ Global.zenModeToString(mode)));
+ }
+ mZenMode = mode;
+ final boolean zen = mZenMode != Global.ZEN_MODE_OFF;
+ final String[] exceptionPackages = null; // none (for now)
+
+ // call restrictions
+ final boolean muteCalls = zen && !mConfig.allowCalls;
+ mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
+ muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+ mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
+ muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+
+ // restrict vibrations with no hints
+ mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
+ zen ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ exceptionPackages);
+ }
+
+ public boolean allowDisable(int what, IBinder token, String pkg) {
+ if (isCall(pkg, null)) {
+ return mZenMode == Global.ZEN_MODE_OFF || mConfig.allowCalls;
+ }
+ return true;
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("mZenMode=");
+ pw.println(Global.zenModeToString(mZenMode));
+ pw.print(prefix); pw.print("mConfig="); pw.println(mConfig);
+ pw.print(prefix); pw.print("mDefaultConfig="); pw.println(mDefaultConfig);
+ }
+
+ public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
+ final ZenModeConfig config = ZenModeConfig.readXml(parser);
+ if (config != null) {
+ setConfig(config);
+ }
+ }
+
+ public void writeXml(XmlSerializer out) throws IOException {
+ mConfig.writeXml(out);
+ }
+
+ public ZenModeConfig getConfig() {
+ return mConfig;
+ }
+
+ public boolean setConfig(ZenModeConfig config) {
+ if (config == null || !config.isValid()) return false;
+ if (config.equals(mConfig)) return true;
+ mConfig = config;
+ Slog.d(TAG, "mConfig=" + mConfig);
+ if (mCallback != null) mCallback.onConfigChanged();
+ final String val = Integer.toString(mConfig.hashCode());
+ Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
+ updateAlarms();
+ updateZenMode();
+ return true;
+ }
+
+ private boolean isCall(String pkg, Notification n) {
+ return CALL_PACKAGES.contains(pkg);
+ }
+
+ private boolean isMessage(String pkg, Notification n) {
+ return MESSAGE_PACKAGES.contains(pkg);
+ }
+
+ private void updateAlarms() {
+ updateAlarm(ACTION_ENTER_ZEN, REQUEST_CODE_ENTER,
+ mConfig.sleepStartHour, mConfig.sleepStartMinute);
+ updateAlarm(ACTION_EXIT_ZEN, REQUEST_CODE_EXIT,
+ mConfig.sleepEndHour, mConfig.sleepEndMinute);
+ }
+
+ private void updateAlarm(String action, int requestCode, int hr, int min) {
+ final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ final long now = System.currentTimeMillis();
+ final Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(now);
+ c.set(Calendar.HOUR_OF_DAY, hr);
+ c.set(Calendar.MINUTE, min);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+ if (c.getTimeInMillis() <= now) {
+ c.add(Calendar.DATE, 1);
+ }
+ final long time = c.getTimeInMillis();
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
+ new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT);
+ alarms.cancel(pendingIntent);
+ if (mConfig.sleepMode != null) {
+ Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s",
+ action, ts(time), time - now, ts(now)));
+ alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
+ }
+ }
+
+ private static String ts(long time) {
+ return new Date(time) + " (" + time + ")";
+ }
+
+ public static boolean isWeekend(long time, int offsetDays) {
+ final Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(time);
+ if (offsetDays != 0) {
+ c.add(Calendar.DATE, offsetDays);
+ }
+ final int day = c.get(Calendar.DAY_OF_WEEK);
+ return day == Calendar.SATURDAY || day == Calendar.SUNDAY;
+ }
+
+ private class SettingsObserver extends ContentObserver {
+ private final Uri ZEN_MODE = Global.getUriFor(Global.ZEN_MODE);
+
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void observe() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(ZEN_MODE, false /*notifyForDescendents*/, this);
+ update(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
+ }
+
+ public void update(Uri uri) {
+ if (ZEN_MODE.equals(uri)) {
+ updateZenMode();
+ }
+ }
+ }
+
+ private class ZenBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_ENTER_ZEN.equals(intent.getAction())) {
+ setZenMode(intent, 1, Global.ZEN_MODE_ON);
+ } else if (ACTION_EXIT_ZEN.equals(intent.getAction())) {
+ setZenMode(intent, 0, Global.ZEN_MODE_OFF);
+ }
+ }
+
+ private void setZenMode(Intent intent, int wkendOffsetDays, int zenModeValue) {
+ final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
+ final long now = System.currentTimeMillis();
+ Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
+ intent.getAction(), ts(schTime), ts(now), now - schTime));
+
+ final boolean skip = ZenModeConfig.SLEEP_MODE_WEEKNIGHTS.equals(mConfig.sleepMode) &&
+ isWeekend(schTime, wkendOffsetDays);
+
+ if (skip) {
+ Slog.d(TAG, "Skipping zen mode update for the weekend");
+ } else {
+ Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenModeValue);
+ }
+ updateAlarms();
+ }
+ }
+
+ public interface Callback {
+ void onConfigChanged();
+ }
+}