Commit b49d3493 authored by alexander.shalamov's avatar alexander.shalamov Committed by Commit bot

[webnfc] Cancel nfc.push operation when timeout expires

This CL adds missing timeout handling functionality [1]. When user
provides timeout value for the push operation, and NFC tag is not
in proximity for the duration of timeout value, operation must be
rejected with timeout error. Unit test is added to test new
functionality.

https://w3c.github.io/web-nfc/#the-push-method (Step 14).

BUG=714037

Review-Url: https://codereview.chromium.org/2851453004
Cr-Commit-Position: refs/heads/master@{#469294}
parent 11b6b379
...@@ -17,6 +17,7 @@ import android.nfc.NfcManager; ...@@ -17,6 +17,7 @@ import android.nfc.NfcManager;
import android.nfc.Tag; import android.nfc.Tag;
import android.nfc.TagLostException; import android.nfc.TagLostException;
import android.os.Build; import android.os.Build;
import android.os.Handler;
import android.os.Process; import android.os.Process;
import android.util.SparseArray; import android.util.SparseArray;
...@@ -103,6 +104,16 @@ public class NfcImpl implements Nfc { ...@@ -103,6 +104,16 @@ public class NfcImpl implements Nfc {
*/ */
private final SparseArray<NfcWatchOptions> mWatchers = new SparseArray<>(); private final SparseArray<NfcWatchOptions> mWatchers = new SparseArray<>();
/**
* Handler that runs delayed push timeout task.
*/
private final Handler mPushTimeoutHandler = new Handler();
/**
* Runnable responsible for cancelling push operation after specified timeout.
*/
private Runnable mPushTimeoutRunnable;
public NfcImpl(Context context) { public NfcImpl(Context context) {
int permission = int permission =
context.checkPermission(Manifest.permission.NFC, Process.myPid(), Process.myUid()); context.checkPermission(Manifest.permission.NFC, Process.myPid(), Process.myUid());
...@@ -162,7 +173,9 @@ public class NfcImpl implements Nfc { ...@@ -162,7 +173,9 @@ public class NfcImpl implements Nfc {
return; return;
} }
if (options.target == NfcPushTarget.PEER) { // Check NfcPushOptions that are not supported by Android platform.
if (options.target == NfcPushTarget.PEER || options.timeout < 0
|| (options.timeout > Long.MAX_VALUE && !Double.isInfinite(options.timeout))) {
callback.call(createError(NfcErrorType.NOT_SUPPORTED)); callback.call(createError(NfcErrorType.NOT_SUPPORTED));
return; return;
} }
...@@ -170,9 +183,13 @@ public class NfcImpl implements Nfc { ...@@ -170,9 +183,13 @@ public class NfcImpl implements Nfc {
// If previous pending push operation is not completed, cancel it. // If previous pending push operation is not completed, cancel it.
if (mPendingPushOperation != null) { if (mPendingPushOperation != null) {
mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CANCELLED)); mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CANCELLED));
cancelPushTimeoutTask();
} }
mPendingPushOperation = new PendingPushOperation(message, options, callback); mPendingPushOperation = new PendingPushOperation(message, options, callback);
// Schedule push timeout task for new #mPendingPushOperation.
schedulePushTimeoutTask(options);
enableReaderModeIfNeeded(); enableReaderModeIfNeeded();
processPendingPushOperation(); processPendingPushOperation();
} }
...@@ -196,10 +213,8 @@ public class NfcImpl implements Nfc { ...@@ -196,10 +213,8 @@ public class NfcImpl implements Nfc {
if (mPendingPushOperation == null) { if (mPendingPushOperation == null) {
callback.call(createError(NfcErrorType.NOT_FOUND)); callback.call(createError(NfcErrorType.NOT_FOUND));
} else { } else {
mPendingPushOperation.complete(createError(NfcErrorType.OPERATION_CANCELLED)); completePendingPushOperation(createError(NfcErrorType.OPERATION_CANCELLED));
mPendingPushOperation = null;
callback.call(null); callback.call(null);
disableReaderModeIfNeeded();
} }
} }
...@@ -432,18 +447,26 @@ public class NfcImpl implements Nfc { ...@@ -432,18 +447,26 @@ public class NfcImpl implements Nfc {
} }
/** /**
* Completes pending push operation. On error, invalidates #mTagHandler. * Handles completion of pending push operation, cancels timeout task and completes push
* operation. On error, invalidates #mTagHandler.
*/ */
private void pendingPushOperationCompleted(NfcError error) { private void pendingPushOperationCompleted(NfcError error) {
if (mPendingPushOperation != null) { completePendingPushOperation(error);
mPendingPushOperation.complete(error);
mPendingPushOperation = null;
disableReaderModeIfNeeded();
}
if (error != null) mTagHandler = null; if (error != null) mTagHandler = null;
} }
/**
* Completes pending push operation and disables reader mode if needed.
*/
private void completePendingPushOperation(NfcError error) {
if (mPendingPushOperation == null) return;
cancelPushTimeoutTask();
mPendingPushOperation.complete(error);
mPendingPushOperation = null;
disableReaderModeIfNeeded();
}
/** /**
* Checks whether there is a #mPendingPushOperation and writes data to NFC tag. In case of * Checks whether there is a #mPendingPushOperation and writes data to NFC tag. In case of
* exception calls pendingPushOperationCompleted() with appropriate error object. * exception calls pendingPushOperationCompleted() with appropriate error object.
...@@ -599,4 +622,33 @@ public class NfcImpl implements Nfc { ...@@ -599,4 +622,33 @@ public class NfcImpl implements Nfc {
} }
} }
} }
/**
* Schedules task that is executed after timeout and cancels pending push operation.
*/
private void schedulePushTimeoutTask(NfcPushOptions options) {
assert mPushTimeoutRunnable == null;
// Default timeout value.
if (Double.isInfinite(options.timeout)) return;
// Create and schedule timeout.
mPushTimeoutRunnable = new Runnable() {
@Override
public void run() {
completePendingPushOperation(createError(NfcErrorType.TIMER_EXPIRED));
}
};
mPushTimeoutHandler.postDelayed(mPushTimeoutRunnable, (long) options.timeout);
}
/**
* Cancels push timeout task.
*/
void cancelPushTimeoutTask() {
if (mPushTimeoutRunnable == null) return;
mPushTimeoutHandler.removeCallbacks(mPushTimeoutRunnable);
mPushTimeoutRunnable = null;
}
} }
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