Commit be1939b4 authored by The Android Open Source Project's avatar The Android Open Source Project
Browse files

Snapshot b80adb2c263702442cf2f2d771168400e6ceb9f8

Change-Id: I391d8e1be1a61e68b01f0db371dbb4ed3e5b5933
parent 525c2603
......@@ -52,5 +52,8 @@
android:noHistory="true"
/>
<service android:name=".handover.HandoverService"
android:process=":handover"
/>
</application>
</manifest>
......@@ -1276,6 +1276,7 @@ static void nfcManager_doDeselectSecureElement(JNIEnv *e, jobject o)
{
ALOGD ("%s: enter", __FUNCTION__);
bool stat = false;
bool bRestartDiscovery = false;
if (! sIsSecElemSelected)
{
......@@ -1290,6 +1291,12 @@ static void nfcManager_doDeselectSecureElement(JNIEnv *e, jobject o)
goto TheEnd;
}
if (sRfEnabled) {
// Stop RF Discovery if we were polling
startRfDiscovery (false);
bRestartDiscovery = true;
}
stat = SecureElement::getInstance().routeToDefault ();
sIsSecElemSelected = false;
......@@ -1299,6 +1306,9 @@ static void nfcManager_doDeselectSecureElement(JNIEnv *e, jobject o)
SecureElement::getInstance().deactivate (0xABCDEF);
TheEnd:
if (bRestartDiscovery)
startRfDiscovery (true);
//if nothing is active after this, then tell the controller to power down
if (! PowerSwitch::getInstance ().setModeOff (PowerSwitch::SE_ROUTING))
PowerSwitch::getInstance ().setLevel (PowerSwitch::LOW_POWER);
......
......@@ -1431,9 +1431,9 @@ void PeerToPeer::nfaClientCallback (tNFA_P2P_EVT p2pEvent, tNFA_P2P_EVT_DATA* ev
if ((pConn = sP2p.findConnection(eventData->disc.handle)) == NULL)
{
// If no connection, may be a client that is trying to connect
if ((pClient = sP2p.findClientCon ((tNFA_HANDLE)NFA_HANDLE_INVALID)) == NULL)
if ((pClient = sP2p.findClient (eventData->disc.handle)) == NULL)
{
ALOGE ("%s: NFA_P2P_DISC_EVT: can't find conn for NFA handle: 0x%04x", fn, eventData->disc.handle);
ALOGE ("%s: NFA_P2P_DISC_EVT: can't find client for NFA handle: 0x%04x", fn, eventData->disc.handle);
return;
}
// Unblock createDataLinkConn()
......
......@@ -156,12 +156,12 @@ bool SecureElement::initialize (nfc_jni_native_data* native)
if (GetNumValue("NFA_HCI_DEFAULT_DEST_GATE", &num, sizeof(num)))
mDestinationGate = num;
ALOGD ("%s: Default destination gate: %d", fn, mDestinationGate);
ALOGD ("%s: Default destination gate: 0x%X", fn, mDestinationGate);
// active SE, if not set active all SEs
if (GetNumValue("ACTIVE_SE", &num, sizeof(num)))
mActiveSeOverride = num;
ALOGD ("%s: Active SE override: %d", fn, mActiveSeOverride);
ALOGD ("%s: Active SE override: 0x%X", fn, mActiveSeOverride);
if (GetNumValue("OBERTHUR_WARM_RESET_COMMAND", &num, sizeof(num)))
{
......@@ -442,7 +442,7 @@ jintArray SecureElement::getListOfEeHandles (JNIEnv* e)
** Function: activate
**
** Description: Turn on the secure element.
** seID: ID of secure element.
** seID: ID of secure element; 0xF3 or 0xF4.
**
** Returns: True if ok.
**
......@@ -474,24 +474,16 @@ bool SecureElement::activate (jint seID)
return false;
}
mActiveEeHandle = getDefaultEeHandle();
ALOGD ("%s: active ee h=0x%X, override se=0x%X", fn, mActiveEeHandle, mActiveSeOverride);
if (mActiveEeHandle == NFA_HANDLE_INVALID)
{
ALOGE ("%s: ee not found", fn);
return false;
}
UINT16 override_se = 0;
UINT16 overrideEeHandle = 0;
if (mActiveSeOverride)
override_se = NFA_HANDLE_GROUP_EE | mActiveSeOverride;
overrideEeHandle = NFA_HANDLE_GROUP_EE | mActiveSeOverride;
if (mRfFieldIsOn) {
ALOGE("%s: RF field indication still on, resetting", fn);
mRfFieldIsOn = false;
}
ALOGD ("%s: override seid=0x%X", fn, override_se );
ALOGD ("%s: override ee h=0x%X", fn, overrideEeHandle );
//activate every discovered secure element
for (int index=0; index < mActualNumEe; index++)
{
......@@ -499,7 +491,7 @@ bool SecureElement::activate (jint seID)
if ((eeItem.ee_handle == EE_HANDLE_0xF3) || (eeItem.ee_handle == EE_HANDLE_0xF4))
{
if (override_se && (override_se != eeItem.ee_handle) )
if (overrideEeHandle && (overrideEeHandle != eeItem.ee_handle) )
continue; // do not enable all SEs; only the override one
if (eeItem.ee_status != NFC_NFCEE_STATUS_INACTIVE)
......@@ -524,18 +516,11 @@ bool SecureElement::activate (jint seID)
}
} //for
for (UINT8 xx = 0; xx < mActualNumEe; xx++)
{
if ((mEeInfo[xx].num_interface != 0) && (mEeInfo[xx].ee_interface[0] != NCI_NFCEE_INTERFACE_HCI_ACCESS) &&
(mEeInfo[xx].ee_status != NFC_NFCEE_STATUS_INACTIVE))
{
mActiveEeHandle = mEeInfo[xx].ee_handle;
break;
}
}
ALOGD ("%s: exit; ok=%u", fn, numActivatedEe > 0);
return numActivatedEe > 0;
mActiveEeHandle = getDefaultEeHandle();
if (mActiveEeHandle == NFA_HANDLE_INVALID)
ALOGE ("%s: ee handle not found", fn);
ALOGD ("%s: exit; active ee h=0x%X", fn, mActiveEeHandle);
return mActiveEeHandle != NFA_HANDLE_INVALID;
}
......@@ -544,7 +529,7 @@ bool SecureElement::activate (jint seID)
** Function: deactivate
**
** Description: Turn off the secure element.
** seID: ID of secure element.
** seID: ID of secure element; 0xF3 or 0xF4.
**
** Returns: True if ok.
**
......@@ -1417,7 +1402,7 @@ void SecureElement::adjustProtocolRoutes (RouteDataSet::Database* db, RouteSelec
{
tNFA_HANDLE eeHandle = NFA_EE_HANDLE_DH;
if (routeSelection == SecElemRoute)
eeHandle = getDefaultEeHandle ();
eeHandle = mActiveEeHandle;
ALOGD ("%s: route to default EE h=0x%X", fn, eeHandle);
SyncEventGuard guard (mRoutingEvent);
nfaStat = NFA_EeSetDefaultProtoRouting (eeHandle, protoMask, 0, 0);
......@@ -1573,7 +1558,7 @@ void SecureElement::adjustTechnologyRoutes (RouteDataSet::Database* db, RouteSel
{
tNFA_HANDLE eeHandle = NFA_EE_HANDLE_DH;
if (routeSelection == SecElemRoute)
eeHandle = getDefaultEeHandle ();
eeHandle = mActiveEeHandle;
ALOGD ("%s: route to default EE h=0x%X", fn, eeHandle);
SyncEventGuard guard (mRoutingEvent);
nfaStat = NFA_EeSetDefaultTechRouting (eeHandle, techMask, 0, 0);
......@@ -1975,26 +1960,31 @@ tNFA_EE_INFO *SecureElement::findEeByHandle (tNFA_HANDLE eeHandle)
*******************************************************************************/
tNFA_HANDLE SecureElement::getDefaultEeHandle ()
{
UINT16 overrideEeHandle = NFA_HANDLE_GROUP_EE | mActiveSeOverride;
// Find the first EE that is not the HCI Access i/f.
for (UINT8 xx = 0; xx < mActualNumEe; xx++)
{
if ((mEeInfo[xx].num_interface != 0) && (mEeInfo[xx].ee_interface[0] != NCI_NFCEE_INTERFACE_HCI_ACCESS) )
if (mActiveSeOverride && (overrideEeHandle != mEeInfo[xx].ee_handle))
continue; //skip all the EE's that are ignored
if ((mEeInfo[xx].num_interface != 0) &&
(mEeInfo[xx].ee_interface[0] != NCI_NFCEE_INTERFACE_HCI_ACCESS) &&
(mEeInfo[xx].ee_status != NFC_NFCEE_STATUS_INACTIVE))
return (mEeInfo[xx].ee_handle);
}
return NFA_HANDLE_INVALID;
}
/*******************************************************************************
**
** Function: findUiccByHandle
**
** Description: Find information about an execution environment.
** eeHandle: Handle of the execution environment.
**
** Returns: Information about the execution environment.
**
*******************************************************************************/
/*******************************************************************************
**
** Function: findUiccByHandle
**
** Description: Find information about an execution environment.
** eeHandle: Handle of the execution environment.
**
** Returns: Information about the execution environment.
**
*******************************************************************************/
tNFA_EE_DISCOVER_INFO *SecureElement::findUiccByHandle (tNFA_HANDLE eeHandle)
{
for (UINT8 index = 0; index < mUiccInfo.num_ee; index++)
......
package com.android.nfc;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.Application;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
import java.util.Iterator;
import java.util.List;
public class NfcApplication extends Application {
public static final String TAG = "NfcApplication";
static final String TAG = "NfcApplication";
static final String NFC_PROCESS = "com.android.nfc";
NfcService mNfcService;
public NfcApplication() {
......@@ -17,7 +23,23 @@ public class NfcApplication extends Application {
public void onCreate() {
super.onCreate();
if (UserHandle.myUserId() == 0) {
boolean isMainProcess = false;
// We start a service in a separate process to do
// handover transfer. We don't want to instantiate an NfcService
// object in those cases, hence check the name of the process
// to determine whether we're the main NFC service, or the
// handover process
ActivityManager am = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);
List processes = am.getRunningAppProcesses();
Iterator i = processes.iterator();
while (i.hasNext()) {
RunningAppProcessInfo appInfo = (RunningAppProcessInfo)(i.next());
if (appInfo.pid == Process.myPid()) {
isMainProcess = (NFC_PROCESS.equals(appInfo.processName));
break;
}
}
if (UserHandle.myUserId() == 0 && isMainProcess) {
mNfcService = new NfcService(this);
}
}
......
......@@ -43,6 +43,7 @@ import java.util.concurrent.atomic.AtomicReference;
*/
public class RegisteredComponentCache {
private static final String TAG = "RegisteredComponentCache";
private static final boolean DEBUG = false;
final Context mContext;
final String mAction;
......@@ -165,7 +166,9 @@ public class RegisteredComponentCache {
}
}
dump(components);
if (DEBUG) {
dump(components);
}
synchronized (this) {
mComponents = components;
......
......@@ -16,7 +16,6 @@
package com.android.nfc.handover;
import android.app.ActivityManager;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
......@@ -29,12 +28,10 @@ import android.content.IntentFilter;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Toast;
import com.android.nfc.handover.HandoverManager.HandoverPowerManager;
import com.android.nfc.R;
/**
......@@ -57,14 +54,13 @@ public class BluetoothHeadsetHandover implements BluetoothProfile.ServiceListene
static final int TIMEOUT_MS = 20000;
static final int STATE_INIT = 0;
static final int STATE_TURNING_ON = 1;
static final int STATE_WAITING_FOR_PROXIES = 2;
static final int STATE_INIT_COMPLETE = 3;
static final int STATE_WAITING_FOR_BOND_CONFIRMATION = 4;
static final int STATE_BONDING = 5;
static final int STATE_CONNECTING = 6;
static final int STATE_DISCONNECTING = 7;
static final int STATE_COMPLETE = 8;
static final int STATE_WAITING_FOR_PROXIES = 1;
static final int STATE_INIT_COMPLETE = 2;
static final int STATE_WAITING_FOR_BOND_CONFIRMATION = 3;
static final int STATE_BONDING = 4;
static final int STATE_CONNECTING = 5;
static final int STATE_DISCONNECTING = 6;
static final int STATE_COMPLETE = 7;
static final int RESULT_PENDING = 0;
static final int RESULT_CONNECTED = 1;
......@@ -80,7 +76,6 @@ public class BluetoothHeadsetHandover implements BluetoothProfile.ServiceListene
final Context mContext;
final BluetoothDevice mDevice;
final String mName;
final HandoverPowerManager mHandoverPowerManager;
final Callback mCallback;
final BluetoothAdapter mBluetoothAdapter;
......@@ -101,18 +96,21 @@ public class BluetoothHeadsetHandover implements BluetoothProfile.ServiceListene
}
public BluetoothHeadsetHandover(Context context, BluetoothDevice device, String name,
HandoverPowerManager powerManager, Callback callback) {
Callback callback) {
checkMainThread(); // mHandler must get get constructed on Main Thread for toasts to work
mContext = context;
mDevice = device;
mName = name;
mHandoverPowerManager = powerManager;
mCallback = callback;
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_INIT;
}
public boolean hasStarted() {
return mState != STATE_INIT;
}
/**
* Main entry point. This method is usually called after construction,
* to begin the BT sequence. Must be called on Main thread.
......@@ -156,18 +154,6 @@ public class BluetoothHeadsetHandover implements BluetoothProfile.ServiceListene
void nextStepInit() {
switch (mState) {
case STATE_INIT:
if (!mHandoverPowerManager.isBluetoothEnabled()) {
if (mHandoverPowerManager.enableBluetooth()) {
// Bluetooth is being enabled
mState = STATE_TURNING_ON;
} else {
toast(mContext.getString(R.string.failed_to_enable_bt));
complete(false);
}
break;
}
// fall-through
case STATE_TURNING_ON:
if (mA2dp == null || mHeadset == null) {
mState = STATE_WAITING_FOR_PROXIES;
if (!getProfileProxys()) {
......@@ -310,18 +296,7 @@ public class BluetoothHeadsetHandover implements BluetoothProfile.ServiceListene
void handleIntent(Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action) && mState == STATE_TURNING_ON) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
if (state == BluetoothAdapter.STATE_ON) {
nextStep();
} else if (state == BluetoothAdapter.STATE_OFF) {
toast(mContext.getString(R.string.failed_to_enable_bt));
complete(false);
}
return;
}
// Everything else requires the device to match...
// Everything requires the device to match...
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (!mDevice.equals(device)) return;
......@@ -387,19 +362,18 @@ public class BluetoothHeadsetHandover implements BluetoothProfile.ServiceListene
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_MEDIA_PLAY));
mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, null, null, 0, null, null);
mContext.sendOrderedBroadcast(intent, null);
intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_MEDIA_PLAY));
mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, null, null, 0, null, null);
mContext.sendOrderedBroadcast(intent, null);
}
void requestPairConfirmation() {
Intent dialogIntent = new Intent(mContext, ConfirmConnectActivity.class);
dialogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
dialogIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
mContext.startActivityAsUser(dialogIntent, new UserHandle(UserHandle.USER_CURRENT));
mContext.startActivity(dialogIntent);
}
final Handler mHandler = new Handler() {
......
......@@ -16,25 +16,17 @@
package com.android.nfc.handover;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
import com.android.nfc.handover.HandoverManager.HandoverPowerManager;
import com.android.nfc.R;
import java.util.ArrayList;
import java.util.Arrays;
......@@ -61,20 +53,19 @@ public class BluetoothOppHandover implements Handler.Callback {
final BluetoothDevice mDevice;
final Uri[] mUris;
final HandoverPowerManager mHandoverPowerManager;
final boolean mRemoteActivating;
final Handler mHandler;
final Long mCreateTime;
int mState;
Long mStartTime;
public BluetoothOppHandover(Context context, BluetoothDevice device, Uri[] uris,
HandoverPowerManager powerManager, boolean remoteActivating) {
boolean remoteActivating) {
mContext = context;
mDevice = device;
mUris = uris;
mHandoverPowerManager = powerManager;
mRemoteActivating = remoteActivating;
mCreateTime = SystemClock.elapsedRealtime();
mHandler = new Handler(context.getMainLooper(),this);
mState = STATE_INIT;
......@@ -104,33 +95,24 @@ public class BluetoothOppHandover implements Handler.Callback {
* to begin the BT sequence. Must be called on Main thread.
*/
public void start() {
mStartTime = SystemClock.elapsedRealtime();
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
mContext.registerReceiver(mReceiver, filter);
if (!mHandoverPowerManager.isBluetoothEnabled()) {
if (mHandoverPowerManager.enableBluetooth()) {
mState = STATE_TURNING_ON;
} else {
Toast.makeText(mContext, mContext.getString(R.string.beam_failed),
Toast.LENGTH_SHORT).show();
complete();
}
} else {
// BT already enabled
if (mRemoteActivating) {
mHandler.sendEmptyMessageDelayed(MSG_START_SEND, REMOTE_BT_ENABLE_DELAY_MS);
if (mRemoteActivating) {
Long timeElapsed = SystemClock.elapsedRealtime() - mCreateTime;
if (timeElapsed < REMOTE_BT_ENABLE_DELAY_MS) {
mHandler.sendEmptyMessageDelayed(MSG_START_SEND,
REMOTE_BT_ENABLE_DELAY_MS - timeElapsed);
} else {
// Remote BT enabled too, start send immediately
// Already waited long enough for BT to come up
// - start send.
sendIntent();
}
} else {
// Remote BT enabled already, start send immediately
sendIntent();
}
}
void complete() {
mState = STATE_COMPLETE;
mContext.unregisterReceiver(mReceiver);
}
void sendIntent() {
......@@ -153,32 +135,6 @@ public class BluetoothOppHandover implements Handler.Callback {
complete();
}
void handleIntent(Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action) && mState == STATE_TURNING_ON) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
if (state == BluetoothAdapter.STATE_ON) {
// Add additional delay if needed
Long timeElapsed = SystemClock.elapsedRealtime() - mStartTime;
if (mRemoteActivating && timeElapsed < REMOTE_BT_ENABLE_DELAY_MS) {
mHandler.sendEmptyMessageDelayed(MSG_START_SEND,
REMOTE_BT_ENABLE_DELAY_MS - timeElapsed);
} else {
sendIntent();
}
} else if (state == BluetoothAdapter.STATE_OFF) {
complete();
}
return;
}
}
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
handleIntent(intent);
}
};
@Override
public boolean handleMessage(Message msg) {
......
......@@ -210,7 +210,8 @@ public final class HandoverServer {
}
// We're done
mCallback.onHandoverRequestReceived();
break;
// We can process another handover transfer
byteStream = new ByteArrayOutputStream();
}
synchronized (HandoverServer.this) {
......
package com.android.nfc.handover;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.SoundPool;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
import com.android.nfc.R;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
public class HandoverService extends Service implements HandoverTransfer.Callback,
BluetoothHeadsetHandover.Callback {
static final String TAG = "HandoverService";
static final int MSG_REGISTER_CLIENT = 0;
static final int MSG_DEREGISTER_CLIENT = 1;
static final int MSG_START_INCOMING_TRANSFER = 2;
static final int MSG_START_OUTGOING_TRANSFER = 3;
static final int MSG_HEADSET_HANDOVER = 4;
static final String BUNDLE_TRANSFER = "transfer";
static final String EXTRA_HEADSET_DEVICE = "device";
static final String EXTRA_HEADSET_NAME = "headsetname";
static final String ACTION_CANCEL_HANDOVER_TRANSFER =
"com.android.nfc.handover.action.CANCEL_HANDOVER_TRANSFER";
static final String EXTRA_SOURCE_ADDRESS =
"com.android.nfc.handover.extra.SOURCE_ADDRESS";
static final String ACTION_BT_OPP_TRANSFER_PROGRESS =
"android.btopp.intent.action.BT_OPP_TRANSFER_PROGRESS";
static final String ACTION_BT_OPP_TRANSFER_DONE =
"android.btopp.intent.action.BT_OPP_TRANSFER_DONE";
static final String EXTRA_BT_OPP_TRANSFER_STATUS =
"android.btopp.intent.extra.BT_OPP_TRANSFER_STATUS";
static final String EXTRA_BT_OPP_TRANSFER_MIMETYPE =
"android.btopp.intent.extra.BT_OPP_TRANSFER_MIMETYPE";
static final String EXTRA_BT_OPP_ADDRESS =
"android.btopp.intent.extra.BT_OPP_ADDRESS";
static final int HANDOVER_TRANSFER_STATUS_SUCCESS = 0;
static final int HANDOVER_TRANSFER_STATUS_FAILURE = 1;
static final String EXTRA_BT_OPP_TRANSFER_DIRECTION =
"android.btopp.intent.extra.BT_OPP_TRANSFER_DIRECTION";
static final int DIRECTION_BLUETOOTH_INCOMING = 0;
static final int DIRECTION_BLUETOOTH_OUTGOING = 1;
static final String EXTRA_BT_OPP_TRANSFER_ID =
"android.btopp.intent.extra.BT_OPP_TRANSFER_ID";
static final String EXTRA_BT_OPP_TRANSFER_PROGRESS =
"android.btopp.intent.extra.BT_OPP_TRANSFER_PROGRESS";
static final String EXTRA_BT_OPP_TRANSFER_URI =
"android.btopp.intent.extra.BT_OPP_TRANSFER_URI";
// permission needed to be able to receive handover status requests
static final String HANDOVER_STATUS_PERMISSION =
"com.android.permission.HANDOVER_STATUS";
// Variables below only accessed on main thread
final Queue<BluetoothOppHandover> mPendingOutTransfers;
final HashMap<Pair<String, Boolean>, HandoverTransfer> mTransfers;
final Messenger mMessenger;
SoundPool mSoundPool;
int mSuccessSound;
BluetoothAdapter mBluetoothAdapter;
Messenger mClient;
Handler mHandler;
BluetoothHeadsetHandover mBluetoothHeadsetHandover;
boolean mBluetoothHeadsetConnected;
boolean mBluetoothEnabledByNfc;
public HandoverService() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mPendingOutTransfers = new LinkedList<BluetoothOppHandover>();
mTransfers = new HashMap<Pair<String, Boolean>, HandoverTransfer>();
mHandler = new MessageHandler();
mMessenger = new Messenger(mHandler);
mBluetoothHeadsetConnected = false;
mBluetoothEnabledByNfc = false;
}
@Override
public void onCreate() {
super.onCreate();
mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
mSuccessSound = mSoundPool.load(this, R.raw.end, 1);
IntentFilter filter = new IntentFilter(ACTION_BT_OPP_TRANSFER_DONE);
filter.addAction(ACTION_BT_OPP_TRANSFER_PROGRESS);
filter.addAction(ACTION_CANCEL_HANDOVER_TRANSFER);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mReceiver, filter, HANDOVER_STATUS_PERMISSION, mHandler);
}
@Override
public void onDestroy() {
super.onDestroy();
if (mSoundPool != null) {
mSoundPool.release();
}
unregisterReceiver(mReceiver);
}
void doOutgoingTransfer(Message msg) {
Bundle msgData = msg.getData();
msgData.setClassLoader(getClassLoader());
PendingHandoverTransfer pendingTransfer = (PendingHandoverTransfer)
msgData.getParcelable(BUNDLE_TRANSFER);
createHandoverTransfer(pendingTransfer);
// Create the actual transfer
BluetoothOppHandover handover = new BluetoothOppHandover(HandoverService.this,
pendingTransfer.remoteDevice, pendingTransfer.uris,
pendingTransfer.remoteActivating);
if (mBluetoothAdapter.isEnabled()) {
// Start the transfer
handover.start();
} else {
if (!enableBluetooth()) {
Log.e(TAG, "Error enabling Bluetooth.");
notifyClientTransferComplete(pendingTransfer.id);
return;
}
mPendingOutTransfers.add(handover);
// Queue the transfer and enable Bluetooth - when it is enabled
// the transfer will be started.
}
}
void doIncomingTransfer(Message msg) {
Bundle msgData = msg.getData();
msgData.setClassLoader(getClassLoader());
PendingHandoverTransfer pendingTransfer = (PendingHandoverTransfer)
msgData.getParcelable(BUNDLE_TRANSFER);
if (!mBluetoothAdapter.isEnabled() && !enableBluetooth()) {
Log.e(TAG, "Error enabling Bluetooth.");
notifyClientTransferComplete(pendingTransfer.id);
return;
}
createHandoverTransfer(pendingTransfer);
// Remote device will connect and finish the transfer
}
void doHeadsetHandover(Message msg) {
Bundle msgData = msg.getData();
BluetoothDevice device = (BluetoothDevice) msgData.getParcelable(EXTRA_HEADSET_DEVICE);
String name = (String) msgData.getString(EXTRA_HEADSET_NAME);
mBluetoothHeadsetHandover = new BluetoothHeadsetHandover(HandoverService.this,
device, name, HandoverService.this);
if (mBluetoothAdapter.isEnabled()) {
mBluetoothHeadsetHandover.start();
} else {
// Once BT is enabled, the headset pairing will be started
if (!enableBluetooth()) {
Log.e(TAG, "Error enabling Bluetooth.");
mBluetoothHeadsetHandover = null;
}
}
}
void startPendingTransfers() {
while (!mPendingOutTransfers.isEmpty()) {
BluetoothOppHandover handover = mPendingOutTransfers.remove();
handover.start();
}
}
class MessageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClient = msg.replyTo;
break;
case MSG_DEREGISTER_CLIENT:
mClient = null;
break;
case MSG_START_INCOMING_TRANSFER:
doIncomingTransfer(msg);
break;
case MSG_START_OUTGOING_TRANSFER:
doOutgoingTransfer(msg);
break;
case MSG_HEADSET_HANDOVER:
doHeadsetHandover(msg);
break;
}
}
}
boolean enableBluetooth() {
if (!mBluetoothAdapter.isEnabled()) {
mBluetoothEnabledByNfc = true;
return mBluetoothAdapter.enableNoAutoConnect();
}
return true;
}
void disableBluetoothIfNeeded() {
if (!mBluetoothEnabledByNfc) return;
if (mTransfers.size() == 0 && !mBluetoothHeadsetConnected) {
mBluetoothAdapter.disable();
mBluetoothEnabledByNfc = false;
}
}
void createHandoverTransfer(PendingHandoverTransfer pendingTransfer) {
Pair<String, Boolean> key = new Pair<String, Boolean>(
pendingTransfer.remoteDevice.getAddress(), pendingTransfer.incoming);
if (mTransfers.containsKey(key)) {
HandoverTransfer transfer = mTransfers.get(key);
if (!transfer.isRunning()) {
mTransfers.remove(key); // new one created below
} else {
// There is already a transfer running to this
// device - it will automatically get combined
// with the existing transfer.
notifyClientTransferComplete(pendingTransfer.id);
return;
}
}
HandoverTransfer transfer = new HandoverTransfer(this, this, pendingTransfer);
mTransfers.put(key, transfer);
transfer.updateNotification();
}
HandoverTransfer findHandoverTransfer(String sourceAddress, boolean incoming) {
Pair<String, Boolean> key = new Pair<String, Boolean>(sourceAddress, incoming);
if (mTransfers.containsKey(key)) {
HandoverTransfer transfer = mTransfers.get(key);
if (transfer.isRunning()) {
return transfer;
}
}
return null;
}
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
if (state == BluetoothAdapter.STATE_ON) {
// If there is a pending headset pairing, start it
if (mBluetoothHeadsetHandover != null &&
!mBluetoothHeadsetHandover.hasStarted()) {
mBluetoothHeadsetHandover.start();
}
// Start any pending transfers
startPendingTransfers();
} else if (state == BluetoothAdapter.STATE_OFF) {
mBluetoothEnabledByNfc = false;
mBluetoothHeadsetConnected = false;
}
}
else if (action.equals(ACTION_CANCEL_HANDOVER_TRANSFER)) {
String sourceAddress = intent.getStringExtra(EXTRA_SOURCE_ADDRESS);
HandoverTransfer transfer = findHandoverTransfer(sourceAddress, true);
if (transfer != null) {
transfer.cancel();
}
} else if (action.equals(ACTION_BT_OPP_TRANSFER_PROGRESS) ||
action.equals(ACTION_BT_OPP_TRANSFER_DONE)) {
int direction = intent.getIntExtra(EXTRA_BT_OPP_TRANSFER_DIRECTION, -1);
int id = intent.getIntExtra(EXTRA_BT_OPP_TRANSFER_ID, -1);
String sourceAddress = intent.getStringExtra(EXTRA_BT_OPP_ADDRESS);
if (direction == -1 || id == -1 || sourceAddress == null) return;
boolean incoming = (direction == DIRECTION_BLUETOOTH_INCOMING);
HandoverTransfer transfer = findHandoverTransfer(sourceAddress, incoming);
if (transfer == null) {
// There is no transfer running for this source address; most likely
// the transfer was cancelled. We need to tell BT OPP to stop transferring
// in case this was an incoming transfer
Intent cancelIntent = new Intent("android.btopp.intent.action.STOP_HANDOVER_TRANSFER");
cancelIntent.putExtra(EXTRA_BT_OPP_TRANSFER_ID, id);
sendBroadcast(cancelIntent);
return;
}
if (action.equals(ACTION_BT_OPP_TRANSFER_DONE)) {
int handoverStatus = intent.getIntExtra(EXTRA_BT_OPP_TRANSFER_STATUS,
HANDOVER_TRANSFER_STATUS_FAILURE);
if (handoverStatus == HANDOVER_TRANSFER_STATUS_SUCCESS) {
String uriString = intent.getStringExtra(EXTRA_BT_OPP_TRANSFER_URI);
String mimeType = intent.getStringExtra(EXTRA_BT_OPP_TRANSFER_MIMETYPE);
Uri uri = Uri.parse(uriString);
if (uri.getScheme() == null) {
uri = Uri.fromFile(new File(uri.getPath()));
}
transfer.finishTransfer(true, uri, mimeType);
} else {
transfer.finishTransfer(false, null, null);
}
} else if (action.equals(ACTION_BT_OPP_TRANSFER_PROGRESS)) {
float progress = intent.getFloatExtra(EXTRA_BT_OPP_TRANSFER_PROGRESS, 0.0f);
transfer.updateFileProgress(progress);
}
}
}
};
void notifyClientTransferComplete(int transferId) {
if (mClient != null) {
Message msg = Message.obtain(null, HandoverManager.MSG_HANDOVER_COMPLETE);
msg.arg1 = transferId;
try {
mClient.send(msg);
} catch (RemoteException e) {
// Ignore
}
}
}
@Override
public void onTransferComplete(HandoverTransfer transfer, boolean success) {
// Called on the main thread
// First, remove the transfer from our list
Iterator it = mTransfers.entrySet().iterator();
while (it.hasNext()) {
Map.Entry hashPair = (Map.Entry)it.next();
HandoverTransfer transferEntry = (HandoverTransfer) hashPair.getValue();
if (transferEntry == transfer) {
it.remove();
}
}
// Notify any clients of the service
notifyClientTransferComplete(transfer.getTransferId());
// Play success sound
if (success) {
mSoundPool.play(mSuccessSound, 1.0f, 1.0f, 0, 0, 1.0f);
}
disableBluetoothIfNeeded();
}
@Override
public void onBluetoothHeadsetHandoverComplete(boolean connected) {
// Called on the main thread
mBluetoothHeadsetHandover = null;
mBluetoothHeadsetConnected = connected;
if (mClient != null) {
Message msg = Message.obtain(null,
connected ? HandoverManager.MSG_HEADSET_CONNECTED
: HandoverManager.MSG_HEADSET_NOT_CONNECTED);
try {
mClient.send(msg);
} catch (RemoteException e) {
// Ignore
}
}
disableBluetoothIfNeeded();
}
}
package com.android.nfc.handover;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Notification.Builder;
import android.bluetooth.BluetoothDevice;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
import com.android.nfc.R;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
/**
* A HandoverTransfer object represents a set of files
* that were received through NFC connection handover
* from the same source address.
*
* For Bluetooth, files are received through OPP, and
* we have no knowledge how many files will be transferred
* as part of a single transaction.
* Hence, a transfer has a notion of being "alive": if
* the last update to a transfer was within WAIT_FOR_NEXT_TRANSFER_MS
* milliseconds, we consider a new file transfer from the
* same source address as part of the same transfer.
* The corresponding URIs will be grouped in a single folder.
*
*/
public class HandoverTransfer implements Handler.Callback,
MediaScannerConnection.OnScanCompletedListener {
interface Callback {
void onTransferComplete(HandoverTransfer transfer, boolean success);
};
static final String TAG = "HandoverTransfer";
static final Boolean DBG = true;
// In the states below we still accept new file transfer
static final int STATE_NEW = 0;
static final int STATE_IN_PROGRESS = 1;
static final int STATE_W4_NEXT_TRANSFER = 2;
// In the states below no new files are accepted.
static final int STATE_W4_MEDIA_SCANNER = 3;
static final int STATE_FAILED = 4;
static final int STATE_SUCCESS = 5;
static final int STATE_CANCELLED = 6;
static final int MSG_NEXT_TRANSFER_TIMER = 0;
static final int MSG_TRANSFER_TIMEOUT = 1;
// We need to receive an update within this time period
// to still consider this transfer to be "alive" (ie
// a reason to keep the handover transport enabled).
static final int ALIVE_CHECK_MS = 20000;
// The amount of time to wait for a new transfer
// once the current one completes.
static final int WAIT_FOR_NEXT_TRANSFER_MS = 4000;
static final String BEAM_DIR = "beam";
final boolean mIncoming; // whether this is an incoming transfer
final int mTransferId; // Unique ID of this transfer used for notifications
final PendingIntent mCancelIntent;
final Context mContext;
final Handler mHandler;
final NotificationManager mNotificationManager;
final BluetoothDevice mRemoteDevice;
final Callback mCallback;
// Variables below are only accessed on the main thread
int mState;
boolean mCalledBack;
Long mLastUpdate; // Last time an event occurred for this transfer
float mProgress; // Progress in range [0..1]
ArrayList<Uri> mBtUris; // Received uris from Bluetooth OPP
ArrayList<String> mBtMimeTypes; // Mime-types received from Bluetooth OPP
ArrayList<String> mPaths; // Raw paths on the filesystem for Beam-stored files
HashMap<String, String> mMimeTypes; // Mime-types associated with each path
HashMap<String, Uri> mMediaUris; // URIs found by the media scanner for each path
int mUrisScanned;
public HandoverTransfer(Context context, Callback callback,
PendingHandoverTransfer pendingTransfer) {
mContext = context;
mCallback = callback;
mRemoteDevice = pendingTransfer.remoteDevice;
mIncoming = pendingTransfer.incoming;
mTransferId = pendingTransfer.id;
mLastUpdate = SystemClock.elapsedRealtime();
mProgress = 0.0f;
mState = STATE_NEW;
mBtUris = new ArrayList<Uri>();
mBtMimeTypes = new ArrayList<String>();
mPaths = new ArrayList<String>();
mMimeTypes = new HashMap<String, String>();
mMediaUris = new HashMap<String, Uri>();
mCancelIntent = buildCancelIntent();
mUrisScanned = 0;
mHandler = new Handler(Looper.getMainLooper(), this);
mHandler.sendEmptyMessageDelayed(MSG_TRANSFER_TIMEOUT, ALIVE_CHECK_MS);
mNotificationManager = (NotificationManager) mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
}
void whitelistOppDevice(BluetoothDevice device) {
if (DBG) Log.d(TAG, "Whitelisting " + device + " for BT OPP");
Intent intent = new Intent(HandoverManager.ACTION_WHITELIST_DEVICE);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
}
public void updateFileProgress(float progress) {
if (!isRunning()) return; // Ignore when we're no longer running
mHandler.removeMessages(MSG_NEXT_TRANSFER_TIMER);
this.mProgress = progress;
// We're still receiving data from this device - keep it in
// the whitelist for a while longer
if (mIncoming) whitelistOppDevice(mRemoteDevice);
updateStateAndNotification(STATE_IN_PROGRESS);
}
public void finishTransfer(boolean success, Uri uri, String mimeType) {
if (!isRunning()) return; // Ignore when we're no longer running
if (success && uri != null) {
if (DBG) Log.d(TAG, "Transfer success, uri " + uri + " mimeType " + mimeType);
this.mProgress = 1.0f;
if (mimeType == null) {
mimeType = BluetoothOppHandover.getMimeTypeForUri(mContext, uri);
}
if (mimeType != null) {
mBtUris.add(uri);
mBtMimeTypes.add(mimeType);
} else {
if (DBG) Log.d(TAG, "Could not get mimeType for file.");
}
} else {
Log.e(TAG, "Handover transfer failed");
// Do wait to see if there's another file coming.
}
mHandler.removeMessages(MSG_NEXT_TRANSFER_TIMER);
mHandler.sendEmptyMessageDelayed(MSG_NEXT_TRANSFER_TIMER, WAIT_FOR_NEXT_TRANSFER_MS);
updateStateAndNotification(STATE_W4_NEXT_TRANSFER);
}
public boolean isRunning() {
if (mState != STATE_NEW && mState != STATE_IN_PROGRESS && mState != STATE_W4_NEXT_TRANSFER) {
return false;
} else {
return true;
}
}
void cancel() {
if (!isRunning()) return;
// Delete all files received so far
for (Uri uri : mBtUris) {
File file = new File(uri.getPath());
if (file.exists()) file.delete();
}
updateStateAndNotification(STATE_CANCELLED);
}
void updateNotification() {
if (!mIncoming) return; // No notifications for outgoing transfers
Builder notBuilder = new Notification.Builder(mContext);
if (mState == STATE_NEW || mState == STATE_IN_PROGRESS ||
mState == STATE_W4_NEXT_TRANSFER || mState == STATE_W4_MEDIA_SCANNER) {
notBuilder.setAutoCancel(false);
notBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
notBuilder.setTicker(mContext.getString(R.string.beam_progress));
notBuilder.setContentTitle(mContext.getString(R.string.beam_progress));
notBuilder.addAction(R.drawable.ic_menu_cancel_holo_dark,
mContext.getString(R.string.cancel), mCancelIntent);
notBuilder.setDeleteIntent(mCancelIntent);
// We do have progress indication on a per-file basis, but in a multi-file
// transfer we don't know the total progress. So for now, just show an
// indeterminate progress bar.
notBuilder.setProgress(100, 0, true);
} else if (mState == STATE_SUCCESS) {
notBuilder.setAutoCancel(true);
notBuilder.setSmallIcon(android.R.drawable.stat_sys_download_done);
notBuilder.setTicker(mContext.getString(R.string.beam_complete));
notBuilder.setContentTitle(mContext.getString(R.string.beam_complete));
notBuilder.setContentText(mContext.getString(R.string.beam_touch_to_view));
Intent viewIntent = buildViewIntent();
PendingIntent contentIntent = PendingIntent.getActivity(
mContext, 0, viewIntent, 0, null);
notBuilder.setContentIntent(contentIntent);
} else if (mState == STATE_FAILED) {
notBuilder.setAutoCancel(false);
notBuilder.setSmallIcon(android.R.drawable.stat_sys_download_done);
notBuilder.setTicker(mContext.getString(R.string.beam_failed));
notBuilder.setContentTitle(mContext.getString(R.string.beam_failed));
} else if (mState == STATE_CANCELLED) {
notBuilder.setAutoCancel(false);
notBuilder.setSmallIcon(android.R.drawable.stat_sys_download_done);
notBuilder.setTicker(mContext.getString(R.string.beam_canceled));
notBuilder.setContentTitle(mContext.getString(R.string.beam_canceled));
} else {
return;
}
mNotificationManager.notify(null, mTransferId, notBuilder.build());
}
void updateStateAndNotification(int newState) {
this.mState = newState;
this.mLastUpdate = SystemClock.elapsedRealtime();
if (mHandler.hasMessages(MSG_TRANSFER_TIMEOUT)) {
// Update timeout timer
mHandler.removeMessages(MSG_TRANSFER_TIMEOUT);
mHandler.sendEmptyMessageDelayed(MSG_TRANSFER_TIMEOUT, ALIVE_CHECK_MS);
}
updateNotification();
if ((mState == STATE_SUCCESS || mState == STATE_FAILED || mState == STATE_CANCELLED)
&& !mCalledBack) {
mCalledBack = true;
// Notify that we're done with this transfer
mCallback.onTransferComplete(this, mState == STATE_SUCCESS);
}
}
void processFiles() {
// Check the amount of files we received in this transfer;
// If more than one, create a separate directory for it.
String extRoot = Environment.getExternalStorageDirectory().getPath();
File beamPath = new File(extRoot + "/" + BEAM_DIR);
if (!checkMediaStorage(beamPath) || mBtUris.size() == 0) {
Log.e(TAG, "Media storage not valid or no uris received.");
updateStateAndNotification(STATE_FAILED);
return;
}
if (mBtUris.size() > 1) {
beamPath = generateMultiplePath(extRoot + "/" + BEAM_DIR + "/");
if (!beamPath.isDirectory() && !beamPath.mkdir()) {
Log.e(TAG, "Failed to create multiple path " + beamPath.toString());
updateStateAndNotification(STATE_FAILED);
return;
}
}
for (int i = 0; i < mBtUris.size(); i++) {
Uri uri = mBtUris.get(i);
String mimeType = mBtMimeTypes.get(i);
File srcFile = new File(uri.getPath());
File dstFile = generateUniqueDestination(beamPath.getAbsolutePath(),
uri.getLastPathSegment());
if (!srcFile.renameTo(dstFile)) {
if (DBG) Log.d(TAG, "Failed to rename from " + srcFile + " to " + dstFile);
srcFile.delete();
return;
} else {
mPaths.add(dstFile.getAbsolutePath());
mMimeTypes.put(dstFile.getAbsolutePath(), mimeType);
if (DBG) Log.d(TAG, "Did successful rename from " + srcFile + " to " + dstFile);
}
}
// We can either add files to the media provider, or provide an ACTION_VIEW
// intent to the file directly. We base this decision on the mime type
// of the first file; if it's media the platform can deal with,
// use the media provider, if it's something else, just launch an ACTION_VIEW
// on the file.
String mimeType = mMimeTypes.get(mPaths.get(0));
if (mimeType.startsWith("image/") || mimeType.startsWith("video/") ||
mimeType.startsWith("audio/")) {
String[] arrayPaths = new String[mPaths.size()];
MediaScannerConnection.scanFile(mContext, mPaths.toArray(arrayPaths), null, this);
updateStateAndNotification(STATE_W4_MEDIA_SCANNER);
} else {
// We're done.
updateStateAndNotification(STATE_SUCCESS);
}
}
public int getTransferId() {
return mTransferId;
}
public boolean handleMessage(Message msg) {
if (msg.what == MSG_NEXT_TRANSFER_TIMER) {
// We didn't receive a new transfer in time, finalize this one
if (mIncoming) {
processFiles();
} else {
updateStateAndNotification(STATE_SUCCESS);
}
return true;
} else if (msg.what == MSG_TRANSFER_TIMEOUT) {
// No update on this transfer for a while, check
// to see if it's still running, and fail it if it is.
if (isRunning()) {
updateStateAndNotification(STATE_FAILED);
}
}
return false;
}
public synchronized void onScanCompleted(String path, Uri uri) {
if (DBG) Log.d(TAG, "Scan completed, path " + path + " uri " + uri);
if (uri != null) {
mMediaUris.put(path, uri);
}
mUrisScanned++;
if (mUrisScanned == mPaths.size()) {
// We're done
updateStateAndNotification(STATE_SUCCESS);
}
}
boolean checkMediaStorage(File path) {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
if (!path.isDirectory() && !path.mkdir()) {
Log.e(TAG, "Not dir or not mkdir " + path.getAbsolutePath());
return false;
}
return true;
} else {
Log.e(TAG, "External storage not mounted, can't store file.");
return false;
}
}
Intent buildViewIntent() {
if (mPaths.size() == 0) return null;
Intent viewIntent = new Intent(Intent.ACTION_VIEW);
String filePath = mPaths.get(0);
Uri mediaUri = mMediaUris.get(filePath);
Uri uri = mediaUri != null ? mediaUri :
Uri.parse(ContentResolver.SCHEME_FILE + "://" + filePath);
viewIntent.setDataAndTypeAndNormalize(uri, mMimeTypes.get(filePath));
viewIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return viewIntent;
}
PendingIntent buildCancelIntent() {
Intent intent = new Intent(HandoverService.ACTION_CANCEL_HANDOVER_TRANSFER);
intent.putExtra(HandoverService.EXTRA_SOURCE_ADDRESS, mRemoteDevice.getAddress());
PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
return pi;
}
File generateUniqueDestination(String path, String fileName) {
int dotIndex = fileName.lastIndexOf(".");
String extension = null;
String fileNameWithoutExtension = null;
if (dotIndex < 0) {
extension = "";
fileNameWithoutExtension = fileName;
} else {
extension = fileName.substring(dotIndex);
fileNameWithoutExtension = fileName.substring(0, dotIndex);
}
File dstFile = new File(path + File.separator + fileName);
int count = 0;
while (dstFile.exists()) {
dstFile = new File(path + File.separator + fileNameWithoutExtension + "-" +
Integer.toString(count) + extension);
count++;
}
return dstFile;
}
File generateMultiplePath(String beamRoot) {
// Generate a unique directory with the date
String format = "yyyy-MM-dd";
SimpleDateFormat sdf = new SimpleDateFormat(format);
String newPath = beamRoot + "beam-" + sdf.format(new Date());
File newFile = new File(newPath);
int count = 0;
while (newFile.exists()) {
newPath = beamRoot + "beam-" + sdf.format(new Date()) + "-" +
Integer.toString(count);
newFile = new File(newPath);
count++;
}
return newFile;
}
}
package com.android.nfc.handover;
import android.bluetooth.BluetoothDevice;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
public class PendingHandoverTransfer implements Parcelable {
public int id;
public boolean incoming;
public BluetoothDevice remoteDevice;
public boolean remoteActivating;
public Uri[] uris;
PendingHandoverTransfer(int id, boolean incoming, BluetoothDevice remoteDevice,
boolean remoteActivating, Uri[] uris) {
this.id = id;
this.incoming = incoming;
this.remoteDevice = remoteDevice;
this.remoteActivating = remoteActivating;
this.uris = uris;
}
public static final Parcelable.Creator<PendingHandoverTransfer> CREATOR
= new Parcelable.Creator<PendingHandoverTransfer>() {
public PendingHandoverTransfer createFromParcel(Parcel in) {
int id = in.readInt();
boolean incoming = (in.readInt() == 1) ? true : false;
BluetoothDevice remoteDevice = in.readParcelable(getClass().getClassLoader());
boolean remoteActivating = (in.readInt() == 1) ? true : false;
int numUris = in.readInt();
Uri[] uris = null;
if (numUris > 0) {
uris = new Uri[numUris];
in.readTypedArray(uris, Uri.CREATOR);
}
return new PendingHandoverTransfer(id, incoming, remoteDevice,
remoteActivating, uris);
}
@Override
public PendingHandoverTransfer[] newArray(int size) {
return new PendingHandoverTransfer[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeInt(incoming ? 1 : 0);
dest.writeParcelable(remoteDevice, 0);
dest.writeInt(remoteActivating ? 1 : 0);
dest.writeInt(uris != null ? uris.length : 0);
if (uris != null && uris.length > 0) {
dest.writeTypedArray(uris, 0);
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment