1 /*
2  * Copyright (C) 2013 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.internal.telephony;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.net.Uri;
24 import android.os.BadParcelableException;
25 import android.os.Build;
26 import android.telephony.NetworkRegistrationState;
27 import android.telephony.Rlog;
28 import android.telephony.ServiceState;
29 import android.telephony.ims.ImsCallProfile;
30 import android.telephony.ims.ImsConferenceState;
31 import android.telephony.ims.ImsExternalCallState;
32 import android.telephony.ims.ImsReasonInfo;
33 
34 import com.android.ims.ImsCall;
35 import com.android.internal.telephony.gsm.SuppServiceNotification;
36 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
37 import com.android.internal.telephony.imsphone.ImsPhone;
38 import com.android.internal.telephony.imsphone.ImsPhoneCall;
39 import com.android.internal.telephony.test.TestConferenceEventPackageParser;
40 
41 import java.io.File;
42 import java.io.FileInputStream;
43 import java.io.FileNotFoundException;
44 import java.util.ArrayList;
45 import java.util.List;
46 
47 /**
48  * Telephony tester receives the following intents where {name} is the phone name
49  *
50  * adb shell am broadcast -a com.android.internal.telephony.{name}.action_detached
51  * adb shell am broadcast -a com.android.internal.telephony.{name}.action_attached
52  * adb shell am broadcast -a com.android.internal.telephony.TestConferenceEventPackage -e filename
53  *      test_filename.xml
54  * adb shell am broadcast -a com.android.internal.telephony.TestServiceState --ei data_rat 10 --ei
55  *      data_roaming_type 3
56  * adb shell am broadcast -a com.android.internal.telephony.TestServiceState --es action reset
57  *
58  */
59 public class TelephonyTester {
60     private static final String LOG_TAG = "TelephonyTester";
61     private static final boolean DBG = true;
62 
63     /**
64      * Test-only intent used to send a test conference event package to the IMS framework.
65      */
66     private static final String ACTION_TEST_CONFERENCE_EVENT_PACKAGE =
67             "com.android.internal.telephony.TestConferenceEventPackage";
68 
69     /**
70      * Test-only intent used to send a test dialog event package to the IMS framework.
71      */
72     private static final String ACTION_TEST_DIALOG_EVENT_PACKAGE =
73             "com.android.internal.telephony.TestDialogEventPackage";
74 
75     private static final String EXTRA_FILENAME = "filename";
76     private static final String EXTRA_STARTPACKAGE = "startPackage";
77     private static final String EXTRA_SENDPACKAGE = "sendPackage";
78     private static final String EXTRA_DIALOGID = "dialogId";
79     private static final String EXTRA_NUMBER = "number";
80     private static final String EXTRA_STATE = "state";
81     private static final String EXTRA_CANPULL = "canPull";
82 
83     /**
84      * Test-only intent used to trigger supp service notification failure.
85      */
86     private static final String ACTION_TEST_SUPP_SRVC_FAIL =
87             "com.android.internal.telephony.TestSuppSrvcFail";
88     private static final String EXTRA_FAILURE_CODE = "failureCode";
89 
90     /**
91      * Test-only intent used to trigger the signalling which occurs when a handover to WIFI fails.
92      */
93     private static final String ACTION_TEST_HANDOVER_FAIL =
94             "com.android.internal.telephony.TestHandoverFail";
95 
96     /**
97      * Test-only intent used to trigger signalling of a
98      * {@link com.android.internal.telephony.gsm.SuppServiceNotification} to the {@link ImsPhone}.
99      * Use {@link #EXTRA_CODE} to specify the
100      * {@link com.android.internal.telephony.gsm.SuppServiceNotification#code}.
101      */
102     private static final String ACTION_TEST_SUPP_SRVC_NOTIFICATION =
103             "com.android.internal.telephony.TestSuppSrvcNotification";
104 
105     private static final String EXTRA_CODE = "code";
106     private static final String EXTRA_TYPE = "type";
107 
108     private static final String ACTION_TEST_SERVICE_STATE =
109             "com.android.internal.telephony.TestServiceState";
110 
111     private static final String EXTRA_ACTION = "action";
112     private static final String EXTRA_VOICE_RAT = "voice_rat";
113     private static final String EXTRA_DATA_RAT = "data_rat";
114     private static final String EXTRA_VOICE_REG_STATE = "voice_reg_state";
115     private static final String EXTRA_DATA_REG_STATE = "data_reg_state";
116     private static final String EXTRA_VOICE_ROAMING_TYPE = "voice_roaming_type";
117     private static final String EXTRA_DATA_ROAMING_TYPE = "data_roaming_type";
118 
119     private static final String ACTION_RESET = "reset";
120 
121     private static List<ImsExternalCallState> mImsExternalCallStates = null;
122 
123     private Intent mServiceStateTestIntent;
124 
125     private Phone mPhone;
126 
127     // The static intent receiver one for all instances and we assume this
128     // is running on the same thread as Dcc.
129     protected BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
130             @Override
131         public void onReceive(Context context, Intent intent) {
132             String action = intent.getAction();
133             try {
134                 if (DBG) log("sIntentReceiver.onReceive: action=" + action);
135                 if (action.equals(mPhone.getActionDetached())) {
136                     log("simulate detaching");
137                     mPhone.getServiceStateTracker().mDetachedRegistrants.notifyRegistrants();
138                 } else if (action.equals(mPhone.getActionAttached())) {
139                     log("simulate attaching");
140                     mPhone.getServiceStateTracker().mAttachedRegistrants.notifyRegistrants();
141                 } else if (action.equals(ACTION_TEST_CONFERENCE_EVENT_PACKAGE)) {
142                     log("inject simulated conference event package");
143                     handleTestConferenceEventPackage(context,
144                             intent.getStringExtra(EXTRA_FILENAME));
145                 } else if (action.equals(ACTION_TEST_DIALOG_EVENT_PACKAGE)) {
146                     log("handle test dialog event package intent");
147                     handleTestDialogEventPackageIntent(intent);
148                 } else if (action.equals(ACTION_TEST_SUPP_SRVC_FAIL)) {
149                     log("handle test supp svc failed intent");
150                     handleSuppServiceFailedIntent(intent);
151                 } else if (action.equals(ACTION_TEST_HANDOVER_FAIL)) {
152                     log("handle handover fail test intent");
153                     handleHandoverFailedIntent();
154                 } else if (action.equals(ACTION_TEST_SUPP_SRVC_NOTIFICATION)) {
155                     log("handle supp service notification test intent");
156                     sendTestSuppServiceNotification(intent);
157                 } else if (action.equals(ACTION_TEST_SERVICE_STATE)) {
158                     log("handle test service state changed intent");
159                     // Trigger the service state update. The replacement will be done in
160                     // overrideServiceState().
161                     mServiceStateTestIntent = intent;
162                     mPhone.getServiceStateTracker().sendEmptyMessage(
163                             ServiceStateTracker.EVENT_NETWORK_STATE_CHANGED);
164                 } else {
165                     if (DBG) log("onReceive: unknown action=" + action);
166                 }
167             } catch (BadParcelableException e) {
168                 Rlog.w(LOG_TAG, e);
169             }
170         }
171     };
172 
TelephonyTester(Phone phone)173     TelephonyTester(Phone phone) {
174         mPhone = phone;
175 
176         if (Build.IS_DEBUGGABLE) {
177             IntentFilter filter = new IntentFilter();
178 
179             filter.addAction(mPhone.getActionDetached());
180             log("register for intent action=" + mPhone.getActionDetached());
181 
182             filter.addAction(mPhone.getActionAttached());
183             log("register for intent action=" + mPhone.getActionAttached());
184 
185             if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
186                 log("register for intent action=" + ACTION_TEST_CONFERENCE_EVENT_PACKAGE);
187                 filter.addAction(ACTION_TEST_CONFERENCE_EVENT_PACKAGE);
188                 filter.addAction(ACTION_TEST_DIALOG_EVENT_PACKAGE);
189                 filter.addAction(ACTION_TEST_SUPP_SRVC_FAIL);
190                 filter.addAction(ACTION_TEST_HANDOVER_FAIL);
191                 filter.addAction(ACTION_TEST_SUPP_SRVC_NOTIFICATION);
192                 mImsExternalCallStates = new ArrayList<ImsExternalCallState>();
193             } else {
194                 filter.addAction(ACTION_TEST_SERVICE_STATE);
195                 log("register for intent action=" + ACTION_TEST_SERVICE_STATE);
196             }
197 
198             phone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone.getHandler());
199         }
200     }
201 
dispose()202     void dispose() {
203         if (Build.IS_DEBUGGABLE) {
204             mPhone.getContext().unregisterReceiver(mIntentReceiver);
205         }
206     }
207 
log(String s)208     private static void log(String s) {
209         Rlog.d(LOG_TAG, s);
210     }
211 
handleSuppServiceFailedIntent(Intent intent)212     private void handleSuppServiceFailedIntent(Intent intent) {
213         ImsPhone imsPhone = (ImsPhone) mPhone;
214         if (imsPhone == null) {
215             return;
216         }
217         int code = intent.getIntExtra(EXTRA_FAILURE_CODE, 0);
218         imsPhone.notifySuppServiceFailed(PhoneInternalInterface.SuppService.values()[code]);
219     }
220 
handleHandoverFailedIntent()221     private void handleHandoverFailedIntent() {
222         // Attempt to get the active IMS call
223         ImsPhone imsPhone = (ImsPhone) mPhone;
224         if (imsPhone == null) {
225             return;
226         }
227 
228         ImsPhoneCall imsPhoneCall = imsPhone.getForegroundCall();
229         if (imsPhoneCall == null) {
230             return;
231         }
232 
233         ImsCall imsCall = imsPhoneCall.getImsCall();
234         if (imsCall == null) {
235             return;
236         }
237 
238         imsCall.getImsCallSessionListenerProxy().callSessionHandoverFailed(imsCall.getCallSession(),
239                 ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN,
240                 new ImsReasonInfo());
241     }
242 
243     /**
244      * Handles request to send a test conference event package to the active Ims call.
245      *
246      * @see com.android.internal.telephony.test.TestConferenceEventPackageParser
247      * @param context The context.
248      * @param fileName The name of the test conference event package file to read.
249      */
handleTestConferenceEventPackage(Context context, String fileName)250     private void handleTestConferenceEventPackage(Context context, String fileName) {
251         // Attempt to get the active IMS call before parsing the test XML file.
252         ImsPhone imsPhone = (ImsPhone) mPhone;
253         if (imsPhone == null) {
254             return;
255         }
256 
257         ImsPhoneCall imsPhoneCall = imsPhone.getForegroundCall();
258         if (imsPhoneCall == null) {
259             return;
260         }
261 
262         ImsCall imsCall = imsPhoneCall.getImsCall();
263         if (imsCall == null) {
264             return;
265         }
266 
267         File packageFile = new File(context.getFilesDir(), fileName);
268         final FileInputStream is;
269         try {
270             is = new FileInputStream(packageFile);
271         } catch (FileNotFoundException ex) {
272             log("Test conference event package file not found: " + packageFile.getAbsolutePath());
273             return;
274         }
275 
276         TestConferenceEventPackageParser parser = new TestConferenceEventPackageParser(is);
277         ImsConferenceState imsConferenceState = parser.parse();
278         if (imsConferenceState == null) {
279             return;
280         }
281 
282         imsCall.conferenceStateUpdated(imsConferenceState);
283     }
284 
285     /**
286      * Handles intents containing test dialog event package data.
287      *
288      * @param intent
289      */
handleTestDialogEventPackageIntent(Intent intent)290     private void handleTestDialogEventPackageIntent(Intent intent) {
291         ImsPhone imsPhone = (ImsPhone) mPhone;
292         if (imsPhone == null) {
293             return;
294         }
295         ImsExternalCallTracker externalCallTracker = imsPhone.getExternalCallTracker();
296         if (externalCallTracker == null) {
297             return;
298         }
299 
300         if (intent.hasExtra(EXTRA_STARTPACKAGE)) {
301             mImsExternalCallStates.clear();
302         } else if (intent.hasExtra(EXTRA_SENDPACKAGE)) {
303             externalCallTracker.refreshExternalCallState(mImsExternalCallStates);
304             mImsExternalCallStates.clear();
305         } else if (intent.hasExtra(EXTRA_DIALOGID)) {
306             ImsExternalCallState state = new ImsExternalCallState(
307                     intent.getIntExtra(EXTRA_DIALOGID, 0),
308                     Uri.parse(intent.getStringExtra(EXTRA_NUMBER)),
309                     intent.getBooleanExtra(EXTRA_CANPULL, true),
310                     intent.getIntExtra(EXTRA_STATE,
311                             ImsExternalCallState.CALL_STATE_CONFIRMED),
312                     ImsCallProfile.CALL_TYPE_VOICE,
313                     false /* isHeld */
314                     );
315             mImsExternalCallStates.add(state);
316         }
317     }
318 
sendTestSuppServiceNotification(Intent intent)319     private void sendTestSuppServiceNotification(Intent intent) {
320         if (intent.hasExtra(EXTRA_CODE) && intent.hasExtra(EXTRA_TYPE)) {
321             int code = intent.getIntExtra(EXTRA_CODE, -1);
322             int type = intent.getIntExtra(EXTRA_TYPE, -1);
323             ImsPhone imsPhone = (ImsPhone) mPhone;
324             if (imsPhone == null) {
325                 return;
326             }
327             log("Test supp service notification:" + code);
328             SuppServiceNotification suppServiceNotification = new SuppServiceNotification();
329             suppServiceNotification.code = code;
330             suppServiceNotification.notificationType = type;
331             imsPhone.notifySuppSvcNotification(suppServiceNotification);
332         }
333     }
334 
overrideServiceState(ServiceState ss)335     void overrideServiceState(ServiceState ss) {
336         if (mServiceStateTestIntent == null || ss == null) return;
337         if (mServiceStateTestIntent.hasExtra(EXTRA_ACTION)
338                 && ACTION_RESET.equals(mServiceStateTestIntent.getStringExtra(EXTRA_ACTION))) {
339             log("Service state override reset");
340             return;
341         }
342         if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_REG_STATE)) {
343             ss.setVoiceRegState(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_REG_STATE,
344                     NetworkRegistrationState.REG_STATE_UNKNOWN));
345             log("Override voice reg state with " + ss.getVoiceRegState());
346         }
347         if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_REG_STATE)) {
348             ss.setDataRegState(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_REG_STATE,
349                     NetworkRegistrationState.REG_STATE_UNKNOWN));
350             log("Override data reg state with " + ss.getDataRegState());
351         }
352         if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_RAT)) {
353             ss.setRilVoiceRadioTechnology(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_RAT,
354                     ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
355             log("Override voice rat with " + ss.getRilVoiceRadioTechnology());
356         }
357         if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_RAT)) {
358             ss.setRilDataRadioTechnology(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_RAT,
359                     ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN));
360             log("Override data rat with " + ss.getRilDataRadioTechnology());
361         }
362         if (mServiceStateTestIntent.hasExtra(EXTRA_VOICE_ROAMING_TYPE)) {
363             ss.setVoiceRoamingType(mServiceStateTestIntent.getIntExtra(EXTRA_VOICE_ROAMING_TYPE,
364                     ServiceState.ROAMING_TYPE_UNKNOWN));
365             log("Override voice roaming type with " + ss.getVoiceRoamingType());
366         }
367         if (mServiceStateTestIntent.hasExtra(EXTRA_DATA_ROAMING_TYPE)) {
368             ss.setDataRoamingType(mServiceStateTestIntent.getIntExtra(EXTRA_DATA_ROAMING_TYPE,
369                     ServiceState.ROAMING_TYPE_UNKNOWN));
370             log("Override data roaming type with " + ss.getDataRoamingType());
371         }
372     }
373 }