blob: e5779b315c933e1d28fa838d60f2d38e7468681e [file] [log] [blame]
Alan Viverette3da604b2020-06-10 18:34:39 +00001/*
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
17package android.telephony.ims.feature;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.annotation.SystemApi;
22import android.annotation.TestApi;
23import android.content.Context;
24import android.os.IInterface;
25import android.os.RemoteException;
26import android.telephony.SubscriptionManager;
27import android.telephony.ims.aidl.IImsCapabilityCallback;
28import android.telephony.ims.stub.ImsRegistrationImplBase;
29import android.util.Log;
30
31import com.android.ims.internal.IImsFeatureStatusCallback;
32import com.android.internal.annotations.VisibleForTesting;
33import com.android.internal.telephony.util.RemoteCallbackListExt;
34
35import java.lang.annotation.Retention;
36import java.lang.annotation.RetentionPolicy;
37import java.util.HashMap;
38import 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@TestApi
48public abstract class ImsFeature {
49
50 private static final String LOG_TAG = "ImsFeature";
51
52 /**
53 * Invalid feature value
54 * @hide
55 */
56 public static final int FEATURE_INVALID = -1;
57 // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
58 // defined values in ImsServiceClass for compatibility purposes.
59 /**
60 * This feature supports emergency calling over MMTEL. If defined, the framework will try to
61 * place an emergency call over IMS first. If it is not defined, the framework will only use
62 * CSFB for emergency calling.
63 * @hide
64 */
65 @SystemApi @TestApi
66 public static final int FEATURE_EMERGENCY_MMTEL = 0;
67 /**
68 * This feature supports the MMTEL feature.
69 * @hide
70 */
71 @SystemApi @TestApi
72 public static final int FEATURE_MMTEL = 1;
73 /**
74 * This feature supports the RCS feature.
75 * @hide
76 */
77 @SystemApi @TestApi
78 public static final int FEATURE_RCS = 2;
79 /**
80 * Total number of features defined
81 * @hide
82 */
83 public static final int FEATURE_MAX = 3;
84
85 /**
86 * Used for logging purposes.
87 * @hide
88 */
89 public static final Map<Integer, String> FEATURE_LOG_MAP = new HashMap<Integer, String>() {{
90 put(FEATURE_EMERGENCY_MMTEL, "EMERGENCY_MMTEL");
91 put(FEATURE_MMTEL, "MMTEL");
92 put(FEATURE_RCS, "RCS");
93 }};
94
95 /**
96 * Integer values defining IMS features that are supported in ImsFeature.
97 * @hide
98 */
99 @IntDef(flag = true,
100 value = {
101 FEATURE_EMERGENCY_MMTEL,
102 FEATURE_MMTEL,
103 FEATURE_RCS
104 })
105 @Retention(RetentionPolicy.SOURCE)
106 public @interface FeatureType {}
107
108 /**
109 * Integer values defining the state of the ImsFeature at any time.
110 * @hide
111 */
112 @IntDef(flag = true,
113 value = {
114 STATE_UNAVAILABLE,
115 STATE_INITIALIZING,
116 STATE_READY,
117 })
118 @Retention(RetentionPolicy.SOURCE)
119 public @interface ImsState {}
120
121 /**
122 * This {@link ImsFeature}'s state is unavailable and should not be communicated with. This will
123 * remove all bindings back to the framework. Any attempt to communicate with the framework
124 * during this time will result in an {@link IllegalStateException}.
125 * @hide
126 */
127 @SystemApi @TestApi
128 public static final int STATE_UNAVAILABLE = 0;
129 /**
130 * This {@link ImsFeature} state is initializing and should not be communicated with. This will
131 * remove all bindings back to the framework. Any attempt to communicate with the framework
132 * during this time will result in an {@link IllegalStateException}.
133 * @hide
134 */
135 @SystemApi @TestApi
136 public static final int STATE_INITIALIZING = 1;
137 /**
138 * This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods
139 * until {@see #onFeatureReady()} is called.
140 * @hide
141 */
142 @SystemApi @TestApi
143 public static final int STATE_READY = 2;
144
145 /**
146 * Used for logging purposes.
147 * @hide
148 */
149 public static final Map<Integer, String> STATE_LOG_MAP = new HashMap<Integer, String>() {{
150 put(STATE_UNAVAILABLE, "UNAVAILABLE");
151 put(STATE_INITIALIZING, "INITIALIZING");
152 put(STATE_READY, "READY");
153 }};
154
155 /**
156 * Integer values defining the result codes that should be returned from
157 * {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability.
158 * @hide
159 */
160 @IntDef(flag = true,
161 value = {
162 CAPABILITY_ERROR_GENERIC,
163 CAPABILITY_SUCCESS
164 })
165 @Retention(RetentionPolicy.SOURCE)
166 public @interface ImsCapabilityError {}
167
168 /**
169 * The capability was unable to be changed.
170 * @hide
171 */
172 @SystemApi @TestApi
173 public static final int CAPABILITY_ERROR_GENERIC = -1;
174 /**
175 * The capability was able to be changed.
176 * @hide
177 */
178 @SystemApi @TestApi
179 public static final int CAPABILITY_SUCCESS = 0;
180
181 /**
182 * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
183 * provided.
184 */
185 protected static class CapabilityCallbackProxy {
186 private final IImsCapabilityCallback mCallback;
187
188 /** @hide */
189 public CapabilityCallbackProxy(IImsCapabilityCallback c) {
190 mCallback = c;
191 }
192
193 /**
194 * This method notifies the provided framework callback that the request to change the
195 * indicated capability has failed and has not changed.
196 *
197 * @param capability The Capability that will be notified to the framework, defined as
198 * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
199 * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
200 * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
201 * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}.
202 * @param radioTech The radio tech that this capability failed for, defined as
203 * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
204 * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
205 * @param reason The reason this capability was unable to be changed, defined as
206 * {@link #CAPABILITY_ERROR_GENERIC} or {@link #CAPABILITY_SUCCESS}.
207 */
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 */
242 public Capabilities() {
243 }
244
245 /**
246 * @hide
247 */
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 */
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 */
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 */
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 */
282 public Capabilities copy() {
283 return new Capabilities(mCapabilities);
284 }
285
286 /**
287 * @return a bitmask containing the capability flags directly.
288 * @hide
289 */
290 public int getMask() {
291 return mCapabilities;
292 }
293
294 /**
295 * @hide
296 */
297 @Override
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
311 public int hashCode() {
312 return mCapabilities;
313 }
314
315 /**
316 * @hide
317 */
318 @Override
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 */
340 public final 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 @TestApi
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 @TestApi
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 @TestApi
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
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
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 */
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 */
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 */
441 final void removeCapabilityCallback(IImsCapabilityCallback c) {
442 mCapabilityCallbacks.unregister(c);
443 }
444
445 /**@hide*/
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
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
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 */
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 public abstract boolean queryCapabilityConfiguration(int capability, int radioTech);
514
515 /**
516 * Features should override this method to receive Capability preference change requests from
517 * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
518 * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
519 * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
520 * each failed capability.
521 *
522 * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
523 * enable/disable.
524 * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
525 * setting a subset of these capabilities fail, using
526 * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
527 */
528 public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
529 CapabilityCallbackProxy c);
530
531 /**
532 * Called when the framework is removing this feature and it needs to be cleaned up.
533 */
534 public abstract void onFeatureRemoved();
535
536 /**
537 * Called after this ImsFeature has been initialized and has been set to the
538 * {@link ImsState#STATE_READY} state.
539 * <p>
540 * Any attempt by this feature to access the framework before this method is called will return
541 * with an {@link IllegalStateException}.
542 * The IMS provider should use this method to trigger registration for this feature on the IMS
543 * network, if needed.
544 */
545 public abstract void onFeatureReady();
546
547 /**
548 * @return Binder instance that the framework will use to communicate with this feature.
549 * @hide
550 */
551 protected abstract IInterface getBinder();
552}