Skip to content

Commit

Permalink
[Darwin] Internal state plumbing for the _XPC classes
Browse files Browse the repository at this point in the history
  • Loading branch information
jtung-apple committed Oct 16, 2024
1 parent 579b1b1 commit 5bc90f3
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 23 deletions.
42 changes: 41 additions & 1 deletion src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ - (void)_updateRegistrationInfo
MTR_REQUIRED_ATTRIBUTE(MTRDeviceControllerRegistrationNodeIDsKey, nodeIDs, registrationInfo)
MTR_REQUIRED_ATTRIBUTE(MTRDeviceControllerRegistrationControllerContextKey, controllerContext, registrationInfo)

MTR_LOG("%@ Starting existing NodeID Registration", self);
for (NSNumber * nodeID in [self.nodeIDToDeviceMap keyEnumerator]) {
MTR_LOG("%@ => Registering nodeID: %@", self, nodeID);
mtr_weakify(self);

[[self.xpcConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
mtr_strongify(self);
MTR_LOG_ERROR("%@ Registration error for device nodeID: %@ : %@", self, nodeID, error);
}] deviceController:self.uniqueIdentifier registerNodeID:nodeID reply:^(NSDictionary * _Nonnull internalState) {
mtr_strongify(self);
MTRDevice_XPC * device = (MTRDevice_XPC *) [self deviceForNodeID:nodeID];
[device device:nodeID internalStateUpdated:internalState];
}];
}

MTR_LOG("%@ Done existing NodeID Registration", self);

[self updateControllerConfiguration:registrationInfo];
}

Expand All @@ -90,6 +107,8 @@ - (void)removeDevice:(MTRDevice *)device
}

#pragma mark - XPC
@synthesize controllerNodeID = _controllerNodeID;

+ (NSMutableSet *)_allowedClasses
{
static NSArray * sBaseAllowedClasses = @[
Expand Down Expand Up @@ -141,6 +160,20 @@ - (NSXPCInterface *)_interfaceForServerProtocol
argumentIndex:0
ofReply:YES];

// registerNodeID: returns dictionary containing standard nsstring / nsnumber / nsdate objects.
allowedClasses = [MTRDeviceController_XPC _allowedClasses];
[interface setClasses:allowedClasses
forSelector:@selector(deviceController:registerNodeID:reply:)
argumentIndex:0
ofReply:YES];

// checkInWithContext: returns dictionary containing standard nsstring / nsnumber objects.
allowedClasses = [MTRDeviceController_XPC _allowedClasses];
[interface setClasses:allowedClasses
forSelector:@selector(deviceController:checkInWithContext:reply:)
argumentIndex:0
ofReply:YES];

return interface;
}

Expand Down Expand Up @@ -248,6 +281,13 @@ - (BOOL)_setupXPCConnection
MTR_LOG("%@ Activating new XPC connection", self);
[self.xpcConnection activate];

[[self.xpcConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
MTR_LOG_ERROR("Checkin error: %@", error);
}] deviceController:self.uniqueIdentifier checkInWithContext:@{} reply:^(NSDictionary * _Nonnull controllerInfo) {
// Get the controller nodeID
;
}];

// FIXME: Trying to kick all the MTRDevices attached to this controller to re-establish connections
// This state needs to be stored properly and re-established at connnection time

