Skip to content

Commit

Permalink
[Darwin] MTRDevice estimate start time based on General Diagnostics U…
Browse files Browse the repository at this point in the history
…pTime attribute when present (#26583)

* [Darwin] MTRDevice estimate start time based on General Diagnostics UpTime attribute when present

* Address bug found in review

* Simplified UpTime use logic, and ignore StartUp event for estimate purpose during priming

* Reworked UpTime-derived estimate logic per discussion with Boris

* Reduced complexity and dependency on _state
  • Loading branch information
jtung-apple authored and pull[bot] committed Dec 5, 2023
1 parent b040ffc commit 1d1a439
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 6 deletions.
38 changes: 36 additions & 2 deletions src/darwin/Framework/CHIP/MTRDevice.mm
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ @interface MTRDevice ()

@property (nonatomic) BOOL expirationCheckScheduled;

@property (nonatomic) NSDate * estimatedStartTimeFromGeneralDiagnosticsUpTime;

/**
* If currentReadClient is non-null, that means that we successfully
* called SendAutoResubscribeRequest on the ReadClient and have not yet gotten
Expand Down Expand Up @@ -260,7 +262,9 @@ - (void)_changeState:(MTRDeviceState)state
_state = state;
if (lastState != state) {
if (state != MTRDeviceStateReachable) {
MTR_LOG_INFO("%@ Set estimated start time to nil due to state change", self);
_estimatedStartTime = nil;
_estimatedStartTimeFromGeneralDiagnosticsUpTime = nil;
}
id<MTRDeviceDelegate> delegate = _weakDelegate.strongObject;
if (delegate) {
Expand Down Expand Up @@ -412,8 +416,18 @@ - (void)_handleEventReport:(NSArray<NSDictionary<NSString *, id> *> *)eventRepor
MTREventPath * eventPath = eventDict[MTREventPathKey];
BOOL isStartUpEvent = (eventPath.cluster.unsignedLongValue == MTRClusterIDTypeBasicInformationID)
&& (eventPath.event.unsignedLongValue == MTREventIDTypeClusterBasicInformationEventStartUpID);
if (isStartUpEvent) {
_estimatedStartTime = nil;
if (isStartUpEvent && (_state == MTRDeviceStateReachable)) {
// StartUp event received when server resumes subscription
if (_estimatedStartTimeFromGeneralDiagnosticsUpTime) {
// If UpTime was received, make use of it as mark of system start time
MTR_LOG_INFO("%@ StartUp event: set estimated start time forward to %@", self,
_estimatedStartTimeFromGeneralDiagnosticsUpTime);
_estimatedStartTime = _estimatedStartTimeFromGeneralDiagnosticsUpTime;
} else {
// If UpTime was not received, reset estimated start time in case of reboot
MTR_LOG_INFO("%@ StartUp event: set estimated start time to nil", self);
_estimatedStartTime = nil;
}
}

// If event time is of MTREventTimeTypeSystemUpTime type, then update estimated start time as needed
Expand Down Expand Up @@ -950,6 +964,26 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
MTR_LOG_INFO("%@ report %@ value filtered - same values as cache", self, attributePath);
}
}

// If General Diagnostics UpTime attribute, update the estimated start time as needed.
if ((attributePath.cluster.unsignedLongValue == MTRClusterGeneralDiagnosticsID)
&& (attributePath.attribute.unsignedLongValue == MTRClusterGeneralDiagnosticsAttributeUpTimeID)) {
// verify that the uptime is indeed the data type we want
if ([attributeDataValue[MTRTypeKey] isEqual:MTRUnsignedIntegerValueType]) {
NSNumber * upTimeNumber = attributeDataValue[MTRValueKey];
NSTimeInterval upTime = upTimeNumber.unsignedLongLongValue; // UpTime unit is defined as seconds in the spec
NSDate * potentialSystemStartTime = [NSDate dateWithTimeIntervalSinceNow:-upTime];
NSDate * oldSystemStartTime = _estimatedStartTime;
if (!_estimatedStartTime || ([potentialSystemStartTime compare:_estimatedStartTime] == NSOrderedAscending)) {
MTR_LOG_INFO("%@ General Diagnostics UpTime %.3lf: estimated start time %@ => %@", self, upTime,
oldSystemStartTime, potentialSystemStartTime);
_estimatedStartTime = potentialSystemStartTime;
}

// Save estimate in the subscription resumption case, for when StartUp event uses it
_estimatedStartTimeFromGeneralDiagnosticsUpTime = potentialSystemStartTime;
}
}
}

if (shouldReportAttribute) {
Expand Down
10 changes: 6 additions & 4 deletions src/darwin/Framework/CHIPTests/MTRDeviceTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -1543,7 +1543,12 @@ - (void)test017_TestMTRDeviceBasics
subscriptionEstablished:^() {
}];

[self waitForExpectations:@[ subscriptionDroppedExpectation, resubscriptionExpectation ] timeout:60 enforceOrder:YES];
[self waitForExpectations:@[ subscriptionDroppedExpectation ] timeout:60];

// Check that device resets start time on subscription drop
XCTAssertNil(device.estimatedStartTime);

[self waitForExpectations:@[ resubscriptionExpectation ] timeout:60];

// Now make sure we ignore later tests. Ideally we would just unsubscribe
// or remove the delegate, but there's no good way to do that.
Expand All @@ -1557,9 +1562,6 @@ - (void)test017_TestMTRDeviceBasics
// with data versions) during the resubscribe.
XCTAssertEqual(attributeReportsReceived, 0);
XCTAssertEqual(eventReportsReceived, 0);

// Check that device resets start time on subscription drop
XCTAssertNil(device.estimatedStartTime);
}

- (void)test018_SubscriptionErrorWhenNotResubscribing
Expand Down

0 comments on commit 1d1a439

Please sign in to comment.