diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:07:18 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:07:18 +0000 |
commit | c6c1ea7c835fbd565b67ea9ae61683f57a1c509f (patch) | |
tree | 387d2f7774be844577676dfc96065f00a1d41a70 | |
parent | d97725cd738aaa9a00989ad769dd4da8b5fe515d (diff) | |
parent | 71e487c437e0d3d33023e4a6357528a771950abe (diff) | |
download | SecureElement-aml_ext_331112010.tar.gz |
Snap for 8564071 from 71e487c437e0d3d33023e4a6357528a771950abe to mainline-extservices-releaseaml_ext_331814220aml_ext_331412000aml_ext_331312000aml_ext_331112010aml_ext_331012020android13-mainline-extservices-release
Change-Id: I668066ba3b813831c817b3ec8abcc0bf3589649f
22 files changed, 806 insertions, 303 deletions
@@ -1,3 +1,23 @@ +package { + default_applicable_licenses: [ + "packages_apps_SecureElement_license", + "Android-Apache-2.0", + ], +} + +// See: http://go/android-license-faq +license { + name: "packages_apps_SecureElement_license", + package_name: "Android Secure Element", + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "SPDX-license-identifier-BSD", + ], + license_text: [ + "NOTICE", + ], +} + genrule { name: "statslog-secure-element-java-gen", tools: ["stats-log-api-gen"], diff --git a/AndroidManifest.xml b/AndroidManifest.xml index a26b112..33477eb 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,14 +1,18 @@ <?xml version="1.0" encoding="utf-8"?> + <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.se" - android:sharedUserId="android.uid.se"> + package="com.android.se" + android:sharedUserId="android.uid.se"> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> <application android:name=".SEApplication" - android:label="SecureElementApplication" - android:persistent="true" - android:directBootAware="true"> + android:label="SecureElementApplication" + android:persistent="true" + android:directBootAware="true" + android:memtagMode="async"> <service android:name=".SecureElementService" - android:visibleToInstantApps="true"> + android:visibleToInstantApps="true" + android:exported="true"> <intent-filter> <action android:name="android.se.omapi.ISecureElementService"/> </intent-filter> @@ -40,196 +40,3 @@ See the License for the specific language governing permissions and limitations under the License. - ---------------------------------------------------------------------------- - - Copyright (C) 2011, 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/TEST_MAPPING b/TEST_MAPPING new file mode 100644 index 0000000..323ab4d --- /dev/null +++ b/TEST_MAPPING @@ -0,0 +1,8 @@ +{ + "postsubmit": [ + { + "name": "SecureElementInstrumentationTests", + "keywords": ["primary-device"] + } + ] +} diff --git a/etc/hal_uuid_map_config.xml b/etc/hal_uuid_map_config.xml new file mode 100644 index 0000000..5388697 --- /dev/null +++ b/etc/hal_uuid_map_config.xml @@ -0,0 +1,41 @@ +<!-- Vendor mapping file --> +<!-- Sample UUID to list of UIDs mapping file --> + +<!-- UUID: Universally Unique IDentifier --> +<!-- 16 Byte UUID need to be generated by vendors to add new entry --> +<!-- As per global platform access control spec, UUID is expected to be of --> +<!-- length 20 bytes. While using this UUID, it is expected to be --> +<!-- automatically padded with ffffffff in initial 4 bytes of 20 Byte length --> + +<!-- UID: user identifier of the service --> + +<!-- This mapping file should contain an entry for VTS tests, since VTS --> +<!-- tests run as root, user identifier 0 should be mapped to its --> +<!-- corresponding UUID to allow VTS tests to access secure element --> +<!-- For VTS tests use UID: 0 and UUID: 9f36407ead0639fc966f14dde7970f68 --> + +<ref_do> + <!-- mapping entries to map unique identifiers to device hal services --> + <!-- uids --> + + <!-- UUID would be automatically padding with ffffffff to fulfill 20 --> + <!-- bytes in access rule. For example for --> + <!-- UUID:9f36407ead0639fc966f14dde7970f68 after padding it should look --> + <!-- like ffffffff9f36407ead0639fc966f14dde7970f68 --> + <uuid_ref_do> + <uids> + <uid>0</uid> + </uids> + <uuid>9f36407ead0639fc966f14dde7970f68</uuid> + </uuid_ref_do> + + <!-- Sample mapping entry with UIDs:1096 and 1097 mapped to --> + <!-- UUID:9f36407ead0639fc966f14dde7970f68 --> + <uuid_ref_do> + <uids> + <uid>1096</uid> + <uid>1097</uid> + </uids> + <uuid>a9b7ba70783b317e9998dc4dd82eb3c5</uuid> + </uuid_ref_do> +</ref_do> diff --git a/res/values/config.xml b/res/values/config.xml index cc310a0..5811b10 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -3,4 +3,8 @@ <!-- The list of AIDs are the candidate of the ARA AID in ESE. The first available AID will be taken as the ARA AID. --> <string-array name="config_ara_aid_candidate_list_ese" translatable="false" /> + + <!-- To enable vendor stable service, set this to true and + make sure its vntf manifest entry is also configured. --> + <bool name="secure_element_vintf_enabled">false</bool> </resources> diff --git a/secure_element-service.xml b/secure_element-service.xml new file mode 100644 index 0000000..37c7465 --- /dev/null +++ b/secure_element-service.xml @@ -0,0 +1,10 @@ +<manifest version="1.0" type="framework"> + <hal format="aidl"> + <name>android.se.omapi</name> + <version>1</version> + <interface> + <name>ISecureElementService</name> + <instance>default</instance> + </interface> + </hal> +</manifest> diff --git a/src/com/android/se/Channel.java b/src/com/android/se/Channel.java index 4fbb2a0..73622fc 100644..100755 --- a/src/com/android/se/Channel.java +++ b/src/com/android/se/Channel.java @@ -89,21 +89,23 @@ public class Channel implements IBinder.DeathRecipient { /** * Closes the channel. */ - public synchronized void close() { + public void close() { synchronized (mLock) { - if (isBasicChannel()) { - Log.i(mTag, "Close basic channel - Select without AID ..."); - mTerminal.selectDefaultApplication(); - } - - mTerminal.closeChannel(this); + if (isClosed()) + return; mIsClosed = true; - if (mBinder != null) { - mBinder.unlinkToDeath(this, 0); - } - if (mSession != null) { - mSession.removeChannel(this); - } + } + if (isBasicChannel()) { + Log.i(mTag, "Close basic channel - Select without AID ..."); + mTerminal.selectDefaultApplication(); + } + + mTerminal.closeChannel(this); + if (mBinder != null) { + mBinder.unlinkToDeath(this, 0); + } + if (mSession != null) { + mSession.removeChannel(this); } } @@ -211,7 +213,7 @@ public class Channel implements IBinder.DeathRecipient { cla = (byte) ((cla & 0xBC) | channelNumber); } else if (channelNumber < 20) { // b7 = 1 indicates the further interindustry class byte coding - boolean isSm = (cla & 0x0C) != 0; + boolean isSm = (((cla & 0x40) == 0x00) && ((cla & 0x0C) != 0)); cla = (byte) ((cla & 0xB0) | 0x40 | (channelNumber - 4)); if (isSm) { cla |= 0x20; @@ -328,5 +330,15 @@ public class Channel implements IBinder.DeathRecipient { throw new ServiceSpecificException(SEService.IO_ERROR, e.getMessage()); } } + + @Override + public String getInterfaceHash() { + return ISecureElementChannel.HASH; + } + + @Override + public int getInterfaceVersion() { + return ISecureElementChannel.VERSION; + } } } diff --git a/src/com/android/se/SecureElementService.java b/src/com/android/se/SecureElementService.java index 758fc77..92b3b51 100644 --- a/src/com/android/se/SecureElementService.java +++ b/src/com/android/se/SecureElementService.java @@ -34,6 +34,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; +import android.os.UserHandle; import android.se.omapi.ISecureElementChannel; import android.se.omapi.ISecureElementListener; import android.se.omapi.ISecureElementReader; @@ -45,6 +46,7 @@ import android.util.Log; import com.android.se.Terminal.SecureElementReader; import com.android.se.internal.ByteArrayConverter; +import com.android.se.security.HalRefDoParser; import java.io.FileDescriptor; import java.io.IOException; @@ -54,6 +56,7 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.NoSuchElementException; +import java.util.Vector; /** * Underlying implementation for OMAPI SEService @@ -62,51 +65,110 @@ public final class SecureElementService extends Service { public static final String UICC_TERMINAL = "SIM"; public static final String ESE_TERMINAL = "eSE"; + public static final String VSTABLE_SECURE_ELEMENT_SERVICE = + "android.se.omapi.ISecureElementService/default"; private final String mTag = "SecureElementService"; private static final boolean DEBUG = Build.IS_DEBUGGABLE; // LinkedHashMap will maintain the order of insertion private LinkedHashMap<String, Terminal> mTerminals = new LinkedHashMap<String, Terminal>(); private int mActiveSimCount = 0; - private final ISecureElementService.Stub mSecureElementServiceBinder = - new ISecureElementService.Stub() { + private class SecureElementServiceBinder extends ISecureElementService.Stub { - @Override - public String[] getReaders() throws RemoteException { - return mTerminals.keySet().toArray(new String[mTerminals.size()]); + @Override + public String[] getReaders() throws RemoteException { + try { + // This determines calling process is application/framework + String packageName = getPackageNameFromCallingUid(Binder.getCallingUid()); + Log.d(mTag, "getReaders() for " + packageName); + return mTerminals.keySet().toArray(new String[mTerminals.size()]); + } catch (AccessControlException e) { + // since packagename not found, UUID might be used to access + // allow only to use eSE readers with UUID based requests + Vector<String> eSEReaders = new Vector<String>(); + for (String reader : mTerminals.keySet()) { + if (reader.startsWith(SecureElementService.ESE_TERMINAL)) { + Log.i(mTag, "Adding Reader: " + reader); + eSEReaders.add(reader); + } } - @Override - public ISecureElementReader getReader(String reader) - throws RemoteException { - Log.d(mTag, "getReader() " + reader); - Terminal terminal = getTerminal(reader); - return terminal.new SecureElementReader(SecureElementService.this); - } + return eSEReaders.toArray(new String[eSEReaders.size()]); + } + } - @Override - public synchronized boolean[] isNFCEventAllowed(String reader, byte[] aid, - String[] packageNames) - throws RemoteException { - if (aid == null || aid.length == 0) { - aid = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00}; - } - if (aid.length < 5 || aid.length > 16) { - throw new IllegalArgumentException("AID out of range"); - } - if (packageNames == null || packageNames.length == 0) { - throw new IllegalArgumentException("package names not specified"); - } - Terminal terminal = getTerminal(reader); - return terminal.isNfcEventAllowed(getPackageManager(), aid, packageNames); + @Override + public ISecureElementReader getReader(String reader) throws RemoteException { + Log.d(mTag, "getReader() " + reader); + Terminal terminal = null; + try { + // This determines calling process is application/framework + String packageName = getPackageNameFromCallingUid(Binder.getCallingUid()); + Log.d(mTag, "getReader() for " + packageName); + terminal = getTerminal(reader); + } catch (AccessControlException e) { + // since packagename not found, UUID might be used to access + // allow only to use eSE readers with UUID based requests + if (reader.startsWith(SecureElementService.ESE_TERMINAL)) { + terminal = getTerminal(reader); + } else { + Log.d(mTag, "only eSE readers can access SE using UUID"); } + } + if (terminal != null) { + return terminal.new SecureElementReader(SecureElementService.this); + } else { + throw new IllegalArgumentException("Reader: " + reader + " not supported"); + } + } - @Override - protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - for (Terminal terminal : mTerminals.values()) { - terminal.dump(writer); - } - } - }; + @Override + public synchronized boolean[] isNfcEventAllowed(String reader, byte[] aid, + String[] packageNames, int userId) throws RemoteException { + if (aid == null || aid.length == 0) { + aid = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00}; + } + if (aid.length < 5 || aid.length > 16) { + throw new IllegalArgumentException("AID out of range"); + } + if (packageNames == null || packageNames.length == 0) { + throw new IllegalArgumentException("package names not specified"); + } + Terminal terminal = getTerminal(reader); + Context context; + try { + context = createContextAsUser(UserHandle.of(userId), /*flags=*/0); + } catch (IllegalStateException e) { + context = null; + Log.d(mTag, "fail to call createContextAsUser for userId:" + userId); + } + return context == null ? null : terminal.isNfcEventAllowed( + context.getPackageManager(), aid, packageNames); + + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + for (Terminal terminal : mTerminals.values()) { + terminal.dump(writer); + } + } + + @Override + public String getInterfaceHash() { + return ISecureElementService.HASH; + } + + @Override + public int getInterfaceVersion() { + return ISecureElementService.VERSION; + } + } + + private final ISecureElementService.Stub mSecureElementServiceBinder = + new SecureElementServiceBinder(); + + private final ISecureElementService.Stub mSecureElementServiceBinderVntf = + new SecureElementServiceBinder(); public SecureElementService() { super(); @@ -148,6 +210,17 @@ public final class SecureElementService extends Service { Log.i(mTag, Thread.currentThread().getName() + " onCreate"); initialize(); createTerminals(); + + // Add vendor stable service only if it is configured + if (getResources().getBoolean(R.bool.secure_element_vintf_enabled)) { + ServiceManager.addService(VSTABLE_SECURE_ELEMENT_SERVICE, + mSecureElementServiceBinderVntf); + } + + // Since ISecureElementService is marked with VINTF stability + // to use this same interface within the system partition, will use + // forceDowngradeToSystemStability and register it. + mSecureElementServiceBinder.forceDowngradeToSystemStability(); ServiceManager.addService(Context.SECURE_ELEMENT_SERVICE, mSecureElementServiceBinder); } @@ -232,6 +305,16 @@ public final class SecureElementService extends Service { throw new AccessControlException("PackageName can not be determined"); } + private byte[] getUUIDFromCallingUid(int uid) { + byte[] uuid = HalRefDoParser.getInstance().findUUID(Binder.getCallingUid()); + + if (uuid != null) { + return uuid; + } + + return null; + } + final class SecureElementSession extends ISecureElementSession.Stub { private final SecureElementReader mReader; @@ -314,12 +397,29 @@ public final class SecureElementService extends Service { + String.format("%02x ", p2 & 0xFF)); } - String packageName = getPackageNameFromCallingUid(Binder.getCallingUid()); + String packageName = null; + byte[] uuid = null; + try { + packageName = getPackageNameFromCallingUid(Binder.getCallingUid()); + } catch (AccessControlException e) { + // Since packageName not found for calling process, try to find UUID mapping + // provided by vendors for the calling process UID + // (vendor provide UUID mapping for native services to access secure element) + Log.d(mTag, "openBasicChannel() trying to find mapping uuid"); + // Allow UUID based access only on embedded secure elements eSE. + if (mReader.getTerminal().getName().startsWith(SecureElementService.ESE_TERMINAL)) { + uuid = getUUIDFromCallingUid(Binder.getCallingUid()); + } + if (uuid == null) { + Log.e(mTag, "openBasicChannel() uuid mapping for calling uid is not found"); + throw e; + } + } Channel channel = null; try { channel = mReader.getTerminal().openBasicChannel(this, aid, p2, listener, - packageName, Binder.getCallingPid()); + packageName, uuid, Binder.getCallingPid()); } catch (IOException e) { throw new ServiceSpecificException(SEService.IO_ERROR, e.getMessage()); } catch (NoSuchElementException e) { @@ -333,7 +433,9 @@ public final class SecureElementService extends Service { Log.i(mTag, "Open basic channel success. Channel: " + channel.getChannelNumber()); - mChannels.add(channel); + synchronized (mLock) { + mChannels.add(channel); + } return channel.new SecureElementChannel(); } @@ -354,12 +456,29 @@ public final class SecureElementService extends Service { + String.format("%02x ", p2 & 0xFF)); } - String packageName = getPackageNameFromCallingUid(Binder.getCallingUid()); + String packageName = null; + byte[] uuid = null; + try { + packageName = getPackageNameFromCallingUid(Binder.getCallingUid()); + } catch (AccessControlException e) { + // Since packageName not found for calling process, try to find UUID mapping + // provided by vendors for the calling process UID + // (vendor provide UUID mapping for native services to access secure element) + Log.d(mTag, "openLogicalChannel() trying to find mapping uuid"); + // Allow UUID based access only on embedded secure elements eSE. + if (mReader.getTerminal().getName().startsWith(SecureElementService.ESE_TERMINAL)) { + uuid = getUUIDFromCallingUid(Binder.getCallingUid()); + } + if (uuid == null) { + Log.e(mTag, "openLogicalChannel() uuid mapping for calling uid is not found"); + throw e; + } + } Channel channel = null; try { channel = mReader.getTerminal().openLogicalChannel(this, aid, p2, listener, - packageName, Binder.getCallingPid()); + packageName, uuid, Binder.getCallingPid()); } catch (IOException e) { throw new ServiceSpecificException(SEService.IO_ERROR, e.getMessage()); } catch (NoSuchElementException e) { @@ -373,9 +492,21 @@ public final class SecureElementService extends Service { Log.i(mTag, "openLogicalChannel() Success. Channel: " + channel.getChannelNumber()); - mChannels.add(channel); + synchronized (mLock) { + mChannels.add(channel); + } return channel.new SecureElementChannel(); } + + @Override + public String getInterfaceHash() { + return ISecureElementSession.HASH; + } + + @Override + public int getInterfaceVersion() { + return ISecureElementSession.VERSION; + } } private final BroadcastReceiver mMultiSimConfigChangedReceiver = new BroadcastReceiver() { diff --git a/src/com/android/se/Terminal.java b/src/com/android/se/Terminal.java index 54dc256..d338cf8 100644 --- a/src/com/android/se/Terminal.java +++ b/src/com/android/se/Terminal.java @@ -24,6 +24,7 @@ package com.android.se; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -31,12 +32,14 @@ import android.hardware.secure_element.V1_0.ISecureElement; import android.hardware.secure_element.V1_0.ISecureElementHalCallback; import android.hardware.secure_element.V1_0.LogicalChannelResponse; import android.hardware.secure_element.V1_0.SecureElementStatus; +import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.HwBinder; import android.os.Message; import android.os.RemoteException; import android.os.ServiceSpecificException; +import android.os.UserHandle; import android.se.omapi.ISecureElementListener; import android.se.omapi.ISecureElementReader; import android.se.omapi.ISecureElementSession; @@ -158,9 +161,19 @@ public class Terminal { reason, mName); } + + sendStateChangedBroadcast(state); } } + private void sendStateChangedBroadcast(boolean state) { + Intent intent = new Intent(SEService.ACTION_SECURE_ELEMENT_STATE_CHANGED); + intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(SEService.EXTRA_READER_NAME, mName); + intent.putExtra(SEService.EXTRA_READER_STATE, state); + mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT); + }; + class SecureElementDeathRecipient implements HwBinder.DeathRecipient { @Override public void serviceDied(long cookie) { @@ -319,11 +332,13 @@ public class Terminal { /** * Cleans up all the channels in use. */ - public synchronized void closeChannels() { - Collection<Channel> col = mChannels.values(); - Channel[] channelList = col.toArray(new Channel[col.size()]); - for (Channel channel : channelList) { - channel.close(); + public void closeChannels() { + synchronized (mLock) { + Collection<Channel> col = mChannels.values(); + Channel[] channelList = col.toArray(new Channel[col.size()]); + for (Channel channel : channelList) { + channel.close(); + } } } @@ -418,9 +433,11 @@ public class Terminal { /** * Opens a Basic Channel with the given AID and P2 paramters + * with the given device app reference package name or uuid */ public Channel openBasicChannel(SecureElementSession session, byte[] aid, byte p2, - ISecureElementListener listener, String packageName, int pid) throws IOException, + ISecureElementListener listener, String packageName, byte[] uuid, + int pid) throws IOException, NoSuchElementException { if (aid != null && aid.length == 0) { aid = null; @@ -432,19 +449,31 @@ public class Terminal { ChannelAccess channelAccess = null; if (packageName != null) { - Log.w(mTag, "Enable access control on basic channel for " + packageName); + Log.w(mTag, "Enable access control on basic channel for package name: " + + packageName); SecureElementStatsLog.write( SecureElementStatsLog.SE_OMAPI_REPORTED, SecureElementStatsLog.SE_OMAPI_REPORTED__OPERATION__OPEN_CHANNEL, mName, packageName); - try { - // For application without privilege permission or carrier privilege, - // openBasicChannel with UICC terminals should be rejected. - channelAccess = setUpChannelAccess(aid, packageName, pid, true); - } catch (MissingResourceException e) { - return null; + } else if (uuid != null) { + Log.w(mTag, "Enable access control on basic channel for uid: " + + Binder.getCallingUid() + + " UUID: " + Arrays.toString(uuid)); + SecureElementStatsLog.write( + SecureElementStatsLog.SE_OMAPI_REPORTED, + SecureElementStatsLog.SE_OMAPI_REPORTED__OPERATION__OPEN_CHANNEL, + mName, + Arrays.toString(uuid)); + } + try { + // For application without privilege permission or carrier privilege, + // openBasicChannel with UICC terminals should be rejected. + if (packageName != null || uuid != null) { + channelAccess = setUpChannelAccess(aid, packageName, uuid, pid, true); } + } catch (MissingResourceException e) { + return null; } synchronized (mLock) { @@ -502,14 +531,15 @@ public class Terminal { */ public Channel openLogicalChannelWithoutChannelAccess(byte[] aid) throws IOException, NoSuchElementException { - return openLogicalChannel(null, aid, (byte) 0x00, null, null, 0); + return openLogicalChannel(null, aid, (byte) 0x00, null, null, null, 0); } /** - * Opens a logical Channel with AID. + * Opens a logical Channel with AID for the given package name or uuid */ public Channel openLogicalChannel(SecureElementSession session, byte[] aid, byte p2, - ISecureElementListener listener, String packageName, int pid) throws IOException, + ISecureElementListener listener, String packageName, + byte[] uuid, int pid) throws IOException, NoSuchElementException { if (aid != null && aid.length == 0) { aid = null; @@ -527,11 +557,22 @@ public class Terminal { SecureElementStatsLog.SE_OMAPI_REPORTED__OPERATION__OPEN_CHANNEL, mName, packageName); - try { - channelAccess = setUpChannelAccess(aid, packageName, pid, false); - } catch (MissingResourceException | UnsupportedOperationException e) { - return null; + } else if (uuid != null) { + Log.w(mTag, "Enable access control on logical channel for uid: " + + Binder.getCallingUid() + + " UUID: " + Arrays.toString(uuid)); + SecureElementStatsLog.write( + SecureElementStatsLog.SE_OMAPI_REPORTED, + SecureElementStatsLog.SE_OMAPI_REPORTED__OPERATION__OPEN_CHANNEL, + mName, + Arrays.toString(uuid)); + } + try { + if (packageName != null || uuid != null) { + channelAccess = setUpChannelAccess(aid, packageName, uuid, pid, false); } + } catch (MissingResourceException | UnsupportedOperationException e) { + return null; } synchronized (mLock) { @@ -670,17 +711,14 @@ public class Terminal { */ public boolean[] isNfcEventAllowed(PackageManager packageManager, byte[] aid, String[] packageNames) { - // Attempt to initialize the access control enforcer if it failed in the previous attempt - // due to a kind of temporary failure or no rule was found. + if (!mIsConnected) { + // Return if not connected + return null; + } + // Return if the access control enforcer failed in previous attempt or no rule was found. if (mAccessControlEnforcer == null || mAccessControlEnforcer.isNoRuleFound()) { - try { - initializeAccessControl(); - // Just finished to initialize the access control enforcer. - // It is too much to check the refresh tag in this case. - } catch (Exception e) { - Log.i(mTag, "isNfcEventAllowed Exception: " + e.getMessage()); - return null; - } + Log.i(mTag, "isNfcEventAllowed: No access rules for checking."); + return null; } mAccessControlEnforcer.setPackageManager(packageManager); @@ -734,10 +772,10 @@ public class Terminal { /** * Initialize the Access Control and set up the channel access. */ - private ChannelAccess setUpChannelAccess(byte[] aid, String packageName, int pid, + private ChannelAccess setUpChannelAccess(byte[] aid, String packageName, byte[] uuid, int pid, boolean isBasicChannel) throws IOException, MissingResourceException { boolean checkRefreshTag = true; - if (isPrivilegedApplication(packageName)) { + if (packageName != null && isPrivilegedApplication(packageName)) { return ChannelAccess.getPrivilegeAccess(packageName, pid); } // Attempt to initialize the access control enforcer if it failed @@ -753,7 +791,7 @@ public class Terminal { mAccessControlEnforcer.setPackageManager(mContext.getPackageManager()); // Check carrier privilege when AID is not ISD-R - if (getName().startsWith(SecureElementService.UICC_TERMINAL) + if (packageName != null && getName().startsWith(SecureElementService.UICC_TERMINAL) && !Arrays.equals(aid, ISD_R_AID)) { try { PackageManager pm = mContext.getPackageManager(); @@ -783,7 +821,7 @@ public class Terminal { synchronized (mLock) { try { ChannelAccess channelAccess = - mAccessControlEnforcer.setUpChannelAccess(aid, packageName, + mAccessControlEnforcer.setUpChannelAccess(aid, packageName, uuid, checkRefreshTag); channelAccess.setCallingPid(pid); return channelAccess; @@ -926,8 +964,9 @@ public class Terminal { if (session == null) { throw new NullPointerException("session is null"); } - mSessions.remove(session); + synchronized (mLock) { + mSessions.remove(session); if (mSessions.size() == 0) { mDefaultApplicationSelectedOnBasicChannel = true; } @@ -956,5 +995,15 @@ public class Terminal { public boolean reset() { return Terminal.this.reset(); } + + @Override + public String getInterfaceHash() { + return ISecureElementReader.HASH; + } + + @Override + public int getInterfaceVersion() { + return ISecureElementReader.VERSION; + } } } diff --git a/src/com/android/se/internal/Util.java b/src/com/android/se/internal/Util.java index 8e9cfe3..8a52962 100755 --- a/src/com/android/se/internal/Util.java +++ b/src/com/android/se/internal/Util.java @@ -135,7 +135,7 @@ public class Util { cla = (byte) ((cla & 0xBC) | channelNumber); } else if (channelNumber < 20) { // b7 = 1 indicates the further interindustry class byte coding - boolean isSM = (cla & 0x0C) != 0; + boolean isSM = (((cla & 0x40) == 0x00) && ((cla & 0x0C) != 0)); cla = (byte) ((cla & 0xB0) | 0x40 | (channelNumber - 4)); if (isSM) { cla |= 0x20; diff --git a/src/com/android/se/security/AccessControlEnforcer.java b/src/com/android/se/security/AccessControlEnforcer.java index 6d595d8..e6f08cf 100644 --- a/src/com/android/se/security/AccessControlEnforcer.java +++ b/src/com/android/se/security/AccessControlEnforcer.java @@ -271,8 +271,8 @@ public class AccessControlEnforcer { } /** Sets up the Channel Access for the given Package */ - public ChannelAccess setUpChannelAccess(byte[] aid, String packageName, boolean checkRefreshTag) - throws IOException, MissingResourceException { + public ChannelAccess setUpChannelAccess(byte[] aid, String packageName, byte[] uuid, + boolean checkRefreshTag) throws IOException, MissingResourceException { ChannelAccess channelAccess = null; // check result of channel access during initialization procedure if (mInitialChannelAccess.getAccess() == ChannelAccess.ACCESS.DENIED) { @@ -281,7 +281,7 @@ public class AccessControlEnforcer { } // this is the new GP Access Control Enforcer implementation if (mUseAra || mUseArf) { - channelAccess = internal_setUpChannelAccess(aid, packageName, + channelAccess = internal_setUpChannelAccess(aid, packageName, uuid, checkRefreshTag); } if (channelAccess == null || (channelAccess.getApduAccess() != ChannelAccess.ACCESS.ALLOWED @@ -299,14 +299,23 @@ public class AccessControlEnforcer { } private synchronized ChannelAccess internal_setUpChannelAccess(byte[] aid, - String packageName, boolean checkRefreshTag) throws IOException, + String packageName, byte[] uuid, boolean checkRefreshTag) throws IOException, MissingResourceException { - if (packageName == null || packageName.isEmpty()) { + if (uuid == null && (packageName == null || packageName.isEmpty())) { throw new AccessControlException("package names must be specified"); } try { // estimate SHA-1 and SHA-256 hash values of the device application's certificate. - List<byte[]> appCertHashes = getAppCertHashes(packageName); + List<byte[]> appCertHashes = null; + if (packageName != null) { + appCertHashes = getAppCertHashes(packageName); + } else { + if (uuid != null) { + appCertHashes = new ArrayList<byte[]>(); + appCertHashes.add(uuid); + } + } + // APP certificates must be available => otherwise Exception if (appCertHashes == null || appCertHashes.size() == 0) { throw new AccessControlException( diff --git a/src/com/android/se/security/HalRefDoParser.java b/src/com/android/se/security/HalRefDoParser.java new file mode 100644 index 0000000..40ea7de --- /dev/null +++ b/src/com/android/se/security/HalRefDoParser.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2021 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 com.android.se.security; + +import android.os.Build; +import android.os.SystemProperties; +import android.util.Log; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Parser for HAL_UID_MAP.XML + * Parses the xml file and collects HAL references (UUID) to identify the corresponding + * access rules for the HAL services. + */ +public class HalRefDoParser { + + private static final boolean DEBUG = Build.IS_DEBUGGABLE; + private final String mTag = "SecureElement-HalRefDoParser"; + + private static final String PROP_PRODUCT_HARDWARE_SKU = "ro.boot.product.hardware.sku"; + private static final String UUID_MAPPING_CONFIG_PREFIX = "hal_uuid_map_"; + private static final String UUID_MAPPING_CONFIG_EXT = ".xml"; + private static final String[] UUID_MAPPING_CONFIG_PATHS = {"/odm/etc/", "/vendor/etc/", + "/etc/"}; + + // Holds UUID to UIDs mapping + private final Map<Integer, byte[]> mUUIDMap = new HashMap<Integer, byte[]>(); + + private static final String REF_DO = "ref_do"; + private static final String UUID_REF_DO = "uuid_ref_do"; + private static final String UUID = "uuid"; + private static final String UIDS = "uids"; + private static final String UID = "uid"; + + private static final byte[] PADDING_BYTES = { + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; + + private HalRefDoParser() { + parseUuidMappings(); + } + + private static class HalRefParserSingleton { + private static final HalRefDoParser INSTANCE = new HalRefDoParser(); + } + + public static HalRefDoParser getInstance() { + return HalRefParserSingleton.INSTANCE; + } + + private File getUuidMapConfigFile() { + // default file name: hal_uuid_map_config.xml + String uuid_map_config_file_name = UUID_MAPPING_CONFIG_PREFIX + + SystemProperties.get(PROP_PRODUCT_HARDWARE_SKU, "config") + + UUID_MAPPING_CONFIG_EXT; + String uuid_map_config_path = null; + + try { + // Search in predefined folders + for (String path : UUID_MAPPING_CONFIG_PATHS) { + uuid_map_config_path = path + uuid_map_config_file_name; + File confFile = new File(uuid_map_config_path); + if (confFile.exists()) { + Log.d(mTag, "UUID mapping config file path: " + uuid_map_config_path); + return confFile; + } + } + } catch (Exception e) { + Log.e(mTag, "Error in finding UUID mapping config file path: " + uuid_map_config_path); + } + + return null; + } + + /** + * Parses the below mapping structure - + * + * <ref_do> + * <uuid_ref_do> + * <uids> + * <uid>1000</uid> + * </uids> + * <uuid>a9b7ba70783b317e9998dc4dd82eb3c5</uuid> + * </uuid_ref_do> + * </ref_do> + */ + private void parse(InputStream is) { + try { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + XmlPullParser parser = factory.newPullParser(); + String text = null; + List<Integer> uids = null; + byte[] uuid = null; + + parser.setInput(is, null); + + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + String tagname = parser.getName(); + switch (eventType) { + case XmlPullParser.START_TAG: + if (tagname.equalsIgnoreCase(UIDS)) { + uids = new ArrayList<Integer>(); + } else if (tagname.equalsIgnoreCase(UUID)) { + uuid = null; + } + break; + + case XmlPullParser.TEXT: + text = parser.getText(); + break; + + case XmlPullParser.END_TAG: + if (tagname.equalsIgnoreCase(UUID_REF_DO)) { + if (uuid != null) { + for (int uid : uids) { + mUUIDMap.put(uid, uuid); + } + } + } else if (tagname.equalsIgnoreCase(UID)) { + uids.add(Integer.parseInt(text)); + } else if (tagname.equalsIgnoreCase(UUID)) { + byte[] uuidValue = decodeHexUUID(text); + uuid = new byte[uuidValue.length + PADDING_BYTES.length]; + System.arraycopy(PADDING_BYTES, 0, uuid, 0, PADDING_BYTES.length); + System.arraycopy(uuidValue, 0, uuid, PADDING_BYTES.length, + uuidValue.length); + + } + break; + + default: + break; + } + eventType = parser.next(); + } + + } catch (XmlPullParserException e) { + Log.e(mTag, "Error while parsing hal uuid mappings"); + Log.e(mTag, e.getMessage()); + } catch (IOException e) { + Log.e(mTag, "IO error while parsing hal uuid mappings"); + Log.e(mTag, e.getMessage()); + } + } + + /** + * Finds the uuid mapping config file path from predefined folders + * Parses the uuid mapping config file + */ + private void parseUuidMappings() { + try { + File uuid_map_file = getUuidMapConfigFile(); + if (uuid_map_file == null) { + Log.e(mTag, "Unable to determine UUID mapping config file path"); + return; + } + + parse(new FileInputStream(uuid_map_file)); + } catch (Exception e) { + Log.e(mTag, "Unable to parse hal uuid mappings"); + Log.e(mTag, e.getMessage()); + } + + if (DEBUG) { + for (Map.Entry<Integer, byte[]> entry : mUUIDMap.entrySet()) { + Log.d(mTag, "UID: " + entry.getKey()); + Log.d(mTag, "UUID: " + Arrays.toString(entry.getValue())); + } + } + } + + /** + * Finds UUID for the give UID + */ + public byte[] findUUID(int uid) { + return mUUIDMap.get(uid); + } + + /** + * Convert char to hex digit + * @param hexChar + * @return hex digit + */ + private int toDigit(char hexChar) { + int digit = Character.digit(hexChar, 16); + if (digit == -1) { + throw new IllegalArgumentException( + "Invalid Hexadecimal Character: " + hexChar); + } + return digit; + } + + /** + * Convert hex digits string to bytes + * @param hextText + * @return hex byte + */ + private byte hexToByte(char ch1, char ch2) { + int firstDigit = toDigit(ch1); + int secondDigit = toDigit(ch2); + return (byte) ((firstDigit << 4) + secondDigit); + } + + /** + * Convert hex string to hex byte array + * @param hextText + * @return hex bytes + */ + private byte[] decodeHexUUID(String hextText) { + if (hextText == null || hextText.length() != 32) { + throw new IllegalArgumentException( + "Invalid UUID supplied"); + } + + byte[] bytes = new byte[hextText.length() / 2]; + for (int i = 0; i < hextText.length(); i += 2) { + bytes[i / 2] = hexToByte(hextText.charAt(i), hextText.charAt(i + 1)); + } + return bytes; + } + +} diff --git a/src/com/android/se/security/gpac/AID_REF_DO.java b/src/com/android/se/security/gpac/AID_REF_DO.java index 70bddfb..31764b4 100644 --- a/src/com/android/se/security/gpac/AID_REF_DO.java +++ b/src/com/android/se/security/gpac/AID_REF_DO.java @@ -114,7 +114,7 @@ public class AID_REF_DO extends BerTlv { } } else if (getTag() == TAG) { - // sanity checks + // quick checks if ((getValueLength() < 5 || getValueLength() > 16) && getValueLength() != 0) { throw new ParserException("Invalid value length for AID-REF-DO!"); } @@ -151,7 +151,7 @@ public class AID_REF_DO extends BerTlv { stream.write(0x00); } else if (getTag() == TAG) { - // sanity check + // quick check if (getValueLength() != 0) { if (getValueLength() < 5 || getValueLength() > 16) { throw new DO_Exception("Invalid length of AID!"); diff --git a/src/com/android/se/security/gpac/BerTlv.java b/src/com/android/se/security/gpac/BerTlv.java index 066bf5e..966a442 100644 --- a/src/com/android/se/security/gpac/BerTlv.java +++ b/src/com/android/se/security/gpac/BerTlv.java @@ -225,7 +225,7 @@ public class BerTlv { /** Returns the byte array of only the data values */ public byte[] getValue() { - // sanity checks + // quick checks if (mRawData == null || mValueLength == 0 || mValueIndex < 0 diff --git a/src/com/android/se/security/gpac/Hash_REF_DO.java b/src/com/android/se/security/gpac/Hash_REF_DO.java index edd019a..e131f8b 100644 --- a/src/com/android/se/security/gpac/Hash_REF_DO.java +++ b/src/com/android/se/security/gpac/Hash_REF_DO.java @@ -111,7 +111,7 @@ public class Hash_REF_DO extends BerTlv { int index = getValueIndex(); int length = getValueLength(); - // sanity checks + // quick checks if (length != 0 && length != SHA1_LEN && length != SHA256_LEN) { throw new ParserException("Invalid value length for Hash-REF-DO!"); } @@ -139,7 +139,7 @@ public class Hash_REF_DO extends BerTlv { @Override public void build(ByteArrayOutputStream stream) throws DO_Exception { - // sanity checks + // quick checks if (mHash.length != SHA1_LEN && mHash.length != SHA256_LEN && mHash.length != 0) { throw new DO_Exception("Hash value must be " + SHA1_LEN + " or " + SHA256_LEN + " bytes in length!"); diff --git a/src/com/android/se/security/gpac/PERM_AR_DO.java b/src/com/android/se/security/gpac/PERM_AR_DO.java index f927042..d62862a 100644 --- a/src/com/android/se/security/gpac/PERM_AR_DO.java +++ b/src/com/android/se/security/gpac/PERM_AR_DO.java @@ -71,7 +71,7 @@ public class PERM_AR_DO extends BerTlv { */ public void build(ByteArrayOutputStream stream) throws DO_Exception { - // sanity checks + // quick checks if (mPermissionMask.length != PERM_MASK_LEN) { throw new DO_Exception("Invalid value length for PERM-AR-DO!"); } diff --git a/src/com/android/se/security/gpac/PKG_REF_DO.java b/src/com/android/se/security/gpac/PKG_REF_DO.java index a35415a..571909f 100644 --- a/src/com/android/se/security/gpac/PKG_REF_DO.java +++ b/src/com/android/se/security/gpac/PKG_REF_DO.java @@ -75,7 +75,7 @@ public class PKG_REF_DO extends BerTlv { byte[] data = getRawData(); int index = getValueIndex(); - // sanity checks + // quick checks if (getValueLength() > 128) { throw new ParserException("Invalid value length for PKG-REF-DO!"); } @@ -98,7 +98,7 @@ public class PKG_REF_DO extends BerTlv { @Override public void build(ByteArrayOutputStream stream) throws DO_Exception { byte[] pkg = mPackageName.getBytes(); - // sanity checks + // quick checks if (pkg.length > 128) { throw new DO_Exception("Invalid value length for PKG-REF-DO!"); } diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp new file mode 100644 index 0000000..497ed09 --- /dev/null +++ b/tests/unit/Android.bp @@ -0,0 +1,32 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test { + name: "SecureElementInstrumentationTests", + + certificate: "platform", + + libs: [ + "android.test.runner", + "android.test.base", + "android.test.mock", + ], + + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-target", + "truth-prebuilt", + ], + + // Include all test java files. + srcs: ["src/**/*.java"], + + platform_apis: true, + + test_suites: ["device-tests"], + + instrumentation_for: "SecureElement", +} diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml new file mode 100644 index 0000000..eaeb718 --- /dev/null +++ b/tests/unit/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.se.tests.unit"> + + <application android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.se" + android:label="SecureElement Test Cases"> + </instrumentation> + +</manifest> diff --git a/tests/unit/AndroidTest.xml b/tests/unit/AndroidTest.xml new file mode 100644 index 0000000..c04fec8 --- /dev/null +++ b/tests/unit/AndroidTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Runs SecureElement Test Cases."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"> + <option name="force-root" value="true" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="SecureElementInstrumentationTests.apk" /> + </target_preparer> + + <option name="test-tag" value="SecureElementInstrumentationTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.se.tests.unit" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/tests/unit/src/com/android/se/SecureElementVendorStableInterfaceFlagTest.java b/tests/unit/src/com/android/se/SecureElementVendorStableInterfaceFlagTest.java new file mode 100644 index 0000000..68d2907 --- /dev/null +++ b/tests/unit/src/com/android/se/SecureElementVendorStableInterfaceFlagTest.java @@ -0,0 +1,64 @@ +/* + * 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 com.android.se; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.IBinder; +import android.os.ServiceManager; + +import androidx.test.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public final class SecureElementVendorStableInterfaceFlagTest { + + private static final String TAG = + SecureElementVendorStableInterfaceFlagTest.class.getSimpleName(); + public static final String VSTABLE_SECURE_ELEMENT_SERVICE = + "android.se.omapi.ISecureElementService/default"; + private Context mContext; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getTargetContext(); + PackageManager pm = mContext.getPackageManager(); + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testIsVendorStableInterfaceEnabled() { + boolean secure_element_vintf_enabled = + mContext.getResources().getBoolean(R.bool.secure_element_vintf_enabled); + + IBinder binder = ServiceManager.getService(VSTABLE_SECURE_ELEMENT_SERVICE); + if (secure_element_vintf_enabled) { + Assert.assertNotNull(binder); + } else { + Assert.assertNull(binder); + } + + } +} |