diff options
-rw-r--r-- | Android.mk | 2 | ||||
-rw-r--r-- | api/current.txt | 37 | ||||
-rw-r--r-- | api/system-current.txt | 37 | ||||
-rw-r--r-- | core/java/android/app/INotificationManager.aidl | 7 | ||||
-rw-r--r-- | core/java/android/app/INotificationManagerCallback.aidl | 24 | ||||
-rw-r--r-- | core/java/android/app/NotificationManager.aidl | 20 | ||||
-rw-r--r-- | core/java/android/app/NotificationManager.java | 303 | ||||
-rw-r--r-- | core/java/android/service/notification/NotificationListenerService.java | 17 | ||||
-rw-r--r-- | core/java/android/service/notification/ZenModeConfig.java | 55 | ||||
-rw-r--r-- | services/core/java/com/android/server/notification/NotificationManagerService.java | 110 | ||||
-rw-r--r-- | services/core/java/com/android/server/notification/ZenModeHelper.java | 29 |
11 files changed, 628 insertions, 13 deletions
@@ -74,6 +74,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ core/java/android/app/INotificationManager.aidl \ + core/java/android/app/INotificationManagerCallback.aidl \ core/java/android/app/IProcessObserver.aidl \ core/java/android/app/ISearchManager.aidl \ core/java/android/app/ISearchManagerCallback.aidl \ @@ -555,6 +556,7 @@ aidl_files := \ frameworks/base/core/java/android/app/AssistStructure.aidl \ frameworks/base/core/java/android/app/AssistContent.aidl \ frameworks/base/core/java/android/app/Notification.aidl \ + frameworks/base/core/java/android/app/NotificationManager.aidl \ frameworks/base/core/java/android/app/WallpaperInfo.aidl \ frameworks/base/core/java/android/app/AppOpsManager.aidl \ frameworks/base/core/java/android/app/ActivityManager.aidl \ diff --git a/api/current.txt b/api/current.txt index fdeda0b..f2841e3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5107,8 +5107,44 @@ package android.app { method public void cancel(int); method public void cancel(java.lang.String, int); method public void cancelAll(); + method public android.app.NotificationManager.Policy getNotificationPolicy(android.app.NotificationManager.Policy.Token); + method public boolean isNotificationPolicyTokenValid(android.app.NotificationManager.Policy.Token); method public void notify(int, android.app.Notification); method public void notify(java.lang.String, int, android.app.Notification); + method public void requestNotificationPolicyToken(android.app.NotificationManager.Policy.Token.RequestCallback, android.os.Handler); + method public void setNotificationPolicy(android.app.NotificationManager.Policy.Token, android.app.NotificationManager.Policy); + field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED"; + } + + public static class NotificationManager.Policy implements android.os.Parcelable { + ctor public NotificationManager.Policy(int, int); + method public int describeContents(); + method public static java.lang.String priorityCategoriesToString(int); + method public static java.lang.String prioritySendersToString(int); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy> CREATOR; + field public static final int PRIORITY_CATEGORY_CALLS = 8; // 0x8 + field public static final int PRIORITY_CATEGORY_EVENTS = 2; // 0x2 + field public static final int PRIORITY_CATEGORY_MESSAGES = 4; // 0x4 + field public static final int PRIORITY_CATEGORY_REMINDERS = 1; // 0x1 + field public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 16; // 0x10 + field public static final int PRIORITY_SENDERS_ANY = 0; // 0x0 + field public static final int PRIORITY_SENDERS_CONTACTS = 1; // 0x1 + field public static final int PRIORITY_SENDERS_STARRED = 2; // 0x2 + field public final int priorityCategories; + field public final int prioritySenders; + } + + public static class NotificationManager.Policy.Token implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy.Token> CREATOR; + } + + public static abstract class NotificationManager.Policy.Token.RequestCallback { + ctor public NotificationManager.Policy.Token.RequestCallback(); + method public abstract void onTokenDenied(); + method public abstract void onTokenGranted(android.app.NotificationManager.Policy.Token); } public final class PendingIntent implements android.os.Parcelable { @@ -28541,6 +28577,7 @@ package android.service.notification { method public final int getCurrentInterruptionFilter(); method public final int getCurrentListenerHints(); method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking(); + method public final android.app.NotificationManager.Policy.Token getNotificationPolicyToken(); method public android.os.IBinder onBind(android.content.Intent); method public void onInterruptionFilterChanged(int); method public void onListenerConnected(); diff --git a/api/system-current.txt b/api/system-current.txt index deaf916..bd2b606 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5198,8 +5198,44 @@ package android.app { method public void cancel(int); method public void cancel(java.lang.String, int); method public void cancelAll(); + method public android.app.NotificationManager.Policy getNotificationPolicy(android.app.NotificationManager.Policy.Token); + method public boolean isNotificationPolicyTokenValid(android.app.NotificationManager.Policy.Token); method public void notify(int, android.app.Notification); method public void notify(java.lang.String, int, android.app.Notification); + method public void requestNotificationPolicyToken(android.app.NotificationManager.Policy.Token.RequestCallback, android.os.Handler); + method public void setNotificationPolicy(android.app.NotificationManager.Policy.Token, android.app.NotificationManager.Policy); + field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED"; + } + + public static class NotificationManager.Policy implements android.os.Parcelable { + ctor public NotificationManager.Policy(int, int); + method public int describeContents(); + method public static java.lang.String priorityCategoriesToString(int); + method public static java.lang.String prioritySendersToString(int); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy> CREATOR; + field public static final int PRIORITY_CATEGORY_CALLS = 8; // 0x8 + field public static final int PRIORITY_CATEGORY_EVENTS = 2; // 0x2 + field public static final int PRIORITY_CATEGORY_MESSAGES = 4; // 0x4 + field public static final int PRIORITY_CATEGORY_REMINDERS = 1; // 0x1 + field public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 16; // 0x10 + field public static final int PRIORITY_SENDERS_ANY = 0; // 0x0 + field public static final int PRIORITY_SENDERS_CONTACTS = 1; // 0x1 + field public static final int PRIORITY_SENDERS_STARRED = 2; // 0x2 + field public final int priorityCategories; + field public final int prioritySenders; + } + + public static class NotificationManager.Policy.Token implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy.Token> CREATOR; + } + + public static abstract class NotificationManager.Policy.Token.RequestCallback { + ctor public NotificationManager.Policy.Token.RequestCallback(); + method public abstract void onTokenDenied(); + method public abstract void onTokenGranted(android.app.NotificationManager.Policy.Token); } public final class PendingIntent implements android.os.Parcelable { @@ -30580,6 +30616,7 @@ package android.service.notification { method public final int getCurrentInterruptionFilter(); method public final int getCurrentListenerHints(); method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking(); + method public final android.app.NotificationManager.Policy.Token getNotificationPolicyToken(); method public android.os.IBinder onBind(android.content.Intent); method public void onInterruptionFilterChanged(int); method public void onListenerConnected(); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index e2230da..913159a 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -17,8 +17,10 @@ package android.app; +import android.app.INotificationManagerCallback; import android.app.ITransientNotification; import android.app.Notification; +import android.app.NotificationManager; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ParceledListSlice; @@ -71,6 +73,7 @@ interface INotificationManager void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter); int getInterruptionFilterFromListener(in INotificationListener token); void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim); + NotificationManager.Policy.Token getPolicyTokenFromListener(in INotificationListener listener); ComponentName getEffectsSuppressor(); boolean matchesCallFilter(in Bundle extras); @@ -82,4 +85,8 @@ interface INotificationManager oneway void setZenMode(int mode, in Uri conditionId, String reason); oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions); oneway void requestZenModeConditions(in IConditionListener callback, int relevance); + oneway void requestNotificationPolicyToken(String pkg, in INotificationManagerCallback callback); + boolean isNotificationPolicyTokenValid(String pkg, in NotificationManager.Policy.Token token); + NotificationManager.Policy getNotificationPolicy(in NotificationManager.Policy.Token token); + void setNotificationPolicy(in NotificationManager.Policy.Token token, in NotificationManager.Policy policy); } diff --git a/core/java/android/app/INotificationManagerCallback.aidl b/core/java/android/app/INotificationManagerCallback.aidl new file mode 100644 index 0000000..b9414ca --- /dev/null +++ b/core/java/android/app/INotificationManagerCallback.aidl @@ -0,0 +1,24 @@ +/** + * 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.app.NotificationManager; + +/** @hide */ +oneway interface INotificationManagerCallback { + void onPolicyToken(in NotificationManager.Policy.Token token); +} diff --git a/core/java/android/app/NotificationManager.aidl b/core/java/android/app/NotificationManager.aidl new file mode 100644 index 0000000..8380b8d --- /dev/null +++ b/core/java/android/app/NotificationManager.aidl @@ -0,0 +1,20 @@ +/** + * 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 NotificationManager.Policy; +parcelable NotificationManager.Policy.Token;
\ No newline at end of file diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index fa61e18..7133dce 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -16,14 +16,19 @@ package android.app; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SdkConstant; import android.app.Notification.Builder; +import android.app.NotificationManager.Policy.Token; import android.content.ComponentName; import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; @@ -33,6 +38,8 @@ import android.service.notification.IConditionListener; import android.service.notification.ZenModeConfig; import android.util.Log; +import java.util.Objects; + /** * Class to notify the user of events that happen. This is how you tell * the user that something has happened in the background. {@more} @@ -89,6 +96,14 @@ public class NotificationManager public static final String ACTION_EFFECTS_SUPPRESSOR_CHANGED = "android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED"; + /** + * Intent that is broadcast when the state of getNotificationPolicy() changes. + * This broadcast is only sent to registered receivers. + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_NOTIFICATION_POLICY_CHANGED + = "android.app.action.NOTIFICATION_POLICY_CHANGED"; + private static INotificationManager sService; /** @hide */ @@ -338,5 +353,293 @@ public class NotificationManager return null; } + /** + * Requests a notification policy token for the calling package. + * + * @param callback required, used to receive the granted token or the deny signal. + * @param handler The handler used when receiving the result. + * If null, the current thread is used. + */ + public void requestNotificationPolicyToken(@NonNull final Policy.Token.RequestCallback callback, + @Nullable Handler handler) { + checkRequired("callback", callback); + final Handler h = handler != null ? handler : new Handler(); + INotificationManager service = getService(); + try { + service.requestNotificationPolicyToken(mContext.getOpPackageName(), + new INotificationManagerCallback.Stub() { + @Override + public void onPolicyToken(final Token token) throws RemoteException { + h.post(new Runnable() { + @Override + public void run() { + if (token != null) { + callback.onTokenGranted(token); + } else { + callback.onTokenDenied(); + } + } + }); + } + }); + } catch (RemoteException e) { + } + } + + /** + * Checks a given notification policy token. + * + * Returns true if the token is still valid for managing policy. + */ + public boolean isNotificationPolicyTokenValid(@NonNull Policy.Token token) { + if (token == null) return false; + INotificationManager service = getService(); + try { + return service.isNotificationPolicyTokenValid(mContext.getOpPackageName(), token); + } catch (RemoteException e) { + } + return false; + } + + /** + * Gets the current notification policy. + * + * @param token A valid notification policy token is required to access the current policy. + */ + public Policy getNotificationPolicy(@NonNull Policy.Token token) { + checkRequired("token", token); + INotificationManager service = getService(); + try { + return service.getNotificationPolicy(token); + } catch (RemoteException e) { + } + return null; + } + + /** + * Sets the current notification policy. + * + * @param token A valid notification policy token is required to modify the current policy. + * @param policy The new desired policy. + */ + public void setNotificationPolicy(@NonNull Policy.Token token, @NonNull Policy policy) { + checkRequired("token", token); + checkRequired("policy", policy); + INotificationManager service = getService(); + try { + service.setNotificationPolicy(token, policy); + } catch (RemoteException e) { + } + } + private Context mContext; + + private static void checkRequired(String name, Object value) { + if (value == null) { + throw new IllegalArgumentException(name + " is required"); + } + } + + /** + * Notification policy configuration. Represents user-preferences for notification + * filtering and prioritization. + */ + public static class Policy implements android.os.Parcelable { + /** Reminder notifications are prioritized. */ + public static final int PRIORITY_CATEGORY_REMINDERS = 1 << 0; + /** Event notifications are prioritized. */ + public static final int PRIORITY_CATEGORY_EVENTS = 1 << 1; + /** Message notifications are prioritized. */ + public static final int PRIORITY_CATEGORY_MESSAGES = 1 << 2; + /** Calls are prioritized. */ + public static final int PRIORITY_CATEGORY_CALLS = 1 << 3; + /** Calls from repeat callers are prioritized. */ + public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 4; + + private static final int[] ALL_PRIORITY_CATEGORIES = { + PRIORITY_CATEGORY_REMINDERS, + PRIORITY_CATEGORY_EVENTS, + PRIORITY_CATEGORY_MESSAGES, + PRIORITY_CATEGORY_CALLS, + PRIORITY_CATEGORY_REPEAT_CALLERS, + }; + + /** Any sender is prioritized. */ + public static final int PRIORITY_SENDERS_ANY = 0; + /** Saved contacts are prioritized. */ + public static final int PRIORITY_SENDERS_CONTACTS = 1; + /** Only starred contacts are prioritized. */ + public static final int PRIORITY_SENDERS_STARRED = 2; + + /** Notification categories to prioritize. Bitmask of PRIORITY_CATEGORY_* constants. */ + public final int priorityCategories; + + /** Notification senders to prioritize. One of: + * PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */ + public final int prioritySenders; + + public Policy(int priorityCategories, int prioritySenders) { + this.priorityCategories = priorityCategories; + this.prioritySenders = prioritySenders; + } + + /** @hide */ + public Policy(Parcel source) { + this(source.readInt(), source.readInt()); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(priorityCategories); + dest.writeInt(prioritySenders); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(priorityCategories, prioritySenders); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Policy)) return false; + if (o == this) return true; + final Policy other = (Policy) o; + return other.priorityCategories == priorityCategories + && other.prioritySenders == prioritySenders; + } + + @Override + public String toString() { + return "NotificationManager.Policy[" + + "priorityCategories=" + priorityCategoriesToString(priorityCategories) + + ",prioritySenders=" + prioritySendersToString(prioritySenders) + + "]"; + } + + public static String priorityCategoriesToString(int priorityCategories) { + if (priorityCategories == 0) return ""; + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < ALL_PRIORITY_CATEGORIES.length; i++) { + final int priorityCategory = ALL_PRIORITY_CATEGORIES[i]; + if ((priorityCategories & priorityCategory) != 0) { + if (sb.length() > 0) sb.append(','); + sb.append(priorityCategoryToString(priorityCategory)); + } + priorityCategories &= ~priorityCategory; + } + if (priorityCategories != 0) { + if (sb.length() > 0) sb.append(','); + sb.append("PRIORITY_CATEGORY_UNKNOWN_").append(priorityCategories); + } + return sb.toString(); + } + + private static String priorityCategoryToString(int priorityCategory) { + switch (priorityCategory) { + case PRIORITY_CATEGORY_REMINDERS: return "PRIORITY_CATEGORY_REMINDERS"; + case PRIORITY_CATEGORY_EVENTS: return "PRIORITY_CATEGORY_EVENTS"; + case PRIORITY_CATEGORY_MESSAGES: return "PRIORITY_CATEGORY_MESSAGES"; + case PRIORITY_CATEGORY_CALLS: return "PRIORITY_CATEGORY_CALLS"; + case PRIORITY_CATEGORY_REPEAT_CALLERS: return "PRIORITY_CATEGORY_REPEAT_CALLERS"; + default: return "PRIORITY_CATEGORY_UNKNOWN_" + priorityCategory; + } + } + + public static String prioritySendersToString(int prioritySenders) { + switch (prioritySenders) { + case PRIORITY_SENDERS_ANY: return "PRIORITY_SENDERS_ANY"; + case PRIORITY_SENDERS_CONTACTS: return "PRIORITY_SENDERS_CONTACTS"; + case PRIORITY_SENDERS_STARRED: return "PRIORITY_SENDERS_STARRED"; + default: return "PRIORITY_SENDERS_UNKNOWN_" + prioritySenders; + } + } + + public static final Parcelable.Creator<Policy> CREATOR = new Parcelable.Creator<Policy>() { + @Override + public Policy createFromParcel(Parcel in) { + return new Policy(in); + } + + @Override + public Policy[] newArray(int size) { + return new Policy[size]; + } + }; + + /** + * Represents a client-specific token required to manage notification policy. + */ + public static class Token implements Parcelable { + private final IBinder mBinder; + + /** @hide */ + public Token(IBinder binder) { + if (binder == null) throw new IllegalArgumentException("Binder required for token"); + mBinder = binder; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(mBinder); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Token)) return false; + if (o == this) return true; + final Token other = (Token) o; + return Objects.equals(other.mBinder, mBinder); + } + + @Override + public String toString() { + return String.format("NotificationManager.Token[0x%08x]", + System.identityHashCode(mBinder)); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(mBinder); + } + + public static final Parcelable.Creator<Token> CREATOR + = new Parcelable.Creator<Token>() { + @Override + public Token createFromParcel(Parcel in) { + return new Token(in.readStrongBinder()); + } + + @Override + public Token[] newArray(int size) { + return new Token[size]; + } + }; + + /** Callback for receiving the result of a token request. */ + public static abstract class RequestCallback { + /** + * Received if the request was granted for this package. + * + * @param token can be used to manage notification policy. + */ + public abstract void onTokenGranted(Policy.Token token); + + /** + * Received if the request was denied for this package. + */ + public abstract void onTokenDenied(); + } + } + } + } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index fa782e4..cc7f880 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -21,6 +21,7 @@ import android.annotation.SdkConstant; import android.app.INotificationManager; import android.app.Notification; import android.app.Notification.Builder; +import android.app.NotificationManager.Policy; import android.app.Service; import android.content.ComponentName; import android.content.Context; @@ -501,6 +502,22 @@ public abstract class NotificationListenerService extends Service { } /** + * Gets the notification policy token associated with this listener. + * + * <p> + * Returns null if this listener is not currently active. + */ + public final Policy.Token getNotificationPolicyToken() { + if (!isBound()) return null; + try { + return getNotificationInterface().getPolicyTokenFromListener(mWrapper); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + return null; + } + } + + /** * Sets the desired {@link #getCurrentListenerHints() listener hints}. * * <p> diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 1ed4779..14e947c 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -16,6 +16,7 @@ package android.service.notification; +import android.app.NotificationManager.Policy; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; @@ -470,6 +471,59 @@ public class ZenModeConfig implements Parcelable { } }; + public Policy toNotificationPolicy() { + int priorityCategories = 0; + int prioritySenders = Policy.PRIORITY_SENDERS_ANY; + if (allowCalls) { + priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; + } + if (allowMessages) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; + } + if (allowEvents) { + priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; + } + if (allowReminders) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; + } + if (allowRepeatCallers) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; + } + switch (allowFrom) { + case SOURCE_ANYONE: + prioritySenders = Policy.PRIORITY_SENDERS_ANY; + break; + case SOURCE_CONTACT: + prioritySenders = Policy.PRIORITY_SENDERS_CONTACTS; + break; + case SOURCE_STAR: + prioritySenders = Policy.PRIORITY_SENDERS_STARRED; + break; + } + return new Policy(priorityCategories, prioritySenders); + } + + public void applyNotificationPolicy(Policy policy) { + if (policy == null) return; + allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0; + allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0; + allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0; + allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0; + allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) + != 0; + switch (policy.prioritySenders) { + case Policy.PRIORITY_SENDERS_CONTACTS: + allowFrom = SOURCE_CONTACT; + break; + case Policy.PRIORITY_SENDERS_STARRED: + allowFrom = SOURCE_STAR; + break; + default: + allowFrom = SOURCE_ANYONE; + break; + } + } + public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) { final long now = System.currentTimeMillis(); final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS; @@ -881,4 +935,5 @@ public class ZenModeConfig implements Parcelable { public interface Migration { ZenModeConfig migrate(XmlV1 v1); } + } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4cf2909..997d546 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -29,9 +29,11 @@ import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.INotificationManager; +import android.app.INotificationManagerCallback; import android.app.ITransientNotification; import android.app.Notification; import android.app.NotificationManager; +import android.app.NotificationManager.Policy; import android.app.PendingIntent; import android.app.StatusBarManager; import android.content.BroadcastReceiver; @@ -227,6 +229,8 @@ public class NotificationManagerService extends SystemService { new ArrayMap<String, NotificationRecord>(); final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>(); + private final ArrayMap<String, Policy.Token> mPolicyTokens = new ArrayMap<>(); + // The last key in this list owns the hardware. ArrayList<String> mLights = new ArrayList<>(); @@ -893,6 +897,13 @@ public class NotificationManagerService extends SystemService { updateInterruptionFilterLocked(); } } + + @Override + void onPolicyChanged() { + getContext().sendBroadcast( + new Intent(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)); + } }); final File systemDir = new File(Environment.getDataDirectory(), "system"); mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml")); @@ -1551,6 +1562,18 @@ public class NotificationManagerService extends SystemService { message); } + private void enforcePolicyToken(Policy.Token token, String method) { + if (!checkPolicyToken(token)) { + Slog.w(TAG, "Invalid notification policy token calling " + method); + throw new SecurityException("Invalid notification policy token"); + } + } + + private boolean checkPolicyToken(Policy.Token token) { + return mPolicyTokens.containsValue(token) + || mListeners.mPolicyTokens.containsValue(token); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -1586,24 +1609,73 @@ public class NotificationManagerService extends SystemService { enforceSystemOrSystemUIOrVolume("INotificationManager.isSystemConditionProviderEnabled"); return mConditionProviders.isSystemProviderEnabled(path); } - }; - private String[] getActiveNotificationKeys(INotificationListener token) { - final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); - final ArrayList<String> keys = new ArrayList<String>(); - if (info.isEnabledForCurrentProfiles()) { - synchronized (mNotificationList) { - final int N = mNotificationList.size(); - for (int i = 0; i < N; i++) { - final StatusBarNotification sbn = mNotificationList.get(i).sbn; - if (info.enabledAndUserMatches(sbn.getUserId())) { - keys.add(sbn.getKey()); + @Override + public Policy.Token getPolicyTokenFromListener(INotificationListener listener) { + final long identity = Binder.clearCallingIdentity(); + try { + return mListeners.getPolicyToken(listener); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void requestNotificationPolicyToken(String pkg, + INotificationManagerCallback callback) throws RemoteException { + if (callback == null) { + Slog.w(TAG, "requestNotificationPolicyToken: no callback specified"); + return; + } + if (pkg == null) { + Slog.w(TAG, "requestNotificationPolicyToken denied: no package specified"); + callback.onPolicyToken(null); + return; + } + Policy.Token token = null; + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mNotificationList) { + token = mPolicyTokens.get(pkg); + if (token == null) { + token = new Policy.Token(new Binder()); + mPolicyTokens.put(pkg, token); } + if (DBG) Slog.w(TAG, "requestNotificationPolicyToken granted for " + pkg); } + } finally { + Binder.restoreCallingIdentity(identity); } + callback.onPolicyToken(token); + } + + @Override + public boolean isNotificationPolicyTokenValid(String pkg, Policy.Token token) { + return checkPolicyToken(token); } - return keys.toArray(new String[keys.size()]); - } + + @Override + public Policy getNotificationPolicy(Policy.Token token) { + enforcePolicyToken(token, "getNotificationPolicy"); + final long identity = Binder.clearCallingIdentity(); + try { + return mZenModeHelper.getNotificationPolicy(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void setNotificationPolicy(Policy.Token token, Policy policy) { + enforcePolicyToken(token, "setNotificationPolicy"); + final long identity = Binder.clearCallingIdentity(); + try { + mZenModeHelper.setNotificationPolicy(policy); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }; private String disableNotificationEffects(NotificationRecord record) { if (mDisableNotificationEffects) { @@ -1718,6 +1790,10 @@ public class NotificationManagerService extends SystemService { pw.print(listener.component); } pw.println(')'); + pw.print(" mPolicyTokens.keys: "); + pw.println(TextUtils.join(",", mPolicyTokens.keySet())); + pw.print(" mListeners.mPolicyTokens.keys: "); + pw.println(TextUtils.join(",", mListeners.mPolicyTokens.keySet())); } pw.println("\n Condition providers:"); @@ -2970,12 +3046,18 @@ public class NotificationManagerService extends SystemService { public class NotificationListeners extends ManagedServices { private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>(); + private final ArrayMap<ComponentName, Policy.Token> mPolicyTokens = new ArrayMap<>(); private boolean mNotificationGroupsDesired; public NotificationListeners() { super(getContext(), mHandler, mNotificationList, mUserProfiles); } + public Policy.Token getPolicyToken(INotificationListener listener) { + final ManagedServiceInfo info = checkServiceTokenLocked(listener); + return info == null ? null : mPolicyTokens.get(info.component); + } + @Override protected Config getConfig() { Config c = new Config(); @@ -3000,6 +3082,7 @@ public class NotificationManagerService extends SystemService { synchronized (mNotificationList) { updateNotificationGroupsDesiredLocked(); update = makeRankingUpdateLocked(info); + mPolicyTokens.put(info.component, new Policy.Token(new Binder())); } try { listener.onListenerConnected(update); @@ -3016,6 +3099,7 @@ public class NotificationManagerService extends SystemService { } mLightTrimListeners.remove(removed); updateNotificationGroupsDesiredLocked(); + mPolicyTokens.remove(removed.component); } public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) { diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 40218bb..9cb8af5 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -21,6 +21,7 @@ import static android.media.AudioAttributes.USAGE_NOTIFICATION; import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; import android.app.AppOpsManager; +import android.app.NotificationManager.Policy; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -57,6 +58,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Objects; /** * NotificationManagerService helper for functionality related to zen mode. @@ -230,6 +232,21 @@ public class ZenModeHelper { mConfig.writeXml(out); } + public Policy getNotificationPolicy() { + return getNotificationPolicy(mConfig); + } + + private static Policy getNotificationPolicy(ZenModeConfig config) { + return config == null ? null : config.toNotificationPolicy(); + } + + public void setNotificationPolicy(Policy policy) { + if (policy == null || mConfig == null) return; + final ZenModeConfig newConfig = mConfig.copy(); + newConfig.applyNotificationPolicy(policy); + setConfig(newConfig, "setNotificationPolicy"); + } + public ZenModeConfig getConfig() { return mConfig; } @@ -247,8 +264,13 @@ public class ZenModeHelper { if (config.equals(mConfig)) return true; if (DEBUG) Log.d(TAG, "setConfig reason=" + reason); ZenLog.traceConfig(mConfig, config); + final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig), + getNotificationPolicy(config)); mConfig = config; 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)) { @@ -355,6 +377,12 @@ public class ZenModeHelper { } } + private void dispatchOnPolicyChanged() { + for (Callback callback : mCallbacks) { + callback.onPolicyChanged(); + } + } + private void dispatchOnZenModeChanged() { for (Callback callback : mCallbacks) { callback.onZenModeChanged(); @@ -617,6 +645,7 @@ public class ZenModeHelper { public static class Callback { void onConfigChanged() {} void onZenModeChanged() {} + void onPolicyChanged() {} } } |