Windows プッシュ通知サービス (WNS) の概要

Windows プッシュ通知サービス (WNS) を利用することで、サードパーティの開発者は、独自のクラウド サービスからトースト更新、タイル更新、バッジ更新、直接更新を送ることができます。 このメカニズムにより、新しい更新を電力効率に優れた信頼できる方法でユーザーに配信できます。

動作方法

次の図は、プッシュ通知を送信するための完全なデータ フローを示しています。 以下のステップが関係します。

  1. アプリがユニバーサル WNS にプッシュ通知チャネルを要求します。
  2. Windows では WNS に通知チャネルの作成を求めます。 このチャネルは、URI (Uniform Resource Identifier) の形式で呼び出し元のデバイスに返されます。
  3. 通知チャネルの URI が、WNS によってアプリに返されます。
  4. アプリは、お客様独自のクラウド サービスに URI を送信します。 その後、お客様は通知を送信するときに URI にアクセスできるように、独自のクラウド サービスに URI を格納します。 URI は、独自のアプリと独自のサービスの間のインターフェイスです。安全で安全な Web 標準を使用してこのインターフェイスを実装するのは、お客様の責任です。
  5. クラウド サービスに送信する更新プログラムがある場合、クラウド サービスはチャネル URI を使用して WNS に通知します。 これは、Secure Sockets Layer (SSL) 経由で、通知ペイロードを含む HTTP POST 要求を発行することで行われます。 このステップには認証が必要です。
  6. WNS は要求を受信し、通知を適切なデバイスにルーティングします。

wns data flow diagram for push notification

アプリを登録し、クラウド サービスの資格情報を受け取る

WNS を使って通知を送るには、こちらに説明されているように、アプリをストア ダッシュボードに登録しておく必要があります。

通知チャネルを要求する

プッシュ通知を受信できるアプリが実行される場合は、まず CreatePushNotificationChannelForApplicationAsync を介して通知チャネルを要求する必要があります。 完全な説明とコード例については、「通知チャネルを要求、作成、保存する方法」を参照してください。 この API は、呼び出し元のアプリケーションとそのタイルに一意にリンクされるチャネル URI を返し、それを通じてすべての通知の種類を送信できます。

アプリは、チャネル URI を正常に作成した後、それをクラウド サービスに、その URI に関連付ける必要があるアプリ固有のメタデータと共に送信します。

重要

  • アプリの通知チャネル URI が常に同じままであることは保証されません。 アプリが実行されるたびに新しいチャネルを要求し、そのサービスを URI が変更されたときに更新することをお勧めします。 開発者はチャネル URI を変更しないでください。また、チャネル URI をブラックボックス文字列と見なす必要があります。 現時点では、チャネル URI は 30 日後に期限切れになります。 Windows 10 アプリがバックグラウンドでチャネルを定期的に更新する場合は、Windows 8.1 のプッシュ通知と定期的な通知のサンプルをダウンロードし、そのソース コードや、それが示すパターンを再利用できます。
  • クラウド サービスとクライアント アプリの間のインターフェイスは、開発者が実装します。 アプリでは、独自のサービスを使用して認証プロセスを実行し、HTTPS などのセキュリティで保護されたプロトコル経由でデータを送信することをお勧めします。
  • クラウド サービスでは、チャネル URI がドメイン "notify.windows.com" を常に使用することが重要です。 サービスは、他のドメイン上のチャネルに通知をプッシュしないでください。 アプリのコールバックが侵害された場合、悪意のある攻撃者が WNS をスプーフィングするためにチャネル URI を送信する可能性があります。 ドメインを調べないと、そのような攻撃者に対して、気付かないうちにクラウド サービスが情報を開示してしまう可能性があります。 チャネル URI のサブドメインは変更される可能性があり、チャネル URI の検証時には考慮しないでください。
  • クラウド サービスが期限切れのチャネルに通知を配信しようとすると、WNS は応答コード 410 を返します。 そのコードに応答して、サービスはその URI に通知を送信しなくなります。

クラウド サービスを認証する

通知を送信するには、クラウド サービスを WNS 経由で認証する必要があります。 このプロセスの最初の手順は、Microsoft Store ダッシュボードにアプリを登録するときに発生します。 登録プロセス中に、アプリにはパッケージ セキュリティ識別子 (SID) とシークレット キーが与えられます。 この情報は、WNS で認証するためにクラウド サービスによって使用されます。

WNS 認証スキームは、OAuth 2.0 プロトコルのクライアント資格情報プロファイルを使用して実装されます。 クラウド サービスは、資格情報 (パッケージ SID とシークレット キー) を指定して WNS で認証します。 その代わりに、アクセス トークンを受け取ります。 このアクセス トークンにより、クラウド サービスは通知を送信できます。 トークンは、WNS に送信されるすべての通知要求で必要です。

