diff --git a/src/Ryujinx.Horizon/Am/Ipc/AppletFifo.cs b/src/Ryujinx.Horizon/Am/Ipc/AppletFifo.cs
new file mode 100644
index 000000000..57388c68f
--- /dev/null
+++ b/src/Ryujinx.Horizon/Am/Ipc/AppletFifo.cs
@@ -0,0 +1,121 @@
+using Ryujinx.Horizon.Sdk.Am;
+using System;
+using System.Collections;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+
+namespace Ryujinx.Horizon.Am.Ipc
+{
+    internal class AppletFifo<T> : IAppletFifo<T>
+    {
+        private readonly ConcurrentQueue<T> _dataQueue;
+
+        public event EventHandler DataAvailable;
+
+        public bool IsSynchronized
+        {
+            get { return ((ICollection)_dataQueue).IsSynchronized; }
+        }
+
+        public object SyncRoot
+        {
+            get { return ((ICollection)_dataQueue).SyncRoot; }
+        }
+
+        public int Count
+        {
+            get { return _dataQueue.Count; }
+        }
+
+        public AppletFifo()
+        {
+            _dataQueue = new ConcurrentQueue<T>();
+        }
+
+        public void Push(T item)
+        {
+            _dataQueue.Enqueue(item);
+
+            DataAvailable?.Invoke(this, null);
+        }
+
+        public bool TryAdd(T item)
+        {
+            try
+            {
+                this.Push(item);
+
+                return true;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+
+        public T Pop()
+        {
+            if (_dataQueue.TryDequeue(out T result))
+            {
+                return result;
+            }
+
+            throw new InvalidOperationException("FIFO empty.");
+        }
+
+        public bool TryPop(out T result)
+        {
+            return _dataQueue.TryDequeue(out result);
+        }
+
+        public bool TryTake(out T item)
+        {
+            return this.TryPop(out item);
+        }
+
+        public T Peek()
+        {
+            if (_dataQueue.TryPeek(out T result))
+            {
+                return result;
+            }
+
+            throw new InvalidOperationException("FIFO empty.");
+        }
+
+        public bool TryPeek(out T result)
+        {
+            return _dataQueue.TryPeek(out result);
+        }
+
+        public void Clear()
+        {
+            _dataQueue.Clear();
+        }
+
+        public T[] ToArray()
+        {
+            return _dataQueue.ToArray();
+        }
+
+        public void CopyTo(T[] array, int arrayIndex)
+        {
+            _dataQueue.CopyTo(array, arrayIndex);
+        }
+
+        public void CopyTo(Array array, int index)
+        {
+            this.CopyTo((T[])array, index);
+        }
+
+        public IEnumerator<T> GetEnumerator()
+        {
+            return _dataQueue.GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return _dataQueue.GetEnumerator();
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/Am/Ipc/Controllers/LibraryAppletAccessor.cs b/src/Ryujinx.Horizon/Am/Ipc/Controllers/LibraryAppletAccessor.cs
new file mode 100644
index 000000000..989f6eca0
--- /dev/null
+++ b/src/Ryujinx.Horizon/Am/Ipc/Controllers/LibraryAppletAccessor.cs
@@ -0,0 +1,204 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Am;
+using Ryujinx.Horizon.Sdk.Am.Controllers;
+using Ryujinx.Horizon.Sdk.Am.Storage;
+using Ryujinx.Horizon.Sdk.OsTypes;
+using Ryujinx.Horizon.Sdk.Sf;
+using System;
+
+namespace Ryujinx.Horizon.Am.Ipc.Controllers
+{
+    partial class LibraryAppletAccessor : ILibraryAppletAccessor
+    {
+        private readonly AppletSession _normalSession;
+        private readonly AppletSession _interactiveSession;
+
+        private SystemEventType _stateChangedEvent;
+        private SystemEventType _normalOutDataEvent;
+        private SystemEventType _interactiveOutDataEvent;
+
+        public LibraryAppletAccessor(AppletId appletId)
+        {
+            Os.CreateSystemEvent(out _stateChangedEvent, EventClearMode.ManualClear, interProcess: true).AbortOnFailure();
+            Os.CreateSystemEvent(out _normalOutDataEvent, EventClearMode.ManualClear, interProcess: true).AbortOnFailure();
+            Os.CreateSystemEvent(out _interactiveOutDataEvent, EventClearMode.ManualClear, interProcess: true).AbortOnFailure();
+
+            _normalSession = new AppletSession();
+            _interactiveSession = new AppletSession();
+
+            _normalSession.DataAvailable += OnNormalOutData;
+            _interactiveSession.DataAvailable += OnInteractiveOutData;
+        }
+
+        private void OnAppletStateChanged(object sender, EventArgs e)
+        {
+            Os.SignalSystemEvent(ref _stateChangedEvent);
+        }
+
+        private void OnNormalOutData(object sender, EventArgs e)
+        {
+            Os.SignalSystemEvent(ref _normalOutDataEvent);
+        }
+
+        private void OnInteractiveOutData(object sender, EventArgs e)
+        {
+            Os.SignalSystemEvent(ref _interactiveOutDataEvent);
+        }
+
+        [CmifCommand(0)]
+        public Result GetAppletStateChangedEvent([CopyHandle] out int arg0)
+        {
+            arg0 = Os.GetReadableHandleOfSystemEvent(ref _stateChangedEvent);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(1)]
+        public Result IsCompleted(out bool arg0)
+        {
+            arg0 = false;
+            Logger.Stub?.PrintStub(LogClass.ServiceAm);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(10)]
+        public Result Start()
+        {
+            Logger.Stub?.PrintStub(LogClass.ServiceAm);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(20)]
+        public Result RequestExit()
+        {
+            Os.SignalSystemEvent(ref _stateChangedEvent);
+
+            Logger.Stub?.PrintStub(LogClass.ServiceAm);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(25)]
+        public Result Terminate()
+        {
+            throw new System.NotImplementedException();
+        }
+
+        [CmifCommand(30)]
+        public Result GetResult()
+        {
+            throw new System.NotImplementedException();
+        }
+
+        [CmifCommand(50)]
+        public Result SetOutOfFocusApplicationSuspendingEnabled(bool arg0)
+        {
+            throw new System.NotImplementedException();
+        }
+
+        [CmifCommand(60)]
+        public Result PresetLibraryAppletGpuTimeSliceZero()
+        {
+            // NOTE: This call reset two internal fields to 0 and one internal field to "true".
+            //       It seems to be used only with software keyboard inline.
+
+            Logger.Stub?.PrintStub(LogClass.ServiceAm);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(100)]
+        public Result PushInData(IStorage data)
+        {
+            var storage = new Storage.Storage(data);
+            _normalSession.Push(storage.Data);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(101)]
+        public Result PopOutData(out IStorage data)
+        {
+            if (_interactiveSession.TryPop(out byte[] bytes))
+            {
+                data = new Storage.Storage(bytes);
+
+                return Result.Success;
+            }
+
+            data = new Storage.Storage([]);
+            return AmResult.NotAvailable;
+        }
+
+        [CmifCommand(102)]
+        public Result PushExtraStorage(IStorage data)
+        {
+            throw new System.NotImplementedException();
+        }
+
+        [CmifCommand(103)]
+        public Result PushInteractiveInData(IStorage data)
+        {
+            var storage = new Storage.Storage(data);
+
+            _interactiveSession.Push(storage.Data);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(104)]
+        public Result PopInteractiveOutData(out IStorage arg0)
+        {
+            throw new System.NotImplementedException();
+        }
+
+        [CmifCommand(105)]
+        public Result GetPopOutDataEvent(out int handle)
+        {
+            handle = Os.GetReadableHandleOfSystemEvent(ref _normalOutDataEvent);
+
+            Logger.Stub?.PrintStub(LogClass.ServiceAm);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(106)]
+        public Result GetPopInteractiveOutDataEvent(out int handle)
+        {
+            handle = Os.GetReadableHandleOfSystemEvent(ref _interactiveOutDataEvent);
+
+            Logger.Stub?.PrintStub(LogClass.ServiceAm);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(110)]
+        public Result NeedsToExitProcess(out bool arg0)
+        {
+            arg0 = false;
+
+            return AmResult.Stubbed;
+        }
+
+        [CmifCommand(120)]
+        public Result GetLibraryAppletInfo(out LibraryAppletInfo arg0)
+        {
+            throw new System.NotImplementedException();
+        }
+
+        [CmifCommand(150)]
+        public Result RequestForAppletToGetForeground()
+        {
+            return AmResult.Stubbed;
+        }
+
+        [CmifCommand(160)]
+        public Result GetIndirectLayerConsumerHandle(out ulong arg0, ulong arg1, ulong pid)
+        {
+            throw new System.NotImplementedException();
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/Am/Ipc/Controllers/LibraryAppletCreator.cs b/src/Ryujinx.Horizon/Am/Ipc/Controllers/LibraryAppletCreator.cs
index 80e49daf3..ca66d47af 100644
--- a/src/Ryujinx.Horizon/Am/Ipc/Controllers/LibraryAppletCreator.cs
+++ b/src/Ryujinx.Horizon/Am/Ipc/Controllers/LibraryAppletCreator.cs
@@ -1,6 +1,7 @@
 using Ryujinx.Common.Logging;
 using Ryujinx.Horizon.Common;
 using Ryujinx.Horizon.Sdk.Am.Controllers;
+using Ryujinx.Horizon.Sdk.Am.Storage;
 using Ryujinx.Horizon.Sdk.Sf;
 
 namespace Ryujinx.Horizon.Am.Ipc.Controllers
@@ -8,7 +9,7 @@ namespace Ryujinx.Horizon.Am.Ipc.Controllers
     partial class LibraryAppletCreator : ILibraryAppletCreator
     {
         [CmifCommand(0)]
-        public Result CreateLibraryApplet()
+        public Result CreateLibraryApplet(out ILibraryAppletAccessor arg0, uint arg1, uint arg2)
         {
             Logger.Stub?.PrintStub(LogClass.ServiceAm);
 
@@ -16,7 +17,7 @@ namespace Ryujinx.Horizon.Am.Ipc.Controllers
         }
 
         [CmifCommand(1)]
-        public Result TerminateAllLibraryApplets()
+        public Result TerminateAllLibraryApplets(out bool arg0)
         {
             Logger.Stub?.PrintStub(LogClass.ServiceAm);
 
@@ -32,7 +33,7 @@ namespace Ryujinx.Horizon.Am.Ipc.Controllers
         }
 
         [CmifCommand(10)]
-        public Result CreateStorage()
+        public Result CreateStorage(out IStorage arg0, long arg1)
         {
             Logger.Stub?.PrintStub(LogClass.ServiceAm);
 
@@ -40,7 +41,7 @@ namespace Ryujinx.Horizon.Am.Ipc.Controllers
         }
 
         [CmifCommand(11)]
-        public Result CreateTransferMemoryStorage()
+        public Result CreateTransferMemoryStorage(out IStorage arg0, int arg1, long arg2, bool arg3)
         {
             Logger.Stub?.PrintStub(LogClass.ServiceAm);
 
@@ -48,7 +49,7 @@ namespace Ryujinx.Horizon.Am.Ipc.Controllers
         }
 
         [CmifCommand(12)]
-        public Result CreateHandleStorage()
+        public Result CreateHandleStorage(out IStorage arg0, int arg1, long arg2)
         {
             Logger.Stub?.PrintStub(LogClass.ServiceAm);
 
diff --git a/src/Ryujinx.Horizon/Am/Ipc/Storage/Storage.cs b/src/Ryujinx.Horizon/Am/Ipc/Storage/Storage.cs
index c0fc66e7f..c69fa1e48 100644
--- a/src/Ryujinx.Horizon/Am/Ipc/Storage/Storage.cs
+++ b/src/Ryujinx.Horizon/Am/Ipc/Storage/Storage.cs
@@ -15,6 +15,12 @@ namespace Ryujinx.Horizon.Am.Ipc.Storage
             Data = data;
         }
 
+        // TODO: Something for this...
+        public Storage(IStorage handle)
+        {
+
+        }
+
         [CmifCommand(0)]
         public Result Open(out IStorageAccessor storageAccessor)
         {
diff --git a/src/Ryujinx.Horizon/Sdk/Am/AppletProcessLaunchReason.cs b/src/Ryujinx.Horizon/Sdk/Am/AppletProcessLaunchReason.cs
index 24e7ac180..b2d8c6654 100644
--- a/src/Ryujinx.Horizon/Sdk/Am/AppletProcessLaunchReason.cs
+++ b/src/Ryujinx.Horizon/Sdk/Am/AppletProcessLaunchReason.cs
@@ -5,7 +5,8 @@ namespace Ryujinx.Horizon.Sdk.Am
     [StructLayout(LayoutKind.Sequential, Size = 0x4)]
     public struct AppletProcessLaunchReason
     {
-        // TODO: Better way to rep single bit flag
-        public bool flag;
+        public byte Flag;
+        public ushort Unknown1;
+        public byte Unknown2;
     }
 }
diff --git a/src/Ryujinx.Horizon/Sdk/Am/AppletSession.cs b/src/Ryujinx.Horizon/Sdk/Am/AppletSession.cs
new file mode 100644
index 000000000..2981ac8dc
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/Am/AppletSession.cs
@@ -0,0 +1,77 @@
+using Ryujinx.Horizon.Am.Ipc;
+using System;
+
+namespace Ryujinx.Horizon.Sdk.Am
+{
+    internal class AppletSession
+    {
+        private readonly IAppletFifo<byte[]> _inputData;
+        private readonly IAppletFifo<byte[]> _outputData;
+
+        public event EventHandler DataAvailable;
+
+        public int Length
+        {
+            get { return _inputData.Count; }
+        }
+
+        public AppletSession()
+        {
+            _inputData = new AppletFifo<byte[]>();
+            _outputData = new AppletFifo<byte[]>();
+        }
+
+        public AppletSession(IAppletFifo<byte[]> inputData, IAppletFifo<byte[]> outputData)
+        {
+            _inputData = inputData;
+            _outputData = outputData;
+
+            _inputData.DataAvailable += OnDataAvailable;
+        }
+
+        private void OnDataAvailable(object sender, EventArgs e)
+        {
+            DataAvailable?.Invoke(this, null);
+        }
+
+        public void Push(byte[] item)
+        {
+            if (!this.TryPush(item))
+            {
+                // TODO(jduncanator): Throw a proper exception
+                throw new InvalidOperationException();
+            }
+        }
+
+        public bool TryPush(byte[] item)
+        {
+            return _outputData.TryAdd(item);
+        }
+
+        public byte[] Pop()
+        {
+            if (this.TryPop(out byte[] item))
+            {
+                return item;
+            }
+
+            throw new InvalidOperationException("Input data empty.");
+        }
+
+        public bool TryPop(out byte[] item)
+        {
+            return _inputData.TryTake(out item);
+        }
+
+        /// <summary>
+        /// This returns an AppletSession that can be used at the
+        /// other end of the pipe. Pushing data into this new session
+        /// will put it in the first session's input buffer, and vice
+        /// versa.
+        /// </summary>
+        public AppletSession GetConsumer()
+        {
+            return new AppletSession(this._outputData, this._inputData);
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/Sdk/Am/Controllers/ILibraryAppletAccessor.cs b/src/Ryujinx.Horizon/Sdk/Am/Controllers/ILibraryAppletAccessor.cs
new file mode 100644
index 000000000..8650629db
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/Am/Controllers/ILibraryAppletAccessor.cs
@@ -0,0 +1,22 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Am.Storage;
+
+namespace Ryujinx.Horizon.Sdk.Am.Controllers
+{
+    interface ILibraryAppletAccessor : IAppletAccessor
+    {
+        Result SetOutOfFocusApplicationSuspendingEnabled(bool arg0);
+        Result PresetLibraryAppletGpuTimeSliceZero();
+        Result PushInData(IStorage arg0);
+        Result PopOutData(out IStorage data);
+        Result PushExtraStorage(IStorage data);
+        Result PushInteractiveInData(IStorage data);
+        Result PopInteractiveOutData(out IStorage arg0);
+        Result GetPopOutDataEvent(out int handle);
+        Result GetPopInteractiveOutDataEvent(out int handle);
+        Result NeedsToExitProcess(out bool arg0);
+        Result GetLibraryAppletInfo(out LibraryAppletInfo arg0);
+        Result RequestForAppletToGetForeground();
+        Result GetIndirectLayerConsumerHandle(out ulong arg0, ulong arg1, ulong pid);
+    }
+}
diff --git a/src/Ryujinx.Horizon/Sdk/Am/Controllers/ILibraryAppletCreator.cs b/src/Ryujinx.Horizon/Sdk/Am/Controllers/ILibraryAppletCreator.cs
index 457ea7a3d..abe0f5ce6 100644
--- a/src/Ryujinx.Horizon/Sdk/Am/Controllers/ILibraryAppletCreator.cs
+++ b/src/Ryujinx.Horizon/Sdk/Am/Controllers/ILibraryAppletCreator.cs
@@ -1,15 +1,16 @@
 using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Am.Storage;
 using Ryujinx.Horizon.Sdk.Sf;
 
 namespace Ryujinx.Horizon.Sdk.Am.Controllers
 {
     interface ILibraryAppletCreator : IServiceObject
     {
-        Result CreateLibraryApplet();
+        Result CreateLibraryApplet(out ILibraryAppletAccessor arg0, uint arg1, uint arg2);
         Result TerminateAllLibraryApplets();
-        Result AreAnyLibraryAppletsLeft();
-        Result CreateStorage();
-        Result CreateTransferMemoryStorage();
-        Result CreateHandleStorage();
+        Result AreAnyLibraryAppletsLeft(out bool arg0);
+        Result CreateStorage(out IStorage arg0, long arg1);
+        Result CreateTransferMemoryStorage(out IStorage arg0, int arg1, long arg2, bool arg3);
+        Result CreateHandleStorage(out IStorage arg0, int arg1, long arg2);
     }
 }
diff --git a/src/Ryujinx.Horizon/Sdk/Am/IAppletFifo.cs b/src/Ryujinx.Horizon/Sdk/Am/IAppletFifo.cs
new file mode 100644
index 000000000..8232d7bb5
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/Am/IAppletFifo.cs
@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Concurrent;
+
+namespace Ryujinx.Horizon.Sdk.Am
+{
+    interface IAppletFifo<T> : IProducerConsumerCollection<T>
+    {
+        event EventHandler DataAvailable;
+    }
+}
diff --git a/src/Ryujinx.Horizon/Sdk/Am/LibraryAppletInfo.cs b/src/Ryujinx.Horizon/Sdk/Am/LibraryAppletInfo.cs
new file mode 100644
index 000000000..cf4b1ba5c
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/Am/LibraryAppletInfo.cs
@@ -0,0 +1,11 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Horizon.Sdk.Am
+{
+    [StructLayout(LayoutKind.Sequential, Size = 0x8)]
+    struct LibraryAppletInfo
+    {
+        public AppletId AppletId;
+        public LibraryAppletMode LibraryAppletMode;
+    }
+}