1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353
  
     | 
    
      /*
 * Copyright (C) 2013 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;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.util.Log;
import dalvik.system.CloseGuard;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
/**
 * MediaDrm can be used to obtain keys for decrypting protected media streams, in
 * conjunction with {@link android.media.MediaCrypto}.  The MediaDrm APIs
 * are designed to support the ISO/IEC 23001-7: Common Encryption standard, but
 * may also be used to implement other encryption schemes.
 * <p>
 * Encrypted content is prepared using an encryption server and stored in a content
 * library. The encrypted content is streamed or downloaded from the content library to
 * client devices via content servers.  Licenses to view the content are obtained from
 * a License Server.
 * <p>
 * <p><img src="../../../images/mediadrm_overview.png"
 *      alt="MediaDrm Overview diagram"
 *      border="0" /></p>
 * <p>
 * Keys are requested from the license server using a key request. The key
 * response is delivered to the client app, which provides the response to the
 * MediaDrm API.
 * <p>
 * A Provisioning server may be required to distribute device-unique credentials to
 * the devices.
 * <p>
 * Enforcing requirements related to the number of devices that may play content
 * simultaneously can be performed either through key renewal or using the secure
 * stop methods.
 * <p>
 * The following sequence diagram shows the interactions between the objects
 * involved while playing back encrypted content:
 * <p>
 * <p><img src="../../../images/mediadrm_decryption_sequence.png"
 *         alt="MediaDrm Overview diagram"
 *         border="0" /></p>
 * <p>
 * The app first constructs {@link android.media.MediaExtractor} and
 * {@link android.media.MediaCodec} objects. It accesses the DRM-scheme-identifying UUID,
 * typically from metadata in the content, and uses this UUID to construct an instance
 * of a MediaDrm object that is able to support the DRM scheme required by the content.
 * Crypto schemes are assigned 16 byte UUIDs.  The method {@link #isCryptoSchemeSupported}
 * can be used to query if a given scheme is supported on the device.
 * <p>
 * The app calls {@link #openSession} to generate a sessionId that will uniquely identify
 * the session in subsequent interactions. The app next uses the MediaDrm object to
 * obtain a key request message and send it to the license server, then provide
 * the server's response to the MediaDrm object.
 * <p>
 * Once the app has a sessionId, it can construct a MediaCrypto object from the UUID and
 * sessionId.  The MediaCrypto object is registered with the MediaCodec in the
 * {@link MediaCodec#configure} method to enable the codec to decrypt content.
 * <p>
 * When the app has constructed {@link android.media.MediaExtractor},
 * {@link android.media.MediaCodec} and {@link android.media.MediaCrypto} objects,
 * it proceeds to pull samples from the extractor and queue them into the decoder.  For
 * encrypted content, the samples returned from the extractor remain encrypted, they
 * are only decrypted when the samples are delivered to the decoder.
 * <p>
 * MediaDrm methods throw {@link android.media.MediaDrm.MediaDrmStateException}
 * when a method is called on a MediaDrm object that has had an unrecoverable failure
 * in the DRM plugin or security hardware.
 * {@link android.media.MediaDrm.MediaDrmStateException} extends
 * {@link java.lang.IllegalStateException} with the addition of a developer-readable
 * diagnostic information string associated with the exception.
 * <p>
 * In the event of a mediaserver process crash or restart while a MediaDrm object
 * is active, MediaDrm methods may throw {@link android.media.MediaDrmResetException}.
 * To recover, the app must release the MediaDrm object, then create and initialize
 * a new one.
 * <p>
 * As {@link android.media.MediaDrmResetException} and
 * {@link android.media.MediaDrm.MediaDrmStateException} both extend
 * {@link java.lang.IllegalStateException}, they should be in an earlier catch()
 * block than {@link java.lang.IllegalStateException} if handled separately.
 * <p>
 * <a name="Callbacks"></a>
 * <h3>Callbacks</h3>
 * <p>Applications should register for informational events in order
 * to be informed of key state updates during playback or streaming.
 * Registration for these events is done via a call to
 * {@link #setOnEventListener}. In order to receive the respective
 * callback associated with this listener, applications are required to create
 * MediaDrm objects on a thread with its own Looper running (main UI
 * thread by default has a Looper running).
 */
