1 /* 2 * Copyright (C) 2016 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 17 package com.android.incallui; 18 19 import com.google.common.base.Preconditions; 20 21 import android.content.Context; 22 import android.net.Uri; 23 import android.os.Bundle; 24 import android.support.annotation.Nullable; 25 import android.telecom.DisconnectCause; 26 import android.telecom.GatewayInfo; 27 import android.telecom.PhoneAccountHandle; 28 import android.telecom.StatusHints; 29 30 import java.lang.reflect.Constructor; 31 import java.lang.reflect.Field; 32 import java.lang.reflect.InvocationTargetException; 33 import java.lang.reflect.Method; 34 35 /** 36 * Wrapper class which uses reflection to create instances of {@link android.telecom.Call} for use 37 * with unit testing. Since {@link android.telecom.Call} is final, it cannot be mocked using 38 * mockito, and since all setter methods are hidden, it is necessary to use reflection. In the 39 * future, it would be desirable to replace this if a different mocking solution is used. 40 */ 41 public class TestTelecomCall { 42 43 private android.telecom.Call mCall; 44 45 public static @Nullable TestTelecomCall createInstance(String callId, 46 Uri handle, 47 int handlePresentation, 48 String callerDisplayName, 49 int callerDisplayNamePresentation, 50 PhoneAccountHandle accountHandle, 51 int capabilities, 52 int properties, 53 DisconnectCause disconnectCause, 54 long connectTimeMillis, 55 GatewayInfo gatewayInfo, 56 int videoState, 57 StatusHints statusHints, 58 Bundle extras, 59 Bundle intentExtras) { 60 61 try { 62 // Phone and InCall adapter are @hide, so we cannot refer to them directly. 63 Class<?> phoneClass = Class.forName("android.telecom.Phone"); 64 Class<?> incallAdapterClass = Class.forName("android.telecom.InCallAdapter"); 65 Class<?> callClass = android.telecom.Call.class; 66 Constructor<?> cons = callClass 67 .getDeclaredConstructor(phoneClass, String.class, incallAdapterClass); 68 cons.setAccessible(true); 69 70 android.telecom.Call call = (android.telecom.Call) cons.newInstance(null, callId, null); 71 72 // Create an instance of the call details. 73 Class<?> callDetailsClass = android.telecom.Call.Details.class; 74 Constructor<?> detailsCons = callDetailsClass.getDeclaredConstructor( 75 String.class, /* telecomCallId */ 76 Uri.class, /* handle */ 77 int.class, /* handlePresentation */ 78 String.class, /* callerDisplayName */ 79 int.class, /* callerDisplayNamePresentation */ 80 PhoneAccountHandle.class, /* accountHandle */ 81 int.class, /* capabilities */ 82 int.class, /* properties */ 83 DisconnectCause.class, /* disconnectCause */ 84 long.class, /* connectTimeMillis */ 85 GatewayInfo.class, /* gatewayInfo */ 86 int.class, /* videoState */ 87 StatusHints.class, /* statusHints */ 88 Bundle.class, /* extras */ 89 Bundle.class /* intentExtras */); 90 detailsCons.setAccessible(true); 91 92 android.telecom.Call.Details details = (android.telecom.Call.Details) 93 detailsCons.newInstance(callId, handle, handlePresentation, callerDisplayName, 94 callerDisplayNamePresentation, accountHandle, capabilities, properties, 95 disconnectCause, connectTimeMillis, gatewayInfo, videoState, 96 statusHints, 97 extras, intentExtras); 98 99 // Finally, set this as the details of the call. 100 Field detailsField = call.getClass().getDeclaredField("mDetails"); 101 detailsField.setAccessible(true); 102 detailsField.set(call, details); 103 104 return new TestTelecomCall(call); 105 } catch (NoSuchMethodException nsm) { 106 return null; 107 } catch (ClassNotFoundException cnf) { 108 return null; 109 } catch (IllegalAccessException e) { 110 return null; 111 } catch (InstantiationException e) { 112 return null; 113 } catch (InvocationTargetException e) { 114 return null; 115 } catch (NoSuchFieldException e) { 116 return null; 117 } 118 } 119 120 private TestTelecomCall(android.telecom.Call call) { 121 mCall = call; 122 } 123 124 public android.telecom.Call getCall() { 125 return mCall; 126 } 127 128 public void forceDetailsUpdate() { 129 Preconditions.checkNotNull(mCall); 130 131 try { 132 Method method = mCall.getClass().getDeclaredMethod("fireDetailsChanged", 133 android.telecom.Call.Details.class); 134 method.setAccessible(true); 135 method.invoke(mCall, mCall.getDetails()); 136 } catch (NoSuchMethodException e) { 137 Log.e(this, "forceDetailsUpdate", e); 138 } catch (InvocationTargetException e) { 139 Log.e(this, "forceDetailsUpdate", e); 140 } catch (IllegalAccessException e) { 141 Log.e(this, "forceDetailsUpdate", e); 142 } 143 } 144 145 public void setCapabilities(int capabilities) { 146 Preconditions.checkNotNull(mCall); 147 try { 148 Field field = mCall.getDetails().getClass().getDeclaredField("mCallCapabilities"); 149 field.setAccessible(true); 150 field.set(mCall.getDetails(), capabilities); 151 } catch (IllegalAccessException e) { 152 Log.e(this, "setProperties", e); 153 } catch (NoSuchFieldException e) { 154 Log.e(this, "setProperties", e); 155 } 156 } 157 158 public void setCall(android.telecom.Call call) { 159 mCall = call; 160 } 161 } 162