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) 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