public final class MediaDrm implements AutoCloseable {
    private static final String TAG = "MediaDrm";
    private final AtomicBoolean mClosed = new AtomicBoolean();
    private final CloseGuard mCloseGuard = CloseGuard.get();
    private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES;
    private long mNativeContext;
    /**
     * Specify no certificate type
     *
     * @hide - not part of the public API at this time
     */
    public static final int CERTIFICATE_TYPE_NONE = 0;
    /**
     * Specify X.509 certificate type
     *
     * @hide - not part of the public API at this time
     */
    public static final int CERTIFICATE_TYPE_X509 = 1;
    /** @hide */
    @IntDef({
        CERTIFICATE_TYPE_NONE,
        CERTIFICATE_TYPE_X509,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface CertificateType {}
    /**
     * Query if the given scheme identified by its UUID is supported on
     * this device.
     * @param uuid The UUID of the crypto scheme.
     */
    public static final boolean isCryptoSchemeSupported(@NonNull UUID uuid) {
        return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), null,
                SECURITY_LEVEL_UNKNOWN);
    }
    /**
     * Query if the given scheme identified by its UUID is supported on
     * this device, and whether the DRM plugin is able to handle the
     * media container format specified by mimeType.
     * @param uuid The UUID of the crypto scheme.
     * @param mimeType The MIME type of the media container, e.g. "video/mp4"
     *   or "video/webm"
     */
    public static final boolean isCryptoSchemeSupported(
            @NonNull UUID uuid, @NonNull String mimeType) {
        return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid),
                mimeType, SECURITY_LEVEL_UNKNOWN);
    }
    /**
     * Query if the given scheme identified by its UUID is supported on
     * this device, and whether the DRM plugin is able to handle the
     * media container format specified by mimeType at the requested
     * security level.
     *
     * @param uuid The UUID of the crypto scheme.
     * @param mimeType The MIME type of the media container, e.g. "video/mp4"
     *   or "video/webm"
     * @param securityLevel the security level requested
     */
    public static final boolean isCryptoSchemeSupported(
            @NonNull UUID uuid, @NonNull String mimeType, @SecurityLevel int securityLevel) {
        return isCryptoSchemeSupportedNative(getByteArrayFromUUID(uuid), mimeType,
                securityLevel);
    }
    private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
        long msb = uuid.getMostSignificantBits();
        long lsb = uuid.getLeastSignificantBits();
        byte[] uuidBytes = new byte[16];
        for (int i = 0; i < 8; ++i) {
            uuidBytes[i] = (byte)(msb >>> (8 * (7 - i)));
            uuidBytes[8 + i] = (byte)(lsb >>> (8 * (7 - i)));
        }
        return uuidBytes;
    }
    private static final native boolean isCryptoSchemeSupportedNative(
            @NonNull byte[] uuid, @Nullable String mimeType, @SecurityLevel int securityLevel);
    private Handler createHandler() {
        Looper looper;
        Handler handler;
        if ((looper = Looper.myLooper()) != null) {
            handler = new Handler(looper);
        } else if ((looper = Looper.getMainLooper()) != null) {
            handler = new Handler(looper);
        } else {
            handler = null;
        }
        return handler;
    }
    /**
     * Instantiate a MediaDrm object
     *
     * @param uuid The UUID of the crypto scheme.
     *
     * @throws UnsupportedSchemeException if the device does not support the
     * specified scheme UUID
     */
    public MediaDrm(@NonNull UUID uuid) throws UnsupportedSchemeException {
        /* Native setup requires a weak reference to our object.
         * It's easier to create it here than in C++.
         */
        native_setup(new WeakReference<MediaDrm>(this),
                getByteArrayFromUUID(uuid),  ActivityThread.currentOpPackageName());
        mCloseGuard.open("release");
    }
    /**
     * Thrown when an unrecoverable failure occurs during a MediaDrm operation.
     * Extends java.lang.IllegalStateException with the addition of an error
     * code that may be useful in diagnosing the failure.
     */
    public static final class MediaDrmStateException extends java.lang.IllegalStateException {
        private final int mErrorCode;
        private final String mDiagnosticInfo;
        /**
         * @hide
         */
        public MediaDrmStateException(int errorCode, @Nullable String detailMessage) {
            super(detailMessage);
            mErrorCode = errorCode;
            // TODO get this from DRM session
            final String sign = errorCode < 0 ? "neg_" : "";
            mDiagnosticInfo =
                "android.media.MediaDrm.error_" + sign + Math.abs(errorCode);
        }
        /**
         * Retrieve the associated error code
         *
         * @hide
         */
        public int getErrorCode() {
            return mErrorCode;
        }
        /**
         * Retrieve a developer-readable diagnostic information string
         * associated with the exception. Do not show this to end-users,
         * since this string will not be localized or generally comprehensible
         * to end-users.
         */
        @NonNull
        public String getDiagnosticInfo() {
            return mDiagnosticInfo;
        }
    }
    /**
     * Thrown when an error occurs in any method that has a session context.
     */
    public static final class SessionException extends RuntimeException {
        public SessionException(int errorCode, @Nullable String detailMessage) {
            super(detailMessage);
            mErrorCode = errorCode;
        }
        /**
         * The SessionException has an unknown error code.
         */
        public static final int ERROR_UNKNOWN = 0;
        /**
         * This indicates that apps using MediaDrm sessions are
         * temporarily exceeding the capacity of available crypto
         * resources. The app should retry the operation later.
         */
        public static final int ERROR_RESOURCE_CONTENTION = 1;
        /** @hide */
        @IntDef({
            ERROR_RESOURCE_CONTENTION,
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface SessionErrorCode {}
        /**
         * Retrieve the error code associated with the SessionException
         */
        @SessionErrorCode
        public int getErrorCode() {
            return mErrorCode;
        }
        private final int mErrorCode;
    }
    /**
     * Register a callback to be invoked when a session expiration update
     * occurs.  The app's OnExpirationUpdateListener will be notified
     * when the expiration time of the keys in the session have changed.
     * @param listener the callback that will be run, or {@code null} to unregister the
     *     previously registered callback.
     * @param handler the handler on which the listener should be invoked, or
     *     {@code null} if the listener should be invoked on the calling thread's looper.
     */
    public void setOnExpirationUpdateListener(
            @Nullable OnExpirationUpdateListener listener, @Nullable Handler handler) {
        setListenerWithHandler(EXPIRATION_UPDATE, handler, listener,
                this::createOnExpirationUpdateListener);
    }
    /**
     * Register a callback to be invoked when a session expiration update
     * occurs.
     *
     * @see #setOnExpirationUpdateListener(OnExpirationUpdateListener, Handler)
     *
     * @param executor the executor through which the listener should be invoked
     * @param listener the callback that will be run.
     */
    public void setOnExpirationUpdateListener(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull OnExpirationUpdateListener listener) {
        setListenerWithExecutor(EXPIRATION_UPDATE, executor, listener,
                this::createOnExpirationUpdateListener);
    }
    /**
     * Clear the {@link OnExpirationUpdateListener}.
     */
    public void clearOnExpirationUpdateListener() {
        clearGenericListener(EXPIRATION_UPDATE);
    }
    /**
     * Interface definition for a callback to be invoked when a drm session
     * expiration update occurs
     */
    public interface OnExpirationUpdateListener
    {
        /**
         * Called when a session expiration update occurs, to inform the app
         * about the change in expiration time
         *
         * @param md the MediaDrm object on which the event occurred
         * @param sessionId the DRM session ID on which the event occurred
         * @param expirationTime the new expiration time for the keys in the session.
         *     The time is in milliseconds, relative to the Unix epoch.  A time of
         *     0 indicates that the keys never expire.
         */
        void onExpirationUpdate(
                @NonNull MediaDrm md, @NonNull byte[] sessionId, long expirationTime);
    }
    /**
     * Register a callback to be invoked when the state of keys in a session
     * change, e.g. when a license update occurs or when a license expires.
     *
     * @param listener the callback that will be run when key status changes, or
     *     {@code null} to unregister the previously registered callback.
     * @param handler the handler on which the listener should be invoked, or
     *     null if the listener should be invoked on the calling thread's looper.
     */
    public void setOnKeyStatusChangeListener(
            @Nullable OnKeyStatusChangeListener listener, @Nullable Handler handler) {
        setListenerWithHandler(KEY_STATUS_CHANGE, handler, listener,
                this::createOnKeyStatusChangeListener);
    }
    /**
     * Register a callback to be invoked when the state of keys in a session
     * change.
     *
     * @see #setOnKeyStatusChangeListener(OnKeyStatusChangeListener, Handler)
     *
     * @param listener the callback that will be run when key status changes.
     * @param executor the executor on which the listener should be invoked.
     */
    public void setOnKeyStatusChangeListener(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull OnKeyStatusChangeListener listener) {
        setListenerWithExecutor(KEY_STATUS_CHANGE, executor, listener,
                this::createOnKeyStatusChangeListener);
    }
    /**
     * Clear the {@link OnKeyStatusChangeListener}.
     */
    public void clearOnKeyStatusChangeListener() {
        clearGenericListener(KEY_STATUS_CHANGE);
    }
    /**
     * Interface definition for a callback to be invoked when the keys in a drm
     * session change states.
     */
    public interface OnKeyStatusChangeListener
    {
        /**
         * Called when the keys in a session change status, such as when the license
         * is renewed or expires.
         *
         * @param md the MediaDrm object on which the event occurred
         * @param sessionId the DRM session ID on which the event occurred
         * @param keyInformation a list of {@link MediaDrm.KeyStatus}
         *     instances indicating the status for each key in the session
         * @param hasNewUsableKey indicates if a key has been added that is usable,
         *     which may trigger an attempt to resume playback on the media stream
         *     if it is currently blocked waiting for a key.
         */
        void onKeyStatusChange(
                @NonNull MediaDrm md, @NonNull byte[] sessionId,
                @NonNull List<KeyStatus> keyInformation,
                boolean hasNewUsableKey);
    }
    /**
     * Register a callback to be invoked when session state has been
     * lost. This event can occur on devices that are not capable of
     * retaining crypto session state across device suspend/resume
     * cycles.  When this event occurs, the session must be closed and
     * a new session opened to resume operation.
     *
     * @param listener the callback that will be run, or {@code null} to unregister the
     *     previously registered callback.
     * @param handler the handler on which the listener should be invoked, or
     *     {@code null} if the listener should be invoked on the calling thread's looper.
     */
    public void setOnSessionLostStateListener(
            @Nullable OnSessionLostStateListener listener, @Nullable Handler handler) {
        setListenerWithHandler(SESSION_LOST_STATE, handler, listener,
                this::createOnSessionLostStateListener);
    }
    /**
     * Register a callback to be invoked when session state has been
     * lost.
     *
     * @see #setOnSessionLostStateListener(OnSessionLostStateListener, Handler)
     *
     * @param listener the callback that will be run.
     * @param executor the executor on which the listener should be invoked.
     */
    public void setOnSessionLostStateListener(
            @NonNull @CallbackExecutor Executor executor,
            @Nullable OnSessionLostStateListener listener) {
        setListenerWithExecutor(SESSION_LOST_STATE, executor, listener,
                this::createOnSessionLostStateListener);
    }
    /**
     * Clear the {@link OnSessionLostStateListener}.
     */
    public void clearOnSessionLostStateListener() {
        clearGenericListener(SESSION_LOST_STATE);
    }
    /**
     * Interface definition for a callback to be invoked when the
     * session state has been lost and is now invalid
     */
    public interface OnSessionLostStateListener
    {
        /**
         * Called when session state has lost state, to inform the app
         * about the condition so it can close the session and open a new
         * one to resume operation.
         *
         * @param md the MediaDrm object on which the event occurred
         * @param sessionId the DRM session ID on which the event occurred
         */
        void onSessionLostState(
                @NonNull MediaDrm md, @NonNull byte[] sessionId);
    }
    /**
     * Defines the status of a key.
     * A KeyStatus for each key in a session is provided to the
     * {@link OnKeyStatusChangeListener#onKeyStatusChange}
     * listener.
     */
    public static final class KeyStatus {
        private final byte[] mKeyId;
        private final int mStatusCode;
        /**
         * The key is currently usable to decrypt media data
         */
        public static final int STATUS_USABLE = 0;
        /**
         * The key is no longer usable to decrypt media data because its
         * expiration time has passed.
         */
        public static final int STATUS_EXPIRED = 1;
        /**
         * The key is not currently usable to decrypt media data because its
         * output requirements cannot currently be met.
         */
        public static final int STATUS_OUTPUT_NOT_ALLOWED = 2;
        /**
         * The status of the key is not yet known and is being determined.
         * The status will be updated with the actual status when it has
         * been determined.
         */
        public static final int STATUS_PENDING = 3;
        /**
         * The key is not currently usable to decrypt media data because of an
         * internal error in processing unrelated to input parameters.  This error
         * is not actionable by an app.
         */
        public static final int STATUS_INTERNAL_ERROR = 4;
        /**
         * The key is not yet usable to decrypt media because the start
         * time is in the future. The key will become usable when
         * its start time is reached.
         */
        public static final int STATUS_USABLE_IN_FUTURE = 5;
        /** @hide */
        @IntDef({
            STATUS_USABLE,
            STATUS_EXPIRED,
            STATUS_OUTPUT_NOT_ALLOWED,
            STATUS_PENDING,
            STATUS_INTERNAL_ERROR,
            STATUS_USABLE_IN_FUTURE,
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface KeyStatusCode {}
        KeyStatus(@NonNull byte[] keyId, @KeyStatusCode int statusCode) {
            mKeyId = keyId;
            mStatusCode = statusCode;
        }
        /**
         * Returns the status code for the key
         */
        @KeyStatusCode
        public int getStatusCode() { return mStatusCode; }
        /**
         * Returns the id for the key
         */
        @NonNull
        public byte[] getKeyId() { return mKeyId; }
    }
    /**
     * Register a callback to be invoked when an event occurs
     *
     * @see #setOnEventListener(OnEventListener, Handler)
     *
     * @param listener the callback that will be run.  Use {@code null} to
     *        stop receiving event callbacks.
     */
    public void setOnEventListener(@Nullable OnEventListener listener)
    {
        setOnEventListener(listener, null);
    }
    /**
     * Register a callback to be invoked when an event occurs
     *
     * @param listener the callback that will be run.  Use {@code null} to
     *        stop receiving event callbacks.
     * @param handler the handler on which the listener should be invoked, or
     *        null if the listener should be invoked on the calling thread's looper.
     */
    public void setOnEventListener(@Nullable OnEventListener listener, @Nullable Handler handler)
    {
        setListenerWithHandler(DRM_EVENT, handler, listener, this::createOnEventListener);
    }
    /**
     * Register a callback to be invoked when an event occurs
     *
     * @see #setOnEventListener(OnEventListener)
     *
     * @param executor the executor through which the listener should be invoked
     * @param listener the callback that will be run.
     */
    public void setOnEventListener(@NonNull @CallbackExecutor Executor executor,
            @NonNull OnEventListener listener) {
        setListenerWithExecutor(DRM_EVENT, executor, listener, this::createOnEventListener);
    }
    /**
     * Clear the {@link OnEventListener}.
     */
    public void clearOnEventListener() {
        clearGenericListener(DRM_EVENT);
    }
    /**
     * Interface definition for a callback to be invoked when a drm event
     * occurs
     */
    public interface OnEventListener
    {
        /**
         * Called when an event occurs that requires the app to be notified
         *
         * @param md the MediaDrm object on which the event occurred
         * @param sessionId the DRM session ID on which the event occurred,
         *        or {@code null} if there is no session ID associated with the event.
         * @param event indicates the event type
         * @param extra an secondary error code
         * @param data optional byte array of data that may be associated with the event
         */
        void onEvent(
                @NonNull MediaDrm md, @Nullable byte[] sessionId,
                @DrmEvent int event, int extra,
                @Nullable byte[] data);
    }
    /**
     * This event type indicates that the app needs to request a certificate from
     * the provisioning server.  The request message data is obtained using
     * {@link #getProvisionRequest}
     *
     * @deprecated Handle provisioning via {@link android.media.NotProvisionedException}
     * instead.
     */
    public static final int EVENT_PROVISION_REQUIRED = 1;
    /**
     * This event type indicates that the app needs to request keys from a license
     * server.  The request message data is obtained using {@link #getKeyRequest}.
     */
    public static final int EVENT_KEY_REQUIRED = 2;
    /**
     * This event type indicates that the licensed usage duration for keys in a session
     * has expired.  The keys are no longer valid.
     * @deprecated Use {@link OnKeyStatusChangeListener#onKeyStatusChange}
     * and check for {@link MediaDrm.KeyStatus#STATUS_EXPIRED} in the {@link MediaDrm.KeyStatus}
     * instead.
     */
    public static final int EVENT_KEY_EXPIRED = 3;
    /**
     * This event may indicate some specific vendor-defined condition, see your
     * DRM provider documentation for details
     */
    public static final int EVENT_VENDOR_DEFINED = 4;
    /**
     * This event indicates that a session opened by the app has been reclaimed by the resource
     * manager.
     */
    public static final int EVENT_SESSION_RECLAIMED = 5;
    /** @hide */
    @IntDef({
        EVENT_PROVISION_REQUIRED,
        EVENT_KEY_REQUIRED,
        EVENT_KEY_EXPIRED,
        EVENT_VENDOR_DEFINED,
        EVENT_SESSION_RECLAIMED,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface DrmEvent {}
    private static final int DRM_EVENT = 200;
    private static final int EXPIRATION_UPDATE = 201;
    private static final int KEY_STATUS_CHANGE = 202;
    private static final int SESSION_LOST_STATE = 203;
    // Use ConcurrentMap to support concurrent read/write to listener settings.
    // ListenerWithExecutor is immutable so we shouldn't need further locks.
    private final Map<Integer, ListenerWithExecutor> mListenerMap = new ConcurrentHashMap<>();
    // called by old-style set*Listener APIs using Handlers; listener & handler are Nullable
    private <T> void setListenerWithHandler(int what, Handler handler, T listener,
            Function<T, Consumer<ListenerArgs>> converter) {
        if (listener == null) {
            clearGenericListener(what);
        } else {
            handler = handler == null ? createHandler() : handler;
            final HandlerExecutor executor = new HandlerExecutor(handler);
            setGenericListener(what, executor, listener, converter);
        }
    }
    // called by new-style set*Listener APIs using Executors; listener & executor must be NonNull
    private <T> void setListenerWithExecutor(int what, Executor executor, T listener,
            Function<T, Consumer<ListenerArgs>> converter) {
        if (executor == null || listener == null) {
            final String errMsg = String.format("executor %s listener %s", executor, listener);
            throw new IllegalArgumentException(errMsg);
        }
        setGenericListener(what, executor, listener, converter);
    }
    private <T> void setGenericListener(int what, Executor executor, T listener,
            Function<T, Consumer<ListenerArgs>> converter) {
        mListenerMap.put(what, new ListenerWithExecutor(executor, converter.apply(listener)));
    }
    private void clearGenericListener(int what) {
        mListenerMap.remove(what);
    }
    private Consumer<ListenerArgs> createOnEventListener(OnEventListener listener) {
        return args -> {
            byte[] sessionId = args.parcel.createByteArray();
            if (sessionId.length == 0) {
                sessionId = null;
            }
            byte[] data = args.parcel.createByteArray();
            if (data.length == 0) {
                data = null;
            }
            Log.i(TAG, "Drm event (" + args.arg1 + "," + args.arg2 + ")");
            listener.onEvent(this, sessionId, args.arg1, args.arg2, data);
        };
    }
    private Consumer<ListenerArgs> createOnKeyStatusChangeListener(
            OnKeyStatusChangeListener listener) {
        return args -> {
            byte[] sessionId = args.parcel.createByteArray();
            if (sessionId.length > 0) {
                List<KeyStatus> keyStatusList = keyStatusListFromParcel(args.parcel);
                boolean hasNewUsableKey = (args.parcel.readInt() != 0);
                Log.i(TAG, "Drm key status changed");
                listener.onKeyStatusChange(this, sessionId, keyStatusList, hasNewUsableKey);
            }
        };
    }
    private Consumer<ListenerArgs> createOnExpirationUpdateListener(
            OnExpirationUpdateListener listener) {
        return args -> {
            byte[] sessionId = args.parcel.createByteArray();
            if (sessionId.length > 0) {
                long expirationTime = args.parcel.readLong();
                Log.i(TAG, "Drm key expiration update: " + expirationTime);
                listener.onExpirationUpdate(this, sessionId, expirationTime);
            }
        };
    }
    private Consumer<ListenerArgs> createOnSessionLostStateListener(
            OnSessionLostStateListener listener) {
        return args -> {
            byte[] sessionId = args.parcel.createByteArray();
            Log.i(TAG, "Drm session lost state event: ");
            listener.onSessionLostState(this, sessionId);
        };
    }
    private static class ListenerArgs {
        private final Parcel parcel;
        private final int arg1;
        private final int arg2;
        public ListenerArgs(Parcel parcel, int arg1, int arg2) {
            this.parcel = parcel;
            this.arg1 = arg1;
            this.arg2 = arg2;
        }
    }
    private static class ListenerWithExecutor {
        private final Consumer<ListenerArgs> mConsumer;
        private final Executor mExecutor;
        public ListenerWithExecutor(Executor executor, Consumer<ListenerArgs> consumer) {
            this.mExecutor = executor;
            this.mConsumer = consumer;
        }
    }
    /**
     * Parse a list of KeyStatus objects from an event parcel
     */
    @NonNull
    private List<KeyStatus> keyStatusListFromParcel(@NonNull Parcel parcel) {
        int nelems = parcel.readInt();
        List<KeyStatus> keyStatusList = new ArrayList(nelems);
        while (nelems-- > 0) {
            byte[] keyId = parcel.createByteArray();
            int keyStatusCode = parcel.readInt();
            keyStatusList.add(new KeyStatus(keyId, keyStatusCode));
        }
        return keyStatusList;
    }
    /**
     * This method is called from native code when an event occurs.  This method
     * just uses the EventHandler system to post the event back to the main app thread.
     * We use a weak reference to the original MediaPlayer object so that the native
     * code is safe from the object disappearing from underneath it.  (This is
     * the cookie passed to native_setup().)
     */
    private static void postEventFromNative(@NonNull Object mediadrm_ref,
            int what, int eventType, int extra, @Nullable Object obj)
    {
        MediaDrm md = (MediaDrm)((WeakReference<MediaDrm>)mediadrm_ref).get();
        if (md == null) {
            return;
        }
        switch (what) {
            case DRM_EVENT:
            case EXPIRATION_UPDATE:
            case KEY_STATUS_CHANGE:
            case SESSION_LOST_STATE:
                ListenerWithExecutor listener  = md.mListenerMap.get(what);
                if (listener != null) {
                    final Runnable command = () -> {
                        if (md.mNativeContext == 0) {
                            Log.w(TAG, "MediaDrm went away with unhandled events");
                            return;
                        }
                        if (obj != null && obj instanceof Parcel) {
                            Parcel p = (Parcel)obj;
                            listener.mConsumer.accept(new ListenerArgs(p, eventType, extra));
                        }
                    };
                    listener.mExecutor.execute(command);
                }
                break;
            default:
                Log.e(TAG, "Unknown message type " + what);
                break;
        }
    }
    /**
     * Open a new session with the MediaDrm object. A session ID is returned.
     * By default, sessions are opened at the native security level of the device.
     *
     * @throws NotProvisionedException if provisioning is needed
     * @throws ResourceBusyException if required resources are in use
     */
    @NonNull
    public byte[] openSession() throws NotProvisionedException,
            ResourceBusyException {
        return openSession(getMaxSecurityLevel());
    }
    /**
     * Open a new session at a requested security level. The security level
     * represents the robustness of the device's DRM implementation. By default,
     * sessions are opened at the native security level of the device.
     * Overriding the security level is necessary when the decrypted frames need
     * to be manipulated, such as for image compositing. The security level
     * parameter must be lower than the native level. Reducing the security
     * level will typically limit the content to lower resolutions, as
     * determined by the license policy. If the requested level is not
     * supported, the next lower supported security level will be set. The level
     * can be queried using {@link #getSecurityLevel}. A session
     * ID is returned.
     *
     * @param level the new security level
     * @throws NotProvisionedException if provisioning is needed
     * @throws ResourceBusyException if required resources are in use
     * @throws IllegalArgumentException if the requested security level is
     * higher than the native level or lower than the lowest supported level or
     * if the device does not support specifying the security level when opening
     * a session
     */
    @NonNull
    public native byte[] openSession(@SecurityLevel int level) throws
            NotProvisionedException, ResourceBusyException;
    /**
     * Close a session on the MediaDrm object that was previously opened
     * with {@link #openSession}.
     */
    public native void closeSession(@NonNull byte[] sessionId);
    /**
     * This key request type species that the keys will be for online use, they will
     * not be saved to the device for subsequent use when the device is not connected
     * to a network.
     */
    public static final int KEY_TYPE_STREAMING = 1;
    /**
     * This key request type specifies that the keys will be for offline use, they
     * will be saved to the device for use when the device is not connected to a network.
     */
    public static final int KEY_TYPE_OFFLINE = 2;
    /**
     * This key request type specifies that previously saved offline keys should be released.
     */
    public static final int KEY_TYPE_RELEASE = 3;
    /** @hide */
    @IntDef({
        KEY_TYPE_STREAMING,
        KEY_TYPE_OFFLINE,
        KEY_TYPE_RELEASE,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface KeyType {}
    /**
     * Contains the opaque data an app uses to request keys from a license server.
     * These request types may or may not be generated by a given plugin. Refer
     * to plugin vendor documentation for more information.
     */
    public static final class KeyRequest {
        private byte[] mData;
        private String mDefaultUrl;
        private int mRequestType;
        /**
         * Key request type is initial license request. A license request
         * is necessary to load keys.
         */
        public static final int REQUEST_TYPE_INITIAL = 0;
        /**
         * Key request type is license renewal. A license request is
         * necessary to prevent the keys from expiring.
         */
        public static final int REQUEST_TYPE_RENEWAL = 1;
        /**
         * Key request type is license release
         */
        public static final int REQUEST_TYPE_RELEASE = 2;
        /**
         * Keys are already loaded and are available for use. No license request is necessary, and
         * no key request data is returned.
         */
        public static final int REQUEST_TYPE_NONE = 3;
        /**
         * Keys have been loaded but an additional license request is needed
         * to update their values.
         */
        public static final int REQUEST_TYPE_UPDATE = 4;
        /** @hide */
        @IntDef({
            REQUEST_TYPE_INITIAL,
            REQUEST_TYPE_RENEWAL,
            REQUEST_TYPE_RELEASE,
            REQUEST_TYPE_NONE,
            REQUEST_TYPE_UPDATE,
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface RequestType {}
        KeyRequest() {}
        /**
         * Get the opaque message data
         */
        @NonNull
        public byte[] getData() {
            if (mData == null) {
                // this should never happen as mData is initialized in
                // JNI after construction of the KeyRequest object. The check
                // is needed here to guarantee @NonNull annotation.
                throw new RuntimeException("KeyRequest is not initialized");
            }
            return mData;
        }
        /**
         * Get the default URL to use when sending the key request message to a
         * server, if known.  The app may prefer to use a different license
         * server URL from other sources.
         * This method returns an empty string if the default URL is not known.
         */
        @NonNull
        public String getDefaultUrl() {
            if (mDefaultUrl == null) {
                // this should never happen as mDefaultUrl is initialized in
                // JNI after construction of the KeyRequest object. The check
                // is needed here to guarantee @NonNull annotation.
                throw new RuntimeException("KeyRequest is not initialized");
            }
            return mDefaultUrl;
        }
        /**
         * Get the type of the request
         */
        @RequestType
        public int getRequestType() { return mRequestType; }
    };
    /**
     * A key request/response exchange occurs between the app and a license server
     * to obtain or release keys used to decrypt encrypted content.
     * <p>
     * getKeyRequest() is used to obtain an opaque key request byte array that is
     * delivered to the license server.  The opaque key request byte array is returned
     * in KeyRequest.data.  The recommended URL to deliver the key request to is
     * returned in KeyRequest.defaultUrl.
     * <p>
     * After the app has received the key request response from the server,
     * it should deliver to the response to the MediaDrm instance using the method
     * {@link #provideKeyResponse}.
     *
     * @param scope may be a sessionId or a keySetId, depending on the specified keyType.
     * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE,
     * scope should be set to the sessionId the keys will be provided to.  When the keyType
     * is KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys
     * being released. Releasing keys from a device invalidates them for all sessions.
     * @param init container-specific data, its meaning is interpreted based on the
     * mime type provided in the mimeType parameter.  It could contain, for example,
     * the content ID, key ID or other data obtained from the content metadata that is
     * required in generating the key request. May be null when keyType is
     * KEY_TYPE_RELEASE or if the request is a renewal, i.e. not the first key
     * request for the session.
     * @param mimeType identifies the mime type of the content. May be null if the
     * keyType is KEY_TYPE_RELEASE or if the request is a renewal, i.e. not the
     * first key request for the session.
     * @param keyType specifes the type of the request. The request may be to acquire
     * keys for streaming or offline content, or to release previously acquired
     * keys, which are identified by a keySetId.
     * @param optionalParameters are included in the key request message to
     * allow a client application to provide additional message parameters to the server.
     * This may be {@code null} if no additional parameters are to be sent.
     * @throws NotProvisionedException if reprovisioning is needed, due to a
     * problem with the certifcate
     */
    @NonNull
    public native KeyRequest getKeyRequest(
            @NonNull byte[] scope, @Nullable byte[] init,
            @Nullable String mimeType, @KeyType int keyType,
            @Nullable HashMap<String, String> optionalParameters)
            throws NotProvisionedException;
    /**
     * A key response is received from the license server by the app, then it is
     * provided to the MediaDrm instance using provideKeyResponse.  When the
     * response is for an offline key request, a keySetId is returned that can be
     * used to later restore the keys to a new session with the method
     * {@link #restoreKeys}.
     * When the response is for a streaming or release request, an empty byte array
     * is returned.
     *
     * @param scope may be a sessionId or keySetId depending on the type of the
     * response.  Scope should be set to the sessionId when the response is for either
     * streaming or offline key requests.  Scope should be set to the keySetId when
     * the response is for a release request.
     * @param response the byte array response from the server
     * @return If the response is for an offline request, the keySetId for the offline
     * keys will be returned. If the response is for a streaming or release request
     * an empty byte array will be returned.
     *
     * @throws NotProvisionedException if the response indicates that
     * reprovisioning is required
     * @throws DeniedByServerException if the response indicates that the
     * server rejected the request
     */
    @Nullable
    public native byte[] provideKeyResponse(
            @NonNull byte[] scope, @NonNull byte[] response)
            throws NotProvisionedException, DeniedByServerException;
    /**
     * Restore persisted offline keys into a new session.  keySetId identifies the
     * keys to load, obtained from a prior call to {@link #provideKeyResponse}.
     *
     * @param sessionId the session ID for the DRM session
     * @param keySetId identifies the saved key set to restore
     */
    public native void restoreKeys(@NonNull byte[] sessionId, @NonNull byte[] keySetId);
    /**
     * Remove the current keys from a session.
     *
     * @param sessionId the session ID for the DRM session
     */
    public native void removeKeys(@NonNull byte[] sessionId);
    /**
     * Request an informative description of the key status for the session.  The status is
     * in the form of {name, value} pairs.  Since DRM license policies vary by vendor,
     * the specific status field names are determined by each DRM vendor.  Refer to your
     * DRM provider documentation for definitions of the field names for a particular
     * DRM plugin.
     *
     * @param sessionId the session ID for the DRM session
     */
    @NonNull
    public native HashMap<String, String> queryKeyStatus(@NonNull byte[] sessionId);
    /**
     * Contains the opaque data an app uses to request a certificate from a provisioning
     * server
     */
    public static final class ProvisionRequest {
        ProvisionRequest() {}
        /**
         * Get the opaque message data
         */
        @NonNull
        public byte[] getData() {
            if (mData == null) {
                // this should never happen as mData is initialized in
                // JNI after construction of the KeyRequest object. The check
                // is needed here to guarantee @NonNull annotation.
                throw new RuntimeException("ProvisionRequest is not initialized");
            }
            return mData;
        }
        /**
         * Get the default URL to use when sending the provision request
         * message to a server, if known. The app may prefer to use a different
         * provisioning server URL obtained from other sources.
         * This method returns an empty string if the default URL is not known.
         */
        @NonNull
        public String getDefaultUrl() {
            if (mDefaultUrl == null) {
                // this should never happen as mDefaultUrl is initialized in
                // JNI after construction of the ProvisionRequest object. The check
                // is needed here to guarantee @NonNull annotation.
                throw new RuntimeException("ProvisionRequest is not initialized");
            }
            return mDefaultUrl;
        }
        private byte[] mData;
        private String mDefaultUrl;
    }
    /**
     * A provision request/response exchange occurs between the app and a provisioning
     * server to retrieve a device certificate.  If provisionining is required, the
     * EVENT_PROVISION_REQUIRED event will be sent to the event handler.
     * getProvisionRequest is used to obtain the opaque provision request byte array that
     * should be delivered to the provisioning server. The provision request byte array
     * is returned in ProvisionRequest.data. The recommended URL to deliver the provision
     * request to is returned in ProvisionRequest.defaultUrl.
     */
    @NonNull
    public ProvisionRequest getProvisionRequest() {
        return getProvisionRequestNative(CERTIFICATE_TYPE_NONE, "");
    }
    @NonNull
    private native ProvisionRequest getProvisionRequestNative(int certType,
           @NonNull String certAuthority);
    /**
     * After a provision response is received by the app, it is provided to the
     * MediaDrm instance using this method.
     *
     * @param response the opaque provisioning response byte array to provide to the
     * MediaDrm instance.
     *
     * @throws DeniedByServerException if the response indicates that the
     * server rejected the request
     */
    public void provideProvisionResponse(@NonNull byte[] response)
            throws DeniedByServerException {
        provideProvisionResponseNative(response);
    }
    @NonNull
    private native Certificate provideProvisionResponseNative(@NonNull byte[] response)
            throws DeniedByServerException;
    /**
     * The keys in an offline license allow protected content to be played even
     * if the device is not connected to a network. Offline licenses are stored
     * on the device after a key request/response exchange when the key request
     * KeyType is OFFLINE. Normally each app is responsible for keeping track of
     * the keySetIds it has created. If an app loses the keySetId for any stored
     * licenses that it created, however, it must be able to recover the stored
     * keySetIds so those licenses can be removed when they expire or when the
     * app is uninstalled.
     * <p>
     * This method returns a list of the keySetIds for all offline licenses.
     * The offline license keySetId may be used to query the status of an
     * offline license with {@link #getOfflineLicenseState} or remove it with
     * {@link #removeOfflineLicense}.
     *
     * @return a list of offline license keySetIds
     */
    @NonNull
    public native List<byte[]> getOfflineLicenseKeySetIds();
    /**
     * Normally offline licenses are released using a key request/response
     * exchange using {@link #getKeyRequest} where the key type is
     * KEY_TYPE_RELEASE, followed by {@link #provideKeyResponse}. This allows
     * the server to cryptographically confirm that the license has been removed
     * and then adjust the count of offline licenses allocated to the device.
     * <p>
     * In some exceptional situations it may be necessary to directly remove
     * offline licenses without notifying the server, which may be performed
     * using this method.
     *
     * @param keySetId the id of the offline license to remove
     * @throws IllegalArgumentException if the keySetId does not refer to an
     * offline license.
     */
    public native void removeOfflineLicense(@NonNull byte[] keySetId);
    /**
     * Offline license state is unknown, an error occurred while trying
     * to access it.
     */
    public static final int OFFLINE_LICENSE_STATE_UNKNOWN = 0;
    /**
     * Offline license is usable, the keys may be used for decryption.
     */
    public static final int OFFLINE_LICENSE_STATE_USABLE = 1;
    /**
     * Offline license is released, the keys have been marked for
     * release using {@link #getKeyRequest} with KEY_TYPE_RELEASE but
     * the key response has not been received.
     */
    public static final int OFFLINE_LICENSE_STATE_RELEASED = 2;
    /** @hide */
    @IntDef({
        OFFLINE_LICENSE_STATE_UNKNOWN,
        OFFLINE_LICENSE_STATE_USABLE,
        OFFLINE_LICENSE_STATE_RELEASED,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface OfflineLicenseState {}
    /**
     * Request the state of an offline license. An offline license may be usable
     * or inactive. The keys in a usable offline license are available for
     * decryption. When the offline license state is inactive, the keys have
     * been marked for release using {@link #getKeyRequest} with
     * KEY_TYPE_RELEASE but the key response has not been received. The keys in
     * an inactive offline license are not usable for decryption.
     *
     * @param keySetId selects the offline license
     * @return the offline license state
     * @throws IllegalArgumentException if the keySetId does not refer to an
     * offline license.
     */
    @OfflineLicenseState
    public native int getOfflineLicenseState(@NonNull byte[] keySetId);
    /**
     * Secure stops are a way to enforce limits on the number of concurrent
     * streams per subscriber across devices. They provide secure monitoring of
     * the lifetime of content decryption keys in MediaDrm sessions.
     * <p>
     * A secure stop is written to secure persistent memory when keys are loaded
     * into a MediaDrm session. The secure stop state indicates that the keys
     * are available for use. When playback completes and the keys are removed
     * or the session is destroyed, the secure stop state is updated to indicate
     * that keys are no longer usable.
     * <p>
     * After playback, the app can query the secure stop and send it in a
     * message to the license server confirming that the keys are no longer
     * active. The license server returns a secure stop release response
     * message to the app which then deletes the secure stop from persistent
     * memory using {@link #releaseSecureStops}.
     * <p>
     * Each secure stop has a unique ID that can be used to identify it during
     * enumeration, access and removal.
     * @return a list of all secure stops from secure persistent memory
     */
    @NonNull
    public native List<byte[]> getSecureStops();
    /**
     * Return a list of all secure stop IDs currently in persistent memory.
     * The secure stop ID can be used to access or remove the corresponding
     * secure stop.
     *
     * @return a list of secure stop IDs
     */
    @NonNull
    public native List<byte[]> getSecureStopIds();
    /**
     * Access a specific secure stop given its secure stop ID.
     * Each secure stop has a unique ID.
     *
     * @param ssid the ID of the secure stop to return
     * @return the secure stop identified by ssid
     */
    @NonNull
    public native byte[] getSecureStop(@NonNull byte[] ssid);
    /**
     * Process the secure stop server response message ssRelease.  After
     * authenticating the message, remove the secure stops identified in the
     * response.
     *
     * @param ssRelease the server response indicating which secure stops to release
     */
    public native void releaseSecureStops(@NonNull byte[] ssRelease);
    /**
     * Remove a specific secure stop without requiring a secure stop release message
     * from the license server.
     * @param ssid the ID of the secure stop to remove
     */
    public native void removeSecureStop(@NonNull byte[] ssid);
    /**
     * Remove all secure stops without requiring a secure stop release message from
     * the license server.
     *
     * This method was added in API 28. In API versions 18 through 27,
     * {@link #releaseAllSecureStops} should be called instead. There is no need to
     * do anything for API versions prior to 18.
     */
    public native void removeAllSecureStops();
    /**
     * Remove all secure stops without requiring a secure stop release message from
     * the license server.
     *
     * @deprecated Remove all secure stops using {@link #removeAllSecureStops} instead.
     */
    public void releaseAllSecureStops() {
        removeAllSecureStops();;
    }
    /**
     * @deprecated Not of any use for application development;
     * please note that the related integer constants remain supported:
     * {@link #HDCP_LEVEL_UNKNOWN},
     * {@link #HDCP_NONE},
     * {@link #HDCP_V1},
     * {@link #HDCP_V2},
     * {@link #HDCP_V2_1},
     * {@link #HDCP_V2_2},
     * {@link #HDCP_V2_3}
     */
    @Deprecated
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({HDCP_LEVEL_UNKNOWN, HDCP_NONE, HDCP_V1, HDCP_V2,
                        HDCP_V2_1, HDCP_V2_2, HDCP_V2_3, HDCP_NO_DIGITAL_OUTPUT})
    public @interface HdcpLevel {}
    /**
     * The DRM plugin did not report an HDCP level, or an error
     * occurred accessing it
     */
    public static final int HDCP_LEVEL_UNKNOWN = 0;
    /**
     * HDCP is not supported on this device, content is unprotected
     */
    public static final int HDCP_NONE = 1;
    /**
     * HDCP version 1.0
     */
    public static final int HDCP_V1 = 2;
    /**
     * HDCP version 2.0 Type 1.
     */
    public static final int HDCP_V2 = 3;
    /**
     * HDCP version 2.1 Type 1.
     */
    public static final int HDCP_V2_1 = 4;
    /**
     *  HDCP version 2.2 Type 1.
     */
    public static final int HDCP_V2_2 = 5;
    /**
     *  HDCP version 2.3 Type 1.
     */
    public static final int HDCP_V2_3 = 6;
    /**
     * No digital output, implicitly secure
     */
    public static final int HDCP_NO_DIGITAL_OUTPUT = Integer.MAX_VALUE;
    /**
     * Return the HDCP level negotiated with downstream receivers the
     * device is connected to. If multiple HDCP-capable displays are
     * simultaneously connected to separate interfaces, this method
     * returns the lowest negotiated level of all interfaces.
     * <p>
     * This method should only be used for informational purposes, not for
     * enforcing compliance with HDCP requirements. Trusted enforcement of
     * HDCP policies must be handled by the DRM system.
     * <p>
     * @return the connected HDCP level
     */
    @HdcpLevel
    public native int getConnectedHdcpLevel();
    /**
     * Return the maximum supported HDCP level. The maximum HDCP level is a
     * constant for a given device, it does not depend on downstream receivers
     * that may be connected. If multiple HDCP-capable interfaces are present,
     * it indicates the highest of the maximum HDCP levels of all interfaces.
     * <p>
     * @return the maximum supported HDCP level
     */
    @HdcpLevel
    public native int getMaxHdcpLevel();
    /**
     * Return the number of MediaDrm sessions that are currently opened
     * simultaneously among all MediaDrm instances for the active DRM scheme.
     * @return the number of open sessions.
     */
    public native int getOpenSessionCount();
    /**
     * Return the maximum number of MediaDrm sessions that may be opened
     * simultaneosly among all MediaDrm instances for the active DRM
     * scheme. The maximum number of sessions is not affected by any
     * sessions that may have already been opened.
     * @return maximum sessions.
     */
    public native int getMaxSessionCount();
    /**
     * Security level indicates the robustness of the device's DRM
     * implementation.
     *
     * @deprecated Not of any use for application development;
     * please note that the related integer constants remain supported:
     * {@link #SECURITY_LEVEL_UNKNOWN},
     * {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO},
     * {@link #SECURITY_LEVEL_SW_SECURE_DECODE},
     * {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO},
     * {@link #SECURITY_LEVEL_HW_SECURE_DECODE},
     * {@link #SECURITY_LEVEL_HW_SECURE_ALL}
     */
    @Deprecated
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({SECURITY_LEVEL_UNKNOWN, SECURITY_LEVEL_SW_SECURE_CRYPTO,
            SECURITY_LEVEL_SW_SECURE_DECODE, SECURITY_LEVEL_HW_SECURE_CRYPTO,
            SECURITY_LEVEL_HW_SECURE_DECODE, SECURITY_LEVEL_HW_SECURE_ALL})
    public @interface SecurityLevel {}
    /**
     * The DRM plugin did not report a security level, or an error occurred
     * accessing it
     */
    public static final int SECURITY_LEVEL_UNKNOWN = 0;
    /**
     * DRM key management uses software-based whitebox crypto.
     */
    public static final int SECURITY_LEVEL_SW_SECURE_CRYPTO = 1;
    /**
     * DRM key management and decoding use software-based whitebox crypto.
     */
    public static final int SECURITY_LEVEL_SW_SECURE_DECODE = 2;
    /**
     * DRM key management and crypto operations are performed within a hardware
     * backed trusted execution environment.
     */
    public static final int SECURITY_LEVEL_HW_SECURE_CRYPTO = 3;
    /**
     * DRM key management, crypto operations and decoding of content are
     * performed within a hardware backed trusted execution environment.
     */
    public static final int SECURITY_LEVEL_HW_SECURE_DECODE = 4;
    /**
     * DRM key management, crypto operations, decoding of content and all
     * handling of the media (compressed and uncompressed) is handled within a
     * hardware backed trusted execution environment.
     */
    public static final int SECURITY_LEVEL_HW_SECURE_ALL = 5;
    /**
     * Indicates that the maximum security level supported by the device should
     * be used when opening a session. This is the default security level
     * selected when a session is opened.
     * @hide
     */
    public static final int SECURITY_LEVEL_MAX = 6;
    /**
     * Returns a value that may be passed as a parameter to {@link #openSession(int)}
     * requesting that the session be opened at the maximum security level of
     * the device.
     */
    public static final int getMaxSecurityLevel() {
        return SECURITY_LEVEL_MAX;
    }
    /**
     * Return the current security level of a session. A session has an initial
     * security level determined by the robustness of the DRM system's
     * implementation on the device. The security level may be changed at the
     * time a session is opened using {@link #openSession}.
     * @param sessionId the session to query.
     * <p>
     * @return the security level of the session
     */
    @SecurityLevel
    public native int getSecurityLevel(@NonNull byte[] sessionId);
    /**
     * String property name: identifies the maker of the DRM plugin
     */
    public static final String PROPERTY_VENDOR = "vendor";
    /**
     * String property name: identifies the version of the DRM plugin
     */
    public static final String PROPERTY_VERSION = "version";
    /**
     * String property name: describes the DRM plugin
     */
    public static final String PROPERTY_DESCRIPTION = "description";
    /**
     * String property name: a comma-separated list of cipher and mac algorithms
     * supported by CryptoSession.  The list may be empty if the DRM
     * plugin does not support CryptoSession operations.
     */
    public static final String PROPERTY_ALGORITHMS = "algorithms";
    /** @hide */
    @StringDef(prefix = { "PROPERTY_" }, value = {
        PROPERTY_VENDOR,
        PROPERTY_VERSION,
        PROPERTY_DESCRIPTION,
        PROPERTY_ALGORITHMS,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface StringProperty {}
    /**
     * Read a MediaDrm String property value, given the property name string.
     * <p>
     * Standard fields names are:
     * {@link #PROPERTY_VENDOR}, {@link #PROPERTY_VERSION},
     * {@link #PROPERTY_DESCRIPTION}, {@link #PROPERTY_ALGORITHMS}
     */
    @NonNull
    public native String getPropertyString(@NonNull String propertyName);
    /**
     * Set a MediaDrm String property value, given the property name string
     * and new value for the property.
     */
    public native void setPropertyString(@NonNull String propertyName,
            @NonNull String value);
    /**
     * Byte array property name: the device unique identifier is established during
     * device provisioning and provides a means of uniquely identifying each device.
     */
    public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
    /** @hide */
    @StringDef(prefix = { "PROPERTY_" }, value = {
        PROPERTY_DEVICE_UNIQUE_ID,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ArrayProperty {}
    /**
     * Read a MediaDrm byte array property value, given the property name string.
     * <p>
     * Standard fields names are {@link #PROPERTY_DEVICE_UNIQUE_ID}
     */
    @NonNull
    public native byte[] getPropertyByteArray(String propertyName);
    /**
    * Set a MediaDrm byte array property value, given the property name string
    * and new value for the property.
    */
    public native void setPropertyByteArray(
            @NonNull String propertyName, @NonNull byte[] value);
    private static final native void setCipherAlgorithmNative(
            @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
    private static final native void setMacAlgorithmNative(
            @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
    @NonNull
    private static final native byte[] encryptNative(
            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
            @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv);
    @NonNull
    private static final native byte[] decryptNative(
            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
            @NonNull byte[] keyId, @NonNull byte[] input, @NonNull byte[] iv);
    @NonNull
    private static final native byte[] signNative(
            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
            @NonNull byte[] keyId, @NonNull byte[] message);
    private static final native boolean verifyNative(
            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
            @NonNull byte[] keyId, @NonNull byte[] message, @NonNull byte[] signature);
    /**
     * Return Metrics data about the current MediaDrm instance.
     *
     * @return a {@link PersistableBundle} containing the set of attributes and values
     * available for this instance of MediaDrm.
     * The attributes are described in {@link MetricsConstants}.
     *
     * Additional vendor-specific fields may also be present in
     * the return value.
     */
    public PersistableBundle getMetrics() {
        PersistableBundle bundle = getMetricsNative();
        return bundle;
    }
    private native PersistableBundle getMetricsNative();
    /**
     * In addition to supporting decryption of DASH Common Encrypted Media, the
     * MediaDrm APIs provide the ability to securely deliver session keys from
     * an operator's session key server to a client device, based on the factory-installed
     * root of trust, and then perform encrypt, decrypt, sign and verify operations
     * with the session key on arbitrary user data.
     * <p>
     * The CryptoSession class implements generic encrypt/decrypt/sign/verify methods
     * based on the established session keys.  These keys are exchanged using the
     * getKeyRequest/provideKeyResponse methods.
     * <p>
     * Applications of this capability could include securing various types of
     * purchased or private content, such as applications, books and other media,
     * photos or media delivery protocols.
     * <p>
     * Operators can create session key servers that are functionally similar to a
     * license key server, except that instead of receiving license key requests and
     * providing encrypted content keys which are used specifically to decrypt A/V media
     * content, the session key server receives session key requests and provides
     * encrypted session keys which can be used for general purpose crypto operations.
     * <p>
     * A CryptoSession is obtained using {@link #getCryptoSession}
     */
    public final class CryptoSession {
        private byte[] mSessionId;
        CryptoSession(@NonNull byte[] sessionId,
                      @NonNull String cipherAlgorithm,
                      @NonNull String macAlgorithm)
        {
            mSessionId = sessionId;
            setCipherAlgorithmNative(MediaDrm.this, sessionId, cipherAlgorithm);
            setMacAlgorithmNative(MediaDrm.this, sessionId, macAlgorithm);
        }
        /**
         * Encrypt data using the CryptoSession's cipher algorithm
         *
         * @param keyid specifies which key to use
         * @param input the data to encrypt
         * @param iv the initialization vector to use for the cipher
         */
        @NonNull
        public byte[] encrypt(
                @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) {
            return encryptNative(MediaDrm.this, mSessionId, keyid, input, iv);
        }
        /**
         * Decrypt data using the CryptoSessions's cipher algorithm
         *
         * @param keyid specifies which key to use
         * @param input the data to encrypt
         * @param iv the initialization vector to use for the cipher
         */
        @NonNull
        public byte[] decrypt(
                @NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) {
            return decryptNative(MediaDrm.this, mSessionId, keyid, input, iv);
        }
        /**
         * Sign data using the CryptoSessions's mac algorithm.
         *
         * @param keyid specifies which key to use
         * @param message the data for which a signature is to be computed
         */
        @NonNull
        public byte[] sign(@NonNull byte[] keyid, @NonNull byte[] message) {
            return signNative(MediaDrm.this, mSessionId, keyid, message);
        }
        /**
         * Verify a signature using the CryptoSessions's mac algorithm. Return true
         * if the signatures match, false if they do no.
         *
         * @param keyid specifies which key to use
         * @param message the data to verify
         * @param signature the reference signature which will be compared with the
         *        computed signature
         */
        public boolean verify(
                @NonNull byte[] keyid, @NonNull byte[] message, @NonNull byte[] signature) {
            return verifyNative(MediaDrm.this, mSessionId, keyid, message, signature);
        }
    };
    /**
     * Obtain a CryptoSession object which can be used to encrypt, decrypt,
     * sign and verify messages or data using the session keys established
     * for the session using methods {@link #getKeyRequest} and
     * {@link #provideKeyResponse} using a session key server.
     *
     * @param sessionId the session ID for the session containing keys
     * to be used for encrypt, decrypt, sign and/or verify
     * @param cipherAlgorithm the algorithm to use for encryption and
     * decryption ciphers. The algorithm string conforms to JCA Standard
     * Names for Cipher Transforms and is case insensitive.  For example
     * "AES/CBC/NoPadding".
     * @param macAlgorithm the algorithm to use for sign and verify
     * The algorithm string conforms to JCA Standard Names for Mac
     * Algorithms and is case insensitive.  For example "HmacSHA256".
     * <p>
     * The list of supported algorithms for a DRM plugin can be obtained
     * using the method {@link #getPropertyString} with the property name
     * "algorithms".
     */
    public CryptoSession getCryptoSession(
            @NonNull byte[] sessionId,
            @NonNull String cipherAlgorithm, @NonNull String macAlgorithm)
    {
        return new CryptoSession(sessionId, cipherAlgorithm, macAlgorithm);
    }
    /**
     * Contains the opaque data an app uses to request a certificate from a provisioning
     * server
     *
     * @hide - not part of the public API at this time
     */
    public static final class CertificateRequest {
        private byte[] mData;
        private String mDefaultUrl;
        CertificateRequest(@NonNull byte[] data, @NonNull String defaultUrl) {
            mData = data;
            mDefaultUrl = defaultUrl;
        }
        /**
         * Get the opaque message data
         */
        @NonNull
        @UnsupportedAppUsage
        public byte[] getData() { return mData; }
        /**
         * Get the default URL to use when sending the certificate request
         * message to a server, if known. The app may prefer to use a different
         * certificate server URL obtained from other sources.
         */
        @NonNull
        @UnsupportedAppUsage
        public String getDefaultUrl() { return mDefaultUrl; }
    }
    /**
     * Generate a certificate request, specifying the certificate type
     * and authority. The response received should be passed to
     * provideCertificateResponse.
     *
     * @param certType Specifies the certificate type.
     *
     * @param certAuthority is passed to the certificate server to specify
     * the chain of authority.
     *
     * @hide - not part of the public API at this time
     */
    @NonNull
    @UnsupportedAppUsage
    public CertificateRequest getCertificateRequest(
            @CertificateType int certType, @NonNull String certAuthority)
    {
        ProvisionRequest provisionRequest = getProvisionRequestNative(certType, certAuthority);
        return new CertificateRequest(provisionRequest.getData(),
                provisionRequest.getDefaultUrl());
    }
    /**
     * Contains the wrapped private key and public certificate data associated
     * with a certificate.
     *
     * @hide - not part of the public API at this time
     */
    public static final class Certificate {
        Certificate() {}
        /**
         * Get the wrapped private key data
         */
        @NonNull
        @UnsupportedAppUsage
        public byte[] getWrappedPrivateKey() {
            if (mWrappedKey == null) {
                // this should never happen as mWrappedKey is initialized in
                // JNI after construction of the KeyRequest object. The check
                // is needed here to guarantee @NonNull annotation.
                throw new RuntimeException("Certificate is not initialized");
            }
            return mWrappedKey;
        }
        /**
         * Get the PEM-encoded certificate chain
         */
        @NonNull
        @UnsupportedAppUsage
        public byte[] getContent() {
            if (mCertificateData == null) {
                // this should never happen as mCertificateData is initialized in
                // JNI after construction of the KeyRequest object. The check
                // is needed here to guarantee @NonNull annotation.
                throw new RuntimeException("Certificate is not initialized");
            }
            return mCertificateData;
        }
        private byte[] mWrappedKey;
        private byte[] mCertificateData;
    }
    /**
     * Process a response from the certificate server.  The response
     * is obtained from an HTTP Post to the url provided by getCertificateRequest.
     * <p>
     * The public X509 certificate chain and wrapped private key are returned
     * in the returned Certificate objec.  The certificate chain is in PEM format.
     * The wrapped private key should be stored in application private
     * storage, and used when invoking the signRSA method.
     *
     * @param response the opaque certificate response byte array to provide to the
     * MediaDrm instance.
     *
     * @throws DeniedByServerException if the response indicates that the
     * server rejected the request
     *
     * @hide - not part of the public API at this time
     */
    @NonNull
    @UnsupportedAppUsage
    public Certificate provideCertificateResponse(@NonNull byte[] response)
            throws DeniedByServerException {
        return provideProvisionResponseNative(response);
    }
    @NonNull
    private static final native byte[] signRSANative(
            @NonNull MediaDrm drm, @NonNull byte[] sessionId,
            @NonNull String algorithm, @NonNull byte[] wrappedKey, @NonNull byte[] message);
    /**
     * Sign data using an RSA key
     *
     * @param sessionId a sessionId obtained from openSession on the MediaDrm object
     * @param algorithm the signing algorithm to use, e.g. "PKCS1-BlockType1"
     * @param wrappedKey - the wrapped (encrypted) RSA private key obtained
     * from provideCertificateResponse
     * @param message the data for which a signature is to be computed
     *
     * @hide - not part of the public API at this time
     */
    @NonNull
    @UnsupportedAppUsage
    public byte[] signRSA(
            @NonNull byte[] sessionId, @NonNull String algorithm,
            @NonNull byte[] wrappedKey, @NonNull byte[] message) {
        return signRSANative(this, sessionId, algorithm, wrappedKey, message);
    }
    @Override
    protected void finalize() throws Throwable {
        try {
            if (mCloseGuard != null) {
                mCloseGuard.warnIfOpen();
            }
            release();
        } finally {
            super.finalize();
        }
    }
    /**
     * Releases resources associated with the current session of
     * MediaDrm. It is considered good practice to call this method when
     * the {@link MediaDrm} object is no longer needed in your
     * application. After this method is called, {@link MediaDrm} is no
     * longer usable since it has lost all of its required resource.
     *
     * This method was added in API 28. In API versions 18 through 27, release()
     * should be called instead. There is no need to do anything for API
     * versions prior to 18.
     */
    @Override
    public void close() {
        release();
    }
    /**
     * @deprecated replaced by {@link #close()}.
     */
    @Deprecated
    public void release() {
        mCloseGuard.close();
        if (mClosed.compareAndSet(false, true)) {
            native_release();
        }
    }
    /** @hide */
    public native final void native_release();
    private static native final void native_init();
    private native final void native_setup(Object mediadrm_this, byte[] uuid,
            String appPackageName);
    static {
        System.loadLibrary("media_jni");
        native_init();
    }
    /**
     * Definitions for the metrics that are reported via the
     * {@link #getMetrics} call.
     */
    public final static class MetricsConstants
    {
        private MetricsConstants() {}
        /**
         * Key to extract the number of successful {@link #openSession} calls
         * from the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String OPEN_SESSION_OK_COUNT
            = "drm.mediadrm.open_session.ok.count";
        /**
         * Key to extract the number of failed {@link #openSession} calls
         * from the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String OPEN_SESSION_ERROR_COUNT
            = "drm.mediadrm.open_session.error.count";
        /**
         * Key to extract the list of error codes that were returned from
         * {@link #openSession} calls. The key is used to lookup the list
         * in the {@link PersistableBundle} returned by a {@link #getMetrics}
         * call.
         * The list is an array of Long values
         * ({@link android.os.BaseBundle#getLongArray}).
         */
        public static final String OPEN_SESSION_ERROR_LIST
            = "drm.mediadrm.open_session.error.list";
        /**
         * Key to extract the number of successful {@link #closeSession} calls
         * from the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String CLOSE_SESSION_OK_COUNT
            = "drm.mediadrm.close_session.ok.count";
        /**
         * Key to extract the number of failed {@link #closeSession} calls
         * from the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String CLOSE_SESSION_ERROR_COUNT
            = "drm.mediadrm.close_session.error.count";
        /**
         * Key to extract the list of error codes that were returned from
         * {@link #closeSession} calls. The key is used to lookup the list
         * in the {@link PersistableBundle} returned by a {@link #getMetrics}
         * call.
         * The list is an array of Long values
         * ({@link android.os.BaseBundle#getLongArray}).
         */
        public static final String CLOSE_SESSION_ERROR_LIST
            = "drm.mediadrm.close_session.error.list";
        /**
         * Key to extract the start times of sessions. Times are
         * represented as milliseconds since epoch (1970-01-01T00:00:00Z).
         * The start times are returned from the {@link PersistableBundle}
         * from a {@link #getMetrics} call.
         * The start times are returned as another {@link PersistableBundle}
         * containing the session ids as keys and the start times as long
         * values. Use {@link android.os.BaseBundle#keySet} to get the list of
         * session ids, and then {@link android.os.BaseBundle#getLong} to get
         * the start time for each session.
         */
        public static final String SESSION_START_TIMES_MS
            = "drm.mediadrm.session_start_times_ms";
        /**
         * Key to extract the end times of sessions. Times are
         * represented as milliseconds since epoch (1970-01-01T00:00:00Z).
         * The end times are returned from the {@link PersistableBundle}
         * from a {@link #getMetrics} call.
         * The end times are returned as another {@link PersistableBundle}
         * containing the session ids as keys and the end times as long
         * values. Use {@link android.os.BaseBundle#keySet} to get the list of
         * session ids, and then {@link android.os.BaseBundle#getLong} to get
         * the end time for each session.
         */
        public static final String SESSION_END_TIMES_MS
            = "drm.mediadrm.session_end_times_ms";
        /**
         * Key to extract the number of successful {@link #getKeyRequest} calls
         * from the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String GET_KEY_REQUEST_OK_COUNT
            = "drm.mediadrm.get_key_request.ok.count";
        /**
         * Key to extract the number of failed {@link #getKeyRequest}
         * calls from the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String GET_KEY_REQUEST_ERROR_COUNT
            = "drm.mediadrm.get_key_request.error.count";
        /**
         * Key to extract the list of error codes that were returned from
         * {@link #getKeyRequest} calls. The key is used to lookup the list
         * in the {@link PersistableBundle} returned by a {@link #getMetrics}
         * call.
         * The list is an array of Long values
         * ({@link android.os.BaseBundle#getLongArray}).
         */
        public static final String GET_KEY_REQUEST_ERROR_LIST
            = "drm.mediadrm.get_key_request.error.list";
        /**
         * Key to extract the average time in microseconds of calls to
         * {@link #getKeyRequest}. The value is retrieved from the
         * {@link PersistableBundle} returned from {@link #getMetrics}.
         * The time is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String GET_KEY_REQUEST_OK_TIME_MICROS
            = "drm.mediadrm.get_key_request.ok.average_time_micros";
        /**
         * Key to extract the number of successful {@link #provideKeyResponse}
         * calls from the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String PROVIDE_KEY_RESPONSE_OK_COUNT
            = "drm.mediadrm.provide_key_response.ok.count";
        /**
         * Key to extract the number of failed {@link #provideKeyResponse}
         * calls from the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String PROVIDE_KEY_RESPONSE_ERROR_COUNT
            = "drm.mediadrm.provide_key_response.error.count";
        /**
         * Key to extract the list of error codes that were returned from
         * {@link #provideKeyResponse} calls. The key is used to lookup the
         * list in the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The list is an array of Long values
         * ({@link android.os.BaseBundle#getLongArray}).
         */
        public static final String PROVIDE_KEY_RESPONSE_ERROR_LIST
            = "drm.mediadrm.provide_key_response.error.list";
        /**
         * Key to extract the average time in microseconds of calls to
         * {@link #provideKeyResponse}. The valus is retrieved from the
         * {@link PersistableBundle} returned from {@link #getMetrics}.
         * The time is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String PROVIDE_KEY_RESPONSE_OK_TIME_MICROS
            = "drm.mediadrm.provide_key_response.ok.average_time_micros";
        /**
         * Key to extract the number of successful {@link #getProvisionRequest}
         * calls from the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String GET_PROVISION_REQUEST_OK_COUNT
            = "drm.mediadrm.get_provision_request.ok.count";
        /**
         * Key to extract the number of failed {@link #getProvisionRequest}
         * calls from the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String GET_PROVISION_REQUEST_ERROR_COUNT
            = "drm.mediadrm.get_provision_request.error.count";
        /**
         * Key to extract the list of error codes that were returned from
         * {@link #getProvisionRequest} calls. The key is used to lookup the
         * list in the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The list is an array of Long values
         * ({@link android.os.BaseBundle#getLongArray}).
         */
        public static final String GET_PROVISION_REQUEST_ERROR_LIST
            = "drm.mediadrm.get_provision_request.error.list";
        /**
         * Key to extract the number of successful
         * {@link #provideProvisionResponse} calls from the
         * {@link PersistableBundle} returned by a {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String PROVIDE_PROVISION_RESPONSE_OK_COUNT
            = "drm.mediadrm.provide_provision_response.ok.count";
        /**
         * Key to extract the number of failed
         * {@link #provideProvisionResponse} calls from the
         * {@link PersistableBundle} returned by a {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String PROVIDE_PROVISION_RESPONSE_ERROR_COUNT
            = "drm.mediadrm.provide_provision_response.error.count";
        /**
         * Key to extract the list of error codes that were returned from
         * {@link #provideProvisionResponse} calls. The key is used to lookup
         * the list in the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The list is an array of Long values
         * ({@link android.os.BaseBundle#getLongArray}).
         */
        public static final String PROVIDE_PROVISION_RESPONSE_ERROR_LIST
            = "drm.mediadrm.provide_provision_response.error.list";
        /**
         * Key to extract the number of successful
         * {@link #getPropertyByteArray} calls were made with the
         * {@link #PROPERTY_DEVICE_UNIQUE_ID} value. The key is used to lookup
         * the value in the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String GET_DEVICE_UNIQUE_ID_OK_COUNT
            = "drm.mediadrm.get_device_unique_id.ok.count";
        /**
         * Key to extract the number of failed
         * {@link #getPropertyByteArray} calls were made with the
         * {@link #PROPERTY_DEVICE_UNIQUE_ID} value. The key is used to lookup
         * the value in the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String GET_DEVICE_UNIQUE_ID_ERROR_COUNT
            = "drm.mediadrm.get_device_unique_id.error.count";
        /**
         * Key to extract the list of error codes that were returned from
         * {@link #getPropertyByteArray} calls with the
         * {@link #PROPERTY_DEVICE_UNIQUE_ID} value. The key is used to lookup
         * the list in the {@link PersistableBundle} returned by a
         * {@link #getMetrics} call.
         * The list is an array of Long values
         * ({@link android.os.BaseBundle#getLongArray}).
         */
        public static final String GET_DEVICE_UNIQUE_ID_ERROR_LIST
            = "drm.mediadrm.get_device_unique_id.error.list";
        /**
         * Key to extraact the count of {@link KeyStatus#STATUS_EXPIRED} events
         * that occured. The count is extracted from the
         * {@link PersistableBundle} returned from a {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String KEY_STATUS_EXPIRED_COUNT
            = "drm.mediadrm.key_status.EXPIRED.count";
        /**
         * Key to extract the count of {@link KeyStatus#STATUS_INTERNAL_ERROR}
         * events that occured. The count is extracted from the
         * {@link PersistableBundle} returned from a {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String KEY_STATUS_INTERNAL_ERROR_COUNT
            = "drm.mediadrm.key_status.INTERNAL_ERROR.count";
        /**
         * Key to extract the count of
         * {@link KeyStatus#STATUS_OUTPUT_NOT_ALLOWED} events that occured.
         * The count is extracted from the
         * {@link PersistableBundle} returned from a {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String KEY_STATUS_OUTPUT_NOT_ALLOWED_COUNT
            = "drm.mediadrm.key_status_change.OUTPUT_NOT_ALLOWED.count";
        /**
         * Key to extract the count of {@link KeyStatus#STATUS_PENDING}
         * events that occured. The count is extracted from the
         * {@link PersistableBundle} returned from a {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String KEY_STATUS_PENDING_COUNT
            = "drm.mediadrm.key_status_change.PENDING.count";
        /**
         * Key to extract the count of {@link KeyStatus#STATUS_USABLE}
         * events that occured. The count is extracted from the
         * {@link PersistableBundle} returned from a {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String KEY_STATUS_USABLE_COUNT
            = "drm.mediadrm.key_status_change.USABLE.count";
        /**
         * Key to extract the count of {@link OnEventListener#onEvent}
         * calls of type PROVISION_REQUIRED occured. The count is
         * extracted from the {@link PersistableBundle} returned from a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String EVENT_PROVISION_REQUIRED_COUNT
            = "drm.mediadrm.event.PROVISION_REQUIRED.count";
        /**
         * Key to extract the count of {@link OnEventListener#onEvent}
         * calls of type KEY_NEEDED occured. The count is
         * extracted from the {@link PersistableBundle} returned from a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String EVENT_KEY_NEEDED_COUNT
            = "drm.mediadrm.event.KEY_NEEDED.count";
        /**
         * Key to extract the count of {@link OnEventListener#onEvent}
         * calls of type KEY_EXPIRED occured. The count is
         * extracted from the {@link PersistableBundle} returned from a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String EVENT_KEY_EXPIRED_COUNT
            = "drm.mediadrm.event.KEY_EXPIRED.count";
        /**
         * Key to extract the count of {@link OnEventListener#onEvent}
         * calls of type VENDOR_DEFINED. The count is
         * extracted from the {@link PersistableBundle} returned from a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String EVENT_VENDOR_DEFINED_COUNT
            = "drm.mediadrm.event.VENDOR_DEFINED.count";
        /**
         * Key to extract the count of {@link OnEventListener#onEvent}
         * calls of type SESSION_RECLAIMED. The count is
         * extracted from the {@link PersistableBundle} returned from a
         * {@link #getMetrics} call.
         * The count is a Long value ({@link android.os.BaseBundle#getLong}).
         */
        public static final String EVENT_SESSION_RECLAIMED_COUNT
            = "drm.mediadrm.event.SESSION_RECLAIMED.count";
    }
}
 
     |