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