PbapClientConnectionHandler.java revision a3d61e3a5d5a80a3eb1e0db76efe3dd1a1f7d337
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 */ 16package com.android.bluetooth.pbapclient; 17 18import android.accounts.Account; 19import android.accounts.AccountManager; 20import android.bluetooth.BluetoothAdapter; 21import android.bluetooth.BluetoothDevice; 22import android.bluetooth.BluetoothSocket; 23import android.bluetooth.BluetoothUuid; 24import android.content.Context; 25import android.os.Handler; 26import android.os.HandlerThread; 27import android.os.Looper; 28import android.os.Message; 29import android.os.Process; 30import android.provider.CallLog; 31import android.util.Log; 32 33import com.android.bluetooth.R; 34 35import java.io.IOException; 36 37import javax.obex.ClientSession; 38import javax.obex.HeaderSet; 39import javax.obex.ResponseCodes; 40 41/* Bluetooth/pbapclient/PbapClientConnectionHandler is responsible 42 * for connecting, disconnecting and downloading contacts from the 43 * PBAP PSE when commanded. It receives all direction from the 44 * controlling state machine. 45 */ 46class PbapClientConnectionHandler extends Handler { 47 static final String TAG = "PBAP PCE handler"; 48 static final boolean DBG = true; 49 static final int MSG_CONNECT = 1; 50 static final int MSG_DISCONNECT = 2; 51 static final int MSG_DOWNLOAD = 3; 52 53 // The following constants are pulled from the Bluetooth Phone Book Access Profile specification 54 // 1.1 55 private static final byte[] PBAP_TARGET = new byte[] { 56 0x79, 0x61, 0x35, (byte) 0xf0, (byte) 0xf0, (byte) 0xc5, 0x11, (byte) 0xd8, 0x09, 0x66, 57 0x08, 0x00, 0x20, 0x0c, (byte) 0x9a, 0x66 58 }; 59 60 public static final String PB_PATH = "telecom/pb.vcf"; 61 public static final String MCH_PATH = "telecom/mch.vcf"; 62 public static final String ICH_PATH = "telecom/ich.vcf"; 63 public static final String OCH_PATH = "telecom/och.vcf"; 64 public static final byte VCARD_TYPE_21 = 0; 65 66 private Account mAccount; 67 private AccountManager mAccountManager; 68 private BluetoothSocket mSocket; 69 private final BluetoothAdapter mAdapter; 70 private final BluetoothDevice mDevice; 71 private ClientSession mObexSession; 72 private Context mContext; 73 private BluetoothPbapObexAuthenticator mAuth = null; 74 private final PbapClientStateMachine mPbapClientStateMachine; 75 private boolean mAccountCreated; 76 77 PbapClientConnectionHandler(Looper looper, Context context, PbapClientStateMachine stateMachine, 78 BluetoothDevice device) { 79 super(looper); 80 mAdapter = BluetoothAdapter.getDefaultAdapter(); 81 mDevice = device; 82 mContext = context; 83 mPbapClientStateMachine = stateMachine; 84 mAuth = new BluetoothPbapObexAuthenticator(this); 85 mAccountManager = AccountManager.get(mPbapClientStateMachine.getContext()); 86 mAccount = new Account(mDevice.getAddress(), mContext.getString( 87 R.string.pbap_account_type)); 88 } 89 90 @Override 91 public void handleMessage(Message msg) { 92 if (DBG) Log.d(TAG,"Handling Message = " +msg.what); 93 switch(msg.what) { 94 case MSG_CONNECT: 95 boolean connectionSuccessful = false; 96 97 try { 98 /* To establish a connection first open a socket, establish a OBEX Transport 99 * abstraction, establish a Bluetooth Authenticator, and finally attempt to 100 * connect via an OBEX session */ 101 mSocket = mDevice.createRfcommSocketToServiceRecord( 102 BluetoothUuid.PBAP_PSE.getUuid()); 103 mSocket.connect(); 104 105 BluetoothPbapObexTransport transport; 106 transport = new BluetoothPbapObexTransport(mSocket); 107 108 mObexSession = new ClientSession(transport); 109 mObexSession.setAuthenticator(mAuth); 110 111 HeaderSet connectionRequest = new HeaderSet(); 112 connectionRequest.setHeader(HeaderSet.TARGET, PBAP_TARGET); 113 HeaderSet connectionResponse = mObexSession.connect(connectionRequest); 114 115 connectionSuccessful = (connectionResponse.getResponseCode() == 116 ResponseCodes.OBEX_HTTP_OK); 117 if (DBG) Log.d(TAG,"Success = " + Boolean.toString(connectionSuccessful)); 118 } catch (IOException e) { 119 Log.w(TAG,"CONNECT Failure " + e.toString()); 120 closeSocket(); 121 } 122 123 if (connectionSuccessful) { 124 mPbapClientStateMachine.obtainMessage( 125 PbapClientStateMachine.MSG_CONNECTION_COMPLETE).sendToTarget(); 126 } else { 127 mPbapClientStateMachine.obtainMessage( 128 PbapClientStateMachine.MSG_CONNECTION_FAILED).sendToTarget(); 129 } 130 break; 131 132 case MSG_DISCONNECT: 133 try { 134 if (mObexSession != null) { 135 mObexSession.disconnect(null); 136 } 137 closeSocket(); 138 } catch (IOException e) { 139 Log.w(TAG,"DISCONNECT Failure " + e.toString()); 140 } 141 removeAccount(mAccount); 142 mContext.getContentResolver() 143 .delete(CallLog.Calls.CONTENT_URI, null, null); 144 mPbapClientStateMachine.obtainMessage( 145 PbapClientStateMachine.MSG_CONNECTION_CLOSED).sendToTarget(); 146 break; 147 148 case MSG_DOWNLOAD: 149 try { 150 mAccountCreated = addAccount(mAccount); 151 if (mAccountCreated == false) { 152 Log.e(TAG,"Account creation failed."); 153 return; 154 } 155 BluetoothPbapRequestPullPhoneBook request = 156 new BluetoothPbapRequestPullPhoneBook(PB_PATH, mAccount, 0, 157 VCARD_TYPE_21, 0, 0); 158 request.execute(mObexSession); 159 PhonebookPullRequest processor = 160 new PhonebookPullRequest(mPbapClientStateMachine.getContext(), mAccount); 161 processor.setResults(request.getList()); 162 processor.onPullComplete(); 163 164 downloadCallLog(MCH_PATH); 165 downloadCallLog(ICH_PATH); 166 downloadCallLog(OCH_PATH); 167 } catch (IOException e) { 168 Log.w(TAG,"DOWNLOAD_CONTACTS Failure" + e.toString()); 169 } 170 break; 171 172 default: 173 Log.w(TAG,"Received Unexpected Message"); 174 } 175 return; 176 } 177 178 public void abort() { 179 // Perform forced cleanup, it is ok if the handler throws an exception this will free the 180 // handler to complete what it is doing and finish with cleanup. 181 closeSocket(); 182 this.getLooper().getThread().interrupt(); 183 } 184 185 private void closeSocket() { 186 try { 187 if (mSocket != null) { 188 mSocket.close(); 189 mSocket = null; 190 } 191 } catch (IOException e) { 192 Log.e(TAG, "Error when closing socket", e); 193 mSocket = null; 194 } 195 } 196 void downloadCallLog(String path) { 197 try { 198 BluetoothPbapRequestPullPhoneBook request = 199 new BluetoothPbapRequestPullPhoneBook(path,mAccount,0,VCARD_TYPE_21,0,0); 200 request.execute(mObexSession); 201 CallLogPullRequest processor = 202 new CallLogPullRequest(mPbapClientStateMachine.getContext(),path); 203 processor.setResults(request.getList()); 204 processor.onPullComplete(); 205 } catch (IOException e) { 206 Log.w(TAG,"Download call log failure"); 207 } 208 } 209 210 private boolean addAccount(Account account) { 211 if (mAccountManager.addAccountExplicitly(account, null, null)) { 212 if (DBG) { 213 Log.d(TAG, "Added account " + mAccount); 214 } 215 return true; 216 } 217 return false; 218 } 219 220 private void removeAccount(Account acc) { 221 if (mAccountManager.removeAccountExplicitly(acc)) { 222 if (DBG) { 223 Log.d(TAG, "Removed account " + acc); 224 } 225 } else { 226 Log.e(TAG, "Failed to remove account " + mAccount); 227 } 228 } 229} 230