Third party DND provider APIs.

Bug: 22977552
Change-Id: I8c1bac317e9aa5d9565afe3f0c4e2be8fc34e6a9
diff --git a/api/current.txt b/api/current.txt
index 7040e4d..e3c9366 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4016,6 +4016,23 @@
     field public java.lang.String serviceDetails;
   }
 
+  public class AutomaticZenRule implements android.os.Parcelable {
+    ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean);
+    ctor public AutomaticZenRule(android.os.Parcel);
+    method public int describeContents();
+    method public android.net.Uri getConditionId();
+    method public int getInterruptionFilter();
+    method public java.lang.String getName();
+    method public android.content.ComponentName getOwner();
+    method public boolean isEnabled();
+    method public void setConditionId(android.net.Uri);
+    method public void setEnabled(boolean);
+    method public void setInterruptionFilter(int);
+    method public void setName(java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR;
+  }
+
   public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
     ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
     ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
@@ -5061,15 +5078,20 @@
   }
 
   public class NotificationManager {
+    method public boolean addOrUpdateAutomaticZenRule(android.app.AutomaticZenRule);
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
+    method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
+    method public java.util.List<android.app.AutomaticZenRule> getAutomaticZenRules();
     method public final int getCurrentInterruptionFilter();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
     method public boolean isNotificationPolicyAccessGranted();
     method public void notify(int, android.app.Notification);
     method public void notify(java.lang.String, int, android.app.Notification);
+    method public boolean removeAutomaticZenRule(java.lang.String);
+    method public boolean renameAutomaticZenRule(java.lang.String, java.lang.String);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
@@ -23567,7 +23589,7 @@
     method public static final int myPid();
     method public static final int myTid();
     method public static final int myUid();
-    method public static final android.os.UserHandle myUserHandle();
+    method public static android.os.UserHandle myUserHandle();
     method public static final void sendSignal(int, int);
     method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
     method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
@@ -45361,7 +45383,10 @@
   }
 
   public final class Constructor extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+    method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
+    method public java.lang.annotation.Annotation[] getAnnotations();
+    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<T> getDeclaringClass();
     method public java.lang.Class<?>[] getExceptionTypes();
     method public java.lang.reflect.Type[] getGenericExceptionTypes();
@@ -45371,6 +45396,7 @@
     method public java.lang.annotation.Annotation[][] getParameterAnnotations();
     method public java.lang.Class<?>[] getParameterTypes();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Constructor<T>>[] getTypeParameters();
+    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isSynthetic();
     method public boolean isVarArgs();
     method public T newInstance(java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException;
@@ -45444,7 +45470,10 @@
   }
 
   public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+    method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
+    method public java.lang.annotation.Annotation[] getAnnotations();
+    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<?> getDeclaringClass();
     method public java.lang.Object getDefaultValue();
     method public java.lang.Class<?>[] getExceptionTypes();
@@ -45458,6 +45487,7 @@
     method public java.lang.Class<?> getReturnType();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
     method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
+    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isBridge();
     method public boolean isSynthetic();
     method public boolean isVarArgs();
diff --git a/api/system-current.txt b/api/system-current.txt
index 9b85df4..b1590d8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4127,6 +4127,23 @@
     field public java.lang.String serviceDetails;
   }
 
