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 
112             if (transferRecord.dataLinkType == BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
113                 if (mBluetoothAdapter.isEnabled()) {
114                     // Start the transfer
115                     mTransferManager.start();
116                 } else {
117                     if (!mBluetoothAdapter.enableNoAutoConnect()) {
118                         Log.e(TAG, "Error enabling Bluetooth.");
119                         mTransferManager = null;
120                         return false;
121                     }
122                     mBluetoothEnabledByNfc = true;
123                     if (DBG) Log.d(TAG, "Queueing out transfer "
124                             + Integer.toString(transferRecord.id));
125                 }
126             }
127             return true;
128         }
129 
130         return false;
131     }
132 
createBeamTransferManager(BeamTransferRecord transferRecord)133     boolean createBeamTransferManager(BeamTransferRecord transferRecord) {
134         if (mTransferManager != null) {
135             return false;
136         }
137 
138         if (transferRecord.dataLinkType != BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
139             // only support BT
140             return false;
141         }
142 
143         mTransferManager = new BeamTransferManager(this, this, transferRecord, false);
144         mTransferManager.updateNotification();
145         return true;
146     }
147 
handleBluetoothStateChanged(Intent intent)148     private void handleBluetoothStateChanged(Intent intent) {
149         int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
150                 BluetoothAdapter.ERROR);
151         if (state == BluetoothAdapter.STATE_ON) {
152             if (mTransferManager != null &&
153                     mTransferManager.mDataLinkType == BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
154                 mTransferManager.start();
155             }
156         }
157     }
158 
invokeCompleteCallback(boolean success)159     private void invokeCompleteCallback(boolean success) {
160         if (mCompleteCallback != null) {
161             try {
162                 Message msg = Message.obtain(null, BeamManager.MSG_BEAM_COMPLETE);
163                 msg.arg1 = success ? 1 : 0;
164                 mCompleteCallback.send(msg);
165             } catch (RemoteException e) {
166                 Log.e(TAG, "failed to invoke Beam complete callback", e);
167             }
168         }
169     }
170 
171     @Override
onTransferComplete(BeamTransferManager transfer, boolean success)172     public void onTransferComplete(BeamTransferManager transfer, boolean success) {
173         // Play success sound
174         if (!success) {
175             if (DBG) Log.d(TAG, "Transfer failed, final state: " +
176                     Integer.toString(transfer.mState));
177         }
178 
179         if (mBluetoothEnabledByNfc) {
180             mBluetoothEnabledByNfc = false;
181             mBluetoothAdapter.disable();
182         }
183 
184         invokeCompleteCallback(success);
185         stopSelf(mStartId);
186     }
187 
188     @Override
onBind(Intent intent)189     public IBinder onBind(Intent intent) {
190         return null;
191     }
192 }
193