content: /*
   * Copyright (C) 2014 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.media.tv;
  
  import android.annotation.NonNull;
  import android.annotation.SystemApi;
  import android.text.TextUtils;
  
  import com.android.internal.util.Preconditions;
  
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.List;
  import java.util.Objects;
  
  /**
   * A class representing a TV content rating. When a TV input service inserts the content rating
   * information on a program into the database, this class can be used to generate the formatted
   * string for
   * {@link TvContract.Programs#COLUMN_CONTENT_RATING TvContract.Programs.COLUMN_CONTENT_RATING}.
   * To create a {@code TvContentRating} object, use the
   * {@link #createRating TvContentRating.createRating} method with valid rating system string
   * constants.
   *
   * <p>It is possible for an application to define its own content rating system by supplying a
   * content rating system definition XML resource (see example below) and declaring a broadcast
   * receiver that filters {@link TvInputManager#ACTION_QUERY_CONTENT_RATING_SYSTEMS} in its manifest.
   *
   * <h3> Example: Rating system definition for the TV Parental Guidelines</h3>
   * The following XML example shows how the TV Parental Guidelines in the United States can be
   * defined:
   * <p><pre class="prettyprint">
   * {@literal
   * <rating-system-definitions xmlns:android="http://schemas.android.com/apk/res/android"
   *     android:versionCode="1">
   *     <rating-system-definition android:name="US_TV"
   *         android:country="US"
   *         android:description="@string/description_us_tv">
   *         <sub-rating-definition android:name="US_TV_D"
   *             android:title="D"
   *             android:description="@string/description_us_tv_d" />
   *         <sub-rating-definition android:name="US_TV_L"
   *             android:title="L"
   *             android:description="@string/description_us_tv_l" />
   *         <sub-rating-definition android:name="US_TV_S"
   *             android:title="S"
   *             android:description="@string/description_us_tv_s" />
   *         <sub-rating-definition android:name="US_TV_V"
   *             android:title="V"
   *             android:description="@string/description_us_tv_v" />
   *         <sub-rating-definition android:name="US_TV_FV"
   *             android:title="FV"
   *             android:description="@string/description_us_tv_fv" />
   *
   *         <rating-definition android:name="US_TV_Y"
   *             android:title="TV-Y"
   *             android:description="@string/description_us_tv_y"
   *             android:icon="@drawable/icon_us_tv_y"
   *             android:contentAgeHint="0" />
   *         <rating-definition android:name="US_TV_Y7"
   *             android:title="TV-Y7"
   *             android:description="@string/description_us_tv_y7"
   *             android:icon="@drawable/icon_us_tv_y7"
   *             android:contentAgeHint="7">
   *             <sub-rating android:name="US_TV_FV" />
   *         </rating-definition>
   *         <rating-definition android:name="US_TV_G"
   *             android:title="TV-G"
   *             android:description="@string/description_us_tv_g"
   *             android:icon="@drawable/icon_us_tv_g"
   *             android:contentAgeHint="0" />
   *         <rating-definition android:name="US_TV_PG"
   *             android:title="TV-PG"
   *             android:description="@string/description_us_tv_pg"
   *             android:icon="@drawable/icon_us_tv_pg"
   *             android:contentAgeHint="14">
   *             <sub-rating android:name="US_TV_D" />
   *             <sub-rating android:name="US_TV_L" />
   *             <sub-rating android:name="US_TV_S" />
   *             <sub-rating android:name="US_TV_V" />
   *         </rating-definition>
   *         <rating-definition android:name="US_TV_14"
   *             android:title="TV-14"
   *             android:description="@string/description_us_tv_14"
   *             android:icon="@drawable/icon_us_tv_14"
   *             android:contentAgeHint="14">
   *             <sub-rating android:name="US_TV_D" />
   *             <sub-rating android:name="US_TV_L" />
   *             <sub-rating android:name="US_TV_S" />
   *             <sub-rating android:name="US_TV_V" />
   *         </rating-definition>
   *         <rating-definition android:name="US_TV_MA"
   *             android:title="TV-MA"
   *             android:description="@string/description_us_tv_ma"
   *             android:icon="@drawable/icon_us_tv_ma"
   *             android:contentAgeHint="17">
   *             <sub-rating android:name="US_TV_L" />
   *             <sub-rating android:name="US_TV_S" />
   *             <sub-rating android:name="US_TV_V" />
   *         </rating-definition>
   *         <rating-order>
   *             <rating android:name="US_TV_Y" />
   *             <rating android:name="US_TV_Y7" />
   *         </rating-order>
   *         <rating-order>
   *             <rating android:name="US_TV_G" />
   *             <rating android:name="US_TV_PG" />
   *             <rating android:name="US_TV_14" />
   *             <rating android:name="US_TV_MA" />
   *         </rating-order>
   *     </rating-system-definition>
   * </rating-system-definitions>}</pre>
   *
   * <h3>System defined rating strings</h3>
   * The following strings are defined by the system to provide a standard way to create
   * {@code TvContentRating} objects.
   *
   * <p>For example, to create an object that represents TV-PG rating with suggestive dialogue and
   * coarse language from the TV Parental Guidelines in the United States, one can use the following
   * code snippet:
   *
   * <pre>
   * TvContentRating rating = TvContentRating.createRating(
   *         "com.android.tv",
   *         "US_TV",
   *         "US_TV_PG",
   *         "US_TV_D", "US_TV_L");
   * </pre>
   * <h4>System defined string for domains</h4>
   * <table>
   *     <tr>
   *         <th>Constant Value</th>
   *         <th>Description</th>
   *     </tr>
   *     <tr>
   *         <td>com.android.tv</td>
   *         <td>Used for creating system defined content ratings</td>
   *     </tr>
   * </table>
   *
   * <h4>System defined strings for rating systems</h4>
   * <table>
   *     <tr>
   *         <th>Constant Value</th>
   *         <th>Description</th>
   *     </tr>
   *     <tr>
   *         <td>AR_TV</td>
   *         <td>TV content rating system for Argentina</td>
   *     </tr>
   *     <tr>
   *         <td>AU_TV</td>
   *         <td>TV content rating system for Australia</td>
   *     </tr>
   *     <tr>
   *         <td>BR_TV</td>
   *         <td>TV content rating system for Brazil</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_EN</td>
   *         <td>TV content rating system for Canada (English)</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_FR</td>
   *         <td>TV content rating system for Canada (French)</td>
   *     </tr>
   *     <tr>
   *         <td>DVB</td>
   *         <td>DVB content rating system</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB</td>
   *         <td>DVB content rating system for Spain</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB</td>
   *         <td>DVB content rating system for France</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB</td>
   *         <td>ISDB content rating system</td>
   *     </tr>
   *     <tr>
   *         <td>KR_TV</td>
   *         <td>TV content rating system for South Korea</td>
   *     </tr>
   *     <tr>
   *         <td>SG_TV</td>
   *         <td>TV content rating system for Singapore</td>
   *     </tr>
   *     <tr>
   *         <td>US_MV</td>
   *         <td>Movie content rating system for the United States</td>
   *     </tr>
   *     <tr>
   *         <td>US_TV</td>
   *         <td>TV content rating system for the United States</td>
   *     </tr>
   * </table>
   *
   * <h4>System defined strings for ratings</h4>
   * <table>
   *     <tr>
   *         <th>Rating System</th>
   *         <th>Constant Value</th>
   *         <th>Description</th>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="4">AR_TV</td>
   *         <td>AR_TV_ATP</td>
   *         <td>Suitable for all audiences. Programs may contain mild violence, language and mature
   *         situations</td>
   *     </tr>
   *     <tr>
   *         <td>AR_TV_SAM_13</td>
   *         <td>Suitable for ages 13 and up. Programs may contain mild to moderate language and mild
   *         violence and sexual references</td>
   *     </tr>
   *     <tr>
   *         <td>AR_TV_SAM_16</td>
   *         <td>Suitable for ages 16 and up. Programs may contain more intensive violence and coarse
   *         language, partial nudity and moderate sexual references</td>
   *     </tr>
   *     <tr>
   *         <td>AR_TV_SAM_18</td>
   *         <td>Suitable for mature audiences only. Programs contain strong violence, coarse language
   *         and explicit sexual references</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="8">AU_TV</td>
   *         <td>AU_TV_P</td>
   *         <td>Recommended for younger children aged between 2 and 11 years</td>
   *     </tr>
   *     <tr>
   *         <td>AU_TV_C</td>
   *         <td>Recommended for older children aged between 5 and 14 years</td>
   *     </tr>
   *     <tr>
   *         <td>AU_TV_G</td>
   *         <td>Recommended for all ages</td>
   *     </tr>
   *     <tr>
   *         <td>AU_TV_PG</td>
   *         <td>Parental guidance is recommended for young viewers under 15</td>
   *     </tr>
   *     <tr>
   *         <td>AU_TV_M</td>
   *         <td>Recommended for mature audiences aged 15 years and over</td>
   *     </tr>
   *     <tr>
   *         <td>AU_TV_MA</td>
   *         <td>Not suitable for children and teens under 15, due to sexual descriptions, course
   *         language, adult themes or drug use</td>
   *     </tr>
   *     <tr>
   *         <td>AU_TV_AV</td>
   *         <td>Not suitable for children and teens under 15. This category is used specifically for
   *         violent programs</td>
   *     </tr>
   *     <tr>
   *         <td>AU_TV_R</td>
   *         <td>Not for children under 18. Content may include graphic violence, sexual situations,
   *         coarse language and explicit drug use</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="6">BR_TV</td>
   *         <td>BR_TV_L</td>
   *         <td>Content is suitable for all audiences</td>
   *     </tr>
   *     <tr>
   *         <td>BR_TV_10</td>
   *         <td>Content suitable for viewers over the age of 10</td>
   *     </tr>
   *     <tr>
   *         <td>BR_TV_12</td>
   *         <td>Content suitable for viewers over the age of 12</td>
   *     </tr>
   *     <tr>
   *         <td>BR_TV_14</td>
   *         <td>Content suitable for viewers over the age of 14</td>
   *     </tr>
   *     <tr>
   *         <td>BR_TV_16</td>
   *         <td>Content suitable for viewers over the age of 16</td>
   *     </tr>
   *     <tr>
   *         <td>BR_TV_18</td>
   *         <td>Content suitable for viewers over the age of 18</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="7">CA_TV_EN</td>
   *         <td>CA_TV_EN_EXEMPT</td>
   *         <td>Exempt from ratings</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_EN_C</td>
   *         <td>Suitable for children ages 2&#8211;7</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_EN_C8</td>
   *         <td>Suitable for children ages 8 and older</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_EN_G</td>
   *         <td>Suitable for the entire family</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_EN_PG</td>
   *         <td>May contain moderate violence, profanity, nudity, and sexual references</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_EN_14</td>
   *         <td>Intended for viewers ages 14 and older</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_EN_18</td>
   *         <td>Intended for viewers ages 18 and older</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="6">CA_TV_FR</td>
   *         <td>CA_TV_FR_E</td>
   *         <td>Exempt from ratings</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_FR_G</td>
   *         <td>Appropriate for all ages</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_FR_8</td>
   *         <td>Appropriate for children 8</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_FR_13</td>
   *         <td>Suitable for children 13</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_FR_16</td>
   *         <td>Recommended for children over the age of 16</td>
   *     </tr>
   *     <tr>
   *         <td>CA_TV_FR_18</td>
   *         <td>Only to be viewed by adults</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="15">DVB</td>
   *         <td>DVB_4</td>
   *         <td>Recommended for ages 4 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_5</td>
   *         <td>Recommended for ages 5 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_6</td>
   *         <td>Recommended for ages 6 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_7</td>
   *         <td>Recommended for ages 7 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_8</td>
   *         <td>Recommended for ages 8 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_9</td>
   *         <td>Recommended for ages 9 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_10</td>
   *         <td>Recommended for ages 10 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_11</td>
   *         <td>Recommended for ages 11 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_12</td>
   *         <td>Recommended for ages 12 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_13</td>
   *         <td>Recommended for ages 13 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_14</td>
   *         <td>Recommended for ages 14 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_15</td>
   *         <td>Recommended for ages 15 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_16</td>
   *         <td>Recommended for ages 16 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_17</td>
   *         <td>Recommended for ages 17 and over</td>
   *     </tr>
   *     <tr>
   *         <td>DVB_18</td>
   *         <td>Recommended for ages 18 and over</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="18">ES_DVB</td>
   *         <td>ES_DVB_ALL</td>
   *         <td>Recommended for all ages</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_C</td>
   *         <td>Recommended for children</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_X</td>
   *         <td>Recommended for adults</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_4</td>
   *         <td>Recommended for ages 4 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_5</td>
   *         <td>Recommended for ages 5 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_6</td>
   *         <td>Recommended for ages 6 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_7</td>
   *         <td>Recommended for ages 7 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_8</td>
   *         <td>Recommended for ages 8 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_9</td>
   *         <td>Recommended for ages 9 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_10</td>
   *         <td>Recommended for ages 10 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_11</td>
   *         <td>Recommended for ages 11 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_12</td>
   *         <td>Recommended for ages 12 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_13</td>
   *         <td>Recommended for ages 13 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_14</td>
   *         <td>Recommended for ages 14 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_15</td>
   *         <td>Recommended for ages 15 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_16</td>
   *         <td>Recommended for ages 16 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_17</td>
   *         <td>Recommended for ages 17 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ES_DVB_18</td>
   *         <td>Recommended for ages 18 and over</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="16">FR_DVB</td>
   *         <td>FR_DVB_U</td>
   *         <td>Recommended for all ages</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_4</td>
   *         <td>Recommended for ages 4 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_5</td>
   *         <td>Recommended for ages 5 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_6</td>
   *         <td>Recommended for ages 6 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_7</td>
   *         <td>Recommended for ages 7 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_8</td>
   *         <td>Recommended for ages 8 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_9</td>
   *         <td>Recommended for ages 9 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_10</td>
   *         <td>Recommended for ages 10 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_11</td>
   *         <td>Recommended for ages 11 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_12</td>
   *         <td>Recommended for ages 12 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_13</td>
   *         <td>Recommended for ages 13 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_14</td>
   *         <td>Recommended for ages 14 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_15</td>
   *         <td>Recommended for ages 15 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_16</td>
   *         <td>Recommended for ages 16 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_17</td>
   *         <td>Recommended for ages 17 and over</td>
   *     </tr>
   *     <tr>
   *         <td>FR_DVB_18</td>
   *         <td>Recommended for ages 18 and over</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="17">ISDB</td>
   *         <td>ISDB_4</td>
   *         <td>Recommended for ages 4 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_5</td>
   *         <td>Recommended for ages 5 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_6</td>
   *         <td>Recommended for ages 6 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_7</td>
   *         <td>Recommended for ages 7 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_8</td>
   *         <td>Recommended for ages 8 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_9</td>
   *         <td>Recommended for ages 9 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_10</td>
   *         <td>Recommended for ages 10 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_11</td>
   *         <td>Recommended for ages 11 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_12</td>
   *         <td>Recommended for ages 12 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_13</td>
   *         <td>Recommended for ages 13 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_14</td>
   *         <td>Recommended for ages 14 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_15</td>
   *         <td>Recommended for ages 15 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_16</td>
   *         <td>Recommended for ages 16 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_17</td>
   *         <td>Recommended for ages 17 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_18</td>
   *         <td>Recommended for ages 18 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_19</td>
   *         <td>Recommended for ages 19 and over</td>
   *     </tr>
   *     <tr>
   *         <td>ISDB_20</td>
   *         <td>Recommended for ages 20 and over</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="5">KR_TV</td>
   *         <td>KR_TV_ALL</td>
   *         <td>Appropriate for all ages</td>
   *     </tr>
   *     <tr>
   *         <td>KR_TV_7</td>
   *         <td>May contain material inappropriate for children younger than 7, and parental
   *         discretion should be used</td>
   *     </tr>
   *     <tr>
   *         <td>KR_TV_12</td>
   *         <td>May deemed inappropriate for those younger than 12, and parental discretion should be
   *         used</td>
   *     </tr>
   *     <tr>
   *         <td>KR_TV_15</td>
   *         <td>May be inappropriate for children under 15, and that parental discretion should be
   *         used</td>
   *     </tr>
   *     <tr>
   *         <td>KR_TV_19</td>
   *         <td>For adults only</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="6">SG_TV</td>
   *         <td>SG_TV_G</td>
   *         <td>Suitable for all ages</td>
   *     </tr>
   *     <tr>
   *         <td>SG_TV_PG</td>
   *         <td>Suitable for all but parents should guide their young</td>
   *     </tr>
   *     <tr>
   *         <td>SG_TV_PG13</td>
   *         <td>Suitable for persons aged 13 and above but parental guidance is advised for children
   *         below 13</td>
   *     </tr>
   *     <tr>
   *         <td>SG_TV_NC16</td>
   *         <td>Suitable for persons aged 16 and above</td>
   *     </tr>
   *     <tr>
   *         <td>SG_TV_M18</td>
   *         <td>Suitable for persons aged 18 and above</td>
   *     </tr>
   *     <tr>
   *         <td>SG_TV_R21</td>
   *         <td>Suitable for adults aged 21 and above</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="5">US_MV</td>
   *         <td>US_MV_G</td>
   *         <td>General audiences</td>
   *     </tr>
   *     <tr>
   *         <td>US_MV_PG</td>
   *         <td>Parental guidance suggested</td>
   *     </tr>
   *     <tr>
   *         <td>US_MV_PG13</td>
   *         <td>Parents strongly cautioned</td>
   *     </tr>
   *     <tr>
   *         <td>US_MV_R</td>
   *         <td>Restricted, under 17 requires accompanying parent or adult guardian</td>
   *     </tr>
   *     <tr>
   *         <td>US_MV_NC17</td>
   *         <td>No one 17 and under admitted</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="6">US_TV</td>
   *         <td>US_TV_Y</td>
   *         <td>This program is designed to be appropriate for all children</td>
   *     </tr>
   *     <tr>
   *         <td>US_TV_Y7</td>
   *         <td>This program is designed for children age 7 and above</td>
   *     </tr>
   *     <tr>
   *         <td>US_TV_G</td>
   *         <td>Most parents would find this program suitable for all ages</td>
   *     </tr>
   *     <tr>
   *         <td>US_TV_PG</td>
   *         <td>This program contains material that parents may find unsuitable for younger children
   *         </td>
   *     </tr>
   *     <tr>
   *         <td>US_TV_14</td>
   *         <td>This program contains some material that many parents would find unsuitable for
   *         children under 14 years of age</td>
   *     </tr>
   *     <tr>
   *         <td>US_TV_MA</td>
   *         <td>This program is specifically designed to be viewed by adults and therefore may be
   *         unsuitable for children under 17</td>
   *     </tr>
   * </table>
   *
   * <h4>System defined strings for sub-ratings</h4>
   * <table>
   *     <tr>
   *         <th>Rating System</th>
   *         <th>Constant Value</th>
   *         <th>Description</th>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="3">BR_TV</td>
   *         <td>BR_TV_D</td>
   *         <td>Drugs<br/>Applicable to BR_TV_L, BR_TV_10, BR_TV_12, BR_TV_14, BR_TV_16, and BR_TV_18
   *         </td>
   *     </tr>
   *     <tr>
   *         <td>BR_TV_S</td>
   *         <td>Sex<br/>Applicable to BR_TV_L, BR_TV_10, BR_TV_12, BR_TV_14, BR_TV_16, and BR_TV_18
   *         </td>
   *     </tr>
   *     <tr>
   *         <td>BR_TV_V</td>
   *         <td>Violence<br/>Applicable to BR_TV_L, BR_TV_10, BR_TV_12, BR_TV_14, BR_TV_16, and
   *         BR_TV_18</td>
   *     </tr>
   *     <tr>
   *         <td valign="top" rowspan="5">US_TV</td>
   *         <td>US_TV_D</td>
   *         <td>Suggestive dialogue (Usually means talks about sex)<br/>Applicable to US_TV_PG, and
   *         US_TV_14</td>
   *     </tr>
   *     <tr>
   *         <td>US_TV_L</td>
   *         <td>Coarse language<br/>Applicable to US_TV_PG, US_TV_14, and US_TV_MA</td>
   *     </tr>
   *     <tr>
   *         <td>US_TV_S</td>
   *         <td>Sexual content<br/>Applicable to US_TV_PG, US_TV_14, and US_TV_MA</td>
   *     </tr>
   *     <tr>
   *         <td>US_TV_V</td>
   *         <td>Violence<br/>Applicable to US_TV_PG, US_TV_14, and US_TV_MA</td>
   *     </tr>
   *     <tr>
   *         <td>US_TV_FV</td>
   *         <td>Fantasy violence (Children's programming only)<br/>Applicable to US_TV_Y7</td>
   *     </tr>
   * </table>
   */
  public final class TvContentRating {
      // TODO: Consider to use other DELIMITER. In some countries such as India may use this delimiter
      // in the main ratings.
      private static final String DELIMITER = "/";
  
      private final String mDomain;
      private final String mRatingSystem;
      private final String mRating;
      private final String[] mSubRatings;
      private final int mHashCode;
  
      /**
       * Rating constant denoting unrated content. Used to handle the case where the content rating
       * information is missing.
       *
       * <p>TV input services can call {@link TvInputManager#isRatingBlocked} with this constant to
       * determine whether they should block unrated content. The subsequent call to
       * {@link TvInputService.Session#notifyContentBlocked} with the same constant notifies
       * applications that the current program content is blocked by parental controls.
       */
      public static final TvContentRating UNRATED = new TvContentRating("null", "null", "null", null);
  
      /**
       * Creates a {@code TvContentRating} object with predefined content rating strings.
       *
       * @param domain The domain string. For example, "com.android.tv".
       * @param ratingSystem The rating system string. For example, "US_TV".
       * @param rating The content rating string. For example, "US_TV_PG".
       * @param subRatings The sub-rating strings. For example, "US_TV_D" and "US_TV_L".
       * @return A {@code TvContentRating} object.
       * @throws IllegalArgumentException If {@code domain}, {@code ratingSystem} or {@code rating} is
       *             {@code null}.
       */
      public static TvContentRating createRating(String domain, String ratingSystem,
              String rating, String... subRatings) {
          if (TextUtils.isEmpty(domain)) {
              throw new IllegalArgumentException("domain cannot be empty");
          }
          if (TextUtils.isEmpty(ratingSystem)) {
              throw new IllegalArgumentException("ratingSystem cannot be empty");
          }
          if (TextUtils.isEmpty(rating)) {
              throw new IllegalArgumentException("rating cannot be empty");
          }
          return new TvContentRating(domain, ratingSystem, rating, subRatings);
      }
  
      /**
       * Recovers a {@code TvContentRating} object from the string that was previously created from
       * {@link #flattenToString}.
       *
       * @param ratingString The string returned by {@link #flattenToString}.
       * @return the {@code TvContentRating} object containing the domain, rating system, rating and
       *         sub-ratings information encoded in {@code ratingString}.
       * @see #flattenToString
       */
      public static TvContentRating unflattenFromString(String ratingString) {
          if (TextUtils.isEmpty(ratingString)) {
              throw new IllegalArgumentException("ratingString cannot be empty");
          }
          String[] strs = ratingString.split(DELIMITER);
          if (strs.length < 3) {
              throw new IllegalArgumentException("Invalid rating string: " + ratingString);
          }
          if (strs.length > 3) {
              String[] subRatings = new String[strs.length - 3];
              System.arraycopy(strs, 3, subRatings, 0, subRatings.length);
              return new TvContentRating(strs[0], strs[1], strs[2], subRatings);
          }
          return new TvContentRating(strs[0], strs[1], strs[2], null);
      }
  
      /**
       * Constructs a TvContentRating object from a given rating and sub-rating constants.
       *
       * @param domain The string for domain of the content rating system such as "com.android.tv".
       * @param ratingSystem The rating system string such as "US_TV".
       * @param rating The content rating string such as "US_TV_PG".
       * @param subRatings The sub-rating strings such as "US_TV_D" and "US_TV_L".
       */
      private TvContentRating(
              String domain, String ratingSystem, String rating, String[] subRatings) {
          mDomain = domain;
          mRatingSystem = ratingSystem;
          mRating = rating;
          if (subRatings == null || subRatings.length == 0) {
              mSubRatings = null;
          } else {
              Arrays.sort(subRatings);
              mSubRatings = subRatings;
          }
          mHashCode = 31 * Objects.hash(mDomain, mRating) + Arrays.hashCode(mSubRatings);
      }
  
      /**
       * Returns the domain of this {@code TvContentRating} object.
       */
      public String getDomain() {
          return mDomain;
      }
  
      /**
       * Returns the rating system of this {@code TvContentRating} object.
       */
      public String getRatingSystem() {
          return mRatingSystem;
      }
  
      /**
       * Returns the main rating of this {@code TvContentRating} object.
       */
      public String getMainRating() {
          return mRating;
      }
  
      /**
       * Returns the unmodifiable sub-rating string {@link List} of this {@code TvContentRating}
       * object.
       */
      public List<String> getSubRatings() {
          if (mSubRatings == null) {
              return null;
          }
          return Collections.unmodifiableList(Arrays.asList(mSubRatings));
      }
  
      /**
       * Returns a string that unambiguously describes the rating information contained in a
       * {@code TvContentRating} object. One can later recover the object from this string through
       * {@link #unflattenFromString}.
       *
       * @return a string containing the rating information, which can later be stored in the
       *         database.
       * @see #unflattenFromString
       */
      public String flattenToString() {
          StringBuilder builder = new StringBuilder();
          builder.append(mDomain);
          builder.append(DELIMITER);
          builder.append(mRatingSystem);
          builder.append(DELIMITER);
          builder.append(mRating);
          if (mSubRatings != null) {
              for (String subRating : mSubRatings) {
                  builder.append(DELIMITER);
                  builder.append(subRating);
              }
          }
          return builder.toString();
      }
  
      /**
       * Returns {@code true} if this rating has the same main rating as the specified rating and when
       * this rating's sub-ratings contain the other's.
       *
       * <p>For example, a {@code TvContentRating} object that represents TV-PG with
       * S(Sexual content) and V(Violence) contains TV-PG, TV-PG/S, TV-PG/V and itself.
       *
       * @param rating The {@link TvContentRating} to check.
       * @return {@code true} if this object contains {@code rating}, {@code false} otherwise.
       */
      public final boolean contains(@NonNull TvContentRating rating) {
          Preconditions.checkNotNull(rating);
          if (!rating.getMainRating().equals(mRating)) {
              return false;
          }
          if (!rating.getDomain().equals(mDomain) ||
                  !rating.getRatingSystem().equals(mRatingSystem) ||
                  !rating.getMainRating().equals(mRating)) {
              return false;
          }
          List<String> subRatings = getSubRatings();
          List<String> subRatingsOther = rating.getSubRatings();
          if (subRatings == null && subRatingsOther == null) {
              return true;
          } else if (subRatings == null && subRatingsOther != null) {
              return false;
          } else if (subRatings != null && subRatingsOther == null) {
              return true;
          } else {
              return subRatings.containsAll(subRatingsOther);
          }
      }
  
      @Override
      public boolean equals(Object obj) {
          if (!(obj instanceof TvContentRating)) {
              return false;
          }
          TvContentRating other = (TvContentRating) obj;
          if (mHashCode != other.mHashCode) {
              return false;
          }
          if (!TextUtils.equals(mDomain, other.mDomain)) {
              return false;
          }
          if (!TextUtils.equals(mRatingSystem, other.mRatingSystem)) {
              return false;
          }
          if (!TextUtils.equals(mRating, other.mRating)) {
              return false;
          }
          return Arrays.equals(mSubRatings, other.mSubRatings);
      }
  
      @Override
      public int hashCode() {
          return mHashCode;
      }
  }
  