diff --git a/ManagedCapturer/SCSingleFrameStreamCapturer.m b/ManagedCapturer/SCSingleFrameStreamCapturer.m
new file mode 100644
index 0000000..38813b5
--- /dev/null
+++ b/ManagedCapturer/SCSingleFrameStreamCapturer.m
@@ -0,0 +1,103 @@
+//
+//  SCSingleFrameStreamCapturer.m
+//  Snapchat
+//
+//  Created by Benjamin Hollis on 5/3/16.
+//  Copyright © 2016 Snapchat, Inc. All rights reserved.
+//
+
+#import "SCSingleFrameStreamCapturer.h"
+
+#import "SCManagedCapturer.h"
+
+@implementation SCSingleFrameStreamCapturer {
+    sc_managed_capturer_capture_video_frame_completion_handler_t _callback;
+}
+
+- (instancetype)initWithCompletion:(sc_managed_capturer_capture_video_frame_completion_handler_t)completionHandler
+{
+    self = [super init];
+    if (self) {
+        _callback = completionHandler;
+    }
+    return self;
+}
+
+#pragma mark - SCManagedVideoDataSourceListener
+
+- (void)managedVideoDataSource:(id<SCManagedVideoDataSource>)managedVideoDataSource
+         didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
+                devicePosition:(SCManagedCaptureDevicePosition)devicePosition
+{
+    if (_callback) {
+        UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
+        _callback(image);
+    }
+    _callback = nil;
+}
+
+/**
+ * Decode a CMSampleBufferRef to our native camera format (kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
+ * as set in SCManagedVideoStreamer) to a UIImage.
+ *
+ * Code from http://stackoverflow.com/a/31553521/11284
+ */
+#define clamp(a) (a > 255 ? 255 : (a < 0 ? 0 : a))
+// TODO: Use the transform code from SCImageProcessIdentityYUVCommand
+- (UIImage *)imageFromSampleBuffer:(CMSampleBufferRef)sampleBuffer
+{
+    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
+    CVPixelBufferLockBaseAddress(imageBuffer, 0);
+
+    size_t width = CVPixelBufferGetWidth(imageBuffer);
+    size_t height = CVPixelBufferGetHeight(imageBuffer);
+    uint8_t *yBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
+    size_t yPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0);
+    uint8_t *cbCrBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 1);
+    size_t cbCrPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 1);
+
+    int bytesPerPixel = 4;
+    uint8_t *rgbBuffer = malloc(width * height * bytesPerPixel);
+
+    for (int y = 0; y < height; y++) {
+        uint8_t *rgbBufferLine = &rgbBuffer[y * width * bytesPerPixel];
+        uint8_t *yBufferLine = &yBuffer[y * yPitch];
+        uint8_t *cbCrBufferLine = &cbCrBuffer[(y >> 1) * cbCrPitch];
+
+        for (int x = 0; x < width; x++) {
+            int16_t y = yBufferLine[x];
+            int16_t cb = cbCrBufferLine[x & ~1] - 128;
+            int16_t cr = cbCrBufferLine[x | 1] - 128;
+
+            uint8_t *rgbOutput = &rgbBufferLine[x * bytesPerPixel];
+
+            int16_t r = (int16_t)roundf(y + cr * 1.4);
+            int16_t g = (int16_t)roundf(y + cb * -0.343 + cr * -0.711);
+            int16_t b = (int16_t)roundf(y + cb * 1.765);
+
+            rgbOutput[0] = 0xff;
+            rgbOutput[1] = clamp(b);
+            rgbOutput[2] = clamp(g);
+            rgbOutput[3] = clamp(r);
+        }
+    }
+
+    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+    CGContextRef context = CGBitmapContextCreate(rgbBuffer, width, height, 8, width * bytesPerPixel, colorSpace,
+                                                 kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
+    CGImageRef quartzImage = CGBitmapContextCreateImage(context);
+
+    // TODO: Hardcoding UIImageOrientationRight seems cheesy
+    UIImage *image = [UIImage imageWithCGImage:quartzImage scale:1.0 orientation:UIImageOrientationRight];
+
+    CGContextRelease(context);
+    CGColorSpaceRelease(colorSpace);
+    CGImageRelease(quartzImage);
+    free(rgbBuffer);
+
+    CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
+
+    return image;
+}
+
+@end
diff --git a/ManagedCapturer/SCStillImageCaptureVideoInputMethod.h b/ManagedCapturer/SCStillImageCaptureVideoInputMethod.h
new file mode 100644
index 0000000..1704e53
--- /dev/null
+++ b/ManagedCapturer/SCStillImageCaptureVideoInputMethod.h
@@ -0,0 +1,19 @@
+//
+//  SCStillImageCaptureVideoInputMethod.h
+//  Snapchat
+//
+//  Created by Alexander Grytsiuk on 3/16/16.
+//  Copyright © 2016 Snapchat, Inc. All rights reserved.
+//
+
+#import "SCManagedCapturerState.h"
+
+#import <AVFoundation/AVFoundation.h>
+
+@interface SCStillImageCaptureVideoInputMethod : NSObject
+
+- (void)captureStillImageWithCapturerState:(SCManagedCapturerState *)state
+                              successBlock:(void (^)(NSData *imageData, NSDictionary *cameraInfo,
+                                                     NSError *error))successBlock
+                              failureBlock:(void (^)(NSError *error))failureBlock;
+@end
diff --git a/ManagedCapturer/SCStillImageCaptureVideoInputMethod.m b/ManagedCapturer/SCStillImageCaptureVideoInputMethod.m
new file mode 100644
index 0000000..ea6cb05
--- /dev/null
+++ b/ManagedCapturer/SCStillImageCaptureVideoInputMethod.m
@@ -0,0 +1,140 @@
+//
+//  SCStillImageCaptureVideoInputMethod.m
+//  Snapchat
+//
+//  Created by Alexander Grytsiuk on 3/16/16.
+//  Copyright © 2016 Snapchat, Inc. All rights reserved.
+//
+
+#import "SCStillImageCaptureVideoInputMethod.h"
+
+#import "SCManagedCapturer.h"
+#import "SCManagedVideoFileStreamer.h"
+
+typedef unsigned char uchar_t;
+int clamp(int val, int low, int high)
+{
+    if (val < low)
+        val = low;
+    if (val > high)
+        val = high;
+    return val;
+}
+
+void yuv2rgb(uchar_t yValue, uchar_t uValue, uchar_t vValue, uchar_t *r, uchar_t *g, uchar_t *b)
+{
+    double red = yValue + (1.370705 * (vValue - 128));
+    double green = yValue - (0.698001 * (vValue - 128)) - (0.337633 * (uValue - 128));
+    double blue = yValue + (1.732446 * (uValue - 128));
+    *r = clamp(red, 0, 255);
+    *g = clamp(green, 0, 255);
+    *b = clamp(blue, 0, 255);
+}
+
+void convertNV21DataToRGBData(int width, int height, uchar_t *nv21Data, uchar_t *rgbData, int rgbBytesPerPixel,
+                              int rgbBytesPerRow)
+{
+    uchar_t *uvData = nv21Data + height * width;
+    for (int h = 0; h < height; h++) {
+        uchar_t *yRowBegin = nv21Data + h * width;
+        uchar_t *uvRowBegin = uvData + h / 2 * width;
+        uchar_t *rgbRowBegin = rgbData + rgbBytesPerRow * h;
+        for (int w = 0; w < width; w++) {
+            uchar_t *rgbPixelBegin = rgbRowBegin + rgbBytesPerPixel * w;
+            yuv2rgb(yRowBegin[w], uvRowBegin[w / 2 * 2], uvRowBegin[w / 2 * 2 + 1], &(rgbPixelBegin[0]),
+                    &(rgbPixelBegin[1]), &(rgbPixelBegin[2]));
+        }
+    }
+}
+
+@implementation SCStillImageCaptureVideoInputMethod
+
+- (void)captureStillImageWithCapturerState:(SCManagedCapturerState *)state
+                              successBlock:(void (^)(NSData *imageData, NSDictionary *cameraInfo,
+                                                     NSError *error))successBlock
+                              failureBlock:(void (^)(NSError *error))failureBlock
+{
+    id<SCManagedVideoDataSource> videoDataSource = [[SCManagedCapturer sharedInstance] currentVideoDataSource];
+    if ([videoDataSource isKindOfClass:[SCManagedVideoFileStreamer class]]) {
+        SCManagedVideoFileStreamer *videoFileStreamer = (SCManagedVideoFileStreamer *)videoDataSource;
+        [videoFileStreamer getNextPixelBufferWithCompletion:^(CVPixelBufferRef pixelBuffer) {
+            BOOL shouldFlip = state.devicePosition == SCManagedCaptureDevicePositionFront;
+#if TARGET_IPHONE_SIMULATOR
+            UIImage *uiImage = [self imageWithCVPixelBuffer:pixelBuffer];
+            CGImageRef videoImage = uiImage.CGImage;
+            UIImage *capturedImage = [UIImage
+                imageWithCGImage:shouldFlip ? [self flipCGImage:videoImage size:uiImage.size].CGImage : videoImage
+                           scale:1.0
+                     orientation:UIImageOrientationRight];
+#else
+            CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
+            CIContext *temporaryContext = [CIContext contextWithOptions:nil];
+
+            CGSize size = CGSizeMake(CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer));
+            CGImageRef videoImage =
+                [temporaryContext createCGImage:ciImage fromRect:CGRectMake(0, 0, size.width, size.height)];
+
+            UIImage *capturedImage =
+                [UIImage imageWithCGImage:shouldFlip ? [self flipCGImage:videoImage size:size].CGImage : videoImage
+                                    scale:1.0
+                              orientation:UIImageOrientationRight];
+
+            CGImageRelease(videoImage);
+#endif
+            if (successBlock) {
+                successBlock(UIImageJPEGRepresentation(capturedImage, 1.0), nil, nil);
+            }
+        }];
+    } else {
+        if (failureBlock) {
+            failureBlock([NSError errorWithDomain:NSStringFromClass(self.class) code:-1 userInfo:nil]);
+        }
+    }
+}
+
+- (UIImage *)flipCGImage:(CGImageRef)cgImage size:(CGSize)size
+{
+    UIGraphicsBeginImageContext(size);
+    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, size.width, size.height), cgImage);
+    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
+    UIGraphicsEndImageContext();
+    return image;
+}
+
+- (UIImage *)imageWithCVPixelBuffer:(CVPixelBufferRef)imageBuffer
+{
+    CVPixelBufferLockBaseAddress(imageBuffer, 0);
+
+    size_t width = CVPixelBufferGetWidth(imageBuffer);
+    size_t height = CVPixelBufferGetHeight(imageBuffer);
+    size_t rgbBytesPerPixel = 4;
+    size_t rgbBytesPerRow = width * rgbBytesPerPixel;
+
+    uchar_t *nv21Data = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
+    uchar_t *rgbData = malloc(rgbBytesPerRow * height);
+
+    convertNV21DataToRGBData((int)width, (int)height, nv21Data, rgbData, (int)rgbBytesPerPixel, (int)rgbBytesPerRow);
+
+    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+    CGContextRef context =
+        CGBitmapContextCreate(rgbData, width, height, 8, rgbBytesPerRow, colorSpace, kCGImageAlphaNoneSkipLast);
+    CGImageRef cgImage = CGBitmapContextCreateImage(context);
+
+    UIImage *result = [UIImage imageWithCGImage:cgImage];
+
+    CGImageRelease(cgImage);
+    CGContextRelease(context);
+    CGColorSpaceRelease(colorSpace);
+    free(rgbData);
+
+    CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
+
+    return result;
+}
+
+- (NSString *)methodName
+{
+    return @"VideoInput";
+}
+
+@end
diff --git a/ManagedCapturer/SCTimedTask.h b/ManagedCapturer/SCTimedTask.h
new file mode 100644
index 0000000..f5a4e15
--- /dev/null
+++ b/ManagedCapturer/SCTimedTask.h
@@ -0,0 +1,28 @@
+//
+//  SCTimedTask.h
+//  Snapchat
+//
+//  Created by Michel Loenngren on 4/2/17.
+//  Copyright © 2017 Snapchat, Inc. All rights reserved.
+//
+
+#import <AVFoundation/AVFoundation.h>
+#import <Foundation/Foundation.h>
+
+/*
+ Block based timed task
+ */
+@interface SCTimedTask : NSObject
+
+@property (nonatomic, assign) CMTime targetTime;
+@property (nonatomic, copy) void (^task)(CMTime relativePresentationTime, CGFloat sessionStartTimeDelayInSecond);
+
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithTargetTime:(CMTime)targetTime
+                              task:(void (^)(CMTime relativePresentationTime,
+                                             CGFloat sessionStartTimeDelayInSecond))task;
+
+- (NSString *)description;
+
+@end
diff --git a/ManagedCapturer/SCTimedTask.m b/ManagedCapturer/SCTimedTask.m
new file mode 100644
index 0000000..babf445
--- /dev/null
+++ b/ManagedCapturer/SCTimedTask.m
@@ -0,0 +1,32 @@
+//
+//  SCTimedTask.m
+//  Snapchat
+//
+//  Created by Michel Loenngren on 4/2/17.
+//  Copyright © 2017 Snapchat, Inc. All rights reserved.
+//
+
+#import "SCTimedTask.h"
+
+#import <SCFoundation/NSString+SCFormat.h>
+
+@implementation SCTimedTask
+
+- (instancetype)initWithTargetTime:(CMTime)targetTime
+                              task:
+                                  (void (^)(CMTime relativePresentationTime, CGFloat sessionStartTimeDelayInSecond))task
+{
+    if (self = [super init]) {
+        _targetTime = targetTime;
+        _task = task;
+    }
+    return self;
+}
+
+- (NSString *)description
+{
+    return [NSString
+        sc_stringWithFormat:@"<%@: %p, targetTime: %lld>", NSStringFromClass([self class]), self, _targetTime.value];
+}
+
+@end
diff --git a/ManagedCapturer/SCVideoCaptureSessionInfo.h b/ManagedCapturer/SCVideoCaptureSessionInfo.h
new file mode 100644
index 0000000..b89da3e
--- /dev/null
+++ b/ManagedCapturer/SCVideoCaptureSessionInfo.h
@@ -0,0 +1,83 @@
+//
+//  SCVideoCaptureSessionInfo.h
+//  Snapchat
+//
+//  Created by Michel Loenngren on 3/27/17.
+//  Copyright © 2017 Snapchat, Inc. All rights reserved.
+//
+
+#import <SCFoundation/NSString+SCFormat.h>
+
+#import <CoreMedia/CoreMedia.h>
+#import <Foundation/Foundation.h>
+
+typedef NS_ENUM(NSInteger, SCManagedVideoCapturerInfoType) {
+    SCManagedVideoCapturerInfoAudioQueueError,
+    SCManagedVideoCapturerInfoAssetWriterError,
+    SCManagedVideoCapturerInfoAudioSessionError,
+    SCManagedVideoCapturerInfoAudioQueueRetrySuccess,
+    SCManagedVideoCapturerInfoAudioQueueRetryDataSourceSuccess_audioQueue,
+    SCManagedVideoCapturerInfoAudioQueueRetryDataSourceSuccess_hardware
+};
+
+typedef u_int32_t sc_managed_capturer_recording_session_t;
+
+/*
+ Container object holding information about the
+ current recording session.
+ */
+typedef struct {
+    CMTime startTime;
+    CMTime endTime;
+    CMTime duration;
+    sc_managed_capturer_recording_session_t sessionId;
+} SCVideoCaptureSessionInfo;
+
+static inline SCVideoCaptureSessionInfo SCVideoCaptureSessionInfoMake(CMTime startTime, CMTime endTime,
+                                                                      sc_managed_capturer_recording_session_t sessionId)
+{
+    SCVideoCaptureSessionInfo session;
+    session.startTime = startTime;
+    session.endTime = endTime;
+    if (CMTIME_IS_VALID(startTime) && CMTIME_IS_VALID(endTime)) {
+        session.duration = CMTimeSubtract(endTime, startTime);
+    } else {
+        session.duration = kCMTimeInvalid;
+    }
+    session.sessionId = sessionId;
+    return session;
+}
+
+static inline NSTimeInterval SCVideoCaptureSessionInfoGetCurrentDuration(SCVideoCaptureSessionInfo sessionInfo)
+{
+    if (CMTIME_IS_VALID(sessionInfo.startTime)) {
+        if (CMTIME_IS_VALID(sessionInfo.endTime)) {
+            return CMTimeGetSeconds(sessionInfo.duration);
+        }
+        return CACurrentMediaTime() - CMTimeGetSeconds(sessionInfo.startTime);
+    }
+    return 0;
+}
+
+static inline NSString *SCVideoCaptureSessionInfoGetDebugString(CMTime time, NSString *label)
+{
+    if (CMTIME_IS_VALID(time)) {
+        return [NSString sc_stringWithFormat:@"%@: %f", label, CMTimeGetSeconds(time)];
+    } else {
+        return [NSString sc_stringWithFormat:@"%@: Invalid", label];
+    }
+}
+
+static inline NSString *SCVideoCaptureSessionInfoGetDebugDescription(SCVideoCaptureSessionInfo sessionInfo)
+{
+    NSMutableString *description = [NSMutableString new];
+    [description appendString:SCVideoCaptureSessionInfoGetDebugString(sessionInfo.startTime, @"StartTime")];
+    [description appendString:@", "];
+    [description appendString:SCVideoCaptureSessionInfoGetDebugString(sessionInfo.endTime, @"EndTime")];
+    [description appendString:@", "];
+    [description appendString:SCVideoCaptureSessionInfoGetDebugString(sessionInfo.duration, @"Duration")];
+    [description appendString:@", "];
+    [description appendString:[NSString sc_stringWithFormat:@"Id: %u", sessionInfo.sessionId]];
+
+    return [description copy];
+}
diff --git a/ManagedCapturer/StateMachine/SCCaptureBaseState.h b/ManagedCapturer/StateMachine/SCCaptureBaseState.h
new file mode 100644
index 0000000..ef18f00
--- /dev/null
+++ b/ManagedCapturer/StateMachine/SCCaptureBaseState.h
@@ -0,0 +1,103 @@
+//
+//  SCCaptureBaseState.h
+//  Snapchat
+//
+//  Created by Lin Jia on 10/19/17.
+//
+//
+
+#import "SCCaptureCommon.h"
+#import "SCCaptureStateDelegate.h"
+#import "SCCaptureStateMachineBookKeeper.h"
+#import "SCCaptureStateUtil.h"
+#import "SCCaptureWorker.h"
+#import "SCManagedCaptureDevice.h"
+#import "SCManagedCapturerState.h"
+#import "SCStateTransitionPayload.h"
+
+#import <Foundation/Foundation.h>
+
+@class SCCaptureResource;
+
+@class SCCapturerToken;
+
+@class SCAudioConfiguration;
+
+@class SCQueuePerformer;
+/*
+ Every state machine state needs to inherent SCCaptureBaseState to have the APIs. State machine state in general will
+ only implement APIs which are legal for itself. If illegal APIs are invoked, SCCaptureBaseState will handle it.
+ The intended behavior:
+ 1) crash using SCAssert in Debug build,
+ 2) ignore api call, and log the call, for alpha/master/production.
+ 3) in the future, we will introduce dangerous API call concept, and restart camera in such case, to avoid bad state.
+
+ Every state machine state is going to be built to follow functional programming as more as possible. The shared
+ resources between them will be passed into the API via SCCaptureResource.
+ */
+
+@interface SCCaptureBaseState : NSObject
+
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate;
+
+/* The following API will be invoked at the moment state context promote the state to be current state. State use this
+ * chance to do something, such as start recording for recording state.
+ */
+- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload
+                     resource:(SCCaptureResource *)resource
+                      context:(NSString *)context;
+
+- (SCCaptureStateMachineStateId)stateId;
+
+- (void)initializeCaptureWithDevicePosition:(SCManagedCaptureDevicePosition)devicePosition
+                                   resource:(SCCaptureResource *)resource
+                          completionHandler:(dispatch_block_t)completionHandler
+                                    context:(NSString *)context;
+
+- (void)startRunningWithCapturerToken:(SCCapturerToken *)token
+                             resource:(SCCaptureResource *)resource
+                    completionHandler:(dispatch_block_t)completionHandler
+                              context:(NSString *)context;
+
+- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token
+                            resource:(SCCaptureResource *)resource
+                   completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler
+                             context:(NSString *)context;
+
+- (void)prepareForRecordingWithResource:(SCCaptureResource *)resource
+                     audioConfiguration:(SCAudioConfiguration *)configuration
+                                context:(NSString *)context;
+
+- (void)startRecordingWithResource:(SCCaptureResource *)resource
+                audioConfiguration:(SCAudioConfiguration *)configuration
+                    outputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings
+                       maxDuration:(NSTimeInterval)maxDuration
+                           fileURL:(NSURL *)fileURL
+                  captureSessionID:(NSString *)captureSessionID
+                 completionHandler:(sc_managed_capturer_start_recording_completion_handler_t)completionHandler
+                           context:(NSString *)context;
+
+- (void)stopRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context;
+
+- (void)cancelRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context;
+
+- (void)captureStillImageWithResource:(SCCaptureResource *)resource
+                          aspectRatio:(CGFloat)aspectRatio
+                     captureSessionID:(NSString *)captureSessionID
+                    completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler
+                              context:(NSString *)context;
+
+- (void)startScanWithScanConfiguration:(SCScanConfiguration *)configuration
+                              resource:(SCCaptureResource *)resource
+                               context:(NSString *)context;
+
+- (void)stopScanWithCompletionHandler:(dispatch_block_t)completionHandler
+                             resource:(SCCaptureResource *)resource
+                              context:(NSString *)context;
+
+@property (nonatomic, strong, readonly) SCCaptureStateMachineBookKeeper *bookKeeper;
+@end
diff --git a/ManagedCapturer/StateMachine/SCCaptureBaseState.m b/ManagedCapturer/StateMachine/SCCaptureBaseState.m
new file mode 100644
index 0000000..569ab54
--- /dev/null
+++ b/ManagedCapturer/StateMachine/SCCaptureBaseState.m
@@ -0,0 +1,169 @@
+//
+//  SCCaptureBaseState.m
+//  Snapchat
+//
+//  Created by Lin Jia on 10/19/17.
+//
+//
+
+#import "SCCaptureBaseState.h"
+
+#import "SCCaptureStateMachineBookKeeper.h"
+#import "SCCapturerToken.h"
+#import "SCManagedCapturerV1_Private.h"
+
+#import <SCFoundation/SCAppEnvironment.h>
+#import <SCFoundation/SCAssertWrapper.h>
+#import <SCFoundation/SCQueuePerformer.h>
+
+@implementation SCCaptureBaseState {
+    SCCaptureStateMachineBookKeeper *_bookKeeper;
+    SCQueuePerformer *_performer;
+    __weak id<SCCaptureStateDelegate> _delegate;
+}
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate
+{
+    self = [super init];
+    if (self) {
+        SCAssert(performer, @"");
+        SCAssert(bookKeeper, @"");
+        _bookKeeper = bookKeeper;
+        _performer = performer;
+        _delegate = delegate;
+    }
+    return self;
+}
+
+- (SCCaptureStateMachineStateId)stateId
+{
+    return SCCaptureBaseStateId;
+}
+
+- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload
+                     resource:(SCCaptureResource *)resource
+                      context:(NSString *)context
+{
+    [self _handleBaseStateBehavior:@"didBecomeCurrentState" context:context];
+}
+
+- (void)initializeCaptureWithDevicePosition:(SCManagedCaptureDevicePosition)devicePosition
+                                   resource:(SCCaptureResource *)resource
+                          completionHandler:(dispatch_block_t)completionHandler
+                                    context:(NSString *)context
+{
+    [self _handleBaseStateBehavior:@"initializeCaptureWithDevicePosition" context:context];
+}
+
+- (void)startRunningWithCapturerToken:(SCCapturerToken *)token
+                             resource:(SCCaptureResource *)resource
+                    completionHandler:(dispatch_block_t)completionHandler
+                              context:(NSString *)context
+{
+    [self _handleBaseStateBehavior:@"startRunningWithCapturerToken" context:context];
+}
+
+- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token
+                            resource:(SCCaptureResource *)resource
+                   completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler
+                             context:(NSString *)context
+{
+    SCAssertPerformer(_performer);
+    BOOL actuallyStopped = [[SCManagedCapturerV1 sharedInstance] stopRunningWithCaptureToken:token
+                                                                           completionHandler:completionHandler
+                                                                                     context:context];
+    // TODO: Fix CCAM-14450
+    // This is a temporary solution for https://jira.sc-corp.net/browse/CCAM-14450
+    // It is caused by switching from scanning state to stop running state when the view is disappearing in the scanning
+    // state, which can be reproduced by triggering scanning and then switch to maps page.
+    // We remove SCAssert to ingore the crashes in master branch and will find a solution for the illegal call for the
+    // state machine later
+
+    if (self.stateId != SCCaptureScanningStateId) {
+        SCAssert(!actuallyStopped, @"actuallyStopped in state: %@ with context: %@", SCCaptureStateName([self stateId]),
+                 context);
+    } else {
+        SCLogCaptureStateMachineInfo(@"actuallyStopped:%d in state: %@ with context: %@", actuallyStopped,
+                                     SCCaptureStateName([self stateId]), context);
+    }
+
+    if (actuallyStopped) {
+        [_delegate currentState:self
+            requestToTransferToNewState:SCCaptureInitializedStateId
+                                payload:nil
+                                context:context];
+    }
+}
+
+- (void)prepareForRecordingWithResource:(SCCaptureResource *)resource
+                     audioConfiguration:(SCAudioConfiguration *)configuration
+                                context:(NSString *)context
+{
+    [self _handleBaseStateBehavior:@"prepareForRecordingWithResource" context:context];
+}
+
+- (void)startRecordingWithResource:(SCCaptureResource *)resource
+                audioConfiguration:(SCAudioConfiguration *)configuration
+                    outputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings
+                       maxDuration:(NSTimeInterval)maxDuration
+                           fileURL:(NSURL *)fileURL
+                  captureSessionID:(NSString *)captureSessionID
+                 completionHandler:(sc_managed_capturer_start_recording_completion_handler_t)completionHandler
+                           context:(NSString *)context
+{
+    [self _handleBaseStateBehavior:@"startRecordingWithResource" context:context];
+}
+
+- (void)stopRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context
+{
+    [self _handleBaseStateBehavior:@"stopRecordingWithResource" context:context];
+}
+
+- (void)cancelRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context
+{
+    [self _handleBaseStateBehavior:@"cancelRecordingWithResource" context:context];
+}
+
+- (void)captureStillImageWithResource:(SCCaptureResource *)resource
+                          aspectRatio:(CGFloat)aspectRatio
+                     captureSessionID:(NSString *)captureSessionID
+                    completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler
+                              context:(NSString *)context
+{
+    [self _handleBaseStateBehavior:@"captureStillImageWithResource" context:context];
+}
+
+- (void)startScanWithScanConfiguration:(SCScanConfiguration *)configuration
+                              resource:(SCCaptureResource *)resource
+                               context:(NSString *)context
+{
+    [self _handleBaseStateBehavior:@"startScanWithScanConfiguration" context:context];
+}
+
+- (void)stopScanWithCompletionHandler:(dispatch_block_t)completionHandler
+                             resource:(SCCaptureResource *)resource
+                              context:(NSString *)context
+{
+    // Temporary solution until IDT-12520 is resolved.
+    [SCCaptureWorker stopScanWithCompletionHandler:completionHandler resource:resource];
+    //[self _handleBaseStateBehavior:@"stopScanWithCompletionHandler"];
+}
+
+- (void)_handleBaseStateBehavior:(NSString *)illegalAPIName context:(NSString *)context
+{
+    [_bookKeeper state:[self stateId]
+        illegalAPIcalled:illegalAPIName
+               callStack:[NSThread callStackSymbols]
+                 context:context];
+    if (SCIsDebugBuild()) {
+        SCAssertFail(@"illegal API invoked on capture state machine");
+    }
+}
+
+- (SCCaptureStateMachineBookKeeper *)bookKeeper
+{
+    return _bookKeeper;
+}
+@end
diff --git a/ManagedCapturer/StateMachine/SCCaptureStateDelegate.h b/ManagedCapturer/StateMachine/SCCaptureStateDelegate.h
new file mode 100644
index 0000000..f07766f
--- /dev/null
+++ b/ManagedCapturer/StateMachine/SCCaptureStateDelegate.h
@@ -0,0 +1,30 @@
+//
+//  SCCaptureStateDelegate.h
+//  Snapchat
+//
+//  Created by Lin Jia on 10/27/17.
+//
+//
+
+#import "SCCaptureStateUtil.h"
+
+#import <Foundation/Foundation.h>
+
+@class SCCaptureBaseState;
+@class SCStateTransitionPayload;
+/*
+ The state machine state delegate is used by state machine states to hint to the system that "I am done, now transfer
+ to other state".
+
+ Currently, SCCaptureStateMachineContext is the central piece that glues all states together, and it is the delegate for
+ those states.
+ */
+
+@protocol SCCaptureStateDelegate <NSObject>
+
+- (void)currentState:(SCCaptureBaseState *)state
+    requestToTransferToNewState:(SCCaptureStateMachineStateId)newState
+                        payload:(SCStateTransitionPayload *)payload
+                        context:(NSString *)context;
+
+@end
diff --git a/ManagedCapturer/StateMachine/SCCaptureStateMachineBookKeeper.h b/ManagedCapturer/StateMachine/SCCaptureStateMachineBookKeeper.h
new file mode 100644
index 0000000..24ea585
--- /dev/null
+++ b/ManagedCapturer/StateMachine/SCCaptureStateMachineBookKeeper.h
@@ -0,0 +1,29 @@
+//
+//  SCCaptureStateTransitionBookKeeper.h
+//  Snapchat
+//
+//  Created by Lin Jia on 10/27/17.
+//
+//
+
+#import "SCCaptureStateUtil.h"
+
+#import <Foundation/Foundation.h>
+
+/*
+ Book keeper is used to record every state transition, and every illegal API call.
+ */
+
+@interface SCCaptureStateMachineBookKeeper : NSObject
+
+- (void)stateTransitionFrom:(SCCaptureStateMachineStateId)fromId
+                         to:(SCCaptureStateMachineStateId)toId
+                    context:(NSString *)context;
+
+- (void)state:(SCCaptureStateMachineStateId)captureState
+    illegalAPIcalled:(NSString *)illegalAPIName
+           callStack:(NSArray<NSString *> *)callStack
+             context:(NSString *)context;
+
+- (void)logAPICalled:(NSString *)apiName context:(NSString *)context;
+@end
diff --git a/ManagedCapturer/StateMachine/SCCaptureStateMachineBookKeeper.m b/ManagedCapturer/StateMachine/SCCaptureStateMachineBookKeeper.m
new file mode 100644
index 0000000..7d9c466
--- /dev/null
+++ b/ManagedCapturer/StateMachine/SCCaptureStateMachineBookKeeper.m
@@ -0,0 +1,63 @@
+//
+//  SCCaptureStateTransitionBookKeeper.m
+//  Snapchat
+//
+//  Created by Lin Jia on 10/27/17.
+//
+//
+
+#import "SCCaptureStateMachineBookKeeper.h"
+
+#import "SCCaptureStateUtil.h"
+#import "SCLogger+Camera.h"
+
+#import <SCFoundation/SCAssertWrapper.h>
+#import <SCLogger/SCCameraMetrics.h>
+
+@interface SCCaptureStateMachineBookKeeper () {
+    NSDate *_lastStateStartTime;
+}
+@end
+
+@implementation SCCaptureStateMachineBookKeeper
+
+- (void)stateTransitionFrom:(SCCaptureStateMachineStateId)fromId
+                         to:(SCCaptureStateMachineStateId)toId
+                    context:(NSString *)context
+{
+    NSDate *date = [NSDate date];
+    SCLogCaptureStateMachineInfo(@"State %@ life span: %f seconds, transition to: %@, in context:%@, at: %@ \n",
+                                 SCCaptureStateName(fromId), [date timeIntervalSinceDate:_lastStateStartTime],
+                                 SCCaptureStateName(toId), context, date);
+    _lastStateStartTime = date;
+}
+
+- (void)state:(SCCaptureStateMachineStateId)captureState
+    illegalAPIcalled:(NSString *)illegalAPIName
+           callStack:(NSArray<NSString *> *)callStack
+             context:(NSString *)context
+
+{
+    SCAssert(callStack, @"call stack empty");
+    SCAssert(illegalAPIName, @"");
+    SCAssert(context, @"Context is empty");
+    SCLogCaptureStateMachineError(@"State: %@, illegal API invoke: %@, at: %@, callstack: %@ \n",
+                                  SCCaptureStateName(captureState), illegalAPIName, [NSDate date], callStack);
+    NSArray<NSString *> *reportedArray =
+        [callStack count] > 15 ? [callStack subarrayWithRange:NSMakeRange(0, 15)] : callStack;
+    [[SCLogger sharedInstance] logEvent:kSCCameraStateMachineIllegalAPICall
+                             parameters:@{
+                                 @"state" : SCCaptureStateName(captureState),
+                                 @"API" : illegalAPIName,
+                                 @"call_stack" : reportedArray,
+                                 @"context" : context
+                             }];
+}
+
+- (void)logAPICalled:(NSString *)apiName context:(NSString *)context
+{
+    SCAssert(apiName, @"API name is empty");
+    SCAssert(context, @"Context is empty");
+    SCLogCaptureStateMachineInfo(@"api: %@ context: %@", apiName, context);
+}
+@end
diff --git a/ManagedCapturer/StateMachine/SCCaptureStateMachineContext.h b/ManagedCapturer/StateMachine/SCCaptureStateMachineContext.h
new file mode 100644
index 0000000..1e98943
--- /dev/null
+++ b/ManagedCapturer/StateMachine/SCCaptureStateMachineContext.h
@@ -0,0 +1,76 @@
+//
+//  SCCaptureStateMachineContext.h
+//  Snapchat
+//
+//  Created by Lin Jia on 10/18/17.
+//
+//
+
+#import "SCCaptureCommon.h"
+#import "SCManagedCaptureDevice.h"
+
+#import <SCAudio/SCAudioConfiguration.h>
+
+#import <Foundation/Foundation.h>
+
+/*
+ SCCaptureStateMachineContext is the central piece that glues all states together.
+
+ It will pass API calls to the current state.
+
+ The classic state machine design pattern:
+ https://en.wikipedia.org/wiki/State_pattern
+
+ It is also the delegate for the states it manages, so that those states can tell stateMachineContext to transit to next
+ state.
+ */
+
+@class SCCaptureResource;
+
+@class SCCapturerToken;
+
+@interface SCCaptureStateMachineContext : NSObject
+
+- (instancetype)initWithResource:(SCCaptureResource *)resource;
+
+- (void)initializeCaptureWithDevicePositionAsynchronously:(SCManagedCaptureDevicePosition)devicePosition
+                                        completionHandler:(dispatch_block_t)completionHandler
+                                                  context:(NSString *)context;
+
+- (SCCapturerToken *)startRunningWithContext:(NSString *)context completionHandler:(dispatch_block_t)completionHandler;
+
+- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token
+                   completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler
+                             context:(NSString *)context;
+
+- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token
+                               after:(NSTimeInterval)delay
+                   completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler
+                             context:(NSString *)context;
+
+- (void)prepareForRecordingAsynchronouslyWithAudioConfiguration:(SCAudioConfiguration *)configuration
+                                                        context:(NSString *)context;
+
+- (void)startRecordingWithOutputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings
+                      audioConfiguration:(SCAudioConfiguration *)configuration
+                             maxDuration:(NSTimeInterval)maxDuration
+                                 fileURL:(NSURL *)fileURL
+                        captureSessionID:(NSString *)captureSessionID
+                       completionHandler:(sc_managed_capturer_start_recording_completion_handler_t)completionHandler
+                                 context:(NSString *)context;
+
+- (void)stopRecordingWithContext:(NSString *)context;
+
+- (void)cancelRecordingWithContext:(NSString *)context;
+
+- (void)captureStillImageAsynchronouslyWithAspectRatio:(CGFloat)aspectRatio
+                                      captureSessionID:(NSString *)captureSessionID
+                                     completionHandler:
+                                         (sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler
+                                               context:(NSString *)context;
+
+#pragma mark - Scanning
+- (void)startScanAsynchronouslyWithScanConfiguration:(SCScanConfiguration *)configuration context:(NSString *)context;
+- (void)stopScanAsynchronouslyWithCompletionHandler:(dispatch_block_t)completionHandler context:(NSString *)context;
+
+@end
diff --git a/ManagedCapturer/StateMachine/SCCaptureStateMachineContext.m b/ManagedCapturer/StateMachine/SCCaptureStateMachineContext.m
new file mode 100644
index 0000000..5fd1b7a
--- /dev/null
+++ b/ManagedCapturer/StateMachine/SCCaptureStateMachineContext.m
@@ -0,0 +1,301 @@
+//
+//  SCCaptureStateMachineContext.m
+//  Snapchat
+//
+//  Created by Lin Jia on 10/18/17.
+//
+//
+
+#import "SCCaptureStateMachineContext.h"
+
+#import "SCCaptureBaseState.h"
+#import "SCCaptureImageState.h"
+#import "SCCaptureImageWhileRecordingState.h"
+#import "SCCaptureInitializedState.h"
+#import "SCCaptureRecordingState.h"
+#import "SCCaptureResource.h"
+#import "SCCaptureRunningState.h"
+#import "SCCaptureScanningState.h"
+#import "SCCaptureStateMachineBookKeeper.h"
+#import "SCCaptureStateUtil.h"
+#import "SCCaptureUninitializedState.h"
+#import "SCCaptureWorker.h"
+#import "SCCapturerToken.h"
+#import "SCStateTransitionPayload.h"
+
+#import <SCAudio/SCAudioConfiguration.h>
+#import <SCFoundation/SCAssertWrapper.h>
+#import <SCFoundation/SCQueuePerformer.h>
+#import <SCFoundation/SCTrace.h>
+#import <SCLogger/SCCameraMetrics.h>
+#import <SCLogger/SCLogger+Performance.h>
+
+@interface SCCaptureStateMachineContext () <SCCaptureStateDelegate> {
+    SCQueuePerformer *_queuePerformer;
+
+    // Cache all the states.
+    NSMutableDictionary<SCCaptureStateKey *, SCCaptureBaseState *> *_states;
+    SCCaptureBaseState *_currentState;
+    SCCaptureStateMachineBookKeeper *_bookKeeper;
+    SCCaptureResource *_captureResource;
+}
+@end
+
+@implementation SCCaptureStateMachineContext
+
+- (instancetype)initWithResource:(SCCaptureResource *)resource
+{
+    self = [super init];
+    if (self) {
+        SCAssert(resource, @"");
+        SCAssert(resource.queuePerformer, @"");
+        _captureResource = resource;
+        _queuePerformer = resource.queuePerformer;
+        _states = [[NSMutableDictionary<SCCaptureStateKey *, SCCaptureBaseState *> alloc] init];
+        _bookKeeper = [[SCCaptureStateMachineBookKeeper alloc] init];
+        [self _setCurrentState:SCCaptureUninitializedStateId payload:nil context:SCCapturerContext];
+    }
+    return self;
+}
+
+- (void)_setCurrentState:(SCCaptureStateMachineStateId)stateId
+                 payload:(SCStateTransitionPayload *)payload
+                 context:(NSString *)context
+{
+    switch (stateId) {
+    case SCCaptureUninitializedStateId:
+        if (![_states objectForKey:@(stateId)]) {
+            SCCaptureUninitializedState *uninitializedState =
+                [[SCCaptureUninitializedState alloc] initWithPerformer:_queuePerformer
+                                                            bookKeeper:_bookKeeper
+                                                              delegate:self];
+            [_states setObject:uninitializedState forKey:@(stateId)];
+        }
+        _currentState = [_states objectForKey:@(stateId)];
+        break;
+    case SCCaptureInitializedStateId:
+        if (![_states objectForKey:@(stateId)]) {
+            SCCaptureInitializedState *initializedState =
+                [[SCCaptureInitializedState alloc] initWithPerformer:_queuePerformer
+                                                          bookKeeper:_bookKeeper
+                                                            delegate:self];
+            [_states setObject:initializedState forKey:@(stateId)];
+        }
+        _currentState = [_states objectForKey:@(stateId)];
+        break;
+    case SCCaptureRunningStateId:
+        if (![_states objectForKey:@(stateId)]) {
+            SCCaptureRunningState *runningState =
+                [[SCCaptureRunningState alloc] initWithPerformer:_queuePerformer bookKeeper:_bookKeeper delegate:self];
+            [_states setObject:runningState forKey:@(stateId)];
+        }
+        _currentState = [_states objectForKey:@(stateId)];
+        break;
+    case SCCaptureImageStateId:
+        if (![_states objectForKey:@(stateId)]) {
+            SCCaptureImageState *captureImageState =
+                [[SCCaptureImageState alloc] initWithPerformer:_queuePerformer bookKeeper:_bookKeeper delegate:self];
+            [_states setObject:captureImageState forKey:@(stateId)];
+        }
+        _currentState = [_states objectForKey:@(stateId)];
+        break;
+    case SCCaptureImageWhileRecordingStateId:
+        if (![_states objectForKey:@(stateId)]) {
+            SCCaptureImageWhileRecordingState *captureImageWhileRecordingState =
+                [[SCCaptureImageWhileRecordingState alloc] initWithPerformer:_queuePerformer
+                                                                  bookKeeper:_bookKeeper
+                                                                    delegate:self];
+            [_states setObject:captureImageWhileRecordingState forKey:@(stateId)];
+        }
+        _currentState = [_states objectForKey:@(stateId)];
+        break;
+    case SCCaptureScanningStateId:
+        if (![_states objectForKey:@(stateId)]) {
+            SCCaptureScanningState *scanningState =
+                [[SCCaptureScanningState alloc] initWithPerformer:_queuePerformer bookKeeper:_bookKeeper delegate:self];
+            [_states setObject:scanningState forKey:@(stateId)];
+        }
+        _currentState = [_states objectForKey:@(stateId)];
+        break;
+    case SCCaptureRecordingStateId:
+        if (![_states objectForKey:@(stateId)]) {
+            SCCaptureRecordingState *recordingState = [[SCCaptureRecordingState alloc] initWithPerformer:_queuePerformer
+                                                                                              bookKeeper:_bookKeeper
+                                                                                                delegate:self];
+            [_states setObject:recordingState forKey:@(stateId)];
+        }
+        _currentState = [_states objectForKey:@(stateId)];
+        break;
+    default:
+        SCAssert(NO, @"illigal state Id");
+        break;
+    }
+    [_currentState didBecomeCurrentState:payload resource:_captureResource context:context];
+}
+
+- (void)initializeCaptureWithDevicePositionAsynchronously:(SCManagedCaptureDevicePosition)devicePosition
+                                        completionHandler:(dispatch_block_t)completionHandler
+                                                  context:(NSString *)context
+{
+    [SCCaptureWorker setupCapturePreviewLayerController];
+
+    SCTraceResumeToken resumeToken = SCTraceCapture();
+    [_queuePerformer perform:^{
+        SCTraceResume(resumeToken);
+        [_currentState initializeCaptureWithDevicePosition:devicePosition
+                                                  resource:_captureResource
+                                         completionHandler:completionHandler
+                                                   context:context];
+    }];
+}
+
+- (SCCapturerToken *)startRunningWithContext:(NSString *)context completionHandler:(dispatch_block_t)completionHandler
+{
+    [[SCLogger sharedInstance] updateLogTimedEventStart:kSCCameraMetricsOpen uniqueId:@""];
+
+    SCCapturerToken *token = [[SCCapturerToken alloc] initWithIdentifier:context];
+    SCTraceResumeToken resumeToken = SCTraceCapture();
+    [_queuePerformer perform:^{
+        SCTraceResume(resumeToken);
+        [_currentState startRunningWithCapturerToken:token
+                                            resource:_captureResource
+                                   completionHandler:completionHandler
+                                             context:context];
+    }];
+
+    return token;
+}
+
+- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token
+                   completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler
+                             context:(NSString *)context
+{
+    SCTraceResumeToken resumeToken = SCTraceCapture();
+    [_queuePerformer perform:^{
+        SCTraceResume(resumeToken);
+        [_currentState stopRunningWithCapturerToken:token
+                                           resource:_captureResource
+                                  completionHandler:completionHandler
+                                            context:context];
+    }];
+}
+
+- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token
+                               after:(NSTimeInterval)delay
+                   completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler
+                             context:(NSString *)context
+{
+    SCTraceResumeToken resumeToken = SCTraceCapture();
+    [_queuePerformer perform:^{
+        SCTraceResume(resumeToken);
+        [_currentState stopRunningWithCapturerToken:token
+                                           resource:_captureResource
+                                  completionHandler:completionHandler
+                                            context:context];
+    }
+                       after:delay];
+}
+
+- (void)prepareForRecordingAsynchronouslyWithAudioConfiguration:(SCAudioConfiguration *)configuration
+                                                        context:(NSString *)context
+{
+    SCTraceResumeToken resumeToken = SCTraceCapture();
+    [_queuePerformer perform:^{
+        SCTraceResume(resumeToken);
+        [_currentState prepareForRecordingWithResource:_captureResource
+                                    audioConfiguration:configuration
+                                               context:context];
+    }];
+}
+
+- (void)startRecordingWithOutputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings
+                      audioConfiguration:(SCAudioConfiguration *)configuration
+                             maxDuration:(NSTimeInterval)maxDuration
+                                 fileURL:(NSURL *)fileURL
+                        captureSessionID:(NSString *)captureSessionID
+                       completionHandler:(sc_managed_capturer_start_recording_completion_handler_t)completionHandler
+                                 context:(NSString *)context
+{
+    SCTraceResumeToken resumeToken = SCTraceCapture();
+    [_queuePerformer perform:^{
+        SCTraceResume(resumeToken);
+        [_currentState startRecordingWithResource:_captureResource
+                               audioConfiguration:configuration
+                                   outputSettings:outputSettings
+                                      maxDuration:maxDuration
+                                          fileURL:fileURL
+                                 captureSessionID:captureSessionID
+                                completionHandler:completionHandler
+                                          context:context];
+    }];
+}
+
+- (void)stopRecordingWithContext:(NSString *)context
+{
+    SCTraceResumeToken resumeToken = SCTraceCapture();
+    [_queuePerformer perform:^{
+        SCTraceResume(resumeToken);
+        [_currentState stopRecordingWithResource:_captureResource context:context];
+    }];
+}
+
+- (void)cancelRecordingWithContext:(NSString *)context
+{
+    SCTraceResumeToken resumeToken = SCTraceCapture();
+    [_queuePerformer perform:^{
+        SCTraceResume(resumeToken);
+        [_currentState cancelRecordingWithResource:_captureResource context:context];
+    }];
+}
+
+- (void)captureStillImageAsynchronouslyWithAspectRatio:(CGFloat)aspectRatio
+                                      captureSessionID:(NSString *)captureSessionID
+                                     completionHandler:
+                                         (sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler
+                                               context:(NSString *)context
+{
+    [_queuePerformer perform:^() {
+        [_currentState captureStillImageWithResource:_captureResource
+                                         aspectRatio:aspectRatio
+                                    captureSessionID:captureSessionID
+                                   completionHandler:completionHandler
+                                             context:context];
+    }];
+}
+
+- (void)startScanAsynchronouslyWithScanConfiguration:(SCScanConfiguration *)configuration context:(NSString *)context
+{
+    [_queuePerformer perform:^() {
+        [_currentState startScanWithScanConfiguration:configuration resource:_captureResource context:context];
+    }];
+}
+
+- (void)stopScanAsynchronouslyWithCompletionHandler:(dispatch_block_t)completionHandler context:(NSString *)context
+{
+    [_queuePerformer perform:^() {
+        [_currentState stopScanWithCompletionHandler:completionHandler resource:_captureResource context:context];
+    }];
+}
+
+- (void)currentState:(SCCaptureBaseState *)state
+    requestToTransferToNewState:(SCCaptureStateMachineStateId)newState
+                        payload:(SCStateTransitionPayload *)payload
+                        context:(NSString *)context
+{
+    SCAssertPerformer(_queuePerformer);
+    SCAssert(_currentState == state, @"state: %@ newState: %@ context:%@", SCCaptureStateName([state stateId]),
+             SCCaptureStateName(newState), context);
+    if (payload) {
+        SCAssert(payload.fromState == [state stateId], @"From state id check");
+        SCAssert(payload.toState == newState, @"To state id check");
+    }
+
+    if (_currentState != state) {
+        return;
+    }
+
+    [_bookKeeper stateTransitionFrom:[state stateId] to:newState context:context];
+    [self _setCurrentState:newState payload:payload context:context];
+}
+
+@end
diff --git a/ManagedCapturer/StateMachine/SCCaptureStateUtil.h b/ManagedCapturer/StateMachine/SCCaptureStateUtil.h
new file mode 100644
index 0000000..1b8ca4a
--- /dev/null
+++ b/ManagedCapturer/StateMachine/SCCaptureStateUtil.h
@@ -0,0 +1,37 @@
+//
+//  SCCaptureStateUtil.h
+//  Snapchat
+//
+//  Created by Lin Jia on 10/27/17.
+//
+//
+
+#import "SCLogger+Camera.h"
+
+#import <SCBase/SCMacros.h>
+#import <SCFoundation/SCLog.h>
+
+#import <Foundation/Foundation.h>
+
+#define SCLogCaptureStateMachineInfo(fmt, ...) SCLogCoreCameraInfo(@"[SCCaptureStateMachine] " fmt, ##__VA_ARGS__)
+#define SCLogCaptureStateMachineError(fmt, ...) SCLogCoreCameraError(@"[SCCaptureStateMachine] " fmt, ##__VA_ARGS__)
+
+typedef NSNumber SCCaptureStateKey;
+
+typedef NS_ENUM(NSUInteger, SCCaptureStateMachineStateId) {
+    SCCaptureBaseStateId = 0,
+    SCCaptureUninitializedStateId,
+    SCCaptureInitializedStateId,
+    SCCaptureImageStateId,
+    SCCaptureImageWhileRecordingStateId,
+    SCCaptureRunningStateId,
+    SCCaptureRecordingStateId,
+    SCCaptureScanningStateId,
+    SCCaptureStateMachineStateIdCount
+};
+
+SC_EXTERN_C_BEGIN
+
+NSString *SCCaptureStateName(SCCaptureStateMachineStateId stateId);
+
+SC_EXTERN_C_END
diff --git a/ManagedCapturer/StateMachine/SCCaptureStateUtil.m b/ManagedCapturer/StateMachine/SCCaptureStateUtil.m
new file mode 100644
index 0000000..deb20a7
--- /dev/null
+++ b/ManagedCapturer/StateMachine/SCCaptureStateUtil.m
@@ -0,0 +1,38 @@
+//
+//  SCCaptureStateUtil.m
+//  Snapchat
+//
+//  Created by Lin Jia on 10/27/17.
+//
+//
+
+#import "SCCaptureStateUtil.h"
+
+#import <SCFoundation/SCAppEnvironment.h>
+#import <SCFoundation/SCAssertWrapper.h>
+
+NSString *SCCaptureStateName(SCCaptureStateMachineStateId stateId)
+{
+    switch (stateId) {
+    case SCCaptureBaseStateId:
+        return @"SCCaptureBaseStateId";
+    case SCCaptureUninitializedStateId:
+        return @"SCCaptureUninitializedStateId";
+    case SCCaptureInitializedStateId:
+        return @"SCCaptureInitializedStateId";
+    case SCCaptureImageStateId:
+        return @"SCCaptureImageStateId";
+    case SCCaptureImageWhileRecordingStateId:
+        return @"SCCaptureImageWhileRecordingStateId";
+    case SCCaptureRunningStateId:
+        return @"SCCaptureRunningStateId";
+    case SCCaptureRecordingStateId:
+        return @"SCCaptureRecordingStateId";
+    case SCCaptureScanningStateId:
+        return @"SCCaptureScanningStateId";
+    default:
+        SCCAssert(NO, @"illegate state id");
+        break;
+    }
+    return @"SCIllegalStateId";
+}
diff --git a/ManagedCapturer/StateMachine/SCManagedCapturerLogging.h b/ManagedCapturer/StateMachine/SCManagedCapturerLogging.h
new file mode 100644
index 0000000..069b438
--- /dev/null
+++ b/ManagedCapturer/StateMachine/SCManagedCapturerLogging.h
@@ -0,0 +1,12 @@
+//
+//  SCManagedCapturerLogging.h
+//  Snapchat
+//
+//  Created by Lin Jia on 11/13/17.
+//
+
+#import <SCFoundation/SCLog.h>
+
+#define SCLogCapturerInfo(fmt, ...) SCLogCoreCameraInfo(@"[SCManagedCapturer] " fmt, ##__VA_ARGS__)
+#define SCLogCapturerWarning(fmt, ...) SCLogCoreCameraWarning(@"[SCManagedCapturer] " fmt, ##__VA_ARGS__)
+#define SCLogCapturerError(fmt, ...) SCLogCoreCameraError(@"[SCManagedCapturer] " fmt, ##__VA_ARGS__)
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureImageState.h b/ManagedCapturer/StateMachine/States/SCCaptureImageState.h
new file mode 100644
index 0000000..561b43f
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureImageState.h
@@ -0,0 +1,22 @@
+//
+//  SCCaptureImageState.h
+//  Snapchat
+//
+//  Created by Lin Jia on 1/8/18.
+//
+
+#import "SCCaptureBaseState.h"
+
+#import <Foundation/Foundation.h>
+
+@class SCQueuePerformer;
+
+@interface SCCaptureImageState : SCCaptureBaseState
+
+SC_INIT_AND_NEW_UNAVAILABLE
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate;
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureImageState.m b/ManagedCapturer/StateMachine/States/SCCaptureImageState.m
new file mode 100644
index 0000000..d26a0f4
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureImageState.m
@@ -0,0 +1,65 @@
+//
+//  SCCaptureImageState.m
+//  Snapchat
+//
+//  Created by Lin Jia on 1/8/18.
+//
+
+#import "SCCaptureImageState.h"
+
+#import "SCCaptureImageStateTransitionPayload.h"
+#import "SCManagedCapturerV1_Private.h"
+#import "SCStateTransitionPayload.h"
+
+#import <SCFoundation/SCAssertWrapper.h>
+#import <SCFoundation/SCQueuePerformer.h>
+
+@interface SCCaptureImageState () {
+    __weak id<SCCaptureStateDelegate> _delegate;
+    SCQueuePerformer *_performer;
+}
+@end
+
+@implementation SCCaptureImageState
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate
+{
+    self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate];
+    if (self) {
+        _delegate = delegate;
+        _performer = performer;
+    }
+    return self;
+}
+
+- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload
+                     resource:(SCCaptureResource *)resource
+                      context:(NSString *)context
+{
+    SCAssertPerformer(_performer);
+    SCAssert(payload.toState == [self stateId], @"");
+    if (![payload isKindOfClass:[SCCaptureImageStateTransitionPayload class]]) {
+        SCAssertFail(@"wrong payload pass in");
+        [_delegate currentState:self requestToTransferToNewState:payload.fromState payload:nil context:context];
+        return;
+    }
+    SCCaptureImageStateTransitionPayload *captureImagePayload = (SCCaptureImageStateTransitionPayload *)payload;
+
+    [SCCaptureWorker
+        captureStillImageWithCaptureResource:resource
+                                 aspectRatio:captureImagePayload.aspectRatio
+                            captureSessionID:captureImagePayload.captureSessionID
+                      shouldCaptureFromVideo:[SCCaptureWorker shouldCaptureImageFromVideoWithResource:resource]
+                           completionHandler:captureImagePayload.block
+                                     context:context];
+
+    [_delegate currentState:self requestToTransferToNewState:SCCaptureRunningStateId payload:nil context:context];
+}
+
+- (SCCaptureStateMachineStateId)stateId
+{
+    return SCCaptureImageStateId;
+}
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureImageStateTransitionPayload.h b/ManagedCapturer/StateMachine/States/SCCaptureImageStateTransitionPayload.h
new file mode 100644
index 0000000..ea82816
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureImageStateTransitionPayload.h
@@ -0,0 +1,29 @@
+//
+//  SCCaptureImageStateTransitionPayload.h
+//  Snapchat
+//
+//  Created by Lin Jia on 1/9/18.
+//
+
+#import "SCCaptureCommon.h"
+#import "SCStateTransitionPayload.h"
+
+#import <Foundation/Foundation.h>
+
+@interface SCCaptureImageStateTransitionPayload : SCStateTransitionPayload
+
+@property (nonatomic, readonly, strong) NSString *captureSessionID;
+
+@property (nonatomic, readonly, copy) sc_managed_capturer_capture_still_image_completion_handler_t block;
+
+@property (nonatomic, readonly, assign) CGFloat aspectRatio;
+
+SC_INIT_AND_NEW_UNAVAILABLE
+
+- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState
+                          toState:(SCCaptureStateMachineStateId)toState
+                 captureSessionId:(NSString *)captureSessionID
+                      aspectRatio:(CGFloat)aspectRatio
+                completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)block;
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureImageStateTransitionPayload.m b/ManagedCapturer/StateMachine/States/SCCaptureImageStateTransitionPayload.m
new file mode 100644
index 0000000..45ba345
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureImageStateTransitionPayload.m
@@ -0,0 +1,27 @@
+//
+//  SCCaptureImageStateTransitionPayload.m
+//  Snapchat
+//
+//  Created by Lin Jia on 1/9/18.
+//
+
+#import "SCCaptureImageStateTransitionPayload.h"
+
+@implementation SCCaptureImageStateTransitionPayload
+
+- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState
+                          toState:(SCCaptureStateMachineStateId)toState
+                 captureSessionId:(NSString *)captureSessionID
+                      aspectRatio:(CGFloat)aspectRatio
+                completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)block
+{
+    self = [super initWithFromState:fromState toState:toState];
+    if (self) {
+        _captureSessionID = captureSessionID;
+        _aspectRatio = aspectRatio;
+        _block = block;
+    }
+    return self;
+}
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingState.h b/ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingState.h
new file mode 100644
index 0000000..281b0a4
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingState.h
@@ -0,0 +1,22 @@
+//
+//  SCCaptureImageWhileRecordingState.h
+//  Snapchat
+//
+//  Created by Sun Lei on 22/02/2018.
+//
+
+#import "SCCaptureBaseState.h"
+
+#import <Foundation/Foundation.h>
+
+@class SCQueuePerformer;
+
+@interface SCCaptureImageWhileRecordingState : SCCaptureBaseState
+
+SC_INIT_AND_NEW_UNAVAILABLE
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate;
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingState.m b/ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingState.m
new file mode 100644
index 0000000..eb1e4e1
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingState.m
@@ -0,0 +1,85 @@
+//
+//  SCCaptureImageWhileRecordingState.m
+//  Snapchat
+//
+//  Created by Sun Lei on 22/02/2018.
+//
+
+#import "SCCaptureImageWhileRecordingState.h"
+
+#import "SCCaptureImageWhileRecordingStateTransitionPayload.h"
+#import "SCManagedCapturerV1_Private.h"
+
+#import <SCFoundation/SCAssertWrapper.h>
+#import <SCFoundation/SCQueuePerformer.h>
+
+@interface SCCaptureImageWhileRecordingState () {
+    __weak id<SCCaptureStateDelegate> _delegate;
+    SCQueuePerformer *_performer;
+}
+@end
+
+@implementation SCCaptureImageWhileRecordingState
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate
+{
+    self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate];
+    if (self) {
+        _delegate = delegate;
+        _performer = performer;
+    }
+    return self;
+}
+
+- (SCCaptureStateMachineStateId)stateId
+{
+    return SCCaptureImageWhileRecordingStateId;
+}
+
+- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload
+                     resource:(SCCaptureResource *)resource
+                      context:(NSString *)context
+{
+    SCAssertPerformer(_performer);
+    SCAssert(payload.fromState == SCCaptureRecordingStateId, @"");
+    SCAssert(payload.toState == [self stateId], @"");
+    SCAssert([payload isKindOfClass:[SCCaptureImageWhileRecordingStateTransitionPayload class]], @"");
+    ;
+    SCCaptureImageWhileRecordingStateTransitionPayload *captureImagePayload =
+        (SCCaptureImageWhileRecordingStateTransitionPayload *)payload;
+
+    @weakify(self);
+    sc_managed_capturer_capture_still_image_completion_handler_t block =
+        ^(UIImage *fullScreenImage, NSDictionary *metadata, NSError *error, SCManagedCapturerState *state) {
+            captureImagePayload.block(fullScreenImage, metadata, error, state);
+            [_performer perform:^{
+                @strongify(self);
+                [self _cancelRecordingWithContext:context resource:resource];
+            }];
+        };
+
+    [SCCaptureWorker
+        captureStillImageWithCaptureResource:resource
+                                 aspectRatio:captureImagePayload.aspectRatio
+                            captureSessionID:captureImagePayload.captureSessionID
+                      shouldCaptureFromVideo:[SCCaptureWorker shouldCaptureImageFromVideoWithResource:resource]
+                           completionHandler:block
+                                     context:context];
+
+    [_delegate currentState:self requestToTransferToNewState:SCCaptureRunningStateId payload:nil context:context];
+}
+
+- (void)_cancelRecordingWithContext:(NSString *)context resource:(SCCaptureResource *)resource
+{
+    SCTraceODPCompatibleStart(2);
+    SCAssertPerformer(_performer);
+
+    [SCCaptureWorker cancelRecordingWithCaptureResource:resource];
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingStateTransitionPayload.h b/ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingStateTransitionPayload.h
new file mode 100644
index 0000000..7079a10
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingStateTransitionPayload.h
@@ -0,0 +1,29 @@
+//
+//  SCCaptureImageWhileRecordingStateTransitionPayload.h
+//  Snapchat
+//
+//  Created by Sun Lei on 22/02/2018.
+//
+
+#import "SCCaptureCommon.h"
+#import "SCStateTransitionPayload.h"
+
+#import <Foundation/Foundation.h>
+
+@interface SCCaptureImageWhileRecordingStateTransitionPayload : SCStateTransitionPayload
+
+@property (nonatomic, readonly, strong) NSString *captureSessionID;
+
+@property (nonatomic, readonly, copy) sc_managed_capturer_capture_still_image_completion_handler_t block;
+
+@property (nonatomic, readonly, assign) CGFloat aspectRatio;
+
+SC_INIT_AND_NEW_UNAVAILABLE
+
+- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState
+                          toState:(SCCaptureStateMachineStateId)toState
+                 captureSessionId:(NSString *)captureSessionID
+                      aspectRatio:(CGFloat)aspectRatio
+                completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)block;
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingStateTransitionPayload.m b/ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingStateTransitionPayload.m
new file mode 100644
index 0000000..ae4f271
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingStateTransitionPayload.m
@@ -0,0 +1,27 @@
+//
+//  SCCaptureImageWhileRecordingStateTransitionPayload.m
+//  Snapchat
+//
+//  Created by Sun Lei on 22/02/2018.
+//
+
+#import "SCCaptureImageWhileRecordingStateTransitionPayload.h"
+
+@implementation SCCaptureImageWhileRecordingStateTransitionPayload
+
+- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState
+                          toState:(SCCaptureStateMachineStateId)toState
+                 captureSessionId:(NSString *)captureSessionID
+                      aspectRatio:(CGFloat)aspectRatio
+                completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)block
+{
+    self = [super initWithFromState:fromState toState:toState];
+    if (self) {
+        _captureSessionID = captureSessionID;
+        _aspectRatio = aspectRatio;
+        _block = block;
+    }
+    return self;
+}
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureInitializedState.h b/ManagedCapturer/StateMachine/States/SCCaptureInitializedState.h
new file mode 100644
index 0000000..5d5876c
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureInitializedState.h
@@ -0,0 +1,22 @@
+//
+//  SCCaptureInitializedState.h
+//  Snapchat
+//
+//  Created by Jingtian Yang on 20/12/2017.
+//
+
+#import "SCCaptureBaseState.h"
+
+#import <Foundation/Foundation.h>
+
+@class SCQueuePerformer;
+
+@interface SCCaptureInitializedState : SCCaptureBaseState
+
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate;
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureInitializedState.m b/ManagedCapturer/StateMachine/States/SCCaptureInitializedState.m
new file mode 100644
index 0000000..7a687a6
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureInitializedState.m
@@ -0,0 +1,68 @@
+//
+//  SCCaptureInitializedState.m
+//  Snapchat
+//
+//  Created by Jingtian Yang on 20/12/2017.
+//
+
+#import "SCCaptureInitializedState.h"
+
+#import "SCCapturerToken.h"
+#import "SCManagedCapturerLogging.h"
+#import "SCManagedCapturerV1_Private.h"
+
+#import <SCFoundation/SCAssertWrapper.h>
+#import <SCFoundation/SCQueuePerformer.h>
+
+@interface SCCaptureInitializedState () {
+    __weak id<SCCaptureStateDelegate> _delegate;
+    SCQueuePerformer *_performer;
+}
+
+@end
+
+@implementation SCCaptureInitializedState
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate
+{
+    self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate];
+    if (self) {
+        _delegate = delegate;
+        _performer = performer;
+    }
+    return self;
+}
+
+- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload
+                     resource:(SCCaptureResource *)resource
+                      context:(NSString *)context
+{
+    // No op.
+}
+
+- (SCCaptureStateMachineStateId)stateId
+{
+    return SCCaptureInitializedStateId;
+}
+
+- (void)startRunningWithCapturerToken:(SCCapturerToken *)token
+                             resource:(SCCaptureResource *)resource
+                    completionHandler:(dispatch_block_t)completionHandler
+                              context:(NSString *)context
+{
+    SCAssertPerformer(_performer);
+    SCTraceODPCompatibleStart(2);
+    SCLogCapturerInfo(@"startRunningAsynchronouslyWithCompletionHandler called. token: %@", token);
+
+    [SCCaptureWorker startRunningWithCaptureResource:resource token:token completionHandler:completionHandler];
+
+    [_delegate currentState:self requestToTransferToNewState:SCCaptureRunningStateId payload:nil context:context];
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureRecordingState.h b/ManagedCapturer/StateMachine/States/SCCaptureRecordingState.h
new file mode 100644
index 0000000..a6bbbf0
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureRecordingState.h
@@ -0,0 +1,22 @@
+//
+//  SCCaptureRecordingState.h
+//  Snapchat
+//
+//  Created by Jingtian Yang on 12/01/2018.
+//
+
+#import "SCCaptureBaseState.h"
+
+#import <Foundation/Foundation.h>
+
+@class SCQueuePerformer;
+
+@interface SCCaptureRecordingState : SCCaptureBaseState
+
+SC_INIT_AND_NEW_UNAVAILABLE
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate;
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureRecordingState.m b/ManagedCapturer/StateMachine/States/SCCaptureRecordingState.m
new file mode 100644
index 0000000..fb7513c
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureRecordingState.m
@@ -0,0 +1,114 @@
+//
+//  SCCaptureRecordingState.m
+//  Snapchat
+//
+//  Created by Jingtian Yang on 12/01/2018.
+//
+
+#import "SCCaptureRecordingState.h"
+
+#import "SCCaptureImageWhileRecordingStateTransitionPayload.h"
+#import "SCCaptureRecordingStateTransitionPayload.h"
+#import "SCManagedCapturerV1_Private.h"
+#import "SCStateTransitionPayload.h"
+
+#import <SCFoundation/SCAssertWrapper.h>
+#import <SCFoundation/SCQueuePerformer.h>
+
+@interface SCCaptureRecordingState () {
+    __weak id<SCCaptureStateDelegate> _delegate;
+    SCQueuePerformer *_performer;
+}
+@end
+
+@implementation SCCaptureRecordingState
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate
+{
+    self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate];
+    if (self) {
+        _delegate = delegate;
+        _performer = performer;
+    }
+    return self;
+}
+
+- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload
+                     resource:(SCCaptureResource *)resource
+                      context:(NSString *)context
+{
+    SCAssertPerformer(resource.queuePerformer);
+    SCAssert(payload.toState == [self stateId], @"");
+    if (![payload isKindOfClass:[SCCaptureRecordingStateTransitionPayload class]]) {
+        SCAssertFail(@"wrong payload pass in");
+        [_delegate currentState:self requestToTransferToNewState:payload.fromState payload:nil context:context];
+        return;
+    }
+
+    SCCaptureRecordingStateTransitionPayload *recordingPayload = (SCCaptureRecordingStateTransitionPayload *)payload;
+    [SCCaptureWorker startRecordingWithCaptureResource:resource
+                                        outputSettings:recordingPayload.outputSettings
+                                    audioConfiguration:recordingPayload.configuration
+                                           maxDuration:recordingPayload.maxDuration
+                                               fileURL:recordingPayload.fileURL
+                                      captureSessionID:recordingPayload.captureSessionID
+                                     completionHandler:recordingPayload.block];
+}
+
+- (void)stopRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context
+{
+    SCTraceODPCompatibleStart(2);
+    SCAssertPerformer(_performer);
+
+    [SCCaptureWorker stopRecordingWithCaptureResource:resource];
+    [_delegate currentState:self requestToTransferToNewState:SCCaptureRunningStateId payload:nil context:context];
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+- (void)cancelRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context
+{
+    SCTraceODPCompatibleStart(2);
+    SCAssertPerformer(_performer);
+
+    [SCCaptureWorker cancelRecordingWithCaptureResource:resource];
+    [_delegate currentState:self requestToTransferToNewState:SCCaptureRunningStateId payload:nil context:context];
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+- (SCCaptureStateMachineStateId)stateId
+{
+    return SCCaptureRecordingStateId;
+}
+
+- (void)captureStillImageWithResource:(SCCaptureResource *)resource
+                          aspectRatio:(CGFloat)aspectRatio
+                     captureSessionID:(NSString *)captureSessionID
+                    completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler
+                              context:(NSString *)context
+{
+    SCAssertPerformer(_performer);
+    SCCaptureImageWhileRecordingStateTransitionPayload *payload = [
+        [SCCaptureImageWhileRecordingStateTransitionPayload alloc] initWithFromState:SCCaptureRecordingStateId
+                                                                             toState:SCCaptureImageWhileRecordingStateId
+                                                                    captureSessionId:captureSessionID
+                                                                         aspectRatio:aspectRatio
+                                                                   completionHandler:completionHandler];
+    [_delegate currentState:self
+        requestToTransferToNewState:SCCaptureImageWhileRecordingStateId
+                            payload:payload
+                            context:context];
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureRecordingStateTransitionPayload.h b/ManagedCapturer/StateMachine/States/SCCaptureRecordingStateTransitionPayload.h
new file mode 100644
index 0000000..4995daa
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureRecordingStateTransitionPayload.h
@@ -0,0 +1,41 @@
+//
+//  SCCaptureRecordingStateTransitionPayload.h
+//  Snapchat
+//
+//  Created by Jingtian Yang on 12/01/2018.
+//
+
+#import "SCCaptureCommon.h"
+#import "SCManagedVideoCapturerOutputSettings.h"
+#import "SCStateTransitionPayload.h"
+
+#import <SCAudio/SCAudioConfiguration.h>
+
+#import <Foundation/Foundation.h>
+
+@interface SCCaptureRecordingStateTransitionPayload : SCStateTransitionPayload
+
+@property (nonatomic, readonly, strong) SCManagedVideoCapturerOutputSettings *outputSettings;
+
+@property (nonatomic, readonly, strong) SCAudioConfiguration *configuration;
+
+@property (nonatomic, readonly, assign) NSTimeInterval maxDuration;
+
+@property (nonatomic, readonly, strong) NSURL *fileURL;
+
+@property (nonatomic, readonly, strong) NSString *captureSessionID;
+
+@property (nonatomic, readonly, copy) sc_managed_capturer_start_recording_completion_handler_t block;
+
+SC_INIT_AND_NEW_UNAVAILABLE
+
+- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState
+                          toState:(SCCaptureStateMachineStateId)toState
+                   outputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings
+               audioConfiguration:(SCAudioConfiguration *)configuration
+                      maxDuration:(NSTimeInterval)maxDuration
+                          fileURL:(NSURL *)fileURL
+                 captureSessionID:(NSString *)captureSessionID
+                completionHandler:(sc_managed_capturer_start_recording_completion_handler_t)block;
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureRecordingStateTransitionPayload.m b/ManagedCapturer/StateMachine/States/SCCaptureRecordingStateTransitionPayload.m
new file mode 100644
index 0000000..167031a
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureRecordingStateTransitionPayload.m
@@ -0,0 +1,33 @@
+//
+//  SCCaptureRecordingStateTransitionPayload.m
+//  Snapchat
+//
+//  Created by Jingtian Yang on 12/01/2018.
+//
+
+#import "SCCaptureRecordingStateTransitionPayload.h"
+
+@implementation SCCaptureRecordingStateTransitionPayload
+
+- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState
+                          toState:(SCCaptureStateMachineStateId)toState
+                   outputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings
+               audioConfiguration:configuration
+                      maxDuration:(NSTimeInterval)maxDuration
+                          fileURL:(NSURL *)fileURL
+                 captureSessionID:(NSString *)captureSessionID
+                completionHandler:(sc_managed_capturer_start_recording_completion_handler_t)block
+{
+    self = [super initWithFromState:fromState toState:toState];
+    if (self) {
+        _outputSettings = outputSettings;
+        _configuration = configuration;
+        _maxDuration = maxDuration;
+        _fileURL = fileURL;
+        _captureSessionID = captureSessionID;
+        _block = block;
+    }
+    return self;
+}
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureRunningState.h b/ManagedCapturer/StateMachine/States/SCCaptureRunningState.h
new file mode 100644
index 0000000..4912a4a
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureRunningState.h
@@ -0,0 +1,22 @@
+//
+//  SCCaptureRunningState.h
+//  Snapchat
+//
+//  Created by Jingtian Yang on 08/01/2018.
+//
+
+#import "SCCaptureBaseState.h"
+
+#import <Foundation/Foundation.h>
+
+@class SCQueuePerformer;
+
+@interface SCCaptureRunningState : SCCaptureBaseState
+
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate;
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureRunningState.m b/ManagedCapturer/StateMachine/States/SCCaptureRunningState.m
new file mode 100644
index 0000000..3fd665e
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureRunningState.m
@@ -0,0 +1,176 @@
+//
+//  SCCaptureRunningState.m
+//  Snapchat
+//
+//  Created by Jingtian Yang on 08/01/2018.
+//
+
+#import "SCCaptureRunningState.h"
+
+#import "SCCaptureImageStateTransitionPayload.h"
+#import "SCCaptureRecordingStateTransitionPayload.h"
+#import "SCCaptureWorker.h"
+#import "SCManagedCapturerLogging.h"
+#import "SCManagedCapturerV1_Private.h"
+#import "SCScanConfiguration.h"
+
+#import <SCFoundation/SCAssertWrapper.h>
+#import <SCFoundation/SCQueuePerformer.h>
+#import <SCFoundation/SCTraceODPCompatible.h>
+
+@interface SCCaptureRunningState () {
+    __weak id<SCCaptureStateDelegate> _delegate;
+    SCQueuePerformer *_performer;
+}
+
+@end
+
+@implementation SCCaptureRunningState
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate
+{
+    self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate];
+    if (self) {
+        _delegate = delegate;
+        _performer = performer;
+    }
+    return self;
+}
+
+- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload
+                     resource:(SCCaptureResource *)resource
+                      context:(NSString *)context
+{
+    // No op.
+}
+
+- (void)captureStillImageWithResource:(SCCaptureResource *)resource
+                          aspectRatio:(CGFloat)aspectRatio
+                     captureSessionID:(NSString *)captureSessionID
+                    completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler
+                              context:(NSString *)context
+{
+    SCAssertPerformer(_performer);
+    SCCaptureImageStateTransitionPayload *payload =
+        [[SCCaptureImageStateTransitionPayload alloc] initWithFromState:SCCaptureRunningStateId
+                                                                toState:SCCaptureImageStateId
+                                                       captureSessionId:captureSessionID
+                                                            aspectRatio:aspectRatio
+                                                      completionHandler:completionHandler];
+    [_delegate currentState:self requestToTransferToNewState:SCCaptureImageStateId payload:payload context:context];
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+- (SCCaptureStateMachineStateId)stateId
+{
+    return SCCaptureRunningStateId;
+}
+
+- (void)startRunningWithCapturerToken:(SCCapturerToken *)token
+                             resource:(SCCaptureResource *)resource
+                    completionHandler:(dispatch_block_t)completionHandler
+                              context:(NSString *)context
+{
+    SCAssertPerformer(_performer);
+    SCTraceODPCompatibleStart(2);
+    SCLogCapturerInfo(@"startRunningAsynchronouslyWithCompletionHandler called. token: %@", token);
+    [SCCaptureWorker startRunningWithCaptureResource:resource token:token completionHandler:completionHandler];
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token
+                            resource:(SCCaptureResource *)resource
+                   completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler
+                             context:(NSString *)context
+{
+    SCTraceODPCompatibleStart(2);
+    SCAssertPerformer(_performer);
+
+    SCLogCapturerInfo(@"Stop running asynchronously. token:%@", token);
+    if ([[SCManagedCapturerV1 sharedInstance] stopRunningWithCaptureToken:token
+                                                        completionHandler:completionHandler
+                                                                  context:context]) {
+        [_delegate currentState:self
+            requestToTransferToNewState:SCCaptureInitializedStateId
+                                payload:nil
+                                context:context];
+    }
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+- (void)startScanWithScanConfiguration:(SCScanConfiguration *)configuration
+                              resource:(SCCaptureResource *)resource
+                               context:(NSString *)context
+{
+    SCTraceODPCompatibleStart(2);
+    SCLogCapturerInfo(@"Start scan on preview asynchronously. configuration:%@", configuration);
+    SCAssertPerformer(_performer);
+    [SCCaptureWorker startScanWithScanConfiguration:configuration resource:resource];
+    [_delegate currentState:self requestToTransferToNewState:SCCaptureScanningStateId payload:nil context:context];
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+- (void)prepareForRecordingWithResource:(SCCaptureResource *)resource
+                     audioConfiguration:(SCAudioConfiguration *)configuration
+                                context:(NSString *)context
+{
+    SCAssertPerformer(_performer);
+    SCTraceODPCompatibleStart(2);
+    [SCCaptureWorker prepareForRecordingWithAudioConfiguration:configuration resource:resource];
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+- (void)startRecordingWithResource:(SCCaptureResource *)resource
+                audioConfiguration:(SCAudioConfiguration *)configuration
+                    outputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings
+                       maxDuration:(NSTimeInterval)maxDuration
+                           fileURL:(NSURL *)fileURL
+                  captureSessionID:(NSString *)captureSessionID
+                 completionHandler:(sc_managed_capturer_start_recording_completion_handler_t)completionHandler
+                           context:(NSString *)context
+{
+    SCTraceODPCompatibleStart(2);
+    SCAssertPerformer(_performer);
+
+    SCCaptureRecordingStateTransitionPayload *payload =
+        [[SCCaptureRecordingStateTransitionPayload alloc] initWithFromState:SCCaptureRunningStateId
+                                                                    toState:SCCaptureRecordingStateId
+                                                             outputSettings:outputSettings
+                                                         audioConfiguration:configuration
+                                                                maxDuration:maxDuration
+                                                                    fileURL:fileURL
+                                                           captureSessionID:captureSessionID
+                                                          completionHandler:completionHandler];
+    [_delegate currentState:self requestToTransferToNewState:SCCaptureRecordingStateId payload:payload context:context];
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+- (void)cancelRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context
+{
+    // Intentionally No Op, this will be removed once CCAM-13851 gets resolved.
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureScanningState.h b/ManagedCapturer/StateMachine/States/SCCaptureScanningState.h
new file mode 100644
index 0000000..0e60f79
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureScanningState.h
@@ -0,0 +1,18 @@
+//
+//  SCCaptureScanningState.h
+//  Snapchat
+//
+//  Created by Xiaokang Liu on 09/01/2018.
+//
+
+#import "SCCaptureBaseState.h"
+
+@class SCQueuePerformer;
+
+@interface SCCaptureScanningState : SCCaptureBaseState
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate;
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureScanningState.m b/ManagedCapturer/StateMachine/States/SCCaptureScanningState.m
new file mode 100644
index 0000000..7b6f0e7
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureScanningState.m
@@ -0,0 +1,75 @@
+//
+//  SCCaptureScanningState.m
+//  Snapchat
+//
+//  Created by Xiaokang Liu on 09/01/2018.
+//
+
+#import "SCCaptureScanningState.h"
+
+#import "SCManagedCapturerLogging.h"
+#import "SCManagedCapturerV1_Private.h"
+
+#import <SCFoundation/SCAssertWrapper.h>
+#import <SCFoundation/SCQueuePerformer.h>
+#import <SCFoundation/SCTraceODPCompatible.h>
+
+@interface SCCaptureScanningState () {
+    __weak id<SCCaptureStateDelegate> _delegate;
+    SCQueuePerformer *_performer;
+}
+
+@end
+
+@implementation SCCaptureScanningState
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate
+{
+    self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate];
+    if (self) {
+        SCAssert(delegate, @"");
+        SCAssert(performer, @"");
+        SCAssert(bookKeeper, @"");
+        _delegate = delegate;
+        _performer = performer;
+    }
+    return self;
+}
+
+- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload
+                     resource:(SCCaptureResource *)resource
+                      context:(NSString *)context
+{
+    // No op.
+}
+
+- (SCCaptureStateMachineStateId)stateId
+{
+    return SCCaptureScanningStateId;
+}
+
+- (void)stopScanWithCompletionHandler:(dispatch_block_t)completionHandler
+                             resource:(SCCaptureResource *)resource
+                              context:(NSString *)context
+{
+    SCAssertPerformer(_performer);
+    SCTraceODPCompatibleStart(2);
+    SCLogCapturerInfo(@"stop scan asynchronously.");
+    [SCCaptureWorker stopScanWithCompletionHandler:completionHandler resource:resource];
+    [_delegate currentState:self requestToTransferToNewState:SCCaptureRunningStateId payload:nil context:context];
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+- (void)cancelRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context
+{
+    // Intentionally No Op, this will be removed once CCAM-13851 gets resolved.
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureUninitializedState.h b/ManagedCapturer/StateMachine/States/SCCaptureUninitializedState.h
new file mode 100644
index 0000000..0809581
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureUninitializedState.h
@@ -0,0 +1,26 @@
+//
+//  SCCaptureUninitializedState.h
+//  Snapchat
+//
+//  Created by Lin Jia on 10/19/17.
+//
+//
+
+#import "SCCaptureBaseState.h"
+
+#import <Foundation/Foundation.h>
+
+/*
+ State which handles capture initialialization, which should be used only once for every app life span.
+*/
+@class SCQueuePerformer;
+
+@interface SCCaptureUninitializedState : SCCaptureBaseState
+
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate;
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCCaptureUninitializedState.m b/ManagedCapturer/StateMachine/States/SCCaptureUninitializedState.m
new file mode 100644
index 0000000..ffe99bf
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCCaptureUninitializedState.m
@@ -0,0 +1,70 @@
+//
+//  SCCaptureUninitializedState.m
+//  Snapchat
+//
+//  Created by Lin Jia on 10/19/17.
+//
+//
+
+#import "SCCaptureUninitializedState.h"
+
+#import "SCManagedCapturerLogging.h"
+#import "SCManagedCapturerV1_Private.h"
+
+#import <SCFoundation/SCAssertWrapper.h>
+#import <SCFoundation/SCQueuePerformer.h>
+#import <SCFoundation/SCTraceODPCompatible.h>
+
+@interface SCCaptureUninitializedState () {
+    __weak id<SCCaptureStateDelegate> _delegate;
+    SCQueuePerformer *_performer;
+}
+
+@end
+
+@implementation SCCaptureUninitializedState
+
+- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
+                       bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper
+                         delegate:(id<SCCaptureStateDelegate>)delegate
+{
+    self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate];
+    if (self) {
+        _delegate = delegate;
+        _performer = performer;
+    }
+    return self;
+}
+
+- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload
+                     resource:(SCCaptureResource *)resource
+                      context:(NSString *)context
+{
+    // No op.
+}
+
+- (SCCaptureStateMachineStateId)stateId
+{
+    return SCCaptureUninitializedStateId;
+}
+
+- (void)initializeCaptureWithDevicePosition:(SCManagedCaptureDevicePosition)devicePosition
+                                   resource:(SCCaptureResource *)resource
+                          completionHandler:(dispatch_block_t)completionHandler
+                                    context:(NSString *)context
+{
+    SCAssertPerformer(_performer);
+    SCTraceODPCompatibleStart(2);
+    SCLogCapturerInfo(@"Setting up with devicePosition:%lu", (unsigned long)devicePosition);
+
+    // TODO: we need to push completionHandler to a payload and let intializedState handle.
+    [[SCManagedCapturerV1 sharedInstance] setupWithDevicePosition:devicePosition completionHandler:completionHandler];
+
+    [_delegate currentState:self requestToTransferToNewState:SCCaptureInitializedStateId payload:nil context:context];
+
+    NSString *apiName =
+        [NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)];
+    [self.bookKeeper logAPICalled:apiName context:context];
+}
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCStateTransitionPayload.h b/ManagedCapturer/StateMachine/States/SCStateTransitionPayload.h
new file mode 100644
index 0000000..8fca174
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCStateTransitionPayload.h
@@ -0,0 +1,22 @@
+//
+//  SCStateTransitionPayload.h
+//  Snapchat
+//
+//  Created by Lin Jia on 1/8/18.
+//
+
+#import "SCCaptureStateUtil.h"
+
+#import <Foundation/Foundation.h>
+
+@interface SCStateTransitionPayload : NSObject
+
+@property (nonatomic, readonly, assign) SCCaptureStateMachineStateId fromState;
+
+@property (nonatomic, readonly, assign) SCCaptureStateMachineStateId toState;
+
+SC_INIT_AND_NEW_UNAVAILABLE
+
+- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState toState:(SCCaptureStateMachineStateId)toState;
+
+@end
diff --git a/ManagedCapturer/StateMachine/States/SCStateTransitionPayload.m b/ManagedCapturer/StateMachine/States/SCStateTransitionPayload.m
new file mode 100644
index 0000000..d4df2bd
--- /dev/null
+++ b/ManagedCapturer/StateMachine/States/SCStateTransitionPayload.m
@@ -0,0 +1,27 @@
+//
+//  SCStateTransitionPayload.m
+//  Snapchat
+//
+//  Created by Lin Jia on 1/8/18.
+//
+
+#import "SCStateTransitionPayload.h"
+
+#import <SCFoundation/SCAssertWrapper.h>
+
+@implementation SCStateTransitionPayload
+
+- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState toState:(SCCaptureStateMachineStateId)toState
+{
+    self = [super init];
+    if (self) {
+        SCAssert(fromState != toState, @"");
+        SCAssert(fromState > SCCaptureBaseStateId && fromState < SCCaptureStateMachineStateIdCount, @"");
+        SCAssert(toState > SCCaptureBaseStateId && toState < SCCaptureStateMachineStateIdCount, @"");
+        _fromState = fromState;
+        _toState = toState;
+    }
+    return self;
+}
+
+@end
diff --git a/ManagedCapturer/UIScreen+Debug.h b/ManagedCapturer/UIScreen+Debug.h
new file mode 100644
index 0000000..58d54a1
--- /dev/null
+++ b/ManagedCapturer/UIScreen+Debug.h
@@ -0,0 +1,13 @@
+//
+//  UIScreen+Debug.h
+//  Snapchat
+//
+//  Created by Derek Peirce on 6/1/17.
+//  Copyright © 2017 Snapchat, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface UIScreen (Debug)
+
+@end
diff --git a/ManagedCapturer/UIScreen+Debug.m b/ManagedCapturer/UIScreen+Debug.m
new file mode 100644
index 0000000..26a121c
--- /dev/null
+++ b/ManagedCapturer/UIScreen+Debug.m
@@ -0,0 +1,28 @@
+
+#import "UIScreen+Debug.h"
+
+#import <SCFoundation/SCAppEnvironment.h>
+#import <SCFoundation/SCLog.h>
+
+#import <objc/runtime.h>
+
+@implementation UIScreen (Debug)
++ (void)load
+{
+    if (SCIsPerformanceLoggingEnabled()) {
+        static dispatch_once_t once_token;
+        dispatch_once(&once_token, ^{
+            SEL setBrightnessSelector = @selector(setBrightness:);
+            SEL setBrightnessLoggerSelector = @selector(logged_setBrightness:);
+            Method originalMethod = class_getInstanceMethod(self, setBrightnessSelector);
+            Method extendedMethod = class_getInstanceMethod(self, setBrightnessLoggerSelector);
+            method_exchangeImplementations(originalMethod, extendedMethod);
+        });
+    }
+}
+- (void)logged_setBrightness:(CGFloat)brightness
+{
+    SCLogGeneralInfo(@"Setting brightness from %f to %f", self.brightness, brightness);
+    [self logged_setBrightness:brightness];
+}
+@end