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 android.app.NotificationManager;
36 import android.bluetooth.BluetoothDevice;
37 import android.bluetooth.BluetoothDevicePicker;
38 import android.bluetooth.BluetoothProfile;
39 import android.bluetooth.BluetoothProtoEnums;
40 import android.bluetooth.BluetoothUtils;
41 import android.content.BroadcastReceiver;
42 import android.content.ContentValues;
43 import android.content.Context;
44 import android.content.Intent;
45 import android.database.Cursor;
46 import android.net.Uri;
47 import android.util.Log;
48 import android.widget.Toast;
49 
50 import com.android.bluetooth.BluetoothMethodProxy;
51 import com.android.bluetooth.BluetoothStatsLog;
52 import com.android.bluetooth.R;
53 import com.android.bluetooth.Utils;
54 import com.android.bluetooth.content_profiles.ContentProfileErrorReportUtils;
55 import com.android.bluetooth.flags.Flags;
56 
57 /**
58  * Receives and handles: system broadcasts; Intents from other applications; Intents from
59  * OppService; Intents from modules in Opp application layer.
60  */
61 // Next tag value for ContentProfileErrorReportUtils.report(): 2
62 public class BluetoothOppReceiver extends BroadcastReceiver {
63     private static final String TAG = "BluetoothOppReceiver";
64 
65     @Override
onReceive(Context context, Intent intent)66     public void onReceive(Context context, Intent intent) {
67         String action = intent.getAction();
68         Log.d(TAG, " action :" + action);
69         if (action == null) return;
70         if (action.equals(BluetoothDevicePicker.ACTION_DEVICE_SELECTED)) {
71             BluetoothOppManager mOppManager = BluetoothOppManager.getInstance(context);
72 
73             BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
74 
75             if (remoteDevice == null) {
76                 mOppManager.cleanUpSendingFileInfo();
77                 return;
78             }
79 
80             Log.d(
81                     TAG,
82                     "Received BT device selected intent, bt device: "
83                             + BluetoothUtils.toAnonymizedAddress(
84                                     Flags.identityAddressNullIfUnknown()
85                                             ? Utils.getBrEdrAddress(remoteDevice)
86                                             : remoteDevice.getIdentityAddress()));
87 
88             // Insert transfer session record to database
89             mOppManager.startTransfer(remoteDevice);
90 
91             // Display toast message
92             String deviceName = mOppManager.getDeviceName(remoteDevice);
93             String toastMsg;
94             int batchSize = mOppManager.getBatchSize();
95             if (mOppManager.mMultipleFlag) {
96                 toastMsg =
97                         context.getString(
98                                 R.string.bt_toast_5, Integer.toString(batchSize), deviceName);
99             } else {
100                 toastMsg = context.getString(R.string.bt_toast_4, deviceName);
101             }
102             Toast.makeText(context, toastMsg, Toast.LENGTH_SHORT).show();
103         } else if (action.equals(Constants.ACTION_INCOMING_FILE_CONFIRM)
104                 && !Flags.oppStartActivityDirectlyFromNotification()) {
105             Log.v(TAG, "Receiver ACTION_INCOMING_FILE_CONFIRM");
106 
107             Uri uri = intent.getData();
108             Intent in = new Intent(context, BluetoothOppIncomingFileConfirmActivity.class);
109             in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
110             in.setDataAndNormalize(uri);
111             context.startActivity(in);
112         } else if (action.equals(Constants.ACTION_DECLINE)) {
113             Log.v(TAG, "Receiver ACTION_DECLINE");
114 
115             Uri uri = intent.getData();
116             ContentValues values = new ContentValues();
117             values.put(BluetoothShare.USER_CONFIRMATION, BluetoothShare.USER_CONFIRMATION_DENIED);
118             BluetoothMethodProxy.getInstance()
119                     .contentResolverUpdate(context.getContentResolver(), uri, values, null, null);
120             cancelNotification(context, BluetoothOppNotification.NOTIFICATION_ID_PROGRESS);
121 
122         } else if (action.equals(Constants.ACTION_ACCEPT)) {
123             Log.v(TAG, "Receiver ACTION_ACCEPT");
124 
125             Uri uri = intent.getData();
126             ContentValues values = new ContentValues();
127             values.put(
128                     BluetoothShare.USER_CONFIRMATION, BluetoothShare.USER_CONFIRMATION_CONFIRMED);
129             BluetoothMethodProxy.getInstance()
130                     .contentResolverUpdate(context.getContentResolver(), uri, values, null, null);
131         } else if (action.equals(Constants.ACTION_OPEN) || action.equals(Constants.ACTION_LIST)) {
132             if (action.equals(Constants.ACTION_OPEN)) {
133                 Log.v(TAG, "Receiver open for " + intent.getData());
134             } else {
135                 Log.v(TAG, "Receiver list for " + intent.getData());
136             }
137 
138             Uri uri = intent.getData();
139             BluetoothOppTransferInfo transInfo = BluetoothOppUtility.queryRecord(context, uri);
140             if (transInfo == null) {
141                 Log.e(TAG, "Error: Can not get data from db");
142                 ContentProfileErrorReportUtils.report(
143                         BluetoothProfile.OPP,
144                         BluetoothProtoEnums.BLUETOOTH_OPP_RECEIVER,
145                         BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_ERROR,
146                         0);
147                 return;
148             }
149 
150             if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND
151                     && BluetoothShare.isStatusSuccess(transInfo.mStatus)) {
152                 // if received file successfully, open this file
153                 BluetoothOppUtility.openReceivedFile(
154                         context,
155                         transInfo.mFileName,
156                         transInfo.mFileType,
157                         transInfo.mTimeStamp,
158                         uri);
159                 BluetoothOppUtility.updateVisibilityToHidden(context, uri);
160             } else {
161                 Intent in = new Intent(context, BluetoothOppTransferActivity.class);
162                 in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
163                 in.setDataAndNormalize(uri);
164                 context.startActivity(in);
165             }
166 
167         } else if (action.equals(Constants.ACTION_OPEN_OUTBOUND_TRANSFER)
168                 && !Flags.oppStartActivityDirectlyFromNotification()) {
169             Log.v(TAG, "Received ACTION_OPEN_OUTBOUND_TRANSFER.");
170 
171             Intent in = new Intent(context, BluetoothOppTransferHistory.class);
172             in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
173             in.putExtra(Constants.EXTRA_DIRECTION, BluetoothShare.DIRECTION_OUTBOUND);
174             context.startActivity(in);
175         } else if (action.equals(Constants.ACTION_OPEN_INBOUND_TRANSFER)
176                 && !Flags.oppStartActivityDirectlyFromNotification()) {
177             Log.v(TAG, "Received ACTION_OPEN_INBOUND_TRANSFER.");
178 
179             Intent in = new Intent(context, BluetoothOppTransferHistory.class);
180             in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
181             in.putExtra(Constants.EXTRA_DIRECTION, BluetoothShare.DIRECTION_INBOUND);
182             context.startActivity(in);
183         } else if (action.equals(Constants.ACTION_HIDE)) {
184             Log.v(TAG, "Receiver hide for " + intent.getData());
185             Cursor cursor =
186                     BluetoothMethodProxy.getInstance()
187                             .contentResolverQuery(
188                                     context.getContentResolver(),
189                                     intent.getData(),
190                                     null,
191                                     null,
192                                     null,
193                                     null);
194             if (cursor != null) {
195                 if (cursor.moveToFirst()) {
196                     int visibilityColumn = cursor.getColumnIndexOrThrow(BluetoothShare.VISIBILITY);
197                     int visibility = cursor.getInt(visibilityColumn);
198                     int userConfirmationColumn =
199                             cursor.getColumnIndexOrThrow(BluetoothShare.USER_CONFIRMATION);
200                     int userConfirmation = cursor.getInt(userConfirmationColumn);
201                     if (((userConfirmation == BluetoothShare.USER_CONFIRMATION_PENDING))
202                             && visibility == BluetoothShare.VISIBILITY_VISIBLE) {
203                         ContentValues values = new ContentValues();
204                         values.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN);
205                         BluetoothMethodProxy.getInstance()
206                                 .contentResolverUpdate(
207                                         context.getContentResolver(),
208                                         intent.getData(),
209                                         values,
210                                         null,
211                                         null);
212                         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                 && !Flags.oppFixMultipleNotificationsIssues()) {
219             Log.v(TAG, "Receiver ACTION_COMPLETE_HIDE");
220             ContentValues updateValues = new ContentValues();
221             updateValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN);
222             BluetoothMethodProxy.getInstance()
223                     .contentResolverUpdate(
224                             context.getContentResolver(),
225                             BluetoothShare.CONTENT_URI,
226                             updateValues,
227                             BluetoothOppNotification.WHERE_COMPLETED,
228                             null);
229         } else if (action.equals(Constants.ACTION_HIDE_COMPLETED_INBOUND_TRANSFER)
230                 && Flags.oppFixMultipleNotificationsIssues()) {
231             Log.v(TAG, "Received ACTION_HIDE_COMPLETED_INBOUND_TRANSFER");
232             ContentValues updateValues = new ContentValues();
233             updateValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN);
234             BluetoothMethodProxy.getInstance()
235                     .contentResolverUpdate(
236                             context.getContentResolver(),
237                             BluetoothShare.CONTENT_URI,
238                             updateValues,
239                             BluetoothOppNotification.WHERE_COMPLETED_INBOUND,
240                             null);
241         } else if (action.equals(Constants.ACTION_HIDE_COMPLETED_OUTBOUND_TRANSFER)
242                 && Flags.oppFixMultipleNotificationsIssues()) {
243             Log.v(TAG, "Received ACTION_HIDE_COMPLETED_OUTBOUND_TRANSFER");
244             ContentValues updateValues = new ContentValues();
245             updateValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN);
246             BluetoothMethodProxy.getInstance()
247                     .contentResolverUpdate(
248                             context.getContentResolver(),
249                             BluetoothShare.CONTENT_URI,
250                             updateValues,
251                             BluetoothOppNotification.WHERE_COMPLETED_OUTBOUND,
252                             null);
253         } else if (action.equals(BluetoothShare.TRANSFER_COMPLETED_ACTION)) {
254             Log.v(TAG, "Receiver Transfer Complete Intent for " + intent.getData());
255 
256             String toastMsg = null;
257             BluetoothOppTransferInfo transInfo =
258                     BluetoothOppUtility.queryRecord(context, intent.getData());
259             if (transInfo == null) {
260                 Log.e(TAG, "Error: Can not get data from db");
261                 ContentProfileErrorReportUtils.report(
262                         BluetoothProfile.OPP,
263                         BluetoothProtoEnums.BLUETOOTH_OPP_RECEIVER,
264                         BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_ERROR,
265                         1);
266                 return;
267             }
268 
269             if (transInfo.mHandoverInitiated) {
270                 // Deal with handover-initiated transfers separately
271                 Intent handoverIntent = new Intent(Constants.ACTION_BT_OPP_TRANSFER_DONE);
272                 if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND) {
273                     handoverIntent.putExtra(
274                             Constants.EXTRA_BT_OPP_TRANSFER_DIRECTION,
275                             Constants.DIRECTION_BLUETOOTH_INCOMING);
276                 } else {
277                     handoverIntent.putExtra(
278                             Constants.EXTRA_BT_OPP_TRANSFER_DIRECTION,
279                             Constants.DIRECTION_BLUETOOTH_OUTGOING);
280                 }
281                 handoverIntent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_ID, transInfo.mID);
282                 handoverIntent.putExtra(Constants.EXTRA_BT_OPP_ADDRESS, transInfo.mDestAddr);
283 
284                 if (BluetoothShare.isStatusSuccess(transInfo.mStatus)) {
285                     handoverIntent.putExtra(
286                             Constants.EXTRA_BT_OPP_TRANSFER_STATUS,
287                             Constants.HANDOVER_TRANSFER_STATUS_SUCCESS);
288                     handoverIntent.putExtra(
289                             Constants.EXTRA_BT_OPP_TRANSFER_URI, transInfo.mFileName);
290                     handoverIntent.putExtra(
291                             Constants.EXTRA_BT_OPP_TRANSFER_MIMETYPE, transInfo.mFileType);
292                 } else {
293                     handoverIntent.putExtra(
294                             Constants.EXTRA_BT_OPP_TRANSFER_STATUS,
295                             Constants.HANDOVER_TRANSFER_STATUS_FAILURE);
296                 }
297                 context.sendBroadcast(
298                         handoverIntent,
299                         Constants.HANDOVER_STATUS_PERMISSION,
300                         Utils.getTempBroadcastOptions().toBundle());
301                 return;
302             }
303 
304             if (BluetoothShare.isStatusSuccess(transInfo.mStatus)) {
305                 if (transInfo.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
306                     toastMsg = context.getString(R.string.notification_sent, transInfo.mFileName);
307                 } else if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND) {
308                     toastMsg =
309                             context.getString(R.string.notification_received, transInfo.mFileName);
310                 }
311 
312             } else if (BluetoothShare.isStatusError(transInfo.mStatus)) {
313                 if (transInfo.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
314                     toastMsg =
315                             context.getString(R.string.notification_sent_fail, transInfo.mFileName);
316                 } else if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND) {
317                     toastMsg = context.getString(R.string.download_fail_line1);
318                 }
319             }
320             Log.v(TAG, "Toast msg == " + toastMsg);
321             if (toastMsg != null) {
322                 Toast.makeText(context, toastMsg, Toast.LENGTH_SHORT).show();
323             }
324         }
325     }
326 
cancelNotification(Context context, int id)327     private void cancelNotification(Context context, int id) {
328         NotificationManager notMgr = context.getSystemService(NotificationManager.class);
329         if (notMgr == null) {
330             return;
331         }
332         notMgr.cancel(id);
333         Log.v(TAG, "notMgr.cancel called");
334     }
335 }
336