/* * 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 GlobalPlatform Open Mobile API */ 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. * * 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.
* 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 true if the SE is present, false 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) { } } } }