| /* |
| * 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.annotation.RequiresPermission; |
| import android.annotation.SystemApi; |
| 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) { } |
| } |
| } |
| |
| /** |
| * Close all the sessions opened on this reader and reset the reader. |
| * All the channels opened by all these sessions will be closed. |
| * @return <code>true</code> if reset success, <code>false</code> otherwise. |
| * @hide |
| */ |
| @SystemApi |
| @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION) |
| public boolean reset() { |
| if (!mService.isConnected()) { |
| Log.e(TAG, "service is not connected"); |
| return false; |
| } |
| synchronized (mLock) { |
| try { |
| closeSessions(); |
| return mReader.reset(); |
| } catch (RemoteException ignore) { |
| return false; |
| } |
| } |
| } |
| } |