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