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.util.Log;
31 import android.text.TextUtils;
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     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         addCachedDevices();
111         mSelectedDevice = null;
112         if (mScanAllowed) {
113             enableScanning();
114             mAvailableDevicesCategory.setProgress(mBluetoothAdapter.isDiscovering());
115         }
116     }
117 
118     @Override
onStop()119     public void onStop() {
120         // Try disable scanning no matter what, no effect if enableScanning has not been called
121         disableScanning();
122         super.onStop();
123     }
124 
125     @Override
onDestroy()126     public void onDestroy() {
127         super.onDestroy();
128         /* Check if any device was selected, if no device selected
129          * send  ACTION_DEVICE_SELECTED with a null device, otherwise
130          * don't do anything */
131         if (mSelectedDevice == null) {
132             sendDevicePickedIntent(null);
133         }
134     }
135 
136     @Override
onDevicePreferenceClick(BluetoothDevicePreference btPreference)137     void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
138         disableScanning();
139         LocalBluetoothPreferences.persistSelectedDeviceInPicker(
140                 getActivity(), mSelectedDevice.getAddress());
141         if ((btPreference.getCachedDevice().getBondState() ==
142                 BluetoothDevice.BOND_BONDED) || !mNeedAuth) {
143             sendDevicePickedIntent(mSelectedDevice);
144             finish();
145         } else {
146             super.onDevicePreferenceClick(btPreference);
147         }
148     }
149 
150     @Override
onScanningStateChanged(boolean started)151     public void onScanningStateChanged(boolean started) {
152         super.onScanningStateChanged(started);
153         started |= mScanEnabled;
154         mAvailableDevicesCategory.setProgress(started);
155     }
156 
onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState)157     public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice,
158             int bondState) {
159         BluetoothDevice device = cachedDevice.getDevice();
160         if (!device.equals(mSelectedDevice)) {
161             return;
162         }
163         if (bondState == BluetoothDevice.BOND_BONDED) {
164             sendDevicePickedIntent(device);
165             finish();
166         } else if (bondState == BluetoothDevice.BOND_NONE) {
167             enableScanning();
168         }
169     }
170 
171     @Override
initDevicePreference(BluetoothDevicePreference preference)172     protected void initDevicePreference(BluetoothDevicePreference preference) {
173         super.initDevicePreference(preference);
174         preference.setNeedNotifyHierarchyChanged(true);
175     }
176 
177     @Override
onBluetoothStateChanged(int bluetoothState)178     public void onBluetoothStateChanged(int bluetoothState) {
179         super.onBluetoothStateChanged(bluetoothState);
180 
181         if (bluetoothState == BluetoothAdapter.STATE_ON) {
182             enableScanning();
183         }
184     }
185 
186     @Override
getLogTag()187     protected String getLogTag() {
188         return TAG;
189     }
190 
191     @Override
getPreferenceScreenResId()192     protected int getPreferenceScreenResId() {
193         return R.xml.device_picker;
194     }
195 
196     @Override
createPreferenceControllers(Context context)197     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
198         return null;
199     }
200 
201     @Override
getDeviceListKey()202     public String getDeviceListKey() {
203         return KEY_BT_DEVICE_LIST;
204     }
205 
sendDevicePickedIntent(BluetoothDevice device)206     private void sendDevicePickedIntent(BluetoothDevice device) {
207         Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
208         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
209         if (mLaunchPackage != null && mLaunchClass != null) {
210             if (TextUtils.equals(mCallingAppPackageName, mLaunchPackage)) {
211                 intent.setClassName(mLaunchPackage, mLaunchClass);
212             }
213         }
214 
215         mContext.sendBroadcast(intent, Manifest.permission.BLUETOOTH_CONNECT);
216     }
217 }
218