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