• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.telephony.ims.feature;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.SystemApi;
22 import android.annotation.TestApi;
23 import android.content.Context;
24 import android.os.IInterface;
25 import android.os.RemoteException;
26 import android.telephony.SubscriptionManager;
27 import android.telephony.ims.aidl.IImsCapabilityCallback;
28 import android.telephony.ims.stub.ImsRegistrationImplBase;
29 import android.util.Log;
30 
31 import com.android.ims.internal.IImsFeatureStatusCallback;
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.internal.telephony.util.RemoteCallbackListExt;
34 
35 import java.lang.annotation.Retention;
36 import java.lang.annotation.RetentionPolicy;
37 import java.util.HashMap;
38 import java.util.Map;
39 
40 /**
41  * Base class for all IMS features that are supported by the framework. Use a concrete subclass
42  * of {@link ImsFeature}, such as {@link MmTelFeature} or {@link RcsFeature}.
43  *
44  * @hide
45  */
46 @SystemApi
47 public abstract class ImsFeature {
48 
49     private static final String LOG_TAG = "ImsFeature";
50 
51     /**
52      * Invalid feature value
53      * @hide
54      */
55     public static final int FEATURE_INVALID = -1;
56     // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
57     // defined values in ImsServiceClass for compatibility purposes.
58     /**
59      * This feature supports emergency calling over MMTEL. If defined, the framework will try to
60      * place an emergency call over IMS first. If it is not defined, the framework will only use
61      * CSFB for emergency calling.
62      * @hide
63      */
64     @SystemApi
65     public static final int FEATURE_EMERGENCY_MMTEL = 0;
66     /**
67      * This feature supports the MMTEL feature.
68      * @hide
69      */
70     @SystemApi
71     public static final int FEATURE_MMTEL = 1;
72     /**
73      * This feature supports the RCS feature.
74      * @hide
75      */
76     @SystemApi
77     public static final int FEATURE_RCS = 2;
78     /**
79      * Total number of features defined
80      * @hide
81      */
82     public static final int FEATURE_MAX = 3;
83 
84     /**
85      * Used for logging purposes.
86      * @hide
87      */
88     public static final Map<Integer, String> FEATURE_LOG_MAP = new HashMap<Integer, String>() {{
89             put(FEATURE_EMERGENCY_MMTEL, "EMERGENCY_MMTEL");
90             put(FEATURE_MMTEL, "MMTEL");
91             put(FEATURE_RCS, "RCS");
92         }};
93 
94     /**
95      * Integer values defining IMS features that are supported in ImsFeature.
96      * @hide
97      */
98     @IntDef(flag = true,
99             value = {
100                     FEATURE_EMERGENCY_MMTEL,
101                     FEATURE_MMTEL,
102                     FEATURE_RCS
103             })
104     @Retention(RetentionPolicy.SOURCE)
105     public @interface FeatureType {}
106 
107     /**
108      * Integer values defining the state of the ImsFeature at any time.
109      * @hide
110      */
111     @IntDef(flag = true,
112             value = {
113                     STATE_UNAVAILABLE,
114                     STATE_INITIALIZING,
115                     STATE_READY,
116             })
117     @Retention(RetentionPolicy.SOURCE)
118     public @interface ImsState {}
119 
120     /**
121      * This {@link ImsFeature}'s state is unavailable and should not be communicated with. This will
122      * remove all bindings back to the framework. Any attempt to communicate with the framework
123      * during this time will result in an {@link IllegalStateException}.
124      * @hide
125      */
126     @SystemApi
127     public static final int STATE_UNAVAILABLE = 0;
128     /**
129      * This {@link ImsFeature} state is initializing and should not be communicated with. This will
130      * remove all bindings back to the framework. Any attempt to communicate with the framework
131      * during this time will result in an {@link IllegalStateException}.
132      * @hide
133      */
134     @SystemApi
135     public static final int STATE_INITIALIZING = 1;
136     /**
137      * This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods
138      * until {@see #onFeatureReady()} is called.
139      * @hide
140      */
141     @SystemApi
142     public static final int STATE_READY = 2;
143 
144     /**
145      * Used for logging purposes.
146      * @hide
147      */
148     public static final Map<Integer, String> STATE_LOG_MAP = new HashMap<Integer, String>() {{
149             put(STATE_UNAVAILABLE, "UNAVAILABLE");
150             put(STATE_INITIALIZING, "INITIALIZING");
151             put(STATE_READY, "READY");
152         }};
153 
154     /**
155      * Integer values defining the result codes that should be returned from
156      * {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability.
157      * @hide
158      */
159     @IntDef(flag = true,
160             value = {
161                     CAPABILITY_ERROR_GENERIC,
162                     CAPABILITY_SUCCESS
163             })
164     @Retention(RetentionPolicy.SOURCE)
165     public @interface ImsCapabilityError {}
166 
167     /**
168      * The capability was unable to be changed.
169      * @hide
170      */
171     @SystemApi
172     public static final int CAPABILITY_ERROR_GENERIC = -1;
173     /**
174      * The capability was able to be changed.
175      * @hide
176      */
177     @SystemApi
178     public static final int CAPABILITY_SUCCESS = 0;
179 
180     /**
181      * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
182      * provided.
183      */
184     protected static class CapabilityCallbackProxy {
185         private final IImsCapabilityCallback mCallback;
186 
187         /** @hide */
CapabilityCallbackProxy(IImsCapabilityCallback c)188         public CapabilityCallbackProxy(IImsCapabilityCallback c) {
189             mCallback = c;
190         }
191 
192         /**
193          * This method notifies the provided framework callback that the request to change the
194          * indicated capability has failed and has not changed.
195          *
196          * @param capability The Capability that will be notified to the framework, defined as
197          * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
198          * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
199          * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
200          * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}.
201          * @param radioTech The radio tech that this capability failed for, defined as
202          * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
203          * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} or
204          * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}.
205          * @param reason The reason this capability was unable to be changed, defined as
206          * {@link #CAPABILITY_ERROR_GENERIC} or {@link #CAPABILITY_SUCCESS}.
207          */
onChangeCapabilityConfigurationError(int capability, int radioTech, @ImsCapabilityError int reason)208         public void onChangeCapabilityConfigurationError(int capability, int radioTech,
209                 @ImsCapabilityError int reason) {
210             if (mCallback == null) {
211                 return;
212             }
213             try {
214                 mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
215             } catch (RemoteException e) {
216                 Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
217             }
218         }
219     }
220 
221     /**
222      * Contains the IMS capabilities defined and supported by an ImsFeature in the form of a
223      * bit-mask.
224      *
225      * @deprecated This class is not used directly, but rather extended in subclasses of
226      * {@link ImsFeature} to provide service specific capabilities.
227      * @see MmTelFeature.MmTelCapabilities
228      * @hide
229      */
230     // Not Actually deprecated, but we need to remove it from the @SystemApi surface.
231     @Deprecated
232     @SystemApi // SystemApi only because it was leaked through type usage in a previous release.
233     @TestApi
234     public static class Capabilities {
235         /** @deprecated Use getters and accessors instead. */
236         // Not actually deprecated, but we need to remove it from the @SystemApi surface eventually.
237         protected int mCapabilities = 0;
238 
239         /**
240          * @hide
241          */
Capabilities()242         public Capabilities() {
243         }
244 
245         /**
246          * @hide
247          */
Capabilities(int capabilities)248         protected Capabilities(int capabilities) {
249             mCapabilities = capabilities;
250         }
251 
252         /**
253          * @param capabilities Capabilities to be added to the configuration in the form of a
254          *     bit mask.
255          * @hide
256          */
addCapabilities(int capabilities)257         public void addCapabilities(int capabilities) {
258             mCapabilities |= capabilities;
259         }
260 
261         /**
262          * @param capabilities Capabilities to be removed to the configuration in the form of a
263          *     bit mask.
264          * @hide
265          */
removeCapabilities(int capabilities)266         public void removeCapabilities(int capabilities) {
267             mCapabilities &= ~capabilities;
268         }
269 
270         /**
271          * @return true if all of the capabilities specified are capable.
272          * @hide
273          */
isCapable(int capabilities)274         public boolean isCapable(int capabilities) {
275             return (mCapabilities & capabilities) == capabilities;
276         }
277 
278         /**
279          * @return a deep copy of the Capabilites.
280          * @hide
281          */
copy()282         public Capabilities copy() {
283             return new Capabilities(mCapabilities);
284         }
285 
286         /**
287          * @return a bitmask containing the capability flags directly.
288          * @hide
289          */
getMask()290         public int getMask() {
291             return mCapabilities;
292         }
293 
294         /**
295          * @hide
296          */
297         @Override
equals(Object o)298         public boolean equals(Object o) {
299             if (this == o) return true;
300             if (!(o instanceof Capabilities)) return false;
301 
302             Capabilities that = (Capabilities) o;
303 
304             return mCapabilities == that.mCapabilities;
305         }
306 
307         /**
308          * @hide
309          */
310         @Override
hashCode()311         public int hashCode() {
312             return mCapabilities;
313         }
314 
315         /**
316          * @hide
317          */
318         @Override
toString()319         public String toString() {
320             return "Capabilities: " + Integer.toBinaryString(mCapabilities);
321         }
322     }
323 
324     /** @hide */
325     protected Context mContext;
326     /** @hide */
327     protected final Object mLock = new Object();
328 
329     private final RemoteCallbackListExt<IImsFeatureStatusCallback> mStatusCallbacks =
330             new RemoteCallbackListExt<>();
331     private @ImsState int mState = STATE_UNAVAILABLE;
332     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
333     private final RemoteCallbackListExt<IImsCapabilityCallback> mCapabilityCallbacks =
334             new RemoteCallbackListExt<>();
335     private Capabilities mCapabilityStatus = new Capabilities();
336 
337     /**
338      * @hide
339      */
initialize(Context context, int slotId)340     public void initialize(Context context, int slotId) {
341         mContext = context;
342         mSlotId = slotId;
343     }
344 
345     /**
346      * @return The SIM slot index associated with this ImsFeature.
347      *
348      * @see SubscriptionManager#getSubscriptionIds(int) for more information on getting the
349      * subscription IDs associated with this slot.
350      * @hide
351      */
352     @SystemApi
getSlotIndex()353     public final int getSlotIndex() {
354         return mSlotId;
355     }
356 
357     /**
358      * @return The current state of the ImsFeature, set previously by {@link #setFeatureState(int)}
359      * or {@link #STATE_UNAVAILABLE} if it has not been updated  yet.
360      * @hide
361      */
362     @SystemApi
getFeatureState()363     public @ImsState int getFeatureState() {
364         synchronized (mLock) {
365             return mState;
366         }
367     }
368 
369     /**
370      * Set the state of the ImsFeature. The state is used as a signal to the framework to start or
371      * stop communication, depending on the state sent.
372      * @param state The ImsFeature's state, defined as {@link #STATE_UNAVAILABLE},
373      * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
374      * @hide
375      */
376     @SystemApi
setFeatureState(@msState int state)377     public final void setFeatureState(@ImsState int state) {
378         synchronized (mLock) {
379             if (mState != state) {
380                 mState = state;
381                 notifyFeatureState(state);
382             }
383         }
384     }
385 
386     /**
387      * Not final for testing, but shouldn't be extended!
388      * @hide
389      */
390     @VisibleForTesting
addImsFeatureStatusCallback(@onNull IImsFeatureStatusCallback c)391     public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
392         try {
393             // If we have just connected, send queued status.
394             c.notifyImsFeatureStatus(getFeatureState());
395             // Add the callback if the callback completes successfully without a RemoteException.
396             mStatusCallbacks.register(c);
397         } catch (RemoteException e) {
398             Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
399         }
400     }
401 
402     /**
403      * Not final for testing, but shouldn't be extended!
404      * @hide
405      */
406     @VisibleForTesting
removeImsFeatureStatusCallback(@onNull IImsFeatureStatusCallback c)407     public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
408         mStatusCallbacks.unregister(c);
409     }
410 
411     /**
412      * Internal method called by ImsFeature when setFeatureState has changed.
413      */
notifyFeatureState(@msState int state)414     private void notifyFeatureState(@ImsState int state) {
415         mStatusCallbacks.broadcastAction((c) -> {
416             try {
417                 c.notifyImsFeatureStatus(state);
418             } catch (RemoteException e) {
419                 Log.w(LOG_TAG, e + " notifyFeatureState() - Skipping "
420                         + "callback.");
421             }
422         });
423     }
424 
425     /**
426      * @hide
427      */
addCapabilityCallback(IImsCapabilityCallback c)428     public final void addCapabilityCallback(IImsCapabilityCallback c) {
429         mCapabilityCallbacks.register(c);
430         try {
431             // Notify the Capability callback that was just registered of the current capabilities.
432             c.onCapabilitiesStatusChanged(queryCapabilityStatus().mCapabilities);
433         } catch (RemoteException e) {
434             Log.w(LOG_TAG, "addCapabilityCallback: error accessing callback: " + e.getMessage());
435         }
436     }
437 
438     /**
439      * @hide
440      */
removeCapabilityCallback(IImsCapabilityCallback c)441     final void removeCapabilityCallback(IImsCapabilityCallback c) {
442         mCapabilityCallbacks.unregister(c);
443     }
444 
445     /**@hide*/
queryCapabilityConfigurationInternal(int capability, int radioTech, IImsCapabilityCallback c)446     final void queryCapabilityConfigurationInternal(int capability, int radioTech,
447             IImsCapabilityCallback c) {
448         boolean enabled = queryCapabilityConfiguration(capability, radioTech);
449         try {
450             if (c != null) {
451                 c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
452             }
453         } catch (RemoteException e) {
454             Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
455         }
456     }
457 
458     /**
459      * @return the cached capabilities status for this feature.
460      * @hide
461      */
462     @VisibleForTesting
queryCapabilityStatus()463     public Capabilities queryCapabilityStatus() {
464         synchronized (mLock) {
465             return mCapabilityStatus.copy();
466         }
467     }
468 
469     /**
470      * Called internally to request the change of enabled capabilities.
471      * @hide
472      */
473     @VisibleForTesting
requestChangeEnabledCapabilities(CapabilityChangeRequest request, IImsCapabilityCallback c)474     public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
475             IImsCapabilityCallback c) {
476         if (request == null) {
477             throw new IllegalArgumentException(
478                     "ImsFeature#requestChangeEnabledCapabilities called with invalid params.");
479         }
480         changeEnabledCapabilities(request, new CapabilityCallbackProxy(c));
481     }
482 
483     /**
484      * Called by the ImsFeature when the capabilities status has changed.
485      *
486      * @param caps the new {@link Capabilities} status of the {@link ImsFeature}.
487      *
488      * @hide
489      */
notifyCapabilitiesStatusChanged(Capabilities caps)490     protected final void notifyCapabilitiesStatusChanged(Capabilities caps) {
491         synchronized (mLock) {
492             mCapabilityStatus = caps.copy();
493         }
494         mCapabilityCallbacks.broadcastAction((callback) -> {
495             try {
496                 callback.onCapabilitiesStatusChanged(caps.mCapabilities);
497             } catch (RemoteException e) {
498                 Log.w(LOG_TAG, e + " notifyCapabilitiesStatusChanged() - Skipping "
499                         + "callback.");
500             }
501         });
502     }
503 
504     /**
505      * Provides the ImsFeature with the ability to return the framework Capability Configuration
506      * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
507      * includes a capability A to enable or disable, this method should return the correct enabled
508      * status for capability A.
509      * @param capability The capability that we are querying the configuration for.
510      * @return true if the capability is enabled, false otherwise.
511      * @hide
512      */
513     @SuppressWarnings("HiddenAbstractMethod")
queryCapabilityConfiguration(int capability, int radioTech)514     public abstract boolean queryCapabilityConfiguration(int capability, int radioTech);
515 
516     /**
517      * Features should override this method to receive Capability preference change requests from
518      * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
519      * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
520      * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
521      * each failed capability.
522      *
523      * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
524      *     enable/disable.
525      * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
526      * setting a subset of these capabilities fail, using
527      * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
528      */
changeEnabledCapabilities(CapabilityChangeRequest request, CapabilityCallbackProxy c)529     public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
530             CapabilityCallbackProxy c);
531 
532     /**
533      * Called when the framework is removing this feature and it needs to be cleaned up.
534      */
onFeatureRemoved()535     public abstract void onFeatureRemoved();
536 
537     /**
538      * Called after this ImsFeature has been initialized and has been set to the
539      * {@link ImsState#STATE_READY} state.
540      * <p>
541      * Any attempt by this feature to access the framework before this method is called will return
542      * with an {@link IllegalStateException}.
543      * The IMS provider should use this method to trigger registration for this feature on the IMS
544      * network, if needed.
545      */
onFeatureReady()546     public abstract void onFeatureReady();
547 
548     /**
549      * @return Binder instance that the framework will use to communicate with this feature.
550      * @hide
551      */
552     @SuppressWarnings("HiddenAbstractMethod")
getBinder()553     protected abstract IInterface getBinder();
554 }
555