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 package com.android.internal.telephony.util;
17 
18 import static android.telephony.Annotation.DataState;
19 
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.ActivityManager;
23 import android.app.role.RoleManager;
24 import android.content.Context;
25 import android.content.pm.ComponentInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.os.Binder;
29 import android.os.Bundle;
30 import android.os.PersistableBundle;
31 import android.os.RemoteException;
32 import android.os.SystemProperties;
33 import android.os.UserHandle;
34 import android.os.UserManager;
35 import android.telephony.SubscriptionManager;
36 import android.telephony.TelephonyFrameworkInitializer;
37 import android.telephony.TelephonyManager;
38 import android.util.Log;
39 
40 import com.android.internal.telephony.ITelephony;
41 
42 import java.io.PrintWriter;
43 import java.util.Collections;
44 import java.util.List;
45 import java.util.concurrent.CountDownLatch;
46 import java.util.concurrent.Executor;
47 import java.util.concurrent.TimeUnit;
48 import java.util.function.Supplier;
49 
50 /**
51  * This class provides various util functions
52  */
53 public final class TelephonyUtils {
54     private static final String LOG_TAG = "TelephonyUtils";
55 
56     public static boolean IS_USER = "user".equals(android.os.Build.TYPE);
57     public static boolean IS_DEBUGGABLE = SystemProperties.getInt("ro.debuggable", 0) == 1;
58 
59     public static final Executor DIRECT_EXECUTOR = Runnable::run;
60 
61     /**
62      * Verify that caller holds {@link android.Manifest.permission#DUMP}.
63      *
64      * @return true if access should be granted.
65      */
checkDumpPermission(Context context, String tag, PrintWriter pw)66     public static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
67         if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
68                 != PackageManager.PERMISSION_GRANTED) {
69             pw.println("Permission Denial: can't dump " + tag + " from from pid="
70                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
71                     + " due to missing android.permission.DUMP permission");
72             return false;
73         } else {
74             return true;
75         }
76     }
77 
78     /** Returns an empty string if the input is {@code null}. */
emptyIfNull(@ullable String str)79     public static String emptyIfNull(@Nullable String str) {
80         return str == null ? "" : str;
81     }
82 
83     /** Returns an empty list if the input is {@code null}. */
emptyIfNull(@ullable List<T> cur)84     public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) {
85         return cur == null ? Collections.emptyList() : cur;
86     }
87 
88     /**
89      * Returns a {@link ComponentInfo} from the {@link ResolveInfo},
90      * or throws an {@link IllegalStateException} if not available.
91      */
getComponentInfo(@onNull ResolveInfo resolveInfo)92     public static ComponentInfo getComponentInfo(@NonNull ResolveInfo resolveInfo) {
93         if (resolveInfo.activityInfo != null) return resolveInfo.activityInfo;
94         if (resolveInfo.serviceInfo != null) return resolveInfo.serviceInfo;
95         if (resolveInfo.providerInfo != null) return resolveInfo.providerInfo;
96         throw new IllegalStateException("Missing ComponentInfo!");
97     }
98 
99     /**
100      * Convenience method for running the provided action enclosed in
101      * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity}
102      *
103      * Any exception thrown by the given action will need to be handled by caller.
104      *
105      */
runWithCleanCallingIdentity( @onNull Runnable action)106     public static void runWithCleanCallingIdentity(
107             @NonNull Runnable action) {
108         final long callingIdentity = Binder.clearCallingIdentity();
109         try {
110             action.run();
111         } finally {
112             Binder.restoreCallingIdentity(callingIdentity);
113         }
114     }
115 
116     /**
117      * Convenience method for running the provided action in the provided
118      * executor enclosed in
119      * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity}
120      *
121      * Any exception thrown by the given action will need to be handled by caller.
122      *
123      */
runWithCleanCallingIdentity( @onNull Runnable action, @NonNull Executor executor)124     public static void runWithCleanCallingIdentity(
125             @NonNull Runnable action, @NonNull Executor executor) {
126         if (action != null) {
127             if (executor != null) {
128                 executor.execute(() -> runWithCleanCallingIdentity(action));
129             } else {
130                 runWithCleanCallingIdentity(action);
131             }
132         }
133     }
134 
135 
136     /**
137      * Convenience method for running the provided action enclosed in
138      * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity} and return
139      * the result.
140      *
141      * Any exception thrown by the given action will need to be handled by caller.
142      *
143      */
runWithCleanCallingIdentity( @onNull Supplier<T> action)144     public static <T> T runWithCleanCallingIdentity(
145             @NonNull Supplier<T> action) {
146         final long callingIdentity = Binder.clearCallingIdentity();
147         try {
148             return action.get();
149         } finally {
150             Binder.restoreCallingIdentity(callingIdentity);
151         }
152     }
153 
154     /**
155      * Filter values in bundle to only basic types.
156      */
filterValues(Bundle bundle)157     public static Bundle filterValues(Bundle bundle) {
158         Bundle ret = new Bundle(bundle);
159         for (String key : bundle.keySet()) {
160             Object value = bundle.get(key);
161             if ((value instanceof Integer) || (value instanceof Long)
162                     || (value instanceof Double) || (value instanceof String)
163                     || (value instanceof int[]) || (value instanceof long[])
164                     || (value instanceof double[]) || (value instanceof String[])
165                     || (value instanceof PersistableBundle) || (value == null)
166                     || (value instanceof Boolean) || (value instanceof boolean[])) {
167                 continue;
168             }
169             if (value instanceof Bundle) {
170                 ret.putBundle(key, filterValues((Bundle) value));
171                 continue;
172             }
173             if (value.getClass().getName().startsWith("android.")) {
174                 continue;
175             }
176             ret.remove(key);
177         }
178         return ret;
179     }
180 
181     /** Wait for latch to trigger */
waitUntilReady(CountDownLatch latch, long timeoutMs)182     public static void waitUntilReady(CountDownLatch latch, long timeoutMs) {
183         try {
184             latch.await(timeoutMs, TimeUnit.MILLISECONDS);
185         } catch (InterruptedException ignored) {
186         }
187     }
188 
189     /**
190      * Convert data state to string
191      *
192      * @return The data state in string format.
193      */
dataStateToString(@ataState int state)194     public static String dataStateToString(@DataState int state) {
195         switch (state) {
196             case TelephonyManager.DATA_DISCONNECTED: return "DISCONNECTED";
197             case TelephonyManager.DATA_CONNECTING: return "CONNECTING";
198             case TelephonyManager.DATA_CONNECTED: return "CONNECTED";
199             case TelephonyManager.DATA_SUSPENDED: return "SUSPENDED";
200             case TelephonyManager.DATA_DISCONNECTING: return "DISCONNECTING";
201             case TelephonyManager.DATA_HANDOVER_IN_PROGRESS: return "HANDOVERINPROGRESS";
202             case TelephonyManager.DATA_UNKNOWN: return "UNKNOWN";
203         }
204         // This is the error case. The well-defined value for UNKNOWN is -1.
205         return "UNKNOWN(" + state + ")";
206     }
207 
208     /**
209      * Convert mobile data policy to string.
210      *
211      * @param mobileDataPolicy The mobile data policy.
212      * @return The mobile data policy in string format.
213      */
mobileDataPolicyToString( @elephonyManager.MobileDataPolicy int mobileDataPolicy)214     public static @NonNull String mobileDataPolicyToString(
215             @TelephonyManager.MobileDataPolicy int mobileDataPolicy) {
216         switch (mobileDataPolicy) {
217             case TelephonyManager.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL:
218                 return "DATA_ON_NON_DEFAULT_DURING_VOICE_CALL";
219             case TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED:
220                 return "MMS_ALWAYS_ALLOWED";
221             case TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH:
222                 return "AUTO_DATA_SWITCH";
223             default:
224                 return "UNKNOWN(" + mobileDataPolicy + ")";
225         }
226     }
227 
228     /**
229      * Utility method to get user handle associated with this subscription.
230      *
231      * This method should be used internally as it returns null instead of throwing
232      * IllegalArgumentException or IllegalStateException.
233      *
234      * @param context Context object
235      * @param subId the subId of the subscription.
236      * @return userHandle associated with this subscription
237      * or {@code null} if:
238      * 1. subscription is not associated with any user
239      * 2. subId is invalid.
240      * 3. subscription service is not available.
241      *
242      * @throws SecurityException if the caller doesn't have permissions required.
243      */
244     @Nullable
getSubscriptionUserHandle(Context context, int subId)245     public static UserHandle getSubscriptionUserHandle(Context context, int subId) {
246         UserHandle userHandle = null;
247         SubscriptionManager subManager =  context.getSystemService(SubscriptionManager.class);
248         if ((subManager != null) && (SubscriptionManager.isValidSubscriptionId(subId))) {
249             userHandle = subManager.getSubscriptionUserHandle(subId);
250         }
251         return userHandle;
252     }
253 
254     /**
255      * Show switch to managed profile dialog if subscription is associated with managed profile.
256      *
257      * @param context Context object
258      * @param subId subscription id
259      * @param callingUid uid for the calling app
260      * @param callingPackage package name of the calling app
261      */
showSwitchToManagedProfileDialogIfAppropriate(Context context, int subId, int callingUid, String callingPackage)262     public static void showSwitchToManagedProfileDialogIfAppropriate(Context context,
263             int subId, int callingUid, String callingPackage) {
264         final long token = Binder.clearCallingIdentity();
265         try {
266             UserHandle callingUserHandle = UserHandle.getUserHandleForUid(callingUid);
267             // We only want to show this dialog, while user actually trying to send the message from
268             // a messaging app, in other cases this dialog don't make sense.
269             if (!TelephonyUtils.isUidForeground(context, callingUid)
270                     || !TelephonyUtils.isPackageSMSRoleHolderForUser(context, callingPackage,
271                     callingUserHandle)) {
272                 return;
273             }
274 
275             SubscriptionManager subscriptionManager = context.getSystemService(
276                     SubscriptionManager.class);
277             UserHandle associatedUserHandle = subscriptionManager.getSubscriptionUserHandle(subId);
278             UserManager um = context.getSystemService(UserManager.class);
279 
280             if (associatedUserHandle != null && um.isManagedProfile(
281                     associatedUserHandle.getIdentifier())) {
282 
283                 ITelephony iTelephony = ITelephony.Stub.asInterface(
284                         TelephonyFrameworkInitializer
285                                 .getTelephonyServiceManager()
286                                 .getTelephonyServiceRegisterer()
287                                 .get());
288                 if (iTelephony != null) {
289                     try {
290                         iTelephony.showSwitchToManagedProfileDialog();
291                     } catch (RemoteException e) {
292                         Log.e(LOG_TAG, "Failed to launch switch to managed profile dialog.");
293                     }
294                 }
295             }
296         } finally {
297             Binder.restoreCallingIdentity(token);
298         }
299     }
300 
isUidForeground(Context context, int uid)301     private static boolean isUidForeground(Context context, int uid) {
302         ActivityManager am = context.getSystemService(ActivityManager.class);
303         boolean result = am != null && am.getUidImportance(uid)
304                 == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
305         return result;
306     }
307 
isPackageSMSRoleHolderForUser(Context context, String callingPackage, UserHandle user)308     private static boolean isPackageSMSRoleHolderForUser(Context context, String callingPackage,
309             UserHandle user) {
310         RoleManager roleManager = context.getSystemService(RoleManager.class);
311         final List<String> smsRoleHolder = roleManager.getRoleHoldersAsUser(
312                 RoleManager.ROLE_SMS, user);
313 
314         // ROLE_SMS is an exclusive role per user, so there would just be one entry in the
315         // retuned list if not empty
316         if (!smsRoleHolder.isEmpty() && callingPackage.equals(smsRoleHolder.get(0))) {
317             return true;
318         }
319         return false;
320 
321     }
322 }