NotificationAssistantService.java revision 52e64d0162bd71164c6e23e3975e98091f70588a
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 posted by an app. Called before alert. 67 * 68 * @param sbn the new notification 69 * @param importance the initial importance of the notification. 70 * @param user true if the initial importance reflects an explicit user preference. 71 * @return an adjustment or null to take no action, within 100ms. 72 */ 73 abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn, 74 int importance, boolean user); 75 76 /** 77 * Updates a notification. N.B. this won’t cause 78 * an existing notification to alert, but might allow a future update to 79 * this notification to alert. 80 * 81 * @param adjustment the adjustment with an explanation 82 */ 83 public final void adjustNotification(Adjustment adjustment) { 84 if (!isBound()) return; 85 try { 86 getNotificationInterface().applyAdjustmentFromAssistant(mWrapper, adjustment); 87 } catch (android.os.RemoteException ex) { 88 Log.v(TAG, "Unable to contact notification manager", ex); 89 throw ex.rethrowFromSystemServer(); 90 } 91 } 92 93 /** 94 * Updates existing notifications. Re-ranking won't occur until all adjustments are applied. 95 * N.B. this won’t cause an existing notification to alert, but might allow a future update to 96 * these notifications to alert. 97 * 98 * @param adjustments a list of adjustments with explanations 99 */ 100 public final void adjustNotifications(List<Adjustment> adjustments) { 101 if (!isBound()) return; 102 try { 103 getNotificationInterface().applyAdjustmentsFromAssistant(mWrapper, adjustments); 104 } catch (android.os.RemoteException ex) { 105 Log.v(TAG, "Unable to contact notification manager", ex); 106 throw ex.rethrowFromSystemServer(); 107 } 108 } 109 110 /** 111 * Creates a notification channel that notifications can be posted to for a given package. 112 * 113 * @param pkg The package to create a channel for. 114 * @param channel the channel to attempt to create. 115 */ 116 public void createNotificationChannel(@NonNull String pkg, 117 @NonNull NotificationChannel channel) { 118 if (!isBound()) return; 119 try { 120 getNotificationInterface().createNotificationChannelFromAssistant( 121 mWrapper, pkg, channel); 122 } catch (RemoteException e) { 123 Log.v(TAG, "Unable to contact notification manager", e); 124 throw e.rethrowFromSystemServer(); 125 } 126 } 127 128 /** 129 * Updates a notification channel for a given package. 130 * 131 * @param pkg The package to the channel belongs to. 132 * @param channel the channel to attempt to update. 133 */ 134 public void updateNotificationChannel(@NonNull String pkg, 135 @NonNull NotificationChannel channel) { 136 if (!isBound()) return; 137 try { 138 getNotificationInterface().updateNotificationChannelFromAssistant( 139 mWrapper, pkg, channel); 140 } catch (RemoteException e) { 141 Log.v(TAG, "Unable to contact notification manager", e); 142 throw e.rethrowFromSystemServer(); 143 } 144 } 145 146 /** 147 * Returns all notification channels belonging to the given package. 148 */ 149 public List<NotificationChannel> getNotificationChannels(@NonNull String pkg) { 150 if (!isBound()) return null; 151 try { 152 return getNotificationInterface().getNotificationChannelsFromAssistant( 153 mWrapper, pkg).getList(); 154 } catch (RemoteException e) { 155 Log.v(TAG, "Unable to contact notification manager", e); 156 throw e.rethrowFromSystemServer(); 157 } 158 } 159 160 /** 161 * Deletes the given notification channel. 162 */ 163 public void deleteNotificationChannel(@NonNull String pkg, @NonNull String channelId) { 164 if (!isBound()) return; 165 try { 166 getNotificationInterface().deleteNotificationChannelFromAssistant( 167 mWrapper, pkg, channelId); 168 } catch (RemoteException e) { 169 throw e.rethrowFromSystemServer(); 170 } 171 } 172 173 174 private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper { 175 @Override 176 public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder, 177 int importance, boolean user) { 178 StatusBarNotification sbn; 179 try { 180 sbn = sbnHolder.get(); 181 } catch (RemoteException e) { 182 Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e); 183 return; 184 } 185 186 SomeArgs args = SomeArgs.obtain(); 187 args.arg1 = sbn; 188 args.argi1 = importance; 189 args.argi2 = user ? 1 : 0; 190 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED, 191 args).sendToTarget(); 192 } 193 } 194 195 private final class MyHandler extends Handler { 196 public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1; 197 198 public MyHandler(Looper looper) { 199 super(looper, null, false); 200 } 201 202 @Override 203 public void handleMessage(Message msg) { 204 switch (msg.what) { 205 case MSG_ON_NOTIFICATION_ENQUEUED: { 206 SomeArgs args = (SomeArgs) msg.obj; 207 StatusBarNotification sbn = (StatusBarNotification) args.arg1; 208 final int importance = args.argi1; 209 final boolean user = args.argi2 == 1; 210 args.recycle(); 211 Adjustment adjustment = onNotificationEnqueued(sbn, importance, user); 212 if (adjustment != null) { 213 adjustNotification(adjustment); 214 } 215 } break; 216 } 217 } 218 } 219} 220