1 /*
2  * Copyright (C) 2012 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.bluetooth.BluetoothDevice;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.os.Handler;
24 import android.os.Message;
25 import android.os.SystemClock;
26 import android.util.Log;
27 
28 import java.util.ArrayList;
29 
30 public class BluetoothOppHandover implements Handler.Callback {
31     static final String TAG = "BluetoothOppHandover";
32     static final boolean DBG = true;
33 
34     static final int STATE_INIT = 0;
35     static final int STATE_TURNING_ON = 1;
36     static final int STATE_WAITING = 2; // Need to wait for remote side turning on BT
37     static final int STATE_COMPLETE = 3;
38 
39     static final int MSG_START_SEND = 0;
40 
41     static final int REMOTE_BT_ENABLE_DELAY_MS = 5000;
42 
43     static final String ACTION_HANDOVER_SEND =
44             "android.nfc.handover.intent.action.HANDOVER_SEND";
45 
46     static final String ACTION_HANDOVER_SEND_MULTIPLE =
47             "android.nfc.handover.intent.action.HANDOVER_SEND_MULTIPLE";
48 
49     final Context mContext;
50     final BluetoothDevice mDevice;
51 
52     final ArrayList<Uri> mUris;
53     final boolean mRemoteActivating;
54     final Handler mHandler;
55     final Long mCreateTime;
56 
57     int mState;
58 
BluetoothOppHandover(Context context, BluetoothDevice device, ArrayList<Uri> uris, boolean remoteActivating)59     public BluetoothOppHandover(Context context, BluetoothDevice device, ArrayList<Uri> uris,
60             boolean remoteActivating) {
61         mContext = context;
62         mDevice = device;
63         mUris = uris;
64         mRemoteActivating = remoteActivating;
65         mCreateTime = SystemClock.elapsedRealtime();
66 
67         mHandler = new Handler(context.getMainLooper(), this);
68         mState = STATE_INIT;
69     }
70 
71     /**
72      * Main entry point. This method is usually called after construction,
73      * to begin the BT sequence. Must be called on Main thread.
74      */
start()75     public void start() {
76         if (mRemoteActivating) {
77             Long timeElapsed = SystemClock.elapsedRealtime() - mCreateTime;
78             if (timeElapsed < REMOTE_BT_ENABLE_DELAY_MS) {
79                 mHandler.sendEmptyMessageDelayed(MSG_START_SEND,
80                         REMOTE_BT_ENABLE_DELAY_MS - timeElapsed);
81             } else {
82                 // Already waited long enough for BT to come up
83                 // - start send.
84                 sendIntent();
85             }
86         } else {
87             // Remote BT enabled already, start send immediately
88             sendIntent();
89         }
90     }
91 
complete()92     void complete() {
93         mState = STATE_COMPLETE;
94     }
95 
sendIntent()96     void sendIntent() {
97         Intent intent = new Intent();
98         intent.setPackage("com.android.bluetooth");
99         String mimeType = MimeTypeUtil.getMimeTypeForUri(mContext, mUris.get(0));
100         intent.setType(mimeType);
101         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
102         for (Uri uri : mUris) {
103             // TODO we need to transfer our permission grant from NFC
104             // to the Bluetooth process. This works, but we don't have
105             // a good framework API for revoking permission yet.
106             try {
107                 mContext.grantUriPermission("com.android.bluetooth", uri,
108                         Intent.FLAG_GRANT_READ_URI_PERMISSION);
109             } catch (SecurityException e) {
110                 Log.e(TAG, "Failed to transfer permission to Bluetooth process.");
111             }
112         }
113         if (mUris.size() == 1) {
114             intent.setAction(ACTION_HANDOVER_SEND);
115             intent.putExtra(Intent.EXTRA_STREAM, mUris.get(0));
116         } else {
117             intent.setAction(ACTION_HANDOVER_SEND_MULTIPLE);
118             intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, mUris);
119         }
120         if (DBG) Log.d(TAG, "Handing off outging transfer to BT");
121         mContext.sendBroadcast(intent);
122 
123         complete();
124     }
125 
126 
127     @Override
handleMessage(Message msg)128     public boolean handleMessage(Message msg) {
129         if (msg.what == MSG_START_SEND) {
130             sendIntent();
131             return true;
132         }
133         return false;
134     }
135 }
136