Merge "Zen: Add notification policy management api."

This commit is contained in:
John Spurlock
2015-04-16 01:19:26 +00:00
committed by Android (Google) Code Review
11 changed files with 630 additions and 15 deletions

View File

@@ -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 \

View File

@@ -5108,8 +5108,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 {
@@ -28603,6 +28639,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();

View File

@@ -5199,8 +5199,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 {
@@ -30646,6 +30682,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();

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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();
}
}
}
}

View File

@@ -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;
@@ -500,6 +501,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}.
*

View File

@@ -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);
}
}

View File

@@ -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);
}
}
return keys.toArray(new String[keys.size()]);
}
@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);
}
@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) {

View File

@@ -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() {}
}
}