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