File: android.rst

package info (click to toggle)
ausweisapp2 2.4.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 23,632 kB
  • sloc: cpp: 114,622; python: 2,833; xml: 1,426; java: 923; sh: 186; makefile: 7
file content (687 lines) | stat: -rw-r--r-- 20,416 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
Android
=======
This chapter deals with the Android specific properties of the |AppName| SDK.
The |AppName| core is encapsulated into an **Android service** which is
running in the background without a user interface. This service is interfaced
via an Android specific interprocess communication (IPC) mechanism. The basics
of this mechanism - the **Android Interface Definition Language** (AIDL) -
are introduced in the following section. Subsequent sections deal with the
SDK interface itself and explain which steps are necessary in order to
communicate with the |AppName| SDK.

The |AppName| is available as an AAR package that can automatically
be fetched by Android's default build system **gradle**.

.. seealso::
   For Android there is also the
   `AusweisApp SDK Wrapper <https://www.ausweisapp.bund.de/sdkwrapper/>`_
   which is a software library that offers a high-level interface to the |AppName| SDK.

.. important::
   The AAR package is available in maven central for free.
   If you need enterprise support feel free to contact us.

.. important::
   Please note that only Android devices that feature Extended Length
   NFC communication are supported by the SDK.
   This can be checked via the Android API methods **getMaxTransceiveLength()**
   and **isExtendedLengthApduSupported()**.

   | https://developer.android.com/reference/android/nfc/tech/NfcA
   | https://developer.android.com/reference/android/nfc/tech/IsoDep



SDK
---
The |AppName| SDK is distributed as an AAR package that contains
native **arm64-v8a**,  **x86_64** and **armeabi-v7a** libraries only.
The AAR package is available in the default repository of Android.
The following listing shows the required **mavenCentral** in **build.gradle**.

.. code-block:: groovy

  buildscript {
      repositories {
          mavenCentral()
      }
  }


The |AppName| SDK will be fetched automatically as a dependency by
your **app/build.gradle** file.
It is recommended to always use the latest version (|version|) of |AppName|.

.. code-block:: groovy

  dependencies {
      implementation 'com.governikus:ausweisapp:x.y.z'
  }



.. note::
  All artifacts are signed with the following key (available on all public
  key servers): 0x699BF3055B0A49224EFDE7C72D7479A531451088

.. seealso::
  The AAR package provides an **AndroidManifest.xml** to register required
  permissions and the background service. If your own AndroidManifest has
  conflicts with our provided file you can add some attributes to resolve
  those conflicts.

  https://developer.android.com/studio/build/manifest-merge.html


Logging
^^^^^^^

The |AppName| SDK uses default logging of Android and has its own log file.
It is **recommended** to collect that log file if an error occurs in
your application to receive better support.

The log file is in your application path:

.. code-block:: text

    /data/data/your.application.name/files/AusweisApp.XXXXXX.log

The *XXXXXX* characters will be replaced by an automatically generated
portion of the filename to avoid conflicts with previous instances.

A new log file will be created for each new instance of the |AppName| and
will be deleted after a correct shutdown.
In case of old or multiple log files, it is highly probable that the
previous instance crashed.

The |AppName| deletes any log files that are older than 14 days.


Initialization of the Android Application
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The |AppName| SDK creates a fork of the Android "main" Application if started.
Due to this, the Application is instantiated a second time. Thus, it must
ensure that any initialization (e.g. Firebase connections) is only carried out
once. To do so the following snippet may prove useful:

.. code-block:: java

  public class MyAwesomeApp extends Application
  {
      private static final String AA2_PROCESS = "ausweisapp2_service";

      @Override
      public void onCreate()
      {
          super.onCreate();
          if (isAA2Process())
              return;

          // Perform one-time initialization of YOUR app, e.g. Firebase connection
      }

      private boolean isAA2Process()
      {
          return Application.getProcessName().endsWith(AA2_PROCESS);
      }
  }


.. _android_import_aidl:

Import the AIDL files
^^^^^^^^^^^^^^^^^^^^^
Android provides an interprocess communication (IPC) mechanism which is based on
messages consisting of primitive types.
In order to abstract from this very basic mechanism, there is the Android
Interface Definition Language (AIDL).
It allows the definition of Java like interfaces.
The Android SDK generates the necessary interface implementations from supplied
AIDL files in order to perform IPC, as if this function had been called directly
in the current process.

In order to interact with the |AppName| SDK there are two AIDL interfaces.
The first one is given to the client application by the SDK and allows the
client to establish a session with the SDK,
to send JSON commands to the SDK and to pass discovered NFC tags to the SDK.