+  public class AutomaticZenRule implements android.os.Parcelable {
+    ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean);
+    ctor public AutomaticZenRule(android.os.Parcel);
+    method public int describeContents();
+    method public android.net.Uri getConditionId();
+    method public int getInterruptionFilter();
+    method public java.lang.String getName();
+    method public android.content.ComponentName getOwner();
+    method public boolean isEnabled();
+    method public void setConditionId(android.net.Uri);
+    method public void setEnabled(boolean);
+    method public void setInterruptionFilter(int);
+    method public void setName(java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR;
+  }
+
   public class BroadcastOptions {
     method public static android.app.BroadcastOptions makeBasic();
     method public void setTemporaryAppWhitelistDuration(long);
@@ -5178,15 +5195,20 @@
   }
 
   public class NotificationManager {
+    method public boolean addOrUpdateAutomaticZenRule(android.app.AutomaticZenRule);
     method public void cancel(int);
     method public void cancel(java.lang.String, int);
     method public void cancelAll();
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
+    method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
+    method public java.util.List<android.app.AutomaticZenRule> getAutomaticZenRules();
     method public final int getCurrentInterruptionFilter();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
     method public boolean isNotificationPolicyAccessGranted();
     method public void notify(int, android.app.Notification);
     method public void notify(java.lang.String, int, android.app.Notification);
+    method public boolean removeAutomaticZenRule(java.lang.String);
+    method public boolean renameAutomaticZenRule(java.lang.String, java.lang.String);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
@@ -25519,7 +25541,7 @@
     method public static final int myPid();
     method public static final int myTid();
     method public static final int myUid();
-    method public static final android.os.UserHandle myUserHandle();
+    method public static android.os.UserHandle myUserHandle();
     method public static final void sendSignal(int, int);
     method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
     method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
@@ -47970,7 +47992,10 @@
   }
 
   public final class Constructor extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+    method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
+    method public java.lang.annotation.Annotation[] getAnnotations();
+    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<T> getDeclaringClass();
     method public java.lang.Class<?>[] getExceptionTypes();
     method public java.lang.reflect.Type[] getGenericExceptionTypes();
@@ -47980,6 +48005,7 @@
     method public java.lang.annotation.Annotation[][] getParameterAnnotations();
     method public java.lang.Class<?>[] getParameterTypes();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Constructor<T>>[] getTypeParameters();
+    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isSynthetic();
     method public boolean isVarArgs();
     method public T newInstance(java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException;
@@ -48053,7 +48079,10 @@
   }
 
   public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+    method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
+    method public java.lang.annotation.Annotation[] getAnnotations();
+    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<?> getDeclaringClass();
     method public java.lang.Object getDefaultValue();
     method public java.lang.Class<?>[] getExceptionTypes();
@@ -48067,6 +48096,7 @@
     method public java.lang.Class<?> getReturnType();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
     method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
+    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isBridge();
     method public boolean isSynthetic();
     method public boolean isVarArgs();
