• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.settings.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.bluetooth.BluetoothDevice;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.os.PowerManager;
28 import android.os.UserHandle;
29 import android.text.TextUtils;
30 import android.util.Log;
31 import android.widget.Toast;
32 
33 import androidx.core.app.NotificationCompat;
34 
35 import com.android.settings.R;
36 import com.android.settings.flags.Flags;
37 import com.android.settingslib.bluetooth.BluetoothUtils;
38 
39 /**
40  * BluetoothKeyMissingReceiver is a receiver for Bluetooth key missing error when reconnecting to a
41  * bonded bluetooth device.
42  */
43 public final class BluetoothKeyMissingReceiver extends BroadcastReceiver {
44     private static final String TAG = "BtKeyMissingReceiver";
45     private static final String CHANNEL_ID = "bluetooth_notification_channel";
46     private static final int NOTIFICATION_ID = android.R.drawable.stat_sys_data_bluetooth;
47 
48     @Override
onReceive(Context context, Intent intent)49     public void onReceive(Context context, Intent intent) {
50         if (!Flags.enableBluetoothKeyMissingDialog()) {
51             return;
52         }
53         String action = intent.getAction();
54         if (action == null) {
55             return;
56         }
57 
58         BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
59         if (device == null) {
60             return;
61         }
62         if (BluetoothUtils.isExclusivelyManagedBluetoothDevice(context, device)) {
63             Log.d(TAG, "Exclusively managed device " + device + ", skip");
64             return;
65         }
66         PowerManager powerManager = context.getSystemService(PowerManager.class);
67         if (TextUtils.equals(action, BluetoothDevice.ACTION_KEY_MISSING)) {
68             Log.d(TAG, "Receive ACTION_KEY_MISSING");
69             if (device.getBondState() == BluetoothDevice.BOND_NONE) {
70                 Log.d(
71                         TAG,
72                         "Device " + device.getAnonymizedAddress() + " is already unbonded, skip.");
73                 return;
74             }
75             Integer keyMissingCount = BluetoothUtils.getKeyMissingCount(device);
76             boolean keyMissingFirstTime = keyMissingCount == null || keyMissingCount == 1;
77             if (shouldShowDialog(context, device, powerManager)) {
78                 if (keyMissingFirstTime) {
79                     Intent pairingIntent = getKeyMissingDialogIntent(context, device);
80                     Log.d(TAG, "Show key missing dialog:" + device);
81                     context.startActivityAsUser(pairingIntent, UserHandle.CURRENT);
82                 } else {
83                     Log.d(TAG, "Show key missing toast:" + device);
84                     Toast.makeText(
85                                     context,
86                                     context.getString(
87                                             R.string.bluetooth_key_missing_toast,
88                                             device.getAlias()),
89                                     Toast.LENGTH_SHORT)
90                             .show();
91                 }
92             } else if (keyMissingFirstTime) {
93                 Log.d(TAG, "Show key missing notification: " + device);
94                 showNotification(context, device);
95             }
96         }
97     }
98 
getKeyMissingDialogIntent(Context context, BluetoothDevice device)99     private Intent getKeyMissingDialogIntent(Context context, BluetoothDevice device) {
100         Intent pairingIntent = new Intent();
101         pairingIntent.setClass(context, BluetoothKeyMissingDialog.class);
102         pairingIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
103         pairingIntent.setAction(BluetoothDevice.ACTION_KEY_MISSING);
104         pairingIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
105         return pairingIntent;
106     }
107 
shouldShowDialog( Context context, BluetoothDevice device, PowerManager powerManager)108     private boolean shouldShowDialog(
109             Context context, BluetoothDevice device, PowerManager powerManager) {
110         return LocalBluetoothPreferences.shouldShowDialogInForeground(context, device)
111                 && powerManager.isInteractive();
112     }
113 
showNotification(Context context, BluetoothDevice bluetoothDevice)114     private void showNotification(Context context, BluetoothDevice bluetoothDevice) {
115         NotificationManager nm = context.getSystemService(NotificationManager.class);
116         NotificationChannel notificationChannel =
117                 new NotificationChannel(
118                         CHANNEL_ID,
119                         context.getString(R.string.bluetooth),
120                         NotificationManager.IMPORTANCE_HIGH);
121         nm.createNotificationChannel(notificationChannel);
122 
123         PendingIntent pairIntent =
124                 PendingIntent.getActivity(
125                         context,
126                         0,
127                         getKeyMissingDialogIntent(context, bluetoothDevice),
128                         PendingIntent.FLAG_ONE_SHOT
129                                 | PendingIntent.FLAG_UPDATE_CURRENT
130                                 | PendingIntent.FLAG_IMMUTABLE);
131 
132         NotificationCompat.Builder builder = new NotificationCompat.Builder(context,
133                 CHANNEL_ID)
134                 .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
135                 .setTicker(context.getString(R.string.bluetooth_notif_ticker))
136                 .setLocalOnly(true);
137         builder.setContentTitle(
138                         context.getString(
139                                 R.string.bluetooth_key_missing_title, bluetoothDevice.getAlias()))
140                 .setContentText(context.getString(R.string.bluetooth_key_missing_message))
141                 .setContentIntent(pairIntent)
142                 .setAutoCancel(true)
143                 .setDefaults(Notification.DEFAULT_SOUND)
144                 .setColor(
145                         context.getColor(
146                                 com.android.internal.R.color.system_notification_accent_color));
147 
148         nm.notify(NOTIFICATION_ID, builder.build());
149     }
150 }
151