1/*
2 * Copyright (c) 2014
3  Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25#include <CoreFoundation/CoreFoundation.h>
26#include <IOKit/IOKitLib.h>
27#include <IOKit/pwr_mgt/IOPMLibPrivate.h>
28#include <IOKit/pwr_mgt/IOPMLib.h>
29
30#include <dispatch/dispatch.h>
31#include <stdio.h>
32
33
34/*
35
36cc -o IOPMPerformBlockWithAssertion-15072112 \
37        IOPMPerformBlockWithAssertion-15072112.c \
38        -framework IOKit -framework CoreFoundation
39
40Inspired by:
41<rdar://problem/15072112> Sub-TLF: Investigating IOPMAssertion block API
42
43*/
44
45
46
47enum {
48    kExpectOn,
49    kExpectOff,
50    kExpectNA
51};
52static CFStringRef kAType = kIOPMAssertNetworkClientActive;
53
54
55static bool globalAssertionLevelFor(CFStringRef type, int expected, const char *print);
56static void asyncPerformBlockWith(CFStringRef type, int assertionTimeoutSec, int blockDurationSec);
57
58static void block10s_execTest(void);
59//static void blockTimeoutExceedsBlockLifespanTest(void);
60
61#define kNoTimeout  0
62
63int main(int argc, char *argv[])
64{
65    globalAssertionLevelFor(kAType, kExpectNA, "Baseline");
66
67    block10s_execTest();
68
69    return 0;
70}
71
72
73static void block10s_execTest(void)
74{
75    asyncPerformBlockWith(kAType, kNoTimeout, 10);
76    sleep(1);
77    globalAssertionLevelFor(kAType, kExpectOn, "#1 block is holding assertion");
78    sleep(3);
79    globalAssertionLevelFor(kAType, kExpectNA, "#1 block should have exited by now");
80    sleep(10);
81    globalAssertionLevelFor(kAType, kExpectNA, "#1 block should have exited by now EXTRA EXTRA EXIT");
82}
83
84
85//static void blockTimeoutExceedsBlockLifespanTest(void)
86//{
87//    asyncPerformBlockWith(kAType, 7, 3);
88//    sleep(1);
89//    globalAssertionLevelFor(kAType, kExpectOn, "#2 block is holding assertion");
90//    sleep(3);
91//    globalAssertionLevelFor(kAType, kExpectOff, "#2 block exited; timeout still going");
92//    sleep(5);
93//    globalAssertionLevelFor(kAType, kExpectOff, "#2 block exited; timeout exited");
94//}
95
96static void asyncPerformBlockWith(CFStringRef type,
97                                  int assertionTimeoutSec,
98                                  int blockDurationSec)
99{
100    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
101    ^() {
102        IOReturn ret;
103        CFMutableDictionaryRef          properties = NULL;
104
105        properties = CFDictionaryCreateMutable(0, 3,
106                                               &kCFTypeDictionaryKeyCallBacks,
107                                               &kCFTypeDictionaryValueCallBacks);
108
109        if (properties) {
110            CFDictionarySetValue(properties,
111                kIOPMAssertionNameKey,
112                CFSTR("IOPMPerformBlockWithAssertion-15072112"));
113
114            CFDictionarySetValue(properties,
115                kIOPMAssertionTypeKey,
116                type);
117
118            if (assertionTimeoutSec)
119            {
120                CFTimeInterval timeint = (CFTimeInterval)assertionTimeoutSec;
121                CFNumberRef timer;
122                timer = CFNumberCreate(0, kCFNumberDoubleType, &timeint);
123                CFDictionarySetValue(properties,
124                                     kIOPMAssertionTimeoutKey,
125                                     timer);
126                CFRelease(timer);
127            }
128
129            int levelOn = kIOPMAssertionLevelOn;
130
131            CFNumberRef numOn = CFNumberCreate(0, kCFNumberIntType, &levelOn);
132
133            CFDictionarySetValue(properties,
134                    kIOPMAssertionLevelKey,
135                    numOn);
136            CFRelease(numOn);
137        }
138
139        IOPMAssertionID aid;
140        IOReturn r2 = IOPMAssertionCreateWithProperties(properties, &aid);
141        printf("r2=0x%08x\n",r2);
142
143        ret = IOPMPerformBlockWithAssertion(properties,
144            ^(){
145                globalAssertionLevelFor(type, kExpectOn, "Within performed block");
146                sleep(blockDurationSec);
147            });
148
149        if (kIOReturnSuccess != ret) {
150            printf ("[FAIL] IOPMPerformBlockWithAssertion returns 0x%08x\n", ret);
151            exit(1);
152        }
153        CFRelease(properties);
154    });
155}
156
157
158static bool globalAssertionLevelFor(CFStringRef type, int expected, const char *print)
159{
160    CFDictionaryRef         out = NULL;
161    CFNumberRef             level = NULL;
162    bool                    actual = false;
163    int                     level_int = 0;
164
165    IOPMCopyAssertionsStatus(&out);
166
167    if (!out) {
168        printf("[FAIL] NULL return from IOPMCopyAssertionsStatus");
169        return false;
170    }
171
172    level = CFDictionaryGetValue(out, type);
173
174    CFNumberGetValue(level, kCFNumberIntType, &level_int);
175
176    actual = (1==level_int);
177
178    if (print) {
179        const char *expectedStr;
180        const char *actualStr;
181        if (kExpectOn == expected) {
182            expectedStr = "On";
183        } else if (kExpectOff == expected) {
184            expectedStr = "Off";
185        } else {
186            expectedStr = "N/A";
187        }
188        actualStr = actual? "On":"Off";
189
190        if ((kExpectNA == expected)
191            || (actual && (kExpectOn == expected))
192            || (!actual && (kExpectOff == expected)))
193        {
194            printf("[PASS] \"%s\" level is %s, expected to be %s\n",print, actualStr, expectedStr);
195        } else {
196            printf("[FAIL] \"%s\" level is %s, should be %s\n",print, actualStr, expectedStr);
197        }
198        fflush(stdout);
199    }
200
201    CFRelease(out);
202
203    return (kIOPMAssertionLevelOn > 0);
204}
205
206