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.compat.feature;
18 
19 import android.annotation.IntDef;
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.content.Context;
22 import android.os.Build;
23 import android.os.IInterface;
24 import android.os.RemoteException;
25 import android.telephony.SubscriptionManager;
26 import android.util.Log;
27 
28 import com.android.ims.internal.IImsFeatureStatusCallback;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.util.Collections;
33 import java.util.Iterator;
34 import java.util.Set;
35 import java.util.WeakHashMap;
36 
37 /**
38  * Base class for all IMS features that are supported by the framework.
39  * @hide
40  */
41 public abstract class ImsFeature {
42 
43     private static final String LOG_TAG = "ImsFeature";
44 
45     // Invalid feature value
46     public static final int INVALID = -1;
47     // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
48     // defined values in ImsServiceClass for compatibility purposes.
49     public static final int EMERGENCY_MMTEL = 0;
50     public static final int MMTEL = 1;
51     public static final int RCS = 2;
52     // Total number of features defined
53     public static final int MAX = 3;
54 
55     // Integer values defining the state of the ImsFeature at any time.
56     @IntDef(flag = true,
57             value = {
58                     STATE_NOT_AVAILABLE,
59                     STATE_INITIALIZING,
60                     STATE_READY,
61             })
62     @Retention(RetentionPolicy.SOURCE)
63     public @interface ImsState {}
64     public static final int STATE_NOT_AVAILABLE = 0;
65     public static final int STATE_INITIALIZING = 1;
66     public static final int STATE_READY = 2;
67 
68     private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
69             new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
70     private @ImsState int mState = STATE_NOT_AVAILABLE;
71     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
72     protected Context mContext;
73 
setContext(Context context)74     public void setContext(Context context) {
75         mContext = context;
76     }
77 
setSlotId(int slotId)78     public void setSlotId(int slotId) {
79         mSlotId = slotId;
80     }
81 
82     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getFeatureState()83     public int getFeatureState() {
84         return mState;
85     }
86 
87     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
setFeatureState(@msState int state)88     protected final void setFeatureState(@ImsState int state) {
89         if (mState != state) {
90             mState = state;
91             notifyFeatureState(state);
92         }
93     }
94 
addImsFeatureStatusCallback(IImsFeatureStatusCallback c)95     public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
96         if (c == null) {
97             return;
98         }
99         try {
100             // If we have just connected, send queued status.
101             c.notifyImsFeatureStatus(mState);
102             // Add the callback if the callback completes successfully without a RemoteException.
103             synchronized (mStatusCallbacks) {
104                 mStatusCallbacks.add(c);
105             }
106         } catch (RemoteException e) {
107             Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
108         }
109     }
110 
removeImsFeatureStatusCallback(IImsFeatureStatusCallback c)111     public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
112         if (c == null) {
113             return;
114         }
115         synchronized (mStatusCallbacks) {
116             mStatusCallbacks.remove(c);
117         }
118     }
119 
120     /**
121      * Internal method called by ImsFeature when setFeatureState has changed.
122      * @param state
123      */
notifyFeatureState(@msState int state)124     private void notifyFeatureState(@ImsState int state) {
125         synchronized (mStatusCallbacks) {
126             for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
127                  iter.hasNext(); ) {
128                 IImsFeatureStatusCallback callback = iter.next();
129                 try {
130                     Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
131                     callback.notifyImsFeatureStatus(state);
132                 } catch (RemoteException e) {
133                     // remove if the callback is no longer alive.
134                     iter.remove();
135                     Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
136                 }
137             }
138         }
139     }
140 
141     /**
142      * Called when the feature is ready to use.
143      */
onFeatureReady()144     public abstract void onFeatureReady();
145 
146     /**
147      * Called when the feature is being removed and must be cleaned up.
148      */
onFeatureRemoved()149     public abstract void onFeatureRemoved();
150 
151     /**
152      * @return Binder instance
153      */
getBinder()154     public abstract IInterface getBinder();
155 }
156