1 /* 2 * Copyright (C) 2014 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.nfc.beam; 18 19 import android.app.Service; 20 import android.bluetooth.BluetoothAdapter; 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.os.Handler; 26 import android.os.IBinder; 27 import android.os.Message; 28 import android.os.Messenger; 29 import android.os.RemoteException; 30 import android.util.Log; 31 32 public class BeamSendService extends Service implements BeamTransferManager.Callback { 33 private static String TAG = "BeamSendService"; 34 private static boolean DBG = true; 35 36 public static String EXTRA_BEAM_TRANSFER_RECORD 37 = "com.android.nfc.beam.EXTRA_BEAM_TRANSFER_RECORD"; 38 public static final String EXTRA_BEAM_COMPLETE_CALLBACK 39 = "com.android.nfc.beam.TRANSFER_COMPLETE_CALLBACK"; 40 41 private BeamTransferManager mTransferManager; 42 private BeamStatusReceiver mBeamStatusReceiver; 43 private boolean mBluetoothEnabledByNfc; 44 private Messenger mCompleteCallback; 45 private int mStartId; 46 47 private final BluetoothAdapter mBluetoothAdapter; 48 private final BroadcastReceiver mBluetoothStateReceiver = new BroadcastReceiver() { 49 @Override 50 public void onReceive(Context context, Intent intent) { 51 String action = intent.getAction(); 52 if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 53 handleBluetoothStateChanged(intent); 54 } 55 } 56 }; 57 BeamSendService()58 public BeamSendService() { 59 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 60 } 61 62 @Override onCreate()63 public void onCreate() { 64 super.onCreate(); 65 66 // register BT state receiver 67 IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); 68 registerReceiver(mBluetoothStateReceiver, filter); 69 } 70 71 @Override onDestroy()72 public void onDestroy() { 73 super.onDestroy(); 74 75 if (mBeamStatusReceiver != null) { 76 unregisterReceiver(mBeamStatusReceiver); 77 } 78 unregisterReceiver(mBluetoothStateReceiver); 79 } 80 81 @Override onStartCommand(Intent intent, int flags, int startId)82 public int onStartCommand(Intent intent, int flags, int startId) { 83 mStartId = startId; 84 85 BeamTransferRecord transferRecord; 86 if (intent == null || 87 (transferRecord = intent.getParcelableExtra(EXTRA_BEAM_TRANSFER_RECORD)) == null) { 88 if (DBG) Log.e(TAG, "No transfer record provided. Stopping."); 89 stopSelf(startId); 90 return START_NOT_STICKY; 91 } 92 93 mCompleteCallback = intent.getParcelableExtra(EXTRA_BEAM_COMPLETE_CALLBACK); 94 95 if (doTransfer(transferRecord)) { 96 if (DBG) Log.i(TAG, "Starting outgoing Beam transfer"); 97 return START_STICKY; 98 } else { 99 invokeCompleteCallback(false); 100 stopSelf(startId); 101 return START_NOT_STICKY; 102 } 103 } 104 doTransfer(BeamTransferRecord transferRecord)105 boolean doTransfer(BeamTransferRecord transferRecord) { 106 if (createBeamTransferManager(transferRecord)) { 107 // register Beam status receiver 108 mBeamStatusReceiver = new BeamStatusReceiver(this, mTransferManager); 109 registerReceiver(mBeamStatusReceiver, mBeamStatusReceiver.getIntentFilter(), 110 BeamStatusReceiver.BEAM_STATUS_PERMISSION, new Handler(), 111 Context.RECEIVER_EXPORTED); 112 113 if (transferRecord.dataLinkType == BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) { 114 if (mBluetoothAdapter.isEnabled()) { 115 // Start the transfer 116 mTransferManager.start(); 117 } else { 118 if (!mBluetoothAdapter.enableNoAutoConnect()) { 119 Log.e(TAG, "Error enabling Bluetooth."); 120 mTransferManager = null; 121 return false; 122 } 123 mBluetoothEnabledByNfc = true; 124 if (DBG) Log.d(TAG, "Queueing out transfer " 125 + Integer.toString(transferRecord.id)); 126 } 127 } 128 return true; 129 } 130 131 return false; 132 } 133 createBeamTransferManager(BeamTransferRecord transferRecord)134 boolean createBeamTransferManager(BeamTransferRecord transferRecord) { 135 if (mTransferManager != null) { 136 return false; 137 } 138 139 if (transferRecord.dataLinkType != BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) { 140 // only support BT 141 return false; 142 } 143 144 mTransferManager = new BeamTransferManager(this, this, transferRecord, false); 145 mTransferManager.updateNotification(); 146 return true; 147 } 148 handleBluetoothStateChanged(Intent intent)149 private void handleBluetoothStateChanged(Intent intent) { 150 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 151 BluetoothAdapter.ERROR); 152 if (state == BluetoothAdapter.STATE_ON) { 153 if (mTransferManager != null && 154 mTransferManager.mDataLinkType == BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) { 155 mTransferManager.start(); 156 } 157 } 158 } 159 invokeCompleteCallback(boolean success)160 private void invokeCompleteCallback(boolean success) { 161 if (mCompleteCallback != null) { 162 try { 163 Message msg = Message.obtain(null, BeamManager.MSG_BEAM_COMPLETE); 164 msg.arg1 = success ? 1 : 0; 165 mCompleteCallback.send(msg); 166 } catch (RemoteException e) { 167 Log.e(TAG, "failed to invoke Beam complete callback", e); 168 } 169 } 170 } 171 172 @Override onTransferComplete(BeamTransferManager transfer, boolean success)173 public void onTransferComplete(BeamTransferManager transfer, boolean success) { 174 // Play success sound 175 if (!success) { 176 if (DBG) Log.d(TAG, "Transfer failed, final state: " + 177 Integer.toString(transfer.mState)); 178 } 179 180 if (mBluetoothEnabledByNfc) { 181 mBluetoothEnabledByNfc = false; 182 mBluetoothAdapter.disable(); 183 } 184 185 invokeCompleteCallback(success); 186 stopSelf(mStartId); 187 } 188 189 @Override onBind(Intent intent)190 public IBinder onBind(Intent intent) { 191 return null; 192 } 193 } 194