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