The second AIDL interface is given to the SDK by the client application. It
enables the client to receive the initial session parameters as well as
JSON messages from the SDK. Furthermore it has a function which is called
when an existing connection with the SDK is dropped by the SDK. Both interfaces
are listed below and you need to import them into your build environment.

.. seealso::

  https://developer.android.com/guide/components/aidl.html


Interface
"""""""""

.. code-block:: java

  package com.governikus.ausweisapp2;

  import com.governikus.ausweisapp2.IAusweisApp2SdkCallback;
  import android.nfc.Tag;

  interface IAusweisApp2Sdk
  {
      boolean connectSdk(IAusweisApp2SdkCallback pCallback);
      boolean send(String pSessionId, String pMessageFromClient);
      boolean updateNfcTag(String pSessionId, in Tag pTag);
  }



Callback
""""""""
.. versionchanged:: 2.4.0
   Parameter **pIsSecureSessionId** from **sessionIdGenerated** removed.

.. code-block:: java

  package com.governikus.ausweisapp2;

  interface IAusweisApp2SdkCallback
  {
      void sessionIdGenerated(String pSessionId);
      void receive(String pJson);
      void sdkDisconnected();
  }




Background service
------------------
The |AppName| SDK is an embedded background service in your own application.


.. _android_binding_service:

Binding to the service
^^^^^^^^^^^^^^^^^^^^^^
In order to start the |AppName| SDK it is necessary to bind to the
Android service supplied by the SDK.
This binding fulfils two purposes:

  - First it starts the SDK.

  - Second it enables the client to establish an IPC connection as
    mentioned above.


Due to the nature of an Android service, there can be only one instance of
the SDK running. If multiple clients in your application bind to the service,
they are interacting with the same instance of the service.
The service is terminated once all previously bound clients are unbound.

To differentiate between different connected clients, virtual sessions are used
once the binding is completed. These sessions are discussed in a separate
section, section :ref:`android_create_session`.



Create connection
"""""""""""""""""
First of all, in order to bind to the service, one needs to instantiate an
Android ServiceConnection.
Subsequently, the object is passed to the Android API and the contained
methods are invoked by Android on service connection and disconnection.


.. code-block:: java

  import android.content.ServiceConnection;

  // [...]

  ServiceConnection mConnection = new ServiceConnection()
  {
    @Override
    public void onServiceConnected(ComponentName className, IBinder service)
    {
        // ... details below
    }

    @Override
    public void onServiceDisconnected(ComponentName className)
    {
        // ... details below
    }
  }



.. _android_raw_connection:

Bind service to raw connection
""""""""""""""""""""""""""""""
In order to perform the actual binding a directed Intent, which identifies
the |AppName| SDK, is created.
This Intent is sent to the Android API along with the ServiceConnection
created above. This API call either starts up the SDK if it is the
first client, or connects to the running SDK instance if there is already
another client bound.

You need to pass your own package name as the |AppName| SDK is a background
service of your application.


.. code-block:: java

  import android.app.Activity;
  import android.content.Context;
  import android.content.Intent;

  // [...]

  String pkg = getApplicationContext().getPackageName();
  String name = "com.governikus.ausweisapp2.START_SERVICE";
  Intent serviceIntent = new Intent(name);
  serviceIntent.setPackage(pkg);
  bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);

.. seealso::

  https://developer.android.com/guide/components/bound-services.html

  https://developer.android.com/reference/android/app/Activity.html


.. _android_init_aidl:

Initializing the AIDL connection
""""""""""""""""""""""""""""""""
Once the Android service of the |AppName| SDK is successfully started
and bound to by the client,
the Android system calls the onServiceConnected method of the ServiceConnection
created and supplied above.
This method receives an instance of the IBinder Android service interface.

The IBinder is then used by the client application to initialize the auto
generated AIDL stub
in order to use the AIDL IPC mechanism.
The used stub is supposed to be auto generated by the Android SDK if you have
properly configured your build environment.

The stub initialization returns an instance of **IAusweisApp2Sdk** which is used
to interact with the SDK.
The example below stores this instance in the member variable mSdk.

