1package com.android.nfc.beam;
2
3import android.app.Service;
4import android.bluetooth.BluetoothAdapter;
5import android.content.BroadcastReceiver;
6import android.content.Context;
7import android.content.Intent;
8import android.content.IntentFilter;
9import android.os.Handler;
10import android.os.IBinder;
11import android.os.Message;
12import android.os.Messenger;
13import android.os.RemoteException;
14import android.util.Log;
15
16
17/**
18 * @hide
19 */
20public class BeamReceiveService extends Service implements BeamTransferManager.Callback {
21    private static String TAG = "BeamReceiveService";
22    private static boolean DBG = true;
23
24    public static final String EXTRA_BEAM_TRANSFER_RECORD
25            = "com.android.nfc.beam.EXTRA_BEAM_TRANSFER_RECORD";
26    public static final String EXTRA_BEAM_COMPLETE_CALLBACK
27            = "com.android.nfc.beam.TRANSFER_COMPLETE_CALLBACK";
28
29    private BeamStatusReceiver mBeamStatusReceiver;
30    private boolean mBluetoothEnabledByNfc;
31    private int mStartId;
32    private BeamTransferManager mTransferManager;
33    private Messenger mCompleteCallback;
34
35    private final BluetoothAdapter mBluetoothAdapter;
36    private final BroadcastReceiver mBluetoothStateReceiver = new BroadcastReceiver() {
37        @Override
38        public void onReceive(Context context, Intent intent) {
39            String action = intent.getAction();
40            if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
41                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
42                        BluetoothAdapter.ERROR);
43                if (state == BluetoothAdapter.STATE_OFF) {
44                    mBluetoothEnabledByNfc = false;
45                }
46            }
47        }
48    };
49
50    public BeamReceiveService() {
51        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
52    }
53
54    @Override
55    public int onStartCommand(Intent intent, int flags, int startId) {
56        mStartId = startId;
57
58        BeamTransferRecord transferRecord;
59        if (intent == null ||
60                (transferRecord = intent.getParcelableExtra(EXTRA_BEAM_TRANSFER_RECORD)) == null) {
61            if (DBG) Log.e(TAG, "No transfer record provided. Stopping.");
62            stopSelf(startId);
63            return START_NOT_STICKY;
64        }
65
66        mCompleteCallback = intent.getParcelableExtra(EXTRA_BEAM_COMPLETE_CALLBACK);
67
68        if (prepareToReceive(transferRecord)) {
69            if (DBG) Log.i(TAG, "Ready for incoming Beam transfer");
70            return START_STICKY;
71        } else {
72            invokeCompleteCallback(false);
73            stopSelf(startId);
74            return START_NOT_STICKY;
75        }
76    }
77
78    // TODO: figure out a way to not duplicate this code
79    @Override
80    public void onCreate() {
81        super.onCreate();
82
83        // register BT state receiver
84        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
85        registerReceiver(mBluetoothStateReceiver, filter);
86    }
87
88    @Override
89    public void onDestroy() {
90        super.onDestroy();
91        if (mBeamStatusReceiver != null) {
92            unregisterReceiver(mBeamStatusReceiver);
93        }
94        unregisterReceiver(mBluetoothStateReceiver);
95    }
96
97    boolean prepareToReceive(BeamTransferRecord transferRecord) {
98        if (mTransferManager != null) {
99            return false;
100        }
101
102        if (transferRecord.dataLinkType != BeamTransferRecord.DATA_LINK_TYPE_BLUETOOTH) {
103            // only support BT
104            return false;
105        }
106
107        if (!mBluetoothAdapter.isEnabled()) {
108            if (!mBluetoothAdapter.enableNoAutoConnect()) {
109                Log.e(TAG, "Error enabling Bluetooth.");
110                return false;
111            }
112            mBluetoothEnabledByNfc = true;
113            if (DBG) Log.d(TAG, "Queueing out transfer "
114                    + Integer.toString(transferRecord.id));
115        }
116
117        mTransferManager = new BeamTransferManager(this, this, transferRecord, true);
118
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        mTransferManager.start();
125        mTransferManager.updateNotification();
126        return true;
127    }
128
129    private void invokeCompleteCallback(boolean success) {
130        if (mCompleteCallback != null) {
131            try {
132                Message msg = Message.obtain(null, BeamManager.MSG_BEAM_COMPLETE);
133                msg.arg1 = success ? 1 : 0;
134                mCompleteCallback.send(msg);
135            } catch (RemoteException e) {
136                Log.e(TAG, "failed to invoke Beam complete callback", e);
137            }
138        }
139    }
140
141    @Override
142    public void onTransferComplete(BeamTransferManager transfer, boolean success) {
143        // Play success sound
144        if (!success) {
145            if (DBG) Log.d(TAG, "Transfer failed, final state: " +
146                    Integer.toString(transfer.mState));
147        }
148
149        if (mBluetoothEnabledByNfc) {
150            mBluetoothEnabledByNfc = false;
151            mBluetoothAdapter.disable();
152        }
153
154        invokeCompleteCallback(success);
155        stopSelf(mStartId);
156    }
157
158    @Override
159    public IBinder onBind(Intent intent) {
160        return null;
161    }
162}
163