Files
app_Settings/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
Hugh Chen 9d00364da4 Fix bluetooth settings will broadcast to anywhere when some cases
BluetoothPermissionActivity and DevicePickerFragment will send
broadcast to return the result to calling apps. As this broadcast
intent is from Settings with uid 1000, it will be sent to any
protected BroadcastReceivers in the device. It can make an attacker
send broadcast to protected BroadcastReceivers like factory reset intent
(android/com.android.server.MasterClearReceiver) via
BluetoothPermissionActivity or DevicePickerFragment.

This CL will compare to calling package name with launch package name.
If they are not equal, the broadcast will not send to launch package name.

Bug: 179386960
Bug: 179386068
Test: make -j42 RunSettingsRoboTests and use test apk to manually test
to verify factory reset not started and no system UI notification.

Change-Id: Ib8a5acde663e875912d300dd4912c4e9416f02f1
2021-03-12 14:56:57 +08:00

198 lines
9.9 KiB
Java

/*
* Copyright (C) 2011 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.NotificationChannel;
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.os.PowerManager;
import android.os.UserManager;
import android.util.Log;
import com.android.settings.R;
/**
* BluetoothPermissionRequest is a receiver to receive Bluetooth connection
* access request.
*/
public final class BluetoothPermissionRequest extends BroadcastReceiver {
private static final String TAG = "BluetoothPermissionRequest";
private static final boolean DEBUG = Utils.V;
private static final int NOTIFICATION_ID = android.R.drawable.stat_sys_data_bluetooth;
private static final String NOTIFICATION_TAG_PBAP = "Phonebook Access" ;
private static final String NOTIFICATION_TAG_MAP = "Message Access";
private static final String NOTIFICATION_TAG_SAP = "SIM Access";
/* TODO: Consolidate this multiple defined but common channel ID with other
* handlers that declare and use the same channel ID */
private static final String BLUETOOTH_NOTIFICATION_CHANNEL =
"bluetooth_notification_channel";
private NotificationChannel mNotificationChannel = null;
Context mContext;
int mRequestType;
BluetoothDevice mDevice;
String mReturnPackage = null;
String mReturnClass = null;
@Override
public void onReceive(Context context, Intent intent) {
mContext = context;
String action = intent.getAction();
if (DEBUG) Log.d(TAG, "onReceive" + action);
if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) {
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
// skip the notification for managed profiles.
if (um.isManagedProfile()) {
if (DEBUG) Log.d(TAG, "Blocking notification for managed profile.");
return;
}
// convert broadcast intent into activity intent (same action string)
mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
mRequestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION);
mReturnPackage = intent.getStringExtra(BluetoothDevice.EXTRA_PACKAGE_NAME);
mReturnClass = intent.getStringExtra(BluetoothDevice.EXTRA_CLASS_NAME);
if (DEBUG) Log.d(TAG, "onReceive request type: " + mRequestType + " return "
+ mReturnPackage + "," + mReturnClass);
Intent connectionAccessIntent = new Intent(action);
connectionAccessIntent.setClass(context, BluetoothPermissionActivity.class);
// We use the FLAG_ACTIVITY_MULTIPLE_TASK since we can have multiple concurrent access
// requests.
connectionAccessIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
// This is needed to create two pending intents to the same activity. The value is not
// used in the activity.
connectionAccessIntent.setType(Integer.toString(mRequestType));
connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
mRequestType);
connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, mReturnPackage);
connectionAccessIntent.putExtra(BluetoothDevice.EXTRA_CLASS_NAME, mReturnClass);
String deviceAddress = mDevice != null ? mDevice.getAddress() : null;
String deviceName = mDevice != null ? mDevice.getName() : null;
String title = null;
String message = null;
PowerManager powerManager =
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (powerManager.isScreenOn()
&& LocalBluetoothPreferences.shouldShowDialogInForeground(
context, deviceAddress, deviceName)) {
context.startActivity(connectionAccessIntent);
} else {
// Put up a notification that leads to the dialog
// Create an intent triggered by clicking on the
// "Clear All Notifications" button
Intent deleteIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
deleteIntent.setPackage("com.android.bluetooth");
deleteIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
deleteIntent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
BluetoothDevice.CONNECTION_ACCESS_NO);
deleteIntent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, mRequestType);
String deviceAlias = Utils.createRemoteName(context, mDevice);
switch (mRequestType) {
case BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS:
title = context.getString(R.string.bluetooth_phonebook_request);
message = context.getString(
R.string.bluetooth_phonebook_access_notification_content);
break;
case BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS:
title = context.getString(R.string.bluetooth_map_request);
message = context.getString(
R.string.bluetooth_message_access_notification_content);
break;
case BluetoothDevice.REQUEST_TYPE_SIM_ACCESS:
title = context.getString(R.string.bluetooth_sap_request);
message = context.getString(R.string.bluetooth_sap_acceptance_dialog_text,
deviceAlias, deviceAlias);
break;
default:
title = context.getString(R.string.bluetooth_connection_permission_request);
message = context.getString(R.string.bluetooth_connection_dialog_text,
deviceAlias, deviceAlias);
break;
}
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationChannel == null) {
mNotificationChannel = new NotificationChannel(BLUETOOTH_NOTIFICATION_CHANNEL,
context.getString(R.string.bluetooth),
NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(mNotificationChannel);
}
Notification notification = new Notification.Builder(context,
BLUETOOTH_NOTIFICATION_CHANNEL)
.setContentTitle(title)
.setTicker(message)
.setContentText(message)
.setStyle(new Notification.BigTextStyle().bigText(message))
.setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
.setAutoCancel(true)
.setPriority(Notification.PRIORITY_MAX)
.setOnlyAlertOnce(false)
.setDefaults(Notification.DEFAULT_ALL)
.setContentIntent(PendingIntent.getActivity(context, 0,
connectionAccessIntent, PendingIntent.FLAG_IMMUTABLE))
.setDeleteIntent(PendingIntent.getBroadcast(context, 0, deleteIntent,
PendingIntent.FLAG_IMMUTABLE))
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color))
.setLocalOnly(true)
.build();
notification.flags |= Notification.FLAG_NO_CLEAR; // Cannot be set with the builder.
notificationManager.notify(getNotificationTag(mRequestType), NOTIFICATION_ID,
notification);
}
} else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)) {
// Remove the notification
NotificationManager manager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
mRequestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
manager.cancel(getNotificationTag(mRequestType), NOTIFICATION_ID);
}
}
private String getNotificationTag(int requestType) {
if(requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
return NOTIFICATION_TAG_PBAP;
} else if(mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) {
return NOTIFICATION_TAG_MAP;
} else if(mRequestType == BluetoothDevice.REQUEST_TYPE_SIM_ACCESS) {
return NOTIFICATION_TAG_SAP;
}
return null;
}
}