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