├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── android-demo ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── openjavacard │ │ └── android │ │ └── demo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── org │ │ │ └── openjavacard │ │ │ └── smartcardio │ │ │ └── android │ │ │ └── demo │ │ │ ├── activity │ │ │ ├── AboutActivity.java │ │ │ ├── MainActivity.java │ │ │ └── SettingsActivity.java │ │ │ ├── adapter │ │ │ └── TerminalsAdapter.java │ │ │ ├── application │ │ │ └── DemoPreferences.java │ │ │ └── fragment │ │ │ ├── AboutFragment.java │ │ │ ├── SettingsFragment.java │ │ │ ├── TerminalInfoFragment.java │ │ │ └── TerminalListFragment.java │ └── res │ │ ├── layout │ │ ├── activity_about.xml │ │ ├── activity_main.xml │ │ ├── activity_settings.xml │ │ ├── fragment_about.xml │ │ ├── fragment_terminal.xml │ │ └── item_terminal.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── settings.xml │ └── test │ └── java │ └── org │ └── openjavacard │ └── android │ └── demo │ └── ExampleUnitTest.java ├── android-nfc ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── org │ │ └── openjavacard │ │ └── smartcardio │ │ └── android │ │ └── nfc │ │ ├── AndroidNfcSCIO.java │ │ ├── NfcCard.java │ │ ├── NfcCardChannel.java │ │ ├── NfcCardTerminal.java │ │ └── NfcCardTerminals.java │ └── res │ └── values │ └── strings.xml ├── android-omapi ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── org │ │ └── openjavacard │ │ └── smartcardio │ │ └── android │ │ └── omapi │ │ ├── AndroidOmapiSCIO.java │ │ ├── OmapiCard.java │ │ ├── OmapiCardChannel.java │ │ ├── OmapiCardTerminal.java │ │ └── OmapiCardTerminals.java │ └── res │ └── values │ └── strings.xml ├── build.gradle ├── framework ├── build.gradle └── src │ └── main │ └── java │ └── javax │ └── smartcardio │ ├── ATR.java │ ├── Card.java │ ├── CardChannel.java │ ├── CardException.java │ ├── CardNotPresentException.java │ ├── CardTerminal.java │ ├── CardTerminals.java │ ├── CommandAPDU.java │ ├── ResponseAPDU.java │ ├── TerminalFactory.java │ └── package-info.java ├── generic ├── build.gradle └── src │ └── main │ └── java │ └── org │ └── openjavacard │ └── smartcardio │ └── generic │ ├── GenericCard.java │ ├── GenericCardChannel.java │ ├── GenericCardTerminal.java │ ├── GenericCardTerminals.java │ └── package-info.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | out/** 2 | build/** 3 | .gradle/** 4 | .idea/** 5 | **/*.iml 6 | **/*.ipr 7 | **/build 8 | local.properties 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: openjdk8 3 | android: 4 | components: 5 | - android-28 6 | - build-tools-28.0.3 7 | - platform-tools 8 | - tools 9 | before_cache: 10 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 11 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 12 | cache: 13 | directories: 14 | - $HOME/.m2 15 | - $HOME/.gradle/caches/ 16 | - $HOME/.gradle/wrapper/ 17 | - $HOME/.android/build-cache 18 | #before_script: 19 | # - echo no | android create avd --force -n test -t android-28 --abi armeabi-v7a 20 | # - emulator -avd test -no-skin -no-audio -no-window & 21 | # - android-wait-for-emulator 22 | # - adb shell input keyevent 82 & 23 | script: ./gradlew build 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## OpenJavaCard SmartcardIO 2 | 3 | This is an implementation of the javax.smartcardio API. It can be used on platforms such as Android that do not provide this framework. 4 | 5 | The project contains both an independent framework implementation and interfaces for Android smartcard access methods. 6 | 7 | We implemented this because we need it for our Android applications. 8 | 9 | [![Build Status](https://travis-ci.org/OpenJavaCard/openjavacard-tools.svg?branch=master)](https://travis-ci.org/OpenJavaCard/openjavacard-smartcardio) 10 | 11 | ### Features 12 | 13 | * Android support 14 | * Access NFC cards and tokens 15 | * Access SIM cards and other SE via OMAPI 16 | * Demo app 17 | * Framework 18 | * Not fully completed 19 | 20 | ### Status 21 | 22 | * Not completed 23 | * Goals 24 | * Support NFC first and well 25 | * Support OMAPI well 26 | * Want to support USB 27 | * Want to support Bluetooth/BLE 28 | 29 | ### Acknowledgements 30 | 31 | We are not the first ones to think of doing something like this. 32 | 33 | ### Legal 34 | 35 | Vast majority of the code has been developed for this project: 36 | 37 | ``` 38 | openjavacard-smartcardio: OpenJavaCard SmartcardIO 39 | Copyright (C) 2018-2019 Ingo Albrecht, prom@berlin.ccc.de 40 | 41 | This library is free software; you can redistribute it and/or 42 | modify it under the terms of the GNU Lesser General Public 43 | License as published by the Free Software Foundation; either 44 | version 3.0 of the License, or (at your option) any later version. 45 | 46 | This library is distributed in the hope that it will be useful, 47 | but WITHOUT ANY WARRANTY; without even the implied warranty of 48 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 49 | Lesser General Public License for more details. 50 | 51 | You should have received a copy of the GNU Lesser General Public 52 | License along with this library; if not, write to the Free Software 53 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 54 | -------------------------------------------------------------------------------- /android-demo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion "28.0.3" 6 | defaultConfig { 7 | applicationId "org.openjavacard.smartcardio.android.demo" 8 | minSdkVersion 20 9 | targetSdkVersion 28 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | vectorDrawables.useSupportLibrary = true 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | sourceSets { 22 | main { 23 | // disable NDK build 24 | jni.srcDirs = [] 25 | } 26 | } 27 | } 28 | 29 | dependencies { 30 | implementation project(':android-nfc') 31 | implementation project(':android-omapi') 32 | // https://mvnrepository.com/artifact/com.android.support/appcompat-v7 33 | implementation 'com.android.support:appcompat-v7:28.0.0' 34 | // https://mvnrepository.com/artifact/com.android.support/cardview-v7 35 | implementation 'com.android.support:cardview-v7:28.0.0' 36 | // https://mvnrepository.com/artifact/com.android.support/design 37 | implementation 'com.android.support:design:28.0.0' 38 | // https://mvnrepository.com/artifact/com.android.support/preference-v7 39 | implementation 'com.android.support:preference-v7:28.0.0' 40 | // https://mvnrepository.com/artifact/com.android.support.constraint/constraint-layout 41 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 42 | // https://mvnrepository.com/artifact/junit/junit 43 | testImplementation 'junit:junit:4.12' 44 | // https://mvnrepository.com/artifact/com.android.support.test.espresso/espresso-core 45 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { 46 | exclude group: 'com.android.support', module: 'support-annotations' 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /android-demo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /opt/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /android-demo/src/androidTest/java/org/openjavacard/android/demo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package org.openjavacard.android.demo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | 11 | /** 12 | * Instrumentation test, which will execute on an Android device. 13 | * 14 | * @see Testing documentation 15 | */ 16 | @RunWith(AndroidJUnit4.class) 17 | public class ExampleInstrumentedTest { 18 | @Test 19 | public void useAppContext() throws Exception { 20 | // Context of the app under test. 21 | Context appContext = InstrumentationRegistry.getTargetContext(); 22 | 23 | assertEquals("org.openjavacard.smartcardio.android.smartcardio.demo", appContext.getPackageName()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /android-demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 30 | 31 | 35 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /android-demo/src/main/java/org/openjavacard/smartcardio/android/demo/activity/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package org.openjavacard.smartcardio.android.demo.activity; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.util.Log; 6 | import org.openjavacard.smartcardio.android.demo.R; 7 | 8 | public class AboutActivity extends AppCompatActivity { 9 | 10 | private static final String TAG = AboutActivity.class.getName(); 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | Log.d(TAG, "onCreate()"); 15 | super.onCreate(savedInstanceState); 16 | // set the layout 17 | setContentView(R.layout.activity_about); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /android-demo/src/main/java/org/openjavacard/smartcardio/android/demo/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.openjavacard.smartcardio.android.demo.activity; 2 | 3 | import android.content.Intent; 4 | import android.content.SharedPreferences; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.os.PowerManager; 8 | import android.support.v4.app.FragmentManager; 9 | import android.support.v7.app.ActionBar; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.support.v7.preference.PreferenceManager; 12 | import android.util.Log; 13 | import android.view.Menu; 14 | import android.view.MenuInflater; 15 | import android.view.MenuItem; 16 | import org.openjavacard.smartcardio.android.demo.R; 17 | import org.openjavacard.smartcardio.android.demo.application.DemoPreferences; 18 | import org.openjavacard.smartcardio.android.demo.fragment.TerminalInfoFragment; 19 | import org.openjavacard.smartcardio.android.demo.fragment.TerminalListFragment; 20 | import org.openjavacard.smartcardio.android.nfc.AndroidNfcSCIO; 21 | import org.openjavacard.smartcardio.android.omapi.AndroidOmapiSCIO; 22 | import org.openjavacard.smartcardio.generic.GenericCardTerminal; 23 | import org.openjavacard.smartcardio.generic.GenericCardTerminals; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | public class MainActivity extends AppCompatActivity 29 | implements GenericCardTerminals.Listener, 30 | SharedPreferences.OnSharedPreferenceChangeListener, FragmentManager.OnBackStackChangedListener{ 31 | 32 | private static final String TAG = MainActivity.class.getName(); 33 | 34 | private Handler mHandler; 35 | 36 | private SharedPreferences mPreferences; 37 | 38 | private FragmentManager mFragmentManager; 39 | private TerminalListFragment mTerminalList; 40 | private TerminalInfoFragment mTerminalInfo; 41 | 42 | private PowerManager mPowerManager; 43 | private PowerManager.WakeLock mWakeLock; 44 | private Runnable mWakeTimer; 45 | 46 | private AndroidNfcSCIO mNfcSC; 47 | private AndroidOmapiSCIO mOmapiSC; 48 | 49 | @Override 50 | protected void onCreate(Bundle savedInstanceState) { 51 | Log.d(TAG, "onCreate()"); 52 | super.onCreate(savedInstanceState); 53 | // set the layout 54 | setContentView(R.layout.activity_main); 55 | // set up action bar 56 | ActionBar actionBar = getSupportActionBar(); 57 | actionBar.setTitle(R.string.activity_main_title); 58 | // create handler 59 | mHandler = new Handler(getMainLooper()); 60 | // get prefs 61 | mPreferences = PreferenceManager.getDefaultSharedPreferences(this); 62 | mPreferences.registerOnSharedPreferenceChangeListener(this); 63 | // get fragment manager and set up up navigation 64 | mFragmentManager = getSupportFragmentManager(); 65 | mFragmentManager.addOnBackStackChangedListener(this); 66 | // create fragments 67 | mTerminalList = new TerminalListFragment(); 68 | mTerminalInfo = new TerminalInfoFragment(); 69 | // create SCIO interfaces 70 | mNfcSC = new AndroidNfcSCIO(this); 71 | mNfcSC.getTerminals().setListener(this); 72 | mOmapiSC = new AndroidOmapiSCIO(this); 73 | mOmapiSC.getTerminals().setListener(this); 74 | // set up wake lock 75 | mPowerManager = (PowerManager)getSystemService(POWER_SERVICE); 76 | mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); 77 | mWakeTimer = new Runnable() { 78 | @Override 79 | public void run() { 80 | releaseWakeLock(); 81 | } 82 | }; 83 | // start with the terminal list 84 | switchToTerminalList(); 85 | } 86 | 87 | @Override 88 | public boolean onCreateOptionsMenu(Menu menu) { 89 | Log.d(TAG, "onCreateOptionsMenu()"); 90 | super.onCreateOptionsMenu(menu); 91 | MenuInflater inflater = getMenuInflater(); 92 | inflater.inflate(R.menu.menu_main, menu); 93 | return true; 94 | } 95 | 96 | @Override 97 | public boolean onOptionsItemSelected(MenuItem item) { 98 | Log.d(TAG, "onOptionsItemSelected(" + item.getTitle() + ")"); 99 | switch (item.getItemId()) { 100 | case R.id.action_settings: 101 | startSettings(); 102 | return true; 103 | case R.id.action_about: 104 | startAbout(); 105 | return true; 106 | } 107 | return super.onOptionsItemSelected(item); 108 | } 109 | 110 | @Override 111 | protected void onResume() { 112 | Log.d(TAG, "onResume()"); 113 | super.onResume(); 114 | mNfcSC.enable(); 115 | mOmapiSC.enable(); 116 | acquireWakeLock(); 117 | updateUpEnabled(); 118 | updateTerminals(); 119 | updateWakeTimer(); 120 | } 121 | 122 | @Override 123 | protected void onPause() { 124 | Log.d(TAG, "onPause()"); 125 | releaseWakeLock(); 126 | mOmapiSC.disable(); 127 | mNfcSC.disable(); 128 | super.onPause(); 129 | } 130 | 131 | @Override 132 | protected void onDestroy() { 133 | Log.d(TAG, "onDestroy()"); 134 | mNfcSC = null; 135 | super.onDestroy(); 136 | } 137 | 138 | @Override 139 | public void onTerminalAdded(GenericCardTerminals terminals, GenericCardTerminal terminal) { 140 | Log.d(TAG, "onTerminalAdded(" + terminal.getName() + ")"); 141 | updateTerminals(); 142 | } 143 | 144 | @Override 145 | public void onTerminalRemoved(GenericCardTerminals terminals, GenericCardTerminal terminal) { 146 | Log.d(TAG, "onTerminalRemoved(" + terminal.getName() + ")"); 147 | updateTerminals(); 148 | } 149 | 150 | @Override 151 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 152 | Log.d(TAG, "onPreferenceChange(" + key + ")"); 153 | switch(key) { 154 | case DemoPreferences.STAY_AWAKE: 155 | updateWakeTimer(); 156 | break; 157 | case DemoPreferences.USE_NFC: 158 | case DemoPreferences.USE_OMAPI: 159 | updateTerminals(); 160 | break; 161 | } 162 | } 163 | 164 | @Override 165 | public void onBackStackChanged() { 166 | Log.d(TAG, "onBackStackChanged()"); 167 | updateUpEnabled(); 168 | } 169 | 170 | @Override 171 | public boolean onSupportNavigateUp() { 172 | Log.d(TAG, "onSupportNavigateUp()"); 173 | mFragmentManager.popBackStack(); 174 | return true; 175 | } 176 | 177 | private void acquireWakeLock() { 178 | Log.d(TAG, "acquireWakeLock()"); 179 | if(!mWakeLock.isHeld()) { 180 | mWakeLock.acquire(); 181 | updateWakeTimer(); 182 | } 183 | } 184 | 185 | private void releaseWakeLock() { 186 | Log.d(TAG, "releaseWakeLock()"); 187 | if(mWakeLock.isHeld()) { 188 | mWakeLock.release(); 189 | } 190 | } 191 | 192 | private void updateUpEnabled() { 193 | boolean haveBackStack = mFragmentManager.getBackStackEntryCount() > 0; 194 | getSupportActionBar().setDisplayHomeAsUpEnabled(haveBackStack); 195 | } 196 | 197 | private void updateWakeTimer() { 198 | Log.d(TAG, "updateWakeTimer()"); 199 | String stayAwakeString = mPreferences.getString(DemoPreferences.STAY_AWAKE, "60"); 200 | int stayAwake = Integer.parseInt(stayAwakeString); 201 | mHandler.removeCallbacks(mWakeTimer); 202 | if(stayAwake > 0) { 203 | mHandler.postDelayed(mWakeTimer, stayAwake * 1000); 204 | } 205 | } 206 | 207 | private void startSettings() { 208 | Log.d(TAG, "startSettings()"); 209 | startActivity(new Intent(this, SettingsActivity.class)); 210 | } 211 | 212 | private void startAbout() { 213 | Log.d(TAG, "startAbout()"); 214 | startActivity(new Intent(this, AboutActivity.class)); 215 | } 216 | 217 | public void switchToTerminalList() { 218 | Log.d(TAG, "switchToTerminalList()"); 219 | updateWakeTimer(); 220 | updateTerminals(); 221 | mFragmentManager.beginTransaction() 222 | .replace(R.id.main_layout, mTerminalList) 223 | .commit(); 224 | } 225 | 226 | public void switchToTerminalInfo(GenericCardTerminal terminal) { 227 | Log.d(TAG, "switchToTerminalInfo(" + terminal + ")"); 228 | updateWakeTimer(); 229 | mTerminalInfo.setTerminal(terminal); 230 | mFragmentManager.beginTransaction() 231 | .replace(R.id.main_layout, mTerminalInfo) 232 | .addToBackStack(null) 233 | .commit(); 234 | } 235 | 236 | private void updateTerminals() { 237 | Log.d(TAG, "updateTerminals()"); 238 | // check preferences 239 | boolean useOMAPI = mPreferences.getBoolean(DemoPreferences.USE_OMAPI, false); 240 | boolean useNFC = mPreferences.getBoolean(DemoPreferences.USE_NFC, false); 241 | // collect all terminals 242 | List terminals = new ArrayList<>(); 243 | if(useOMAPI) { 244 | terminals.addAll(mOmapiSC.getTerminals().getTerminals()); 245 | } 246 | if(useNFC) { 247 | terminals.addAll(mNfcSC.getTerminals().getTerminals()); 248 | } 249 | // update the list 250 | mTerminalList.setTerminals(terminals); 251 | } 252 | 253 | } 254 | -------------------------------------------------------------------------------- /android-demo/src/main/java/org/openjavacard/smartcardio/android/demo/activity/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package org.openjavacard.smartcardio.android.demo.activity; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.ActionBar; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.util.Log; 8 | import org.openjavacard.smartcardio.android.demo.R; 9 | 10 | public class SettingsActivity extends AppCompatActivity { 11 | 12 | private static final String TAG = SettingsActivity.class.getName(); 13 | 14 | @Override 15 | protected void onCreate(@Nullable Bundle savedInstanceState) { 16 | Log.d(TAG, "onCreate()"); 17 | super.onCreate(savedInstanceState); 18 | // set the layout 19 | setContentView(R.layout.activity_settings); 20 | // configure the action bar 21 | ActionBar actionbar = getSupportActionBar(); 22 | if(actionbar != null) { 23 | // enable up-navigation 24 | actionbar.setDisplayHomeAsUpEnabled(true); 25 | // set up action bar 26 | actionbar.setTitle(R.string.activity_settings_title); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /android-demo/src/main/java/org/openjavacard/smartcardio/android/demo/adapter/TerminalsAdapter.java: -------------------------------------------------------------------------------- 1 | package org.openjavacard.smartcardio.android.demo.adapter; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | import android.widget.BaseAdapter; 6 | 7 | public class TerminalsAdapter extends BaseAdapter { 8 | 9 | @Override 10 | public int getCount() { 11 | return 0; 12 | } 13 | 14 | @Override 15 | public int getViewTypeCount() { 16 | return 2; 17 | } 18 | 19 | @Override 20 | public Object getItem(int position) { 21 | return null; 22 | } 23 | 24 | @Override 25 | public long getItemId(int position) { 26 | return 0; 27 | } 28 | 29 | @Override 30 | public int getItemViewType(int position) { 31 | return super.getItemViewType(position); 32 | } 33 | 34 | @Override 35 | public View getView(int position, View convertView, ViewGroup parent) { 36 | return null; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /android-demo/src/main/java/org/openjavacard/smartcardio/android/demo/application/DemoPreferences.java: -------------------------------------------------------------------------------- 1 | package org.openjavacard.smartcardio.android.demo.application; 2 | 3 | public class DemoPreferences { 4 | 5 | public static final String STAY_AWAKE = "pref_stay_awake"; 6 | public static final String USE_NFC = "pref_use_nfc"; 7 | public static final String USE_OMAPI = "pref_use_omapi"; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /android-demo/src/main/java/org/openjavacard/smartcardio/android/demo/fragment/AboutFragment.java: -------------------------------------------------------------------------------- 1 | package org.openjavacard.smartcardio.android.demo.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.util.Log; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import org.openjavacard.smartcardio.android.demo.R; 12 | 13 | public class AboutFragment extends Fragment { 14 | 15 | private static final String TAG = AboutFragment.class.getName(); 16 | 17 | @Nullable 18 | @Override 19 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 20 | Log.d(TAG, "onCreateView()"); 21 | View view = inflater.inflate(R.layout.fragment_about, container, false); 22 | return view; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /android-demo/src/main/java/org/openjavacard/smartcardio/android/demo/fragment/SettingsFragment.java: -------------------------------------------------------------------------------- 1 | package org.openjavacard.smartcardio.android.demo.fragment; 2 | 3 | import android.content.SharedPreferences; 4 | import android.os.Bundle; 5 | import android.support.v14.preference.PreferenceFragment; 6 | import android.support.v7.preference.ListPreference; 7 | import android.support.v7.preference.Preference; 8 | import android.support.v7.preference.PreferenceManager; 9 | import android.util.Log; 10 | import org.openjavacard.smartcardio.android.demo.R; 11 | 12 | import java.util.Map; 13 | 14 | public class SettingsFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener { 15 | 16 | private static final String TAG = SettingsFragment.class.getName(); 17 | 18 | private SharedPreferences mPreferences; 19 | 20 | @Override 21 | public void onCreate(Bundle savedInstanceState) { 22 | Log.d(TAG, "onCreate()"); 23 | super.onCreate(savedInstanceState); 24 | } 25 | 26 | @Override 27 | public void onCreatePreferences(Bundle savedState, String rootKey) { 28 | Log.d(TAG, "onCreatePreferences()"); 29 | // create preferences from XML 30 | addPreferencesFromResource(R.xml.settings); 31 | // bind summaries of list preferences 32 | PreferenceManager pm = getPreferenceManager(); 33 | mPreferences = pm.getSharedPreferences(); 34 | Map map = mPreferences.getAll(); 35 | if(map != null) { 36 | for (String key : map.keySet()) { 37 | Preference pref = pm.findPreference(key); 38 | bindPreferenceSummaryToValue(pref); 39 | } 40 | } 41 | } 42 | 43 | @Override 44 | public boolean onPreferenceChange(Preference preference, Object newValue) { 45 | Log.d(TAG, "onPreferenceChange(" + preference.getKey() + ")"); 46 | String stringValue = newValue.toString(); 47 | if (preference instanceof ListPreference) { 48 | ListPreference listPreference = (ListPreference) preference; 49 | int index = listPreference.findIndexOfValue(stringValue); 50 | preference.setSummary( 51 | index >= 0 ? listPreference.getEntries()[index] : ""); 52 | } else { 53 | preference.setSummary(stringValue); 54 | } 55 | return true; 56 | } 57 | 58 | private void bindPreferenceSummaryToValue(Preference preference) { 59 | Log.v(TAG, "bindPreferenceSummaryToValue(" + preference.getKey() + ")"); 60 | if(preference instanceof ListPreference) { 61 | preference.setOnPreferenceChangeListener(this); 62 | onPreferenceChange(preference, ((ListPreference) preference).getValue()); 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /android-demo/src/main/java/org/openjavacard/smartcardio/android/demo/fragment/TerminalInfoFragment.java: -------------------------------------------------------------------------------- 1 | package org.openjavacard.smartcardio.android.demo.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v4.app.FragmentManager; 8 | import android.support.v7.widget.CardView; 9 | import android.util.Log; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.Button; 14 | import android.widget.TextView; 15 | import org.openjavacard.smartcardio.android.demo.R; 16 | import org.openjavacard.smartcardio.android.demo.activity.MainActivity; 17 | import org.openjavacard.smartcardio.generic.GenericCardTerminal; 18 | 19 | import javax.smartcardio.Card; 20 | import javax.smartcardio.CardException; 21 | 22 | public class TerminalInfoFragment extends Fragment implements View.OnClickListener { 23 | 24 | private static final String TAG = TerminalInfoFragment.class.getName(); 25 | 26 | /** Our parent activity */ 27 | private MainActivity mActivity; 28 | /** Fragment manager (for stack interactions) */ 29 | private FragmentManager mFragmentManager; 30 | 31 | /** True once views have been instantiated */ 32 | private boolean mInitialized; 33 | 34 | private GenericCardTerminal mTerminal; 35 | private Card mCard; 36 | 37 | private CardView mTerminalLayout; 38 | private CardView mCardLayout; 39 | 40 | private TextView mTerminalClass; 41 | private TextView mTerminalName; 42 | private TextView mCardClass; 43 | private TextView mCardProtocol; 44 | private TextView mCardATR; 45 | 46 | private Button mDismissButton; 47 | 48 | @Override 49 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 50 | Log.d(TAG, "onActivityCreated()"); 51 | super.onActivityCreated(savedInstanceState); 52 | mActivity = (MainActivity)getActivity(); 53 | mFragmentManager = getFragmentManager(); 54 | } 55 | 56 | @Nullable 57 | @Override 58 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 59 | Log.d(TAG, "onCreateView()"); 60 | // instantiate things 61 | View view = inflater.inflate(R.layout.fragment_terminal, container, false); 62 | // get layout containers 63 | mTerminalLayout = view.findViewById(R.id.terminal_layout); 64 | mCardLayout = view.findViewById(R.id.card_layout); 65 | // get info fields 66 | mTerminalClass = view.findViewById(R.id.terminal_info_class); 67 | mTerminalName = view.findViewById(R.id.terminal_info_name); 68 | mCardClass = view.findViewById(R.id.card_info_class); 69 | mCardProtocol = view.findViewById(R.id.card_info_protocol); 70 | mCardATR = view.findViewById(R.id.card_info_atr); 71 | // get dismiss button 72 | mDismissButton = view.findViewById(R.id.dismiss_button); 73 | mDismissButton.setOnClickListener(this); 74 | // we are now initialized 75 | mInitialized = true; 76 | // update views 77 | updateViews(); 78 | // return root 79 | return view; 80 | } 81 | 82 | public void setTerminal(GenericCardTerminal terminal) { 83 | Card card = null; 84 | try { 85 | if(terminal != null) { 86 | if(terminal.isCardPresent()) { 87 | card = terminal.connect("*"); 88 | } 89 | } 90 | } catch (CardException e) { 91 | e.printStackTrace(); 92 | } 93 | // remember things 94 | mTerminal = terminal; 95 | mCard = card; 96 | // update views 97 | updateViews(); 98 | } 99 | 100 | private void updateViews() { 101 | if(!mInitialized) { 102 | return; 103 | } 104 | 105 | mTerminalLayout.setVisibility(mTerminal == null ? View.GONE : View.VISIBLE); 106 | if(mTerminal != null) { 107 | mTerminalClass.setText(mTerminal.getClass().getSimpleName()); 108 | mTerminalName.setText(mTerminal.getName()); 109 | } 110 | 111 | mCardLayout.setVisibility(mCard == null ? View.GONE : View.VISIBLE); 112 | if(mCard != null) { 113 | mCardClass.setText(mCard.getClass().getSimpleName()); 114 | mCardProtocol.setText(mCard.getProtocol()); 115 | } 116 | } 117 | 118 | @Override 119 | public void onClick(View v) { 120 | if(v == mDismissButton) { 121 | mFragmentManager.popBackStack(); 122 | } 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /android-demo/src/main/java/org/openjavacard/smartcardio/android/demo/fragment/TerminalListFragment.java: -------------------------------------------------------------------------------- 1 | package org.openjavacard.smartcardio.android.demo.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.ListFragment; 7 | import android.util.Log; 8 | import android.view.View; 9 | import android.widget.ArrayAdapter; 10 | import android.widget.ListView; 11 | import org.openjavacard.smartcardio.android.demo.R; 12 | import org.openjavacard.smartcardio.android.demo.activity.MainActivity; 13 | import org.openjavacard.smartcardio.generic.GenericCardTerminal; 14 | import org.openjavacard.smartcardio.generic.GenericCardTerminals; 15 | 16 | import java.util.List; 17 | 18 | public class TerminalListFragment extends ListFragment { 19 | 20 | private static final String TAG = TerminalListFragment.class.getName(); 21 | 22 | private MainActivity mActivity; 23 | 24 | private List mTerminals; 25 | private ArrayAdapter mAdapter; 26 | 27 | @Override 28 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 29 | Log.d(TAG, "onActivityCreated()"); 30 | super.onActivityCreated(savedInstanceState); 31 | mActivity = (MainActivity)getActivity(); 32 | update(); 33 | } 34 | 35 | @Override 36 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 37 | Log.d(TAG, "onViewCreated()"); 38 | super.onViewCreated(view, savedInstanceState); 39 | mAdapter = new ArrayAdapter<>(getContext(), R.layout.item_terminal); 40 | setListAdapter(mAdapter); 41 | update(); 42 | } 43 | 44 | @Override 45 | public void onResume() { 46 | Log.d(TAG, "onResume()"); 47 | super.onResume(); 48 | update(); 49 | } 50 | 51 | public void setTerminals(List terminals) { 52 | mTerminals = terminals; 53 | update(); 54 | } 55 | 56 | @Override 57 | public void onListItemClick(ListView l, View v, int position, long id) { 58 | GenericCardTerminal terminal = mAdapter.getItem(position); 59 | mActivity.switchToTerminalInfo(terminal); 60 | } 61 | 62 | private void update() { 63 | if(mActivity != null) { 64 | mActivity.runOnUiThread(new Runnable() { 65 | @Override 66 | public void run() { 67 | mAdapter.clear(); 68 | if(mTerminals != null) { 69 | mAdapter.addAll(mTerminals); 70 | } 71 | } 72 | }); 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /android-demo/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android-demo/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /android-demo/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android-demo/src/main/res/layout/fragment_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 15 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /android-demo/src/main/res/layout/fragment_terminal.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 21 | 24 | 30 | 33 | 35 | 41 | 47 | 48 | 50 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 79 | 82 | 88 | 91 | 93 | 99 | 105 | 106 | 108 | 114 | 120 | 121 | 123 | 129 | 135 | 136 | 137 | 138 | 139 | 140 | 141 |