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