1 /* 2 * Copyright (C) 2011 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 static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; 20 21 import android.bluetooth.BluetoothDevice; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.os.Bundle; 29 import android.telephony.SubscriptionInfo; 30 import android.telephony.SubscriptionManager; 31 import android.text.TextUtils; 32 import android.util.Log; 33 import android.view.View; 34 import android.widget.Button; 35 import android.widget.TextView; 36 37 import androidx.preference.Preference; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.internal.app.AlertActivity; 41 import com.android.internal.app.AlertController; 42 import com.android.settings.R; 43 import com.android.settings.network.SubscriptionUtil; 44 45 import java.util.List; 46 47 /** 48 * BluetoothPermissionActivity shows a dialog for accepting incoming 49 * profile connection request from untrusted devices. 50 * It is also used to show a dialogue for accepting incoming phonebook 51 * read request. The request could be initiated by PBAP PCE or by HF AT+CPBR. 52 */ 53 public class BluetoothPermissionActivity extends AlertActivity implements 54 DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener { 55 private static final String TAG = "BluetoothPermissionActivity"; 56 private static final boolean DEBUG = Utils.D; 57 58 private View mView; 59 private TextView messageView; 60 private Button mOkButton; 61 private BluetoothDevice mDevice; 62 63 private int mRequestType = 0; 64 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 65 @Override 66 public void onReceive(Context context, Intent intent) { 67 String action = intent.getAction(); 68 if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)) { 69 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 70 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 71 if (requestType != mRequestType) return; 72 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 73 if (mDevice.equals(device)) dismissDialog(); 74 } 75 } 76 }; 77 private boolean mReceiverRegistered = false; 78 dismissDialog()79 private void dismissDialog() { 80 this.dismiss(); 81 } 82 83 @Override onCreate(Bundle savedInstanceState)84 protected void onCreate(Bundle savedInstanceState) { 85 super.onCreate(savedInstanceState); 86 87 getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); 88 Intent i = getIntent(); 89 String action = i.getAction(); 90 if (!action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST)) { 91 Log.e(TAG, "Error: this activity may be started only with intent " 92 + "ACTION_CONNECTION_ACCESS_REQUEST"); 93 finish(); 94 return; 95 } 96 97 mDevice = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 98 mRequestType = i.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 99 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 100 101 if(DEBUG) Log.i(TAG, "onCreate() Request type: " + mRequestType); 102 103 if (mRequestType == BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION) { 104 showDialog(getString(R.string.bluetooth_connect_access_dialog_title), mRequestType); 105 } else if (mRequestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { 106 showDialog(getString(R.string.bluetooth_phonebook_access_dialog_title), mRequestType); 107 } else if (mRequestType == BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS) { 108 showDialog(getString(R.string.bluetooth_message_access_dialog_title), mRequestType); 109 } else if (mRequestType == BluetoothDevice.REQUEST_TYPE_SIM_ACCESS) { 110 showDialog(getString(R.string.bluetooth_sim_card_access_dialog_title), mRequestType); 111 } 112 else { 113 Log.e(TAG, "Error: bad request type: " + mRequestType); 114 finish(); 115 return; 116 } 117 registerReceiver(mReceiver, 118 new IntentFilter(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL)); 119 mReceiverRegistered = true; 120 } 121 122 showDialog(String title, int requestType)123 private void showDialog(String title, int requestType) 124 { 125 final AlertController.AlertParams p = mAlertParams; 126 p.mTitle = title; 127 if(DEBUG) Log.i(TAG, "showDialog() Request type: " + mRequestType + " this: " + this); 128 switch(requestType) 129 { 130 case BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION: 131 p.mView = createConnectionDialogView(); 132 break; 133 case BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS: 134 p.mView = createPhonebookDialogView(); 135 break; 136 case BluetoothDevice.REQUEST_TYPE_MESSAGE_ACCESS: 137 p.mView = createMapDialogView(); 138 break; 139 case BluetoothDevice.REQUEST_TYPE_SIM_ACCESS: 140 p.mView = createSapDialogView(); 141 break; 142 } 143 p.mPositiveButtonText = getString( 144 requestType == BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION 145 ? R.string.bluetooth_connect_access_dialog_positive : R.string.allow); 146 p.mPositiveButtonListener = this; 147 p.mNegativeButtonText = getString( 148 requestType == BluetoothDevice.REQUEST_TYPE_PROFILE_CONNECTION 149 ? R.string.bluetooth_connect_access_dialog_negative 150 : R.string.request_manage_bluetooth_permission_dont_allow); 151 p.mNegativeButtonListener = this; 152 mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); 153 setupAlert(); 154 155 } 156 @Override onBackPressed()157 public void onBackPressed() { 158 /*we need an answer so ignore back button presses during auth */ 159 if(DEBUG) Log.i(TAG, "Back button pressed! ignoring"); 160 } 161 162 // TODO(edjee): createConnectionDialogView, createPhonebookDialogView and createMapDialogView 163 // are similar. Refactor them into one method. createConnectionDialogView()164 private View createConnectionDialogView() { 165 String mRemoteName = Utils.createRemoteName(this, mDevice); 166 mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null); 167 messageView = (TextView)mView.findViewById(R.id.message); 168 messageView.setText(getString(R.string.bluetooth_connect_access_dialog_content, 169 mRemoteName, mRemoteName)); 170 return mView; 171 } 172 createPhonebookDialogView()173 private View createPhonebookDialogView() { 174 String mRemoteName = Utils.createRemoteName(this, mDevice); 175 mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null); 176 messageView = (TextView)mView.findViewById(R.id.message); 177 messageView.setText(getString(R.string.bluetooth_phonebook_access_dialog_content, 178 mRemoteName, mRemoteName)); 179 return mView; 180 } 181 createMapDialogView()182 private View createMapDialogView() { 183 String mRemoteName = Utils.createRemoteName(this, mDevice); 184 mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null); 185 messageView = (TextView)mView.findViewById(R.id.message); 186 messageView.setText(getString(R.string.bluetooth_message_access_dialog_content, 187 mRemoteName, mRemoteName)); 188 return mView; 189 } 190 createSapDialogView()191 private View createSapDialogView() { 192 String mRemoteName = Utils.createRemoteName(this, mDevice); 193 mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null); 194 messageView = (TextView)mView.findViewById(R.id.message); 195 messageView.setText(getString(R.string.bluetooth_sim_card_access_dialog_content, 196 mRemoteName, mRemoteName, getAnyPhoneNumberFromSubscriptions())); 197 return mView; 198 } 199 onPositive()200 private void onPositive() { 201 if (DEBUG) Log.d(TAG, "onPositive"); 202 sendReplyIntentToReceiver(true, true); 203 finish(); 204 } 205 onNegative()206 private void onNegative() { 207 if (DEBUG) Log.d(TAG, "onNegative"); 208 sendReplyIntentToReceiver(false, true); 209 } 210 211 @VisibleForTesting sendReplyIntentToReceiver(final boolean allowed, final boolean always)212 void sendReplyIntentToReceiver(final boolean allowed, final boolean always) { 213 String bluetoothName; 214 try { 215 bluetoothName = Utils.findBluetoothPackageName(this); 216 } catch (NameNotFoundException e) { 217 Log.e(TAG, "Failed to find bluetooth package name", e); 218 return; 219 } 220 221 Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 222 223 if (DEBUG) { 224 Log.i(TAG, "sendReplyIntentToReceiver() Request type: " + mRequestType 225 + " mReturnPackage"); 226 } 227 228 intent.setPackage(bluetoothName); 229 intent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT, 230 allowed ? BluetoothDevice.CONNECTION_ACCESS_YES 231 : BluetoothDevice.CONNECTION_ACCESS_NO); 232 intent.putExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, always); 233 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 234 intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, mRequestType); 235 sendBroadcast(intent, android.Manifest.permission.BLUETOOTH_CONNECT); 236 } 237 onClick(DialogInterface dialog, int which)238 public void onClick(DialogInterface dialog, int which) { 239 switch (which) { 240 case DialogInterface.BUTTON_POSITIVE: 241 onPositive(); 242 break; 243 244 case DialogInterface.BUTTON_NEGATIVE: 245 onNegative(); 246 break; 247 default: 248 break; 249 } 250 } 251 252 @Override onDestroy()253 protected void onDestroy() { 254 super.onDestroy(); 255 if (mReceiverRegistered) { 256 unregisterReceiver(mReceiver); 257 mReceiverRegistered = false; 258 } 259 } 260 onPreferenceChange(Preference preference, Object newValue)261 public boolean onPreferenceChange(Preference preference, Object newValue) { 262 return true; 263 } 264 265 // find any phone number available from active subscriptions getAnyPhoneNumberFromSubscriptions()266 String getAnyPhoneNumberFromSubscriptions() { 267 SubscriptionManager sm = getSystemService(SubscriptionManager.class); 268 List<SubscriptionInfo> subs = SubscriptionUtil.getActiveSubscriptions(sm); 269 if ((subs == null) || (subs.size() == 0)) { 270 return ""; 271 } 272 return subs.stream() 273 .map(subinfo -> SubscriptionUtil.getFormattedPhoneNumber(this, subinfo)) 274 .filter(phoneNumber -> !TextUtils.isEmpty(phoneNumber)) 275 .findAny().orElse(""); 276 } 277 } 278