diff --git a/core/java/android/app/AutomaticZenRule.aidl b/core/java/android/app/AutomaticZenRule.aidl
new file mode 100644
index 0000000..feb21d6
--- /dev/null
+++ b/core/java/android/app/AutomaticZenRule.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2015, 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 android.app;
+
+parcelable AutomaticZenRule;
\ No newline at end of file
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
new file mode 100644
index 0000000..fea5624
--- /dev/null
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -0,0 +1,190 @@
+/**
+ * Copyright (c) 2015, 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 android.app;
+
+import android.content.ComponentName;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Rule instance information for zen mode.
+ */
+public class AutomaticZenRule implements Parcelable {
+
+    private boolean enabled = false;
+    private String name;
+    private int interruptionFilter;
+    private Uri conditionId;
+    private ComponentName owner;
+
+    /**
+     * Creates an automatic zen rule.
+     *
+     * @param name The name of the rule.
+     * @param owner The Condition Provider service that owns this rule.
+     * @param conditionId A representation of the state that should cause the Condition Provider
+     *                    service to apply the interruption filter.
+     * @param interruptionFilter The interruption filter defines which notifications are allowed to
+     *                           interrupt the user (e.g. via sound &amp; vibration) while this rule
+     *                           is active.
+     * @param enabled Whether the rule is enabled.
+     */
+    public AutomaticZenRule(String name, ComponentName owner, Uri conditionId,
+            int interruptionFilter, boolean enabled) {
+        this.name = name;
+        this.owner = owner;
+        this.conditionId = conditionId;
+        this.interruptionFilter = interruptionFilter;
+        this.enabled = enabled;
+    }
+
+    public AutomaticZenRule(Parcel source) {
+        enabled = source.readInt() == 1;
+        if (source.readInt() == 1) {
+            name = source.readString();
+        }
+        interruptionFilter = source.readInt();
+        conditionId = source.readParcelable(null);
+        owner = source.readParcelable(null);
+    }
+
+    /**
+     * Returns the {@link ComponentName} of the condition provider service that owns this rule.
+     */
+    public ComponentName getOwner() {
+        return owner;
+    }
+
+    /**
+     * Returns the representation of the state that causes this rule to become active.
+     */
+    public Uri getConditionId() {
+        return conditionId;
+    }
+
+    /**
+     * Returns the interruption filter that is applied when this rule is active.
+     */
+    public int getInterruptionFilter() {
+        return interruptionFilter;
+    }
+
+    /**
+     * Returns the name of this rule.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns whether this rule is enabled.
+     */
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    /**
+     * Sets the representation of the state that causes this rule to become active.
+     */
+    public void setConditionId(Uri conditionId) {
+        this.conditionId = conditionId;
+    }
+
+    /**
+     * Sets the interruption filter that is applied when this rule is active.
+     * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants in NotificationManager.
+     */
+    public void setInterruptionFilter(int interruptionFilter) {
+        this.interruptionFilter = interruptionFilter;
+    }
+
+    /**
+     * Sets the name of this rule.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Enables this rule.
+     */
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(enabled ? 1 : 0);
+        if (name != null) {
+            dest.writeInt(1);
+            dest.writeString(name);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(interruptionFilter);
+        dest.writeParcelable(conditionId, 0);
+        dest.writeParcelable(owner, 0);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder(AutomaticZenRule.class.getSimpleName()).append('[')
+                .append("enabled=").append(enabled)
+                .append(",name=").append(name)
+                .append(",interruptionFilter=").append(interruptionFilter)
+                .append(",conditionId=").append(conditionId)
+                .append(",owner=").append(owner)
+                .append(']').toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof AutomaticZenRule)) return false;
+        if (o == this) return true;
+        final AutomaticZenRule other = (AutomaticZenRule) o;
+        return other.enabled == enabled
+                && Objects.equals(other.name, name)
+                && other.interruptionFilter == interruptionFilter
+                && Objects.equals(other.conditionId, conditionId)
+                && Objects.equals(other.owner, owner);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(enabled, name, interruptionFilter, conditionId, owner);
+    }
+
+    public static final Parcelable.Creator<AutomaticZenRule> CREATOR
+            = new Parcelable.Creator<AutomaticZenRule>() {
+        @Override
+        public AutomaticZenRule createFromParcel(Parcel source) {
+            return new AutomaticZenRule(source);
+        }
+        @Override
+        public AutomaticZenRule[] newArray(int size) {
+            return new AutomaticZenRule[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index f78fb47..920fbe9 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -30,6 +30,7 @@
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
 import android.service.notification.StatusBarNotification;
+import android.app.AutomaticZenRule;
 import android.service.notification.ZenModeConfig;
 
 /** {@hide} */
@@ -92,6 +93,11 @@
     String[] getPackagesRequestingNotificationPolicyAccess();
     boolean isNotificationPolicyAccessGrantedForPackage(String pkg);
     void setNotificationPolicyAccessGranted(String pkg, boolean granted);
+    AutomaticZenRule getAutomaticZenRule(String name);
+    List<AutomaticZenRule> getAutomaticZenRules();
+    boolean addOrUpdateAutomaticZenRule(in AutomaticZenRule automaticZenRule);
+    boolean renameAutomaticZenRule(String oldName, String newName);
+    boolean removeAutomaticZenRule(String name);
 
     byte[] getBackupPayload(int user);
     void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 605c006..cbf198b 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -420,6 +420,106 @@
     }
 
     /**
+     * Returns AutomaticZenRules owned by the caller.
+     *
+     * <p>
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
+     */
+    public List<AutomaticZenRule> getAutomaticZenRules() {
+        INotificationManager service = getService();
+        try {
+            return service.getAutomaticZenRules();
+        } catch (RemoteException e) {
+        }
+        return null;
+    }
+
+    /**
+     * Returns the AutomaticZenRule with the given name, if it exists and the caller has access.
+     *
+     * <p>
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
+     *
+     * <p>
+     * Returns null if there are no zen rules that match the given name, or if the calling package
+     * doesn't own the matching rule. See {@link AutomaticZenRule#getOwner}.
+     */
+    public AutomaticZenRule getAutomaticZenRule(String name) {
+        INotificationManager service = getService();
+        try {
+            return service.getAutomaticZenRule(name);
+        } catch (RemoteException e) {
+        }
+        return null;
+    }
+
+    /**
+     * Creates or updates the given zen rule.
+     *
+     * <p>
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
+     *
+     * <p>
+     * Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
+     * @param automaticZenRule the rule to create or update.
+     * @return Whether the rule was successfully created or updated.
+     */
+    public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule) {
+        INotificationManager service = getService();
+        try {
+            return service.addOrUpdateAutomaticZenRule(automaticZenRule);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
+     * Renames a zen rule.
+     *
+     * <p>
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
+     *
+     * <p>
+     * Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
+     * @param oldName The name of the rule to update.
+     * @param newName The new name for the rule.
+     * @return Whether the rule was successfully updated.
+     */
+    public boolean renameAutomaticZenRule(String oldName, String newName) {
+        INotificationManager service = getService();
+        try {
+            return service.renameAutomaticZenRule(oldName, newName);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
+     * Deletes the automatic zen rule with the given name.
+     *
+     * <p>
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
+     *
+     * <p>
+     * Callers can only delete rules that they own. See {@link AutomaticZenRule#getOwner}.
+     * @param name the name of the rule to delete.
+     * @return Whether the rule was successfully deleted.
+     */
+    public boolean removeAutomaticZenRule(String name) {
+        INotificationManager service = getService();
+        try {
+            return service.removeAutomaticZenRule(name);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
      * Checks the ability to read/modify notification policy for the calling package.
      *
      * <p>
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 6310570..4de903e 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -755,6 +755,10 @@
         return rt;
     }
 
+    public static ComponentName getScheduleConditionProvider() {
+        return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider");
+    }
+
     public static class ScheduleInfo {
         public int[] days;
         public int startHour;
@@ -827,6 +831,10 @@
         return rt;
     }
 
+    public static ComponentName getEventConditionProvider() {
+        return new ComponentName(SYSTEM_AUTHORITY, "EventConditionProvider");
+    }
+
     public static class EventInfo {
         public static final int REPLY_ANY_EXCEPT_NO = 0;
         public static final int REPLY_YES_OR_MAYBE = 1;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f6c9374..7b15aad 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -27,6 +27,7 @@
 import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.AutomaticZenRule;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
 import android.app.ITransientNotification;
@@ -101,6 +102,7 @@
 import com.android.internal.R;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -1172,8 +1174,8 @@
             // Don't allow client applications to cancel foreground service notis.
             cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
                     Binder.getCallingUid() == Process.SYSTEM_UID
-                    ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId, REASON_NOMAN_CANCEL,
-                    null);
+                            ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId,
+                    REASON_NOMAN_CANCEL, null);
         }
 
         @Override
@@ -1594,6 +1596,50 @@
         }
 
         @Override
+        public List<AutomaticZenRule> getAutomaticZenRules() throws RemoteException {
+            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
+            return mZenModeHelper.getAutomaticZenRules();
+        }
+
+        @Override
+        public AutomaticZenRule getAutomaticZenRule(String name) throws RemoteException {
+            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
+            return mZenModeHelper.getAutomaticZenRule(name);
+        }
+
+        @Override
+        public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule)
+                throws RemoteException {
+            Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
+            Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
+            Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
+            Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
+            enforcePolicyAccess(Binder.getCallingUid(), "addOrUpdateZenModeRule");
+
+            return mZenModeHelper.addOrUpdateAutomaticZenRule(automaticZenRule,
+                    "addOrUpdateAutomaticZenRule");
+        }
+
+        @Override
+        public boolean renameAutomaticZenRule(String oldName, String newName) {
+            Preconditions.checkNotNull(oldName, "oldName is null");
+            Preconditions.checkNotNull(newName, "newName is null");
+            enforcePolicyAccess(Binder.getCallingUid(), "renameAutomaticZenRule");
+
+            return mZenModeHelper.renameAutomaticZenRule(
+                    oldName, newName, "renameAutomaticZenRule");
+        }
+
+        @Override
+        public boolean removeAutomaticZenRule(String name) throws RemoteException {
+            Preconditions.checkNotNull(name, "Name is null");
+            // Verify that they can modify zen rules.
+            enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
+
+            return mZenModeHelper.removeAutomaticZenRule(name, "removeAutomaticZenRule");
+        }
+
+        @Override
         public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
             enforcePolicyAccess(pkg, "setInterruptionFilter");
             final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
@@ -1641,6 +1687,25 @@
                     message);
         }
 
+        private void enforcePolicyAccess(int uid, String method) {
+            if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
+                    android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
+                return;
+            }
+            boolean accessAllowed = false;
+            String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
+            final int packageCount = packages.length;
+            for (int i = 0; i < packageCount; i++) {
+                if (checkPolicyAccess(packages[i])) {
+                    accessAllowed = true;
+                }
+            }
+            if (!accessAllowed) {
+                Slog.w(TAG, "Notification policy access denied calling " + method);
+                throw new SecurityException("Notification policy access denied");
+            }
+        }
+
         private void enforcePolicyAccess(String pkg, String method) {
             if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
                     android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 9131e5e..4d41e3a 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,11 +21,13 @@
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 
 import android.app.AppOpsManager;
+import android.app.AutomaticZenRule;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.database.ContentObserver;
@@ -34,6 +36,7 @@
 import android.media.AudioSystem;
 import android.media.VolumePolicy;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -62,6 +65,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -196,6 +200,121 @@
         return mZenMode;
     }
 
