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 & 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");