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