NotificationCompatSideChannelService.java revision b1b21830e4dda2c931141f0843f856fe871fe7fc
1/* 2 * Copyright (C) 2014 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.support.v4.app; 18 19import android.app.Notification; 20import android.app.Service; 21import android.content.Intent; 22import android.os.IBinder; 23import android.os.RemoteException; 24 25/** 26 * Abstract service to receive side channel notifications sent from 27 * {@link android.support.v4.app.NotificationManagerCompat}. 28 * 29 * <p>To receive side channel notifications, extend this service and register it in your 30 * android manifest with an intent filter for the BIND_SIDE_CHANNEL action. 31 * Note: you must also have an enabled 32 * {@link android.service.notification.NotificationListenerService} within your package. 33 * 34 * <p>Example AndroidManifest.xml addition: 35 * <pre> 36 * <service android:name="com.example.NotificationSideChannelService"> 37 * <intent-filter> 38 * <action android:name="android.support.BIND_NOTIFICATION_SIDE_CHANNEL" /> 39 * </intent-filter> 40 * </service></pre> 41 * 42 */ 43public abstract class NotificationCompatSideChannelService extends Service { 44 @Override 45 public IBinder onBind(Intent intent) { 46 if (intent.getAction().equals(NotificationManagerCompat.ACTION_BIND_SIDE_CHANNEL)) { 47 return new NotificationSideChannelStub(); 48 } 49 return null; 50 } 51 52 /** 53 * Handle a side-channel notification was posted to the service. 54 */ 55 public abstract void notify(String packageName, int id, String tag, Notification notification); 56 57 /** 58 * Handle a side-channel cancelling of an already-notified notification. 59 */ 60 public abstract void cancel(String packageName, int id, String tag); 61 62 /** 63 * Handle a side-channel cancelling of all notifications for the given package. 64 */ 65 public abstract void cancelAll(String packageName); 66 67 private class NotificationSideChannelStub extends INotificationSideChannel.Stub { 68 @Override 69 public void notify(String packageName, int id, String tag, Notification notification) 70 throws RemoteException { 71 checkPermission(getCallingUid(), packageName); 72 long idToken = clearCallingIdentity(); 73 try { 74 NotificationCompatSideChannelService.this.notify(packageName, id, tag, notification); 75 } finally { 76 restoreCallingIdentity(idToken); 77 } 78 } 79 80 @Override 81 public void cancel(String packageName, int id, String tag) throws RemoteException { 82 checkPermission(getCallingUid(), packageName); 83 long idToken = clearCallingIdentity(); 84 try { 85 NotificationCompatSideChannelService.this.cancel(packageName, id, tag); 86 } finally { 87 restoreCallingIdentity(idToken); 88 } 89 } 90 91 @Override 92 public void cancelAll(String packageName) { 93 checkPermission(getCallingUid(), packageName); 94 long idToken = clearCallingIdentity(); 95 try { 96 NotificationCompatSideChannelService.this.cancelAll(packageName); 97 } finally { 98 restoreCallingIdentity(idToken); 99 } 100 } 101 } 102 103 private void checkPermission(int callingUid, String packageName) { 104 for (String validPackage : getPackageManager().getPackagesForUid(callingUid)) { 105 if (validPackage.equals(packageName)) { 106 return; 107 } 108 } 109 throw new SecurityException("NotificationSideChannelService: Uid " + callingUid 110 + " is not authorized for package " + packageName); 111 } 112} 113