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) 2014-2017, The Linux Foundation. 18 */ 19 /* 20 * Contributed by: Giesecke & Devrient GmbH. 21 */ 22 23 package com.android.se; 24 25 import android.app.Service; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.pm.PackageManager; 29 import android.os.Binder; 30 import android.os.IBinder; 31 import android.os.RemoteException; 32 import android.os.ServiceManager; 33 import android.os.ServiceSpecificException; 34 import android.se.omapi.ISecureElementChannel; 35 import android.se.omapi.ISecureElementListener; 36 import android.se.omapi.ISecureElementReader; 37 import android.se.omapi.ISecureElementService; 38 import android.se.omapi.ISecureElementSession; 39 import android.se.omapi.SEService; 40 import android.util.Log; 41 42 import com.android.se.Terminal.SecureElementReader; 43 44 import java.io.FileDescriptor; 45 import java.io.IOException; 46 import java.io.PrintWriter; 47 import java.security.AccessControlException; 48 import java.util.ArrayList; 49 import java.util.LinkedHashMap; 50 import java.util.List; 51 import java.util.NoSuchElementException; 52 53 /** 54 * Underlying implementation for OMAPI SEService 55 */ 56 public final class SecureElementService extends Service { 57 58 public static final String UICC_TERMINAL = "SIM"; 59 public static final String ESE_TERMINAL = "eSE"; 60 private final String mTag = "SecureElementService"; 61 // LinkedHashMap will maintain the order of insertion 62 private LinkedHashMap<String, Terminal> mTerminals = new LinkedHashMap<String, Terminal>(); 63 private final ISecureElementService.Stub mSecureElementServiceBinder = 64 new ISecureElementService.Stub() { 65 66 @Override 67 public String[] getReaders() throws RemoteException { 68 return mTerminals.keySet().toArray(new String[mTerminals.size()]); 69 } 70 71 @Override 72 public ISecureElementReader getReader(String reader) 73 throws RemoteException { 74 Log.d(mTag, "getReader() " + reader); 75 Terminal terminal = getTerminal(reader); 76 return terminal.new SecureElementReader(SecureElementService.this); 77 } 78 79 @Override 80 public synchronized boolean[] isNFCEventAllowed(String reader, byte[] aid, 81 String[] packageNames) 82 throws RemoteException { 83 if (aid == null || aid.length == 0) { 84 aid = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00}; 85 } 86 if (aid.length < 5 || aid.length > 16) { 87 throw new IllegalArgumentException("AID out of range"); 88 } 89 if (packageNames == null || packageNames.length == 0) { 90 throw new IllegalArgumentException("package names not specified"); 91 } 92 Terminal terminal = getTerminal(reader); 93 return terminal.isNfcEventAllowed(getPackageManager(), aid, packageNames); 94 } 95 96 @Override 97 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 98 for (Terminal terminal : mTerminals.values()) { 99 terminal.dump(writer); 100 } 101 } 102 }; 103 104 public SecureElementService() { 105 super(); 106 } 107 108 /** Returns the terminal from the Reader name. */ 109 private Terminal getTerminal(String reader) { 110 if (reader == null) { 111 throw new NullPointerException("reader must not be null"); 112 } 113 if (reader.equals("SIM")) { 114 reader = "SIM1"; 115 } 116 Terminal terminal = mTerminals.get(reader); 117 if (terminal == null) { 118 throw new IllegalArgumentException("Reader: " + reader + " doesn't exist"); 119 } 120 return terminal; 121 } 122 123 @Override 124 public IBinder onBind(Intent intent) { 125 Log.i(mTag, Thread.currentThread().getName() + " onBind"); 126 if (ISecureElementService.class.getName().equals(intent.getAction())) { 127 return mSecureElementServiceBinder; 128 } 129 return null; 130 } 131 132 @Override 133 public void onCreate() { 134 Log.i(mTag, Thread.currentThread().getName() + " onCreate"); 135 createTerminals(); 136 ServiceManager.addService(Context.SECURE_ELEMENT_SERVICE, mSecureElementServiceBinder); 137 } 138 139 /** 140 * In case the onDestroy is called, we free the memory and 141 * close all the channels. 142 */ 143 public void onDestroy() { 144 Log.i(mTag, "onDestroy"); 145 for (Terminal terminal : mTerminals.values()) { 146 terminal.closeChannels(); 147 terminal.close(); 148 } 149 } 150 151 private void addTerminals(String terminalName) { 152 int index = 1; 153 String name = null; 154 try { 155 do { 156 name = terminalName + Integer.toString(index); 157 Terminal terminal = new Terminal(name, this); 158 terminal.initialize(); 159 mTerminals.put(name, terminal); 160 } while (++index > 0); 161 } catch (NoSuchElementException e) { 162 Log.i(mTag, "No HAL implementation for " + name); 163 } catch (RemoteException | RuntimeException e) { 164 Log.e(mTag, "Error in getService() for " + name); 165 } 166 } 167 168 private void createTerminals() { 169 // Check for all SE HAL implementations 170 addTerminals(ESE_TERMINAL); 171 addTerminals(UICC_TERMINAL); 172 } 173 174 private String getPackageNameFromCallingUid(int uid) { 175 PackageManager packageManager = getPackageManager(); 176 if (packageManager != null) { 177 String[] packageName = packageManager.getPackagesForUid(uid); 178 if (packageName != null && packageName.length > 0) { 179 return packageName[0]; 180 } 181 } 182 throw new AccessControlException("PackageName can not be determined"); 183 } 184 185 final class SecureElementSession extends ISecureElementSession.Stub { 186 187 private final SecureElementReader mReader; 188 /** List of open channels in use of by this client. */ 189 private final List<Channel> mChannels = new ArrayList<>(); 190 private final Object mLock = new Object(); 191 private boolean mIsClosed; 192 private byte[] mAtr; 193 194 SecureElementSession(SecureElementReader reader) { 195 if (reader == null) { 196 throw new NullPointerException("SecureElementReader cannot be null"); 197 } 198 mReader = reader; 199 mAtr = mReader.getAtr(); 200 mIsClosed = false; 201 } 202 203 public ISecureElementReader getReader() throws RemoteException { 204 return mReader; 205 } 206 207 @Override 208 public byte[] getAtr() throws RemoteException { 209 return mAtr; 210 } 211 212 @Override 213 public void close() throws RemoteException { 214 closeChannels(); 215 mReader.removeSession(this); 216 synchronized (mLock) { 217 mIsClosed = true; 218 } 219 } 220 221 void removeChannel(Channel channel) { 222 synchronized (mLock) { 223 if (mChannels != null) { 224 mChannels.remove(channel); 225 } 226 } 227 } 228 229 @Override 230 public void closeChannels() throws RemoteException { 231 synchronized (mLock) { 232 while (mChannels.size() > 0) { 233 try { 234 mChannels.get(0).close(); 235 } catch (Exception ignore) { 236 Log.e(mTag, "SecureElementSession Channel - close Exception " 237 + ignore.getMessage()); 238 } 239 } 240 } 241 } 242 243 @Override 244 public boolean isClosed() throws RemoteException { 245 synchronized (mLock) { 246 return mIsClosed; 247 } 248 } 249 250 @Override 251 public ISecureElementChannel openBasicChannel(byte[] aid, byte p2, 252 ISecureElementListener listener) throws RemoteException { 253 if (isClosed()) { 254 throw new IllegalStateException("Session is closed"); 255 } else if (listener == null) { 256 throw new NullPointerException("listener must not be null"); 257 } else if (mReader.getTerminal().getName().startsWith( 258 SecureElementService.UICC_TERMINAL)) { 259 return null; 260 } else if ((p2 != 0x00) && (p2 != 0x04) && (p2 != 0x08) 261 && (p2 != (byte) 0x0C)) { 262 throw new UnsupportedOperationException("p2 not supported: " 263 + String.format("%02x ", p2 & 0xFF)); 264 } 265 266 String packageName = getPackageNameFromCallingUid(Binder.getCallingUid()); 267 Channel channel = null; 268 269 try { 270 channel = mReader.getTerminal().openBasicChannel(this, aid, p2, listener, 271 packageName, Binder.getCallingPid()); 272 } catch (IOException e) { 273 throw new ServiceSpecificException(SEService.IO_ERROR, e.getMessage()); 274 } catch (NoSuchElementException e) { 275 throw new ServiceSpecificException(SEService.NO_SUCH_ELEMENT_ERROR, e.getMessage()); 276 } 277 278 if (channel == null) { 279 Log.i(mTag, "OpenBasicChannel() - returning null"); 280 return null; 281 } 282 Log.i(mTag, "Open basic channel success. Channel: " 283 + channel.getChannelNumber()); 284 285 mChannels.add(channel); 286 return channel.new SecureElementChannel(); 287 } 288 289 @Override 290 public ISecureElementChannel openLogicalChannel(byte[] aid, byte p2, 291 ISecureElementListener listener) throws RemoteException { 292 if (isClosed()) { 293 throw new IllegalStateException("Session is closed"); 294 } else if (listener == null) { 295 throw new NullPointerException("listener must not be null"); 296 } else if (((aid == null) || (aid.length == 0)) && mReader.getTerminal().getName() 297 .startsWith(SecureElementService.UICC_TERMINAL)) { 298 return null; 299 } else if ((p2 != 0x00) && (p2 != 0x04) && (p2 != 0x08) 300 && (p2 != (byte) 0x0C)) { 301 throw new UnsupportedOperationException("p2 not supported: " 302 + String.format("%02x ", p2 & 0xFF)); 303 } 304 305 String packageName = getPackageNameFromCallingUid(Binder.getCallingUid()); 306 Channel channel = null; 307 308 try { 309 channel = mReader.getTerminal().openLogicalChannel(this, aid, p2, listener, 310 packageName, Binder.getCallingPid()); 311 } catch (IOException e) { 312 throw new ServiceSpecificException(SEService.IO_ERROR, e.getMessage()); 313 } catch (NoSuchElementException e) { 314 throw new ServiceSpecificException(SEService.NO_SUCH_ELEMENT_ERROR, e.getMessage()); 315 } 316 317 if (channel == null) { 318 Log.i(mTag, "openLogicalChannel() - returning null"); 319 return null; 320 } 321 Log.i(mTag, "openLogicalChannel() Success. Channel: " 322 + channel.getChannelNumber()); 323 324 mChannels.add(channel); 325 return channel.new SecureElementChannel(); 326 } 327 } 328 } 329