Expand Down Expand Up @@ -347,7 +387,7 @@ - (MTRDevice *)_setupDeviceForNodeID:(NSNumber *)nodeID prefetchedClusterData:(N
#pragma mark - XPC Action Overrides

MTR_DEVICECONTROLLER_SIMPLE_REMOTE_XPC_GETTER(isRunning, BOOL, NO, getIsRunningWithReply)
MTR_DEVICECONTROLLER_SIMPLE_REMOTE_XPC_GETTER(controllerNodeID, NSNumber *, nil, controllerNodeIDWithReply)
//MTR_DEVICECONTROLLER_SIMPLE_REMOTE_XPC_GETTER(controllerNodeID, NSNumber *, nil, controllerNodeIDWithReply)

// Not Supported via XPC
// - (oneway void)deviceController:(NSUUID *)controller setupCommissioningSessionWithPayload:(MTRSetupPayload *)payload newNodeID:(NSNumber *)newNodeID withReply:(void(^)(BOOL success, NSError * _Nullable error))reply;
Expand Down
66 changes: 55 additions & 11 deletions src/darwin/Framework/CHIP/MTRDevice_Concrete.mm
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,14 @@ @implementation MTRDevice_Concrete {
NSDate * _Nullable _mostRecentReportTimeForDescription;
// Copy of _lastSubscriptionFailureTime that is safe to use in description.
NSDate * _Nullable _lastSubscriptionFailureTimeForDescription;
// Copy of _state that is safe to use in description.
MTRDeviceState _deviceStateForDescription;
// Copy of _deviceCachePrimed that is safe to use in description.
BOOL _deviceCachePrimedForDescription;
// Copy of _estimatedStartTime that is safe to use in description.
NSDate * _Nullable _estimatedStartTimeForDescription;
// Copy of _estimatedSubscriptionLatency that is safe to use in description.
NSNumber * _Nullable _estimatedSubscriptionLatencyForDescription;
}

// synthesize superclass property readwrite accessors
Expand Down Expand Up @@ -473,11 +481,16 @@ - (NSDictionary *)_internalProperties
MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyKeyVendorID, _vid, properties);
MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyKeyProductID, _pid, properties);
MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyNetworkFeatures, _allNetworkFeatures, properties);
MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyDeviceState, [NSNumber numberWithUnsignedInteger:_internalDeviceStateForDescription], properties);
MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyDeviceInternalState, [NSNumber numberWithUnsignedInteger:_internalDeviceStateForDescription], properties);
MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyLastSubscriptionAttemptWait, [NSNumber numberWithUnsignedInt:_lastSubscriptionAttemptWaitForDescription], properties);
MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyMostRecentReportTime, _mostRecentReportTimeForDescription, properties);
MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyLastSubscriptionFailureTime, _lastSubscriptionFailureTimeForDescription, properties);

MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyDeviceState, [NSNumber numberWithUnsignedInteger:_deviceStateForDescription], properties);
MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyDeviceCachePrimed, @(_deviceCachePrimedForDescription), properties);
MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyEstimatedStartTime, _estimatedStartTimeForDescription, properties);
MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyEstimatedSubscriptionLatency, _estimatedSubscriptionLatencyForDescription, properties);

return properties;
}

