1 /*
2  * Copyright (C) 2021 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 package android.telephony.mockmodem;
18 
19 import android.app.Instrumentation;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.ServiceConnection;
24 import android.os.IBinder;
25 import android.telephony.cts.util.TelephonyUtils;
26 import android.text.TextUtils;
27 import android.util.Log;
28 
29 import java.util.concurrent.CountDownLatch;
30 import java.util.concurrent.TimeUnit;
31 
32 /** Connects Telephony Framework to MockModemService. */
33 class MockModemServiceConnector {
34 
35     private static final String TAG = "MockModemServiceConnector";
36 
37     private static final String COMMAND_BASE = "cmd phone ";
38     private static final String COMMAND_SET_MODEM_SERVICE = "radio set-modem-service ";
39     private static final String COMMAND_GET_MODEM_SERVICE = "radio get-modem-service ";
40     private static final String COMMAND_SERVICE_IDENTIFIER = "-s ";
41     private static final String COMMAND_MODEM_SERVICE_UNKNOWN = "unknown";
42     private static final String COMMAND_MODEM_SERVICE_DEFAULT = "default";
43 
44     private static final int BIND_LOCAL_MOCKMODEM_SERVICE_TIMEOUT_MS = 25000;
45     private static final int BIND_RADIO_INTERFACE_READY_TIMEOUT_MS = 5000;
46 
47     private class MockModemServiceConnection implements ServiceConnection {
48 
49         private final CountDownLatch mLatch;
50 
MockModemServiceConnection(CountDownLatch latch)51         MockModemServiceConnection(CountDownLatch latch) {
52             mLatch = latch;
53         }
54 
55         @Override
onServiceConnected(ComponentName name, IBinder service)56         public void onServiceConnected(ComponentName name, IBinder service) {
57             String serviceName;
58             mMockModemService = ((MockModemService.LocalBinder) service).getService();
59             serviceName = name.getPackageName() + "/" + name.getClassName();
60             updateModemServiceName(serviceName);
61             mLatch.countDown();
62             Log.d(TAG, "MockModemServiceConnection - " + serviceName + " onServiceConnected");
63         }
64 
65         @Override
onServiceDisconnected(ComponentName name)66         public void onServiceDisconnected(ComponentName name) {
67             mMockModemService = null;
68             Log.d(TAG, "MockModemServiceConnection - onServiceDisconnected");
69         }
70     }
71 
72     private Instrumentation mInstrumentation;
73 
74     private MockModemService mMockModemService;
75     private MockModemServiceConnection mMockModemServiceConn;
76     private boolean mIsServiceOverridden;
77     private String mModemServiceName;
78 
MockModemServiceConnector(Instrumentation instrumentation)79     MockModemServiceConnector(Instrumentation instrumentation) {
80         mInstrumentation = instrumentation;
81     }
82 
setupLocalMockModemService()83     private boolean setupLocalMockModemService() {
84         Log.d(TAG, "setupLocalMockModemService");
85         if (mMockModemService != null) {
86             return true;
87         }
88 
89         CountDownLatch latch = new CountDownLatch(1);
90         if (mMockModemServiceConn == null) {
91             mMockModemServiceConn = new MockModemServiceConnection(latch);
92         }
93 
94         mInstrumentation
95                 .getContext()
96                 .bindService(
97                         new Intent(mInstrumentation.getContext(), MockModemService.class),
98                         mMockModemServiceConn,
99                         Context.BIND_AUTO_CREATE);
100         try {
101             return latch.await(BIND_LOCAL_MOCKMODEM_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
102         } catch (InterruptedException e) {
103             return false;
104         }
105     }
106 
constructSetModemServiceOverrideCommand()107     private String constructSetModemServiceOverrideCommand() {
108         return COMMAND_BASE
109                 + COMMAND_SET_MODEM_SERVICE
110                 + COMMAND_SERVICE_IDENTIFIER
111                 + mModemServiceName;
112     }
113 
constructGetModemServiceCommand()114     private String constructGetModemServiceCommand() {
115         return COMMAND_BASE + COMMAND_GET_MODEM_SERVICE;
116     }
117 
constructClearModemServiceOverrideCommand()118     private String constructClearModemServiceOverrideCommand() {
119         return COMMAND_BASE + COMMAND_SET_MODEM_SERVICE;
120     }
121 
setModemService()122     private boolean setModemService() throws Exception {
123         String result =
124                 TelephonyUtils.executeShellCommand(
125                         mInstrumentation, constructSetModemServiceOverrideCommand());
126         Log.d(TAG, "setModemService result: " + result);
127         return "true".equals(result);
128     }
129 
getModemService()130     private String getModemService() throws Exception {
131         String result =
132                 TelephonyUtils.executeShellCommand(
133                         mInstrumentation, constructGetModemServiceCommand());
134         Log.d(TAG, "getModemService result: " + result);
135         return result;
136     }
137 
clearModemServiceOverride()138     private boolean clearModemServiceOverride() throws Exception {
139         String result =
140                 TelephonyUtils.executeShellCommand(
141                         mInstrumentation, constructClearModemServiceOverrideCommand());
142         Log.d(TAG, "clearModemServiceOverride result: " + result);
143         return "true".equals(result);
144     }
145 
isServiceTheSame(String serviceA, String serviceB)146     private boolean isServiceTheSame(String serviceA, String serviceB) {
147         if (TextUtils.isEmpty(serviceA) && TextUtils.isEmpty(serviceB)) {
148             return true;
149         }
150         return TextUtils.equals(serviceA, serviceB);
151     }
152 
updateModemServiceName(String serviceName)153     private void updateModemServiceName(String serviceName) {
154         mModemServiceName = serviceName;
155     }
156 
overrideModemService()157     private boolean overrideModemService() throws Exception {
158         boolean result = setModemService();
159 
160         if (result) mIsServiceOverridden = true;
161 
162         return result;
163     }
164 
165     /**
166      * Bind to the local implementation of MockModemService.
167      *
168      * @return true if this request succeeded, false otherwise.
169      */
connectMockModemServiceLocally()170     boolean connectMockModemServiceLocally() {
171         if (!setupLocalMockModemService()) {
172             Log.w(TAG, "connectMockModemService: couldn't set up service.");
173             return false;
174         }
175         return true;
176     }
177 
178     /**
179      * Trigger the telephony framework to bind to the local MockModemService implementation.
180      *
181      * @return true if this request succeeded, false otherwise.
182      */
switchFrameworkConnectionToMockModemService(int[] simprofiles)183     boolean switchFrameworkConnectionToMockModemService(int[] simprofiles) throws Exception {
184         boolean isComplete = false;
185 
186         if (overrideModemService()) {
187             isComplete =
188                     mMockModemService.waitForLatchCountdown(
189                             MockModemService.LATCH_RADIO_INTERFACES_READY,
190                             BIND_RADIO_INTERFACE_READY_TIMEOUT_MS);
191 
192             if (isComplete) {
193                 isComplete = mMockModemService.initialize(simprofiles);
194             } else {
195                 Log.e(TAG, "Timeout: Wait for radio proxy clients to bind failed!");
196             }
197         }
198 
199         return isComplete;
200     }
201 
checkDefaultModemServiceConnected(String serviceName)202     boolean checkDefaultModemServiceConnected(String serviceName) throws Exception {
203         return isServiceTheSame(COMMAND_MODEM_SERVICE_DEFAULT, serviceName);
204     }
205 
checkModemServiceOverridden(String serviceName)206     boolean checkModemServiceOverridden(String serviceName) throws Exception {
207         return isServiceTheSame(mModemServiceName, serviceName);
208     }
209 
connectMockModemService(int[] simprofiles)210     boolean connectMockModemService(int[] simprofiles) throws Exception {
211         boolean result = false;
212         if (!connectMockModemServiceLocally()) return false;
213 
214         result = checkModemServiceOverridden(getModemService());
215         if (result) mIsServiceOverridden = true;
216         else result = switchFrameworkConnectionToMockModemService(simprofiles);
217 
218         return result;
219     }
220 
triggerFrameworkDisconnectionFromMockModemService()221     boolean triggerFrameworkDisconnectionFromMockModemService() throws Exception {
222         boolean result = false;
223         if (!mIsServiceOverridden) {
224             Log.d(TAG, "Service didn't override.");
225             return true;
226         }
227 
228         result = clearModemServiceOverride();
229         if (result) mIsServiceOverridden = false;
230 
231         return result;
232     }
233 
disconnectMockModemService()234     boolean disconnectMockModemService() throws Exception {
235         boolean isComplete;
236         isComplete = triggerFrameworkDisconnectionFromMockModemService();
237 
238         if (isComplete) {
239             // waiting for binding to default modem service
240             TimeUnit.SECONDS.sleep(5);
241             String serviceName = getModemService();
242             isComplete =
243                     (!checkModemServiceOverridden(serviceName)
244                             && checkDefaultModemServiceConnected(serviceName));
245         }
246 
247         // Remove local connection
248         Log.d(TAG, "disconnectMockModemService");
249         if (mMockModemServiceConn != null) {
250             mInstrumentation.getContext().unbindService(mMockModemServiceConn);
251             mMockModemService = null;
252             mMockModemServiceConn = null;
253         }
254 
255         return isComplete;
256     }
257 
getMockModemService()258     MockModemService getMockModemService() {
259         return mMockModemService;
260     }
261 }
262