diff --git a/cas/aidl/Android.bp b/cas/aidl/Android.bp new file mode 100644 index 0000000000..0dfc0cefed --- /dev/null +++ b/cas/aidl/Android.bp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 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. + */ + +aidl_interface { + name: "android.hardware.cas", + vendor_available: true, + srcs: ["android/hardware/cas/*.aidl"], + stability: "vintf", + imports: ["android.hardware.common-V2"], + backend: { + cpp: { + enabled: false, + }, + java: { + sdk_version: "module_current", + }, + }, +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/AidlCasPluginDescriptor.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/AidlCasPluginDescriptor.aidl new file mode 100644 index 0000000000..7b8099f756 --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/AidlCasPluginDescriptor.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@VintfStability +parcelable AidlCasPluginDescriptor { + int caSystemId; + String name; +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/DestinationBuffer.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/DestinationBuffer.aidl new file mode 100644 index 0000000000..dd355afed0 --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/DestinationBuffer.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@VintfStability +union DestinationBuffer { + android.hardware.cas.SharedBuffer nonsecureMemory; + android.hardware.common.NativeHandle secureMemory; +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ICas.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ICas.aidl new file mode 100644 index 0000000000..e169beb4ff --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ICas.aidl @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@VintfStability +interface ICas { + void closeSession(in byte[] sessionId); + byte[] openSession(in android.hardware.cas.SessionIntent intent, in android.hardware.cas.ScramblingMode mode); + void processEcm(in byte[] sessionId, in byte[] ecm); + void processEmm(in byte[] emm); + void provision(in String provisionString); + void refreshEntitlements(in int refreshType, in byte[] refreshData); + void release(); + void sendEvent(in int event, in int arg, in byte[] eventData); + void sendSessionEvent(in byte[] sessionId, in int event, in int arg, in byte[] eventData); + void setPrivateData(in byte[] pvtData); + void setSessionPrivateData(in byte[] sessionId, in byte[] pvtData); +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ICasListener.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ICasListener.aidl new file mode 100644 index 0000000000..ebc13ce760 --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ICasListener.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@VintfStability +interface ICasListener { + void onEvent(in int event, in int arg, in byte[] data); + void onSessionEvent(in byte[] sessionId, in int event, in int arg, in byte[] data); + void onStatusUpdate(in android.hardware.cas.StatusEvent event, in int number); +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/IDescrambler.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/IDescrambler.aidl new file mode 100644 index 0000000000..9bf7903e59 --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/IDescrambler.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@VintfStability +interface IDescrambler { + int descramble(in android.hardware.cas.ScramblingControl scramblingControl, in android.hardware.cas.SubSample[] subSamples, in android.hardware.cas.SharedBuffer srcBuffer, in long srcOffset, in android.hardware.cas.DestinationBuffer dstBuffer, in long dstOffset); + void release(); + boolean requiresSecureDecoderComponent(in String mime); + void setMediaCasSession(in byte[] sessionId); +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/IMediaCasService.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/IMediaCasService.aidl new file mode 100644 index 0000000000..f5c80187b8 --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/IMediaCasService.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@VintfStability +interface IMediaCasService { + android.hardware.cas.IDescrambler createDescrambler(in int CA_system_id); + android.hardware.cas.ICas createPlugin(in int CA_system_id, in android.hardware.cas.ICasListener listener); + android.hardware.cas.AidlCasPluginDescriptor[] enumeratePlugins(); + boolean isDescramblerSupported(in int CA_system_id); + boolean isSystemIdSupported(in int CA_system_id); +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ScramblingControl.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ScramblingControl.aidl new file mode 100644 index 0000000000..d71d4be9d7 --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ScramblingControl.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@Backing(type="int") @VintfStability +enum ScramblingControl { + UNSCRAMBLED = 0, + RESERVED = 1, + EVENKEY = 2, + ODDKEY = 3, +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ScramblingMode.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ScramblingMode.aidl new file mode 100644 index 0000000000..e3923c7914 --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/ScramblingMode.aidl @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@Backing(type="int") @VintfStability +enum ScramblingMode { + RESERVED = 0, + DVB_CSA1 = 1, + DVB_CSA2 = 2, + DVB_CSA3_STANDARD = 3, + DVB_CSA3_MINIMAL = 4, + DVB_CSA3_ENHANCE = 5, + DVB_CISSA_V1 = 6, + DVB_IDSA = 7, + MULTI2 = 8, + AES128 = 9, + AES_CBC = 10, + AES_ECB = 11, + AES_SCTE52 = 12, + TDES_ECB = 13, + TDES_SCTE52 = 14, +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SessionIntent.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SessionIntent.aidl new file mode 100644 index 0000000000..af95f80033 --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SessionIntent.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@Backing(type="int") @VintfStability +enum SessionIntent { + LIVE = 0, + PLAYBACK = 1, + RECORD = 2, + TIMESHIFT = 3, +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SharedBuffer.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SharedBuffer.aidl new file mode 100644 index 0000000000..a18aa57424 --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SharedBuffer.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@VintfStability +parcelable SharedBuffer { + android.hardware.common.Ashmem heapBase; + long offset; + long size; +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/Status.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/Status.aidl new file mode 100644 index 0000000000..3d3a8a01f2 --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/Status.aidl @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@VintfStability +parcelable Status { + const int OK = 0; + const int ERROR_CAS_NO_LICENSE = -1; + const int ERROR_CAS_LICENSE_EXPIRED = -2; + const int ERROR_CAS_SESSION_NOT_OPENED = -3; + const int ERROR_CAS_CANNOT_HANDLE = -4; + const int ERROR_CAS_INVALID_STATE = -5; + const int BAD_VALUE = -6; + const int ERROR_CAS_NOT_PROVISIONED = -7; + const int ERROR_CAS_RESOURCE_BUSY = -8; + const int ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION = -9; + const int ERROR_CAS_TAMPER_DETECTED = -10; + const int ERROR_CAS_DEVICE_REVOKED = -11; + const int ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED = -12; + const int ERROR_CAS_DECRYPT = -13; + const int ERROR_CAS_UNKNOWN = -14; + const int ERROR_CAS_NEED_ACTIVATION = -15; + const int ERROR_CAS_NEED_PAIRING = -16; + const int ERROR_CAS_NO_CARD = -17; + const int ERROR_CAS_CARD_MUTE = -18; + const int ERROR_CAS_CARD_INVALID = -19; + const int ERROR_CAS_BLACKOUT = -20; + const int ERROR_CAS_REBOOTING = -21; +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/StatusEvent.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/StatusEvent.aidl new file mode 100644 index 0000000000..178cabc979 --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/StatusEvent.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@Backing(type="byte") @VintfStability +enum StatusEvent { + PLUGIN_PHYSICAL_MODULE_CHANGED = 0, + PLUGIN_SESSION_NUMBER_CHANGED = 1, +} diff --git a/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SubSample.aidl b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SubSample.aidl new file mode 100644 index 0000000000..d9ee3b4873 --- /dev/null +++ b/cas/aidl/aidl_api/android.hardware.cas/current/android/hardware/cas/SubSample.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m -update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.hardware.cas; +@VintfStability +parcelable SubSample { + int numBytesOfClearData; + int numBytesOfEncryptedData; +} diff --git a/cas/aidl/android/hardware/cas/AidlCasPluginDescriptor.aidl b/cas/aidl/android/hardware/cas/AidlCasPluginDescriptor.aidl new file mode 100644 index 0000000000..55b328a841 --- /dev/null +++ b/cas/aidl/android/hardware/cas/AidlCasPluginDescriptor.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +/** + * Describes a CAS plugin with its system ID and name. + */ +@VintfStability +parcelable AidlCasPluginDescriptor { + int caSystemId; + String name; +} diff --git a/cas/aidl/android/hardware/cas/DestinationBuffer.aidl b/cas/aidl/android/hardware/cas/DestinationBuffer.aidl new file mode 100644 index 0000000000..068f29d644 --- /dev/null +++ b/cas/aidl/android/hardware/cas/DestinationBuffer.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +import android.hardware.cas.SharedBuffer; +import android.hardware.common.NativeHandle; + +@VintfStability +union DestinationBuffer { + /** + * If type == SHARED_MEMORY, the descrambled data must be written + * to user-space non-secure shared memory. + */ + SharedBuffer nonsecureMemory; + + /** + * If type == NATIVE_HANDLE, the descrambled data must be written + * to secure memory referenced by the vendor's buffer allocator. + */ + NativeHandle secureMemory; +} diff --git a/cas/aidl/android/hardware/cas/ICas.aidl b/cas/aidl/android/hardware/cas/ICas.aidl new file mode 100644 index 0000000000..4c938c7b4a --- /dev/null +++ b/cas/aidl/android/hardware/cas/ICas.aidl @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +import android.hardware.cas.ScramblingMode; +import android.hardware.cas.SessionIntent; + +/** + * ICas is the API to control the CAS. It is used to manage sessions, provision/refresh the cas + * system, and process the EMM/ECM messages. It also allows bi-directional, scheme-specific + * communications between the client and the cas system. + */ +@VintfStability +interface ICas { + /** + * Close a session. + * + * @param sessionId The id of the session to be closed. + */ + void closeSession(in byte[] sessionId); + + /** + * Open a session to descramble one or more streams by specifying intention + * and scrambling mode. + * + * @param intent the intention of the session to be opened. + * @param mode the scrambling mode the session will use. + * + * @return sessionId The id of the newly opened session. + */ + byte[] openSession(in SessionIntent intent, in ScramblingMode mode); + + /** + * Process an ECM from the ECM stream for this session’s elementary stream. + * + * @param sessionId the id of the session which the ecm data applies to. + * @param ecm a byte array containing the ecm data. + */ + void processEcm(in byte[] sessionId, in byte[] ecm); + + /** + * Process an in-band EMM from the EMM stream. + * + * @param emm a byte array containing the emm data. + */ + void processEmm(in byte[] emm); + + /** + * Initiate a provisioning operation for a CA system. + * + * @param provisionString string containing information needed for the + * provisioning operation, the format of which is scheme and implementation + * specific. + */ + void provision(in String provisionString); + + /** + * Notify the CA system to refresh entitlement keys. + * + * @param refreshType the type of the refreshment. + * @param refreshData private data associated with the refreshment. + */ + void refreshEntitlements(in int refreshType, in byte[] refreshData); + + /** + * Release the descrambler instance. + */ + void release(); + + /** + * Send an scheme-specific event to the CasPlugin. + * + * @param event an integer denoting a scheme-specific event to be sent. + * @param arg a scheme-specific integer argument for the event. + * @param data a byte array containing scheme-specific data for the event. + */ + void sendEvent(in int event, in int arg, in byte[] eventData); + + /** + * Send an scheme-specific session event to the CasPlugin. + * + * @param sessionId the id of an opened session. + * @param event an integer denoting a scheme-specific event to be sent. + * @param arg a scheme-specific integer argument for the event. + * @param data a byte array containing scheme-specific data for the event. + */ + void sendSessionEvent(in byte[] sessionId, in int event, in int arg, in byte[] eventData); + + /** + * Provide the CA private data from a CA_descriptor in the conditional + * access table to a CasPlugin. + * + * @param pvtData a byte array containing the private data, the format of + * which is scheme-specific and opaque to the framework. + */ + void setPrivateData(in byte[] pvtData); + + /** + * Provide the CA private data from a CA_descriptor in the program map + * table to a session. + * + * @param sessionId the id of the session which the private data applies to. + * @param pvtData a byte array containing the private data, the format of + * which is scheme-specific and opaque to the framework. + */ + void setSessionPrivateData(in byte[] sessionId, in byte[] pvtData); +} diff --git a/cas/aidl/android/hardware/cas/ICasListener.aidl b/cas/aidl/android/hardware/cas/ICasListener.aidl new file mode 100644 index 0000000000..32d843f6d6 --- /dev/null +++ b/cas/aidl/android/hardware/cas/ICasListener.aidl @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +import android.hardware.cas.StatusEvent; + +@VintfStability +interface ICasListener { + /** + * Notify the listener of a scheme-specific event from the CA system. + * + * @param event an integer whose meaning is scheme-specific. + * @param arg an integer whose meaning is scheme-specific. + * @param data a byte array of data whose format and meaning are + * scheme-specific. + */ + void onEvent(in int event, in int arg, in byte[] data); + + /** + * Notify the listener of a scheme-specific session event from CA system. + * + * @param sessionId the id of an opened session. + * @param event an integer whose meaning is scheme-specific. + * @param arg an integer whose meaning is scheme-specific. + * @param data a byte array of data whose format and meaning are + * scheme-specific. + */ + void onSessionEvent(in byte[] sessionId, in int event, in int arg, in byte[] data); + + /** + * Notify the listener that the status of CAS system has changed. + * + * @param event the event type of status change. + * @param number value for status event. + * For PLUGIN_PHYSICAL_MODULE_CHANGED event: + * the positive number presents how many plugins are inserted; + * the negative number presents how many plugins are removed. + * Client must enumerate plugins after receive the event. + * For PLUGIN_SESSION_NUMBER_CHANGED event: + * the number presents how many sessions are supported + * in the plugin. + */ + void onStatusUpdate(in StatusEvent event, in int number); +} diff --git a/cas/aidl/android/hardware/cas/IDescrambler.aidl b/cas/aidl/android/hardware/cas/IDescrambler.aidl new file mode 100644 index 0000000000..33fbe75483 --- /dev/null +++ b/cas/aidl/android/hardware/cas/IDescrambler.aidl @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +import android.hardware.cas.DestinationBuffer; +import android.hardware.cas.ScramblingControl; +import android.hardware.cas.SharedBuffer; +import android.hardware.cas.SubSample; + +/** + * IDescrambler is the API to control the descrambling operations. + */ +@VintfStability +interface IDescrambler { + /** + * Descramble the data in a source SharedBuffer, described by an array of + * SubSample structures. + * + * @param scramblingControl an enumeration indicating the key that the subsamples + * were scrambled with. + * @param subSamples an array of SubSample structures describing the number of + * clear and scrambled bytes within each subsample. + * @param srcBuffer the SharedBuffer containing the source scrambled data. + * @param srcOffset the position where the source scrambled data starts at. + * @param dstBuffer the DestinationBuffer to hold the descrambled data. + * @param dstOffset the position where the descrambled data should start at. + * + * @return bytesWritten Number of bytes that have been successfully written. + */ + int descramble(in ScramblingControl scramblingControl, in SubSample[] subSamples, + in SharedBuffer srcBuffer, in long srcOffset, in DestinationBuffer dstBuffer, + in long dstOffset); + + /** + * Release the descrambler instance. + */ + void release(); + + /** + * Query if the scrambling scheme requires the use of a secure decoder + * to decode data of the given mime type. + * + * @param mime the mime type of the media data. + * @return whether the descrambler requires a secure decoder. + */ + boolean requiresSecureDecoderComponent(in String mime); + + /** + * Associate a MediaCas session with this MediaDescrambler instance. + * + * @param sessionId the id of the session to associate with this descrambler instance. + */ + void setMediaCasSession(in byte[] sessionId); +} diff --git a/cas/aidl/android/hardware/cas/IMediaCasService.aidl b/cas/aidl/android/hardware/cas/IMediaCasService.aidl new file mode 100644 index 0000000000..8bc31f65f4 --- /dev/null +++ b/cas/aidl/android/hardware/cas/IMediaCasService.aidl @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +import android.hardware.cas.AidlCasPluginDescriptor; +import android.hardware.cas.ICas; +import android.hardware.cas.ICasListener; +import android.hardware.cas.IDescrambler; + +/** + * IMediaCasService is the main entry point for interacting with a vendor's + * cas HAL to create cas and descrambler plugin instances. A cas plugin instance + * opens cas sessions which are used to obtain keys for a descrambler session, + * which can in turn be used to descramble protected video content. + */ +@VintfStability +interface IMediaCasService { + /** + * Construct a new instance of a DescramblerPlugin given a CA_system_id. + * + * @param CA_system_id the id of the CA system. + * @return the newly created plugin interface. + */ + IDescrambler createDescrambler(in int CA_system_id); + + /** + * Construct a new instance of a CasPlugin given a CA_system_id. + * + * @param CA_system_id the id of the CA system. + * @param listener the event listener to receive events coming from the CasPlugin. + * @return the newly created CasPlugin interface. + */ + ICas createPlugin(in int CA_system_id, in ICasListener listener); + + /** + * List all available CA systems on the device. + * + * @return an array of descriptors for the available CA systems. + */ + AidlCasPluginDescriptor[] enumeratePlugins(); + + /** + * Query if the descrambling scheme for a CA system is supported on this device. + * + * @param CA_system_id the id of the CA system. + * @return whether the specified descrambling scheme is supported on this device. + */ + boolean isDescramblerSupported(in int CA_system_id); + + /** + * Query if a certain CA system is supported on this device. + * + * @param CA_system_id the id of the CA system. + * @return whether the specified CA system is supported on this device. + */ + boolean isSystemIdSupported(in int CA_system_id); +} diff --git a/cas/aidl/android/hardware/cas/ScramblingControl.aidl b/cas/aidl/android/hardware/cas/ScramblingControl.aidl new file mode 100644 index 0000000000..b36787c348 --- /dev/null +++ b/cas/aidl/android/hardware/cas/ScramblingControl.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +/** + * Enumerates the keys used to scramble the content. + */ +@VintfStability +@Backing(type="int") +enum ScramblingControl { + UNSCRAMBLED = 0, + RESERVED = 1, + EVENKEY = 2, + ODDKEY = 3, +} diff --git a/cas/aidl/android/hardware/cas/ScramblingMode.aidl b/cas/aidl/android/hardware/cas/ScramblingMode.aidl new file mode 100644 index 0000000000..9d73eba08e --- /dev/null +++ b/cas/aidl/android/hardware/cas/ScramblingMode.aidl @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +/** + * The Scrambling Mode. + */ +@VintfStability +@Backing(type="int") +enum ScramblingMode { + RESERVED = 0, + + /** + * DVB (Digital Video Broadcasting) CSA1 (Common Scrambling Algorithm 1) is + * the default mode and shall be used when the scrambling descriptor + * is not present in the program map section. DVB scrambling mode is + * specified in ETSI EN 300 468 specification. + */ + DVB_CSA1, + + DVB_CSA2, + + /** + * DVB-CSA3 in standard mode. + */ + DVB_CSA3_STANDARD, + + /** + * DVB-CSA3 in minimally enhanced mode. + */ + DVB_CSA3_MINIMAL, + + /** + * DVB-CSA3 in fully enhanced mode. + */ + DVB_CSA3_ENHANCE, + + /** + * DVB-CISSA version 1. + */ + DVB_CISSA_V1, + + /** + * ATIS-0800006 IIF Default Scrambling Algorithm (IDSA). + */ + DVB_IDSA, + + /** + * a symmetric key algorithm. + */ + MULTI2, + + /** + * Advanced Encryption System (AES) 128-bit Encryption mode. + */ + AES128, + + /** + * Advanced Encryption System (AES) Cipher Block Chaining (CBC) mode. + */ + AES_CBC, + + /** + * Advanced Encryption System (AES) Electronic Code Book (ECB) mode. + */ + AES_ECB, + + /** + * Advanced Encryption System (AES) Society of Cable Telecommunications + * Engineers (SCTE) 52 mode. + */ + AES_SCTE52, + + /** + * Triple Data Encryption Algorithm (TDES) Electronic Code Book (ECB) mode. + */ + TDES_ECB, + + /** + * Triple Data Encryption Algorithm (TDES) Society of Cable Telecommunications + * Engineers (SCTE) 52 mode. + */ + TDES_SCTE52, +} diff --git a/cas/aidl/android/hardware/cas/SessionIntent.aidl b/cas/aidl/android/hardware/cas/SessionIntent.aidl new file mode 100644 index 0000000000..844deaba29 --- /dev/null +++ b/cas/aidl/android/hardware/cas/SessionIntent.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +/** + * The intented usage for the session. + */ +@VintfStability +@Backing(type="int") +enum SessionIntent { + /** + * Live Stream. + */ + LIVE, + + /** + * Playback Recorded Stream. + */ + PLAYBACK, + + /** + * Record Live Stream. + */ + RECORD, + + /** + * View the content with Time Shift capability + */ + TIMESHIFT, +} diff --git a/cas/aidl/android/hardware/cas/SharedBuffer.aidl b/cas/aidl/android/hardware/cas/SharedBuffer.aidl new file mode 100644 index 0000000000..8a94ff7f00 --- /dev/null +++ b/cas/aidl/android/hardware/cas/SharedBuffer.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +import android.hardware.common.Ashmem; + +/** + * SharedBuffer describes a shared buffer which is defined by a heapBase, an + * offset and a size. The offset is relative to the shared memory base for the + * memory region identified by heapBase. + */ +@VintfStability +parcelable SharedBuffer { + /** + * Ashmem shared memory + */ + Ashmem heapBase; + + /** + * The offset from the shared memory base + */ + long offset; + + /** + * The size of the shared buffer in bytes + */ + long size; +} diff --git a/cas/aidl/android/hardware/cas/Status.aidl b/cas/aidl/android/hardware/cas/Status.aidl new file mode 100644 index 0000000000..b2be34b02f --- /dev/null +++ b/cas/aidl/android/hardware/cas/Status.aidl @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +@VintfStability +parcelable Status { + /** + * The CAS plugin must return OK when an operation completes without any + * errors. + */ + const int OK = 0; + + /** + * The CAS plugin must return ERROR_CAS_NO_LICENSE, when descrambling is + * attempted and no license keys have been provided. + */ + const int ERROR_CAS_NO_LICENSE = -1; + + /** + * ERROR_CAS_LICENSE_EXPIRED must be returned when an attempt is made + * to use a license and the keys in that license have expired. + */ + const int ERROR_CAS_LICENSE_EXPIRED = -2; + + /** + * The CAS plugin must return ERROR_CAS_SESSION_NOT_OPENED when an + * attempt is made to use a session that has not been opened. + */ + const int ERROR_CAS_SESSION_NOT_OPENED = -3; + + /** + * The CAS plugin must return ERROR_CAS_CANNOT_HANDLE when an unsupported + * data format or operation is attempted. + */ + const int ERROR_CAS_CANNOT_HANDLE = -4; + + /** + * ERROR_CAS_INVALID_STATE must be returned when the device is in a state + * where it is not able to perform descrambling. + */ + const int ERROR_CAS_INVALID_STATE = -5; + + /** + * The CAS plugin must return BAD_VALUE whenever an illegal parameter is + * passed to one of the interface functions. + */ + const int BAD_VALUE = -6; + + /** + * The CAS plugin must return ERROR_CAS_NOT_PROVISIONED when the device + * has not yet been provisioned. + */ + const int ERROR_CAS_NOT_PROVISIONED = -7; + + /** + * ERROR_CAS_RESOURCE_BUSY must be returned when resources, such as CAS + * sessions or secure buffers are not available to perform a requested + * operation because they are already in use. + */ + const int ERROR_CAS_RESOURCE_BUSY = -8; + + /** + * The CAS Plugin must return ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION + * when the output protection level enabled on the device is not + * sufficient to meet the requirements in the license policy. HDCP is an + * example of a form of output protection. + */ + const int ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION = -9; + + /** + * The CAS Plugin must return ERROR_CAS_TAMPER_DETECTED if an attempt to + * tamper with the CAS system is detected. + */ + const int ERROR_CAS_TAMPER_DETECTED = -10; + + /** + * The CAS Plugin must return ERROR_CAS_DEVICE_REVOKED if the response + * indicates that the device has been revoked. Device revocation means + * that the device is no longer permitted to play content. + */ + const int ERROR_CAS_DEVICE_REVOKED = -11; + + /** + * The CAS plugin must return ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED when + * descrambling is failing because the session is not initialized properly. + */ + const int ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED = -12; + + /** + * The CAS Plugin must return ERROR_CAS_DECRYPT if the DescramblerPlugin's + * descramble operation fails. + */ + const int ERROR_CAS_DECRYPT = -13; + + /** + * ERROR_CAS_UNKNOWN must be returned when a fatal failure occurs and no + * other defined error is appropriate. + */ + const int ERROR_CAS_UNKNOWN = -14; + + /** + * ERROR_CAS_NEED_ACTIVATION is used to trigger device activation process. + */ + const int ERROR_CAS_NEED_ACTIVATION = -15; + + /** + * ERROR_CAS_NEED_PAIRING is used to trigger pairing process. + */ + const int ERROR_CAS_NEED_PAIRING = -16; + + /** + * ERROR_CAS_NO_CARD is used to report no smart card for descrambling. + */ + const int ERROR_CAS_NO_CARD = -17; + + /** + * ERROR_CAS_CARD_MUTE is used to report smart card is muted for + * descrambling. + */ + const int ERROR_CAS_CARD_MUTE = -18; + + /** + * ERROR_CAS_CARD_INVALID is used to report smart card isn't valid. + */ + const int ERROR_CAS_CARD_INVALID = -19; + + /** + * ERROR_CAS_BLACKOUT is used to report geographical blackout. + */ + const int ERROR_CAS_BLACKOUT = -20; + + /** + * ERROR_CAS_REBOOTING is used to report CAS is during rebooting. + */ + const int ERROR_CAS_REBOOTING = -21; +} diff --git a/cas/aidl/android/hardware/cas/StatusEvent.aidl b/cas/aidl/android/hardware/cas/StatusEvent.aidl new file mode 100644 index 0000000000..0f6263443f --- /dev/null +++ b/cas/aidl/android/hardware/cas/StatusEvent.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +/** + * The Event Type for status change. + */ +@VintfStability +@Backing(type="byte") +enum StatusEvent { + /** + * The status of CAS plugin was changed due to physical module insertion or + * removal. Client must call enumeratePlugins to update plugins' status. + */ + PLUGIN_PHYSICAL_MODULE_CHANGED, + + /** + * The status of supported session number was changed due to physical module + * insertion or removal. Client must update session resource according to + * latest StatusMessage from the StatusEvent. The plugin supports unlimited + * session by default. + */ + PLUGIN_SESSION_NUMBER_CHANGED, +} diff --git a/cas/aidl/android/hardware/cas/SubSample.aidl b/cas/aidl/android/hardware/cas/SubSample.aidl new file mode 100644 index 0000000000..c1353cbb0e --- /dev/null +++ b/cas/aidl/android/hardware/cas/SubSample.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 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.hardware.cas; + +/** + * A subsample consists of some number of bytes of clear (unscrambled) + * data followed by a number of bytes of scrambled data. + */ +@VintfStability +parcelable SubSample { + int numBytesOfClearData; + int numBytesOfEncryptedData; +} diff --git a/cas/aidl/default/Android.bp b/cas/aidl/default/Android.bp new file mode 100755 index 0000000000..3c16d577fb --- /dev/null +++ b/cas/aidl/default/Android.bp @@ -0,0 +1,93 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +cc_library_static { + name: "libcasexampleimpl", + vendor_available: true, + + srcs: [ + "CasImpl.cpp", + "DescramblerImpl.cpp", + "MediaCasService.cpp", + "SharedLibrary.cpp", + "TypeConvert.cpp", + ], + + shared_libs: [ + "android.hardware.cas-V1-ndk", + "libbinder_ndk", + "liblog", + "libutils", + "libcutils", + ], + static_libs: [ + "libaidlcommonsupport", + ], + header_libs: [ + "libstagefright_foundation_headers", + "media_plugin_headers", + ], +} + +cc_defaults { + name: "cas_service_example_defaults", + vendor: true, + relative_install_path: "hw", + + srcs: ["service.cpp"], + + static_libs: [ + "libaidlcommonsupport", + "libcasexampleimpl", + ], + shared_libs: [ + "android.hardware.cas-V1-ndk", + "libbinder_ndk", + "liblog", + "libutils", + "libcutils", + ], + header_libs: ["media_plugin_headers"], + vintf_fragments: ["android.hardware.cas-service.xml"], +} + +cc_binary { + name: "android.hardware.cas-service.example", + defaults: ["cas_service_example_defaults"], + init_rc: ["cas-default.rc"], +} + +cc_binary { + name: "android.hardware.cas-service.example-lazy", + defaults: ["cas_service_example_defaults"], + init_rc: ["cas-default-lazy.rc"], + cflags: ["-DLAZY_SERVICE"], +} + +cc_fuzz { + name: "android.hardware.cas-service_fuzzer", + vendor: true, + + defaults: ["service_fuzzer_defaults"], + srcs: ["fuzzer.cpp"], + + shared_libs: [ + "android.hardware.cas-V1-ndk", + "libcutils", + "liblog", + ], + static_libs: [ + "libaidlcommonsupport", + "libcasexampleimpl", + ], + header_libs: ["media_plugin_headers"], + fuzz_config: { + componentid: 1344, + }, +} diff --git a/cas/aidl/default/CasImpl.cpp b/cas/aidl/default/CasImpl.cpp new file mode 100755 index 0000000000..2d31b352bb --- /dev/null +++ b/cas/aidl/default/CasImpl.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + icensed 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. + */ + +#define LOG_TAG "android.hardware.cas-CasImpl" + +#include +#include + +#include "CasImpl.h" +#include "TypeConvert.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace cas { + +CasImpl::CasImpl(const shared_ptr& listener) : mListener(listener) { + ALOGV("CTOR"); +} + +CasImpl::~CasImpl() { + ALOGV("DTOR"); + release(); +} + +// static +void CasImpl::OnEvent(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size) { + if (appData == NULL) { + ALOGE("Invalid appData!"); + return; + } + CasImpl* casImpl = static_cast(appData); + casImpl->onEvent(event, arg, data, size); +} + +// static +void CasImpl::CallBackExt(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size, + const CasSessionId* sessionId) { + if (appData == NULL) { + ALOGE("Invalid appData!"); + return; + } + CasImpl* casImpl = static_cast(appData); + casImpl->onEvent(sessionId, event, arg, data, size); +} + +// static +void CasImpl::StatusUpdate(void* appData, int32_t event, int32_t arg) { + if (appData == NULL) { + ALOGE("Invalid appData!"); + return; + } + CasImpl* casImpl = static_cast(appData); + casImpl->onStatusUpdate(event, arg); +} + +void CasImpl::init(CasPlugin* plugin) { + shared_ptr holder(plugin); + atomic_store(&mPluginHolder, holder); +} + +void CasImpl::onEvent(int32_t event, int32_t arg, uint8_t* data, size_t size) { + if (mListener == NULL) { + return; + } + + vector eventData; + if (data != NULL) { + eventData.assign(data, data + size); + } + + mListener->onEvent(event, arg, eventData); +} + +void CasImpl::onEvent(const CasSessionId* sessionId, int32_t event, int32_t arg, uint8_t* data, + size_t size) { + if (mListener == NULL) { + return; + } + + vector eventData; + if (data != NULL) { + eventData.assign(data, data + size); + } + + if (sessionId != NULL) { + mListener->onSessionEvent(*sessionId, event, arg, eventData); + } else { + mListener->onEvent(event, arg, eventData); + } +} + +void CasImpl::onStatusUpdate(int32_t event, int32_t arg) { + if (mListener == NULL) { + return; + } + mListener->onStatusUpdate(static_cast(event), arg); +} + +ScopedAStatus CasImpl::setPluginStatusUpdateCallback() { + ALOGV("%s", __FUNCTION__); + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + return toStatus(INVALID_OPERATION); + } + return toStatus(holder->setStatusCallback(&CasImpl::StatusUpdate)); +} + +ScopedAStatus CasImpl::setPrivateData(const vector& pvtData) { + ALOGV("%s", __FUNCTION__); + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + return toStatus(INVALID_OPERATION); + } + return toStatus(holder->setPrivateData(pvtData)); +} + +ScopedAStatus CasImpl::openSession(SessionIntent intent, ScramblingMode mode, + vector* sessionId) { + ALOGV("%s", __FUNCTION__); + + shared_ptr holder = atomic_load(&mPluginHolder); + status_t err = INVALID_OPERATION; + if (holder.get() != nullptr) { + err = holder->openSession(static_cast(intent), static_cast(mode), + sessionId); + holder.reset(); + } + + return toStatus(err); +} + +ScopedAStatus CasImpl::setSessionPrivateData(const vector& sessionId, + const vector& pvtData) { + ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string()); + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + return toStatus(INVALID_OPERATION); + } + return toStatus(holder->setSessionPrivateData(sessionId, pvtData)); +} + +ScopedAStatus CasImpl::closeSession(const vector& sessionId) { + ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string()); + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + return toStatus(INVALID_OPERATION); + } + return toStatus(holder->closeSession(sessionId)); +} + +ScopedAStatus CasImpl::processEcm(const vector& sessionId, const vector& ecm) { + ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(sessionId).string()); + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + return toStatus(INVALID_OPERATION); + } + + return toStatus(holder->processEcm(sessionId, ecm)); +} + +ScopedAStatus CasImpl::processEmm(const vector& emm) { + ALOGV("%s", __FUNCTION__); + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + return toStatus(INVALID_OPERATION); + } + + return toStatus(holder->processEmm(emm)); +} + +ScopedAStatus CasImpl::sendEvent(int32_t event, int32_t arg, const vector& eventData) { + ALOGV("%s", __FUNCTION__); + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + return toStatus(INVALID_OPERATION); + } + + status_t err = holder->sendEvent(event, arg, eventData); + return toStatus(err); +} + +ScopedAStatus CasImpl::sendSessionEvent(const vector& sessionId, int32_t event, + int32_t arg, const vector& eventData) { + ALOGV("%s", __FUNCTION__); + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + return toStatus(INVALID_OPERATION); + } + + status_t err = holder->sendSessionEvent(sessionId, event, arg, eventData); + return toStatus(err); +} + +ScopedAStatus CasImpl::provision(const string& provisionString) { + ALOGV("%s: provisionString=%s", __FUNCTION__, provisionString.c_str()); + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + return toStatus(INVALID_OPERATION); + } + + return toStatus(holder->provision(String8(provisionString.c_str()))); +} + +ScopedAStatus CasImpl::refreshEntitlements(int32_t refreshType, + const vector& refreshData) { + ALOGV("%s", __FUNCTION__); + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + return toStatus(INVALID_OPERATION); + } + + status_t err = holder->refreshEntitlements(refreshType, refreshData); + return toStatus(err); +} + +ScopedAStatus CasImpl::release() { + ALOGV("%s: plugin=%p", __FUNCTION__, mPluginHolder.get()); + + shared_ptr holder(nullptr); + atomic_store(&mPluginHolder, holder); + + return ScopedAStatus::ok(); +} + +} // namespace cas +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/cas/aidl/default/CasImpl.h b/cas/aidl/default/CasImpl.h new file mode 100755 index 0000000000..84a8115c2f --- /dev/null +++ b/cas/aidl/default/CasImpl.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 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. + */ + +#include +#include +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace cas { + +using namespace ::android; +using namespace std; +using ndk::ScopedAStatus; + +class CasImpl : public BnCas { + public: + CasImpl(const shared_ptr& listener); + virtual ~CasImpl(); + + static void OnEvent(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size); + + static void CallBackExt(void* appData, int32_t event, int32_t arg, uint8_t* data, size_t size, + const CasSessionId* sessionId); + + static void StatusUpdate(void* appData, int32_t event, int32_t arg); + + void init(CasPlugin* plugin); + void onEvent(int32_t event, int32_t arg, uint8_t* data, size_t size); + + void onEvent(const CasSessionId* sessionId, int32_t event, int32_t arg, uint8_t* data, + size_t size); + + void onStatusUpdate(int32_t event, int32_t arg); + + // ICas inherits + + ScopedAStatus setPluginStatusUpdateCallback(); + + virtual ScopedAStatus setPrivateData(const vector& pvtData) override; + + virtual ScopedAStatus openSession(SessionIntent intent, ScramblingMode mode, + vector* sessionId) override; + + virtual ScopedAStatus closeSession(const vector& sessionId) override; + + virtual ScopedAStatus setSessionPrivateData(const vector& sessionId, + const vector& pvtData) override; + + virtual ScopedAStatus processEcm(const vector& sessionId, + const vector& ecm) override; + + virtual ScopedAStatus processEmm(const vector& emm) override; + + virtual ScopedAStatus sendEvent(int32_t event, int32_t arg, + const vector& eventData) override; + + virtual ScopedAStatus sendSessionEvent(const vector& sessionId, int32_t event, + int32_t arg, const vector& eventData) override; + + virtual ScopedAStatus provision(const string& provisionString) override; + + virtual ScopedAStatus refreshEntitlements(int32_t refreshType, + const vector& refreshData) override; + + virtual ScopedAStatus release() override; + + private: + struct PluginHolder; + shared_ptr mPluginHolder; + shared_ptr mListener; + + DISALLOW_EVIL_CONSTRUCTORS(CasImpl); +}; + +} // namespace cas +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/cas/aidl/default/DescramblerImpl.cpp b/cas/aidl/default/DescramblerImpl.cpp new file mode 100755 index 0000000000..a96fd466bc --- /dev/null +++ b/cas/aidl/default/DescramblerImpl.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2022 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. + */ + +#define LOG_TAG "android.hardware.cas-DescramblerImpl" + +#include +#include +#include +#include +#include +#include +#include + +#include "DescramblerImpl.h" +#include "TypeConvert.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace cas { + +#define CHECK_SUBSAMPLE_DEF(type) \ + static_assert(sizeof(SubSample) == sizeof(type::SubSample), "SubSample: size doesn't match"); \ + static_assert(offsetof(SubSample, numBytesOfClearData) == \ + offsetof(type::SubSample, mNumBytesOfClearData), \ + "SubSample: numBytesOfClearData offset doesn't match"); \ + static_assert(offsetof(SubSample, numBytesOfEncryptedData) == \ + offsetof(type::SubSample, mNumBytesOfEncryptedData), \ + "SubSample: numBytesOfEncryptedData offset doesn't match") + +CHECK_SUBSAMPLE_DEF(DescramblerPlugin); +CHECK_SUBSAMPLE_DEF(CryptoPlugin); + +DescramblerImpl::DescramblerImpl(DescramblerPlugin* plugin) : mPluginHolder(plugin) { + ALOGV("CTOR: plugin=%p", mPluginHolder.get()); +} + +DescramblerImpl::~DescramblerImpl() { + ALOGV("DTOR: plugin=%p", mPluginHolder.get()); + release(); +} + +ScopedAStatus DescramblerImpl::setMediaCasSession(const vector& in_sessionId) { + ALOGV("%s: sessionId=%s", __FUNCTION__, sessionIdToString(in_sessionId).string()); + + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + return toStatus(INVALID_OPERATION); + } + + return toStatus(holder->setMediaCasSession(in_sessionId)); +} + +ScopedAStatus DescramblerImpl::requiresSecureDecoderComponent(const string& in_mime, + bool* _aidl_return) { + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + *_aidl_return = false; + } + + *_aidl_return = holder->requiresSecureDecoderComponent(String8(in_mime.c_str())); + return ScopedAStatus::ok(); +} + +static inline bool validateRangeForSize(int64_t offset, int64_t length, int64_t size) { + return isInRange(0, (uint64_t)size, offset, (uint64_t)length); +} + +ScopedAStatus DescramblerImpl::descramble(ScramblingControl scramblingControl, + const vector& subSamples, + const SharedBuffer& srcBuffer, int64_t srcOffset, + const DestinationBuffer& dstBuffer, int64_t dstOffset, + int32_t* _aidl_return) { + ALOGV("%s", __FUNCTION__); + + // heapbase's size is stored in int64_t, but mapMemory's mmap will map size in + // size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed but the + // mapped memory's actual size will be smaller than the reported size. + if (srcBuffer.heapBase.size > SIZE_MAX) { + ALOGE("Invalid memory size: %" PRIu64 "", srcBuffer.heapBase.size); + android_errorWriteLog(0x534e4554, "79376389"); + return toStatus(BAD_VALUE); + } + + void* srcPtr = mmap(NULL, srcBuffer.heapBase.size, PROT_READ | PROT_WRITE, MAP_SHARED, + srcBuffer.heapBase.fd.get(), 0); + + // Validate if the offset and size in the SharedBuffer is consistent with the + // mapped heapbase, since the offset and size is controlled by client. + if (srcPtr == NULL) { + ALOGE("Failed to map src buffer."); + return toStatus(BAD_VALUE); + } + if (!validateRangeForSize(srcBuffer.offset, srcBuffer.size, srcBuffer.heapBase.size)) { + ALOGE("Invalid src buffer range: offset %" PRIu64 ", size %" PRIu64 + ", srcMem" + "size %" PRIu64 "", + srcBuffer.offset, srcBuffer.size, srcBuffer.heapBase.size); + android_errorWriteLog(0x534e4554, "67962232"); + return toStatus(BAD_VALUE); + } + + // use 64-bit here to catch bad subsample size that might be overflowing. + uint64_t totalBytesInSubSamples = 0; + for (size_t i = 0; i < subSamples.size(); i++) { + uint32_t numBytesOfClearData = subSamples[i].numBytesOfClearData; + uint32_t numBytesOfEncryptedData = subSamples[i].numBytesOfEncryptedData; + totalBytesInSubSamples += (uint64_t)numBytesOfClearData + numBytesOfEncryptedData; + } + // Further validate if the specified srcOffset and requested total subsample size + // is consistent with the source shared buffer size. + if (!validateRangeForSize(srcOffset, totalBytesInSubSamples, srcBuffer.size)) { + ALOGE("Invalid srcOffset and subsample size: " + "srcOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 + ", srcBuffer" + "size %" PRIu64 "", + srcOffset, totalBytesInSubSamples, srcBuffer.size); + android_errorWriteLog(0x534e4554, "67962232"); + return toStatus(BAD_VALUE); + } + srcPtr = (uint8_t*)srcPtr + srcBuffer.offset; + + void* dstPtr = NULL; + if (dstBuffer.getTag() == DestinationBuffer::Tag::nonsecureMemory) { + // When using shared memory, src buffer is also used as dst, + // we don't map it again here. + dstPtr = srcPtr; + + // In this case the dst and src would be the same buffer, need to validate + // dstOffset against the buffer size too. + if (!validateRangeForSize(dstOffset, totalBytesInSubSamples, srcBuffer.size)) { + ALOGE("Invalid dstOffset and subsample size: " + "dstOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 + ", srcBuffer" + "size %" PRIu64 "", + dstOffset, totalBytesInSubSamples, srcBuffer.size); + android_errorWriteLog(0x534e4554, "67962232"); + return toStatus(BAD_VALUE); + } + } else { + native_handle_t* handle = makeFromAidl(dstBuffer.get()); + dstPtr = static_cast(handle); + } + + // Get a local copy of the shared_ptr for the plugin. Note that before + // calling the callback, this shared_ptr must be manually reset, since + // the client side could proceed as soon as the callback is called + // without waiting for this method to go out of scope. + shared_ptr holder = atomic_load(&mPluginHolder); + if (holder.get() == nullptr) { + return toStatus(INVALID_OPERATION); + } + + // Casting SubSample to DescramblerPlugin::SubSample, but need to ensure + // structs are actually identical + + auto returnStatus = + holder->descramble(dstBuffer.getTag() != DestinationBuffer::Tag::nonsecureMemory, + (DescramblerPlugin::ScramblingControl)scramblingControl, + subSamples.size(), (DescramblerPlugin::SubSample*)subSamples.data(), + srcPtr, srcOffset, dstPtr, dstOffset, NULL); + + holder.reset(); + *_aidl_return = returnStatus; + return toStatus(returnStatus >= 0 ? OK : returnStatus); +} + +ScopedAStatus DescramblerImpl::release() { + ALOGV("%s: plugin=%p", __FUNCTION__, mPluginHolder.get()); + + shared_ptr holder(nullptr); + atomic_store(&mPluginHolder, holder); + + return ScopedAStatus::ok(); +} + +} // namespace cas +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/cas/aidl/default/DescramblerImpl.h b/cas/aidl/default/DescramblerImpl.h new file mode 100755 index 0000000000..2efc1a0b69 --- /dev/null +++ b/cas/aidl/default/DescramblerImpl.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 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. + */ + +#include +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace cas { + +using namespace ::android; +using namespace std; +using ndk::ScopedAStatus; + +class DescramblerImpl : public BnDescrambler { + public: + DescramblerImpl(DescramblerPlugin* plugin); + virtual ~DescramblerImpl(); + + virtual ScopedAStatus setMediaCasSession(const vector& in_sessionId) override; + + virtual ScopedAStatus requiresSecureDecoderComponent(const string& in_mime, + bool* _aidl_return) override; + + virtual ScopedAStatus descramble(ScramblingControl in_scramblingControl, + const vector& in_subSamples, + const SharedBuffer& in_srcBuffer, int64_t in_srcOffset, + const DestinationBuffer& in_dstBuffer, int64_t in_dstOffset, + int32_t* _aidl_return) override; + + virtual ScopedAStatus release() override; + + private: + shared_ptr mPluginHolder; + + DISALLOW_EVIL_CONSTRUCTORS(DescramblerImpl); +}; + +} // namespace cas +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/cas/aidl/default/FactoryLoader.h b/cas/aidl/default/FactoryLoader.h new file mode 100755 index 0000000000..f90b109718 --- /dev/null +++ b/cas/aidl/default/FactoryLoader.h @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2022 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. + */ + +#include +#include +#include +#include +#include +#include "SharedLibrary.h" + +using namespace std; + +namespace aidl { +namespace android { +namespace hardware { +namespace cas { + +using namespace ::android; + +template +class FactoryLoader { + public: + FactoryLoader(const char* name) : mFactory(NULL), mCreateFactoryFuncName(name) {} + + virtual ~FactoryLoader() { closeFactory(); } + + bool findFactoryForScheme(int32_t CA_system_id, shared_ptr* library = NULL, + T** factory = NULL); + + bool enumeratePlugins(vector* results); + + private: + typedef T* (*CreateFactoryFunc)(); + + Mutex mMapLock; + T* mFactory; + const char* mCreateFactoryFuncName; + shared_ptr mLibrary; + KeyedVector mCASystemIdToLibraryPathMap; + KeyedVector> mLibraryPathToOpenLibraryMap; + + bool loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id, + shared_ptr* library, T** factory); + + bool queryPluginsFromPath(const String8& path, vector* results); + + bool openFactory(const String8& path); + void closeFactory(); +}; + +template +bool FactoryLoader::findFactoryForScheme(int32_t CA_system_id, + shared_ptr* library, T** factory) { + if (library != NULL) { + library->reset(); + } + if (factory != NULL) { + *factory = NULL; + } + + Mutex::Autolock autoLock(mMapLock); + + // first check cache + ssize_t index = mCASystemIdToLibraryPathMap.indexOfKey(CA_system_id); + if (index >= 0) { + return loadFactoryForSchemeFromPath(mCASystemIdToLibraryPathMap[index], CA_system_id, + library, factory); + } + + // no luck, have to search +#ifdef __LP64__ + String8 dirPath("/vendor/lib64/mediacas"); +#else + String8 dirPath("/vendor/lib/mediacas"); +#endif + DIR* pDir = opendir(dirPath.string()); + + if (pDir == NULL) { + ALOGE("Failed to open plugin directory %s", dirPath.string()); + return false; + } + + struct dirent* pEntry; + while ((pEntry = readdir(pDir))) { + String8 pluginPath = dirPath + "/" + pEntry->d_name; + if (pluginPath.getPathExtension() == ".so") { + if (loadFactoryForSchemeFromPath(pluginPath, CA_system_id, library, factory)) { + mCASystemIdToLibraryPathMap.add(CA_system_id, pluginPath); + closedir(pDir); + + return true; + } + } + } + + closedir(pDir); + + ALOGE("Failed to find plugin"); + return false; +} + +template +bool FactoryLoader::enumeratePlugins(vector* results) { + ALOGI("enumeratePlugins"); + + results->clear(); + +#ifdef __LP64__ + String8 dirPath("/vendor/lib64/mediacas"); +#else + String8 dirPath("/vendor/lib/mediacas"); +#endif + DIR* pDir = opendir(dirPath.string()); + + if (pDir == NULL) { + ALOGE("Failed to open plugin directory %s", dirPath.string()); + return false; + } + + Mutex::Autolock autoLock(mMapLock); + + struct dirent* pEntry; + while ((pEntry = readdir(pDir))) { + String8 pluginPath = dirPath + "/" + pEntry->d_name; + if (pluginPath.getPathExtension() == ".so") { + queryPluginsFromPath(pluginPath, results); + } + } + return true; +} + +template +bool FactoryLoader::loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id, + shared_ptr* library, + T** factory) { + closeFactory(); + + if (!openFactory(path) || !mFactory->isSystemIdSupported(CA_system_id)) { + closeFactory(); + return false; + } + + if (library != NULL) { + *library = mLibrary; + } + if (factory != NULL) { + *factory = mFactory; + } + return true; +} + +template +bool FactoryLoader::queryPluginsFromPath(const String8& path, + vector* results) { + closeFactory(); + + vector descriptors; + if (!openFactory(path) || mFactory->queryPlugins(&descriptors) != OK) { + closeFactory(); + return false; + } + + for (auto it = descriptors.begin(); it != descriptors.end(); it++) { + results->push_back( + AidlCasPluginDescriptor{.caSystemId = it->CA_system_id, .name = it->name.c_str()}); + } + return true; +} + +template +bool FactoryLoader::openFactory(const String8& path) { + // get strong pointer to open shared library + ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path); + if (index >= 0) { + mLibrary = mLibraryPathToOpenLibraryMap[index]; + } else { + index = mLibraryPathToOpenLibraryMap.add(path, NULL); + } + + if (!mLibrary.get()) { + mLibrary = ::ndk::SharedRefBase::make(path); + if (!*mLibrary) { + return false; + } + + mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary); + } + + CreateFactoryFunc createFactory = (CreateFactoryFunc)mLibrary->lookup(mCreateFactoryFuncName); + if (createFactory == NULL || (mFactory = createFactory()) == NULL) { + return false; + } + return true; +} + +template +void FactoryLoader::closeFactory() { + delete mFactory; + mFactory = NULL; + mLibrary.reset(); +} + +} // namespace cas +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/cas/aidl/default/MediaCasService.cpp b/cas/aidl/default/MediaCasService.cpp new file mode 100755 index 0000000000..8285613c31 --- /dev/null +++ b/cas/aidl/default/MediaCasService.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2022 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. + */ + +#define LOG_TAG "android.hardware.cas-MediaCasService" + +#include +#include +#include + +#include "CasImpl.h" +#include "DescramblerImpl.h" +#include "MediaCasService.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace cas { + +MediaCasService::MediaCasService() + : mCasLoader("createCasFactory"), mDescramblerLoader("createDescramblerFactory") {} + +MediaCasService::~MediaCasService() {} + +ScopedAStatus MediaCasService::enumeratePlugins( + vector* aidlCasPluginDescriptors) { + ALOGV("%s", __FUNCTION__); + + mCasLoader.enumeratePlugins(aidlCasPluginDescriptors); + return ScopedAStatus::ok(); +} + +ScopedAStatus MediaCasService::isSystemIdSupported(int32_t CA_system_id, bool* _aidl_return) { + ALOGV("isSystemIdSupported: CA_system_id=%d", CA_system_id); + + *_aidl_return = mCasLoader.findFactoryForScheme(CA_system_id); + return ScopedAStatus::ok(); +} + +ScopedAStatus MediaCasService::createPlugin(int32_t CA_system_id, + const shared_ptr& listener, + shared_ptr* _aidl_return) { + ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id); + if (listener == NULL) ALOGV("%s: Listener is NULL", __FUNCTION__); + + CasFactory* factory; + shared_ptr library; + if (mCasLoader.findFactoryForScheme(CA_system_id, &library, &factory)) { + CasPlugin* plugin = NULL; + shared_ptr casImpl = ::ndk::SharedRefBase::make(listener); + if (factory->createPlugin(CA_system_id, casImpl.get(), &CasImpl::CallBackExt, &plugin) == + OK && + plugin != NULL) { + casImpl->init(plugin); + *_aidl_return = casImpl; + casImpl->setPluginStatusUpdateCallback(); + } + } + + return ScopedAStatus::ok(); +} + +ScopedAStatus MediaCasService::isDescramblerSupported(int32_t CA_system_id, bool* _aidl_return) { + ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id); + + *_aidl_return = mDescramblerLoader.findFactoryForScheme(CA_system_id); + return ScopedAStatus::ok(); +} + +ScopedAStatus MediaCasService::createDescrambler(int32_t CA_system_id, + shared_ptr* _aidl_return) { + ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id); + + DescramblerFactory* factory; + shared_ptr library; + if (mDescramblerLoader.findFactoryForScheme(CA_system_id, &library, &factory)) { + DescramblerPlugin* plugin = NULL; + if (factory->createPlugin(CA_system_id, &plugin) == OK && plugin != NULL) { + *_aidl_return = ::ndk::SharedRefBase::make(plugin); + } + } + + return ScopedAStatus::ok(); +} + +} // namespace cas +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/cas/aidl/default/MediaCasService.h b/cas/aidl/default/MediaCasService.h new file mode 100755 index 0000000000..96623135ba --- /dev/null +++ b/cas/aidl/default/MediaCasService.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 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. + */ +#include +#include + +#include "FactoryLoader.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace cas { + +using namespace ::android; +using namespace std; +using ndk::ScopedAStatus; + +class MediaCasService : public BnMediaCasService { + public: + MediaCasService(); + + virtual ScopedAStatus enumeratePlugins( + vector* aidlCasPluginDescriptors) override; + + virtual ScopedAStatus isSystemIdSupported(int32_t in_CA_system_id, bool* _aidl_return) override; + + virtual ScopedAStatus createPlugin(int32_t in_CA_system_id, + const shared_ptr& in_listener, + shared_ptr* _aidl_return) override; + + virtual ScopedAStatus isDescramblerSupported(int32_t in_CA_system_id, + bool* _aidl_return) override; + + virtual ScopedAStatus createDescrambler(int32_t in_CA_system_id, + shared_ptr* _aidl_return) override; + + private: + FactoryLoader mCasLoader; + FactoryLoader mDescramblerLoader; + + virtual ~MediaCasService(); +}; + +} // namespace cas +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/cas/aidl/default/OWNERS b/cas/aidl/default/OWNERS new file mode 100755 index 0000000000..4c55752be9 --- /dev/null +++ b/cas/aidl/default/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 1344 +quxiangfang@google.com +hgchen@google.com diff --git a/cas/aidl/default/SharedLibrary.cpp b/cas/aidl/default/SharedLibrary.cpp new file mode 100755 index 0000000000..e79f383e1f --- /dev/null +++ b/cas/aidl/default/SharedLibrary.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 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. + */ + +#define LOG_TAG "android.hardware.cas-SharedLibrary" + +#include "SharedLibrary.h" +#include +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace cas { + +SharedLibrary::SharedLibrary(const String8& path) { + mLibHandle = dlopen(path.string(), RTLD_NOW); +} + +SharedLibrary::~SharedLibrary() { + if (mLibHandle != NULL) { + dlclose(mLibHandle); + mLibHandle = NULL; + } +} + +bool SharedLibrary::operator!() const { + return mLibHandle == NULL; +} + +void* SharedLibrary::lookup(const char* symbol) const { + if (!mLibHandle) { + return NULL; + } + // Clear last error before we load the symbol again, + // in case the caller didn't retrieve it. + (void)dlerror(); + return dlsym(mLibHandle, symbol); +} + +const char* SharedLibrary::lastError() const { + const char* error = dlerror(); + return error ? error : "No errors or unknown error"; +} + +} // namespace cas +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/cas/aidl/default/SharedLibrary.h b/cas/aidl/default/SharedLibrary.h new file mode 100755 index 0000000000..d367866596 --- /dev/null +++ b/cas/aidl/default/SharedLibrary.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 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. + */ + +#include +#include +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace cas { + +using namespace ::android; + +class SharedLibrary : public ndk::SharedRefBase { + public: + explicit SharedLibrary(const String8& path); + ~SharedLibrary(); + + bool operator!() const; + void* lookup(const char* symbol) const; + const char* lastError() const; + + private: + void* mLibHandle; + DISALLOW_EVIL_CONSTRUCTORS(SharedLibrary); +}; + +} // namespace cas +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/cas/aidl/default/TypeConvert.cpp b/cas/aidl/default/TypeConvert.cpp new file mode 100755 index 0000000000..4f7005f72f --- /dev/null +++ b/cas/aidl/default/TypeConvert.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2022 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. + */ + +#define LOG_TAG "android.hardware.cas-TypeConvert" + +#include +#include + +#include "TypeConvert.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace cas { + +ScopedAStatus toStatus(status_t legacyStatus) { + int status; + switch (legacyStatus) { + case OK: + return ndk::ScopedAStatus::ok(); + case ERROR_CAS_NO_LICENSE: + status = Status::ERROR_CAS_NO_LICENSE; + break; + case ERROR_CAS_LICENSE_EXPIRED: + status = Status::ERROR_CAS_LICENSE_EXPIRED; + break; + case ERROR_CAS_SESSION_NOT_OPENED: + status = Status::ERROR_CAS_SESSION_NOT_OPENED; + break; + case ERROR_CAS_CANNOT_HANDLE: + status = Status::ERROR_CAS_CANNOT_HANDLE; + break; + case ERROR_CAS_TAMPER_DETECTED: + status = Status::ERROR_CAS_INVALID_STATE; + break; + case BAD_VALUE: + status = Status::BAD_VALUE; + break; + case ERROR_CAS_NOT_PROVISIONED: + status = Status::ERROR_CAS_NOT_PROVISIONED; + break; + case ERROR_CAS_RESOURCE_BUSY: + status = Status::ERROR_CAS_RESOURCE_BUSY; + break; + case ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION: + status = Status::ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION; + break; + case ERROR_CAS_DEVICE_REVOKED: + status = Status::ERROR_CAS_DEVICE_REVOKED; + break; + case ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED: + status = Status::ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED; + break; + case ERROR_CAS_DECRYPT: + status = Status::ERROR_CAS_DECRYPT; + break; + case ERROR_CAS_NEED_ACTIVATION: + status = Status::ERROR_CAS_NEED_ACTIVATION; + break; + case ERROR_CAS_NEED_PAIRING: + status = Status::ERROR_CAS_NEED_PAIRING; + break; + case ERROR_CAS_NO_CARD: + status = Status::ERROR_CAS_NO_CARD; + break; + case ERROR_CAS_CARD_MUTE: + status = Status::ERROR_CAS_CARD_MUTE; + break; + case ERROR_CAS_CARD_INVALID: + status = Status::ERROR_CAS_CARD_INVALID; + break; + case ERROR_CAS_BLACKOUT: + status = Status::ERROR_CAS_BLACKOUT; + break; + default: + ALOGW("Unable to convert legacy status: %d, defaulting to UNKNOWN", legacyStatus); + status = Status::ERROR_CAS_UNKNOWN; + break; + } + return ScopedAStatus::fromServiceSpecificError(status); +} + +String8 sessionIdToString(const std::vector& sessionId) { + String8 result; + for (auto it = sessionId.begin(); it != sessionId.end(); it++) { + result.appendFormat("%02x ", *it); + } + if (result.isEmpty()) { + result.append("(null)"); + } + return result; +} + +} // namespace cas +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/cas/aidl/default/TypeConvert.h b/cas/aidl/default/TypeConvert.h new file mode 100755 index 0000000000..ebfa2869f4 --- /dev/null +++ b/cas/aidl/default/TypeConvert.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 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. + */ + +#include +#include +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace cas { + +using namespace ::android; +using ndk::ScopedAStatus; + +ScopedAStatus toStatus(status_t legacyStatus); + +String8 sessionIdToString(const std::vector& sessionId); + +} // namespace cas +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/cas/aidl/default/android.hardware.cas-service.xml b/cas/aidl/default/android.hardware.cas-service.xml new file mode 100755 index 0000000000..6389fe2797 --- /dev/null +++ b/cas/aidl/default/android.hardware.cas-service.xml @@ -0,0 +1,6 @@ + + + android.hardware.cas + IMediaCasService/default + + diff --git a/cas/aidl/default/cas-default-lazy.rc b/cas/aidl/default/cas-default-lazy.rc new file mode 100755 index 0000000000..60b59ca2c7 --- /dev/null +++ b/cas/aidl/default/cas-default-lazy.rc @@ -0,0 +1,9 @@ +service vendor.cas-default-lazy /vendor/bin/hw/android.hardware.cas-service.example-lazy + interface aidl android.hardware.cas.IMediaCasService/default + class hal + user media + group mediadrm drmrpc + ioprio rt 4 + task_profiles ProcessCapacityHigh + oneshot + disabled diff --git a/cas/aidl/default/cas-default.rc b/cas/aidl/default/cas-default.rc new file mode 100755 index 0000000000..e00b9c52af --- /dev/null +++ b/cas/aidl/default/cas-default.rc @@ -0,0 +1,7 @@ +service vendor.cas-default /vendor/bin/hw/android.hardware.cas-service.example + interface aidl android.hardware.cas.IMediaCasService/default + class hal + user media + group mediadrm drmrpc + ioprio rt 4 + task_profiles ProcessCapacityHigh diff --git a/cas/aidl/default/fuzzer.cpp b/cas/aidl/default/fuzzer.cpp new file mode 100755 index 0000000000..db1369ebb9 --- /dev/null +++ b/cas/aidl/default/fuzzer.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 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. + */ +#include +#include +#include + +using aidl::android::hardware::cas::MediaCasService; +using android::fuzzService; +using ndk::SharedRefBase; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + auto mediaCasServiceAidl = SharedRefBase::make(); + + fuzzService(mediaCasServiceAidl->asBinder().get(), FuzzedDataProvider(data, size)); + + return 0; +} \ No newline at end of file diff --git a/cas/aidl/default/service.cpp b/cas/aidl/default/service.cpp new file mode 100755 index 0000000000..bed2f01267 --- /dev/null +++ b/cas/aidl/default/service.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2022 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. + */ + +#ifdef LAZY_SERVICE +const bool kLazyService = true; +#define LOG_TAG "android.hardware.cas-service.example-lazy" +#else +const bool kLazyService = false; +#define LOG_TAG "android.hardware.cas-service.example" +#endif + +#include +#include + +#include "MediaCasService.h" + +using aidl::android::hardware::cas::MediaCasService; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(8); + + // Setup hwbinder service + std::shared_ptr service = ::ndk::SharedRefBase::make(); + + const std::string instance = std::string() + MediaCasService::descriptor + "/default"; + binder_status_t status; + + if (kLazyService) { + status = AServiceManager_registerLazyService(service->asBinder().get(), instance.c_str()); + } else { + status = AServiceManager_addService(service->asBinder().get(), instance.c_str()); + } + LOG_ALWAYS_FATAL_IF(status != STATUS_OK, "Error while registering cas service: %d", status); + + ABinderProcess_joinThreadPool(); + return 0; +} diff --git a/cas/aidl/vts/functional/Android.bp b/cas/aidl/vts/functional/Android.bp new file mode 100644 index 0000000000..295ae7c0ce --- /dev/null +++ b/cas/aidl/vts/functional/Android.bp @@ -0,0 +1,45 @@ +// +// Copyright (C) 2022 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +cc_test { + name: "VtsHalCasAidlTargetTest", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: ["VtsHalCasAidlTargetTest.cpp"], + static_libs: [ + "android.hardware.cas-V1-ndk", + "android.hardware.common-V2-ndk", + ], + shared_libs: [ + "libbinder_ndk", + ], + test_suites: [ + "general-tests", + "vts", + ], + disable_framework: true, +} diff --git a/cas/aidl/vts/functional/OWNERS b/cas/aidl/vts/functional/OWNERS new file mode 100644 index 0000000000..4c55752be9 --- /dev/null +++ b/cas/aidl/vts/functional/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 1344 +quxiangfang@google.com +hgchen@google.com diff --git a/cas/aidl/vts/functional/VtsHalCasAidlTargetTest.cpp b/cas/aidl/vts/functional/VtsHalCasAidlTargetTest.cpp new file mode 100644 index 0000000000..266b55d1a1 --- /dev/null +++ b/cas/aidl/vts/functional/VtsHalCasAidlTargetTest.cpp @@ -0,0 +1,809 @@ +/* + * Copyright (C) 2022 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. + */ + +#define LOG_TAG "mediacas_aidl_hal_test" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CLEAR_KEY_SYSTEM_ID 0xF6D8 +#define INVALID_SYSTEM_ID 0 +#define WAIT_TIMEOUT 3000000000 + +#define PROVISION_STR \ + "{ " \ + " \"id\": 21140844, " \ + " \"name\": \"Test Title\", " \ + " \"lowercase_organization_name\": \"Android\", " \ + " \"asset_key\": { " \ + " \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\" " \ + " }, " \ + " \"cas_type\": 1, " \ + " \"track_types\": [ ] " \ + "} " + +using aidl::android::hardware::common::Ashmem; +using android::Mutex; +using namespace aidl::android::hardware::cas; +using namespace ndk; +using namespace std; +using namespace testing; + +const uint8_t kEcmBinaryBuffer[] = { + 0x00, 0x00, 0x01, 0xf0, 0x00, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x46, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x27, 0x10, 0x02, 0x00, + 0x01, 0x77, 0x01, 0x42, 0x95, 0x6c, 0x0e, 0xe3, 0x91, 0xbc, 0xfd, 0x05, 0xb1, 0x60, 0x4f, + 0x17, 0x82, 0xa4, 0x86, 0x9b, 0x23, 0x56, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x27, 0x10, 0x02, 0x00, 0x01, 0x77, 0x01, 0x42, 0x95, 0x6c, 0xd7, 0x43, 0x62, 0xf8, 0x1c, + 0x62, 0x19, 0x05, 0xc7, 0x3a, 0x42, 0xcd, 0xfd, 0xd9, 0x13, 0x48, +}; + +const SubSample kSubSamples[] = {{162, 0}, {0, 184}, {0, 184}}; + +const uint8_t kInBinaryBuffer[] = { + 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb, + 0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03, + 0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06, + 0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8, + 0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72, + 0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d, + 0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63, + 0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30, + 0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f, + 0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x6e, 0x45, 0x21, + 0x82, 0x38, 0xf0, 0x9d, 0x7d, 0x96, 0xe6, 0x94, 0xae, 0xe2, 0x87, 0x8f, 0x04, 0x49, 0xe5, + 0xf6, 0x8c, 0x8b, 0x9a, 0x10, 0x18, 0xba, 0x94, 0xe9, 0x22, 0x31, 0x04, 0x7e, 0x60, 0x5b, + 0xc4, 0x24, 0x00, 0x90, 0x62, 0x0d, 0xdc, 0x85, 0x74, 0x75, 0x78, 0xd0, 0x14, 0x08, 0xcb, + 0x02, 0x1d, 0x7d, 0x9d, 0x34, 0xe8, 0x81, 0xb9, 0xf7, 0x09, 0x28, 0x79, 0x29, 0x8d, 0xe3, + 0x14, 0xed, 0x5f, 0xca, 0xaf, 0xf4, 0x1c, 0x49, 0x15, 0xe1, 0x80, 0x29, 0x61, 0x76, 0x80, + 0x43, 0xf8, 0x58, 0x53, 0x40, 0xd7, 0x31, 0x6d, 0x61, 0x81, 0x41, 0xe9, 0x77, 0x9f, 0x9c, + 0xe1, 0x6d, 0xf2, 0xee, 0xd9, 0xc8, 0x67, 0xd2, 0x5f, 0x48, 0x73, 0xe3, 0x5c, 0xcd, 0xa7, + 0x45, 0x58, 0xbb, 0xdd, 0x28, 0x1d, 0x68, 0xfc, 0xb4, 0xc6, 0xf6, 0x92, 0xf6, 0x30, 0x03, + 0xaa, 0xe4, 0x32, 0xf6, 0x34, 0x51, 0x4b, 0x0f, 0x8c, 0xf9, 0xac, 0x98, 0x22, 0xfb, 0x49, + 0xc8, 0xbf, 0xca, 0x8c, 0x80, 0x86, 0x5d, 0xd7, 0xa4, 0x52, 0xb1, 0xd9, 0xa6, 0x04, 0x4e, + 0xb3, 0x2d, 0x1f, 0xb8, 0x35, 0xcc, 0x45, 0x6d, 0x9c, 0x20, 0xa7, 0xa4, 0x34, 0x59, 0x72, + 0xe3, 0xae, 0xba, 0x49, 0xde, 0xd1, 0xaa, 0xee, 0x3d, 0x77, 0xfc, 0x5d, 0xc6, 0x1f, 0x9d, + 0xac, 0xc2, 0x15, 0x66, 0xb8, 0xe1, 0x54, 0x4e, 0x74, 0x93, 0xdb, 0x9a, 0x24, 0x15, 0x6e, + 0x20, 0xa3, 0x67, 0x3e, 0x5a, 0x24, 0x41, 0x5e, 0xb0, 0xe6, 0x35, 0x87, 0x1b, 0xc8, 0x7a, + 0xf9, 0x77, 0x65, 0xe0, 0x01, 0xf2, 0x4c, 0xe4, 0x2b, 0xa9, 0x64, 0x96, 0x96, 0x0b, 0x46, + 0xca, 0xea, 0x79, 0x0e, 0x78, 0xa3, 0x5f, 0x43, 0xfc, 0x47, 0x6a, 0x12, 0xfa, 0xc4, 0x33, + 0x0e, 0x88, 0x1c, 0x19, 0x3a, 0x00, 0xc3, 0x4e, 0xb5, 0xd8, 0xfa, 0x8e, 0xf1, 0xbc, 0x3d, + 0xb2, 0x7e, 0x50, 0x8d, 0x67, 0xc3, 0x6b, 0xed, 0xe2, 0xea, 0xa6, 0x1f, 0x25, 0x24, 0x7c, + 0x94, 0x74, 0x50, 0x49, 0xe3, 0xc6, 0x58, 0x2e, 0xfd, 0x28, 0xb4, 0xc6, 0x73, 0xb1, 0x53, + 0x74, 0x27, 0x94, 0x5c, 0xdf, 0x69, 0xb7, 0xa1, 0xd7, 0xf5, 0xd3, 0x8a, 0x2c, 0x2d, 0xb4, + 0x5e, 0x8a, 0x16, 0x14, 0x54, 0x64, 0x6e, 0x00, 0x6b, 0x11, 0x59, 0x8a, 0x63, 0x38, 0x80, + 0x76, 0xc3, 0xd5, 0x59, 0xf7, 0x3f, 0xd2, 0xfa, 0xa5, 0xca, 0x82, 0xff, 0x4a, 0x62, 0xf0, + 0xe3, 0x42, 0xf9, 0x3b, 0x38, 0x27, 0x8a, 0x89, 0xaa, 0x50, 0x55, 0x4b, 0x29, 0xf1, 0x46, + 0x7c, 0x75, 0xef, 0x65, 0xaf, 0x9b, 0x0d, 0x6d, 0xda, 0x25, 0x94, 0x14, 0xc1, 0x1b, 0xf0, + 0xc5, 0x4c, 0x24, 0x0e, 0x65, +}; + +const uint8_t kOutRefBinaryBuffer[] = { + 0x00, 0x00, 0x00, 0x01, 0x09, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x1e, 0xdb, + 0x01, 0x40, 0x16, 0xec, 0x04, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x00, 0x0f, 0x03, + 0xc5, 0x8b, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xca, 0x8c, 0xb2, 0x00, 0x00, 0x01, 0x06, + 0x05, 0xff, 0xff, 0x70, 0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8, + 0x20, 0xd9, 0x23, 0xee, 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72, + 0x65, 0x20, 0x31, 0x34, 0x32, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, 0x36, 0x34, 0x2f, 0x4d, + 0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63, + 0x20, 0x2d, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30, + 0x33, 0x2d, 0x32, 0x30, 0x31, 0x34, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, 0x61, 0x6e, 0x2e, 0x6f, + 0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x2d, 0x20, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d, + 0x30, 0x20, 0x72, 0x65, 0x66, 0x3d, 0x32, 0x20, 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x3d, 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x65, 0x3d, + 0x30, 0x78, 0x31, 0x3a, 0x30, 0x78, 0x31, 0x31, 0x31, 0x20, 0x6d, 0x65, 0x3d, 0x68, 0x65, + 0x78, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x65, 0x3d, 0x37, 0x20, 0x70, 0x73, 0x79, 0x3d, 0x31, + 0x20, 0x70, 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, 0x30, 0x30, 0x3a, 0x30, 0x2e, + 0x30, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x66, 0x3d, 0x31, 0x20, + 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72, + 0x6f, 0x6d, 0x61, 0x5f, 0x6d, 0x65, 0x3d, 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69, + 0x73, 0x3d, 0x31, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x71, + 0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, 0x6f, 0x6e, 0x65, 0x3d, 0x32, 0x31, + 0x2c, 0x31, 0x31, 0x20, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x73, 0x6b, 0x69, 0x70, 0x3d, + 0x31, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, + 0x36, 0x30, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x68, + 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x35, 0x20, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x64, 0x5f, + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x30, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20, + 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x3d, 0x30, 0x20, 0x62, 0x6c, 0x75, 0x72, 0x61, 0x79, + 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x3d, 0x30, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x72, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x3d, 0x30, 0x20, + 0x62, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, 0x30, 0x20, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x70, 0x3d, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x3d, 0x32, 0x35, 0x30, + 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x3d, 0x32, 0x35, 0x20, + 0x73, 0x63, 0x65, 0x6e, 0x65, +}; + +class MediaCasListener : public BnCasListener { + public: + virtual ScopedAStatus onEvent(int32_t event, int32_t arg, + const vector& data) override { + Mutex::Autolock autoLock(mMsgLock); + mEvent = event; + mEventArg = arg; + mEventData = data; + + mEventReceived = true; + mMsgCondition.signal(); + return ScopedAStatus::ok(); + } + + virtual ScopedAStatus onSessionEvent(const vector& sessionId, int32_t event, + int32_t arg, const vector& data) override { + Mutex::Autolock autoLock(mMsgLock); + mSessionId = sessionId; + mEvent = event; + mEventArg = arg; + mEventData = data; + + mEventReceived = true; + mMsgCondition.signal(); + return ScopedAStatus::ok(); + } + + virtual ScopedAStatus onStatusUpdate(StatusEvent event, int32_t arg) override { + Mutex::Autolock autoLock(mMsgLock); + mStatusEvent = event; + mEventArg = arg; + + mEventReceived = true; + mMsgCondition.signal(); + return ScopedAStatus::ok(); + } + + void testEventEcho(shared_ptr& mediaCas, int32_t& event, int32_t& eventArg, + vector& eventData); + + void testSessionEventEcho(shared_ptr& mediaCas, const vector& sessionId, + int32_t& event, int32_t& eventArg, vector& eventData); + + void testStatusUpdate(shared_ptr& mediaCas, vector* sessionId, + SessionIntent intent, ScramblingMode mode); + + private: + int32_t mEvent = -1; + int32_t mEventArg = -1; + StatusEvent mStatusEvent; + bool mEventReceived = false; + vector mEventData; + vector mSessionId; + Mutex mMsgLock; + android::Condition mMsgCondition; +}; + +void MediaCasListener::testEventEcho(shared_ptr& mediaCas, int32_t& event, int32_t& eventArg, + vector& eventData) { + mEventReceived = false; + auto returnStatus = mediaCas->sendEvent(event, eventArg, eventData); + EXPECT_TRUE(returnStatus.isOk()); + + Mutex::Autolock autoLock(mMsgLock); + while (!mEventReceived) { + if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) { + ADD_FAILURE() << "event not received within timeout"; + return; + } + } + + EXPECT_EQ(mEvent, event); + EXPECT_EQ(mEventArg, eventArg); + EXPECT_TRUE(mEventData == eventData); +} + +void MediaCasListener::testSessionEventEcho(shared_ptr& mediaCas, + const vector& sessionId, int32_t& event, + int32_t& eventArg, vector& eventData) { + mEventReceived = false; + EXPECT_TRUE(mediaCas->sendSessionEvent(sessionId, event, eventArg, eventData).isOk()); + + Mutex::Autolock autoLock(mMsgLock); + while (!mEventReceived) { + if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) { + ADD_FAILURE() << "event not received within timeout"; + return; + } + } + + EXPECT_TRUE(mSessionId == sessionId); + EXPECT_EQ(mEvent, event); + EXPECT_EQ(mEventArg, eventArg); + EXPECT_TRUE(mEventData == eventData); +} + +void MediaCasListener::testStatusUpdate(shared_ptr& mediaCas, vector* sessionId, + SessionIntent intent, ScramblingMode mode) { + mEventReceived = false; + EXPECT_TRUE(mediaCas->openSession(intent, mode, sessionId).isOk()); + + Mutex::Autolock autoLock(mMsgLock); + while (!mEventReceived) { + if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) { + ADD_FAILURE() << "event not received within timeout"; + return; + } + } + EXPECT_EQ(mStatusEvent, static_cast(intent)); + EXPECT_EQ(mEventArg, static_cast(mode)); +} + +class MediaCasAidlTest : public testing::TestWithParam { + public: + virtual void SetUp() override { + if (AServiceManager_isDeclared(GetParam().c_str())) { + SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str())); + mService = IMediaCasService::fromBinder(binder); + } else { + mService = nullptr; + } + ASSERT_NE(mService, nullptr); + } + + shared_ptr mService = nullptr; + + protected: + static void description(const string& description) { + RecordProperty("description", description); + } + + shared_ptr mMediaCas; + shared_ptr mDescrambler; + shared_ptr mCasListener; + typedef struct _OobInputTestParams { + const SubSample* subSamples; + int32_t numSubSamples; + int64_t imemSizeActual; + int64_t imemOffset; + int64_t imemSize; + int64_t srcOffset; + int64_t dstOffset; + } OobInputTestParams; + + AssertionResult createCasPlugin(int32_t caSystemId); + AssertionResult openCasSession(vector* sessionId, SessionIntent intent, + ScramblingMode mode); + AssertionResult descrambleTestInputBuffer(const shared_ptr& descrambler, + ScopedAStatus& descrambleStatus, uint8_t*& inMemory); + AssertionResult descrambleTestOobInput(const shared_ptr& descrambler, + ScopedAStatus& descrambleStatus, + const OobInputTestParams& params); +}; + +AssertionResult MediaCasAidlTest::createCasPlugin(int32_t caSystemId) { + bool isSystemIdSupported; + auto status = mService->isSystemIdSupported(caSystemId, &isSystemIdSupported); + bool skipDescrambler = false; + if (!status.isOk() || !isSystemIdSupported) { + return AssertionFailure(); + } + bool isDescramblerSupported; + status = mService->isDescramblerSupported(caSystemId, &isDescramblerSupported); + if (!status.isOk() || !isDescramblerSupported) { + ALOGI("Skip Descrambler test since it's not required in cas."); + mDescrambler = nullptr; + skipDescrambler = true; + } + + mCasListener = SharedRefBase::make(); + status = mService->createPlugin(caSystemId, mCasListener, &mMediaCas); + if (!status.isOk()) { + return AssertionFailure(); + } + if (mMediaCas == nullptr) { + return AssertionFailure(); + } + + if (skipDescrambler) { + return AssertionSuccess(); + } + + status = mService->createDescrambler(caSystemId, &mDescrambler); + if (!status.isOk()) { + return AssertionFailure(); + } + + return AssertionResult(mDescrambler != nullptr); +} + +AssertionResult MediaCasAidlTest::openCasSession(vector* sessionId, SessionIntent intent, + ScramblingMode mode) { + return AssertionResult(mMediaCas->openSession(intent, mode, sessionId).isOk()); +} + +AssertionResult MediaCasAidlTest::descrambleTestInputBuffer( + const shared_ptr& descrambler, ScopedAStatus& descrambleStatus, + uint8_t*& sharedMemory) { + vector subSample(kSubSamples, + kSubSamples + (sizeof(kSubSamples) / sizeof(SubSample))); + + int size = sizeof(kInBinaryBuffer); + auto fd = ashmem_create_region("vts-cas", size); + if (fd < 0) { + ALOGE("ashmem_create_region failed"); + return AssertionFailure(); + } + + sharedMemory = + static_cast(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); + if (sharedMemory == reinterpret_cast(MAP_FAILED)) { + ALOGE("mmap failed"); + return AssertionFailure(); + } + + memcpy(sharedMemory, kInBinaryBuffer, size); + + auto dupFd = dup(fd); + + SharedBuffer srcBuffer = {.heapBase.fd = ScopedFileDescriptor(std::move(fd)), + .heapBase.size = size, + .offset = 0, + .size = size}; + + SharedBuffer dupBuffer = {.heapBase.fd = ScopedFileDescriptor(dupFd), + .heapBase.size = size, + .offset = 0, + .size = size}; + + DestinationBuffer dstBuffer; + dstBuffer.set(std::move(dupBuffer)); + + int32_t outBytes; + descrambleStatus = descrambler->descramble(ScramblingControl::EVENKEY /*2*/, subSample, + srcBuffer, 0, dstBuffer, 0, &outBytes); + if (!descrambleStatus.isOk()) { + ALOGI("descramble failed, status=%d, outBytes=%u, error=%s", descrambleStatus.getStatus(), + outBytes, descrambleStatus.getDescription().c_str()); + } + return AssertionResult(descrambleStatus.isOk()); +} + +AssertionResult MediaCasAidlTest::descrambleTestOobInput( + const shared_ptr& descrambler, ScopedAStatus& descrambleStatus, + const OobInputTestParams& params) { + vector subSample(params.subSamples, params.subSamples + params.numSubSamples); + + auto fd = ashmem_create_region("vts-cas", params.imemSizeActual); + if (fd < 0) { + ALOGE("ashmem_create_region failed"); + return AssertionFailure(); + } + + auto dupFd = dup(fd); + + SharedBuffer srcBuffer = {.heapBase.fd = ScopedFileDescriptor(std::move(fd)), + .heapBase.size = params.imemSizeActual, + .offset = params.imemOffset, + .size = params.imemSize}; + + SharedBuffer dupBuffer = {.heapBase.fd = ScopedFileDescriptor(dupFd), + .heapBase.size = params.imemSizeActual, + .offset = params.imemOffset, + .size = params.imemSize}; + + DestinationBuffer dstBuffer; + dstBuffer.set(std::move(dupBuffer)); + + int32_t outBytes; + descrambleStatus = + descrambler->descramble(ScramblingControl::EVENKEY /*2*/, subSample, srcBuffer, + params.srcOffset, dstBuffer, params.dstOffset, &outBytes); + if (!descrambleStatus.isOk()) { + ALOGI("descramble failed, status=%d, outBytes=%u, error=%s", descrambleStatus.getStatus(), + outBytes, descrambleStatus.getDescription().c_str()); + } + return AssertionResult(descrambleStatus.isOk()); +} + +TEST_P(MediaCasAidlTest, EnumeratePlugins) { + description("Test enumerate plugins"); + + vector descriptors; + EXPECT_TRUE(mService->enumeratePlugins(&descriptors).isOk()); + + if (descriptors.size() == 0) { + ALOGW("[ WARN ] enumeratePlugins list empty"); + return; + } + + for (size_t i = 0; i < descriptors.size(); i++) { + int32_t caSystemId = descriptors[i].caSystemId; + + ASSERT_TRUE(createCasPlugin(caSystemId)); + } +} + +TEST_P(MediaCasAidlTest, TestInvalidSystemIdFails) { + description("Test failure for invalid system ID"); + + bool isSystemIdSupported; + auto status = mService->isSystemIdSupported(INVALID_SYSTEM_ID, &isSystemIdSupported); + + EXPECT_TRUE(status.isOk()); + ASSERT_FALSE(isSystemIdSupported); + + bool isDescramblerSupported; + status = mService->isDescramblerSupported(INVALID_SYSTEM_ID, &isDescramblerSupported); + + EXPECT_TRUE(status.isOk()); + ASSERT_FALSE(isDescramblerSupported); + + shared_ptr mediaCas; + shared_ptr casListener = SharedRefBase::make(); + status = mService->createPlugin(INVALID_SYSTEM_ID, casListener, &mediaCas); + ASSERT_TRUE(status.isOk()); + EXPECT_EQ(mediaCas, nullptr); + + shared_ptr descrambler; + status = mService->createDescrambler(INVALID_SYSTEM_ID, &descrambler); + ASSERT_TRUE(status.isOk()); + EXPECT_EQ(descrambler, nullptr); +} + +TEST_P(MediaCasAidlTest, TestClearKeyPluginInstalled) { + description("Test if ClearKey plugin is installed"); + + vector descriptors; + EXPECT_TRUE(mService->enumeratePlugins(&descriptors).isOk()); + + if (descriptors.size() == 0) { + ALOGW("[ WARN ] enumeratePlugins list empty"); + } + + for (size_t i = 0; i < descriptors.size(); i++) { + int32_t caSystemId = descriptors[i].caSystemId; + if (CLEAR_KEY_SYSTEM_ID == caSystemId) { + return; + } + } + + ADD_FAILURE() << "ClearKey plugin not installed"; +} + +TEST_P(MediaCasAidlTest, TestClearKeySessionClosedAfterRelease) { + description("Test that all sessions are closed after a MediaCas object is released"); + + ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID)); + + EXPECT_TRUE(mMediaCas->provision(PROVISION_STR).isOk()); + + SessionIntent intent = SessionIntent::LIVE; + ScramblingMode mode = ScramblingMode::DVB_CSA1; + + vector sessionId; + ASSERT_TRUE(openCasSession(&sessionId, intent, mode)); + + vector streamSessionId; + ASSERT_TRUE(openCasSession(&streamSessionId, intent, mode)); + + EXPECT_TRUE(mMediaCas->release().isOk()); + + if (mDescrambler != nullptr) { + auto status = mDescrambler->setMediaCasSession(sessionId); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, status.getServiceSpecificError()); + + status = mDescrambler->setMediaCasSession(streamSessionId); + EXPECT_FALSE(status.isOk()); + EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, status.getServiceSpecificError()); + } +} + +TEST_P(MediaCasAidlTest, TestClearKeyErrors) { + description("Test that invalid call sequences fail with expected error codes"); + + ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID)); + + /* + * Test MediaCas error codes + */ + // Provision should fail with an invalid asset string + auto returnStatus = mMediaCas->provision("invalid asset string"); + EXPECT_FALSE(returnStatus.isOk()); + EXPECT_EQ(Status::ERROR_CAS_NO_LICENSE, returnStatus.getServiceSpecificError()); + + SessionIntent intent = SessionIntent::LIVE; + ScramblingMode mode = ScramblingMode::DVB_CSA1; + + // Open a session, then close it so that it should become invalid + vector invalidSessionId; + ASSERT_TRUE(openCasSession(&invalidSessionId, intent, mode)); + EXPECT_TRUE(mMediaCas->closeSession(invalidSessionId).isOk()); + + // processEcm should fail with an invalid session id + vector ecm(kEcmBinaryBuffer, kEcmBinaryBuffer + sizeof(kEcmBinaryBuffer)); + returnStatus = mMediaCas->processEcm(invalidSessionId, ecm); + EXPECT_FALSE(returnStatus.isOk()); + EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, returnStatus.getServiceSpecificError()); + + vector sessionId; + ASSERT_TRUE(openCasSession(&sessionId, intent, mode)); + + // processEcm should fail without provisioning + returnStatus = mMediaCas->processEcm(sessionId, ecm); + EXPECT_FALSE(returnStatus.isOk()); + EXPECT_EQ(Status::ERROR_CAS_NOT_PROVISIONED, returnStatus.getServiceSpecificError()); + + EXPECT_TRUE(mMediaCas->provision(PROVISION_STR).isOk()); + + // processEcm should fail with ecm with bad descriptor count + ecm[17] = 0x03; // change the descriptor count field to 3 (invalid) + returnStatus = mMediaCas->processEcm(sessionId, ecm); + EXPECT_FALSE(returnStatus.isOk()); + EXPECT_EQ(Status::ERROR_CAS_UNKNOWN, returnStatus.getServiceSpecificError()); + + // processEcm should fail with ecm buffer that's too short + ecm.resize(8); + returnStatus = mMediaCas->processEcm(sessionId, ecm); + EXPECT_FALSE(returnStatus.isOk()); + EXPECT_EQ(Status::BAD_VALUE, returnStatus.getServiceSpecificError()); + + if (mDescrambler != nullptr) { + /* + * Test MediaDescrambler error codes + */ + // setMediaCasSession should fail with an invalid session id + returnStatus = mDescrambler->setMediaCasSession(invalidSessionId); + EXPECT_FALSE(returnStatus.isOk()); + EXPECT_EQ(Status::ERROR_CAS_SESSION_NOT_OPENED, returnStatus.getServiceSpecificError()); + + // descramble should fail without a valid session + ScopedAStatus descrambleStatus = ScopedAStatus::ok(); + uint8_t* sharedBuffer = nullptr; + + ASSERT_FALSE(descrambleTestInputBuffer(mDescrambler, descrambleStatus, sharedBuffer)); + EXPECT_EQ(Status::ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED, + descrambleStatus.getServiceSpecificError()); + + // Now set a valid session, should still fail because no valid ecm is processed + EXPECT_TRUE(mDescrambler->setMediaCasSession(sessionId).isOk()); + ASSERT_FALSE(descrambleTestInputBuffer(mDescrambler, descrambleStatus, sharedBuffer)); + EXPECT_EQ(Status::ERROR_CAS_DECRYPT, descrambleStatus.getServiceSpecificError()); + + // Verify that requiresSecureDecoderComponent handles empty mime + bool requiresSecureDecoderComponent = true; + EXPECT_TRUE( + mDescrambler->requiresSecureDecoderComponent("", &requiresSecureDecoderComponent) + .isOk()); + EXPECT_FALSE(requiresSecureDecoderComponent); + + // Verify that requiresSecureDecoderComponent handles invalid mime + requiresSecureDecoderComponent = true; + EXPECT_TRUE( + mDescrambler->requiresSecureDecoderComponent("bad", &requiresSecureDecoderComponent) + .isOk()); + EXPECT_FALSE(requiresSecureDecoderComponent); + } +} + +TEST_P(MediaCasAidlTest, TestClearKeyApisWithSession) { + description("Test that valid call sequences with SessionEvent send and receive"); + + ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID)); + + EXPECT_TRUE(mMediaCas->provision(PROVISION_STR).isOk()); + + vector pvtData; + pvtData.resize(256); + EXPECT_TRUE(mMediaCas->setPrivateData(pvtData).isOk()); + + SessionIntent intent = SessionIntent::LIVE; + ScramblingMode mode = ScramblingMode::DVB_CSA1; + + vector sessionId; + ASSERT_TRUE(openCasSession(&sessionId, intent, mode)); + EXPECT_TRUE(mMediaCas->setSessionPrivateData(sessionId, pvtData).isOk()); + + vector streamSessionId; + ASSERT_TRUE(openCasSession(&streamSessionId, intent, mode)); + EXPECT_TRUE(mMediaCas->setSessionPrivateData(streamSessionId, pvtData).isOk()); + + if (mDescrambler != nullptr) { + EXPECT_TRUE(mDescrambler->setMediaCasSession(sessionId).isOk()); + EXPECT_TRUE(mDescrambler->setMediaCasSession(streamSessionId).isOk()); + } + + vector nullPtrVector(0); + EXPECT_TRUE(mMediaCas->refreshEntitlements(3, nullPtrVector).isOk()); + + vector refreshData{0, 1, 2, 3}; + EXPECT_TRUE(mMediaCas->refreshEntitlements(10, refreshData).isOk()); + + int32_t eventID = 1; + int32_t eventArg = 2; + mCasListener->testEventEcho(mMediaCas, eventID, eventArg, nullPtrVector); + mCasListener->testSessionEventEcho(mMediaCas, sessionId, eventID, eventArg, nullPtrVector); + + eventID = 3; + eventArg = 4; + vector eventData{'e', 'v', 'e', 'n', 't', 'd', 'a', 't', 'a'}; + mCasListener->testEventEcho(mMediaCas, eventID, eventArg, eventData); + mCasListener->testSessionEventEcho(mMediaCas, sessionId, eventID, eventArg, eventData); + + mCasListener->testStatusUpdate(mMediaCas, &sessionId, intent, mode); + + vector clearKeyEmmData{'c', 'l', 'e', 'a', 'r', 'k', 'e', 'y', 'e', 'm', 'm'}; + EXPECT_TRUE(mMediaCas->processEmm(clearKeyEmmData).isOk()); + + vector ecm(kEcmBinaryBuffer, kEcmBinaryBuffer + sizeof(kEcmBinaryBuffer)); + EXPECT_TRUE(mMediaCas->processEcm(sessionId, ecm).isOk()); + EXPECT_TRUE(mMediaCas->processEcm(streamSessionId, ecm).isOk()); + + if (mDescrambler != nullptr) { + bool requiresSecureDecoderComponent = true; + EXPECT_TRUE(mDescrambler + ->requiresSecureDecoderComponent("video/avc", + &requiresSecureDecoderComponent) + .isOk()); + EXPECT_FALSE(requiresSecureDecoderComponent); + + ScopedAStatus descrambleStatus = ScopedAStatus::ok(); + uint8_t* sharedBuffer = nullptr; + + ASSERT_TRUE(descrambleTestInputBuffer(mDescrambler, descrambleStatus, sharedBuffer)); + + int compareResult = + memcmp(static_cast(sharedBuffer), + static_cast(kOutRefBinaryBuffer), sizeof(kOutRefBinaryBuffer)); + EXPECT_EQ(0, compareResult); + + EXPECT_TRUE(mDescrambler->release().isOk()); + } + + EXPECT_TRUE(mMediaCas->release().isOk()); +} + +TEST_P(MediaCasAidlTest, TestClearKeyOobFails) { + description("Test that oob descramble request fails with expected error"); + + ASSERT_TRUE(createCasPlugin(CLEAR_KEY_SYSTEM_ID)); + EXPECT_TRUE(mMediaCas->provision(PROVISION_STR).isOk()); + + SessionIntent intent = SessionIntent::LIVE; + ScramblingMode mode = ScramblingMode::DVB_CSA1; + + vector sessionId; + ASSERT_TRUE(openCasSession(&sessionId, intent, mode)); + + if (mDescrambler != nullptr) { + EXPECT_TRUE(mDescrambler->setMediaCasSession(sessionId).isOk()); + } + + vector ecm(kEcmBinaryBuffer, kEcmBinaryBuffer + sizeof(kEcmBinaryBuffer)); + EXPECT_TRUE(mMediaCas->processEcm(sessionId, ecm).isOk()); + + if (mDescrambler != nullptr) { + ScopedAStatus descrambleStatus = ScopedAStatus::ok(); + + // test invalid src buffer offset + ASSERT_FALSE( + descrambleTestOobInput(mDescrambler, descrambleStatus, + {.subSamples = kSubSamples, + .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample), + .imemSizeActual = sizeof(kInBinaryBuffer), + .imemOffset = 0xcccccc, + .imemSize = sizeof(kInBinaryBuffer), + .srcOffset = 0, + .dstOffset = 0})); + EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError()); + + // test invalid src buffer size + ASSERT_FALSE( + descrambleTestOobInput(mDescrambler, descrambleStatus, + {.subSamples = kSubSamples, + .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample), + .imemSizeActual = sizeof(kInBinaryBuffer), + .imemOffset = 0, + .imemSize = 0xcccccc, + .srcOffset = 0, + .dstOffset = 0})); + EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError()); + + // test invalid src buffer size + ASSERT_FALSE( + descrambleTestOobInput(mDescrambler, descrambleStatus, + {.subSamples = kSubSamples, + .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample), + .imemSizeActual = sizeof(kInBinaryBuffer), + .imemOffset = 1, + .imemSize = -1, + .srcOffset = 0, + .dstOffset = 0})); + EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError()); + + // test invalid srcOffset + ASSERT_FALSE( + descrambleTestOobInput(mDescrambler, descrambleStatus, + {.subSamples = kSubSamples, + .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample), + .imemSizeActual = sizeof(kInBinaryBuffer), + .imemOffset = 0, + .imemSize = sizeof(kInBinaryBuffer), + .srcOffset = 0xcccccc, + .dstOffset = 0})); + EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError()); + + // test invalid dstOffset + ASSERT_FALSE( + descrambleTestOobInput(mDescrambler, descrambleStatus, + {.subSamples = kSubSamples, + .numSubSamples = sizeof(kSubSamples) / sizeof(SubSample), + .imemSizeActual = sizeof(kInBinaryBuffer), + .imemOffset = 0, + .imemSize = sizeof(kInBinaryBuffer), + .srcOffset = 0, + .dstOffset = 0xcccccc})); + EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError()); + + // test detection of oob subsample sizes + const SubSample invalidSubSamples1[] = {{162, 0}, {0, 184}, {0, 0xdddddd}}; + + ASSERT_FALSE(descrambleTestOobInput( + mDescrambler, descrambleStatus, + {.subSamples = invalidSubSamples1, + .numSubSamples = sizeof(invalidSubSamples1) / sizeof(SubSample), + .imemSizeActual = sizeof(kInBinaryBuffer), + .imemOffset = 0, + .imemSize = sizeof(kInBinaryBuffer), + .srcOffset = 0, + .dstOffset = 0})); + EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError()); + + // test detection of overflowing subsample sizes + const SubSample invalidSubSamples2[] = {{162, 0}, {0, 184}, {2, -1}}; + + ASSERT_FALSE(descrambleTestOobInput( + mDescrambler, descrambleStatus, + {.subSamples = invalidSubSamples2, + .numSubSamples = sizeof(invalidSubSamples2) / sizeof(SubSample), + .imemSizeActual = sizeof(kInBinaryBuffer), + .imemOffset = 0, + .imemSize = sizeof(kInBinaryBuffer), + .srcOffset = 0, + .dstOffset = 0})); + EXPECT_EQ(Status::BAD_VALUE, descrambleStatus.getServiceSpecificError()); + + EXPECT_TRUE(mDescrambler->release().isOk()); + } + EXPECT_TRUE(mMediaCas->release().isOk()); +} + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MediaCasAidlTest); + +INSTANTIATE_TEST_SUITE_P( + PerInstance, MediaCasAidlTest, + testing::ValuesIn(android::getAidlHalInstanceNames(IMediaCasService::descriptor)), + android::PrintInstanceNameToString); + +// Start thread pool to receive callbacks from AIDL service. +int main(int argc, char** argv) { + InitGoogleTest(&argc, argv); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + return RUN_ALL_TESTS(); +} diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index d4be2bf860..f0fe2c6ef4 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -199,6 +199,13 @@ default + + android.hardware.cas + + IMediaCasService + default + + android.hardware.confirmationui 1