1 /* 2 * Copyright (C) 2019 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.ims.cts; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.pm.PackageManager; 22 import android.content.pm.ResolveInfo; 23 import android.os.Binder; 24 import android.service.carrier.CarrierService; 25 import android.telephony.SubscriptionInfo; 26 import android.telephony.SubscriptionManager; 27 import android.telephony.TelephonyManager; 28 import android.util.Log; 29 30 import androidx.test.platform.app.InstrumentationRegistry; 31 32 import com.android.compatibility.common.util.ShellIdentityUtils; 33 34 import java.io.ByteArrayInputStream; 35 import java.io.ByteArrayOutputStream; 36 import java.io.IOException; 37 import java.util.List; 38 import java.util.concurrent.Callable; 39 import java.util.concurrent.TimeUnit; 40 import java.util.zip.GZIPInputStream; 41 import java.util.zip.GZIPOutputStream; 42 43 public class ImsUtils { 44 public static final boolean VDBG = true; 45 46 // ImsService rebind has an exponential backoff capping at 64 seconds. Wait for 70 seconds to 47 // allow for the new poll to happen in the framework. 48 public static final int TEST_TIMEOUT_MS = 70000; 49 50 // Id for non compressed auto configuration xml. 51 public static final int ITEM_NON_COMPRESSED = 2000; 52 // Id for compressed auto configuration xml. 53 public static final int ITEM_COMPRESSED = 2001; 54 55 private static final String TAG = "ImsUtils"; 56 shouldTestTelephony()57 public static boolean shouldTestTelephony() { 58 try { 59 InstrumentationRegistry.getInstrumentation().getContext() 60 .getSystemService(TelephonyManager.class) 61 .getHalVersion(TelephonyManager.HAL_SERVICE_RADIO); 62 } catch (IllegalStateException e) { 63 return false; 64 } 65 final PackageManager pm = InstrumentationRegistry.getInstrumentation().getContext() 66 .getPackageManager(); 67 return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY); 68 } 69 shouldTestImsService()70 public static boolean shouldTestImsService() { 71 boolean hasIms = InstrumentationRegistry.getInstrumentation().getContext() 72 .getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS); 73 return shouldTestTelephony() && hasIms; 74 } 75 shouldTestImsCall()76 public static boolean shouldTestImsCall() { 77 final PackageManager pm = InstrumentationRegistry.getInstrumentation().getContext() 78 .getPackageManager(); 79 return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS) 80 && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING); 81 } 82 shouldTestImsSingleRegistration()83 public static boolean shouldTestImsSingleRegistration() { 84 boolean hasSingleReg = InstrumentationRegistry.getInstrumentation().getContext() 85 .getPackageManager().hasSystemFeature( 86 PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION); 87 return shouldTestTelephony() && hasSingleReg; 88 } 89 getPreferredActiveSubId()90 public static int getPreferredActiveSubId() { 91 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 92 SubscriptionManager sm = (SubscriptionManager) context.getSystemService( 93 Context.TELEPHONY_SUBSCRIPTION_SERVICE); 94 List<SubscriptionInfo> infos = ShellIdentityUtils.invokeMethodWithShellPermissions(sm, 95 SubscriptionManager::getActiveSubscriptionInfoList); 96 97 int defaultSubId = SubscriptionManager.getDefaultVoiceSubscriptionId(); 98 if (defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID 99 && isSubIdInInfoList(infos, defaultSubId)) { 100 return defaultSubId; 101 } 102 103 defaultSubId = SubscriptionManager.getDefaultSubscriptionId(); 104 if (defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID 105 && isSubIdInInfoList(infos, defaultSubId)) { 106 return defaultSubId; 107 } 108 109 // Couldn't resolve a default. We can try to resolve a default using the active 110 // subscriptions. 111 if (!infos.isEmpty()) { 112 return infos.get(0).getSubscriptionId(); 113 } 114 // There must be at least one active subscription. 115 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 116 } 117 isSubIdInInfoList(List<SubscriptionInfo> infos, int subId)118 private static boolean isSubIdInInfoList(List<SubscriptionInfo> infos, int subId) { 119 return infos.stream().anyMatch(info -> info.getSubscriptionId() == subId); 120 } 121 122 /** 123 * If a carrier app implements CarrierMessagingService it can choose to take care of handling 124 * SMS OTT so SMS over IMS APIs won't be triggered which would be WAI so we do not run the tests 125 * if there exist a carrier app that declares a CarrierMessagingService 126 */ shouldRunSmsImsTests(int subId)127 public static boolean shouldRunSmsImsTests(int subId) { 128 if (!shouldTestImsService()) { 129 return false; 130 } 131 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 132 TelephonyManager tm = 133 (TelephonyManager) InstrumentationRegistry.getInstrumentation().getContext() 134 .getSystemService(Context.TELEPHONY_SERVICE); 135 tm = tm.createForSubscriptionId(subId); 136 final long token = Binder.clearCallingIdentity(); 137 List<String> carrierPackages; 138 try { 139 carrierPackages = ShellIdentityUtils.invokeMethodWithShellPermissions(tm, 140 (m) -> m.getCarrierPackageNamesForIntent( 141 new Intent(CarrierService.CARRIER_SERVICE_INTERFACE))); 142 } finally { 143 Binder.restoreCallingIdentity(token); 144 } 145 146 final PackageManager packageManager = context.getPackageManager(); 147 Intent intent = new Intent("android.service.carrier.CarrierMessagingService"); 148 List<ResolveInfo> resolveInfos = packageManager.queryIntentServices(intent, 0); 149 boolean detected = resolveInfos != null && !resolveInfos.isEmpty(); 150 Log.i(TAG, "resolveInfos are detected: " + detected); 151 152 boolean exist = carrierPackages != null && !carrierPackages.isEmpty(); 153 Log.i(TAG, "carrierPackages exist: " + exist); 154 155 if (!exist) { 156 return true; 157 } 158 159 for (ResolveInfo info : resolveInfos) { 160 if (carrierPackages.contains(info.serviceInfo.packageName)) { 161 return false; 162 } 163 } 164 165 return true; 166 } 167 168 /** 169 * Retry every 5 seconds until the condition is true or fail after TEST_TIMEOUT_MS seconds. 170 */ retryUntilTrue(Callable<Boolean> condition)171 public static boolean retryUntilTrue(Callable<Boolean> condition) throws Exception { 172 return retryUntilTrue(condition, TEST_TIMEOUT_MS, 14 /*numTries*/); 173 } 174 175 /** 176 * Retry every timeoutMs/numTimes until the condition is true or fail if the condition is never 177 * met. 178 */ retryUntilTrue(Callable<Boolean> condition, int timeoutMs, int numTimes)179 public static boolean retryUntilTrue(Callable<Boolean> condition, 180 int timeoutMs, int numTimes) throws Exception { 181 int sleepTime = timeoutMs / numTimes; 182 int retryCounter = 0; 183 while (retryCounter < numTimes) { 184 try { 185 Boolean isSuccessful = condition.call(); 186 isSuccessful = (isSuccessful == null) ? false : isSuccessful; 187 if (isSuccessful) return true; 188 } catch (Exception e) { 189 // we will retry 190 } 191 Thread.sleep(sleepTime); 192 retryCounter++; 193 } 194 return false; 195 } 196 197 /** 198 * compress the gzip format data 199 * @hide 200 */ compressGzip(byte[] data)201 public static byte[] compressGzip(byte[] data) { 202 if (data == null || data.length == 0) { 203 return data; 204 } 205 byte[] out = null; 206 try { 207 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); 208 GZIPOutputStream gzipCompressingStream = 209 new GZIPOutputStream(outputStream); 210 gzipCompressingStream.write(data); 211 gzipCompressingStream.close(); 212 out = outputStream.toByteArray(); 213 outputStream.close(); 214 } catch (IOException e) { 215 } 216 return out; 217 } 218 219 /** 220 * decompress the gzip format data 221 * @hide 222 */ decompressGzip(byte[] data)223 public static byte[] decompressGzip(byte[] data) { 224 if (data == null || data.length == 0) { 225 return data; 226 } 227 byte[] out = null; 228 try { 229 ByteArrayInputStream inputStream = new ByteArrayInputStream(data); 230 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 231 GZIPInputStream gzipDecompressingStream = 232 new GZIPInputStream(inputStream); 233 byte[] buf = new byte[1024]; 234 int size = gzipDecompressingStream.read(buf); 235 while (size >= 0) { 236 outputStream.write(buf, 0, size); 237 size = gzipDecompressingStream.read(buf); 238 } 239 gzipDecompressingStream.close(); 240 inputStream.close(); 241 out = outputStream.toByteArray(); 242 outputStream.close(); 243 } catch (IOException e) { 244 } 245 return out; 246 } 247 waitInCurrentState(long ms)248 public static void waitInCurrentState(long ms) { 249 try { 250 TimeUnit.MILLISECONDS.sleep(ms); 251 } catch (Exception e) { 252 Log.d(TAG, "InterruptedException"); 253 } 254 } 255 } 256