.. code-block:: java

  import android.content.ComponentName;
  import android.content.ServiceConnection;
  import android.os.IBinder;

  import com.governikus.ausweisapp2.IAusweisApp2Sdk;

  // [...]

  IAusweisApp2Sdk mSdk;

  ServiceConnection mConnection = new ServiceConnection(){
    @Override
    public void onServiceConnected(ComponentName className, IBinder service)
    {
        try {
            mSdk = IAusweisApp2Sdk.Stub.asInterface(service);
        } catch (ClassCastException|RemoteException e) {
            // ...
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName className)
    {
        mSdk = null;
    }
  }

.. seealso::

  :ref:`android_import_aidl`



.. _android_create_session:

Create session to |AppName|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Once your client is bound to the |AppName| SDK service and you have initialized
the AIDL IPC mechanism, you are ready to use the actual SDK API.

Since the Android system does not allow to limit the number of clients which
can connect to a service, the SDK API uses custom **sessions** to manage the
connected clients. There is a maximum of one established session at a time.

In order to open a session with the SDK you need to pass an
instance of **IAusweisApp2SdkCallback** to the **connectSdk** function of your
previously acquired instance of **IAusweisApp2Sdk**. If your callback is accepted,
the function returns true. Otherwise there is a problem with your supplied callback.
Sessions will be disconnected once the IBinder instance of the connected client is
invalidated, another communication error occurs or another Client connects. Please see
:ref:`android_disconnect_sdk` for instructions to gracefully disconnect from the SDK.

As mentioned above: If there already is a connected client and a second client attempts
to connect, the first client is disconnected and the second client is granted exclusive
access to the SDK. The first client is informed via its callback by **sdkDisconnected**.
The second client is presented a fresh environment and it has no access to any data of
the first client.

If you have successfully established a session, the **sessionIdGenerated** function
of your callback is invoked. With this invocation you receive one argument.
The **pSessionId** holds the actual session ID generated by the SDK.
This ID is used to identify your session and you need to pass it to all future SDK
function invocations of this session.

The listing below shows an example for an instantiation of IAusweisApp2SdkCallback
and establishing a session.




.. code-block:: java

  import com.governikus.ausweisapp2.IAusweisApp2Sdk;
  import com.governikus.ausweisapp2.IAusweisApp2SdkCallback;

  // [...]

  LocalCallback mCallback = new LocalCallback();
  class LocalCallback extends IAusweisApp2SdkCallback.Stub
  {
    public String mSessionID = null;

    @Override
    public void sessionIdGenerated(String pSessionId) throws RemoteException
    {
        mSessionID = pSessionId;
    }

    @Override
    public void receive(String pJson) throws RemoteException
    {
        // handle message from SDK
    }
  }

  // [...]

  try
  {
    if (!mSdk.connectSdk(mCallback))
    {
        // already connected? Handle error...
    }
  }
  catch (RemoteException e)
  {
      // handle exception
  }

.. seealso::

  :ref:`android_init_aidl`
  :ref:`android_disconnect_sdk`



Send command
""""""""""""
In order to send a JSON command to the |AppName| SDK, you need to invoke
the **send** function of your instance of **IAusweisApp2Sdk**. For this command
to be processed by the SDK you need to supply the session ID which you have
previously received. The listing below shows an example.



.. code-block:: java

  String cmd = "{\"cmd\": \"GET_INFO\"}";
  try
  {
    if (!mSdk.send(mCallback.mSessionID, cmd))
    {
        // disconnected? Handle error...
    }
  }
  catch (RemoteException e)
  {
      // handle exception
  }




Receive message
"""""""""""""""
Messages from the |AppName| SDK are passed to you via the same instance of
**IAusweisApp2SdkCallback** in which you have received the session ID.
The **receive** method is called each time the SDK sends a message.

.. seealso::

  :ref:`android_create_session`


.. _android_disconnect_sdk:

Disconnect from SDK
^^^^^^^^^^^^^^^^^^^
In order to disconnect from the |AppName| SDK you need to invalidate your
instance of **IBinder**. There are two possibilities to do this. The first
one is to unbind from the SDK Android service to undo your binding, like
shown in the code listing below. The second one is to return false in the
**pingBinder** function of your IBinder instance.

.. code-block:: java

  unbindService(mConnection);

.. seealso::

  :ref:`android_binding_service`

  https://developer.android.com/reference/android/os/IBinder.html




.. _android_nfc_tags:

Passing NFC tags to the SDK
^^^^^^^^^^^^^^^^^^^^^^^^^^^
NFC tags can only be detected by applications which have a foreground window
on the Android platform. A common workaround for this problem is
to equip background services with a transparent window which is shown
to dispatch NFC tags.

However, if there are multiple applications installed, which are capable
of dispatching NFC tags, the Android system will display an **App Chooser**
for each discovered tag enabling the user to select the appropriate application
to handle the NFC tag. To have such a chooser
display the name and image of the client application instead of the SDK,
the client application is required to dispatch discovered NFC tags and to
pass them to the SDK.

Furthermore, this interface design enables the client application to do
**foreground dispatching** of NFC tags. If the active application registers itself for
foreground dispatching, it receives discovered NFC tags directly without
Android displaying an App Chooser.


Intent-Filter in AndroidManifest.xml
""""""""""""""""""""""""""""""""""""
In order to be informed about attached NFC tags by Android, the client
application is required to register an intent filter. The appropriate
filter is shown in the listing below.

.. code-block:: xml

  <intent-filter>
    <action android:name="android.nfc.action.TECH_DISCOVERED" />
  </intent-filter>
  <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" />

.. seealso::

  https://developer.android.com/guide/components/intents-filters.html


NFC Technology Filter
"""""""""""""""""""""
Since there are many different kinds of NFC tags, Android requires the
application to register a technology filter for the kind of tags the application
wants to receive. The proper filter for the German eID card is shown
in the listing below.

.. code-block:: xml

  <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
      <tech>android.nfc.tech.IsoDep</tech>
    </tech-list>
  </resources>



Implementation
""""""""""""""
As it is common on the Android platform, information is sent to applications
encapsulated in instances of the **Intent** class. In order to process newly
discovered NFC tags, Intents which are given to the application need to be
checked for the parcelable NFC extra as shown in the code listing below.
Subsequently the client is required to send them to the |AppName| SDK by
calling the **updateNfcTag** method of the previously acquired **IAusweisApp2Sdk**
instance.
The listing below shows an example for the described process.


.. code-block:: java

  import android.content.Intent;
  import android.nfc.NfcAdapter;
  import android.nfc.Tag;

  import com.governikus.ausweisapp2.IAusweisApp2Sdk;
  import com.governikus.ausweisapp2.IAusweisApp2SdkCallback;

  // [...]

  void handleIntent(Intent intent)
  {
    final Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    if (tag != null)
    {
      try {
        mSdk.updateNfcTag(mCallback.mSessionID, tag);
      } catch (RemoteException e) {
        // ...
      }
    }
  }






Dispatching NFC tags in foreground
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
As already mentioned under :ref:`android_nfc_tags`, an App Chooser is displayed
for discovered NFC tags by Android if multiple applications which are able to
dispatch NFC tags are installed. An application can suppress this App Chooser
if it registers itself for **foreground dispatching** at runtime. This way NFC
tags are handled directly by the application without a chooser being displayed.
Subsequently the client is required to send them to the |AppName| SDK by
calling the **updateNfcTag** method of the previously acquired **IAusweisApp2Sdk**
instance.
The required steps to handle NFC tags directly are shown in the code listing below
by way of example.


.. code-block:: java

  import android.app.Activity;
  import android.nfc.NfcAdapter;
  import android.nfc.tech.IsoDep;
  import java.util.Arrays;

  import com.governikus.ausweisapp2.IAusweisApp2Sdk;

  class ForegroundDispatcher
  {
    private final Activity mActivity;
    private final NfcAdapter mAdapter;
    private final int mFlags;
    private final NfcAdapter.ReaderCallback mReaderCallback;

    ForegroundDispatcher(Activity pActivity, final IAusweisApp2Sdk pSdk, final String pSdkSessionID)
    {
      mActivity = pActivity;
      mAdapter = NfcAdapter.getDefaultAdapter(mActivity);
      mFlags = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK;
      mReaderCallback = new NfcAdapter.ReaderCallback()
      {
        public void onTagDiscovered(Tag pTag)
        {
          if (Arrays.asList(pTag.getTechList()).contains(IsoDep.class.getName()))
          {
            pSdk.updateNfcTag(pSdkSessionID, pTag);
          }
        }
      };
    }

    void enable()
    {
      if (mAdapter != null)
      {
        mAdapter.enableReaderMode(mActivity, mReaderCallback, mFlags, null);
      }
    }

    void disable()
    {
      if (mAdapter != null)
      {
        mAdapter.disableReaderMode(mActivity);
      }
    }
  }

  // [...]

  ForegroundDispatcher mDispatcher = new ForegroundDispatcher(this);



The example implementation from above needs to be invoked when the application
is brought to foreground and when it looses focus. An example for appropriate
places are the **onResume** and the **onPause** methods of Activities as shown
in the code listing below.

.. code-block:: java

  @Override
  public void onResume()
  {
    super.onResume();
    mDispatcher.enable();
  }

  @Override
  public void onPause()
  {
    super.onPause();
    mDispatcher.disable();
  }


.. seealso::

  https://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle