diff --git a/Android.mk b/Android.mk index 23aeae547ee5e..5d902c2059038 100644 --- a/Android.mk +++ b/Android.mk @@ -79,6 +79,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IEphemeralResolver.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ + core/java/android/app/IOnNotificationChannelCreatedListener.aidl \ core/java/android/app/INotificationManager.aidl \ core/java/android/app/IProcessObserver.aidl \ core/java/android/app/ISearchManager.aidl \ diff --git a/api/current.txt b/api/current.txt index 871c896ad4433..94890213d3a8e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5395,7 +5395,7 @@ package android.app { method public void cancel(int); method public void cancel(java.lang.String, int); method public void cancelAll(); - method public void createNotificationChannel(android.app.NotificationChannel); + method public void createNotificationChannel(android.app.NotificationChannel, android.app.NotificationManager.OnNotificationChannelCreatedListener, android.os.Handler); method public void deleteNotificationChannel(java.lang.String); method public android.service.notification.StatusBarNotification[] getActiveNotifications(); method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String); @@ -5429,6 +5429,10 @@ package android.app { field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0 } + public static abstract interface NotificationManager.OnNotificationChannelCreatedListener { + method public abstract void onNotificationChannelCreated(android.app.NotificationChannel); + } + public static class NotificationManager.Policy implements android.os.Parcelable { ctor public NotificationManager.Policy(int, int, int); ctor public NotificationManager.Policy(int, int, int, int); diff --git a/api/system-current.txt b/api/system-current.txt index 5a8a1b027e8d4..2190abb2ef67f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5562,7 +5562,7 @@ package android.app { method public void cancel(int); method public void cancel(java.lang.String, int); method public void cancelAll(); - method public void createNotificationChannel(android.app.NotificationChannel); + method public void createNotificationChannel(android.app.NotificationChannel, android.app.NotificationManager.OnNotificationChannelCreatedListener, android.os.Handler); method public void deleteNotificationChannel(java.lang.String); method public android.service.notification.StatusBarNotification[] getActiveNotifications(); method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String); @@ -5596,6 +5596,10 @@ package android.app { field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0 } + public static abstract interface NotificationManager.OnNotificationChannelCreatedListener { + method public abstract void onNotificationChannelCreated(android.app.NotificationChannel); + } + public static class NotificationManager.Policy implements android.os.Parcelable { ctor public NotificationManager.Policy(int, int, int); ctor public NotificationManager.Policy(int, int, int, int); diff --git a/api/test-current.txt b/api/test-current.txt index 9f854dce1e6cf..a55fa4e6b7a17 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5405,7 +5405,7 @@ package android.app { method public void cancel(int); method public void cancel(java.lang.String, int); method public void cancelAll(); - method public void createNotificationChannel(android.app.NotificationChannel); + method public void createNotificationChannel(android.app.NotificationChannel, android.app.NotificationManager.OnNotificationChannelCreatedListener, android.os.Handler); method public void deleteNotificationChannel(java.lang.String); method public android.service.notification.StatusBarNotification[] getActiveNotifications(); method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String); @@ -5440,6 +5440,10 @@ package android.app { field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0 } + public static abstract interface NotificationManager.OnNotificationChannelCreatedListener { + method public abstract void onNotificationChannelCreated(android.app.NotificationChannel); + } + public static class NotificationManager.Policy implements android.os.Parcelable { ctor public NotificationManager.Policy(int, int, int); ctor public NotificationManager.Policy(int, int, int, int); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 2a7341ae6911c..927ef6c35b7ac 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -2,21 +2,22 @@ ** ** Copyright 2007, 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 +** 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 +** 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 +** 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.IOnNotificationChannelCreatedListener; import android.app.ITransientNotification; import android.app.Notification; import android.app.NotificationChannel; @@ -58,7 +59,8 @@ interface INotificationManager int getImportance(String pkg, int uid); int getPackageImportance(String pkg); - void createNotificationChannel(String pkg, in NotificationChannel channel); + void createNotificationChannel(String pkg, in NotificationChannel channel, + in IOnNotificationChannelCreatedListener listener); void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel); NotificationChannel getNotificationChannel(String pkg, String channelId); NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId); diff --git a/core/java/android/app/IOnNotificationChannelCreatedListener.aidl b/core/java/android/app/IOnNotificationChannelCreatedListener.aidl new file mode 100644 index 0000000000000..8e6654200017a --- /dev/null +++ b/core/java/android/app/IOnNotificationChannelCreatedListener.aidl @@ -0,0 +1,25 @@ +/* +** +** Copyright 2016, 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.NotificationChannel; + +/** {@hide} */ +oneway interface IOnNotificationChannelCreatedListener { + void onNotificationChannelCreated(in NotificationChannel channel); +} diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 047f349b1e540..7693d7666e19e 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -18,6 +18,7 @@ package android.app; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.TestApi; import android.app.Notification.Builder; @@ -30,6 +31,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; @@ -379,12 +381,42 @@ public class NotificationManager } /** - * Creates a notification channel that notifications can be posted to. + * Listener passed to {@link NotificationManager#createNotificationChannel} to notify + * caller of result. */ - public void createNotificationChannel(NotificationChannel channel) { + public interface OnNotificationChannelCreatedListener { + /** + * @param createdChannel NotificationChannel created by the system. Value is null iff an + * exception was thrown during channel creation. + */ + public void onNotificationChannelCreated(NotificationChannel createdChannel); + } + + /** + * Creates a notification channel that notifications can be posted to. + * + * @param channel the channel to attempt to create. Note that the created channel may differ + * from this value. + * @param listener Called when operation is finished. + * @param handler The handler to invoke the listener on, or {@code null} to use the main + * handler. + */ + public void createNotificationChannel( + @NonNull NotificationChannel channel, + @NonNull OnNotificationChannelCreatedListener listener, + @Nullable Handler handler) { INotificationManager service = getService(); try { - service.createNotificationChannel(mContext.getPackageName(), channel); + final Handler actualHandler = + handler != null ? handler : new Handler(Looper.getMainLooper()); + service.createNotificationChannel(mContext.getPackageName(), channel, + new IOnNotificationChannelCreatedListener.Stub() { + @Override public void onNotificationChannelCreated( + NotificationChannel channel) { + actualHandler.post( + () -> { listener.onNotificationChannelCreated(channel); }); + } + }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -583,7 +615,7 @@ public class NotificationManager *
* Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
* @param id The id of the rule to update
- * @param automaticZenRule the rule to update.
+ * @param automaticZenRule the rule to update.
* @return Whether the rule was successfully updated.
*/
public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 84c298b06284d..c78a0f57b0ca1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -53,16 +53,17 @@ import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
+import android.app.backup.BackupManager;
import android.app.IActivityManager;
+import android.app.IOnNotificationChannelCreatedListener;
import android.app.INotificationManager;
import android.app.ITransientNotification;
import android.app.Notification;
import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
-import android.app.backup.BackupManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
@@ -224,6 +225,7 @@ public class NotificationManagerService extends SystemService {
private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
private IActivityManager mAm;
+ private IPackageManager mPackageManager;
AudioManager mAudioManager;
AudioManagerInternal mAudioManagerInternal;
@Nullable StatusBarManagerInternal mStatusBar;
@@ -718,10 +720,10 @@ public class NotificationManagerService extends SystemService {
if (packageChanged) {
// We cancel notifications for packages which have just been disabled
try {
- final IPackageManager pm = AppGlobals.getPackageManager();
- final int enabled = pm.getApplicationEnabledSetting(pkgName,
+ final int enabled = mPackageManager.getApplicationEnabledSetting(
+ pkgName,
changeUserId != UserHandle.USER_ALL ? changeUserId :
- UserHandle.USER_SYSTEM);
+ UserHandle.USER_SYSTEM);
if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|| enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
cancelNotifications = false;
@@ -884,6 +886,7 @@ public class NotificationManagerService extends SystemService {
super(context);
}
+ // TODO - replace these methods with a single VisibleForTesting constructor
@VisibleForTesting
void setAudioManager(AudioManager audioMananger) {
mAudioManager = audioMananger;
@@ -931,6 +934,17 @@ public class NotificationManagerService extends SystemService {
mFallbackVibrationPattern = vibrationPattern;
}
+ @VisibleForTesting
+ void setPackageManager(IPackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
+ // TODO: This probably should not be mocked, it's an implementation detail.
+ @VisibleForTesting
+ void setRankingHelper(RankingHelper rankingHelper) {
+ mRankingHelper = rankingHelper;
+ }
+
@Override
public void onStart() {
Resources resources = getContext().getResources();
@@ -940,6 +954,7 @@ public class NotificationManagerService extends SystemService {
DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
mAm = ActivityManager.getService();
+ mPackageManager = AppGlobals.getPackageManager();
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
@@ -1312,6 +1327,11 @@ public class NotificationManagerService extends SystemService {
scheduleInterruptionFilterChanged(interruptionFilter);
}
+ @VisibleForTesting
+ INotificationManager getBinderService() {
+ return INotificationManager.Stub.asInterface(mService);
+ }
+
private final IBinder mService = new INotificationManager.Stub() {
// Toasts
// ============================================================================
@@ -1529,13 +1549,15 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public void createNotificationChannel(String pkg, NotificationChannel channel) {
+ public void createNotificationChannel(String pkg, NotificationChannel channel,
+ IOnNotificationChannelCreatedListener listener) throws RemoteException {
Preconditions.checkNotNull(channel);
Preconditions.checkNotNull(channel.getId());
Preconditions.checkNotNull(channel.getName());
checkCallerIsSystemOrSameApp(pkg);
mRankingHelper.createNotificationChannel(pkg, Binder.getCallingUid(), channel);
savePolicyFile();
+ listener.onNotificationChannelCreated(channel);
}
@Override
@@ -3906,23 +3928,23 @@ public class NotificationManagerService extends SystemService {
throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
}
- private static void checkCallerIsSystemOrSameApp(String pkg) {
+ private void checkCallerIsSystemOrSameApp(String pkg) {
if (isCallerSystem()) {
return;
}
checkCallerIsSameApp(pkg);
}
- private static void checkCallerIsSameApp(String pkg) {
+ private void checkCallerIsSameApp(String pkg) {
final int uid = Binder.getCallingUid();
try {
- ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
+ ApplicationInfo ai = mPackageManager.getApplicationInfo(
pkg, 0, UserHandle.getCallingUserId());
if (ai == null) {
throw new SecurityException("Unknown package " + pkg);
}
if (!UserHandle.isSameApp(ai.uid, uid)) {
- throw new SecurityException("Calling uid " + uid + " gave package"
+ throw new SecurityException("Calling uid " + uid + " gave package "
+ pkg + " which is owned by uid " + ai.uid);
}
} catch (RemoteException re) {
@@ -4009,7 +4031,7 @@ public class NotificationManagerService extends SystemService {
private boolean isPackageSuspendedForUser(String pkg, int uid) {
int userId = UserHandle.getUserId(uid);
try {
- return AppGlobals.getPackageManager().isPackageSuspendedForUser(pkg, userId);
+ return mPackageManager.isPackageSuspendedForUser(pkg, userId);
} catch (RemoteException re) {
throw new SecurityException("Could not talk to package manager service");
} catch (IllegalArgumentException ex) {
@@ -4510,7 +4532,7 @@ public class NotificationManagerService extends SystemService {
}
public String[] getRequestingPackages() throws RemoteException {
- final ParceledListSlice list = AppGlobals.getPackageManager()
+ final ParceledListSlice list = mPackageManager
.getPackagesHoldingPermissions(PERM, 0 /*flags*/,
ActivityManager.getCurrentUser());
final List