1 /* 2 * Copyright (C) 2016 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.settingslib.bluetooth; 18 19 import android.bluetooth.BluetoothPbapClient; 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothClass; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothProfile; 24 import android.bluetooth.BluetoothUuid; 25 import android.content.Context; 26 import android.os.ParcelUuid; 27 import android.util.Log; 28 29 import com.android.settingslib.R; 30 31 import java.util.ArrayList; 32 import java.util.Collection; 33 import java.util.List; 34 35 final class PbapClientProfile implements LocalBluetoothProfile { 36 private static final String TAG = "PbapClientProfile"; 37 private static boolean V = false; 38 39 private BluetoothPbapClient mService; 40 private boolean mIsProfileReady; 41 42 private final LocalBluetoothAdapter mLocalAdapter; 43 private final CachedBluetoothDeviceManager mDeviceManager; 44 45 static final ParcelUuid[] SRC_UUIDS = { 46 BluetoothUuid.PBAP_PSE, 47 }; 48 49 static final String NAME = "PbapClient"; 50 private final LocalBluetoothProfileManager mProfileManager; 51 52 // Order of this profile in device profiles list 53 private static final int ORDINAL = 6; 54 55 // These callbacks run on the main thread. 56 private final class PbapClientServiceListener 57 implements BluetoothProfile.ServiceListener { 58 59 public void onServiceConnected(int profile, BluetoothProfile proxy) { 60 if (V) { 61 Log.d(TAG,"Bluetooth service connected"); 62 } 63 mService = (BluetoothPbapClient) proxy; 64 // We just bound to the service, so refresh the UI for any connected PBAP devices. 65 List<BluetoothDevice> deviceList = mService.getConnectedDevices(); 66 while (!deviceList.isEmpty()) { 67 BluetoothDevice nextDevice = deviceList.remove(0); 68 CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice); 69 // we may add a new device here, but generally this should not happen 70 if (device == null) { 71 Log.w(TAG, "PbapClientProfile found new device: " + nextDevice); 72 device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice); 73 } 74 device.onProfileStateChanged(PbapClientProfile.this, BluetoothProfile.STATE_CONNECTED); 75 device.refresh(); 76 } 77 mIsProfileReady = true; 78 } 79 80 public void onServiceDisconnected(int profile) { 81 if (V) { 82 Log.d(TAG,"Bluetooth service disconnected"); 83 } 84 mIsProfileReady = false; 85 } 86 } 87 88 private void refreshProfiles() { 89 Collection<CachedBluetoothDevice> cachedDevices = mDeviceManager.getCachedDevicesCopy(); 90 for (CachedBluetoothDevice device : cachedDevices) { 91 device.onUuidChanged(); 92 } 93 } 94 95 public boolean pbapClientExists() { 96 return (mService != null); 97 } 98 99 public boolean isProfileReady() { 100 return mIsProfileReady; 101 } 102 103 PbapClientProfile(Context context, LocalBluetoothAdapter adapter, 104 CachedBluetoothDeviceManager deviceManager, 105 LocalBluetoothProfileManager profileManager) { 106 mLocalAdapter = adapter; 107 mDeviceManager = deviceManager; 108 mProfileManager = profileManager; 109 mLocalAdapter.getProfileProxy(context, new PbapClientServiceListener(), 110 BluetoothProfile.PBAP_CLIENT); 111 } 112 113 public boolean isConnectable() { 114 return true; 115 } 116 117 public boolean isAutoConnectable() { 118 return true; 119 } 120 121 public List<BluetoothDevice> getConnectedDevices() { 122 if (mService == null) { 123 return new ArrayList<BluetoothDevice>(0); 124 } 125 return mService.getDevicesMatchingConnectionStates( 126 new int[] {BluetoothProfile.STATE_CONNECTED, 127 BluetoothProfile.STATE_CONNECTING, 128 BluetoothProfile.STATE_DISCONNECTING}); 129 } 130 131 public boolean connect(BluetoothDevice device) { 132 if (V) { 133 Log.d(TAG,"PBAPClientProfile got connect request"); 134 } 135 if (mService == null) { 136 return false; 137 } 138 List<BluetoothDevice> srcs = getConnectedDevices(); 139 if (srcs != null) { 140 for (BluetoothDevice src : srcs) { 141 if (src.equals(device)) { 142 // Connect to same device, Ignore it 143 Log.d(TAG,"Ignoring Connect"); 144 return true; 145 } 146 } 147 for (BluetoothDevice src : srcs) { 148 mService.disconnect(device); 149 } 150 } 151 Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress()); 152 153 return mService.connect(device); 154 } 155 156 public boolean disconnect(BluetoothDevice device) { 157 if (V) { 158 Log.d(TAG,"PBAPClientProfile got disconnect request"); 159 } 160 if (mService == null) { 161 return false; 162 } 163 return mService.disconnect(device); 164 } 165 166 public int getConnectionStatus(BluetoothDevice device) { 167 if (mService == null) { 168 return BluetoothProfile.STATE_DISCONNECTED; 169 } 170 return mService.getConnectionState(device); 171 } 172 173 public boolean isPreferred(BluetoothDevice device) { 174 if (mService == null) { 175 return false; 176 } 177 return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF; 178 } 179 180 public int getPreferred(BluetoothDevice device) { 181 if (mService == null) { 182 return BluetoothProfile.PRIORITY_OFF; 183 } 184 return mService.getPriority(device); 185 } 186 187 public void setPreferred(BluetoothDevice device, boolean preferred) { 188 if (mService == null) { 189 return; 190 } 191 if (preferred) { 192 if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) { 193 mService.setPriority(device, BluetoothProfile.PRIORITY_ON); 194 } 195 } else { 196 mService.setPriority(device, BluetoothProfile.PRIORITY_OFF); 197 } 198 } 199 200 public String toString() { 201 return NAME; 202 } 203 204 public int getOrdinal() { 205 return ORDINAL; 206 } 207 208 public int getNameResource(BluetoothDevice device) { 209 // we need to have same string in UI as the server side. 210 return R.string.bluetooth_profile_pbap; 211 } 212 213 public int getSummaryResourceForDevice(BluetoothDevice device) { 214 return R.string.bluetooth_profile_pbap_summary; 215 } 216 217 public int getDrawableResource(BluetoothClass btClass) { 218 return R.drawable.ic_bt_cellphone; 219 } 220 221 protected void finalize() { 222 if (V) { 223 Log.d(TAG, "finalize()"); 224 } 225 if (mService != null) { 226 try { 227 BluetoothAdapter.getDefaultAdapter().closeProfileProxy( 228 BluetoothProfile.PBAP_CLIENT,mService); 229 mService = null; 230 } catch (Throwable t) { 231 Log.w(TAG, "Error cleaning up PBAP Client proxy", t); 232 } 233 } 234 } 235 } 236