1 /*
2  * Copyright (C) 2018 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.ims;
18 
19 import android.app.PendingIntent;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.net.Uri;
25 import android.os.Message;
26 import android.os.RemoteException;
27 import android.telephony.TelephonyManager;
28 import android.telephony.ims.ImsCallProfile;
29 import android.telephony.ims.ImsReasonInfo;
30 import android.telephony.ims.feature.CapabilityChangeRequest;
31 import android.telephony.ims.feature.MmTelFeature;
32 import android.telephony.ims.stub.ImsRegistrationImplBase;
33 import android.util.Log;
34 
35 import com.android.ims.ImsConfigListener;
36 import com.android.ims.ImsManager;
37 import com.android.ims.internal.IImsCallSession;
38 import com.android.ims.internal.IImsConfig;
39 import com.android.ims.internal.IImsEcbm;
40 import com.android.ims.internal.IImsMultiEndpoint;
41 import com.android.ims.internal.IImsRegistrationListener;
42 import com.android.ims.internal.IImsUt;
43 
44 import java.util.HashMap;
45 import java.util.Map;
46 import java.util.concurrent.CountDownLatch;
47 import java.util.concurrent.TimeUnit;
48 
49 public class MmTelFeatureCompatAdapter extends MmTelFeature {
50 
51     private static final String TAG = "MmTelFeatureCompat";
52 
53     public static final String ACTION_IMS_INCOMING_CALL = "com.android.ims.IMS_INCOMING_CALL";
54 
55     private static final int WAIT_TIMEOUT_MS = 2000;
56 
57     private final MmTelInterfaceAdapter mCompatFeature;
58     private ImsRegistrationCompatAdapter mRegCompatAdapter;
59     private int mSessionId = -1;
60 
61     private static final Map<Integer, Integer> REG_TECH_TO_NET_TYPE = new HashMap<>(2);
62 
63     static {
REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_NR, TelephonyManager.NETWORK_TYPE_NR)64         REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_NR,
65                 TelephonyManager.NETWORK_TYPE_NR);
REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, TelephonyManager.NETWORK_TYPE_LTE)66         REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
67                 TelephonyManager.NETWORK_TYPE_LTE);
REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, TelephonyManager.NETWORK_TYPE_IWLAN)68         REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
69                 TelephonyManager.NETWORK_TYPE_IWLAN);
REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM, TelephonyManager.NETWORK_TYPE_IWLAN)70         REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM,
71                 TelephonyManager.NETWORK_TYPE_IWLAN);
72     }
73 
74     // Feature Type for compatibility with old "feature" updates
75     public static final int FEATURE_TYPE_UNKNOWN = -1;
76     public static final int FEATURE_TYPE_VOICE_OVER_LTE = 0;
77     public static final int FEATURE_TYPE_VIDEO_OVER_LTE = 1;
78     public static final int FEATURE_TYPE_VOICE_OVER_WIFI = 2;
79     public static final int FEATURE_TYPE_VIDEO_OVER_WIFI = 3;
80     public static final int FEATURE_TYPE_UT_OVER_LTE = 4;
81     public static final int FEATURE_TYPE_UT_OVER_WIFI = 5;
82 
83     public static final int FEATURE_UNKNOWN = -1;
84     public static final int FEATURE_DISABLED = 0;
85     public static final int FEATURE_ENABLED = 1;
86 
87     private static class ConfigListener extends ImsConfigListener.Stub {
88 
89         private final int mCapability;
90         private final int mTech;
91         private final CountDownLatch mLatch;
92 
ConfigListener(int capability, int tech, CountDownLatch latch)93         public ConfigListener(int capability, int tech, CountDownLatch latch) {
94             mCapability = capability;
95             mTech = tech;
96             mLatch = latch;
97         }
98 
99         @Override
onGetFeatureResponse(int feature, int network, int value, int status)100         public void onGetFeatureResponse(int feature, int network, int value, int status)
101                 throws RemoteException {
102             if (feature == mCapability && network == mTech) {
103                 mLatch.countDown();
104                 getFeatureValueReceived(value);
105             } else {
106                 Log.i(TAG, "onGetFeatureResponse: response different than requested: feature="
107                         + feature + " and network=" + network);
108             }
109         }
110 
111         @Override
onSetFeatureResponse(int feature, int network, int value, int status)112         public void onSetFeatureResponse(int feature, int network, int value, int status)
113                 throws RemoteException {
114             if (feature == mCapability && network == mTech) {
115                 mLatch.countDown();
116                 setFeatureValueReceived(value);
117             } else {
118                 Log.i(TAG, "onSetFeatureResponse: response different than requested: feature="
119                         + feature + " and network=" + network);
120             }
121         }
122 
123         @Override
onGetVideoQuality(int status, int quality)124         public void onGetVideoQuality(int status, int quality) throws RemoteException {
125         }
126 
127         @Override
onSetVideoQuality(int status)128         public void onSetVideoQuality(int status) throws RemoteException {
129         }
130 
getFeatureValueReceived(int value)131         public void getFeatureValueReceived(int value) {
132         }
133 
setFeatureValueReceived(int value)134         public void setFeatureValueReceived(int value) {
135         }
136     }
137 
138     // Trampolines "old" listener events to the new interface.
139     private final IImsRegistrationListener mListener = new IImsRegistrationListener.Stub() {
140         @Override
141         public void registrationConnected() throws RemoteException {
142             // Implemented in the Registration Adapter
143         }
144 
145         @Override
146         public void registrationProgressing() throws RemoteException {
147             // Implemented in the Registration Adapter
148         }
149 
150         @Override
151         public void registrationConnectedWithRadioTech(int imsRadioTech) throws RemoteException {
152             // Implemented in the Registration Adapter
153         }
154 
155         @Override
156         public void registrationProgressingWithRadioTech(int imsRadioTech) throws RemoteException {
157             // Implemented in the Registration Adapter
158         }
159 
160         @Override
161         public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
162             // At de-registration, notify the framework that no IMS capabilities are currently
163             // available.
164             Log.i(TAG, "registrationDisconnected: resetting MMTEL capabilities.");
165             notifyCapabilitiesStatusChanged(new MmTelCapabilities());
166             // Implemented in the Registration Adapter
167         }
168 
169         @Override
170         public void registrationResumed() throws RemoteException {
171             // Don't care
172         }
173 
174         @Override
175         public void registrationSuspended() throws RemoteException {
176             // Don't care
177         }
178 
179         @Override
180         public void registrationServiceCapabilityChanged(int serviceClass, int event)
181                 throws RemoteException {
182             // Don't care
183         }
184 
185         @Override
186         public void registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures,
187                 int[] disabledFeatures) throws RemoteException {
188             notifyCapabilitiesStatusChanged(convertCapabilities(enabledFeatures));
189         }
190 
191         @Override
192         public void voiceMessageCountUpdate(int count) throws RemoteException {
193             notifyVoiceMessageCountUpdate(count);
194         }
195 
196         @Override
197         public void registrationAssociatedUriChanged(Uri[] uris) throws RemoteException {
198             // Implemented in the Registration Adapter
199         }
200 
201         @Override
202         public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo)
203                 throws RemoteException {
204             // Implemented in the Registration Adapter
205         }
206     };
207 
208     /**
209      * Stub implementation of the "old" Registration listener interface that provides no
210      * functionality. Instead, it is used to ensure compatibility with older devices that require
211      * a listener on startSession. The actual Registration Listener Interface is added separately
212      * in ImsRegistration.
213      */
214     private class ImsRegistrationListenerBase extends IImsRegistrationListener.Stub {
215 
216         @Override
registrationConnected()217         public void registrationConnected() throws RemoteException {
218         }
219 
220         @Override
registrationProgressing()221         public void registrationProgressing() throws RemoteException {
222         }
223 
224         @Override
registrationConnectedWithRadioTech(int imsRadioTech)225         public void registrationConnectedWithRadioTech(int imsRadioTech) throws RemoteException {
226         }
227 
228         @Override
registrationProgressingWithRadioTech(int imsRadioTech)229         public void registrationProgressingWithRadioTech(int imsRadioTech) throws RemoteException {
230         }
231 
232         @Override
registrationDisconnected(ImsReasonInfo imsReasonInfo)233         public void registrationDisconnected(ImsReasonInfo imsReasonInfo) throws RemoteException {
234         }
235 
236         @Override
registrationResumed()237         public void registrationResumed() throws RemoteException {
238         }
239 
240         @Override
registrationSuspended()241         public void registrationSuspended() throws RemoteException {
242         }
243 
244         @Override
registrationServiceCapabilityChanged(int serviceClass, int event)245         public void registrationServiceCapabilityChanged(int serviceClass, int event)
246                 throws RemoteException {
247         }
248 
249         @Override
registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures, int[] disabledFeatures)250         public void registrationFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures,
251                 int[] disabledFeatures) throws RemoteException {
252         }
253 
254         @Override
voiceMessageCountUpdate(int count)255         public void voiceMessageCountUpdate(int count) throws RemoteException {
256         }
257 
258         @Override
registrationAssociatedUriChanged(Uri[] uris)259         public void registrationAssociatedUriChanged(Uri[] uris) throws RemoteException {
260         }
261 
262         @Override
registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo)263         public void registrationChangeFailed(int targetAccessTech, ImsReasonInfo imsReasonInfo)
264                 throws RemoteException {
265         }
266     }
267 
268     // Handle Incoming Call as PendingIntent, the old method
269     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
270         @Override
271         public void onReceive(Context context, Intent intent) {
272             Log.i(TAG, "onReceive");
273             if (intent.getAction().equals(ACTION_IMS_INCOMING_CALL)) {
274                 Log.i(TAG, "onReceive : incoming call intent.");
275 
276                 String callId = intent.getStringExtra("android:imsCallID");
277                 try {
278                     IImsCallSession session = mCompatFeature.getPendingCallSession(mSessionId,
279                             callId);
280                     notifyIncomingCallSession(session, intent.getExtras());
281                 } catch (RemoteException e) {
282                     Log.w(TAG, "onReceive: Couldn't get Incoming call session.");
283                 }
284             }
285         }
286     };
287 
MmTelFeatureCompatAdapter(Context context, int slotId, MmTelInterfaceAdapter compatFeature)288     public MmTelFeatureCompatAdapter(Context context, int slotId,
289             MmTelInterfaceAdapter compatFeature) {
290         initialize(context, slotId);
291         mCompatFeature = compatFeature;
292     }
293 
294     @Override
queryCapabilityConfiguration(int capability, int radioTech)295     public boolean queryCapabilityConfiguration(int capability, int radioTech) {
296         int capConverted = convertCapability(capability, radioTech);
297         // Wait for the result from the ImsService
298         CountDownLatch latch = new CountDownLatch(1);
299         final int[] returnValue = new int[1];
300         returnValue[0] = FEATURE_UNKNOWN;
301         int regTech = REG_TECH_TO_NET_TYPE.getOrDefault(radioTech,
302                 ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
303         try {
304             mCompatFeature.getConfigInterface().getFeatureValue(capConverted, regTech,
305                     new ConfigListener(capConverted, regTech, latch) {
306                         @Override
307                         public void getFeatureValueReceived(int value) {
308                             returnValue[0] = value;
309                         }
310                     });
311         } catch (RemoteException e) {
312             Log.w(TAG, "queryCapabilityConfiguration");
313         }
314         try {
315             latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
316         } catch (InterruptedException e) {
317             Log.w(TAG, "queryCapabilityConfiguration - error waiting: " + e.getMessage());
318         }
319         return returnValue[0] == FEATURE_ENABLED;
320     }
321 
322     @Override
changeEnabledCapabilities(CapabilityChangeRequest request, CapabilityCallbackProxy c)323     public void changeEnabledCapabilities(CapabilityChangeRequest request,
324             CapabilityCallbackProxy c) {
325         if (request == null) {
326             return;
327         }
328         try {
329             IImsConfig imsConfig = mCompatFeature.getConfigInterface();
330             // Disable Capabilities
331             for (CapabilityChangeRequest.CapabilityPair cap : request.getCapabilitiesToDisable()) {
332                 CountDownLatch latch = new CountDownLatch(1);
333                 int capConverted = convertCapability(cap.getCapability(), cap.getRadioTech());
334                 int radioTechConverted = REG_TECH_TO_NET_TYPE.getOrDefault(cap.getRadioTech(),
335                         ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
336                 Log.i(TAG, "changeEnabledCapabilities - cap: " + capConverted + " radioTech: "
337                         + radioTechConverted + " disabled");
338                 imsConfig.setFeatureValue(capConverted, radioTechConverted, FEATURE_DISABLED,
339                         new ConfigListener(capConverted, radioTechConverted, latch) {
340                             @Override
341                             public void setFeatureValueReceived(int value) {
342                                 if (value != FEATURE_DISABLED) {
343                                     if (c == null) {
344                                         return;
345                                     }
346                                     c.onChangeCapabilityConfigurationError(cap.getCapability(),
347                                             cap.getRadioTech(), CAPABILITY_ERROR_GENERIC);
348                                 }
349                                 Log.i(TAG, "changeEnabledCapabilities - setFeatureValueReceived"
350                                         + " with value " + value);
351                             }
352                         });
353                 latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
354             }
355             // Enable Capabilities
356             for (CapabilityChangeRequest.CapabilityPair cap : request.getCapabilitiesToEnable()) {
357                 CountDownLatch latch = new CountDownLatch(1);
358                 int capConverted = convertCapability(cap.getCapability(), cap.getRadioTech());
359                 int radioTechConverted = REG_TECH_TO_NET_TYPE.getOrDefault(cap.getRadioTech(),
360                         ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
361                 Log.i(TAG, "changeEnabledCapabilities - cap: " + capConverted + " radioTech: "
362                         + radioTechConverted + " enabled");
363                 imsConfig.setFeatureValue(capConverted, radioTechConverted, FEATURE_ENABLED,
364                         new ConfigListener(capConverted, radioTechConverted, latch) {
365                             @Override
366                             public void setFeatureValueReceived(int value) {
367                                 if (value != FEATURE_ENABLED) {
368                                     if (c == null) {
369                                         return;
370                                     }
371                                     c.onChangeCapabilityConfigurationError(cap.getCapability(),
372                                             cap.getRadioTech(), CAPABILITY_ERROR_GENERIC);
373                                 }
374                                 Log.i(TAG, "changeEnabledCapabilities - setFeatureValueReceived"
375                                         + " with value " + value);
376                             }
377                         });
378                 latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
379             }
380         } catch (RemoteException | InterruptedException e) {
381             Log.w(TAG, "changeEnabledCapabilities: Error processing: " + e.getMessage());
382         }
383     }
384 
385     @Override
createCallProfile(int callSessionType, int callType)386     public ImsCallProfile createCallProfile(int callSessionType, int callType) {
387         try {
388             return mCompatFeature.createCallProfile(mSessionId, callSessionType, callType);
389         } catch (RemoteException e) {
390             throw new RuntimeException(e.getMessage());
391         }
392     }
393 
394     @Override
createCallSessionInterface(ImsCallProfile profile)395     public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
396             throws RemoteException {
397         return mCompatFeature.createCallSession(mSessionId, profile);
398     }
399 
400     @Override
getUtInterface()401     public IImsUt getUtInterface() throws RemoteException {
402         return mCompatFeature.getUtInterface();
403     }
404 
405     @Override
getEcbmInterface()406     public IImsEcbm getEcbmInterface() throws RemoteException {
407         return mCompatFeature.getEcbmInterface();
408     }
409 
410     @Override
getMultiEndpointInterface()411     public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
412         return mCompatFeature.getMultiEndpointInterface();
413     }
414 
415     @Override
getFeatureState()416     public int getFeatureState() {
417         try {
418             return mCompatFeature.getFeatureState();
419         } catch (RemoteException e) {
420             throw new RuntimeException(e.getMessage());
421         }
422     }
423 
424     @Override
setUiTtyMode(int mode, Message onCompleteMessage)425     public void setUiTtyMode(int mode, Message onCompleteMessage) {
426         try {
427             mCompatFeature.setUiTTYMode(mode, onCompleteMessage);
428         } catch (RemoteException e) {
429             throw new RuntimeException(e.getMessage());
430         }
431     }
432 
433 
434     @Override
onFeatureRemoved()435     public void onFeatureRemoved() {
436         mContext.unregisterReceiver(mReceiver);
437         try {
438             mCompatFeature.endSession(mSessionId);
439             mCompatFeature.removeRegistrationListener(mListener);
440             if (mRegCompatAdapter != null) {
441                 mCompatFeature.removeRegistrationListener(
442                         mRegCompatAdapter.getRegistrationListener());
443             }
444         } catch (RemoteException e) {
445             Log.w(TAG, "onFeatureRemoved: Couldn't end session: " + e.getMessage());
446         }
447     }
448 
449     @Override
onFeatureReady()450     public void onFeatureReady() {
451         Log.i(TAG, "onFeatureReady called!");
452         // This gets called when MmTelFeature.setListener is called. We need to use this time to
453         // call openSession on the old MMTelFeature implementation.
454         IntentFilter intentFilter = new IntentFilter(ImsManager.ACTION_IMS_INCOMING_CALL);
455         mContext.registerReceiver(mReceiver, intentFilter);
456         try {
457             mSessionId = mCompatFeature.startSession(createIncomingCallPendingIntent(),
458                     new ImsRegistrationListenerBase());
459             mCompatFeature.addRegistrationListener(mListener);
460             mCompatFeature.addRegistrationListener(mRegCompatAdapter.getRegistrationListener());
461         } catch (RemoteException e) {
462             Log.e(TAG, "Couldn't start compat feature: " + e.getMessage());
463         }
464     }
465 
enableIms()466     public void enableIms() throws RemoteException {
467         mCompatFeature.turnOnIms();
468     }
469 
disableIms()470     public void disableIms() throws RemoteException {
471         mCompatFeature.turnOffIms();
472     }
473 
getOldConfigInterface()474     public IImsConfig getOldConfigInterface() {
475         try {
476             return mCompatFeature.getConfigInterface();
477         } catch (RemoteException e) {
478             Log.w(TAG, "getOldConfigInterface(): " + e.getMessage());
479             return null;
480         }
481     }
482 
addRegistrationAdapter(ImsRegistrationCompatAdapter regCompat)483     public void addRegistrationAdapter(ImsRegistrationCompatAdapter regCompat)
484             throws RemoteException {
485         mRegCompatAdapter = regCompat;
486     }
487 
convertCapabilities(int[] enabledFeatures)488     private MmTelCapabilities convertCapabilities(int[] enabledFeatures) {
489         boolean[] featuresEnabled = new boolean[enabledFeatures.length];
490         for (int i = FEATURE_TYPE_VOICE_OVER_LTE; i <= FEATURE_TYPE_UT_OVER_WIFI
491                 && i < enabledFeatures.length; i++) {
492             if (enabledFeatures[i] == i) {
493                 featuresEnabled[i] = true;
494             } else if (enabledFeatures[i] == FEATURE_TYPE_UNKNOWN) {
495                 // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled.
496                 featuresEnabled[i] = false;
497             }
498         }
499         MmTelCapabilities capabilities = new MmTelCapabilities();
500         if (featuresEnabled[FEATURE_TYPE_VOICE_OVER_LTE]
501                 || featuresEnabled[FEATURE_TYPE_VOICE_OVER_WIFI]) {
502             // voice is enabled
503             capabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_VOICE);
504         }
505         if (featuresEnabled[FEATURE_TYPE_VIDEO_OVER_LTE]
506                 || featuresEnabled[FEATURE_TYPE_VIDEO_OVER_WIFI]) {
507             // video is enabled
508             capabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
509         }
510         if (featuresEnabled[FEATURE_TYPE_UT_OVER_LTE]
511                 || featuresEnabled[FEATURE_TYPE_UT_OVER_WIFI]) {
512             // ut is enabled
513             capabilities.addCapabilities(MmTelCapabilities.CAPABILITY_TYPE_UT);
514         }
515         Log.i(TAG, "convertCapabilities - capabilities: " + capabilities);
516         return capabilities;
517     }
518 
createIncomingCallPendingIntent()519     private PendingIntent createIncomingCallPendingIntent() {
520         Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
521         intent.setPackage(TelephonyManager.PHONE_PROCESS_NAME);
522         return PendingIntent.getBroadcast(mContext, 0, intent,
523                 // Mutable because information associated with the call is passed back here.
524                 PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
525     }
526 
convertCapability(int capability, int radioTech)527     private int convertCapability(int capability, int radioTech) {
528         int capConverted = FEATURE_TYPE_UNKNOWN;
529         if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
530             switch (capability) {
531                 case MmTelCapabilities.CAPABILITY_TYPE_VOICE:
532                     capConverted = FEATURE_TYPE_VOICE_OVER_LTE;
533                     break;
534                 case MmTelCapabilities.CAPABILITY_TYPE_VIDEO:
535                     capConverted = FEATURE_TYPE_VIDEO_OVER_LTE;
536                     break;
537                 case MmTelCapabilities.CAPABILITY_TYPE_UT:
538                     capConverted = FEATURE_TYPE_UT_OVER_LTE;
539                     break;
540             }
541         } else if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
542             switch (capability) {
543                 case MmTelCapabilities.CAPABILITY_TYPE_VOICE:
544                     capConverted = FEATURE_TYPE_VOICE_OVER_WIFI;
545                     break;
546                 case MmTelCapabilities.CAPABILITY_TYPE_VIDEO:
547                     capConverted = FEATURE_TYPE_VIDEO_OVER_WIFI;
548                     break;
549                 case MmTelCapabilities.CAPABILITY_TYPE_UT:
550                     capConverted = FEATURE_TYPE_UT_OVER_WIFI;
551                     break;
552             }
553         }
554         return capConverted;
555     }
556 }
557