大まかに言えば、情報チェーンは次のとおりです。

  1. クラウド サービスは、OAuth 2.0 プロトコルに従って HTTPS 経由で資格情報を WNS に送信します。 これにより、WNS でサービスが認証されます。
  2. 認証が成功した場合、WNS はアクセス トークンを返します。 このアクセス トークンは、有効期限が切れるまで、以降のすべての通知要求で使用されます。

wns diagram for cloud service authentication

WNS を使用した認証では、クラウド サービスは Secure Sockets Layer (SSL) 経由で HTTP 要求を送信します。 パラメーターは、"application/x-www-for-urlencoded" 形式で指定されます。 次の例に示すように、"client_id" フィールドにパッケージ SID を指定し、"client_secret" フィールドに秘密鍵を指定します。 構文の詳細については、「アクセス トークンの要求」のリファレンスを参照してください。

Note

これは単なる例であり、切り取って貼り付けても独自のコードで使うことはできません。 

 POST /accesstoken.srf HTTP/1.1
 Content-Type: application/x-www-form-urlencoded
 Host: https://login.live.com
 Content-Length: 211
 
 grant_type=client_credentials&client_id=ms-app%3a%2f%2fS-1-15-2-2972962901-2322836549-3722629029-1345238579-3987825745-2155616079-650196962&client_secret=Vex8L9WOFZuj95euaLrvSH7XyoDhLJc7&scope=notify.windows.com

WNS はクラウド サービスを認証し、成功した場合は "200 OK" の応答を送信します。 アクセス トークンは、"application/json" メディアの種類を使用して、HTTP 応答の本文に含まれるパラメーターで返されます。 サービスがアクセス トークンを受信したら、通知を送信する準備が整います。

次の例は、アクセス トークンを含む、成功した認証応答を示しています。 構文の詳細については、「プッシュ通知サービスの要求ヘッダーと応答ヘッダー」を参照してください。

 HTTP/1.1 200 OK   
 Cache-Control: no-store
 Content-Length: 422
 Content-Type: application/json
 
 {
     "access_token":"EgAcAQMAAAAALYAAY/c+Huwi3Fv4Ck10UrKNmtxRO6Njk2MgA=", 
     "token_type":"bearer"
 }

重要

  • この手順でサポートされている OAuth 2.0 プロトコルは、ドラフト バージョン V16 に従います。
  • OAuth Request for Comments (RFC) では、"client" という用語を使用してクラウド サービスを参照します。
  • OAuth ドラフトが完成すると、この手順が変更される可能性があります。
  • アクセス トークンは、複数の通知要求に再利用できます。 これにより、クラウド サービスは 1 回だけ認証を行い、多くの通知を送信できます。 ただし、アクセス トークンの有効期限が切れると、クラウド サービスは新しいアクセス トークンを受け取るために再び認証を行う必要があります。

通知を送信する

チャネル URI を使用すると、クラウド サービスは、ユーザーの更新があるたびに通知を送信できます。

上記のアクセス トークンは、複数の通知要求に再利用できます。クラウド サーバーは、すべての通知に対して新しいアクセス トークンを要求する必要はありません。 アクセス トークンの有効期限が切れている場合、通知要求はエラーを返します。 アクセス トークンが拒否された場合は、通知を複数回再送信しないことをお勧めします。 このエラーが発生した場合は、新しいアクセス トークンを要求し、通知を再送信する必要があります。 正確なエラー コードについては、「プッシュ通知の応答コード」を参照してください。

  1. クラウド サービスは、チャネル URI に対する HTTP POST 要求を行います。 この要求は SSL 経由で行う必要があり、必要なヘッダーと通知ペイロードが含まれています。 承認ヘッダーには、承認のために取得したアクセス トークンを含める必要があります。

    要求の例を次に示します。 構文の詳細については、「プッシュ通知の応答コード」を参照してください。

    通知ペイロードの作成の詳細については、「クイック スタート: プッシュ通知の送信」を参照してください。 タイル、トースト、バッジのプッシュ通知のペイロードは、定義されたそれぞれのアダプティブ タイル スキーマまたはレガシ タイル スキーマに準拠する XML コンテンツとして提供されます。 未加工の通知のペイロードには、指定された構造がありません。 これは厳密にアプリで定義されています。

     POST https://cloud.notify.windows.com/?token=AQE%bU%2fSjZOCvRjjpILow%3d%3d HTTP/1.1
     Content-Type: text/xml
     X-WNS-Type: wns/tile
     Authorization: Bearer EgAcAQMAAAAALYAAY/c+Huwi3Fv4Ck10UrKNmtxRO6Njk2MgA=
     Host: cloud.notify.windows.com
     Content-Length: 24
    
     <body>
     ....
    
  2. WNS が応答し、通知が受信されて次に利用可能な機会に配信されることが示されます。 ただし、WNS では、通知がデバイスまたはアプリケーションによって受信されたことをエンド ツー エンドで確認することはできません。

