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 8, 2024
1 parent 143bc7c commit bfb416b
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 27 deletions.
33 changes: 28 additions & 5 deletions src/darwin/Framework/CHIP/MTRDeviceController_XPC.mm
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ @interface MTRDeviceController_XPC ()

@implementation MTRDeviceController_XPC

@synthesize controllerNodeID = _controllerNodeID;

+ (NSMutableSet *)_allowedClasses
{
static NSArray * sBaseAllowedClasses = @[
Expand Down Expand Up @@ -95,6 +97,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 @@ -202,9 +218,12 @@ - (BOOL)_setupXPCConnection
MTR_LOG("%@ Activating new XPC connection", self);
[self.xpcConnection activate];

[[self.xpcConnection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
[[self.xpcConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
MTR_LOG_ERROR("Checkin error: %@", error);
}] deviceController:self.uniqueIdentifier checkInWithContext:[NSDictionary dictionary]];
}] 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 All @@ -214,10 +233,14 @@ - (BOOL)_setupXPCConnection
MTR_LOG("%@ => Registering nodeID: %@", self, nodeID);
mtr_weakify(self);

[[self.xpcConnection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
[[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];
}] 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);
Expand Down Expand Up @@ -320,7 +343,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 @@ -1733,6 +1759,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 @@ -1764,7 +1799,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];
}

Expand Down Expand Up @@ -1954,6 +1989,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 @@ -1999,11 +2043,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 @@ -2023,7 +2067,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 @@ -3601,7 +3645,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 @@ -3693,7 +3737,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 @@ -3705,7 +3749,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
61 changes: 51 additions & 10 deletions src/darwin/Framework/CHIP/MTRDevice_XPC.mm
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ - (NSString *)description
}

// TODO: Add these to the description
// MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyDeviceState, _internalDeviceStateForDescription, properties);
// MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyDeviceInternalState, _internalDeviceStateForDescription, properties);
// MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyLastSubscriptionAttemptWait, _lastSubscriptionAttemptWaitForDescription, properties);
// MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyMostRecentReportTime, _mostRecentReportTimeForDescription, properties);
// MTR_OPTIONAL_ATTRIBUTE(kMTRDeviceInternalPropertyLastSubscriptionFailureTime, _lastSubscriptionFailureTimeForDescription, properties);
Expand All @@ -136,23 +136,23 @@ - (NSString *)description
// required methods for MTRDeviceDelegates
- (oneway void)device:(NSNumber *)nodeID stateChanged:(MTRDeviceState)state
{
MTR_LOG("%s", __PRETTY_FUNCTION__);
MTR_LOG("%@ %s", self, __PRETTY_FUNCTION__);
[self _lockAndCallDelegatesWithBlock:^(id<MTRDeviceDelegate> delegate) {
[delegate device:self stateChanged:state];
}];
}

- (oneway void)device:(NSNumber *)nodeID receivedAttributeReport:(NSArray<NSDictionary<NSString *, id> *> *)attributeReport
{
MTR_LOG("%s", __PRETTY_FUNCTION__);
MTR_LOG("%@ %s", self, __PRETTY_FUNCTION__);
[self _lockAndCallDelegatesWithBlock:^(id<MTRDeviceDelegate> delegate) {
[delegate device:self receivedAttributeReport:attributeReport];
}];
}

