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