次の図は、データ フローを示しています。

wns diagram for sending a notification

重要

  • WNS では、通知の信頼性や待機時間は保証されません。
  • 通知には、機密データ、重要情報、個人データを含めてはなりません。
  • 通知を送信するには、クラウド サービスが最初に WNS で認証され、アクセス トークンを受け取る必要があります。
  • アクセス トークンを使用すると、クラウド サービスは、トークンが作成された 1 つのアプリにのみ通知を送信できます。 1 つのアクセス トークンを使用して、複数のアプリ間で通知を送信することはできません。 そのため、クラウド サービスが複数のアプリをサポートしている場合は、各チャネル URI に通知をプッシュするときに、アプリの正しいアクセス トークンを提供する必要があります。
  • デバイスがオフラインの場合、既定では、WNS はチャネル URI ごとに通知の種類 (タイル、バッジ、トースト) のいずれかを格納し、未加工の通知は保存しません。
  • 通知コンテンツがユーザーに合わせてカスタマイズされるシナリオでは、WNS では、受信した更新プログラムをクラウド サービスが直ちに送信することをお勧めします。 このシナリオの例としては、ソーシャル メディア フィードの更新、インスタント コミュニケーションの招待、新しいメッセージ通知、アラートなどがあります。 他にも、同じ一般的な更新プログラムがユーザーの大規模なサブセットに頻繁に配信されるシナリオも考えられます。たとえば、天気、在庫、ニュースの更新などです。 WNS ガイドラインでは、これらの更新の頻度は多くとも 30 分あたり 1 回にするように指定されています。 これより頻繁に更新があると、エンド ユーザーまたは WNS は、不正であると判断する場合があります。
  • Windows 通知プラットフォームは、ソケットをアクティブにして正常な状態に保つために、WNS との定期的なデータ接続を保持します。 通知チャネルを要求または使用するアプリケーションがない場合、ソケットは作成されません。

タイル通知とバッジ通知の有効期限

既定では、タイルとバッジの通知は、ダウンロードされてから 3 日後に有効期限が切れます。 通知の有効期限が切れると、コンテンツはタイルまたはキューから削除され、ユーザーに表示されなくなります。 タイルのコンテンツが関連しなくなっても保持されることのないように、すべてのタイルとバッジ通知に有効期限を設定することをお勧めします (アプリに理にかなった時間を使用)。 明示的な有効期限は、定義された有効期間を持つコンテンツに不可欠です。 これにより、クラウド サービスが通知の送信を停止した場合や、ユーザーがネットワークから長時間切断した場合にも、古いコンテンツが確実に削除されます。

クラウド サービスでは、X-WNS-TTL HTTP ヘッダーを設定して、通知が送信された後も有効な時間 (秒単位) を指定することで、各通知の有効期限を設定できます。 詳しくは、「プッシュ通知サービスの要求ヘッダーと応答ヘッダー」を参照してください。

たとえば、株式市場の取引が活発な日には、株価更新の有効期限を送信間隔の 2 倍に設定できます (30 分ごとに通知を送信する場合は、受信から 1 時間後など)。 また、ニュース アプリの場合、毎日のニュースを表示するタイルの更新の有効期限は 1 日が適しています。

プッシュ通知とバッテリー節約機能

バッテリー節約機能は、デバイスのバックグラウンド アクティビティを制限することで、バッテリーの寿命を延ばします。 Windows 10 を使用すると、バッテリーが指定したしきい値を下回ったときに、ユーザーがバッテリー節約機能を自動的にオンにできます。 バッテリー節約機能がオンの場合、エネルギーを節約するためにプッシュ通知の受信が無効になります。 ただし、これにはいくつかの例外があります。 次の Windows 10 バッテリー節約機能の設定 (設定 アプリにあります) を使用すると、バッテリー節約機能がオンになっている場合でも、アプリでプッシュ通知を受信できます。

  • [Allow push notifications from any app while in battery saver] (バッテリー節約機能中に任意のアプリからのプッシュ通知を許可する): この設定では、バッテリー節約機能がオンになっている間にすべてのアプリがプッシュ通知を受信できます。 この設定は、デスクトップ エディション (Home、Pro、Enterprise、Education) の Windows 10 にのみ適用されることに注意してください。
  • [常に許可]: この設定を使用すると、バッテリー節約機能がオンになっている間に、特定のアプリをバックグラウンドで実行できます (プッシュ通知の受信を含む)。 この一覧は、ユーザーが手動で管理します。