Expand Down Expand Up @@ -964,11 +977,15 @@ - (void)_changeState:(MTRDeviceState)state
os_unfair_lock_assert_owner(&self->_lock);
MTRDeviceState lastState = _state;
_state = state;
{
std::lock_guard lock(_descriptionLock);
_deviceStateForDescription = _state;
}
if (lastState != state) {
if (state != MTRDeviceStateReachable) {
MTR_LOG("%@ reachability state change %lu => %lu, set estimated start time to nil", self, static_cast<unsigned long>(lastState),
static_cast<unsigned long>(state));
_estimatedStartTime = nil;
[self _updateEstimatedStartTime:nil];
_estimatedStartTimeFromGeneralDiagnosticsUpTime = nil;
} else {
MTR_LOG(
Expand Down Expand Up @@ -1025,6 +1042,15 @@ - (MTRInternalDeviceState)_getInternalState
}
#endif

- (void)_updateEstimatedSubscriptionLatency:(NSNumber *)estimatedSubscriptionLatency
{
os_unfair_lock_assert_owner(&_lock);
_estimatedSubscriptionLatency = estimatedSubscriptionLatency;

std::lock_guard lock(_descriptionLock);
_estimatedSubscriptionLatencyForDescription = estimatedSubscriptionLatency;
}

// First Time Sync happens 2 minutes after reachability (this can be changed in the future)
#define MTR_DEVICE_TIME_UPDATE_INITIAL_WAIT_TIME_SEC (60 * 2)
- (void)_handleSubscriptionEstablished
Expand Down Expand Up @@ -1056,10 +1082,10 @@ - (void)_handleSubscriptionEstablished
// way around.
NSTimeInterval subscriptionLatency = -[initialSubscribeStart timeIntervalSinceNow];
if (_estimatedSubscriptionLatency == nil) {
_estimatedSubscriptionLatency = @(subscriptionLatency);
[self _updateEstimatedSubscriptionLatency:@(subscriptionLatency)];
} else {
NSTimeInterval newSubscriptionLatencyEstimate = MTRDEVICE_SUBSCRIPTION_LATENCY_NEW_VALUE_WEIGHT * subscriptionLatency + (1 - MTRDEVICE_SUBSCRIPTION_LATENCY_NEW_VALUE_WEIGHT) * _estimatedSubscriptionLatency.doubleValue;
_estimatedSubscriptionLatency = @(newSubscriptionLatencyEstimate);
[self _updateEstimatedSubscriptionLatency:@(newSubscriptionLatencyEstimate)];
}
[self _storePersistedDeviceData];
}
Expand Down Expand Up @@ -1742,6 +1768,15 @@ - (void)setStorageBehaviorConfiguration:(MTRDeviceStorageBehaviorConfiguration *
[self _resetStorageBehaviorState];
}

- (void)_updateDeviceCachePrimed:(BOOL)deviceCachePrimed
{
os_unfair_lock_assert_owner(&_lock);
_deviceCachePrimed = deviceCachePrimed;

std::lock_guard lock(_descriptionLock);
_deviceCachePrimedForDescription = deviceCachePrimed;
}

- (void)_handleReportEnd
{
MTR_LOG("%@ handling report end", self);
Expand Down Expand Up @@ -1774,7 +1809,7 @@ - (void)_handleReportEnd
if (!_deviceCachePrimed) {
// This is the end of the priming sequence of data reports, so we have
// all the data for the device now.
_deviceCachePrimed = YES;
[self _updateDeviceCachePrimed:YES];
[self _callDelegateDeviceCachePrimed];
[self _notifyDelegateOfPrivateInternalPropertiesChanges];
}
Expand Down Expand Up @@ -1963,6 +1998,15 @@ - (BOOL)_interestedPaths:(NSArray * _Nullable)interestedPaths includesEventPath:
return filteredEvents;
}

- (void)_updateEstimatedStartTime:(NSDate *)estimatedStartTime
{
os_unfair_lock_assert_owner(&_lock);
_estimatedStartTime = estimatedStartTime;

std::lock_guard lock(_descriptionLock);
_estimatedStartTimeForDescription = _estimatedStartTime;
}

- (void)_handleEventReport:(NSArray<NSDictionary<NSString *, id> *> *)eventReport
{
std::lock_guard lock(_lock);
Expand Down Expand Up @@ -2008,11 +2052,11 @@ - (void)_handleEventReport:(NSArray<NSDictionary<NSString *, id> *> *)eventRepor
// If UpTime was received, make use of it as mark of system start time
MTR_LOG("%@ StartUp event: set estimated start time forward to %@", self,
_estimatedStartTimeFromGeneralDiagnosticsUpTime);
_estimatedStartTime = _estimatedStartTimeFromGeneralDiagnosticsUpTime;
[self _updateEstimatedStartTime:_estimatedStartTimeFromGeneralDiagnosticsUpTime];
} else {
// If UpTime was not received, reset estimated start time in case of reboot
MTR_LOG("%@ StartUp event: set estimated start time to nil", self);
_estimatedStartTime = nil;
[self _updateEstimatedStartTime:nil];
}
}

