Content Restriction

The Content Restriction API enables content provider apps to honor Family Safety content settings and create fun and appropriate experiences for people of all ages. Content restrictions are provided for apps, games, movies, music, and television programs. Content restriction settings are divided into two categories: content browsing and content consumption.

Use the content browsing settings to determine which content to present to users browsing an app’s content catalog, for example, a movie app’s list of movies or television shows. Content browsing information is what your app uses to ensure that the content it lists in a catalog matches the Family Safety content restrictions in force on the console.

Use content consumption settings to determine whether a specific piece of content can be consumed—that is, whether an app can be installed or a movie can be watched. When a content consumption API determines that content is blocked for consumption, it pops up the user interface that a parent uses if they choose to override the setting for a particular piece of content.

Specify Your Content Restriction Support in the Application Manifest

Application manifests for Xbox One can now include a mx:Capability with the value of “contentRestrictions” to indicate that your title will enforce content restrictions for the content that it displays. Titles with this capability are exempt from system rating checks during launch.

If your application does not implement content restriction checks, your application manifest must instead specify all ratings that are appropriate for the title. These ratings are checked against the current content restrictions on the system during activation. If you do not specify any ratings, your title is considered “Unrated”, which indicates that your title is appropriate only for adults.

The following application manifest XML demonstrates how to specify content ratings for your title using the mx:Ratings element.

  <package ...>
  ...
  <Applications>
    <Application ...>
      ...
      <mx:Ratings Category="game">
      <mx:Rating>ESRB:T</mx:Rating>
      <mx:Rating>USK:12</mx:Rating>
      <mx:Rating>COB-AU:PG</mx:Rating>
      <mx:Rating>OFLC-NZ:PG</mx:Rating>
      <mx:Rating>DJCTQ:12<mx:Rating>
      <mx:Rating>PEGI:12</mx:Rating>
      <mx:Rating>Microsoft:16</mx:Rating>
      </mx:Ratings>
      ...
    </Application>
    ...
  </Applications>
  ...
</package>  

Create a Rated Content Information Object

For all operations that inquire about a specific piece of media content, you must create a RatedContentDescription Class object that represents that content. The RatedContentDescription.Image Property property is not required, although it is best to provide it so that the UI can display a proper graphical representation of the content. This image should be appropriate for all audiences.

The following code example demonstrates how to create a RatedContentDescription Class object for a piece of media content.

C++

RatedContentDescription^ CreateRatedContentDescription(
    Platform::String szId,                                     
    Platform::String szTitle,                                  
    RatedContentCategory category,                             
    Platform::Collections::Vector<Platform::String^>^ ratings 
    )
{
    RatedContentDescription^ contentItemInfo = ref new RatedContentDescription(
        szId,
        szTitle,
        category
        );
    contentItemInfo->Ratings = ratings;
    return contentItemInfo;
}  

Get the Content Restriction Browsing Policy

When a user is browsing media content, the system provides policy information about the current state of content restrictions on the console, given system settings and current user profiles. All content services should, as part of the service query, use this data to filter out content that cannot be shown to a current user. The content displayed to the user cannot exceed an age rating above ContentRestrictionsBrowsePolicy.MaxBrowsableAgeRating Property. The rating must also be appropriate for the rating board in charge of the console’s ContentRestrictionsBrowsePolicy.GeographicRegion Property. If there is no rating appropriate for the rating board of that region, a Microsoft rating may used instead.

The following code example demonstrates how to create a RatedContentRestrictions object that contains policy information about the current state of content restrictions on the console and user profile.

C++

void GetContentRestrictionsBrowsePolicy(
    Platform::String& geoRegion,
    Platform::IBox<UINT>^& maxAgeRating,
    Platform::IBox<UINT>^& preferredAgeRating
    )
{
    RatedContentRestrictions contentRestrictions = ref new RatedContentRestrictions();
    auto op = contentRestrictions->GetBrowsePolicyAsync();
    concurrency::create_task(op).then([&](ContentRestrictionsBrowsePolicy^ browsePolicy){
        geoRegion = browsePolicy->GeographicRegion;
        maxAgeRating = browsePolicy->MaxAgeRating;
        preferredAgeRating = browsePolicy->PreferredAgeRating;
    }).wait();
    // NOTE: Either defer this work to a background thread or do not use wait().
}  

Filter Content in Xbox Live Service Calls

When communicating with some Xbox Live services, you must specify a content restriction filter header. This header is based on the information in the ContentRestrictionsBrowsePolicy Class object.

The following Base64-encoded JSON blob is an example of an x-xbl-contentRestrictions filter header. The maxAgeRating and preferredAgeRating fields are optional. If these fields are not specified, there is no restriction.

{
    "version":"1",
    "data":
    {
        "geographicRegion":"US",
        "maxAgeRating":"13",
        "preferredAgeRating":"13"
    }
}  

The following code example demonstrates how to create a filter header for an Xbox Live service call.

C++

