1ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen/* 2ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * Copyright (C) 2014 The Android Open Source Project 3ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * 4ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * Licensed under the Apache License, Version 2.0 (the "License"); 5ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * you may not use this file except in compliance with the License. 6ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * You may obtain a copy of the License at 7ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * 8ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * http://www.apache.org/licenses/LICENSE-2.0 9ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * 10ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * Unless required by applicable law or agreed to in writing, software 11ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * distributed under the License is distributed on an "AS IS" BASIS, 12ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * See the License for the specific language governing permissions and 14ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * limitations under the License. 15ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen */ 16ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 17ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenpackage android.support.v4.app; 18ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 19ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.app.Notification; 20ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.app.Service; 21ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.content.Intent; 2241d83b6efd08c558de282223123235e373d7815aHui Luimport android.os.Build; 23ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.os.IBinder; 24ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenimport android.os.RemoteException; 25ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 26ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen/** 27ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * Abstract service to receive side channel notifications sent from 28ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * {@link android.support.v4.app.NotificationManagerCompat}. 29ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * 30ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * <p>To receive side channel notifications, extend this service and register it in your 3133176ed929ad69d9b700fb4da97948e61d0e7c93Griff Hazen * android manifest with an intent filter for the BIND_NOTIFICATION_SIDE_CHANNEL action. 32ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * Note: you must also have an enabled 33ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * {@link android.service.notification.NotificationListenerService} within your package. 34ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * 35ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * <p>Example AndroidManifest.xml addition: 36ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * <pre> 37ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * <service android:name="com.example.NotificationSideChannelService"> 38ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * <intent-filter> 391a7f163fad9e7d0f5bc67ad44d6bf9d73d672a86Griff Hazen * <action android:name="android.support.BIND_NOTIFICATION_SIDE_CHANNEL" /> 40ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * </intent-filter> 41ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * </service></pre> 42ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen * 43ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen */ 44ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazenpublic abstract class NotificationCompatSideChannelService extends Service { 45ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen @Override 46ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen public IBinder onBind(Intent intent) { 47ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen if (intent.getAction().equals(NotificationManagerCompat.ACTION_BIND_SIDE_CHANNEL)) { 486d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen // Block side channel service connections if the current sdk has no need for 496d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen // side channeling. 506d7958ab7712efa7d6aece311208e4f2d43c1886Griff Hazen if (Build.VERSION.SDK_INT > NotificationManagerCompat.MAX_SIDE_CHANNEL_SDK_VERSION) { 5141d83b6efd08c558de282223123235e373d7815aHui Lu return null; 5241d83b6efd08c558de282223123235e373d7815aHui Lu } 53ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen return new NotificationSideChannelStub(); 54ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 55ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen return null; 56ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 57ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 58ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen /** 5933176ed929ad69d9b700fb4da97948e61d0e7c93Griff Hazen * Handle a side-channeled notification being posted. 60ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen */ 61ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen public abstract void notify(String packageName, int id, String tag, Notification notification); 62ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 63ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen /** 6433176ed929ad69d9b700fb4da97948e61d0e7c93Griff Hazen * Handle a side-channelled notification being cancelled. 65ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen */ 66ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen public abstract void cancel(String packageName, int id, String tag); 67ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 68ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen /** 6933176ed929ad69d9b700fb4da97948e61d0e7c93Griff Hazen * Handle the side-channelled cancelling of all notifications for a package. 70ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen */ 71ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen public abstract void cancelAll(String packageName); 72ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 73ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen private class NotificationSideChannelStub extends INotificationSideChannel.Stub { 74ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen @Override 75ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen public void notify(String packageName, int id, String tag, Notification notification) 76ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen throws RemoteException { 77ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen checkPermission(getCallingUid(), packageName); 78ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen long idToken = clearCallingIdentity(); 79ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen try { 80ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen NotificationCompatSideChannelService.this.notify(packageName, id, tag, notification); 81ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } finally { 82ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen restoreCallingIdentity(idToken); 83ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 84ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 85ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 86ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen @Override 87ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen public void cancel(String packageName, int id, String tag) throws RemoteException { 88ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen checkPermission(getCallingUid(), packageName); 89ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen long idToken = clearCallingIdentity(); 90ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen try { 91ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen NotificationCompatSideChannelService.this.cancel(packageName, id, tag); 92ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } finally { 93ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen restoreCallingIdentity(idToken); 94ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 95ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 96ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 97ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen @Override 98ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen public void cancelAll(String packageName) { 99ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen checkPermission(getCallingUid(), packageName); 100ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen long idToken = clearCallingIdentity(); 101ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen try { 102ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen NotificationCompatSideChannelService.this.cancelAll(packageName); 103ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } finally { 104ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen restoreCallingIdentity(idToken); 105ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 106ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 107ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 108ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen 109ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen private void checkPermission(int callingUid, String packageName) { 110ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen for (String validPackage : getPackageManager().getPackagesForUid(callingUid)) { 111ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen if (validPackage.equals(packageName)) { 112ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen return; 113ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 114ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 115ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen throw new SecurityException("NotificationSideChannelService: Uid " + callingUid 116ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen + " is not authorized for package " + packageName); 117ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen } 118ce16e4276c2f61109a23b3f6707cfcd87b07c735Griff Hazen} 119