1 /*
2  * Copyright (C) 2021 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.car.dialer.bluetooth;
18 
19 import android.Manifest;
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothDevice;
22 import android.content.Context;
23 import android.content.pm.PackageManager;
24 import android.telecom.PhoneAccountHandle;
25 import android.telecom.TelecomManager;
26 import android.text.TextUtils;
27 
28 import androidx.annotation.NonNull;
29 import androidx.annotation.Nullable;
30 import androidx.core.app.ActivityCompat;
31 
32 import com.android.car.dialer.Constants;
33 
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.Set;
37 
38 import javax.inject.Inject;
39 import javax.inject.Singleton;
40 
41 import dagger.hilt.android.qualifiers.ApplicationContext;
42 
43 /** User phone account handle management. */
44 @Singleton
45 public class PhoneAccountManager {
46     private final Context mContext;
47     private final TelecomManager mTelecomManager;
48     private final BluetoothAdapter mBluetoothAdapter;
49 
50     @Inject
PhoneAccountManager( @pplicationContext Context context, TelecomManager telecomManager, @Nullable BluetoothAdapter bluetoothAdapter)51     public PhoneAccountManager(
52             @ApplicationContext Context context,
53             TelecomManager telecomManager,
54             @Nullable BluetoothAdapter bluetoothAdapter) {
55         mContext = context;
56         mTelecomManager = telecomManager;
57         mBluetoothAdapter = bluetoothAdapter;
58     }
59 
60     /**
61      * Sets the {@link PhoneAccountHandle} associated with the {@link BluetoothDevice} as the
62      * user selected outgoing phone account.
63      */
setUserSelectedOutgoingDevice(BluetoothDevice device)64     public void setUserSelectedOutgoingDevice(BluetoothDevice device) {
65         PhoneAccountHandle phoneAccountHandle = getMatchingPhoneAccount(device);
66         mTelecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccountHandle);
67     }
68 
69     /**
70      * Returns the {@link BluetoothDevice} for the given {@link PhoneAccountHandle} if the account
71      * is for hfp connection.
72      */
getMatchingDevice( @ullable PhoneAccountHandle phoneAccountHandle)73     public BluetoothDevice getMatchingDevice(
74             @Nullable PhoneAccountHandle phoneAccountHandle) {
75         if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_CONNECT)
76                 != PackageManager.PERMISSION_GRANTED) {
77             return null;
78         }
79         Set<BluetoothDevice> bondedDevices =
80                 mBluetoothAdapter == null ? null : mBluetoothAdapter.getBondedDevices();
81         if (bondedDevices == null) {
82             return null;
83         }
84         if (isHfpConnectionService(phoneAccountHandle)) {
85             for (BluetoothDevice bluetoothDevice : bondedDevices) {
86                 if (TextUtils.equals(bluetoothDevice.getAddress(), phoneAccountHandle.getId())) {
87                     return bluetoothDevice;
88                 }
89             }
90         }
91         return null;
92     }
93 
94     /** Returns the list of hfp {@link BluetoothDevice}s for current callable phone accounts. */
95     @NonNull
getHfpDeviceList()96     public List<BluetoothDevice> getHfpDeviceList() {
97         List<PhoneAccountHandle> phoneAccountHandles =
98                 mTelecomManager.getCallCapablePhoneAccounts(true);
99         List<BluetoothDevice> hfpDeviceList = new ArrayList<>();
100         for (PhoneAccountHandle phoneAccountHandle : phoneAccountHandles) {
101             BluetoothDevice bluetoothDevice = getMatchingDevice(phoneAccountHandle);
102             if (bluetoothDevice != null) {
103                 hfpDeviceList.add(bluetoothDevice);
104             }
105         }
106         return hfpDeviceList;
107     }
108 
109     /** Returns the {@link PhoneAccountHandle} for the given bluetooth device. */
getMatchingPhoneAccount(@ullable BluetoothDevice bluetoothDevice)110     public PhoneAccountHandle getMatchingPhoneAccount(@Nullable BluetoothDevice bluetoothDevice) {
111         if (bluetoothDevice == null) {
112             return null;
113         }
114 
115         List<PhoneAccountHandle> phoneAccountHandleList =
116                 mTelecomManager.getCallCapablePhoneAccounts(/* includeDisabledAccounts= */true);
117         for (PhoneAccountHandle phoneAccountHandle : phoneAccountHandleList) {
118             if (isHfpConnectionService(phoneAccountHandle)) {
119                 if (TextUtils.equals(phoneAccountHandle.getId(), bluetoothDevice.getAddress())) {
120                     return phoneAccountHandle;
121                 }
122             }
123         }
124         return null;
125     }
126 
127     /** Returns if the {@link PhoneAccountHandle} is from a hfp connection for bluetooth call. */
isHfpConnectionService(@ullable PhoneAccountHandle phoneAccountHandle)128     public boolean isHfpConnectionService(@Nullable PhoneAccountHandle phoneAccountHandle) {
129         return phoneAccountHandle != null
130                 && Constants.HFP_CLIENT_CONNECTION_SERVICE_CLASS_NAME.equals(
131                 phoneAccountHandle.getComponentName().getClassName());
132     }
133 }
134