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