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.content.Context;
23 import android.content.pm.ComponentInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.ResolveInfo;
26 import android.os.Binder;
27 import android.os.Bundle;
28 import android.os.PersistableBundle;
29 import android.os.RemoteException;
30 import android.os.SystemProperties;
31 import android.telephony.TelephonyManager;
32 
33 import java.io.PrintWriter;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.concurrent.CountDownLatch;
37 import java.util.concurrent.Executor;
38 import java.util.concurrent.TimeUnit;
39 import java.util.function.Supplier;
40 
41 /**
42  * This class provides various util functions
43  */
44 public final class TelephonyUtils {
45     public static boolean IS_USER = "user".equals(android.os.Build.TYPE);
46     public static boolean IS_DEBUGGABLE = SystemProperties.getInt("ro.debuggable", 0) == 1;
47 
48     public static final Executor DIRECT_EXECUTOR = Runnable::run;
49 
50     /**
51      * Verify that caller holds {@link android.Manifest.permission#DUMP}.
52      *
53      * @return true if access should be granted.
54      */
checkDumpPermission(Context context, String tag, PrintWriter pw)55     public static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
56         if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
57                 != PackageManager.PERMISSION_GRANTED) {
58             pw.println("Permission Denial: can't dump " + tag + " from from pid="
59                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
60                     + " due to missing android.permission.DUMP permission");
61             return false;
62         } else {
63             return true;
64         }
65     }
66 
67     /** Returns an empty string if the input is {@code null}. */
emptyIfNull(@ullable String str)68     public static String emptyIfNull(@Nullable String str) {
69         return str == null ? "" : str;
70     }
71 
72     /** Returns an empty list if the input is {@code null}. */
emptyIfNull(@ullable List<T> cur)73     public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) {
74         return cur == null ? Collections.emptyList() : cur;
75     }
76 
77     /** Throws a {@link RuntimeException} that wrapps the {@link RemoteException}. */
rethrowAsRuntimeException(RemoteException remoteException)78     public static RuntimeException rethrowAsRuntimeException(RemoteException remoteException) {
79         throw new RuntimeException(remoteException);
80     }
81 
82     /**
83      * Returns a {@link ComponentInfo} from the {@link ResolveInfo},
84      * or throws an {@link IllegalStateException} if not available.
85      */
getComponentInfo(@onNull ResolveInfo resolveInfo)86     public static ComponentInfo getComponentInfo(@NonNull ResolveInfo resolveInfo) {
87         if (resolveInfo.activityInfo != null) return resolveInfo.activityInfo;
88         if (resolveInfo.serviceInfo != null) return resolveInfo.serviceInfo;
89         if (resolveInfo.providerInfo != null) return resolveInfo.providerInfo;
90         throw new IllegalStateException("Missing ComponentInfo!");
91     }
92 
93     /**
94      * Convenience method for running the provided action enclosed in
95      * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity}
96      *
97      * Any exception thrown by the given action will need to be handled by caller.
98      *
99      */
runWithCleanCallingIdentity( @onNull Runnable action)100     public static void runWithCleanCallingIdentity(
101             @NonNull Runnable action) {
102         final long callingIdentity = Binder.clearCallingIdentity();
103         try {
104             action.run();
105         } finally {
106             Binder.restoreCallingIdentity(callingIdentity);
107         }
108     }
109 
110 
111     /**
112      * Convenience method for running the provided action enclosed in
113      * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity} and return
114      * the result.
115      *
116      * Any exception thrown by the given action will need to be handled by caller.
117      *
118      */
runWithCleanCallingIdentity( @onNull Supplier<T> action)119     public static <T> T runWithCleanCallingIdentity(
120             @NonNull Supplier<T> action) {
121         final long callingIdentity = Binder.clearCallingIdentity();
122         try {
123             return action.get();
124         } finally {
125             Binder.restoreCallingIdentity(callingIdentity);
126         }
127     }
128 
129     /**
130      * Filter values in bundle to only basic types.
131      */
filterValues(Bundle bundle)132     public static Bundle filterValues(Bundle bundle) {
133         Bundle ret = new Bundle(bundle);
134         for (String key : bundle.keySet()) {
135             Object value = bundle.get(key);
136             if ((value instanceof Integer) || (value instanceof Long)
137                     || (value instanceof Double) || (value instanceof String)
138                     || (value instanceof int[]) || (value instanceof long[])
139                     || (value instanceof double[]) || (value instanceof String[])
140                     || (value instanceof PersistableBundle) || (value == null)
141                     || (value instanceof Boolean) || (value instanceof boolean[])) {
142                 continue;
143             }
144             if (value instanceof Bundle) {
145                 ret.putBundle(key, filterValues((Bundle) value));
146                 continue;
147             }
148             if (value.getClass().getName().startsWith("android.")) {
149                 continue;
150             }
151             ret.remove(key);
152         }
153         return ret;
154     }
155 
156     /** Wait for latch to trigger */
waitUntilReady(CountDownLatch latch, long timeoutMs)157     public static void waitUntilReady(CountDownLatch latch, long timeoutMs) {
158         try {
159             latch.await(timeoutMs, TimeUnit.MILLISECONDS);
160         } catch (InterruptedException ignored) {
161         }
162     }
163 
164     /**
165      * Convert data state to string
166      *
167      * @return The data state in string format.
168      */
dataStateToString(@ataState int state)169     public static String dataStateToString(@DataState int state) {
170         switch (state) {
171             case TelephonyManager.DATA_DISCONNECTED: return "DISCONNECTED";
172             case TelephonyManager.DATA_CONNECTING: return "CONNECTING";
173             case TelephonyManager.DATA_CONNECTED: return "CONNECTED";
174             case TelephonyManager.DATA_SUSPENDED: return "SUSPENDED";
175             case TelephonyManager.DATA_DISCONNECTING: return "DISCONNECTING";
176             case TelephonyManager.DATA_UNKNOWN: return "UNKNOWN";
177         }
178         // This is the error case. The well-defined value for UNKNOWN is -1.
179         return "UNKNOWN(" + state + ")";
180     }
181 }
182