summaryrefslogtreecommitdiff
path: root/android/se/omapi/Reader.java
blob: 80262f7533c8b8ea9e3048a564f0b06759b6b809 (plain)
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
/*
 * Copyright (C) 2017 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.
 */
/*
 * Copyright (c) 2015-2017, The Linux Foundation.
 */
/*
 * Contributed by: Giesecke & Devrient GmbH.
 */

package android.se.omapi;

import android.annotation.NonNull;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;

import java.io.IOException;

/**
 * Instances of this class represent Secure Element Readers supported to this
 * device. These Readers can be physical devices or virtual devices. They can be
 * removable or not. They can contain Secure Element that can or cannot be
 * removed.
 *
 * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
 */
public final class Reader {

    private static final String TAG = "OMAPI.Reader";
    private final String mName;
    private final SEService mService;
    private ISecureElementReader mReader;
    private final Object mLock = new Object();


    Reader(@NonNull SEService service, @NonNull String name, @NonNull ISecureElementReader reader) {
        if (reader == null || service == null || name == null) {
            throw new IllegalArgumentException("Parameters cannot be null");
        }
        mName = name;
        mService = service;
        mReader = reader;
    }

    /**
     * Return the name of this reader.
     * <ul>
     * <li>If this reader is a SIM reader, then its name must be "SIM[Slot]".</li>
     * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li>
     * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li>
     * </ul>
     * Slot is a decimal number without leading zeros. The Numbering must start with 1
     * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...).
     * The slot number “1” for a reader is optional
     * (SIM and SIM1 are both valid for the first SIM-reader,
     * but if there are two readers then the second reader must be named SIM2).
     * This applies also for other SD or SE readers.
     *
     * @return the reader name, as a String.
     */
    public @NonNull String getName() {
        return mName;
    }

    /**
     * Connects to a Secure Element in this reader. <br>
     * This method prepares (initialises) the Secure Element for communication
     * before the Session object is returned (e.g. powers the Secure Element by
     * ICC ON if its not already on). There might be multiple sessions opened at
     * the same time on the same reader. The system ensures the interleaving of
     * APDUs between the respective sessions.
     *
     * @throws IOException if something went wrong with the communicating to the
     *             Secure Element or the reader.
     * @return a Session object to be used to create Channels.
     */
    public @NonNull Session openSession() throws IOException {
        if (!mService.isConnected()) {
            throw new IllegalStateException("service is not connected");
        }

        synchronized (mLock) {
            ISecureElementSession session;
            try {
                session = mReader.openSession();
            } catch (ServiceSpecificException e) {
                throw new IOException(e.getMessage());
            } catch (RemoteException e) {
                throw new IllegalStateException(e.getMessage());
            }
            if (session == null) {
                throw new IOException("service session is null.");
            }
            return new Session(mService, session, this);
        }
    }

    /**
     * Check if a Secure Element is present in this reader.
     *
     * @throws IllegalStateException if the service is not connected
     * @return <code>true</code> if the SE is present, <code>false</code> otherwise.
     */
    public boolean isSecureElementPresent() {
        if (!mService.isConnected()) {
            throw new IllegalStateException("service is not connected");
        }

        try {
            return mReader.isSecureElementPresent();
        } catch (RemoteException e) {
            throw new IllegalStateException("Error in isSecureElementPresent()");
        }
    }

    /**
     * Return the Secure Element service this reader is bound to.
     *
     * @return the SEService object.
     */
    public @NonNull SEService getSEService() {
        return mService;
    }

    /**
     * Close all the sessions opened on this reader.
     * All the channels opened by all these sessions will be closed.
     */
    public void closeSessions() {
        if (!mService.isConnected()) {
            Log.e(TAG, "service is not connected");
            return;
        }
        synchronized (mLock) {
            try {
                mReader.closeSessions();
            } catch (RemoteException ignore) { }
        }
    }
}