1 /* 2 * Copyright 2022 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 17 package com.android.server.bluetooth; 18 19 import android.app.Notification; 20 import android.app.NotificationChannel; 21 import android.app.NotificationManager; 22 import android.app.PendingIntent; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.PackageManager; 26 import android.net.Uri; 27 import android.os.Binder; 28 import android.os.UserHandle; 29 import android.service.notification.StatusBarNotification; 30 import android.util.Log; 31 32 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 37 /** 38 * Notification manager for Bluetooth. All notification will be sent to the current user. 39 */ 40 public class BluetoothNotificationManager { 41 private static final String TAG = "BluetoothNotificationManager"; 42 private static final String NOTIFICATION_TAG = "com.android.bluetooth"; 43 public static final String APM_NOTIFICATION_CHANNEL = "apm_notification_channel"; 44 private static final String APM_NOTIFICATION_GROUP = "apm_notification_group"; 45 private static final String HELP_PAGE_URL = 46 "https://support.google.com/pixelphone/answer/12639358"; 47 48 private final Context mContext; 49 private NotificationManager mNotificationManager; 50 51 private boolean mInitialized = false; 52 53 /** 54 * Constructor 55 * 56 * @param ctx The context to use to obtain access to the Notification Service 57 */ BluetoothNotificationManager(Context ctx)58 BluetoothNotificationManager(Context ctx) { 59 mContext = ctx; 60 } 61 getNotificationManagerForCurrentUser()62 private NotificationManager getNotificationManagerForCurrentUser() { 63 final long callingIdentity = Binder.clearCallingIdentity(); 64 try { 65 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, 66 UserHandle.CURRENT).getSystemService(NotificationManager.class); 67 } catch (PackageManager.NameNotFoundException e) { 68 Log.e(TAG, "Failed to get NotificationManager for current user: " + e.getMessage()); 69 } finally { 70 Binder.restoreCallingIdentity(callingIdentity); 71 } 72 return null; 73 } 74 75 /** 76 * Update to the notification manager fot current user and create notification channels. 77 */ createNotificationChannels()78 public void createNotificationChannels() { 79 if (mNotificationManager != null) { 80 // Cancel all active notification from Bluetooth Stack. 81 cleanAllBtNotification(); 82 } 83 mNotificationManager = getNotificationManagerForCurrentUser(); 84 if (mNotificationManager == null) { 85 return; 86 } 87 List<NotificationChannel> channelsList = new ArrayList<>(); 88 89 final NotificationChannel apmChannel = new NotificationChannel( 90 APM_NOTIFICATION_CHANNEL, 91 APM_NOTIFICATION_GROUP, 92 NotificationManager.IMPORTANCE_HIGH); 93 channelsList.add(apmChannel); 94 95 final long callingIdentity = Binder.clearCallingIdentity(); 96 try { 97 mNotificationManager.createNotificationChannels(channelsList); 98 } catch (Exception e) { 99 Log.e(TAG, "Error Message: " + e.getMessage()); 100 e.printStackTrace(); 101 } finally { 102 Binder.restoreCallingIdentity(callingIdentity); 103 } 104 } 105 cleanAllBtNotification()106 private void cleanAllBtNotification() { 107 for (StatusBarNotification notification : getActiveNotifications()) { 108 if (NOTIFICATION_TAG.equals(notification.getTag())) { 109 cancel(notification.getId()); 110 } 111 } 112 } 113 114 /** 115 * Send notification to the current user. 116 */ notify(int id, Notification notification)117 public void notify(int id, Notification notification) { 118 if (!mInitialized) { 119 createNotificationChannels(); 120 mInitialized = true; 121 } 122 if (mNotificationManager == null) { 123 return; 124 } 125 mNotificationManager.notify(NOTIFICATION_TAG, id, notification); 126 } 127 128 /** 129 * Build and send the APM notification. 130 */ sendApmNotification(String title, String message)131 public void sendApmNotification(String title, String message) { 132 if (!mInitialized) { 133 createNotificationChannels(); 134 mInitialized = true; 135 } 136 137 Intent openLinkIntent = new Intent(Intent.ACTION_VIEW) 138 .setData(Uri.parse(HELP_PAGE_URL)) 139 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 140 PendingIntent tapPendingIntent = PendingIntent.getActivity( 141 mContext.createContextAsUser(UserHandle.CURRENT, 0), 142 PendingIntent.FLAG_UPDATE_CURRENT, openLinkIntent, PendingIntent.FLAG_IMMUTABLE); 143 144 Notification notification = new Notification.Builder(mContext, APM_NOTIFICATION_CHANNEL) 145 .setAutoCancel(true) 146 .setLocalOnly(true) 147 .setContentTitle(title) 148 .setContentText(message) 149 .setContentIntent(tapPendingIntent) 150 .setVisibility(Notification.VISIBILITY_PUBLIC) 151 .setStyle(new Notification.BigTextStyle().bigText(message)) 152 .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth) 153 .build(); 154 notify(SystemMessage.NOTE_BT_APM_NOTIFICATION, notification); 155 } 156 157 /** 158 * Cancel the notification fot current user. 159 */ cancel(int id)160 public void cancel(int id) { 161 if (mNotificationManager == null) { 162 return; 163 } 164 mNotificationManager.cancel(NOTIFICATION_TAG, id); 165 } 166 167 /** 168 * Get active notifications for current user. 169 */ getActiveNotifications()170 public StatusBarNotification[] getActiveNotifications() { 171 if (mNotificationManager == null) { 172 return new StatusBarNotification[0]; 173 } 174 return mNotificationManager.getActiveNotifications(); 175 } 176 } 177