Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(106)

Unified Diff: ios/chrome/common/physical_web/physical_web_scanner.mm

Issue 2413923002: Enable lost URL detection in the Physical Web scanner (Closed)
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ios/chrome/common/physical_web/physical_web_scanner.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ios/chrome/common/physical_web/physical_web_scanner.mm
diff --git a/ios/chrome/common/physical_web/physical_web_scanner.mm b/ios/chrome/common/physical_web/physical_web_scanner.mm
index 02c5d0c0c03d6f1fb5c979a23eb6c11217c30bd8..4a0fb9e496331df2fdefdd6890ed5f390e0e0c61 100644
--- a/ios/chrome/common/physical_web/physical_web_scanner.mm
+++ b/ios/chrome/common/physical_web/physical_web_scanner.mm
@@ -25,6 +25,12 @@ namespace {
NSString* const kUriBeaconServiceUUID = @"FED8";
NSString* const kEddystoneBeaconServiceUUID = @"FEAA";
+// The length of time in seconds since a URL was last seen before it should be
+// considered lost (ie, no longer nearby).
+const NSTimeInterval kLostThresholdSeconds = 15.0;
+// The time interval in seconds between checks for lost URLs.
+const NSTimeInterval kUpdateIntervalSeconds = 6.0;
+
enum BeaconType {
BEACON_TYPE_NONE,
BEACON_TYPE_URIBEACON,
@@ -41,8 +47,14 @@ enum BeaconType {
+ (PhysicalWebDevice*)newDeviceFromData:(NSData*)data
rssi:(int)rssi
type:(BeaconType)type;
-// Starts the CoreBluetooth scanner when the bluetooth is powered on.
+// Starts the CoreBluetooth scanner when the bluetooth is powered on and starts
+// the update timer.
- (void)reallyStart;
+// Stops the CoreBluetooth scanner and update timer.
+- (void)reallyStop;
+// Timer callback to check for lost URLs based on the elapsed time since they
+// were last seen.
+- (void)onUpdate:(NSTimer*)timer;
// Requests metadata of a device if the same URL has not been requested before.
- (void)requestMetadataForDevice:(PhysicalWebDevice*)device;
// Returns the beacon type given the advertisement data.
@@ -72,12 +84,21 @@ enum BeaconType {
base::scoped_nsobject<NSMutableSet> finalUrls_;
// CoreBluetooth scanner.
base::scoped_nsobject<CBCentralManager> centralManager_;
+ // When YES, we will notify the delegate if a previously nearby URL is lost
+ // and remove it from the list of nearby devices.
+ BOOL onLostDetectionEnabled_;
// The value is YES if network requests can be sent.
BOOL networkRequestEnabled_;
// List of unresolved PhysicalWebDevice when network requests are not enabled.
base::scoped_nsobject<NSMutableArray> unresolvedDevices_;
+ // Map from URLs to timestamps of the last time the URL was nearby.
+ base::scoped_nsobject<NSMutableDictionary> scanTimestamps_;
+ // A repeating timer to check for lost URLs. If the elapsed time since an URL
+ // was last seen exceeds a threshold, the URL is considered lost.
+ base::scoped_nsobject<NSTimer> updateTimer_;
}
+@synthesize onLostDetectionEnabled = onLostDetectionEnabled_;
@synthesize networkRequestEnabled = networkRequestEnabled_;
- (instancetype)initWithDelegate:(id<PhysicalWebScannerDelegate>)delegate {
@@ -92,6 +113,7 @@ enum BeaconType {
initWithDelegate:self
queue:dispatch_get_main_queue()]);
unresolvedDevices_.reset([[NSMutableArray alloc] init]);
+ scanTimestamps_.reset([[NSMutableDictionary alloc] init]);
[[NSHTTPCookieStorage sharedHTTPCookieStorage]
setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyNever];
}
@@ -106,6 +128,10 @@ enum BeaconType {
- (void)dealloc {
[centralManager_ setDelegate:nil];
centralManager_.reset();
+ if (updateTimer_.get()) {
+ [updateTimer_ invalidate];
+ updateTimer_.reset();
+ }
[super dealloc];
}
@@ -114,6 +140,7 @@ enum BeaconType {
[finalUrls_ removeAllObjects];
[devicesUrls_ removeAllObjects];
[devices_ removeAllObjects];
+ [scanTimestamps_ removeAllObjects];
started_ = YES;
if ([self bluetoothEnabled])
[self reallyStart];
@@ -129,7 +156,7 @@ enum BeaconType {
}
[pendingRequests_ removeAllObjects];
if (!pendingStart_ && [self bluetoothEnabled]) {
- [centralManager_ stopScan];
+ [self reallyStop];
}
pendingStart_ = NO;
started_ = NO;
@@ -189,6 +216,14 @@ enum BeaconType {
[unresolvedDevices_ removeAllObjects];
}
+- (void)setOnLostDetectionEnabled:(BOOL)enabled {
+ BOOL oldOnLostDetectionEnabled = onLostDetectionEnabled_;
+ onLostDetectionEnabled_ = enabled;
+ if (started_ && oldOnLostDetectionEnabled != onLostDetectionEnabled_) {
+ [self start];
Olivier 2016/10/13 13:00:19 This will reset all the lists. Is that expected? S
mattreynolds 2016/10/18 18:56:47 I added a comment documenting the behavior
+ }
+}
+
- (int)unresolvedBeaconsCount {
return [unresolvedDevices_ count];
}
@@ -205,11 +240,97 @@ enum BeaconType {
- (void)reallyStart {
pendingStart_ = NO;
+
+ if (updateTimer_.get()) {
+ [updateTimer_ invalidate];
+ updateTimer_.reset();
+ }
+
NSArray* serviceUUIDs = @[
[CBUUID UUIDWithString:kUriBeaconServiceUUID],
[CBUUID UUIDWithString:kEddystoneBeaconServiceUUID]
];
- [centralManager_ scanForPeripheralsWithServices:serviceUUIDs options:nil];
+ NSDictionary* options = nil;
+ if (onLostDetectionEnabled_) {
+ // To detect lost URLs, we configure the scanner with the AllowDuplicates
+ // option enabled, which will notify us each time an advertising packet is
+ // received rather than only the first time. A URL is considered lost if the
+ // most recent sighting was more than |kLostThresholdSeconds| ago.
+ options =
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:YES],
+ CBCentralManagerScanOptionAllowDuplicatesKey, nil];
+
+ // Register a repeating timer to periodically check for lost URLs.
+ updateTimer_.reset([NSTimer
+ scheduledTimerWithTimeInterval:kUpdateIntervalSeconds
+ target:self
+ selector:@selector(onUpdate:)
+ userInfo:nil
+ repeats:YES]);
+ }
+ [centralManager_ scanForPeripheralsWithServices:serviceUUIDs options:options];
+}
+
+- (void)reallyStop {
+ if (updateTimer_.get()) {
+ [updateTimer_ invalidate];
+ updateTimer_.reset();
+ }
+
+ [centralManager_ stopScan];
+}
+
+- (void)onUpdate:(NSTimer*)timer {
+ NSDate* now = [NSDate date];
+ NSMutableArray* lostDevices = [NSMutableArray array];
+ NSMutableArray* lostUnresolvedDevices = [NSMutableArray array];
+ NSMutableArray* lostScannedUrls = [NSMutableArray array];
+ NSMutableArray* lostResolvedUrls = [NSMutableArray array];
+
+ for (PhysicalWebDevice* device in [self devices]) {
+ NSDate* scanTimestamp = [scanTimestamps_ objectForKey:[device requestURL]];
+ NSTimeInterval elapsedSeconds = [now timeIntervalSinceDate:scanTimestamp];
+ if (elapsedSeconds > kLostThresholdSeconds) {
+ [lostDevices addObject:device];
+ [lostScannedUrls addObject:[device requestURL]];
+ [lostResolvedUrls addObject:[device url]];
+ [devicesUrls_ removeObject:[device requestURL]];
+ [finalUrls_ removeObject:[device url]];
+ }
+ }
+
+ for (PhysicalWebDevice* device in unresolvedDevices_.get()) {
+ NSDate* scanTimestamp = [scanTimestamps_ objectForKey:[device requestURL]];
+ NSTimeInterval elapsedSeconds = [now timeIntervalSinceDate:scanTimestamp];
+ if (elapsedSeconds > kLostThresholdSeconds) {
+ [lostUnresolvedDevices addObject:device];
+ [lostScannedUrls addObject:[device requestURL]];
+ [devicesUrls_ removeObject:[device requestURL]];
+ }
+ }
+
+ [devices_ removeObjectsInArray:lostDevices];
+ [unresolvedDevices_ removeObjectsInArray:lostUnresolvedDevices];
Olivier 2016/10/13 13:00:19 should we cancel the resolve URL query?
mattreynolds 2016/10/18 18:56:47 Done.
+ [scanTimestamps_ removeObjectsForKeys:lostScannedUrls];
+
+ if ([lostDevices count]) {
+ [delegate_ scannerUpdatedDevices:self];
+ }
+
+ // For unknown reasons, when scanning for longer periods (on the order of
Olivier 2016/10/13 13:00:19 Can you file a radar on this, file a crbug that re
mattreynolds 2016/10/18 18:56:47 Done.
+ // minutes), the scanner is less reliable at detecting all nearby URLs. As a
+ // workaround, we restart the scanner each time we check for lost URLs.
+ NSArray* serviceUUIDs = @[
+ [CBUUID UUIDWithString:kUriBeaconServiceUUID],
+ [CBUUID UUIDWithString:kEddystoneBeaconServiceUUID]
+ ];
+ NSDictionary* options = [NSDictionary
+ dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],
+ CBCentralManagerScanOptionAllowDuplicatesKey,
+ nil];
+ [centralManager_ stopScan];
+ [centralManager_ scanForPeripheralsWithServices:serviceUUIDs options:options];
}
#pragma mark -
@@ -222,7 +343,7 @@ enum BeaconType {
} else {
if (started_ && !pendingStart_) {
pendingStart_ = YES;
- [centralManager_ stopScan];
+ [self reallyStop];
}
}
[delegate_ scannerBluetoothStatusUpdated:self];
@@ -274,10 +395,12 @@ enum BeaconType {
if (!device.get())
return;
+ [scanTimestamps_ setObject:[NSDate date] forKey:[device requestURL]];
Olivier 2016/10/13 13:00:19 Would it make sense to add this to the device obje
mattreynolds 2016/10/18 18:56:47 Done.
+
// Skip if the URL has already been seen.
- if ([devicesUrls_ containsObject:[device url]])
+ if ([devicesUrls_ containsObject:[device requestURL]])
return;
- [devicesUrls_ addObject:[device url]];
+ [devicesUrls_ addObject:[device requestURL]];
if (networkRequestEnabled_) {
[self requestMetadataForDevice:device];
@@ -326,7 +449,7 @@ enum BeaconType {
return nil;
return [[PhysicalWebDevice alloc] initWithURL:url
- requestURL:nil
+ requestURL:url
icon:nil
title:nil
description:nil
« no previous file with comments | « ios/chrome/common/physical_web/physical_web_scanner.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698