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