Home | History | Annotate | Download | only in se
      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 /*
     21  * Contributed by: Giesecke & Devrient GmbH.
     22  */
     23 
     24 package com.android.se;
     25 
     26 import android.content.Context;
     27 import android.content.pm.PackageManager;
     28 import android.hardware.secure_element.V1_0.ISecureElement;
     29 import android.hardware.secure_element.V1_0.ISecureElementHalCallback;
     30 import android.hardware.secure_element.V1_0.LogicalChannelResponse;
     31 import android.hardware.secure_element.V1_0.SecureElementStatus;
     32 import android.os.Build;
     33 import android.os.Handler;
     34 import android.os.HwBinder;
     35 import android.os.Message;
     36 import android.os.RemoteException;
     37 import android.os.ServiceSpecificException;
     38 import android.se.omapi.ISecureElementListener;
     39 import android.se.omapi.ISecureElementReader;
     40 import android.se.omapi.ISecureElementSession;
     41 import android.se.omapi.SEService;
     42 import android.util.Log;
     43 
     44 import com.android.se.SecureElementService.SecureElementSession;
     45 import com.android.se.internal.ByteArrayConverter;
     46 import com.android.se.security.AccessControlEnforcer;
     47 import com.android.se.security.ChannelAccess;
     48 
     49 import java.io.IOException;
     50 import java.io.PrintWriter;
     51 import java.util.ArrayList;
     52 import java.util.Collection;
     53 import java.util.HashMap;
     54 import java.util.Map;
     55 import java.util.MissingResourceException;
     56 import java.util.NoSuchElementException;
     57 
     58 /**
     59  * Each Terminal represents a Secure Element.
     60  * Communicates to the SE via SecureElement HAL.
     61  */
     62 public class Terminal {
     63 
     64     private final String mTag;
     65     private final Map<Integer, Channel> mChannels = new HashMap<Integer, Channel>();
     66     private final Object mLock = new Object();
     67     private final String mName;
     68     public boolean mIsConnected = false;
     69     private Context mContext;
     70     private boolean mDefaultApplicationSelectedOnBasicChannel = true;
     71 
     72     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
     73     private static final int GET_SERVICE_DELAY_MILLIS = 4 * 1000;
     74     private static final int EVENT_GET_HAL = 1;
     75 
     76     private ISecureElement mSEHal;
     77 
     78     /** For each Terminal there will be one AccessController object. */
     79     private AccessControlEnforcer mAccessControlEnforcer;
     80 
     81     private ISecureElementHalCallback.Stub mHalCallback = new ISecureElementHalCallback.Stub() {
     82         @Override
     83         public void onStateChange(boolean state) {
     84             synchronized (mLock) {
     85                 Log.i(mTag, "OnStateChange:" + state);
     86                 mIsConnected = state;
     87                 if (!state) {
     88                     if (mAccessControlEnforcer != null) {
     89                         mAccessControlEnforcer.reset();
     90                     }
     91                 } else {
     92                     // If any logical channel in use is in the channel list, it should be closed
     93                     // because the access control enfocer allowed to open it by checking the access
     94                     // rules retrieved before. Now we are going to retrieve the rules again and
     95                     // the new rules can be different from the previous ones.
     96                     closeChannels();
     97                     try {
     98                         initializeAccessControl();
     99                     } catch (Exception e) {
    100                         // ignore
    101                     }
    102                     mDefaultApplicationSelectedOnBasicChannel = true;
    103                 }
    104             }
    105         }
    106     };
    107 
    108     class SecureElementDeathRecipient implements HwBinder.DeathRecipient {
    109         @Override
    110         public void serviceDied(long cookie) {
    111             Log.e(mTag, mName + " died");
    112             synchronized (mLock) {
    113                 mIsConnected = false;
    114                 if (mAccessControlEnforcer != null) {
    115                     mAccessControlEnforcer.reset();
    116                 }
    117             }
    118             mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_GET_HAL, 0),
    119                     GET_SERVICE_DELAY_MILLIS);
    120         }
    121     }
    122 
    123     private HwBinder.DeathRecipient mDeathRecipient = new SecureElementDeathRecipient();
    124 
    125     private Handler mHandler = new Handler() {
    126         @Override
    127         public void handleMessage(Message message) {
    128             switch (message.what) {
    129                 case EVENT_GET_HAL:
    130                     try {
    131                         initialize();
    132                     } catch (Exception e) {
    133                         Log.e(mTag, mName + " could not be initialized again");
    134                         sendMessageDelayed(obtainMessage(EVENT_GET_HAL, 0),
    135                                 GET_SERVICE_DELAY_MILLIS);
    136                     }
    137                     break;
    138                 default:
    139                     break;
    140             }
    141         }
    142     };
    143 
    144     public Terminal(String name, Context context) {
    145         mContext = context;
    146         mName = name;
    147         mTag = "SecureElement-Terminal-" + getName();
    148     }
    149 
    150     /**
    151      * Initializes the terminal
    152      *
    153      * @throws NoSuchElementException if there is no HAL implementation for the specified SE name
    154      * @throws RemoteException if there is a failure communicating with the remote
    155      */
    156     public void initialize() throws NoSuchElementException, RemoteException {
    157         synchronized (mLock) {
    158             mSEHal = ISecureElement.getService(mName);
    159             if (mSEHal == null) {
    160                 throw new NoSuchElementException("No HAL is provided for " + mName);
    161             }
    162             mSEHal.init(mHalCallback);
    163             mSEHal.linkToDeath(mDeathRecipient, 0);
    164         }
    165         Log.i(mTag, mName + " was initialized");
    166     }
    167 
    168     private ArrayList<Byte> byteArrayToArrayList(byte[] array) {
    169         ArrayList<Byte> list = new ArrayList<Byte>();
    170         if (array == null) {
    171             return list;
    172         }
    173 
    174         for (Byte b : array) {
    175             list.add(b);
    176         }
    177         return list;
    178     }
    179 
    180     private byte[] arrayListToByteArray(ArrayList<Byte> list) {
    181         Byte[] byteArray = list.toArray(new Byte[list.size()]);
    182         int i = 0;
    183         byte[] result = new byte[list.size()];
    184         for (Byte b : byteArray) {
    185             result[i++] = b.byteValue();
    186         }
    187         return result;
    188     }
    189 
    190     /**
    191      * Closes the given channel
    192      */
    193     public void closeChannel(Channel channel) {
    194         if (channel == null) {
    195             return;
    196         }
    197         if (mIsConnected) {
    198             try {
    199                 byte status = mSEHal.closeChannel((byte) channel.getChannelNumber());
    200                 /* For Basic Channels, errors are expected.
    201                  * Underlying implementations use this call as an indication when there
    202                  * aren't any users actively using the channel, and the chip can go
    203                  * into low power state.
    204                  */
    205                 if (!channel.isBasicChannel() && status != SecureElementStatus.SUCCESS) {
    206                     Log.e(mTag, "Error closing channel " + channel.getChannelNumber());
    207                 }
    208             } catch (RemoteException e) {
    209                 Log.e(mTag, "Exception in closeChannel() " + e);
    210             }
    211         }
    212         synchronized (mLock) {
    213             mChannels.remove(channel.getChannelNumber(), channel);
    214             if (mChannels.get(channel.getChannelNumber()) != null) {
    215                 Log.e(mTag, "Removing channel failed");
    216             }
    217         }
    218     }
    219 
    220     /**
    221      * Cleans up all the channels in use.
    222      */
    223     public synchronized void closeChannels() {
    224         Collection<Channel> col = mChannels.values();
    225         Channel[] channelList = col.toArray(new Channel[col.size()]);
    226         for (Channel channel : channelList) {
    227             channel.close();
    228         }
    229     }
    230 
    231     /**
    232      * Closes the terminal.
    233      */
    234     public void close() {
    235         synchronized (mLock) {
    236             if (mSEHal != null) {
    237                 try {
    238                     mSEHal.unlinkToDeath(mDeathRecipient);
    239                 } catch (RemoteException e) {
    240                     // ignore
    241                 }
    242             }
    243         }
    244     }
    245 
    246     public String getName() {
    247         return mName;
    248     }
    249 
    250     /**
    251      * Returns the ATR of the Secure Element, or null if not available.
    252      */
    253     public byte[] getAtr() {
    254         if (!mIsConnected) {
    255             return null;
    256         }
    257 
    258         try {
    259             ArrayList<Byte> responseList = mSEHal.getAtr();
    260             if (responseList.isEmpty()) {
    261                 return null;
    262             }
    263             return arrayListToByteArray(responseList);
    264         } catch (RemoteException e) {
    265             Log.e(mTag, "Exception in getAtr()" + e);
    266             return null;
    267         }
    268     }
    269 
    270     /**
    271      * Selects the default application on the basic channel.
    272      *
    273      * If there is an exception selecting the default application, select
    274      * is performed with the default access control aid.
    275      */
    276     public void selectDefaultApplication() {
    277         try {
    278             select(null);
    279         } catch (NoSuchElementException e) {
    280             if (getAccessControlEnforcer() != null) {
    281                 try {
    282                     select(mAccessControlEnforcer.getDefaultAccessControlAid());
    283                 } catch (Exception ignore) {
    284                 }
    285             }
    286         } catch (Exception ignore) {
    287         }
    288     }
    289 
    290     private void select(byte[] aid) throws IOException {
    291         int commandSize = (aid == null ? 0 : aid.length) + 5;
    292         byte[] selectCommand = new byte[commandSize];
    293         selectCommand[0] = 0x00;
    294         selectCommand[1] = (byte) 0xA4;
    295         selectCommand[2] = 0x04;
    296         selectCommand[3] = 0x00;
    297         if (aid != null && aid.length != 0) {
    298             selectCommand[4] = (byte) aid.length;
    299             System.arraycopy(aid, 0, selectCommand, 5, aid.length);
    300         } else {
    301             selectCommand[4] = 0x00;
    302         }
    303         byte[] selectResponse = transmit(selectCommand);
    304         if (selectResponse.length < 2) {
    305             selectResponse = null;
    306             throw new NoSuchElementException("Response length is too small");
    307         }
    308         int sw1 = selectResponse[selectResponse.length - 2] & 0xFF;
    309         int sw2 = selectResponse[selectResponse.length - 1] & 0xFF;
    310         if (sw1 != 0x90 || sw2 != 0x00) {
    311             selectResponse = null;
    312             throw new NoSuchElementException("Status word is incorrect");
    313         }
    314     }
    315 
    316     /**
    317      * Opens a Basic Channel with the given AID and P2 paramters
    318      */
    319     public Channel openBasicChannel(SecureElementSession session, byte[] aid, byte p2,
    320             ISecureElementListener listener, String packageName, int pid) throws IOException,
    321             NoSuchElementException {
    322         if (aid != null && aid.length == 0) {
    323             aid = null;
    324         } else if (aid != null && (aid.length < 5 || aid.length > 16)) {
    325             throw new IllegalArgumentException("AID out of range");
    326         } else if (!mIsConnected) {
    327             throw new IOException("Secure Element is not connected");
    328         }
    329 
    330         Log.w(mTag, "Enable access control on basic channel for " + packageName);
    331         ChannelAccess channelAccess;
    332         try {
    333             channelAccess = setUpChannelAccess(aid, packageName, pid);
    334         } catch (MissingResourceException e) {
    335             return null;
    336         }
    337 
    338         synchronized (mLock) {
    339             if (mChannels.get(0) != null) {
    340                 Log.e(mTag, "basic channel in use");
    341                 return null;
    342             }
    343             if (aid == null && !mDefaultApplicationSelectedOnBasicChannel) {
    344                 Log.e(mTag, "default application is not selected");
    345                 return null;
    346             }
    347 
    348             ArrayList<byte[]> responseList = new ArrayList<byte[]>();
    349             byte[] status = new byte[1];
    350 
    351             try {
    352                 mSEHal.openBasicChannel(byteArrayToArrayList(aid), p2,
    353                         new ISecureElement.openBasicChannelCallback() {
    354                             @Override
    355                             public void onValues(ArrayList<Byte> responseObject, byte halStatus) {
    356                                 status[0] = halStatus;
    357                                 responseList.add(arrayListToByteArray(responseObject));
    358                                 return;
    359                             }
    360                         });
    361             } catch (RemoteException e) {
    362                 throw new IOException(e.getMessage());
    363             }
    364 
    365             byte[] selectResponse = responseList.get(0);
    366             if (status[0] == SecureElementStatus.CHANNEL_NOT_AVAILABLE) {
    367                 return null;
    368             } else if (status[0] == SecureElementStatus.UNSUPPORTED_OPERATION) {
    369                 throw new UnsupportedOperationException("OpenBasicChannel() failed");
    370             } else if (status[0] == SecureElementStatus.IOERROR) {
    371                 throw new IOException("OpenBasicChannel() failed");
    372             } else if (status[0] == SecureElementStatus.NO_SUCH_ELEMENT_ERROR) {
    373                 throw new NoSuchElementException("OpenBasicChannel() failed");
    374             }
    375 
    376             Channel basicChannel = new Channel(session, this, 0, selectResponse, aid,
    377                     listener);
    378             basicChannel.setChannelAccess(channelAccess);
    379 
    380             if (aid != null) {
    381                 mDefaultApplicationSelectedOnBasicChannel = false;
    382             }
    383             mChannels.put(0, basicChannel);
    384             return basicChannel;
    385         }
    386     }
    387 
    388     /**
    389      * Opens a logical Channel without Channel Access initialization.
    390      */
    391     public Channel openLogicalChannelWithoutChannelAccess(byte[] aid) throws IOException,
    392             NoSuchElementException {
    393         return openLogicalChannel(null, aid, (byte) 0x00, null, null, 0);
    394     }
    395 
    396     /**
    397      * Opens a logical Channel with AID.
    398      */
    399     public Channel openLogicalChannel(SecureElementSession session, byte[] aid, byte p2,
    400             ISecureElementListener listener, String packageName, int pid) throws IOException,
    401             NoSuchElementException {
    402         if (aid != null && aid.length == 0) {
    403             aid = null;
    404         } else if (aid != null && (aid.length < 5 || aid.length > 16)) {
    405             throw new IllegalArgumentException("AID out of range");
    406         } else if (!mIsConnected) {
    407             throw new IOException("Secure Element is not connected");
    408         }
    409 
    410         ChannelAccess channelAccess = null;
    411         if (packageName != null) {
    412             Log.w(mTag, "Enable access control on logical channel for " + packageName);
    413             try {
    414                 channelAccess = setUpChannelAccess(aid, packageName, pid);
    415             } catch (MissingResourceException e) {
    416                 return null;
    417             }
    418         }
    419 
    420         synchronized (mLock) {
    421             LogicalChannelResponse[] responseArray = new LogicalChannelResponse[1];
    422             byte[] status = new byte[1];
    423 
    424             try {
    425                 mSEHal.openLogicalChannel(byteArrayToArrayList(aid), p2,
    426                         new ISecureElement.openLogicalChannelCallback() {
    427                             @Override
    428                             public void onValues(LogicalChannelResponse response, byte halStatus) {
    429                                 status[0] = halStatus;
    430                                 responseArray[0] = response;
    431                                 return;
    432                             }
    433                         });
    434             } catch (RemoteException e) {
    435                 throw new IOException(e.getMessage());
    436             }
    437 
    438             if (status[0] == SecureElementStatus.CHANNEL_NOT_AVAILABLE) {
    439                 return null;
    440             } else if (status[0] == SecureElementStatus.UNSUPPORTED_OPERATION) {
    441                 throw new UnsupportedOperationException("OpenLogicalChannel() failed");
    442             } else if (status[0] == SecureElementStatus.IOERROR) {
    443                 throw new IOException("OpenLogicalChannel() failed");
    444             } else if (status[0] == SecureElementStatus.NO_SUCH_ELEMENT_ERROR) {
    445                 throw new NoSuchElementException("OpenLogicalChannel() failed");
    446             }
    447             if (responseArray[0].channelNumber <= 0 || status[0] != SecureElementStatus.SUCCESS) {
    448                 return null;
    449             }
    450             int channelNumber = responseArray[0].channelNumber;
    451             byte[] selectResponse = arrayListToByteArray(responseArray[0].selectResponse);
    452             Channel logicalChannel = new Channel(session, this, channelNumber,
    453                     selectResponse, aid, listener);
    454             logicalChannel.setChannelAccess(channelAccess);
    455 
    456             mChannels.put(channelNumber, logicalChannel);
    457             return logicalChannel;
    458         }
    459     }
    460 
    461     /**
    462      * Returns true if the given AID can be selected on the Terminal
    463      */
    464     public boolean isAidSelectable(byte[] aid) {
    465         if (aid == null) {
    466             throw new NullPointerException("aid must not be null");
    467         } else if (!mIsConnected) {
    468             Log.e(mTag, "Secure Element is not connected");
    469             return false;
    470         }
    471 
    472         synchronized (mLock) {
    473             LogicalChannelResponse[] responseArray = new LogicalChannelResponse[1];
    474             byte[] status = new byte[1];
    475             try {
    476                 mSEHal.openLogicalChannel(byteArrayToArrayList(aid), (byte) 0x00,
    477                         new ISecureElement.openLogicalChannelCallback() {
    478                             @Override
    479                             public void onValues(LogicalChannelResponse response, byte halStatus) {
    480                                 status[0] = halStatus;
    481                                 responseArray[0] = response;
    482                                 return;
    483                             }
    484                         });
    485                 if (status[0] == SecureElementStatus.SUCCESS) {
    486                     mSEHal.closeChannel(responseArray[0].channelNumber);
    487                     return true;
    488                 }
    489                 return false;
    490             } catch (RemoteException e) {
    491                 Log.e(mTag, "Error in isAidSelectable() returning false" + e);
    492                 return false;
    493             }
    494         }
    495     }
    496 
    497     /**
    498      * Transmits the specified command and returns the response.
    499      *
    500      * @param cmd the command APDU to be transmitted.
    501      * @return the response received.
    502      */
    503     public byte[] transmit(byte[] cmd) throws IOException {
    504         if (!mIsConnected) {
    505             Log.e(mTag, "Secure Element is not connected");
    506             throw new IOException("Secure Element is not connected");
    507         }
    508 
    509         byte[] rsp = transmitInternal(cmd);
    510         int sw1 = rsp[rsp.length - 2] & 0xFF;
    511         int sw2 = rsp[rsp.length - 1] & 0xFF;
    512 
    513         if (sw1 == 0x6C) {
    514             cmd[cmd.length - 1] = rsp[rsp.length - 1];
    515             rsp = transmitInternal(cmd);
    516         } else if (sw1 == 0x61) {
    517             do {
    518                 byte[] getResponseCmd = new byte[]{
    519                         cmd[0], (byte) 0xC0, 0x00, 0x00, (byte) sw2
    520                 };
    521                 byte[] tmp = transmitInternal(getResponseCmd);
    522                 byte[] aux = rsp;
    523                 rsp = new byte[aux.length + tmp.length - 2];
    524                 System.arraycopy(aux, 0, rsp, 0, aux.length - 2);
    525                 System.arraycopy(tmp, 0, rsp, aux.length - 2, tmp.length);
    526                 sw1 = rsp[rsp.length - 2] & 0xFF;
    527                 sw2 = rsp[rsp.length - 1] & 0xFF;
    528             } while (sw1 == 0x61);
    529         }
    530         return rsp;
    531     }
    532 
    533     private byte[] transmitInternal(byte[] cmd) throws IOException {
    534         ArrayList<Byte> response;
    535         try {
    536             response = mSEHal.transmit(byteArrayToArrayList(cmd));
    537         } catch (RemoteException e) {
    538             throw new IOException(e.getMessage());
    539         }
    540         if (response.isEmpty()) {
    541             throw new IOException("Error in transmit()");
    542         }
    543         byte[] rsp = arrayListToByteArray(response);
    544         if (DEBUG) {
    545             Log.i(mTag, "Sent : " + ByteArrayConverter.byteArrayToHexString(cmd));
    546             Log.i(mTag, "Received : " + ByteArrayConverter.byteArrayToHexString(rsp));
    547         }
    548         return rsp;
    549     }
    550 
    551     /**
    552      * Checks if the application is authorized to receive the transaction event.
    553      */
    554     public boolean[] isNfcEventAllowed(PackageManager packageManager, byte[] aid,
    555             String[] packageNames) {
    556         boolean checkRefreshTag = true;
    557         if (mAccessControlEnforcer == null) {
    558             try {
    559                 initializeAccessControl();
    560                 // Just finished to initialize the access control enforcer.
    561                 // It is too much to check the refresh tag in this case.
    562                 checkRefreshTag = false;
    563             } catch (Exception e) {
    564                 Log.i(mTag, "isNfcEventAllowed Exception: " + e.getMessage());
    565                 return null;
    566             }
    567         }
    568         mAccessControlEnforcer.setPackageManager(packageManager);
    569 
    570         synchronized (mLock) {
    571             try {
    572                 return mAccessControlEnforcer.isNfcEventAllowed(aid, packageNames,
    573                         checkRefreshTag);
    574             } catch (Exception e) {
    575                 Log.i(mTag, "isNfcEventAllowed Exception: " + e.getMessage());
    576                 return null;
    577             }
    578         }
    579     }
    580 
    581     /**
    582      * Returns true if the Secure Element is present
    583      */
    584     public boolean isSecureElementPresent() {
    585         try {
    586             return mSEHal.isCardPresent();
    587         } catch (RemoteException e) {
    588             Log.e(mTag, "Error in isSecureElementPresent() " + e);
    589             return false;
    590         }
    591     }
    592 
    593     /**
    594      * Initialize the Access Control and set up the channel access.
    595      */
    596     private ChannelAccess setUpChannelAccess(byte[] aid, String packageName, int pid)
    597             throws IOException, MissingResourceException {
    598         boolean checkRefreshTag = true;
    599         if (mAccessControlEnforcer == null) {
    600             initializeAccessControl();
    601             // Just finished to initialize the access control enforcer.
    602             // It is too much to check the refresh tag in this case.
    603             checkRefreshTag = false;
    604         }
    605         mAccessControlEnforcer.setPackageManager(mContext.getPackageManager());
    606 
    607         synchronized (mLock) {
    608             try {
    609                 ChannelAccess channelAccess =
    610                         mAccessControlEnforcer.setUpChannelAccess(aid, packageName,
    611                                 checkRefreshTag);
    612                 channelAccess.setCallingPid(pid);
    613                 return channelAccess;
    614             } catch (IOException | MissingResourceException e) {
    615                 throw e;
    616             } catch (Exception e) {
    617                 throw new SecurityException("Exception in setUpChannelAccess()" + e);
    618             }
    619         }
    620     }
    621 
    622     /**
    623      * Initializes the Access Control for this Terminal
    624      */
    625     private synchronized void initializeAccessControl() throws IOException,
    626             MissingResourceException {
    627         synchronized (mLock) {
    628             if (mAccessControlEnforcer == null) {
    629                 mAccessControlEnforcer = new AccessControlEnforcer(this);
    630             }
    631             try {
    632                 mAccessControlEnforcer.initialize();
    633             } catch (IOException | MissingResourceException e) {
    634                 // Retrieving access rules failed because of an IO error happened between
    635                 // the terminal and the secure element or the lack of a logical channel available.
    636                 // It might be a temporary failure, so the terminal shall attempt to cache
    637                 // the access rules again later.
    638                 mAccessControlEnforcer = null;
    639                 throw e;
    640             }
    641         }
    642     }
    643 
    644     public AccessControlEnforcer getAccessControlEnforcer() {
    645         return mAccessControlEnforcer;
    646     }
    647 
    648     /** Dump data for debug purpose . */
    649     public void dump(PrintWriter writer) {
    650         writer.println("SECURE ELEMENT SERVICE TERMINAL: " + mName);
    651         writer.println();
    652 
    653         writer.println("mIsConnected:" + mIsConnected);
    654         writer.println();
    655 
    656         /* Dump the list of currunlty openned channels */
    657         writer.println("List of open channels:");
    658 
    659         for (Channel channel : mChannels.values()) {
    660             writer.println("channel " + channel.getChannelNumber() + ": ");
    661             writer.println("package: " + channel.getChannelAccess().getPackageName());
    662             writer.println("pid: " + channel.getChannelAccess().getCallingPid());
    663             writer.println("aid selected: " + channel.hasSelectedAid());
    664             writer.println("basic channel: " + channel.isBasicChannel());
    665             writer.println();
    666         }
    667         writer.println();
    668 
    669         /* Dump ACE data */
    670         if (mAccessControlEnforcer != null) {
    671             mAccessControlEnforcer.dump(writer);
    672         }
    673     }
    674 
    675     // Implementation of the SecureElement Reader interface according to OMAPI.
    676     final class SecureElementReader extends ISecureElementReader.Stub {
    677 
    678         private final SecureElementService mService;
    679         private final ArrayList<SecureElementSession> mSessions =
    680                 new ArrayList<SecureElementSession>();
    681 
    682         SecureElementReader(SecureElementService service) {
    683             mService = service;
    684         }
    685 
    686         public byte[] getAtr() {
    687             return Terminal.this.getAtr();
    688         }
    689 
    690         @Override
    691         public boolean isSecureElementPresent() throws RemoteException {
    692             return Terminal.this.isSecureElementPresent();
    693         }
    694 
    695         @Override
    696         public void closeSessions() {
    697             synchronized (mLock) {
    698                 while (mSessions.size() > 0) {
    699                     try {
    700                         mSessions.get(0).close();
    701                     } catch (Exception ignore) {
    702                     }
    703                 }
    704                 mSessions.clear();
    705             }
    706         }
    707 
    708         public void removeSession(SecureElementSession session) {
    709             if (session == null) {
    710                 throw new NullPointerException("session is null");
    711             }
    712             mSessions.remove(session);
    713             synchronized (mLock) {
    714                 if (mSessions.size() == 0) {
    715                     mDefaultApplicationSelectedOnBasicChannel = true;
    716                 }
    717             }
    718         }
    719 
    720         @Override
    721         public ISecureElementSession openSession() throws RemoteException {
    722             if (!isSecureElementPresent()) {
    723                 throw new ServiceSpecificException(SEService.IO_ERROR,
    724                         "Secure Element is not present.");
    725             }
    726 
    727             synchronized (mLock) {
    728                 SecureElementSession session = mService.new SecureElementSession(this);
    729                 mSessions.add(session);
    730                 return session;
    731             }
    732         }
    733 
    734         Terminal getTerminal() {
    735             return Terminal.this;
    736         }
    737     }
    738 }
    739