From: Marie Janssen Date: Wed, 15 Feb 2017 02:49:17 +0000 (-0800) Subject: Bluetooth: service for pairing notification X-Git-Tag: android-x86-8.1-r1~475^2~11^2~10^2^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=d06d83c531a9f347494c4a7de0582638a8196b4f;p=android-x86%2Fpackages-apps-Settings.git Bluetooth: service for pairing notification Use a foreground service to show the notification, separating dialog creation from the receiver. Test: pair request from remote device while the screen is off Bug: 35234069 Change-Id: Ia3be3e8efdacf465e095af5a498ef5eb0fca83cd --- diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 7e4267bbc4..62faaa07b5 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2091,11 +2091,11 @@ android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert"> + + - - diff --git a/src/com/android/settings/bluetooth/BluetoothPairingRequest.java b/src/com/android/settings/bluetooth/BluetoothPairingRequest.java index 1c36bca73e..96aace9eac 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingRequest.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingRequest.java @@ -16,110 +16,47 @@ package com.android.settings.bluetooth; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.os.PowerManager; -import android.text.TextUtils; - -import com.android.settings.R; +import android.os.UserHandle; /** * BluetoothPairingRequest is a receiver for any Bluetooth pairing request. It * checks if the Bluetooth Settings is currently visible and brings up the PIN, the passkey or a - * confirmation entry dialog. Otherwise it puts a Notification in the status bar, which can - * be clicked to bring up the Pairing entry dialog. + * confirmation entry dialog. Otherwise it starts the BluetoothPairingService which + * starts a notification in the status bar that can be clicked to bring up the same dialog. */ public final class BluetoothPairingRequest extends BroadcastReceiver { - private static final int NOTIFICATION_ID = android.R.drawable.stat_sys_data_bluetooth; - - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) { - // convert broadcast intent into activity intent (same action string) - BluetoothDevice device = - intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, - BluetoothDevice.ERROR); - Intent pairingIntent = new Intent(); - pairingIntent.setClass(context, BluetoothPairingDialog.class); - pairingIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); - pairingIntent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, type); - if (type == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION || - type == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY || - type == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) { - int pairingKey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, - BluetoothDevice.ERROR); - pairingIntent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pairingKey); - } - pairingIntent.setAction(BluetoothDevice.ACTION_PAIRING_REQUEST); - pairingIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - PowerManager powerManager = - (PowerManager)context.getSystemService(Context.POWER_SERVICE); - String deviceAddress = device != null ? device.getAddress() : null; - String deviceName = device != null ? device.getName() : null; - boolean shouldShowDialog= LocalBluetoothPreferences.shouldShowDialogInForeground( - context, deviceAddress, deviceName); - if (powerManager.isInteractive() && shouldShowDialog) { - // Since the screen is on and the BT-related activity is in the foreground, - // just open the dialog - context.startActivity(pairingIntent); - } else { - // Put up a notification that leads to the dialog - Resources res = context.getResources(); - Notification.Builder builder = new Notification.Builder(context) - .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth) - .setTicker(res.getString(R.string.bluetooth_notif_ticker)); - - PendingIntent pending = PendingIntent.getActivity(context, 0, - pairingIntent, PendingIntent.FLAG_ONE_SHOT); - - String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME); - if (TextUtils.isEmpty(name)) { - name = device != null ? device.getAliasName() : - context.getString(android.R.string.unknownName); - } - - builder.setContentTitle(res.getString(R.string.bluetooth_notif_title)) - .setContentText(res.getString(R.string.bluetooth_notif_message, name)) - .setContentIntent(pending) - .setAutoCancel(true) - .setDefaults(Notification.DEFAULT_SOUND) - .setColor(context.getColor( - com.android.internal.R.color.system_notification_accent_color)); - - NotificationManager manager = (NotificationManager) - context.getSystemService(Context.NOTIFICATION_SERVICE); - manager.notify(NOTIFICATION_ID, builder.getNotification()); - } - - } else if (action.equals(BluetoothDevice.ACTION_PAIRING_CANCEL)) { - - // Remove the notification - NotificationManager manager = (NotificationManager) context - .getSystemService(Context.NOTIFICATION_SERVICE); - manager.cancel(NOTIFICATION_ID); - - } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { - int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, - BluetoothDevice.ERROR); - int oldState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, - BluetoothDevice.ERROR); - if((oldState == BluetoothDevice.BOND_BONDING) && - (bondState == BluetoothDevice.BOND_NONE)) { - // Remove the notification - NotificationManager manager = (NotificationManager) context - .getSystemService(Context.NOTIFICATION_SERVICE); - manager.cancel(NOTIFICATION_ID); - } - } + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (!action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) { + return; + } + // convert broadcast intent into activity intent (same action string) + Intent pairingIntent = BluetoothPairingService.getPairingDialogIntent(context, intent); + + PowerManager powerManager = + (PowerManager)context.getSystemService(Context.POWER_SERVICE); + BluetoothDevice device = + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + String deviceAddress = device != null ? device.getAddress() : null; + String deviceName = device != null ? device.getName() : null; + boolean shouldShowDialog = LocalBluetoothPreferences.shouldShowDialogInForeground( + context, deviceAddress, deviceName); + if (powerManager.isInteractive() && shouldShowDialog) { + // Since the screen is on and the BT-related activity is in the foreground, + // just open the dialog + context.startActivity(pairingIntent); + } else { + // Put up a notification that leads to the dialog + intent.setClass(context, BluetoothPairingService.class); + context.startServiceAsUser(intent, UserHandle.CURRENT); } + } } diff --git a/src/com/android/settings/bluetooth/BluetoothPairingService.java b/src/com/android/settings/bluetooth/BluetoothPairingService.java new file mode 100644 index 0000000000..a24a3f04bd --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothPairingService.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.bluetooth; + +import android.app.Notification; +import android.app.PendingIntent; +import android.app.Service; +import android.bluetooth.BluetoothDevice; +import android.content.IntentFilter; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.IBinder; +import android.text.TextUtils; +import android.util.Log; + +import com.android.settings.R; + +/** + * BluetoothPairingService shows a notification if there is a pending bond request + * which can launch the appropriate pairing dialog when tapped. + */ +public final class BluetoothPairingService extends Service { + + private static final int NOTIFICATION_ID = android.R.drawable.stat_sys_data_bluetooth; + + private static final String TAG = "BluetoothPairingService"; + + private BluetoothDevice mDevice; + + public static Intent getPairingDialogIntent(Context context, Intent intent) { + + BluetoothDevice device = + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, + BluetoothDevice.ERROR); + Intent pairingIntent = new Intent(); + pairingIntent.setClass(context, BluetoothPairingDialog.class); + pairingIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + pairingIntent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, type); + if (type == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION || + type == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY || + type == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) { + int pairingKey = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, + BluetoothDevice.ERROR); + pairingIntent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pairingKey); + } + pairingIntent.setAction(BluetoothDevice.ACTION_PAIRING_REQUEST); + pairingIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return pairingIntent; + } + + private final BroadcastReceiver mCancelReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) { + int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, + BluetoothDevice.ERROR); + if ((bondState != BluetoothDevice.BOND_NONE) && (bondState != BluetoothDevice.BOND_BONDED)) { + return; + } + Log.d(TAG, "Dismiss pairing for " + mDevice.getAddress() + " (" + mDevice.getName() + "), BondState: " + bondState); + } else { + Log.d(TAG, "Dismiss pairing for " + mDevice.getAddress() + " (" + mDevice.getName() + "), Cancelled."); + } + stopForeground(true); + } + }; + + @Override + public void onCreate() { + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Resources res = getResources(); + Notification.Builder builder = new Notification.Builder(this) + .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth) + .setTicker(res.getString(R.string.bluetooth_notif_ticker)); + + PendingIntent pending = PendingIntent.getActivity(this, 0, + getPairingDialogIntent(this, intent), PendingIntent.FLAG_ONE_SHOT); + + mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + + String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME); + if (TextUtils.isEmpty(name)) { + BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + name = device != null ? device.getAliasName() : getString(android.R.string.unknownName); + } + + Log.d(TAG, "Show pairing notification for " + mDevice.getAddress() + " (" + name + ")"); + + builder.setContentTitle(res.getString(R.string.bluetooth_notif_title)) + .setContentText(res.getString(R.string.bluetooth_notif_message, name)) + .setContentIntent(pending) + .setDefaults(Notification.DEFAULT_SOUND) + .setColor(getColor(com.android.internal.R.color.system_notification_accent_color)); + + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + filter.addAction(BluetoothDevice.ACTION_PAIRING_CANCEL); + registerReceiver(mCancelReceiver, filter); + + startForeground(NOTIFICATION_ID, builder.getNotification()); + return START_STICKY; + } + + @Override + public void onDestroy() { + unregisterReceiver(mCancelReceiver); + stopForeground(true); + } + + @Override + public IBinder onBind(Intent intent) { + // No binding. + return null; + } + +}