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.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
20 
21 import android.Manifest;
22 import android.app.settings.SettingsEnums;
23 import android.bluetooth.BluetoothAdapter;
24 import android.bluetooth.BluetoothDevice;
25 import android.bluetooth.BluetoothDevicePicker;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.os.Bundle;
29 import android.os.UserManager;
30 import android.text.TextUtils;
31 import android.util.Log;
32 import android.view.Menu;
33 import android.view.MenuInflater;
34 
35 import androidx.annotation.VisibleForTesting;
36 
37 import com.android.settings.R;
38 import com.android.settings.password.PasswordUtils;
39 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
40 import com.android.settingslib.core.AbstractPreferenceController;
41 
42 import java.util.List;
43 
44 /**
45  * BluetoothSettings is the Settings screen for Bluetooth configuration and
46  * connection management.
47  */
48 public final class DevicePickerFragment extends DeviceListPreferenceFragment {
49     private static final String KEY_BT_DEVICE_LIST = "bt_device_list";
50     private static final String TAG = "DevicePickerFragment";
51 
52     @VisibleForTesting
53     BluetoothProgressCategory mAvailableDevicesCategory;
54     @VisibleForTesting
55     Context mContext;
56     @VisibleForTesting
57     String mLaunchPackage;
58     @VisibleForTesting
59     String mLaunchClass;
60     @VisibleForTesting
61     String mCallingAppPackageName;
62 
63     private boolean mNeedAuth;
64     private boolean mScanAllowed;
65 
DevicePickerFragment()66     public DevicePickerFragment() {
67         super(null /* Not tied to any user restrictions. */);
68     }
69 
70     @Override
initPreferencesFromPreferenceScreen()71     public void initPreferencesFromPreferenceScreen() {
72         Intent intent = getActivity().getIntent();
73         mNeedAuth = intent.getBooleanExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false);
74         setFilter(intent.getIntExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE,
75                 BluetoothDevicePicker.FILTER_TYPE_ALL));
76         mLaunchPackage = intent.getStringExtra(BluetoothDevicePicker.EXTRA_LAUNCH_PACKAGE);
77         mLaunchClass = intent.getStringExtra(BluetoothDevicePicker.EXTRA_LAUNCH_CLASS);
78         mAvailableDevicesCategory = (BluetoothProgressCategory) findPreference(KEY_BT_DEVICE_LIST);
79     }
80 
81     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)82     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
83         super.onCreateOptionsMenu(menu, inflater);
84     }
85 
86     @Override
getMetricsCategory()87     public int getMetricsCategory() {
88         return SettingsEnums.BLUETOOTH_DEVICE_PICKER;
89     }
90 
91     @Override
onCreate(Bundle savedInstanceState)92     public void onCreate(Bundle savedInstanceState) {
93         super.onCreate(savedInstanceState);
94         getActivity().setTitle(getString(R.string.device_picker));
95         UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
96         mScanAllowed = !um.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH);
97         mCallingAppPackageName = PasswordUtils.getCallingAppPackageName(
98                 getActivity().getActivityToken());
99         if (!TextUtils.equals(mCallingAppPackageName, mLaunchPackage)) {
100             Log.w(TAG, "sendDevicePickedIntent() launch package name is not equivalent to"
101                     + " calling package name!");
102         }
103         mContext = getContext();
104         setHasOptionsMenu(true);
105     }
106 
107     @Override
onStart()108     public void onStart() {
109         super.onStart();
110         mLocalManager.getCachedDeviceManager().clearNonBondedDevices();
111         removeAllDevices();
112         addCachedDevices();
113         mSelectedDevice = null;
114         if (mScanAllowed) {
115             enableScanning();
116             mAvailableDevicesCategory.setProgress(mBluetoothAdapter.isDiscovering());
117         }
118     }
119 
120     @Override
onStop()121     public void onStop() {
122         // Try disable scanning no matter what, no effect if enableScanning has not been called
123         disableScanning();
124         super.onStop();
125     }
126 
127     @Override
onDestroy()128     public void onDestroy() {
129         super.onDestroy();
130         /* Check if any device was selected, if no device selected
131          * send  ACTION_DEVICE_SELECTED with a null device, otherwise
132          * don't do anything */
133         if (mSelectedDevice == null) {
134             sendDevicePickedIntent(null);
135         }
136     }
137 
138     @Override
onDevicePreferenceClick(BluetoothDevicePreference btPreference)139     public void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
140         disableScanning();
141         LocalBluetoothPreferences.persistSelectedDeviceInPicker(
142                 getActivity(), mSelectedDevice.getAddress());
143         if ((btPreference.getCachedDevice().getBondState() ==
144                 BluetoothDevice.BOND_BONDED) || !mNeedAuth) {
145             sendDevicePickedIntent(mSelectedDevice);
146             finish();
147         } else {
148             super.onDevicePreferenceClick(btPreference);
149         }
150     }
151 
152     @Override
onScanningStateChanged(boolean started)153     public void onScanningStateChanged(boolean started) {
154         super.onScanningStateChanged(started);
155         started |= mScanEnabled;
156         mAvailableDevicesCategory.setProgress(started);
157     }
158 
onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState)159     public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice,
160             int bondState) {
161         BluetoothDevice device = cachedDevice.getDevice();
162         if (!device.equals(mSelectedDevice)) {
163             return;
164         }
165         if (bondState == BluetoothDevice.BOND_BONDED) {
166             sendDevicePickedIntent(device);
167             finish();
168         } else if (bondState == BluetoothDevice.BOND_NONE) {
169             enableScanning();
170         }
171     }
172 
173     @Override
initDevicePreference(BluetoothDevicePreference preference)174     protected void initDevicePreference(BluetoothDevicePreference preference) {
175         super.initDevicePreference(preference);
176         preference.setNeedNotifyHierarchyChanged(true);
177     }
178 
179     @Override
onBluetoothStateChanged(int bluetoothState)180     public void onBluetoothStateChanged(int bluetoothState) {
181         super.onBluetoothStateChanged(bluetoothState);
182 
183         if (bluetoothState == BluetoothAdapter.STATE_ON) {
184             enableScanning();
185         }
186     }
187 
188     @Override
getLogTag()189     protected String getLogTag() {
190         return TAG;
191     }
192 
193     @Override
getPreferenceScreenResId()194     protected int getPreferenceScreenResId() {
195         return R.xml.device_picker;
196     }
197 
198     @Override
createPreferenceControllers(Context context)199     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
200         return null;
201     }
202 
203     @Override
getDeviceListKey()204     public String getDeviceListKey() {
205         return KEY_BT_DEVICE_LIST;
206     }
207 
sendDevicePickedIntent(BluetoothDevice device)208     private void sendDevicePickedIntent(BluetoothDevice device) {
209         Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
210         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
211         if (mLaunchPackage != null && mLaunchClass != null) {
212             if (TextUtils.equals(mCallingAppPackageName, mLaunchPackage)) {
213                 intent.setClassName(mLaunchPackage, mLaunchClass);
214             }
215         }
216 
217         mContext.sendBroadcast(intent, Manifest.permission.BLUETOOTH_CONNECT);
218     }
219 }
220