diff --git a/specs/PushNotifications/Legacy.PNG b/specs/PushNotifications/Legacy.PNG
new file mode 100644
index 0000000000..d4efdf3859
Binary files /dev/null and b/specs/PushNotifications/Legacy.PNG differ
diff --git a/specs/PushNotifications/Push.PNG b/specs/PushNotifications/Push.PNG
new file mode 100644
index 0000000000..c202997981
Binary files /dev/null and b/specs/PushNotifications/Push.PNG differ
diff --git a/specs/PushNotifications/PushNotifications-spec.md b/specs/PushNotifications/PushNotifications-spec.md
new file mode 100644
index 0000000000..ca98dac326
--- /dev/null
+++ b/specs/PushNotifications/PushNotifications-spec.md
@@ -0,0 +1,487 @@
+# Push Notifications in Project Reunion SDK
+
+# Background
+
+The Windows Push Notification Service (WNS) enables third-party developers to send toast, tile, badge,
+and raw updates from their own cloud service. This provides a mechanism to deliver new updates to your
+users in a power-efficient and dependable way. For example, the skype app service can triggering an incoming
+call toast by Pushing a Payload to the WNS service. Push is generally used for time critical
+events that require immediate user/developer action.
+
+* [The Push notification WinRT Public APIs](https://docs.microsoft.com/uwp/api/windows.networking.pushnotifications?view=winrt-19041)
+ Defines the classes that encapsulate Push notifications. (UWP and Win32)
+* [The Windows Push Notification Services (WNS) Overview](https://docs.microsoft.com/windows/uwp/design/shell/tiles-and-notifications/windows-push-notification-services--wns--overview) Defines how an app today can subscribe to Push Notifications and send a payload down to the corresponding client.
+
+The below diagram is an illustration of the overall Push Flow that we have today:
+![Legacy Flow For Project Reunion](Legacy.png)
+
+## The problems today
+
+**Portal Registration**: The entire Registration flow for Push is dependent on registering an app in the
+partner center portal by [associating an app with the Windows Store](https://docs.microsoft.com/azure/notification-hubs/notification-hubs-windows-store-dotnet-get-started-wns-push-notification). As a result, non-store provisioned
+apps cannot use this feature.
+
+**Client Registration**: Unpackaged 3rd party applications cannot register themselves as a Push target.
+The only way to get an implicit client side registration is to package the app as an MSIX Desktop Bridge
+or SSP which may not satisfy 3rd party requirements.
+
+**Push Channels**: All the channel APIs are dependent on the caller app having a store provisioned identity.
+This means that Win32 and unpackaged apps that are not store distributed cannot use our Public APIs.
+Sideloaded UWPs have exactly the same problem.
+
+**Activation**: When the OS receives a Push over the WNS socket, it signals the Background
+Infrastructure to trigger a task and run some code on the app's behalf. This flow is undefined for Win32 apps
+that need to use Push today due to the limitations described above.
+
+**Channel Request Error Handling**: The channel APIs exposed today can throw exceptions via their Projections
+Some errors can be critical errors while others are retryable errors. The msdn guidance for retrying isn't
+very clear and has been the root cause of multiple app bugs in the past. We want to abstract away the details of
+channel retry operations for developers in Project Reunion.
+
+# Description
+
+At a high level, we need to provide a way for all Win32 applications to subscribe to and receive Push notifications
+irrespective of their app type. This includes unpackaged apps and packaged win32 (MSIX Desktop Bridge, Sparse Signed Packages).
+Moreover, all Push scenarios should adhere to OS resource management policies like Power Saver, Network Attribution,
+Enterprise group policies etc. The Project Reunion Push component will abstract away the complexities of dealing with WNS
+channel registrations and Push related activations as much as possible freeing the developer to focus on other app
+related challenges.
+
+We will prioritize the following feature set for Project Reunion:
+* Supporting raw push for Packaged Win32(MSIX Desktop Bridge)
+* Supporting raw push for Unpackaged Win32.
+* Supporting Visual Cloud Toasts for packaged Win32 apps.
+* Supporting Visual Cloud Toasts for unpackaged Win32 apps.
+
+A Portal Registration flow through [AAD (Azure Active Directory)](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app) will also be defined that removes the dependency of the Push Flow with
+the Partner Center Portal. The RemoteIdentifier GUID in this spec maps to the ApplicationId(ClientId) in the AAD App
+Registration process. Below is an illustration of the proposed flow through AAD:
+![Push Flow For Project Reunion](Push.png)
+
+Phase 1 (Project Reunion Preview):
+WNS Push APIs will only support WIN32 Packaged app (MSIX Desktop Bridge).
+
+Phase 2 (Project Reunion V1):
+WNS Push APIs will support unpackaged Win32 scenarios.
+
+Link to the official Project Reunion timeline can be found [here](https://github.com/microsoft/ProjectReunion/blob/main/docs/roadmap.md).
+
+# Examples
+
+## In this scenario, the process that Registers the Push Activator and the process specified as the COM server are the same
+The code in Main would follow the pattern below:
+* The Registration will take in a CLSID of the COM server as part of the Activator setup. The registration API will simply be
+a thin wrapper around the BackgroundInfra WinRT APIs and abstract away the COM details from the developer including registration
+of the inproc Project Reunion component.
+* The app will query ApplifeCycle APIs to retrieve an ActivationKind. (Note: This is covered in a seperate AppLifeCycle API spec.)
+* If the Activation is Push, the app will QI the Arguments property to retrieve an instance of PushActivatedEventArgs and get
+the Push payload from it. Care needs to be taken to Get a Deferral and Complete the Deferral while processing the payload for
+Power Management compliance.
+* If the Activation is Foreground, the app will do 2 things:
+ * It will request a Push Channel Asynchronously with an implementation of Progress and Completed event handler.
+ * Expect Channel operations to take around 2-16 minutes in some rare cases (retryable errors). In the 90th percentile case,
+ it should be fairly quick operation (in a few seconds).
+ * It will subscribe to a In-memory Push event handler hanging off the retrieved Push Channel component.
+ * The app should ideally handle the event by setting the Handled property to true to prevent Background Activation from
+ launching a new process.
+
+```cpp
+int main()
+{
+ // Register the COM Activator GUID
+ PushNotificationActivationInfo info(
+ PushNotificationRegistrationKind::PushTrigger | PushNotificationRegistrationKind::ComActivator,
+ winrt::guid("BACCFA91-F1DE-4CA2-B80E-90BE66934EC6"));
+
+ // Registers a Push Trigger and sets up an inproc COM Server for Activations
+ auto token = PushNotificationManager::RegisterActivator(info);
+
+ // Check to see if the WinMain activation is due to a Push Activator
+ // Note: This API is currently not present in Reunion but will be included as part of the AppLifecycle Public API spec.
+ auto activation = AppLifecycle::Activation();
+ auto kind = activation.Kind();
+
+ if (kind == ReunionActivationKind::Push)
+ {
+ auto args = activation.Arguments().as();
+
+ // Call GetDeferral to ensure that code runs in low power
+ auto deferral = args.GetDeferral();
+
+ auto payload = args.Payload();
+
+ // Do stuff to process the raw payload
+
+ // Call Complete on the deferral as good practise: Needed mainly for low power usage
+ deferral.Complete();
+ }
+ else if (kind == ReunionActivationKind::Launch) // This indicates that the app is launching in the foreground
+ {
+ // Register the AAD RemoteIdentifier for the App to receive Push
+ auto channelOperation = PushNotificationManager::CreateChannelAsync(
+ winrt::guid("F80E541E-3606-48FB-935E-118A3C5F41F4"));
+
+ // Setup the inprogress event handler
+ channelOperation.Progress(
+ [](
+ IAsyncOperationWithProgress const& sender,
+ PushNotificationCreateChannelStatus const& args)
+ {
+ if (args.status == PushNotificationChannelStatus::InProgress)
+ {
+ // This is basically a noop since it isn't really an error state
+ printf("The first channel request is still in progress! \n");
+ }
+ else if (args.status == PushNotificationChannelStatus::InProgressRetry)
+ {
+ LOG_HR_MSG(
+ args.extendedError,
+ "The channel request is in back-off retry mode because of a retryable error! Expect delays in acquiring it. RetryCount = %d",
+ args.retryCount);
+ }
+ });
+
+ winrt::event_token pushToken;
+
+ // Setup the completed event handler
+ channelOperation.Completed(
+ [&](
+ IAsyncOperationWithProgress const& sender,
+ AsyncStatus const asyncStatus)
+ {
+ auto result = sender.GetResults();
+ if (result.Status() == PushNotificationChannelStatus::CompletedSuccess)
+ {
+ auto channelUri = result.Channel().Uri();
+ auto channelExpiry = result.Channel().ExpirationTime();
+
+ // Register Push Event for Foreground
+ pushToken = result.Channel().PushReceived([&](const auto&, PushNotificationReceivedEventArgs args)
+ {
+ auto payload = args.Payload();
+
+ // Do stuff to process the raw payload
+
+ // Stop the subsequent background activation from launching process again with this payload
+ args.Handled(true);
+ });
+
+ // Persist the channelUri and Expiry in the App Service for subsequent Push operations
+ }
+ else if (result.Status() == PushNotificationChannelStatus::CompletedFailure)
+ {
+ LOG_HR_MSG(result.ExtendedError(), "We hit a critical non-retryable error with channel request!");
+ }
+ });
+
+ // Draw window and other foreground UI stuff here
+
+ // Unregister the Push event for Foreground before exiting
+ auto result = channelOperation.GetResults();
+ if (result.Status() == PushNotificationChannelStatus::CompletedSuccess)
+ {
+ result.Channel().PushReceived(pushToken);
+ }
+ }
+
+ // Unregisters the inproc COM Activator before exiting
+ PushNotificationManager::UnregisterActivator(token, PushNotificationRegistrationKind::ComActivator);
+
+ return 0;
+}
+```
+
+## In this scenario, the process that Registers the Push Trigger and the process specified as the COM server are different.
+Process A (Registration of the Push Trigger only):
+```cpp
+int main()
+{
+ PushNotificationActivationInfo info(
+ PushNotificationRegistrationKind::PushTrigger,
+ winrt::guid("BACCFA91-F1DE-4CA2-B80E-90BE66934EC6"));
+
+ // Registers a Push Trigger with the Background Infra component
+ auto token = PushNotificationManager::RegisterActivator(info);
+
+ // Some app code ....
+
+ return 0;
+}
+```
+Process B (Register the inproc COM server and handle the background activation):
+```cpp
+int main()
+{
+ PushNotificationActivationInfo info(
+ PushNotificationRegistrationKind::ComActivator,
+ winrt::guid("BACCFA91-F1DE-4CA2-B80E-90BE66934EC6"));
+
+ // Registers the current process as an InProc COM server
+ auto token = PushNotificationManager::RegisterActivator(info);
+
+ // Check to see if the WinMain activation is due to a Push Activator
+ // Note: This API is currently not present in Project Reunion but will be included as part of the AppLifecycle Public API spec.
+ auto activation = AppLifecycle::Activation();
+ auto kind = activation.Kind();
+
+ if (kind == ReunionActivationKind::Push)
+ {
+ // Do Push processing stuff
+ }
+
+ // Some code ....
+
+ // Unregisters the inproc COM Activator
+ PushNotificationManager::UnregisterActivator(token, PushNotificationRegistrationKind::ComActivator);
+
+ return 0;
+}
+```
+## Registration of the Push Activator for LOW IL apps like UWP (Inproc)
+The app will simply call into the Default implementation of PushNotificationActivationInfo for the Registration Flow instead of the CLSID overload.
+```cpp
+PushNotificationActivationInfo info();
+auto token = PushNotificationChannelManager::RegisterPushNotificationActivator(info);
+```
+
+To intercept the payload, OnBackgroundActivated will have to be implemented by the app.
+```cpp
+sealed partial class App : Application
+{
+ ...
+
+ protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
+ {
+ base.OnBackgroundActivated(args);
+ IBackgroundTaskInstance taskInstance = args.TaskInstance;
+ DoYourBackgroundWork(taskInstance);
+ }
+}
+```
+
+# Remarks
+
+## Registration
+The developer should always call the Push Registration API first to register the current process as
+the COM server before retrieving the PushActivationArgs. Order matters!
+
+## Foreground API calls
+It is recommended that the developer subscribes to channel requests and Push events if the app is launched
+in foreground context. This is to ensure that subsequent Push and channel events can be handled by the
+
running process.
+
+## Seperating Activator Registration flow from Channel Request flow
+We decided to have the following 2 APIs to be seperate calls instead of a single combined API call:
+```cpp
+PushNotificationChannelManager::RegisterActivator(info)
+PushNotificationChannelManager::CreatePushChannelAsync(remoteIdentifier)
+```
+Mainly for 2 reasons:
+* The app developer is expected to Register an activator for every WinMain app launch. Combining the channel
+request API with the registration call would force the developer to keep the client channel in sync with the
+App Service more frequently (both for foreground and background launch) which can cause potential synchronization
+bugs. The preference is for developers to request new channels only on Foreground launches triggered by the user.
+* It isn't required that developers Register a Push Activator for Visual Toast operations. In the case of
+Visual Toasts, payloads are directed to the Shell and not to the App.
+
+## Handling Push ChannelFailures
+PushNotification channel request calls are expected to fail since they go over the wire. We don't want the developer
+to deal with the complexities of retryable failures and critical failures. We do however expose in-progress states and
+internal error codes to developers if they prefer to track it for debugging, settings user expectations etc.
+
+For example, we have 2 different Progress states, one indicating basic progress and another indicating retry progress.
+```cpp
+if (args.Status() == ChannelStatus::InProgress)
+{
+ // This is basically a noop since it isn't really an error state
+ printf("The first channel request is still in progress! \n");
+}
+else if (args.Status() == ChannelStatus::InProgressRetry)
+{
+ LOG_HR_MSG(args.ExtendedError(), "The channel request is in back-off retry mode because of a retryable error! Expect delays in acquiring it.");
+}
+```
+
+Similarly, operations are expected to fail or succeed on completion.
+```cpp
+if (result.Status() == ChannelStatus::CompletedSuccess)
+{
+ auto channelUri = result.Channel().Uri();
+ auto channelExpiry = result.Channel().ExpirationTime();
+
+ // Persist the channelUri and Expiry in the App Service
+}
+else if (result.Status() == ChannelStatus::CompletedFailure)
+{
+ LOG_HR_MSG(result.ExtendedError(), "We hit a critical non-retryable error with channel request!");
+}
+```
+
+## Manifest Registration
+For MSIX, the COM activator GUID and the exe path need to be registered in the manifest. The launch args would need to be pre set to a well-known string that defines Push Triggers.
+```xml
+
+
+
+
+
+
+
+
+
+```
+
+# API Details
+```c# (but really MIDL3)
+
+namespace Microsoft.Windows.PushNotifications
+{
+ // Event args for the Push payload.
+ runtimeclass PushNotificationReceivedEventArgs
+ {
+ // Initialize using the IBackgroundInstance: used specifically for the Background Activation scenario
+ static PushNotificationReceivedEventArgs CreateFromBackgroundTaskInstance(Windows.ApplicationModel.Background.IBackgroundTaskInstance backgroundTask);
+
+ // Initialize using the PushNotificationEventArgs from Windows: used specifically for in-memory event handling when app is already in foreground
+ static PushNotificationReceivedEventArgs CreateFromPushNotificationReceivedEventArgs(Windows.Networking.PushNotifications.PushNotificationReceivedEventArgs args);
+
+ // The Push payload
+ byte[] Payload { get; };
+
+ // Gets a deferral to run under specific modes like low power mode
+ Windows.ApplicationModel.Background.BackgroundTaskDeferral GetDeferral();
+
+ // Subscribe to Cancelled event handler to be signalled when resource policies are no longer true like 30s wallclock timer
+ event Windows.ApplicationModel.Background.BackgroundTaskCanceledEventHandler Canceled;
+
+ // Set to true to prevent proceeding launch due to Background Activation: false by default
+ Boolean Handled;
+ };
+
+ [flags]
+ enum PushNotificationRegistrationKind
+ {
+ PushTrigger = 0x1, // Registers a Push Trigger with Background Infrastructure
+ ComActivator = 0x2, // Registers the Project Reunion Background Task component as an InProc COM server
+ };
+
+ // An abstraction over the activation Registration flow
+ runtimeclass PushNotificationActivationInfo
+ {
+ // Initialize using a RegistrationKind and optionally defined parameters like manifest defined activatorId
+ // 1) If kind = PushTrigger is specified, only the Push Trigger will be Registered with Background Infra
+ // 2) If kind = ComActivator is specified, the Project Reunion Background Task component will be Registered as an InProc COM server
+ PushNotificationActivationInfo(PushNotificationRegistrationKind kind, Guid taskClsid);
+
+ // The CLSID associated with the Client COM server that Project Reunion will activate
+ Guid TaskClsid{ get; };
+
+ PushNotificationRegistrationKind Kind{ get; };
+
+ // The conditions under which Push Triggers would execute
+ Windows.ApplicationModel.Background.IBackgroundCondition[] GetConditions();
+ void SetConditions(Windows.ApplicationModel.Background.IBackgroundCondition[] conditions);
+ };
+
+ enum PushNotificationChannelStatus
+ {
+ InProgress, // The request is in progress and there is no retry operation
+ InProgressRetry, // The request is in progress and is in a backoff retry state. Check ExtendedError for HRESULT for retryable error.
+ CompletedSuccess, // The request completed successfully
+ CompletedFailure, // The request failed with some critical internal error. Check ExtendedError for HRESULT
+ };
+
+ // The PushNotificationChannel Progress result
+ struct PushNotificationCreateChannelStatus
+ {
+ // The last extended error we failed Channel requests on that caused the inprogress retry status. S_OK if this is just progress status.
+ HRESULT extendedError;
+
+ // Either InProgress or InProgressRetry status
+ PushNotificationChannelStatus status;
+
+ // Total Retries so far
+ UInt32 retryCount;
+ };
+
+ runtimeclass PushNotificationChannel
+ {
+ PushNotificationChannel(String uri, Windows.Foundation.DateTime expiration);
+
+ // The Channel Uri for app to Post a notification to.
+ String Uri { get; };
+
+ // Expiration of the Channel
+ Windows.Foundation.DateTime ExpirationTime { get; };
+
+ // Unsubscribes the channel
+ void Close();
+
+ // In-memory Event handler for Push Notifications
+ event Windows.Foundation.TypedEventHandler PushReceived;
+ }
+
+ runtimeclass PushNotificationCreateChannelResult
+ {
+ PushNotificationCreateChannelResult(
+ PushNotificationChannel channel,
+ HRESULT extendedError,
+ PushNotificationChannelStatus status);
+
+ // The Push channel associated with the Result. Null if InProgress or completion failed
+ PushNotificationChannel Channel { get; };
+
+ // More detailed error code in addition to the ChannelStatus state.
+ HRESULT ExtendedError{ get; };
+
+ // The Status of the ChannelComplete operation
+ PushNotificationChannelStatus Status { get; };
+ };
+
+ runtimeclass PushNotificationRegistrationToken
+ {
+ PushNotificationRegistrationToken(
+ UInt64 cookie,
+ Windows.ApplicationModel.Background.BackgroundTaskRegistration taskRegistration);
+
+ // The cookie from CoRegisterClassObject
+ UInt64 Cookie{ get; };
+
+ // The Registration token for the Push Trigger
+ Windows.ApplicationModel.Background.BackgroundTaskRegistration TaskRegistration { get; };
+ };
+
+ static runtimeclass PushNotificationManager
+ {
+ // Register an activator using an ActivationInfo context and return a RegistrationToken
+ static PushNotificationRegistrationToken RegisterActivator(PushNotificationActivationInfo details);
+
+ // Unregister any activator if present using a token and registrationKind
+ // 1) If kind = PushTrigger is specified, the trigger itself will be removed
+ // 2) If kind = ComActivator is specified, the Project Reunion Background Task component will no longer act as an InProc COM Server
+ static void UnregisterActivator(PushNotificationRegistrationToken token, PushNotificationRegistrationKind kind);
+
+ // Request a Push Channel with an encoded RemoteId from WNS. RemoteId is an AAD identifier GUID
+ static Windows.Foundation.IAsyncOperationWithProgress CreateChannelAsync(Guid remoteId);
+ };
+}
+```
+# Appendix
+* For all OS images before 21H1 (19043), the RemoteId will be a noop and the native platform will
+send the PFN (PackageFamilyName) in the channel request protocol. The WNS server will maintain an
+internal mapping of PFN -> RemoteId and will return a channelUri with the encoded RemoteId after querying
+the map.
+* For OS images 21H1(19043) and beyond, the Project Reunion component will call into newly added Closed source
+APIs in the OS to actually pass a RemoteId in the Channel Request operation.
+* For unpackaged Win32 apps, the Project Reunion component will call into Closed source APIs to register the
+unpackaged Win32 process with the RemoteId.
+* A long running Project Reunion component will be needed to intercept Push payloads from the native platform and
+Launch the corresponding unpackaged Win32 app. We will be using Protocol Activation via WinMain to launch the
+unpackaged Win32 process from the long running service. The requirement is mainly because the native client
+today does not have support to launch Win32 apps directly in response to a Push payload unless they are
+already running in which case we simply marshall the payload back to the process via a registered callback sink.
+* For packaged applications like MSIX Desktop Bridge, we buid a thin wrapper around existing Background Manager
+APIs rather than re-inventing new Background Triggers. This is mainly because Background Infrastructure native
+client stack is built with Power Management and resourcing concerns already addressed. Building a new stack that
+addresses similar concerns is likely not the best use of our time or resources. Instead we should invest in
+Reunioninzing the Background Infra stack itself.