summaryrefslogtreecommitdiff
path: root/SmartDeviceLink/SDLIAPSession.m
blob: c69efce6d1160396050b6d38efea537095c81c82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//
//  SDLIAPSession.m
//

#import "SDLIAPSession.h"
#import "SDLLogMacros.h"
#import "SDLMutableDataQueue.h"
#import "SDLTimer.h"

NS_ASSUME_NONNULL_BEGIN

@interface SDLIAPSession ()

@property (nullable, strong, nonatomic, readwrite) EASession *eaSession;
@property (nullable, strong, nonatomic, readwrite) EAAccessory *accessory;
@property (nullable, strong, nonatomic, readwrite) NSString *protocolString;

@end

@implementation SDLIAPSession

#pragma mark - Lifecycle

- (instancetype)initWithAccessory:(nullable EAAccessory *)accessory forProtocol:(NSString *)protocol {
    SDLLogD(@"SDLIAPSession init with accessory:%@ for protocol:%@", accessory.name, protocol);

    self = [super init];
    if (!self) { return nil; }

    _accessory = accessory;
    _protocolString = protocol;
    _isInputStreamOpen = NO;
    _isOutputStreamOpen = NO;

    return self;
}

#pragma mark - Abstract Methods

- (void)startSession {}

- (void)destroySession {}

#pragma mark - Private Stream Lifecycle Helpers

- (BOOL)createSession {
    SDLLogD(@"Opening EASession with accessory: %@", self.accessory.name);
    self.eaSession = [[EASession alloc] initWithAccessory:self.accessory forProtocol:self.protocolString];
    return (self.eaSession != nil);
}

- (void)startStream:(NSStream *)stream {
    stream.delegate = self;
    [stream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [stream open];
}

- (void)stopStream:(NSStream *)stream {
    // Verify stream is in a state that can be closed. Closing a stream that has not been opened has very bad effects.
    NSUInteger status1 = stream.streamStatus;
    if (status1 != NSStreamStatusNotOpen &&
        status1 != NSStreamStatusClosed) {
        [stream close];
    } else if (status1 == NSStreamStatusNotOpen) {
        // It's implicitly removed from the stream when it's closed, but not if it was never opened.
        // When the USB cable is disconnected, the app will will call this method after the `NSStreamEventEndEncountered` event. The stream will already be in the closed state but it still needs to be removed from the run loop.
        [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }

    [stream setDelegate:nil];

    NSUInteger status2 = stream.streamStatus;
    if (status2 == NSStreamStatusClosed) {
        if (stream == [self.eaSession inputStream]) {
            SDLLogD(@"Input stream closed");
			self.isInputStreamOpen = NO;
        } else if (stream == [self.eaSession outputStream]) {
            SDLLogD(@"Output stream closed");
			self.isOutputStreamOpen = NO;
        }
    }
}

- (void)cleanupClosedSession {
    if (self.eaSession == nil) {
        SDLLogD(@"Attempting to close session with accessory: %@, but it is already closed", self.accessory.name);
        return;
    }

    self.eaSession = nil;
    SDLLogD(@"Session closed with: %@", self.accessory.name);
    self.accessory.delegate = nil;
}

#pragma mark - Getters

- (BOOL)isStopped {
    return !self.isOutputStreamOpen && !self.isInputStreamOpen;
}

- (NSUInteger)connectionID {
    return self.eaSession.accessory.connectionID;
}

- (BOOL)isSessionInProgress {
    return !self.isStopped;
}

@end

NS_ASSUME_NONNULL_END