Expand All @@ -2032,7 +2076,7 @@ - (void)_handleEventReport:(NSArray<NSDictionary<NSString *, id> *> *)eventRepor
NSTimeInterval eventTimeValue = eventTimeValueNumber.doubleValue;
NSDate * potentialSystemStartTime = [NSDate dateWithTimeIntervalSinceNow:-eventTimeValue];
if (!_estimatedStartTime || ([potentialSystemStartTime compare:_estimatedStartTime] == NSOrderedAscending)) {
_estimatedStartTime = potentialSystemStartTime;
[self _updateEstimatedStartTime:potentialSystemStartTime];
}
}

Expand Down Expand Up @@ -3623,7 +3667,7 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray<NSDictionary<NSSt
if (!_estimatedStartTime || ([potentialSystemStartTime compare:_estimatedStartTime] == NSOrderedAscending)) {
MTR_LOG("%@ General Diagnostics UpTime %.3lf: estimated start time %@ => %@", self, upTime,
oldSystemStartTime, potentialSystemStartTime);
_estimatedStartTime = potentialSystemStartTime;
[self _updateEstimatedStartTime:potentialSystemStartTime];
}

// Save estimate in the subscription resumption case, for when StartUp event uses it
Expand Down Expand Up @@ -3715,7 +3759,7 @@ - (void)setPersistedClusterData:(NSDictionary<MTRClusterPath *, MTRDeviceCluster

// We have some stored data. Since we don't store data until the end of the
// initial priming report, our device cache must be primed.
_deviceCachePrimed = YES;
[self _updateDeviceCachePrimed:YES];
}

- (void)_setLastInitialSubscribeLatency:(id)latency
Expand All @@ -3727,7 +3771,7 @@ - (void)_setLastInitialSubscribeLatency:(id)latency
return;
}

_estimatedSubscriptionLatency = latency;
[self _updateEstimatedSubscriptionLatency:latency];
}

- (void)setPersistedDeviceData:(NSDictionary<NSString *, id> *)data
Expand Down
6 changes: 5 additions & 1 deletion src/darwin/Framework/CHIP/MTRDevice_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,13 @@ static NSString * const sLastInitialSubscribeLatencyKey = @"lastInitialSubscribe
static NSString * const kMTRDeviceInternalPropertyKeyVendorID = @"MTRDeviceInternalStateKeyVendorID";
static NSString * const kMTRDeviceInternalPropertyKeyProductID = @"MTRDeviceInternalStateKeyProductID";
static NSString * const kMTRDeviceInternalPropertyNetworkFeatures = @"MTRDeviceInternalPropertyNetworkFeatures";
static NSString * const kMTRDeviceInternalPropertyDeviceState = @"MTRDeviceInternalPropertyDeviceState";
static NSString * const kMTRDeviceInternalPropertyDeviceInternalState = @"MTRDeviceInternalPropertyDeviceInternalState";
static NSString * const kMTRDeviceInternalPropertyLastSubscriptionAttemptWait = @"kMTRDeviceInternalPropertyLastSubscriptionAttemptWait";
static NSString * const kMTRDeviceInternalPropertyMostRecentReportTime = @"MTRDeviceInternalPropertyMostRecentReportTime";
static NSString * const kMTRDeviceInternalPropertyLastSubscriptionFailureTime = @"MTRDeviceInternalPropertyLastSubscriptionFailureTime";
static NSString * const kMTRDeviceInternalPropertyDeviceState = @"MTRDeviceInternalPropertyDeviceState";
static NSString * const kMTRDeviceInternalPropertyDeviceCachePrimed = @"MTRDeviceInternalPropertyDeviceCachePrimed";
static NSString * const kMTRDeviceInternalPropertyEstimatedStartTime = @"MTRDeviceInternalPropertyEstimatedStartTime";
static NSString * const kMTRDeviceInternalPropertyEstimatedSubscriptionLatency = @"MTRDeviceInternalPropertyEstimatedSubscriptionLatency";

NS_ASSUME_NONNULL_END
Loading

0 comments on commit 5bc90f3

Please sign in to comment.