import android.preference.Preference;
import android.util.Log;
import android.view.View;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.Button;
-import android.widget.CompoundButton.OnCheckedChangeListener;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
String action = intent.getAction();
if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)) {
int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
- BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
+ BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
if (requestType != mRequestType) return;
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (mDevice.equals(device)) dismissDialog();
private void onPositive() {
if (DEBUG) Log.d(TAG, "onPositive");
- savePermissionChoice(mRequestType, CachedBluetoothDevice.ACCESS_ALLOWED);
- // TODO(edjee): Now that we always save the user's choice,
- // we can get rid of BluetoothDevice#EXTRA_ALWAYS_ALLOWED.
- sendIntentToReceiver(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, true,
- BluetoothDevice.EXTRA_ALWAYS_ALLOWED, true);
+ sendReplyIntentToReceiver(true, true);
finish();
}
private void onNegative() {
if (DEBUG) Log.d(TAG, "onNegative");
- savePermissionChoice(mRequestType, CachedBluetoothDevice.ACCESS_REJECTED);
- sendIntentToReceiver(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, false,
- null, false // dummy value, no effect since last param is null
- );
- finish();
+
+ boolean always = true;
+ if (mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) {
+ LocalBluetoothManager bluetoothManager = LocalBluetoothManager.getInstance(this);
+ CachedBluetoothDeviceManager cachedDeviceManager =
+ bluetoothManager.getCachedDeviceManager();
+ CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(mDevice);
+ if (cachedDevice == null) {
+ cachedDevice = cachedDeviceManager.addDevice(bluetoothManager.getBluetoothAdapter(),
+ bluetoothManager.getProfileManager(),
+ mDevice);
+ }
+ always = cachedDevice.checkAndIncreaseMessageRejectionCount();
+ }
+
+ sendReplyIntentToReceiver(false, always);
}
- private void sendIntentToReceiver(final String intentName, final boolean allowed,
- final String extraName, final boolean extraValue) {
- Intent intent = new Intent(intentName);
+ private void sendReplyIntentToReceiver(final boolean allowed, final boolean always) {
+ Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
if (mReturnPackage != null && mReturnClass != null) {
intent.setClassName(mReturnPackage, mReturnClass);
}
- if(DEBUG) Log.i(TAG, "sendIntentToReceiver() Request type: " + mRequestType +
+ if (DEBUG) Log.i(TAG, "sendReplyIntentToReceiver() Request type: " + mRequestType +
" mReturnPackage" + mReturnPackage + " mReturnClass" + mReturnClass);
intent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
- allowed ? BluetoothDevice.CONNECTION_ACCESS_YES :
- BluetoothDevice.CONNECTION_ACCESS_NO);
-
- if (extraName != null) {
- intent.putExtra(extraName, extraValue);
- }
+ allowed ? BluetoothDevice.CONNECTION_ACCESS_YES
+ : BluetoothDevice.CONNECTION_ACCESS_NO);
+ intent.putExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, always);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, mRequestType);
sendBroadcast(intent, android.Manifest.permission.BLUETOOTH_ADMIN);
public boolean onPreferenceChange(Preference preference, Object newValue) {
return true;
}
-
- private void savePermissionChoice(int permissionType, int permissionChoice) {
- LocalBluetoothManager bluetoothManager = LocalBluetoothManager.getInstance(this);
- CachedBluetoothDeviceManager cachedDeviceManager =
- bluetoothManager.getCachedDeviceManager();
- CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(mDevice);
- if (DEBUG) Log.d(TAG, "savePermissionChoice permissionType: " + permissionType);
- if (cachedDevice == null ) {
- cachedDevice = cachedDeviceManager.addDevice(bluetoothManager.getBluetoothAdapter(),
- bluetoothManager.getProfileManager(),
- mDevice);
- }
- if(permissionType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS){
- cachedDevice.setPhonebookPermissionChoice(permissionChoice);
- }else if (permissionType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS){
- cachedDevice.setMessagePermissionChoice(permissionChoice);
- }
- }
-
}
if (DEBUG) Log.d(TAG, "onReceive request type: " + mRequestType + " return "
+ mReturnPackage + "," + mReturnClass);
- // Check if user had made decisions on accepting or rejecting the phonebook access
- // request. If there is, reply the request and return, no need to start permission
- // activity dialog or notification.
+ // Even if the user has already made the choice, Bluetooth still may not know that if
+ // the user preference data have not been migrated from Settings app's shared
+ // preferences to Bluetooth app's. In that case, Bluetooth app broadcasts an
+ // ACTION_CONNECTION_ACCESS_REQUEST intent to ask to Settings app.
+ //
+ // If that happens, 'checkUserChoice()' here will do migration because it finds or
+ // creates a 'CachedBluetoothDevice' object for the device.
+ //
+ // After migration is done, 'checkUserChoice()' replies to the request by sending an
+ // ACTION_CONNECTION_ACCESS_REPLY intent. And we don't need to start permission activity
+ // dialog or notification.
if (checkUserChoice()) {
return;
}
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);
- connectionAccessIntent.setType(Integer.toString(mRequestType)); /* This is needed to create two pending
- intents to the same activity.
- The value is not used in the activity. */
+ // 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);
PowerManager powerManager =
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
- if (powerManager.isScreenOn() &&
- LocalBluetoothPreferences.shouldShowDialogInForeground(context, deviceAddress) ) {
+ if (powerManager.isScreenOn()
+ && LocalBluetoothPreferences.shouldShowDialogInForeground(
+ context, deviceAddress)) {
context.startActivity(connectionAccessIntent);
} else {
// Put up a notification that leads to the dialog
switch (mRequestType) {
case BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS:
title = context.getString(R.string.bluetooth_phonebook_request);
- message = context.getString(R.string.bluetooth_pb_acceptance_dialog_text, deviceName, deviceName);
+ message = context.getString(R.string.bluetooth_pb_acceptance_dialog_text,
+ deviceName, deviceName);
break;
case BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS:
title = context.getString(R.string.bluetooth_map_request);
- message = context.getString(R.string.bluetooth_map_acceptance_dialog_text, deviceName, deviceName);
+ message = context.getString(R.string.bluetooth_map_acceptance_dialog_text,
+ deviceName, deviceName);
break;
default:
title = context.getString(R.string.bluetooth_connection_permission_request);
- message = context.getString(R.string.bluetooth_connection_dialog_text, deviceName, deviceName);
+ message = context.getString(R.string.bluetooth_connection_dialog_text,
+ deviceName, deviceName);
break;
}
Notification notification = new Notification.Builder(context)
com.android.internal.R.color.system_notification_accent_color))
.build();
- notification.flags |= Notification.FLAG_NO_CLEAR; /* cannot be set with the builder */
+ notification.flags |= Notification.FLAG_NO_CLEAR; // Cannot be set with the builder.
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.notify(getNotificationTag(mRequestType),NOTIFICATION_ID, notification);
+ notificationManager.notify(getNotificationTag(mRequestType), NOTIFICATION_ID,
+ notification);
}
} else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)) {
// Remove the notification
// ignore if it is something else than phonebook/message settings it wants us to remember
if (mRequestType != BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS
&& mRequestType != BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) {
- if (DEBUG) Log.d(TAG, "Unknown RequestType: " + mRequestType);
+ if (DEBUG) Log.d(TAG, "checkUserChoice(): Unknown RequestType " + mRequestType);
return processed;
}
CachedBluetoothDeviceManager cachedDeviceManager =
bluetoothManager.getCachedDeviceManager();
CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(mDevice);
-
if (cachedDevice == null) {
cachedDevice = cachedDeviceManager.addDevice(bluetoothManager.getBluetoothAdapter(),
bluetoothManager.getProfileManager(), mDevice);
}
- if(mRequestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
+ String intentName = BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY;
+ if (mRequestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) {
int phonebookPermission = cachedDevice.getPhonebookPermissionChoice();
if (phonebookPermission == CachedBluetoothDevice.ACCESS_UNKNOWN) {
- return processed;
- }
-
- String intentName = BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY;
- if (phonebookPermission == CachedBluetoothDevice.ACCESS_ALLOWED) {
- sendIntentToReceiver(intentName, true, BluetoothDevice.EXTRA_ALWAYS_ALLOWED, true);
+ // Leave 'processed' as false.
+ } else if (phonebookPermission == CachedBluetoothDevice.ACCESS_ALLOWED) {
+ sendReplyIntentToReceiver(true);
processed = true;
} else if (phonebookPermission == CachedBluetoothDevice.ACCESS_REJECTED) {
- sendIntentToReceiver(intentName, false,
- null, false ); // dummy value, no effect since previous param is null
+ sendReplyIntentToReceiver(false);
processed = true;
} else {
Log.e(TAG, "Bad phonebookPermission: " + phonebookPermission);
}
-
- } else if(mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) {
-
+ } else if (mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) {
int messagePermission = cachedDevice.getMessagePermissionChoice();
if (messagePermission == CachedBluetoothDevice.ACCESS_UNKNOWN) {
- return processed;
- }
-
- String intentName = BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY;
- if (messagePermission == CachedBluetoothDevice.ACCESS_ALLOWED) {
- sendIntentToReceiver(intentName, true, BluetoothDevice.EXTRA_ALWAYS_ALLOWED, true);
+ // Leave 'processed' as false.
+ } else if (messagePermission == CachedBluetoothDevice.ACCESS_ALLOWED) {
+ sendReplyIntentToReceiver(true);
processed = true;
} else if (messagePermission == CachedBluetoothDevice.ACCESS_REJECTED) {
- sendIntentToReceiver(intentName, false,
- null, false); // dummy value, no effect since previous param is null
+ sendReplyIntentToReceiver(false);
processed = true;
} else {
Log.e(TAG, "Bad messagePermission: " + messagePermission);
}
}
- if(DEBUG) Log.d(TAG,"checkUserChoice(): returning " + processed);
+ if (DEBUG) Log.d(TAG,"checkUserChoice(): returning " + processed);
return processed;
}
- private void sendIntentToReceiver(final String intentName, final boolean allowed,
- final String extraName, final boolean extraValue) {
- Intent intent = new Intent(intentName);
+ private void sendReplyIntentToReceiver(final boolean allowed) {
+ Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
if (mReturnPackage != null && mReturnClass != null) {
intent.setClassName(mReturnPackage, mReturnClass);
}
intent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
- allowed ? BluetoothDevice.CONNECTION_ACCESS_YES :
- BluetoothDevice.CONNECTION_ACCESS_NO);
-
- if (extraName != null) {
- intent.putExtra(extraName, extraValue);
- }
+ allowed ? BluetoothDevice.CONNECTION_ACCESS_YES
+ : BluetoothDevice.CONNECTION_ACCESS_NO);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, mRequestType);
mContext.sendBroadcast(intent, android.Manifest.permission.BLUETOOTH_ADMIN);
private int mMessagePermissionChoice;
- private int mMessageRejectedTimes;
+ private int mMessageRejectionCount;
private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
// User has rejected the connection and let Settings app remember the decision
public final static int ACCESS_REJECTED = 2;
- // how many times did User reject the connection to make the rejected persist.
- final static int PERSIST_REJECTED_TIMES_LIMIT = 2;
+ // How many times user should reject the connection to make the choice persist.
+ private final static int MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST = 2;
- private final static String PHONEBOOK_PREFS_NAME = "bluetooth_phonebook_permission";
- private final static String MESSAGE_PREFS_NAME = "bluetooth_message_permission";
- private final static String MESSAGE_REJECT_TIMES = "bluetooth_message_reject";
+ private final static String MESSAGE_REJECTION_COUNT_PREFS_NAME = "bluetooth_message_reject";
/**
* When we connect to multiple profiles, we only want to display a single
fetchName();
fetchBtClass();
updateProfiles();
- fetchPhonebookPermissionChoice();
- fetchMessagePermissionChoice();
- fetchMessageRejectTimes();
+ migratePhonebookPermissionChoice();
+ migrateMessagePermissionChoice();
+ fetchMessageRejectionCount();
mVisible = false;
dispatchAttributesChanged();
mConnectAfterPairing = false; // cancel auto-connect
setPhonebookPermissionChoice(ACCESS_UNKNOWN);
setMessagePermissionChoice(ACCESS_UNKNOWN);
- mMessageRejectedTimes = 0;
- saveMessageRejectTimes();
+ mMessageRejectionCount = 0;
+ saveMessageRejectionCount();
}
refresh();
}
int getPhonebookPermissionChoice() {
- return mPhonebookPermissionChoice;
+ int permission = mDevice.getPhonebookAccessPermission();
+ if (permission == BluetoothDevice.ACCESS_ALLOWED) {
+ return ACCESS_ALLOWED;
+ } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
+ return ACCESS_REJECTED;
+ }
+ return ACCESS_UNKNOWN;
}
void setPhonebookPermissionChoice(int permissionChoice) {
- mPhonebookPermissionChoice = permissionChoice;
-
- SharedPreferences.Editor editor =
- mContext.getSharedPreferences(PHONEBOOK_PREFS_NAME, Context.MODE_PRIVATE).edit();
- if (permissionChoice == ACCESS_UNKNOWN) {
- editor.remove(mDevice.getAddress());
- } else {
- editor.putInt(mDevice.getAddress(), permissionChoice);
+ int permission = BluetoothDevice.ACCESS_UNKNOWN;
+ if (permissionChoice == ACCESS_ALLOWED) {
+ permission = BluetoothDevice.ACCESS_ALLOWED;
+ } else if (permissionChoice == ACCESS_REJECTED) {
+ permission = BluetoothDevice.ACCESS_REJECTED;
}
- editor.commit();
+ mDevice.setPhonebookAccessPermission(permission);
}
- private void fetchPhonebookPermissionChoice() {
- SharedPreferences preference = mContext.getSharedPreferences(PHONEBOOK_PREFS_NAME,
- Context.MODE_PRIVATE);
- mPhonebookPermissionChoice = preference.getInt(mDevice.getAddress(),
- ACCESS_UNKNOWN);
+ // Migrates data from old data store (in Settings app's shared preferences) to new (in Bluetooth
+ // app's shared preferences).
+ private void migratePhonebookPermissionChoice() {
+ SharedPreferences preferences = mContext.getSharedPreferences(
+ "bluetooth_phonebook_permission", Context.MODE_PRIVATE);
+ if (!preferences.contains(mDevice.getAddress())) {
+ return;
+ }
+
+ if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
+ int oldPermission = preferences.getInt(mDevice.getAddress(), ACCESS_UNKNOWN);
+ if (oldPermission == ACCESS_ALLOWED) {
+ mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
+ } else if (oldPermission == ACCESS_REJECTED) {
+ mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
+ }
+ }
+
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.remove(mDevice.getAddress());
+ editor.commit();
}
int getMessagePermissionChoice() {
- return mMessagePermissionChoice;
+ int permission = mDevice.getMessageAccessPermission();
+ if (permission == BluetoothDevice.ACCESS_ALLOWED) {
+ return ACCESS_ALLOWED;
+ } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
+ return ACCESS_REJECTED;
+ }
+ return ACCESS_UNKNOWN;
}
void setMessagePermissionChoice(int permissionChoice) {
- // if user reject it, only save it when reject exceed limit.
- if (permissionChoice == ACCESS_REJECTED) {
- mMessageRejectedTimes++;
- saveMessageRejectTimes();
- if (mMessageRejectedTimes < PERSIST_REJECTED_TIMES_LIMIT) {
- return;
- }
+ int permission = BluetoothDevice.ACCESS_UNKNOWN;
+ if (permissionChoice == ACCESS_ALLOWED) {
+ permission = BluetoothDevice.ACCESS_ALLOWED;
+ } else if (permissionChoice == ACCESS_REJECTED) {
+ permission = BluetoothDevice.ACCESS_REJECTED;
}
+ mDevice.setMessageAccessPermission(permission);
+ }
- mMessagePermissionChoice = permissionChoice;
+ // Migrates data from old data store (in Settings app's shared preferences) to new (in Bluetooth
+ // app's shared preferences).
+ private void migrateMessagePermissionChoice() {
+ SharedPreferences preferences = mContext.getSharedPreferences(
+ "bluetooth_message_permission", Context.MODE_PRIVATE);
+ if (!preferences.contains(mDevice.getAddress())) {
+ return;
+ }
- SharedPreferences.Editor editor =
- mContext.getSharedPreferences(MESSAGE_PREFS_NAME, Context.MODE_PRIVATE).edit();
- if (permissionChoice == ACCESS_UNKNOWN) {
- editor.remove(mDevice.getAddress());
- } else {
- editor.putInt(mDevice.getAddress(), permissionChoice);
+ if (mDevice.getMessageAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
+ int oldPermission = preferences.getInt(mDevice.getAddress(), ACCESS_UNKNOWN);
+ if (oldPermission == ACCESS_ALLOWED) {
+ mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
+ } else if (oldPermission == ACCESS_REJECTED) {
+ mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
+ }
}
+
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.remove(mDevice.getAddress());
editor.commit();
}
- private void fetchMessagePermissionChoice() {
- SharedPreferences preference = mContext.getSharedPreferences(MESSAGE_PREFS_NAME,
- Context.MODE_PRIVATE);
- mMessagePermissionChoice = preference.getInt(mDevice.getAddress(),
- ACCESS_UNKNOWN);
+ /**
+ * @return Whether this rejection should persist.
+ */
+ boolean checkAndIncreaseMessageRejectionCount() {
+ if (mMessageRejectionCount < MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST) {
+ mMessageRejectionCount++;
+ saveMessageRejectionCount();
+ }
+ return mMessageRejectionCount >= MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST;
}
- private void fetchMessageRejectTimes() {
- SharedPreferences preference = mContext.getSharedPreferences(MESSAGE_REJECT_TIMES,
- Context.MODE_PRIVATE);
- mMessageRejectedTimes = preference.getInt(mDevice.getAddress(), 0);
+ private void fetchMessageRejectionCount() {
+ SharedPreferences preference = mContext.getSharedPreferences(
+ MESSAGE_REJECTION_COUNT_PREFS_NAME, Context.MODE_PRIVATE);
+ mMessageRejectionCount = preference.getInt(mDevice.getAddress(), 0);
}
- private void saveMessageRejectTimes() {
- SharedPreferences.Editor editor =
- mContext.getSharedPreferences(MESSAGE_REJECT_TIMES, Context.MODE_PRIVATE).edit();
- if (mMessageRejectedTimes == 0) {
+ private void saveMessageRejectionCount() {
+ SharedPreferences.Editor editor = mContext.getSharedPreferences(
+ MESSAGE_REJECTION_COUNT_PREFS_NAME, Context.MODE_PRIVATE).edit();
+ if (mMessageRejectionCount == 0) {
editor.remove(mDevice.getAddress());
} else {
- editor.putInt(mDevice.getAddress(), mMessageRejectedTimes);
+ editor.putInt(mDevice.getAddress(), mMessageRejectionCount);
}
editor.commit();
}
-
}