1 /*
2  * Copyright (C) 2018 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 package android.telephony.utility;
17 
18 import android.app.Instrumentation;
19 import android.content.Context;
20 import android.device.collectors.util.SendToInstrumentation;
21 import android.os.Bundle;
22 import android.se.omapi.Reader;
23 import android.se.omapi.SEService;
24 import android.se.omapi.SEService.OnConnectedListener;
25 import android.telephony.TelephonyManager;
26 
27 import java.util.Timer;
28 import java.util.TimerTask;
29 import java.util.concurrent.Executor;
30 
31 import androidx.test.InstrumentationRegistry;
32 import androidx.test.runner.AndroidJUnit4;
33 
34 import org.junit.After;
35 import org.junit.Before;
36 import org.junit.Test;
37 import org.junit.runner.RunWith;
38 
39 import static org.junit.Assert.assertNotNull;
40 
41 /**
42  * Instrumentation test that allows to get some telephony states.
43  * <p>If TelephonyManager does not exists or is not supported, a test failure will be reported
44  */
45 @RunWith(AndroidJUnit4.class)
46 public class SimCardUtil {
47 
48     private static final String SIM_STATE = "sim_state";
49     private static final String CARRIER_PRIVILEGES = "has_carried_privileges";
50     private static final String SECURED_ELEMENT = "has_secured_element";
51     private static final String SE_SERVICE = "has_se_service";
52 
53     private static final long SERVICE_CONNECTION_TIME_OUT = 3000;
54 
55     private SEService mSeService;
56     private Object mServiceMutex = new Object();
57     private Timer mConnectionTimer;
58     private ServiceConnectionTimerTask mTimerTask = new ServiceConnectionTimerTask();
59     private boolean mConnected = false;
60     private final OnConnectedListener mListener = new OnConnectedListener() {
61                 @Override
62                 public void onConnected() {
63                     synchronized (mServiceMutex) {
64                         mConnected = true;
65                         mServiceMutex.notify();
66                     }
67                 }
68             };
69 
70     @Before
setUp()71     public void setUp() throws Exception {
72         mSeService = new SEService(InstrumentationRegistry.getContext(), new SynchronousExecutor(), mListener);
73         mConnectionTimer = new Timer();
74         mConnectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
75     }
76 
77     @Test
getSimCardInformation()78     public void getSimCardInformation() throws Exception {
79         // Context of the app under test.
80         Context context = InstrumentationRegistry.getTargetContext();
81         Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
82 
83         TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
84         assertNotNull(tm);
85 
86         Bundle returnBundle = new Bundle();
87         // Sim card - SIM_STATE_READY 5
88         int state = tm.getSimState();
89         returnBundle.putInt(SIM_STATE, state);
90 
91         // UICC check
92         boolean carrierPrivileges = tm.hasCarrierPrivileges();
93         returnBundle.putBoolean(CARRIER_PRIVILEGES, carrierPrivileges);
94 
95         // Secured element check
96         if (waitForConnection()) {
97             Reader[] readers = mSeService.getReaders();
98             for (Reader reader : readers) {
99                 returnBundle.putBoolean(SECURED_ELEMENT, reader.isSecureElementPresent());
100                 returnBundle.putBoolean(SE_SERVICE, reader.getSEService() != null ? true : false);
101             }
102         } else {
103             returnBundle.putBoolean(SECURED_ELEMENT, false);
104             returnBundle.putBoolean(SE_SERVICE, false);
105         }
106         SendToInstrumentation.sendBundle(instrumentation, returnBundle);
107     }
108 
109     @After
tearDown()110     public void tearDown() throws Exception {
111         if (mSeService != null && mSeService.isConnected()) {
112             mSeService.shutdown();
113             mConnected = false;
114         }
115     }
116 
waitForConnection()117     private boolean waitForConnection() {
118         synchronized (mServiceMutex) {
119             if (!mConnected) {
120                 try {
121                     mServiceMutex.wait();
122                 } catch (InterruptedException e) {
123                     return false;
124                 }
125             }
126             if (!mConnected) {
127                 return false;
128             }
129             if (mConnectionTimer != null) {
130                 mConnectionTimer.cancel();
131             }
132             return true;
133         }
134     }
135 
136     private class SynchronousExecutor implements Executor {
137         @Override
execute(Runnable r)138         public void execute(Runnable r) {
139             r.run();
140         }
141     }
142 
143     private class ServiceConnectionTimerTask extends TimerTask {
144         @Override
run()145         public void run() {
146             synchronized (mServiceMutex) {
147                 mServiceMutex.notifyAll();
148             }
149         }
150     }
151 }
152