これら 2 つの設定の状態を確認する方法はありませんが、バッテリー節約機能の状態を確認できます。 Windows 10 では、EnergySaverStatus プロパティを使用してバッテリー節約機能の状態を確認します。 アプリでは、EnergySaverStatusChanged イベントを使用して、バッテリー節約機能の変更をリッスンすることもできます。

アプリがプッシュ通知に大きく依存している場合は、バッテリー節約機能がオンになっている間に通知を受け取らない可能性があることをユーザーに通知し、ユーザーがバッテリー節約機能の設定を簡単に調整できるようにすることをお勧めします。 Windows 10 のバッテリー節約機能の設定 URI スキーム ms-settings:batterysaver-settings を使用すると、設定アプリへの便利なリンクを提供できます。

ヒント

バッテリー節約機能の設定をユーザーに通知するときは、今後、メッセージを表示しないようにする方法を提供することをお勧めします。 たとえば、次の例の [dontAskMeAgainBox] チェックボックスは、LocalSettings でユーザーの設定を保持します。

Windows 10 でバッテリー節約機能がオンになっているかどうかを確認する方法の例を示します。 この例では、ユーザーに通知し、設定アプリを起動してバッテリー節約機能の設定を行います。 ユーザーが再度通知を受け取りたくない場合は、dontAskAgainSetting を使用してメッセージを抑制できます。

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using Windows.System;
using Windows.System.Power;
...
...
async public void CheckForEnergySaving()
{
   //Get reminder preference from LocalSettings
   bool dontAskAgain;
   var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
   object dontAskSetting = localSettings.Values["dontAskAgainSetting"];
   if (dontAskSetting == null)
   {  // Setting does not exist
      dontAskAgain = false;
   }
   else
   {  // Retrieve setting value
      dontAskAgain = Convert.ToBoolean(dontAskSetting);
   }
   
   // Check if battery saver is on and that it's okay to raise dialog
   if ((PowerManager.EnergySaverStatus == EnergySaverStatus.On)
         && (dontAskAgain == false))
   {
      // Check dialog results
      ContentDialogResult dialogResult = await saveEnergyDialog.ShowAsync();
      if (dialogResult == ContentDialogResult.Primary)
      {
         // Launch battery saver settings (settings are available only when a battery is present)
         await Launcher.LaunchUriAsync(new Uri("ms-settings:batterysaver-settings"));
      }

      // Save reminder preference
      if (dontAskAgainBox.IsChecked == true)
      {  // Don't raise dialog again
         localSettings.Values["dontAskAgainSetting"] = "true";
      }
   }
}
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.System.Power.h>
#include <winrt/Windows.UI.Xaml.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Navigation.h>
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::System;
using namespace winrt::Windows::System::Power;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Navigation;
...
winrt::fire_and_forget CheckForEnergySaving()
{
    // Get reminder preference from LocalSettings.
    bool dontAskAgain{ false };
    auto localSettings = ApplicationData::Current().LocalSettings();
    IInspectable dontAskSetting = localSettings.Values().Lookup(L"dontAskAgainSetting");
    if (!dontAskSetting)
    {
        // Setting doesn't exist.
        dontAskAgain = false;
    }
    else
    {
        // Retrieve setting value
        dontAskAgain = winrt::unbox_value<bool>(dontAskSetting);
    }

    // Check whether battery saver is on, and whether it's okay to raise dialog.
    if ((PowerManager::EnergySaverStatus() == EnergySaverStatus::On) && (!dontAskAgain))
    {
        // Check dialog results.
        ContentDialogResult dialogResult = co_await saveEnergyDialog().ShowAsync();
        if (dialogResult == ContentDialogResult::Primary)
        {
            // Launch battery saver settings
            // (settings are available only when a battery is present).
            co_await Launcher::LaunchUriAsync(Uri(L"ms-settings:batterysaver-settings"));
        }

        // Save reminder preference.
        if (dontAskAgainBox().IsChecked())
        {
            // Don't raise the dialog again.
            localSettings.Values().Insert(L"dontAskAgainSetting", winrt::box_value(true));
        }
    }
}

これは、この例で取り上げられる ContentDialog の XAML です。

<ContentDialog x:Name="saveEnergyDialog"
               PrimaryButtonText="Open battery saver settings"
               SecondaryButtonText="Ignore"
               Title="Battery saver is on."> 
   <StackPanel>
      <TextBlock TextWrapping="WrapWholeWords">
         <LineBreak/><Run>Battery saver is on and you may 
          not receive push notifications.</Run><LineBreak/>
         <LineBreak/><Run>You can choose to allow this app to work normally
         while in battery saver, including receiving push notifications.</Run>
         <LineBreak/>
      </TextBlock>
      <CheckBox x:Name="dontAskAgainBox" Content="OK, got it."/>
   </StackPanel>
</ContentDialog>