Platform::String^ CreateLiveServiceContentRestrictionFilterHeader()
{
    const CHAR headerFormat_part1[] = "{\"version\":\"%d\",\"data\":{\"geographicRegion\":\"%ws\"";
    const CHAR headerFormat_part2[] = ",\"maxAgeRating\":\"%u\"";
    const CHAR headerFormat_part3[] = ",\"preferredAgeRating\":\"%u\"";
    const CHAR headerFormat_part4[] = "} }";
    
    const UINT headerVersion = 1;

    Platform::String geoRegion;
    Platform::IBox<UINT>^ maxAgeRating;
    Platform::IBox<UINT>^ preferredAgeRating;

    GetContentRestrictionsBrowsePolicy(geoRegion, maxAgeRating, preferredAgeRating);

    CHAR jsonText[1024] = {0};
    LPSTR curr = jsonText;
    size_t len = _countof(jsonText);

    // Format JSON data.
    HRESULT hr = StringCchPrintfExA(
        curr, len,
        &curr, &len,
        STRSAFE_NULL_ON_FAILURE,
        headerFormat_part1,
        headerVersion,
        geoRegion->Data()
        );
    if (SUCCEEDED(hr) && maxAgeRating != nullptr)
    {
        hr = StringCchPrintfExA(
            curr, len,
            &curr, &len,
            STRSAFE_NULL_ON_FAILURE,
            headerFormat_part2,
            maxAgeRating->Value
            );
    }
    if (SUCCEEDED(hr) && preferredAgeRating != nullptr)
    {
        hr = StringCchPrintfExA(
            curr, len,
            &curr, &len,
            STRSAFE_NULL_ON_FAILURE,
            headerFormat_part3,
            preferredAgeRating->Value
            );
    }
    if (SUCCEEDED(hr))
    {
        hr = StringCchCopyA(
            curr,
            len,
            headerFormat_part4
            );
    }
    if (FAILED(hr))
    {
        throw ref new Platform::Exception(hr);
    }

    // Base64 encode JSON body.
    int buffSize = static_cast<int>(strlen(jsonText));
    int encodedStringLength = ATL::Base64EncodeGetRequiredLength(buffSize);
    std::unique_ptr<CHAR[]> encodedString(new CHAR[encodedStringLength]);
    if (!encodedString)
    {
        throw ref new Platform::Exception(E_OUTOFMEMORY);
    }

    if (!ATL::Base64Encode(reinterpret_cast<BYTE*>((CHAR*)jsonText), buffSize, encodedString.get(), &encodedStringLength, ATL_BASE64_FLAG_NONE))
    {
        throw ref new Platform::Exception(E_FAIL);
    }

    // Convert to Wide Chars to return as Platform::String^.
    int headerStringlength = MultiByteToWideChar(CP_UTF8, 0, encodedString.get(), encodedStringLength, nullptr, 0);
    std::unique_ptr<WCHAR[]> headerString(new WCHAR[headerStringlength+1]);
    if (!headerString)
    {
        throw ref new Platform::Exception(E_OUTOFMEMORY);
    }

    if (MultiByteToWideChar(CP_UTF8, 0, encodedString.get(), encodedStringLength, headerString.get(), headerStringlength+1) == 0)
    {
        throw ref new Platform::Exception(HRESULT_FROM_WIN32(GetLastError()));
    }

    return ref new Platform::String(headerString.get());
}  

Get the Content Restriction Level for a Single Piece of Media Content

Before displaying any content to the user, your title must ensure that no restricted content is shown. This can be done by implementing a filter in a service query (as explained in the Filtering content in Xbox Live service calls section). Alternatively, you can query each item individually to determine the ContentAccessRestrictionLevel value for the current user. Your title must never display content with a restriction level of Hide.

The following code example demonstrates how to get the current user’s restriction level for a single piece of media content.

C++

bool CanShowContent(RatedContentDescription^ contentDescription)
{
    bool ret = false;
    RatedContentRestrictions contentRestrictions = ref new RatedContentRestrictions();
    auto op = contentRestrictions->GetRestrictionLevelAsync(contentDescription);
    concurrency::create_task(op).then([&](ContentAccessRestrictionLevel restrictionLevel) {
        ret = restrictionLevel != ContentAccessRestrictionLevel::Hide;
    }).wait();
    // NOTE: Either defer this work to a background thread or do not use wait().
    return ret;
}  

Check Access Before Content Consumption

Finally, your title must call the RatedContentRestrictions.RequestContentAccessAsync Method method before allowing the user to consume media content. If this method returns false, the content is blocked. The system will display a message that informs the user why the content is blocked and potentially offer a resolution; your title should not display any UI if the content is blocked. If this method returns true, the content can be immediately consumed.

The following code example demonstrates how to perform the final content restriction check before media can be consumed.

C++

void LaunchContent(RatedContentDescription^ contentDescription)
{
    RatedContentRestrictions contentRestrictions = ref new RatedContentRestrictions();
    auto op = contentRestrictions->RequestContentAccessAsync(contentDescription);
    concurrency::create_task(op).then([&](bool canLaunch) {
        if (canLaunch)
        {
            // Launch the content.
        }
        else
        {
            // Do nothing. The system is responsible for handling blocked content.
        }
    }).wait();
    // NOTE: Either defer this work to a background thread or do not use wait().
}  

Test Parental Controls

To test content restriction and parental controls, you should create an additional developer account and classify it as a child account. In the UI of the console, add the child account to your family. Once it has been added, you can change the settings for what content should be displayed, and how restricted content should be treated. Restricted content can either be hidden completely from searches and the OneGuide, or it can be listed but classified as unavailable. You should verify that your title handles content restriction appropriately by signing into the console with the child account and attempting to gain access to the content your title offers.

See also

White Paper: Content Controls for Xbox One (Developer Education Materials > All NDA Whitepapers)

White Paper: Title Ratings for Xbox One (Developer Education Materials > All NDA Whitepapers)