- (oneway void)device:(NSNumber *)nodeID receivedEventReport:(NSArray<NSDictionary<NSString *, id> *> *)eventReport
{
MTR_LOG("%s", __PRETTY_FUNCTION__);
MTR_LOG("%@ %s", self, __PRETTY_FUNCTION__);
[self _lockAndCallDelegatesWithBlock:^(id<MTRDeviceDelegate> delegate) {
[delegate device:self receivedEventReport:eventReport];
}];
Expand All @@ -161,7 +161,7 @@ - (oneway void)device:(NSNumber *)nodeID receivedEventReport:(NSArray<NSDictiona
// optional methods for MTRDeviceDelegates - check for implementation before calling
- (oneway void)deviceBecameActive:(NSNumber *)nodeID
{
MTR_LOG("%s", __PRETTY_FUNCTION__);
MTR_LOG("%@ %s", self, __PRETTY_FUNCTION__);
[self _lockAndCallDelegatesWithBlock:^(id<MTRDeviceDelegate> delegate) {
if ([delegate respondsToSelector:@selector(deviceBecameActive:)]) {
[delegate deviceBecameActive:self];
Expand All @@ -171,6 +171,7 @@ - (oneway void)deviceBecameActive:(NSNumber *)nodeID

- (oneway void)deviceCachePrimed:(NSNumber *)nodeID
{
MTR_LOG("%@ %s", self, __PRETTY_FUNCTION__);
[self _lockAndCallDelegatesWithBlock:^(id<MTRDeviceDelegate> delegate) {
if ([delegate respondsToSelector:@selector(deviceCachePrimed:)]) {
[delegate deviceCachePrimed:self];
Expand All @@ -180,6 +181,7 @@ - (oneway void)deviceCachePrimed:(NSNumber *)nodeID

- (oneway void)deviceConfigurationChanged:(NSNumber *)nodeID
{
MTR_LOG("%@ %s", self, __PRETTY_FUNCTION__);
[self _lockAndCallDelegatesWithBlock:^(id<MTRDeviceDelegate> delegate) {
if ([delegate respondsToSelector:@selector(deviceConfigurationChanged:)]) {
[delegate deviceConfigurationChanged:self];
Expand All @@ -189,16 +191,55 @@ - (oneway void)deviceConfigurationChanged:(NSNumber *)nodeID

- (oneway void)device:(NSNumber *)nodeID internalStateUpdated:(NSDictionary *)dictionary
{
MTR_LOG("%@ %s", self, __PRETTY_FUNCTION__);

// Save old state for comparison later
NSNumber *oldStateNumber = self._internalState[kMTRDeviceInternalPropertyDeviceState];
NSNumber *newStateNumber = dictionary[kMTRDeviceInternalPropertyDeviceState];
[self _setInternalState:dictionary];
MTR_LOG("%@ internal state updated", self);

// Call delegate if state changed. State is considered changed if:
// 1) old state is nil but new state is not nil
// 2) old state is not nil but new state is nil
// 3) both old and new state are not nil, and they are not equal
if ((!oldStateNumber && newStateNumber) ||
(oldStateNumber && !newStateNumber) ||
(oldStateNumber && newStateNumber && ![newStateNumber isEqualToNumber:oldStateNumber])) {
MTRDeviceState state = static_cast<MTRDeviceState>(newStateNumber ? newStateNumber.unsignedIntegerValue : MTRDeviceStateUnknown);
[self _lockAndCallDelegatesWithBlock:^(id<MTRDeviceDelegate> delegate) {
[delegate device:self stateChanged:state];
}];
}
}

- (MTRDeviceState)state
{
NSNumber *stateNumber = self._internalState[kMTRDeviceInternalPropertyDeviceState];
return stateNumber ? static_cast<MTRDeviceState>(stateNumber.unsignedIntegerValue) : MTRDeviceStateUnknown;
}

- (BOOL)deviceCachePrimed
{
NSNumber *deviceCachePrimedNumber = self._internalState[kMTRDeviceInternalPropertyDeviceCachePrimed];
return deviceCachePrimedNumber.boolValue;
}

- (NSDate *)estimatedStartTime
{
return self._internalState[kMTRDeviceInternalPropertyEstimatedStartTime];
}

- (NSNumber *)estimatedSubscriptionLatency
{
return self._internalState[kMTRDeviceInternalPropertyEstimatedSubscriptionLatency];
}

#pragma mark - Remote Commands

MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(state, MTRDeviceState, MTRDeviceStateUnknown, getStateWithReply)
MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(deviceCachePrimed, BOOL, NO, getDeviceCachePrimedWithReply)
MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(estimatedStartTime, NSDate * _Nullable, nil, getEstimatedStartTimeWithReply)
MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(estimatedSubscriptionLatency, NSNumber * _Nullable, nil, getEstimatedSubscriptionLatencyWithReply)
//MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(state, MTRDeviceState, MTRDeviceStateUnknown, getStateWithReply)
//MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(deviceCachePrimed, BOOL, NO, getDeviceCachePrimedWithReply)
//MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(estimatedStartTime, NSDate * _Nullable, nil, getEstimatedStartTimeWithReply)
//MTR_DEVICE_SIMPLE_REMOTE_XPC_GETTER(estimatedSubscriptionLatency, NSNumber * _Nullable, nil, getEstimatedSubscriptionLatencyWithReply)

typedef NSDictionary<NSString *, id> * _Nullable ReadAttributeResponseType;
MTR_DEVICE_COMPLEX_REMOTE_XPC_GETTER(readAttributeWithEndpointID
Expand Down
Loading

0 comments on commit bfb416b

Please sign in to comment.