1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 /*
17  * Copyright (c) 2015-2017, The Linux Foundation.
18  */
19 /*
20  * Contributed by: Giesecke & Devrient GmbH.
21  */
22 
23 package android.se.omapi;
24 
25 import android.annotation.NonNull;
26 import android.annotation.RequiresPermission;
27 import android.annotation.SystemApi;
28 import android.os.RemoteException;
29 import android.os.ServiceSpecificException;
30 import android.util.Log;
31 
32 import java.io.IOException;
33 
34 /**
35  * Instances of this class represent Secure Element Readers supported to this
36  * device. These Readers can be physical devices or virtual devices. They can be
37  * removable or not. They can contain Secure Element that can or cannot be
38  * removed.
39  *
40  * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
41  */
42 public final class Reader {
43 
44     private static final String TAG = "OMAPI.Reader";
45     private final String mName;
46     private final SEService mService;
47     private ISecureElementReader mReader;
48     private final Object mLock = new Object();
49 
50 
Reader(@onNull SEService service, @NonNull String name, @NonNull ISecureElementReader reader)51     Reader(@NonNull SEService service, @NonNull String name, @NonNull ISecureElementReader reader) {
52         if (reader == null || service == null || name == null) {
53             throw new IllegalArgumentException("Parameters cannot be null");
54         }
55         mName = name;
56         mService = service;
57         mReader = reader;
58     }
59 
60     /**
61      * Return the name of this reader.
62      * <ul>
63      * <li>If this reader is a SIM reader, then its name must be "SIM[Slot]".</li>
64      * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li>
65      * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li>
66      * </ul>
67      * Slot is a decimal number without leading zeros. The Numbering must start with 1
68      * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...).
69      * The slot number “1” for a reader is optional
70      * (SIM and SIM1 are both valid for the first SIM-reader,
71      * but if there are two readers then the second reader must be named SIM2).
72      * This applies also for other SD or SE readers.
73      *
74      * @return the reader name, as a String.
75      */
getName()76     public @NonNull String getName() {
77         return mName;
78     }
79 
80     /**
81      * Connects to a Secure Element in this reader. <br>
82      * This method prepares (initialises) the Secure Element for communication
83      * before the Session object is returned (e.g. powers the Secure Element by
84      * ICC ON if its not already on). There might be multiple sessions opened at
85      * the same time on the same reader. The system ensures the interleaving of
86      * APDUs between the respective sessions.
87      *
88      * @throws IOException if something went wrong with the communicating to the
89      *             Secure Element or the reader.
90      * @return a Session object to be used to create Channels.
91      */
openSession()92     public @NonNull Session openSession() throws IOException {
93         if (!mService.isConnected()) {
94             throw new IllegalStateException("service is not connected");
95         }
96 
97         synchronized (mLock) {
98             ISecureElementSession session;
99             try {
100                 session = mReader.openSession();
101             } catch (ServiceSpecificException e) {
102                 throw new IOException(e.getMessage());
103             } catch (RemoteException e) {
104                 throw new IllegalStateException(e.getMessage());
105             }
106             if (session == null) {
107                 throw new IOException("service session is null.");
108             }
109             return new Session(mService, session, this);
110         }
111     }
112 
113     /**
114      * Check if a Secure Element is present in this reader.
115      *
116      * @throws IllegalStateException if the service is not connected
117      * @return <code>true</code> if the SE is present, <code>false</code> otherwise.
118      */
isSecureElementPresent()119     public boolean isSecureElementPresent() {
120         if (!mService.isConnected()) {
121             throw new IllegalStateException("service is not connected");
122         }
123 
124         try {
125             return mReader.isSecureElementPresent();
126         } catch (RemoteException e) {
127             throw new IllegalStateException("Error in isSecureElementPresent()");
128         }
129     }
130 
131     /**
132      * Return the Secure Element service this reader is bound to.
133      *
134      * @return the SEService object.
135      */
getSEService()136     public @NonNull SEService getSEService() {
137         return mService;
138     }
139 
140     /**
141      * Close all the sessions opened on this reader.
142      * All the channels opened by all these sessions will be closed.
143      */
closeSessions()144     public void closeSessions() {
145         if (!mService.isConnected()) {
146             Log.e(TAG, "service is not connected");
147             return;
148         }
149         synchronized (mLock) {
150             try {
151                 mReader.closeSessions();
152             } catch (RemoteException ignore) { }
153         }
154     }
155 
156     /**
157      * Close all the sessions opened on this reader and reset the reader.
158      * All the channels opened by all these sessions will be closed.
159      * @return <code>true</code> if reset success, <code>false</code> otherwise.
160      * @hide
161      */
162     @SystemApi
163     @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION)
reset()164     public boolean reset() {
165         if (!mService.isConnected()) {
166             Log.e(TAG, "service is not connected");
167             return false;
168         }
169         synchronized (mLock) {
170             try {
171                 closeSessions();
172                 return mReader.reset();
173             } catch (RemoteException ignore) {
174                 return false;
175             }
176         }
177     }
178 }
179