• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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