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