1 /* 2 * Copyright (C) 2015 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.contacts.common.compat; 17 18 import android.os.Build; 19 import android.os.Build.VERSION; 20 import android.support.annotation.Nullable; 21 import android.support.v4.os.BuildCompat; 22 import android.text.TextUtils; 23 import android.util.Log; 24 25 import com.android.contacts.common.model.CPOWrapper; 26 27 import java.lang.reflect.InvocationTargetException; 28 import java.lang.reflect.Method; 29 30 public final class CompatUtils { 31 32 private static final String TAG = CompatUtils.class.getSimpleName(); 33 34 /** 35 * These 4 variables are copied from ContentProviderOperation for compatibility. 36 */ 37 public final static int TYPE_INSERT = 1; 38 39 public final static int TYPE_UPDATE = 2; 40 41 public final static int TYPE_DELETE = 3; 42 43 public final static int TYPE_ASSERT = 4; 44 45 /** 46 * Returns whether the operation in CPOWrapper is of TYPE_INSERT; 47 */ isInsertCompat(CPOWrapper cpoWrapper)48 public static boolean isInsertCompat(CPOWrapper cpoWrapper) { 49 if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) { 50 return cpoWrapper.getOperation().isInsert(); 51 } 52 return (cpoWrapper.getType() == TYPE_INSERT); 53 } 54 55 /** 56 * Returns whether the operation in CPOWrapper is of TYPE_UPDATE; 57 */ isUpdateCompat(CPOWrapper cpoWrapper)58 public static boolean isUpdateCompat(CPOWrapper cpoWrapper) { 59 if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) { 60 return cpoWrapper.getOperation().isUpdate(); 61 } 62 return (cpoWrapper.getType() == TYPE_UPDATE); 63 } 64 65 /** 66 * Returns whether the operation in CPOWrapper is of TYPE_DELETE; 67 */ isDeleteCompat(CPOWrapper cpoWrapper)68 public static boolean isDeleteCompat(CPOWrapper cpoWrapper) { 69 if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) { 70 return cpoWrapper.getOperation().isDelete(); 71 } 72 return (cpoWrapper.getType() == TYPE_DELETE); 73 } 74 /** 75 * Returns whether the operation in CPOWrapper is of TYPE_ASSERT; 76 */ isAssertQueryCompat(CPOWrapper cpoWrapper)77 public static boolean isAssertQueryCompat(CPOWrapper cpoWrapper) { 78 if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) { 79 return cpoWrapper.getOperation().isAssertQuery(); 80 } 81 return (cpoWrapper.getType() == TYPE_ASSERT); 82 } 83 84 /** 85 * PrioritizedMimeType is added in API level 23. 86 */ hasPrioritizedMimeType()87 public static boolean hasPrioritizedMimeType() { 88 return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) 89 >= Build.VERSION_CODES.M; 90 } 91 92 /** 93 * Determines if this version is compatible with multi-SIM and the phone account APIs. Can also 94 * force the version to be lower through SdkVersionOverride. 95 * 96 * @return {@code true} if multi-SIM capability is available, {@code false} otherwise. 97 */ isMSIMCompatible()98 public static boolean isMSIMCompatible() { 99 return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP) 100 >= Build.VERSION_CODES.LOLLIPOP_MR1; 101 } 102 103 /** 104 * Determines if this version is compatible with video calling. Can also force the version to be 105 * lower through SdkVersionOverride. 106 * 107 * @return {@code true} if video calling is allowed, {@code false} otherwise. 108 */ isVideoCompatible()109 public static boolean isVideoCompatible() { 110 return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP) 111 >= Build.VERSION_CODES.M; 112 } 113 114 /** 115 * Determines if this version is capable of using presence checking for video calling. Support 116 * for video call presence indication is added in SDK 24. 117 * 118 * @return {@code true} if video presence checking is allowed, {@code false} otherwise. 119 */ isVideoPresenceCompatible()120 public static boolean isVideoPresenceCompatible() { 121 return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) 122 > Build.VERSION_CODES.M; 123 } 124 125 /** 126 * Determines if this version is compatible with call subject. Can also force the version to be 127 * lower through SdkVersionOverride. 128 * 129 * @return {@code true} if call subject is a feature on this device, {@code false} otherwise. 130 */ isCallSubjectCompatible()131 public static boolean isCallSubjectCompatible() { 132 return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP) 133 >= Build.VERSION_CODES.M; 134 } 135 136 /** 137 * Determines if this version is compatible with a default dialer. Can also force the version to 138 * be lower through {@link SdkVersionOverride}. 139 * 140 * @return {@code true} if default dialer is a feature on this device, {@code false} otherwise. 141 */ isDefaultDialerCompatible()142 public static boolean isDefaultDialerCompatible() { 143 return isMarshmallowCompatible(); 144 } 145 146 /** 147 * Determines if this version is compatible with Lollipop Mr1-specific APIs. Can also force the 148 * version to be lower through SdkVersionOverride. 149 * 150 * @return {@code true} if runtime sdk is compatible with Lollipop MR1, {@code false} otherwise. 151 */ isLollipopMr1Compatible()152 public static boolean isLollipopMr1Compatible() { 153 return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP_MR1) 154 >= Build.VERSION_CODES.LOLLIPOP_MR1; 155 } 156 157 /** 158 * Determines if this version is compatible with Marshmallow-specific APIs. Can also force the 159 * version to be lower through SdkVersionOverride. 160 * 161 * @return {@code true} if runtime sdk is compatible with Marshmallow, {@code false} otherwise. 162 */ isMarshmallowCompatible()163 public static boolean isMarshmallowCompatible() { 164 return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP) 165 >= Build.VERSION_CODES.M; 166 } 167 168 /** 169 * Determines if this version is compatible with N-specific APIs. 170 * 171 * @return {@code true} if runtime sdk is compatible with N and the app is built with N, {@code 172 * false} otherwise. 173 */ isNCompatible()174 public static boolean isNCompatible() { 175 return BuildCompat.isAtLeastN(); 176 } 177 178 /** 179 * Determines if the given class is available. Can be used to check if system apis exist at 180 * runtime. 181 * 182 * @param className the name of the class to look for. 183 * @return {@code true} if the given class is available, {@code false} otherwise or if className 184 * is empty. 185 */ isClassAvailable(@ullable String className)186 public static boolean isClassAvailable(@Nullable String className) { 187 if (TextUtils.isEmpty(className)) { 188 return false; 189 } 190 try { 191 Class.forName(className); 192 return true; 193 } catch (ClassNotFoundException e) { 194 return false; 195 } catch (Throwable t) { 196 Log.e(TAG, "Unexpected exception when checking if class:" + className + " exists at " 197 + "runtime", t); 198 return false; 199 } 200 } 201 202 /** 203 * Determines if the given class's method is available to call. Can be used to check if system 204 * apis exist at runtime. 205 * 206 * @param className the name of the class to look for 207 * @param methodName the name of the method to look for 208 * @param parameterTypes the needed parameter types for the method to look for 209 * @return {@code true} if the given class is available, {@code false} otherwise or if className 210 * or methodName are empty. 211 */ isMethodAvailable(@ullable String className, @Nullable String methodName, Class<?>... parameterTypes)212 public static boolean isMethodAvailable(@Nullable String className, @Nullable String methodName, 213 Class<?>... parameterTypes) { 214 if (TextUtils.isEmpty(className) || TextUtils.isEmpty(methodName)) { 215 return false; 216 } 217 218 try { 219 Class.forName(className).getMethod(methodName, parameterTypes); 220 return true; 221 } catch (ClassNotFoundException | NoSuchMethodException e) { 222 Log.v(TAG, "Could not find method: " + className + "#" + methodName); 223 return false; 224 } catch (Throwable t) { 225 Log.e(TAG, "Unexpected exception when checking if method: " + className + "#" 226 + methodName + " exists at runtime", t); 227 return false; 228 } 229 } 230 231 /** 232 * Invokes a given class's method using reflection. Can be used to call system apis that exist 233 * at runtime but not in the SDK. 234 * 235 * @param instance The instance of the class to invoke the method on. 236 * @param methodName The name of the method to invoke. 237 * @param parameterTypes The needed parameter types for the method. 238 * @param parameters The parameter values to pass into the method. 239 * @return The result of the invocation or {@code null} if instance or methodName are empty, or 240 * if the reflection fails. 241 */ 242 @Nullable invokeMethod(@ullable Object instance, @Nullable String methodName, Class<?>[] parameterTypes, Object[] parameters)243 public static Object invokeMethod(@Nullable Object instance, @Nullable String methodName, 244 Class<?>[] parameterTypes, Object[] parameters) { 245 if (instance == null || TextUtils.isEmpty(methodName)) { 246 return null; 247 } 248 249 String className = instance.getClass().getName(); 250 try { 251 return Class.forName(className).getMethod(methodName, parameterTypes) 252 .invoke(instance, parameters); 253 } catch (ClassNotFoundException | NoSuchMethodException | IllegalArgumentException 254 | IllegalAccessException | InvocationTargetException e) { 255 Log.v(TAG, "Could not invoke method: " + className + "#" + methodName); 256 return null; 257 } catch (Throwable t) { 258 Log.e(TAG, "Unexpected exception when invoking method: " + className 259 + "#" + methodName + " at runtime", t); 260 return null; 261 } 262 } 263 264 /** 265 * Determines if this version is compatible with Lollipop-specific APIs. Can also force the 266 * version to be lower through SdkVersionOverride. 267 * 268 * @return {@code true} if call subject is a feature on this device, {@code false} otherwise. 269 */ isLollipopCompatible()270 public static boolean isLollipopCompatible() { 271 return SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.LOLLIPOP) 272 >= Build.VERSION_CODES.LOLLIPOP; 273 } 274 } 275