+    public List<AutomaticZenRule> getAutomaticZenRules() {
+        List<AutomaticZenRule> rules = new ArrayList<>();
+        if (mConfig == null) return rules;
+        for(ZenRule rule : mConfig.automaticRules.values()) {
+            if (canManageAutomaticZenRule(rule)) {
+                rules.add(createAutomaticZenRule(rule));
+            }
+        }
+        return rules;
+    }
+
+    public AutomaticZenRule getAutomaticZenRule(String name) {
+        if (mConfig == null) return null;
+        for(ZenRule rule : mConfig.automaticRules.values()) {
+            if (canManageAutomaticZenRule(rule) && rule.name.equals(name)) {
+                return createAutomaticZenRule(rule);
+            }
+        }
+        return null;
+    }
+
+    public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
+        if (mConfig == null) return false;
+        if (DEBUG) {
+            Log.d(TAG, "addOrUpdateAutomaticZenRule zenRule=" + automaticZenRule
+                    + " reason=" + reason);
+        }
+        final ZenModeConfig newConfig = mConfig.copy();
+        String ruleId = findMatchingRuleId(newConfig, automaticZenRule.getName());
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        if (ruleId == null) {
+            ruleId = newConfig.newRuleId();
+            rule.name = automaticZenRule.getName();
+            rule.component = automaticZenRule.getOwner();
+        } else {
+            rule = newConfig.automaticRules.get(ruleId);
+            if (!canManageAutomaticZenRule(rule)) {
+                throw new SecurityException(
+                        "Cannot update rules not owned by your condition provider");
+            }
+        }
+        if (rule.enabled != automaticZenRule.isEnabled()) {
+            rule.snoozing = false;
+        }
+        rule.condition = null;
+        rule.conditionId = automaticZenRule.getConditionId();
+        rule.enabled = automaticZenRule.isEnabled();
+        rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
+                automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+        newConfig.automaticRules.put(ruleId, rule);
+        return setConfig(newConfig, reason, true);
+    }
+
+    public boolean renameAutomaticZenRule(String oldName, String newName, String reason) {
+        if (mConfig == null) return false;
+        if (DEBUG) {
+            Log.d(TAG, "renameAutomaticZenRule oldName=" + oldName + "  newName=" + newName
+                    + " reason=" + reason);
+        }
+        final ZenModeConfig newConfig = mConfig.copy();
+        String ruleId = findMatchingRuleId(newConfig, oldName);
+        if (ruleId == null) {
+            return false;
+        } else {
+            ZenRule rule = newConfig.automaticRules.get(ruleId);
+            if (!canManageAutomaticZenRule(rule)) {
+                throw new SecurityException(
+                        "Cannot update rules not owned by your condition provider");
+            }
+            rule.name = newName;
+            return setConfig(newConfig, reason, true);
+        }
+    }
+
+    public boolean removeAutomaticZenRule(String name, String reason) {
+        if (mConfig == null) return false;
+        final ZenModeConfig newConfig = mConfig.copy();
+        String ruleId = findMatchingRuleId(newConfig, name);
+        if (ruleId != null) {
+            ZenRule rule = newConfig.automaticRules.get(ruleId);
+            if (canManageAutomaticZenRule(rule)) {
+                newConfig.automaticRules.remove(ruleId);
+                if (DEBUG) Log.d(TAG, "removeZenRule zenRule=" + name + " reason=" + reason);
+            } else {
+                throw new SecurityException(
+                        "Cannot delete rules not owned by your condition provider");
+            }
+        }
+        return setConfig(newConfig, reason, true);
+    }
+
+    public boolean canManageAutomaticZenRule(ZenRule rule) {
+        if (mContext.checkCallingPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS)
+                == PackageManager.PERMISSION_GRANTED) {
+            return true;
+        } else {
+            String[] packages = mContext.getPackageManager().getPackagesForUid(
+                    Binder.getCallingUid());
+            if (packages != null) {
+                final int packageCount = packages.length;
+                for (int i = 0; i < packageCount; i++) {
+                    if (packages[i].equals(rule.component.getPackageName())) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    private AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
+        return new AutomaticZenRule(rule.name, rule.component, rule.conditionId,
+                NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled);
+    }
+
     public void setManualZenMode(int zenMode, Uri conditionId, String reason) {
         setManualZenMode(zenMode, conditionId, reason, true /*setRingerMode*/);
     }
@@ -225,6 +344,15 @@
         setConfig(newConfig, reason, setRingerMode);
     }
 
+    private String findMatchingRuleId(ZenModeConfig config, String ruleName) {
+        for (String ruleId : config.automaticRules.keySet()) {
+            if (config.automaticRules.get(ruleId).name.equals(ruleName)) {
+                return ruleId;
+            }
+        }
+        return null;
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mZenMode=");
         pw.println(Global.zenModeToString(mZenMode));
@@ -272,7 +400,7 @@
                 }
                 config.manualRule = null;  // don't restore the manual rule
                 if (config.automaticRules != null) {
-                    for (ZenModeConfig.ZenRule automaticRule : config.automaticRules.values()) {
+                    for (ZenRule automaticRule : config.automaticRules.values()) {
                         // don't restore transient state from restored automatic rules
                         automaticRule.snoozing = false;
                         automaticRule.condition = null;
@@ -319,36 +447,41 @@
     }
 
     private boolean setConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
-        if (config == null || !config.isValid()) {
-            Log.w(TAG, "Invalid config in setConfig; " + config);
-            return false;
-        }
-        if (config.user != mUser) {
-            // simply store away for background users
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (config == null || !config.isValid()) {
+                Log.w(TAG, "Invalid config in setConfig; " + config);
+                return false;
+            }
+            if (config.user != mUser) {
+                // simply store away for background users
+                mConfigs.put(config.user, config);
+                if (DEBUG) Log.d(TAG, "setConfig: store config for user " + config.user);
+                return true;
+            }
+            mConditions.evaluateConfig(config, false /*processSubscriptions*/);  // may modify config
             mConfigs.put(config.user, config);
-            if (DEBUG) Log.d(TAG, "setConfig: store config for user " + config.user);
+            if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
+            ZenLog.traceConfig(reason, mConfig, config);
+            final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
+                    getNotificationPolicy(config));
+            mConfig = config;
+            if (config.equals(mConfig)) {
+                dispatchOnConfigChanged();
+            }
+            if (policyChanged){
+                dispatchOnPolicyChanged();
+            }
+            final String val = Integer.toString(mConfig.hashCode());
+            Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
+            if (!evaluateZenMode(reason, setRingerMode)) {
+                applyRestrictions();  // evaluateZenMode will also apply restrictions if changed
+            }
+            mConditions.evaluateConfig(config, true /*processSubscriptions*/);
             return true;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        mConditions.evaluateConfig(config, false /*processSubscriptions*/);  // may modify config
-        mConfigs.put(config.user, config);
-        if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
-        ZenLog.traceConfig(reason, mConfig, config);
-        final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
-                getNotificationPolicy(config));
-        mConfig = config;
-        if (config.equals(mConfig)) {
-            dispatchOnConfigChanged();
-        }
-        if (policyChanged){
-            dispatchOnPolicyChanged();
-        }
-        final String val = Integer.toString(mConfig.hashCode());
-        Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
-        if (!evaluateZenMode(reason, setRingerMode)) {
-            applyRestrictions();  // evaluateZenMode will also apply restrictions if changed
-        }
-        mConditions.evaluateConfig(config, true /*processSubscriptions*/);
-        return true;
     }
 
     private int getZenModeSetting() {
@@ -506,6 +639,7 @@
                 .getString(R.string.zen_mode_default_weeknights_name);
         rule1.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
         rule1.zenMode = Global.ZEN_MODE_ALARMS;
+        rule1.component = ScheduleConditionProvider.COMPONENT;
         config.automaticRules.put(config.newRuleId(), rule1);
 
         final ScheduleInfo weekends = new ScheduleInfo();
@@ -519,6 +653,7 @@
                 .getString(R.string.zen_mode_default_weekends_name);
         rule2.conditionId = ZenModeConfig.toScheduleConditionId(weekends);
         rule2.zenMode = Global.ZEN_MODE_ALARMS;
+        rule2.component = ScheduleConditionProvider.COMPONENT;
         config.automaticRules.put(config.newRuleId(), rule2);
     }
 
@@ -533,6 +668,7 @@
         rule.name = mContext.getResources().getString(R.string.zen_mode_default_events_name);
         rule.conditionId = ZenModeConfig.toEventConditionId(events);
         rule.zenMode = Global.ZEN_MODE_ALARMS;
+        rule.component = EventConditionProvider.COMPONENT;
         config.automaticRules.put(config.newRuleId(), rule);
     }
 
@@ -573,6 +709,7 @@
                 rule.conditionId = ZenModeConfig.toScheduleConditionId(schedule);
                 rule.zenMode = v1.sleepNone ? Global.ZEN_MODE_NO_INTERRUPTIONS
                         : Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+                rule.component = ScheduleConditionProvider.COMPONENT;
                 rt.automaticRules.put(rt.newRuleId(), rule);
             } else {
                 Log.i(TAG, "No existing V1 downtime found, generating default schedules");