NotificationAssistantService.java revision ceecfcf5ccd4790f9ab3a08c3cb7ce4baa2c1eb1
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.service.notification; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.annotation.SdkConstant; 22import android.app.NotificationChannel; 23import android.content.Context; 24import android.content.Intent; 25import android.os.Handler; 26import android.os.IBinder; 27import android.os.Looper; 28import android.os.Message; 29import android.os.RemoteException; 30import android.util.Log; 31import com.android.internal.os.SomeArgs; 32 33import java.util.ArrayList; 34import java.util.List; 35 36/** 37 * A service that helps the user manage notifications. 38 */ 39public abstract class NotificationAssistantService extends NotificationListenerService { 40 private static final String TAG = "NotificationAssistants"; 41 42 /** 43 * The {@link Intent} that must be declared as handled by the service. 44 */ 45 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 46 public static final String SERVICE_INTERFACE 47 = "android.service.notification.NotificationAssistantService"; 48 49 private Handler mHandler; 50 51 @Override 52 protected void attachBaseContext(Context base) { 53 super.attachBaseContext(base); 54 mHandler = new MyHandler(getContext().getMainLooper()); 55 } 56 57 @Override 58 public final IBinder onBind(Intent intent) { 59 if (mWrapper == null) { 60 mWrapper = new NotificationAssistantServiceWrapper(); 61 } 62 return mWrapper; 63 } 64 65 /** 66 * A notification was snoozed until a context. For use with 67 * {@link Adjustment#KEY_SNOOZE_CRITERIA}. When the device reaches the given context, the 68 * assistant should restore the notification with {@link #unsnoozeNotification(String)}. 69 * 70 * @param sbn the notification to snooze 71 * @param snoozeCriterionId the {@link SnoozeCriterion#getId()} representing a device context. 72 */ 73 abstract public void onNotificationSnoozedUntilContext(StatusBarNotification sbn, 74 String snoozeCriterionId); 75 76 /** 77 * A notification was posted by an app. Called before alert. 78 * 79 * @param sbn the new notification 80 * @return an adjustment or null to take no action, within 100ms. 81 */ 82 abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn); 83 84 /** 85 * Updates a notification. N.B. this won’t cause 86 * an existing notification to alert, but might allow a future update to 87 * this notification to alert. 88 * 89 * @param adjustment the adjustment with an explanation 90 */ 91 public final void adjustNotification(Adjustment adjustment) { 92 if (!isBound()) return; 93 try { 94 getNotificationInterface().applyAdjustmentFromAssistant(mWrapper, adjustment); 95 } catch (android.os.RemoteException ex) { 96 Log.v(TAG, "Unable to contact notification manager", ex); 97 throw ex.rethrowFromSystemServer(); 98 } 99 } 100 101 /** 102 * Updates existing notifications. Re-ranking won't occur until all adjustments are applied. 103 * N.B. this won’t cause an existing notification to alert, but might allow a future update to 104 * these notifications to alert. 105 * 106 * @param adjustments a list of adjustments with explanations 107 */ 108 public final void adjustNotifications(List<Adjustment> adjustments) { 109 if (!isBound()) return; 110 try { 111 getNotificationInterface().applyAdjustmentsFromAssistant(mWrapper, adjustments); 112 } catch (android.os.RemoteException ex) { 113 Log.v(TAG, "Unable to contact notification manager", ex); 114 throw ex.rethrowFromSystemServer(); 115 } 116 } 117 118 /** 119 * Inform the notification manager about un-snoozing a specific notification. 120 * <p> 121 * This should only be used for notifications snoozed by this listener using 122 * {@link #snoozeNotification(String, String)}. Once un-snoozed, you will get a 123 * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the 124 * notification. 125 * @param key The key of the notification to snooze 126 */ 127 public final void unsnoozeNotification(String key) { 128 if (!isBound()) return; 129 try { 130 getNotificationInterface().unsnoozeNotificationFromAssistant(mWrapper, key); 131 } catch (android.os.RemoteException ex) { 132 Log.v(TAG, "Unable to contact notification manager", ex); 133 } 134 } 135 136 /** 137 * Creates a notification channel that notifications can be posted to for a given package. 138 * 139 * @param pkg The package to create a channel for. 140 * @param channel the channel to attempt to create. 141 */ 142 public void createNotificationChannel(@NonNull String pkg, 143 @NonNull NotificationChannel channel) { 144 if (!isBound()) return; 145 try { 146 getNotificationInterface().createNotificationChannelFromAssistant( 147 mWrapper, pkg, channel); 148 } catch (RemoteException e) { 149 Log.v(TAG, "Unable to contact notification manager", e); 150 throw e.rethrowFromSystemServer(); 151 } 152 } 153 154 /** 155 * Updates a notification channel for a given package. 156 * 157 * @param pkg The package to the channel belongs to. 158 * @param channel the channel to attempt to update. 159 */ 160 public void updateNotificationChannel(@NonNull String pkg, 161 @NonNull NotificationChannel channel) { 162 if (!isBound()) return; 163 try { 164 getNotificationInterface().updateNotificationChannelFromAssistant( 165 mWrapper, pkg, channel); 166 } catch (RemoteException e) { 167 Log.v(TAG, "Unable to contact notification manager", e); 168 throw e.rethrowFromSystemServer(); 169 } 170 } 171 172 /** 173 * Returns all notification channels belonging to the given package. 174 */ 175 public List<NotificationChannel> getNotificationChannels(@NonNull String pkg) { 176 if (!isBound()) return null; 177 try { 178 return getNotificationInterface().getNotificationChannelsFromAssistant( 179 mWrapper, pkg).getList(); 180 } catch (RemoteException e) { 181 Log.v(TAG, "Unable to contact notification manager", e); 182 throw e.rethrowFromSystemServer(); 183 } 184 } 185 186 /** 187 * Deletes the given notification channel. 188 */ 189 public void deleteNotificationChannel(@NonNull String pkg, @NonNull String channelId) { 190 if (!isBound()) return; 191 try { 192 getNotificationInterface().deleteNotificationChannelFromAssistant( 193 mWrapper, pkg, channelId); 194 } catch (RemoteException e) { 195 throw e.rethrowFromSystemServer(); 196 } 197 } 198 199 200 private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper { 201 @Override 202 public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder) { 203 StatusBarNotification sbn; 204 try { 205 sbn = sbnHolder.get(); 206 } catch (RemoteException e) { 207 Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e); 208 return; 209 } 210 211 SomeArgs args = SomeArgs.obtain(); 212 args.arg1 = sbn; 213 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED, 214 args).sendToTarget(); 215 } 216 217 @Override 218 public void onNotificationSnoozedUntilContext( 219 IStatusBarNotificationHolder sbnHolder, String snoozeCriterionId) 220 throws RemoteException { 221 StatusBarNotification sbn; 222 try { 223 sbn = sbnHolder.get(); 224 } catch (RemoteException e) { 225 Log.w(TAG, "onNotificationSnoozed: Error receiving StatusBarNotification", e); 226 return; 227 } 228 229 SomeArgs args = SomeArgs.obtain(); 230 args.arg1 = sbn; 231 args.arg2 = snoozeCriterionId; 232 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_SNOOZED, 233 args).sendToTarget(); 234 } 235 } 236 237 private final class MyHandler extends Handler { 238 public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1; 239 public static final int MSG_ON_NOTIFICATION_SNOOZED = 2; 240 241 public MyHandler(Looper looper) { 242 super(looper, null, false); 243 } 244 245 @Override 246 public void handleMessage(Message msg) { 247 switch (msg.what) { 248 case MSG_ON_NOTIFICATION_ENQUEUED: { 249 SomeArgs args = (SomeArgs) msg.obj; 250 StatusBarNotification sbn = (StatusBarNotification) args.arg1; 251 args.recycle(); 252 Adjustment adjustment = onNotificationEnqueued(sbn); 253 if (adjustment != null) { 254 if (!isBound()) return; 255 try { 256 getNotificationInterface().applyEnqueuedAdjustmentFromAssistant( 257 mWrapper, adjustment); 258 } catch (android.os.RemoteException ex) { 259 Log.v(TAG, "Unable to contact notification manager", ex); 260 throw ex.rethrowFromSystemServer(); 261 } 262 } 263 break; 264 } 265 case MSG_ON_NOTIFICATION_SNOOZED: { 266 SomeArgs args = (SomeArgs) msg.obj; 267 StatusBarNotification sbn = (StatusBarNotification) args.arg1; 268 String snoozeCriterionId = (String) args.arg2; 269 args.recycle(); 270 onNotificationSnoozedUntilContext(sbn, snoozeCriterionId); 271 break; 272 } 273 } 274 } 275 } 276} 277