/* * 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 static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import androidx.preference.Preference; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.AlertActivity; import com.android.internal.app.AlertController; import com.android.settings.R; import com.android.settings.network.SubscriptionUtil; import java.util.List; /** * BluetoothPermissionActivity shows a dialog for accepting incoming * profile connection request from untrusted devices. * It is also used to show a dialogue for accepting incoming phonebook * read request. The request could be initiated by PBAP PCE or by HF AT+CPBR. */ public class BluetoothPermissionActivity extends AlertActivity implements DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener { private static final String TAG = "BluetoothPermissionActivity"; private static final boolean DEBUG = Utils.D; private View mView; private TextView messageView; private Button mOkButton; private BluetoothDevice mDevice; private int mRequestType = 0; private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { 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); if (requestType != mRequestType) return; BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (mDevice.equals(device)) dismissDialog(); } } }; private boolean mReceiverRegistered = false; private void dismissDialog() { this.dismiss(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); Intent i = getIntent(); String action = i.getAction(); if (!action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) { Log.e(TAG, "Error: this activity may be started only with intent " + "ACTION_CONNECTION_ACCESS_REQUEST"); finish(); return; } mDevice = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); mRequestType = i.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); if(DEBUG) Log.i(TAG, "onCreate() Request type: " + mRequestType); if (mRequestType == BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION) { showDialog(getString(R.string.bluetooth_connect_access_dialog_title), mRequestType); } else if (mRequestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { showDialog(getString(R.string.bluetooth_phonebook_access_dialog_title), mRequestType); } else if (mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) { showDialog(getString(R.string.bluetooth_message_access_dialog_title), mRequestType); } else if (mRequestType == BluetoothDevice.REQUEST_TYPE_SIM_ACCESS) { showDialog(getString(R.string.bluetooth_sim_card_access_dialog_title), mRequestType); } else { Log.e(TAG, "Error: bad request type: " + mRequestType); finish(); return; } registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)); mReceiverRegistered = true; } private void showDialog(String title, int requestType) { final AlertController.AlertParams p = mAlertParams; p.mTitle = title; if(DEBUG) Log.i(TAG, "showDialog() Request type: " + mRequestType + " this: " + this); switch(requestType) { case BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION: p.mView = createConnectionDialogView(); break; case BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS: p.mView = createPhonebookDialogView(); break; case BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS: p.mView = createMapDialogView(); break; case BluetoothDevice.REQUEST_TYPE_SIM_ACCESS: p.mView = createSapDialogView(); break; } p.mPositiveButtonText = getString( requestType == BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION ? R.string.bluetooth_connect_access_dialog_positive : R.string.allow); p.mPositiveButtonListener = this; p.mNegativeButtonText = getString( requestType == BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION ? R.string.bluetooth_connect_access_dialog_negative : R.string.request_manage_bluetooth_permission_dont_allow); p.mNegativeButtonListener = this; mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); setupAlert(); } @Override public void onBackPressed() { /*we need an answer so ignore back button presses during auth */ if(DEBUG) Log.i(TAG, "Back button pressed! ignoring"); } // TODO(edjee): createConnectionDialogView, createPhonebookDialogView and createMapDialogView // are similar. Refactor them into one method. private View createConnectionDialogView() { String mRemoteName = Utils.createRemoteName(this, mDevice); mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null); messageView = (TextView)mView.findViewById(R.id.message); messageView.setText(getString(R.string.bluetooth_connect_access_dialog_content, mRemoteName, mRemoteName)); return mView; } private View createPhonebookDialogView() { String mRemoteName = Utils.createRemoteName(this, mDevice); mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null); messageView = (TextView)mView.findViewById(R.id.message); messageView.setText(getString(R.string.bluetooth_phonebook_access_dialog_content, mRemoteName, mRemoteName)); return mView; } private View createMapDialogView() { String mRemoteName = Utils.createRemoteName(this, mDevice); mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null); messageView = (TextView)mView.findViewById(R.id.message); messageView.setText(getString(R.string.bluetooth_message_access_dialog_content, mRemoteName, mRemoteName)); return mView; } private View createSapDialogView() { String mRemoteName = Utils.createRemoteName(this, mDevice); mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null); messageView = (TextView)mView.findViewById(R.id.message); messageView.setText(getString(R.string.bluetooth_sim_card_access_dialog_content, mRemoteName, mRemoteName, getAnyPhoneNumberFromSubscriptions())); return mView; } private void onPositive() { if (DEBUG) Log.d(TAG, "onPositive"); sendReplyIntentToReceiver(true, true); finish(); } private void onNegative() { if (DEBUG) Log.d(TAG, "onNegative"); sendReplyIntentToReceiver(false, true); } @VisibleForTesting void sendReplyIntentToReceiver(final boolean allowed, final boolean always) { String bluetoothName; try { bluetoothName = Utils.findBluetoothPackageName(this); } catch (NameNotFoundException e) { Log.e(TAG, "Failed to find bluetooth package name", e); return; } Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); if (DEBUG) { Log.i(TAG, "sendReplyIntentToReceiver() Request type: " + mRequestType + " mReturnPackage"); } intent.setPackage(bluetoothName); intent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 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_CONNECT); } public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: onPositive(); break; case DialogInterface.BUTTON_NEGATIVE: onNegative(); break; default: break; } } @Override protected void onDestroy() { super.onDestroy(); if (mReceiverRegistered) { unregisterReceiver(mReceiver); mReceiverRegistered = false; } } public boolean onPreferenceChange(Preference preference, Object newValue) { return true; } // find any phone number available from active subscriptions String getAnyPhoneNumberFromSubscriptions() { SubscriptionManager sm = getSystemService(SubscriptionManager.class); List subs = SubscriptionUtil.getActiveSubscriptions(sm); if ((subs == null) || (subs.size() == 0)) { return ""; } return subs.stream() .map(subinfo -> SubscriptionUtil.getFormattedPhoneNumber(this, subinfo)) .filter(phoneNumber -> !TextUtils.isEmpty(phoneNumber)) .findAny().orElse(""); } }