1 /*
2  * Copyright (c) 2008-2009, Motorola, Inc.
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * - Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * - Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * - Neither the name of the Motorola, Inc. nor the names of its contributors
17  * may be used to endorse or promote products derived from this software
18  * without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 package com.android.bluetooth.opp;
34 
35 import com.android.bluetooth.R;
36 
37 import android.app.NotificationManager;
38 import android.bluetooth.BluetoothAdapter;
39 import android.bluetooth.BluetoothDevice;
40 import android.bluetooth.BluetoothDevicePicker;
41 import android.content.BroadcastReceiver;
42 import android.content.ContentUris;
43 import android.content.ContentValues;
44 import android.content.Context;
45 import android.content.Intent;
46 import android.database.Cursor;
47 import android.net.Uri;
48 import android.util.Log;
49 import android.widget.Toast;
50 
51 /**
52  * Receives and handles: system broadcasts; Intents from other applications;
53  * Intents from OppService; Intents from modules in Opp application layer.
54  */
55 public class BluetoothOppReceiver extends BroadcastReceiver {
56     private static final String TAG = "BluetoothOppReceiver";
57     private static final boolean D = Constants.DEBUG;
58     private static final boolean V = Constants.VERBOSE;
59 
60     @Override
onReceive(Context context, Intent intent)61     public void onReceive(Context context, Intent intent) {
62         String action = intent.getAction();
63 
64 
65         if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
66             if (BluetoothAdapter.STATE_ON == intent.getIntExtra(
67                     BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
68                 if (V) Log.v(TAG, "Received BLUETOOTH_STATE_CHANGED_ACTION, BLUETOOTH_STATE_ON");
69                 context.startService(new Intent(context, BluetoothOppService.class));
70 
71                 // If this is within a sending process, continue the handle
72                 // logic to display device picker dialog.
73                 synchronized (this) {
74                     if (BluetoothOppManager.getInstance(context).mSendingFlag) {
75                         // reset the flags
76                         BluetoothOppManager.getInstance(context).mSendingFlag = false;
77 
78                         Intent in1 = new Intent(BluetoothDevicePicker.ACTION_LAUNCH);
79                         in1.putExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false);
80                         in1.putExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE,
81                                 BluetoothDevicePicker.FILTER_TYPE_TRANSFER);
82                         in1.putExtra(BluetoothDevicePicker.EXTRA_LAUNCH_PACKAGE,
83                                 Constants.THIS_PACKAGE_NAME);
84                         in1.putExtra(BluetoothDevicePicker.EXTRA_LAUNCH_CLASS,
85                                 BluetoothOppReceiver.class.getName());
86 
87                         in1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
88                         context.startActivity(in1);
89                     }
90                 }
91             }
92         } else if (action.equals(BluetoothDevicePicker.ACTION_DEVICE_SELECTED)) {
93             BluetoothOppManager mOppManager = BluetoothOppManager.getInstance(context);
94 
95             BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
96 
97             if (V) Log.v(TAG, "Received BT device selected intent, bt device: " + remoteDevice);
98 
99             // Insert transfer session record to database
100             mOppManager.startTransfer(remoteDevice);
101 
102             // Display toast message
103             String deviceName = mOppManager.getDeviceName(remoteDevice);
104             String toastMsg;
105             int batchSize = mOppManager.getBatchSize();
106             if (mOppManager.mMultipleFlag) {
107                 toastMsg = context.getString(R.string.bt_toast_5, Integer.toString(batchSize),
108                         deviceName);
109             } else {
110                 toastMsg = context.getString(R.string.bt_toast_4, deviceName);
111             }
112             Toast.makeText(context, toastMsg, Toast.LENGTH_SHORT).show();
113         } else if (action.equals(Constants.ACTION_INCOMING_FILE_CONFIRM)) {
114             if (V) Log.v(TAG, "Receiver ACTION_INCOMING_FILE_CONFIRM");
115 
116             Uri uri = intent.getData();
117             Intent in = new Intent(context, BluetoothOppIncomingFileConfirmActivity.class);
118             in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
119             in.setDataAndNormalize(uri);
120             context.startActivity(in);
121             cancelNotification(context, uri);
122 
123         } else if (action.equals(Constants.ACTION_DECLINE)) {
124             if (V) Log.v(TAG, "Receiver ACTION_DECLINE");
125 
126             Uri uri = intent.getData();
127             ContentValues values = new ContentValues();
128             values.put(BluetoothShare.USER_CONFIRMATION, BluetoothShare.USER_CONFIRMATION_DENIED);
129             context.getContentResolver().update(uri, values, null, null);
130             cancelNotification(context, uri);
131 
132         } else if (action.equals(Constants.ACTION_ACCEPT)) {
133             if (V) Log.v(TAG, "Receiver ACTION_ACCEPT");
134 
135             Uri uri = intent.getData();
136             ContentValues values = new ContentValues();
137             values.put(BluetoothShare.USER_CONFIRMATION, BluetoothShare.USER_CONFIRMATION_CONFIRMED);
138             context.getContentResolver().update(uri, values, null, null);
139             cancelNotification(context, uri);
140 
141         } else if (action.equals(Constants.ACTION_OPEN) || action.equals(Constants.ACTION_LIST)) {
142             if (V) {
143                 if (action.equals(Constants.ACTION_OPEN)) {
144                     Log.v(TAG, "Receiver open for " + intent.getData());
145                 } else {
146                     Log.v(TAG, "Receiver list for " + intent.getData());
147                 }
148             }
149 
150             BluetoothOppTransferInfo transInfo = new BluetoothOppTransferInfo();
151             Uri uri = intent.getData();
152             transInfo = BluetoothOppUtility.queryRecord(context, uri);
153             if (transInfo == null) {
154                 Log.e(TAG, "Error: Can not get data from db");
155                 return;
156             }
157 
158             if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND
159                     && BluetoothShare.isStatusSuccess(transInfo.mStatus)) {
160                 // if received file successfully, open this file
161                 BluetoothOppUtility.openReceivedFile(context, transInfo.mFileName,
162                         transInfo.mFileType, transInfo.mTimeStamp, uri);
163                 BluetoothOppUtility.updateVisibilityToHidden(context, uri);
164             } else {
165                 Intent in = new Intent(context, BluetoothOppTransferActivity.class);
166                 in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
167                 in.setDataAndNormalize(uri);
168                 context.startActivity(in);
169             }
170 
171             cancelNotification(context, uri);
172         } else if (action.equals(Constants.ACTION_OPEN_OUTBOUND_TRANSFER)) {
173             if (V) Log.v(TAG, "Received ACTION_OPEN_OUTBOUND_TRANSFER.");
174 
175             Intent in = new Intent(context, BluetoothOppTransferHistory.class);
176             in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
177             in.putExtra("direction", BluetoothShare.DIRECTION_OUTBOUND);
178             context.startActivity(in);
179         } else if (action.equals(Constants.ACTION_OPEN_INBOUND_TRANSFER)) {
180             if (V) Log.v(TAG, "Received ACTION_OPEN_INBOUND_TRANSFER.");
181 
182             Intent in = new Intent(context, BluetoothOppTransferHistory.class);
183             in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
184             in.putExtra("direction", BluetoothShare.DIRECTION_INBOUND);
185             context.startActivity(in);
186         } else if (action.equals(Constants.ACTION_OPEN_RECEIVED_FILES)) {
187             if (V) Log.v(TAG, "Received ACTION_OPEN_RECEIVED_FILES.");
188 
189             Intent in = new Intent(context, BluetoothOppTransferHistory.class);
190             in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
191             in.putExtra("direction", BluetoothShare.DIRECTION_INBOUND);
192             in.putExtra(Constants.EXTRA_SHOW_ALL_FILES, true);
193             context.startActivity(in);
194         } else if (action.equals(Constants.ACTION_HIDE)) {
195             if (V) Log.v(TAG, "Receiver hide for " + intent.getData());
196             Cursor cursor = context.getContentResolver().query(intent.getData(), null, null, null,
197                     null);
198             if (cursor != null) {
199                 if (cursor.moveToFirst()) {
200                     int statusColumn = cursor.getColumnIndexOrThrow(BluetoothShare.STATUS);
201                     int status = cursor.getInt(statusColumn);
202                     int visibilityColumn = cursor.getColumnIndexOrThrow(BluetoothShare.VISIBILITY);
203                     int visibility = cursor.getInt(visibilityColumn);
204                     int userConfirmationColumn = cursor
205                             .getColumnIndexOrThrow(BluetoothShare.USER_CONFIRMATION);
206                     int userConfirmation = cursor.getInt(userConfirmationColumn);
207                     if (((userConfirmation == BluetoothShare.USER_CONFIRMATION_PENDING))
208                             && visibility == BluetoothShare.VISIBILITY_VISIBLE) {
209                         ContentValues values = new ContentValues();
210                         values.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN);
211                         context.getContentResolver().update(intent.getData(), values, null, null);
212                         if (V) Log.v(TAG, "Action_hide received and db updated");
213                         }
214                 }
215                 cursor.close();
216             }
217         } else if (action.equals(Constants.ACTION_COMPLETE_HIDE)) {
218             if (V) Log.v(TAG, "Receiver ACTION_COMPLETE_HIDE");
219             ContentValues updateValues = new ContentValues();
220             updateValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN);
221             context.getContentResolver().update(BluetoothShare.CONTENT_URI, updateValues,
222                     BluetoothOppNotification.WHERE_COMPLETED, null);
223         } else if (action.equals(BluetoothShare.TRANSFER_COMPLETED_ACTION)) {
224             if (V) Log.v(TAG, "Receiver Transfer Complete Intent for " + intent.getData());
225 
226             String toastMsg = null;
227             BluetoothOppTransferInfo transInfo = new BluetoothOppTransferInfo();
228             transInfo = BluetoothOppUtility.queryRecord(context, intent.getData());
229             if (transInfo == null) {
230                 Log.e(TAG, "Error: Can not get data from db");
231                 return;
232             }
233 
234             if (transInfo.mHandoverInitiated) {
235                 // Deal with handover-initiated transfers separately
236                 Intent handoverIntent = new Intent(Constants.ACTION_BT_OPP_TRANSFER_DONE);
237                 if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND) {
238                     handoverIntent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_DIRECTION,
239                             Constants.DIRECTION_BLUETOOTH_INCOMING);
240                 } else {
241                     handoverIntent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_DIRECTION,
242                             Constants.DIRECTION_BLUETOOTH_OUTGOING);
243                 }
244                 handoverIntent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_ID, transInfo.mID);
245                 handoverIntent.putExtra(Constants.EXTRA_BT_OPP_ADDRESS, transInfo.mDestAddr);
246 
247                 if (BluetoothShare.isStatusSuccess(transInfo.mStatus)) {
248                     handoverIntent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_STATUS,
249                             Constants.HANDOVER_TRANSFER_STATUS_SUCCESS);
250                     handoverIntent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_URI,
251                             transInfo.mFileName);
252                     handoverIntent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_MIMETYPE,
253                             transInfo.mFileType);
254                 } else {
255                     handoverIntent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_STATUS,
256                             Constants.HANDOVER_TRANSFER_STATUS_FAILURE);
257                 }
258                 context.sendBroadcast(handoverIntent, Constants.HANDOVER_STATUS_PERMISSION);
259                 return;
260             }
261 
262             if (BluetoothShare.isStatusSuccess(transInfo.mStatus)) {
263                 if (transInfo.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
264                     toastMsg = context.getString(R.string.notification_sent, transInfo.mFileName);
265                 } else if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND) {
266                     toastMsg = context.getString(R.string.notification_received,
267                             transInfo.mFileName);
268                 }
269 
270             } else if (BluetoothShare.isStatusError(transInfo.mStatus)) {
271                 if (transInfo.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
272                     toastMsg = context.getString(R.string.notification_sent_fail,
273                             transInfo.mFileName);
274                 } else if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND) {
275                     toastMsg = context.getString(R.string.download_fail_line1);
276                 }
277             }
278             if (V) Log.v(TAG, "Toast msg == " + toastMsg);
279             if (toastMsg != null) {
280                 Toast.makeText(context, toastMsg, Toast.LENGTH_SHORT).show();
281             }
282         }
283     }
284 
cancelNotification(Context context, Uri uri)285     private void cancelNotification(Context context, Uri uri) {
286         NotificationManager notMgr = (NotificationManager)context
287                 .getSystemService(Context.NOTIFICATION_SERVICE);
288         if (notMgr == null) return;
289 
290         int id = -1;
291         try {
292           id = (int) ContentUris.parseId(uri);
293         } catch (NumberFormatException ex) {
294           Log.v(TAG, "Can't parse notification ID from Uri!");
295           return;
296         }
297 
298         notMgr.cancel(id);
299         if (V) Log.v(TAG, "notMgr.cancel called");
300     }
301 }
302