Commit Graph

633 Commits

Author SHA1 Message Date
John W. Bruce
1f1ba94a61 Cache Max Subsample Size
(This is a merge of http://go/wvgerrit/95003.)

To reduce the number of OEMCrypto calls on the decrypt path, the maximum
subsample size will now be cached after the first call to retrieve it.

Bug: 150018606
Test: Android Unit Tests
Test: CE CDM Unit Tests
Test: ExoPlayer high-bitrate playback on OEC v15
Change-Id: I0b5d38d8a082c0a127d2a47f112b76c64085bddb
2020-03-04 12:20:58 -08:00
Alex Dale
fb5c67929f CDM handles mixed output warnings.
[ Merge of http://go/wvgerrit/94483 ]

With OEMCrypto V16 comes a new potential error code from calls to
DecryptCENC().  WARNING_MIXED_OUTPUT_PROTECTION may be returned by
supporting devices if one of the output devices does not meet the
required HDCP level for the decryption key/license; however the output
is instead restricted (by OEMCrypto) to devices that are secure. This
warning is informative to the CDM; but no action can/should be taken
by the CDM.

In addition, if DecryptCENC() returns an error/warning, it is likely
that the same status code will be returned on subsequent calls to
decrypt within the same crypto session.  To reduce the number of logs
the CDM produces within the same crypto session only changes in error
codes are logged.

Bug: 140825538
Change-Id: Iaf9da3f0c88889525f73f3153a5977c8416286bb
(cherry picked from commit d9c703ef9e)
Merged-In: Iaf9da3f0c88889525f73f3153a5977c8416286bb
2020-02-29 01:24:12 +00:00
John W. Bruce
a62886b925 Combined Decrypt Calls
(This is a merge of http://go/wvgerrit/93829,
http://go/wvgerrit/93830, http://go/wvgerrit/93832,
http://go/wvgerrit/93833, and http://go/wvgerrit/93834 from the
Widevine repo.)

This implements the CDM code changes necessary to take advantage of
Combined Decrypt Calls on OEMCrypto v16. The result of this is that
WVCryptoPlugin is much lighter now because it can pass the full sample
down to the core in one call, but CryptoSession is heavier, as it now
has to handle more complex fallback logic when devices can't handle
multiple subsamples at once.

This patch also removes support for the 'cens' and 'cbc1' schema, which
are being dropped in OEMCrypto v16. This fixes an overflow in the code
for handling those schemas by removing it entirely.

This patch also fixes the "in chunks" legacy decrypt path to use larger
chunk sizes on devices with higher resource rating tiers.

Bug: 135285640
Bug: 123435824
Bug: 138584971
Bug: 139257871
Bug: 78289910
Bug: 149361893
Test: no new CE CDM Unit Test failures
Test: Google Play plays
Test: Netflix plays
Test: no new GTS failures
Change-Id: Ic4952c9fa3bc7fd5ed08698e88254380a7a18514
2020-02-19 11:44:35 -08:00
Rahul Frias
3708c4d53f Merge changes I442b7919,Ie5b4ff75
* changes:
  Improve android MediaDrm property latency
  Delay OEMCrypto Termination
2020-02-19 15:51:14 +00:00
Rahul Frias
0e02d6a1f6 Merge changes Iacbbd51a,Id925ddcc
* changes:
  Add policy handling for v16
  More policy engine/timers refactoring
2020-02-19 15:37:35 +00:00
Rahul Frias
c033892f2a Add policy handling for v16
[ Merge of http://go/wvgerrit/93865 ]

This allows for handling of timer and clock values as supported when both
the license service and the OEMCrypto on the device support v16.
A flag based on a value in the SignedResponse license indicates
whether this support should be enabled. A new class PolicyTimerV16
performs the duration value evaluation.

Bug: 139372190
Test: Android WV unit/integration tests
Change-Id: Iacbbd51ad26c9f29cb5418ff832f8822982644b7
2020-02-18 22:33:47 -08:00
Rahul Frias
68587be8a0 More policy engine/timers refactoring
[ Merge of http://go/wvgerrit/93838 ]

Some more rework of policy engine/policy timers code to support
timer and clock value handling introduced by OEMCrypto v16.

Changes are
* renamed methods to include rental duration since policies for v16 use
  rental and playback duration for all licenses. Previously rental and
  playback durations enforced timing for persistent licenses and license
  duration was used for streaming licenses.
* Moved some common code to the base PolicyTimer class from
  PolicyTimerV15.
* Corrected data member naming (policy_timers -> policy_timers_)
* Updated comments

Bug: 139372190
Test: Android WV unit/integration tests
Change-Id: Id925ddcc14608a8500f30c2c68486d91608a9abe
2020-02-18 22:29:49 -08:00
Alex Dale
4a8aeb29ef LRU algorithm adapts to usage table capacity.
[ Merge of http://go/wvgerrit/93564 ]

OEMCrypto v16 introduced the ability to report the maximum possible
size of the usage table to the CDM.  The LRU algorithm will take the
table capacity into account when deciding which entry is removed.

Bug: 148795097
Bug: 135298906
Test: CDM unit tests
Change-Id: Ibba88813618c13a9bf1121e560b8cc02b1c7e7a6
2020-02-18 12:22:23 -08:00
Rahul Frias
78d2fa5e9e Improve android MediaDrm property latency
[ Merge of http://go/wvgerrit/89848 ]

Apps query a number of properties at initialization. The mediaDrm
API getProperty allows the query of a single property at a time.
This causes a series of requests. If no crypto
sessions are concurrently open, a series of expensive OEMCrypto
Initialization and Termination calls will occur.

In this change OEMCrypto termination is delayed. If an OEMCrypto
Terminate is followed in close succession by an Initialize, neither
will occur avoiding the overhead. A timer enables a countdown process.
If no session activity occurs, the timer will eventually terminate
OEMCrypto and exit.

Bug: 136282358
Test: Android unit/integration tests
Change-Id: I442b7919b4e7835c52583516c8bc64d0c150241d
2020-02-17 20:50:32 -08:00
Rahul Frias
068035140b Delay OEMCrypto Termination
[ Merge of http://go/wvgerrit/89847 ]

This adds a platform specific property that allows OEMCrypto Termination
calls to be delayed. On android this allows a way to avoid
expensive back to back OEMCrypto_Terminate followed by Initialize
calls.

Bug: 136282358
Test: wv unit/integration tests
Change-Id: Ie5b4ff7503dafe77d974caac9c52fc1f169dec89
2020-02-15 01:34:02 -08:00
Rahul Frias
c2cee8406e Policy Engine refactoring
[ Merge of http://go/wvgerrit/93743 ]

Reworks policy engine in preparation for changes to support timer and
clock value handling by OEMCrypto core messages in OEMCrypto v16.

No major functional changes have yet been introduced. Time and duration
evaluation has been devolved to a new policy timer class. Policy
specific to licenses that do not support OEMCrypto core messages
is handled by a Policy Timer V15 class. This ensures backward compatibility.

Backward compatibility may be needed if
 * OEMCrypto has not been upgraded to v16
 * Licenses were persisted before the device was upgraded to v16
 * License service does not yet support core messages

Some minor changes to when the current time was retrieved required
minor modification to test expectations.

Bug: 139372190
Test: Android unit/integration tests
Change-Id: I420fb181f656ed9a6bfe54f09e8b398c130d23da
2020-02-12 01:00:08 -08:00
Alex Dale
8977119d05 Small fixes leftover from V16 merge.
[ Merge of http://go/wvgerrit/93505 ]

During the merge process there were a few CL comments (ag/10122083)
that were not able to be addressed.  Most changes in the CL are
spelling / grammar corrections.

Bug: 148907684
Bug: 141247171
Test: CDM unit tests
Change-Id: I9a8648525bbe5ed319521ebf01741a958ab69ae2
2020-02-06 17:44:45 -08:00
Fred Gylys-Colwell
db2050dff1 OEMCrypto v16.2
Merge from Widevine repo of http://go/wvgerrit/93404

This is the unit tests, reference code, and documentation for
OEMCrypto v16.2. Backwards compatibility should work for a v15
OEMCrypto.

Some review comments will be addressed in future CLs.

Bug: 141247171
Test: Unit tests
Test: Media GTS tests on bonito
Change-Id: I9d427c07580e180c0a4cfdc4a68f538d351c0ddd
2020-02-04 20:33:16 -08:00
Fred Gylys-Colwell
7665614b2e OEMCrypto v16.1
Merge of http://go/wvgerrit/93404

This CL updates the Widevine CDM to support OEMCrypto v16.1

Test: Tested in 16.2 CL
Bug: 141247171
Change-Id: I69bd993500f6fb63bf6010c8b0250dc7acc3d71b
2020-02-03 14:45:32 -08:00
Alex Dale
061b0e7caf Merge CDM LRU change to Android.
[ Merge of http://go/wvgerrit/81903 ]
[ Merge of http://go/wvgerrit/87473 ]
[ Merge of http://go/wvgerrit/82568 ]
[ Merge of http://go/wvgerrit/87266 ]
[ Merge of http://go/wvgerrit/87474 ]
[ Merge of http://go/wvgerrit/87475 ]

Bug: 135046978
Test: GTS and Android unit tests
Change-Id: Iff2ff62cea21eeb36d7b56c8bb852fce8447ff89
2019-12-06 13:23:26 -08:00
Rahul Frias
09b16a0b17 Correct GetDecryptHashSupport signature
[ Merge of http://go/wvgerrit/89906 ]

The change allows the GetDecryptHashSupport method to return
an error.

Bug: 144851430
Test: WV android unit/integration tests
Change-Id: Ib3b95788adb21b5ed0daee51ad338f9674b04c3c
2019-12-06 01:28:25 -08:00
Alex Dale
176096a2d3 Merge "Removed a few more log prefixes in core/" 2019-11-27 21:04:53 +00:00
Alex Dale
093fd4718c Removed a few more log prefixes in core/
[ Merge of http://go/wvgerrit/89705 ]

Test: Android and Linux unit tests
Bug: 134460638
Change-Id: Ieb84296eefd00478260ebc42d242f9bef2d8c8d1
2019-11-26 13:19:28 -08:00
Rahul Frias
10eb4da035 Simplify parameter validation
[ Merge of http://go/wvgerrit/89888 ]

A macro validates parameters in CryptoSession and return a
specified error code. Some error codes have been retired and replaced with
the error code PARAMETER_NULL.

Bug: 136123217
Test: Android unit/integration tests
Change-Id: I6ecbad53e87cce04dfd9ea27861400e83044cf5e
2019-11-22 12:05:52 -08:00
Rahul Frias
af15f8c83d Send spoids in provisioning requests
[ Merge of http://go/wvgerrit/87283 ]

SPOIDs (Stable Per-Origin IDentifiers) were not correctly being
set during CdmEngine construction. This resulted in SPOID values not
being sent in provisioning requests. This caused the serial number in
the drm certificate to not be stable after a reprovision.
This behaviour appears to be true going back to O.

CdmEngine no longer takes a SPOID in the constructor since not all
callers use SPOIDs. A setter has been added in its place. Previously
spoid had a default argument to the constructor.

Bug: 142368328
Test: android unit/integration tests
Change-Id: I711346df609636ecf1475dc37873454a7ef000c0
2019-10-23 00:17:24 -07:00
Rahul Frias
0acde10d1a Add certificate_provisioning_unittest
[ Merge of http://go/wvgerrit/87964 ]

A preliminary test has been added, more to follow.

Bug: 142747616
Test: android unit tests
Change-Id: Ida8eb853c14f73f60f7bc354f14a02224c2ce66c
2019-10-17 18:03:00 -07:00
Rahul Frias
8769e12b01 Consolidate provisioning related protobuf parsing
[ Merge of http://go/wvgerrit/87905 ]

Protobuf parsing of the provisioning message has been centralized in
certificate_provisioning.cpp since it will be invoked from
multiple locations. This will also ease maintainability of the code.

Bug: 142731300
Test: android unit/integration tests
Change-Id: Idebf6b0145b317698559cac1cf18a3a0b98315ad
2019-10-16 18:43:30 -07:00
John W. Bruce
63b2ea86d9 Allow CE CDM to Create Sessions Without a Service Certificate
(This is a merge of http://go/wvgerrit/84510)

When the CE CDM 3.5 behavior around service certificates was originally
implemented, it allowed sessions to be created if a service certificate
had not yet been installed, in keeping with the EME spec. However, the
service certificate in use at session creation time was cached, and so
there was a bug where any sessions open before a service certificate was
installed would never be updated with any future service certificates.
The code also caused problems for Android. When it was merged to master,
it was fixed to simply not allow session creation on CE CDM without a
service certificate. However, this created an impedance mismatch between
the CE CDM and EME that has caused pain for Shaka Player Embedded,
Chrome, Chromecast, Fuchsia, and likely every partner that is trying to
implement a fully-compliant EME stack on top of CE CDM.

Removing the code that blocks session creation without a service
certificate is easy. Fixing the bug that motivated it is not. Removing
the caching is not possible because Android needs it for certain
behavior on its end. So instead, the CE CDM will have to iterate over
all open sessions and update their service certificates if the installed
service certificate changes.

Test: CE CDM Unit Tests
Test: Android Unit Tests
Bug: 111766009
Change-Id: I1bd70553e2209b823a6acdc221c0497a5f3181b2
2019-09-09 16:17:26 -07:00
Alex Dale
5bfdd515eb Fewer parameters for storing and retrieve license information.
[ Merge of http://go/wvgerrit/84990 ]

Storing and retrieving licenses from device files had required 15
parameters to the DeviceFiles methods.  Now, licenses information is
bundled together in a single struct `CdmLicenseData`, similar to
`CdmUsageData`.

Bug: 137882164
Test: Linux and Android unittest
Change-Id: I149b39573800e7c66681343b252b41341a8902f7
2019-09-03 11:58:27 -07:00
Alex Dale
ee56d93454 Creating a new RNG and replacing rand().
[ Merge of http://go/wvgerrit/84607 ]
[ Merge of http://go/wvgerrit/84608 ]

The primary goal is to replace the use of `rand()` with the random
number generators provided with the C++11 standard.

This simplified generator wraps some of the technical aspects of the
<random> library and provides an interface for uniformly distributed
integers.

As part of the `rand()` purge in the CDM, all uses of the C random int
function in `core()` have been removed.  Places that previously used
`rand()` now use `CdmRandom` facilities.

Test: Linux unittest and Android unittest
Bug: 130680365
Change-Id: Ica383870536ed462dbb80e630c2d66845e38b937
2019-08-26 14:39:50 -07:00
Alex Dale
ee995d5fae Replacing NULL with nullptr in core/
[ Merge of http://go/wvgerrit/84647 ]
[ Merge of http://go/wvgerrit/84648 ]

Replacing most instances of C's NULL with C++'s nullptr.  Also changed
how a NULL check is performed on smart pointers.  They provided an
implicit boolean operator for null checks, meaning the underlying
pointer does not need to be compared directly (as it was in some places
before).

Note that clang-format has performed additional changes to some of the
test files that have not yet been formatted.

Bug: 120602075
Test: Linux and Android unittests
Change-Id: I06ddebe34b0ea6dfecedb5527e7e808e32f5269a
2019-08-19 14:18:25 -07:00
Alex Dale
79a28e5ddb Changed UsageTableHeader::Shrink to tolerate over shrinking.
[ Merge of http://go/wvgerrit/83804 ]

There is a private helper method in `UsageTableHeader` which is used by
other methods to shrink the table by removing a specified number of
entries.

Prior to this change, if `Shrink` was called to remove more entries
than there are, it would: 1) do nothing and 2) return `NO_ERROR`.
Obviously, at least one of those action should change.

Instead of doing nothing, it will simply remove all the entries from
the table and return `NO_ERROR`.  A warning will be logged that it was
requested to shrink by more entries than there are.

Four (4) new tests have been created to ensure that `Shrink()` works as
expected.

Test: Linux unit tests
Bug: 138242127
Change-Id: Idedd922bd883d7ae1b84ce8ec1255fdce00c0948
2019-07-31 12:06:58 -07:00
John W. Bruce
510804a295 Add Missing Header to CDM Engine Metrics Decorator
(This is a merge of http://go/wvgerrit/83324)

Bug: 134116645
Test: Build
Change-Id: I813381437201b9e7842ba491c36d58c9be4efdda
2019-07-24 15:09:25 -07:00
Rahul Frias
57f2a7fe31 Address request license test failures
[ Merge of http://go/wvgerrit/81743 and http://go/ag/7747989 ]

This fixes some failures in tests. A platform property has been added
which controls whether an offline license can be restored if a release
request has been previously made. This behaviour was introduced by
CE CDM in b/113167010 but is not permitted for android.

The tests failures addressed are
* ProvisioningTestWithServiceCertificate
* ReleaseRetryOfflineKeyTest
* ReleaseRetryL3OfflineKeyTest
* ReleaseRetryL3OfflineKeySessionUsageDisable

Bug: 119428680
Bug: 133684744
Test: WV unit/integration tests
Change-Id: I5beacecea32f26c8a319a6d73a45cc36f04d8aa1
2019-06-28 14:10:01 -07:00
John W. Bruce
435f839f7e Support CE CDM in OEMCrypto Testbed
(This is a merge of http://go/wvgerrit/81628. Although it is primarily
to support a CE CDM feature, this patch touched shared code and so must
be merged.)

The problem that has long stopped the OEMCrypto Testbed from working
with the CE CDM build is that the OEMCrypto Testbed sometimes accesses
the storage via the normal filesystem APIs rather than the FileSystem
abstraction. Furthermore, when doing this, it assumes that FileSystem
abstraction is just a wrapper around direct filesystem access and thus
it should use the same paths in both kinds of filesystem access.
However, this is not true on the CE CDM where FileSystem wraps an opaque
key/value store.

This patch adds a property that allows a platform to indicate if its
FileSystem base path represents a real file system path and sets it
appropriately. ("true" for all platforms except CE CDM) It also adds
code to the OEMCrypto Testbed that makes use of this property to modify
its behavior. When running on a device where the FileSystem base path is
not a real file system path, it will instead use the directory of the
current executable as its base path when accessing the filesystem
directly.

Bug: 129311942
Test: CE CDM Build with Fake L1
Test: Android Build
Change-Id: Iadb3cc57d3bbc8ce0d49224b7df31c46bd5ea56c
2019-06-24 11:52:23 -07:00
Alex Dale
96c36fa25e Merge "Replace typedef with using statement in cdm/core/ files." 2019-06-10 15:44:00 +00:00
Alex Dale
ce0ea7b20b Replace typedef with using statement in cdm/core/ files.
[ Merge of http://go/wvgerrit/80805 ]

This change was used only where `typedef` was used for type aliasing,
and not for defining a new type (such as enums, structs, or function
pointer types).

Clang-format was used on the changed files.

Test: WV unit tests
Bug: 134437705
Change-Id: I730b9709a5ac773b3036daa79024caab665b3daa
2019-06-07 11:17:45 -07:00
Srujan Gaddam
dcf3f21289 Add query to CDM to get provisioning model
Bug: b/133855570
Tests: Android tests/CE CDM tests/Linux tests
Merge of http://go/wvgerrit/80163

Adds a query option to QueryStatus to get the provisioning model of the
OEMCrypto.

Change-Id: I1896984be6294a5ada9a97b63e6d9080297e92b0
2019-06-07 09:40:59 -07:00
Rahul Frias
47c2068d6a Reformat wv core/include files
[ Merge of http://go/wvgerrit/80483 ]

Clang-format has been run on files in core/include

Bug: 134365840
Test: WV unit/integration tests
Change-Id: I890127f23f30f0e63f826d3638521b4cc12fb995
2019-06-03 13:53:31 -07:00
Rahul Frias
6c9c4697f0 Merge "Return key request type none after key rotation" into qt-dev am: 999a466275 am: 4690da2bcb
am: 4c9a3a6f02

Change-Id: Iddcac699401f64d920fcb8ff73053410019ec028
2019-05-31 09:55:31 -07:00
Rahul Frias
3c9175fec5 Return key request type none after key rotation
[ Merge of http://go/wvgerrit/80084 ]

Corrected the key type being returned after entitled keys from
init data are loaded. Made test changes to validate.

Bug: 133903028
Test: WV unit/integration tests
Change-Id: Icb44587f5c3aba3b0facae6d83dc9344d6b60833
2019-05-29 19:03:43 -07:00
Robert Shih
fe8924f814 Add CdmKeyStatus kKeyStatusUsableInFuture am: 69c8f488ca am: 6a329e6c9a
am: e6ba3ec747

Change-Id: Iee17c92d9a3f3fd390e05bb0324ba04861236210
2019-05-16 20:07:33 -07:00
Robert Shih
69c8f488ca Add CdmKeyStatus kKeyStatusUsableInFuture
Bug: 116738851
Test: unit tests
Change-Id: I49728788e57905806b72e891f3654fbf0c8b1bc0
2019-05-15 15:16:05 -07:00
Rahul Frias
f1bb063682 Merge "Key Status should reflect key container security level" into qt-dev am: 80f5e283ad
am: 555d7cdc83

Change-Id: Ib3c97a96e8482fafc888d71780daa047cc6428ac
2019-05-13 09:49:24 -07:00
Rahul Frias
80f5e283ad Merge "Key Status should reflect key container security level" into qt-dev 2019-05-13 16:25:24 +00:00
Fred Gylys-Colwell
878e39c740 Report OEMCrypto Build Information in License Request am: 5ee5d00514
am: 23c3bc8ebc

Change-Id: I1006614f9eadc97e9287816723ec179851bf2e67
2019-05-10 06:30:12 -07:00
Rahul Frias
7d78609548 Key Status should reflect key container security level
[ Merge of http://go/wvgerrit/77506 ]

Callers of MediaDrm can register to be notified when key status
changes and if they are usable for decryption. A number of factors
are evaluated when making this determination. Key container security
level will now be included in the evaluation.

Bug: 78652608
Test: WV unit/integration test, GtsMediaDrmTest, Play movies playback
      testing.

Change-Id: I20243e5cb160f7957e3239e8d05f715ff0ee6dd6
2019-05-09 22:16:23 -07:00
Fred Gylys-Colwell
5ee5d00514 Report OEMCrypto Build Information in License Request
Merge from Widevine repo of http://go/wvgerrit/78623

This updates the license request client identification to include
OEMCrypto build information.

Bug: 129070445
Test: ExoPlayer on crosshatch with mod mock
Change-Id: I0dbce0cca4e9810e14f60561e4e434f1dbcadfb6
2019-05-09 13:24:04 -07:00
Rahul Frias
4bd6f2bed0 Merge "Handle key rotation" into qt-dev am: 862cb96e77
am: 5b8fc4a05b

Change-Id: I0bbf6b0e12cde87c768d1b6408b7ef05801d7a3d
2019-05-03 23:00:50 -07:00
Rahul Frias
2e2e92280e Handle key rotation
[ Merge of http://go/wvgerrit/77049 ]

Entitlement PSSHs can now be provided in follow on key generation
requests to cause keys to be rotated without needing a license
exchange.

Bug: 128462397
Test: WV unit/integration tests, Netflix and GPlay tests,
      GtsMediaDrmTests

Change-Id: I6ed0901a35c498240f42e405a522d82ea8dce2f7
2019-04-23 13:54:51 -07:00
John W. Bruce
125856238c Rename WvGenericOperationsTest.
(This is a merge of http://go/wvgerrit/76043)

This renames the tests to be *GenericCrypto* to match the OEMCrypto
tests and ensures these tests are skipped when the device doesn't
support generic crypto.

Test: Android Unit Tests
Bug: 126392281
Change-Id: Ib16ca8c57ed5d99dde802d4d9a63eced0fa807a5
2019-04-19 22:22:59 +00:00
Rahul Frias
43efc6d84a Corrections for limited duration licenses
[ Merge of http://go/wvgerrit/74627 ]

Limited duration licenses (where license duration is unlimited and
playback window is set to a small value) did not work correctly when
the initial decrypt call occured before the license was received.

Bug: 109653883
Test: WV unit/integration tests
Change-Id: I0738e97db525b239455e2ac93ab0bedd6611b311
2019-03-23 00:53:26 -07:00
John Bruce
907630a018 Merge "Fix unconditional use of __attribute__." 2019-03-19 18:47:19 +00:00
Jacob Trimble
86f6fb0ef7 Fix unconditional use of __attribute__.
(This is a merge of http://go/wvgerrit/74844)

__attribute__ is not supported on all compilers, notably it's not
supported on MSVC.  Commenting out the variable should suppress the
warning this is trying to fix.

Test: Android CDM Build
Bug: 122953649
Change-Id: I0101df1cca271415a6444e5455eb0085676bcabf
2019-03-18 16:16:26 -07:00
Robert Shih
d54634baa4 hidl: return BAD_VALUE from RemoveOfflineLicense when keyset is not found
Bug: 127828539
Test: VtsHalDrmV1_2TargetTest
Change-Id: Ia567be076a9460546c59c2a38231e18bb0549ce3
2019-03-18 15:24:22 -07:00