• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.content.Context;
21 import android.content.Intent;
22 import android.os.RemoteException;
23 import android.telephony.SubscriptionManager;
24 import android.util.Log;
25 
26 import com.android.ims.internal.IImsFeatureStatusCallback;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.Iterator;
33 import java.util.List;
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     /**
46      * Action to broadcast when ImsService is up.
47      * Internal use only.
48      * Only defined here separately compatibility purposes with the old ImsService.
49      * @hide
50      */
51     public static final String ACTION_IMS_SERVICE_UP =
52             "com.android.ims.IMS_SERVICE_UP";
53 
54     /**
55      * Action to broadcast when ImsService is down.
56      * Internal use only.
57      * Only defined here separately for compatibility purposes with the old ImsService.
58      * @hide
59      */
60     public static final String ACTION_IMS_SERVICE_DOWN =
61             "com.android.ims.IMS_SERVICE_DOWN";
62 
63     /**
64      * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
65      * A long value; the phone ID corresponding to the IMS service coming up or down.
66      * Only defined here separately for compatibility purposes with the old ImsService.
67      * @hide
68      */
69     public static final String EXTRA_PHONE_ID = "android:phone_id";
70 
71     // Invalid feature value
72     public static final int INVALID = -1;
73     // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
74     // defined values in ImsServiceClass for compatibility purposes.
75     public static final int EMERGENCY_MMTEL = 0;
76     public static final int MMTEL = 1;
77     public static final int RCS = 2;
78     // Total number of features defined
79     public static final int MAX = 3;
80 
81     // Integer values defining the state of the ImsFeature at any time.
82     @IntDef(flag = true,
83             value = {
84                     STATE_NOT_AVAILABLE,
85                     STATE_INITIALIZING,
86                     STATE_READY,
87             })
88     @Retention(RetentionPolicy.SOURCE)
89     public @interface ImsState {}
90     public static final int STATE_NOT_AVAILABLE = 0;
91     public static final int STATE_INITIALIZING = 1;
92     public static final int STATE_READY = 2;
93 
94     private List<INotifyFeatureRemoved> mRemovedListeners = new ArrayList<>();
95     private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
96             new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
97     private @ImsState int mState = STATE_NOT_AVAILABLE;
98     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
99     private Context mContext;
100 
101     public interface INotifyFeatureRemoved {
onFeatureRemoved(int slotId)102         void onFeatureRemoved(int slotId);
103     }
104 
setContext(Context context)105     public void setContext(Context context) {
106         mContext = context;
107     }
108 
setSlotId(int slotId)109     public void setSlotId(int slotId) {
110         mSlotId = slotId;
111     }
112 
addFeatureRemovedListener(INotifyFeatureRemoved listener)113     public void addFeatureRemovedListener(INotifyFeatureRemoved listener) {
114         synchronized (mRemovedListeners) {
115             mRemovedListeners.add(listener);
116         }
117     }
118 
removeFeatureRemovedListener(INotifyFeatureRemoved listener)119     public void removeFeatureRemovedListener(INotifyFeatureRemoved listener) {
120         synchronized (mRemovedListeners) {
121             mRemovedListeners.remove(listener);
122         }
123     }
124 
125     // Not final for testing.
notifyFeatureRemoved(int slotId)126     public void notifyFeatureRemoved(int slotId) {
127         synchronized (mRemovedListeners) {
128             mRemovedListeners.forEach(l -> l.onFeatureRemoved(slotId));
129             onFeatureRemoved();
130         }
131     }
132 
getFeatureState()133     public int getFeatureState() {
134         return mState;
135     }
136 
setFeatureState(@msState int state)137     protected final void setFeatureState(@ImsState int state) {
138         if (mState != state) {
139             mState = state;
140             notifyFeatureState(state);
141         }
142     }
143 
addImsFeatureStatusCallback(IImsFeatureStatusCallback c)144     public void addImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
145         if (c == null) {
146             return;
147         }
148         try {
149             // If we have just connected, send queued status.
150             c.notifyImsFeatureStatus(mState);
151             // Add the callback if the callback completes successfully without a RemoteException.
152             synchronized (mStatusCallbacks) {
153                 mStatusCallbacks.add(c);
154             }
155         } catch (RemoteException e) {
156             Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
157         }
158     }
159 
removeImsFeatureStatusCallback(IImsFeatureStatusCallback c)160     public void removeImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
161         if (c == null) {
162             return;
163         }
164         synchronized (mStatusCallbacks) {
165             mStatusCallbacks.remove(c);
166         }
167     }
168 
169     /**
170      * Internal method called by ImsFeature when setFeatureState has changed.
171      * @param state
172      */
notifyFeatureState(@msState int state)173     private void notifyFeatureState(@ImsState int state) {
174         synchronized (mStatusCallbacks) {
175             for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
176                  iter.hasNext(); ) {
177                 IImsFeatureStatusCallback callback = iter.next();
178                 try {
179                     Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
180                     callback.notifyImsFeatureStatus(state);
181                 } catch (RemoteException e) {
182                     // remove if the callback is no longer alive.
183                     iter.remove();
184                     Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
185                 }
186             }
187         }
188         sendImsServiceIntent(state);
189     }
190 
191     /**
192      * Provide backwards compatibility using deprecated service UP/DOWN intents.
193      */
sendImsServiceIntent(@msState int state)194     private void sendImsServiceIntent(@ImsState int state) {
195         if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
196             return;
197         }
198         Intent intent;
199         switch (state) {
200             case ImsFeature.STATE_NOT_AVAILABLE:
201             case ImsFeature.STATE_INITIALIZING:
202                 intent = new Intent(ACTION_IMS_SERVICE_DOWN);
203                 break;
204             case ImsFeature.STATE_READY:
205                 intent = new Intent(ACTION_IMS_SERVICE_UP);
206                 break;
207             default:
208                 intent = new Intent(ACTION_IMS_SERVICE_DOWN);
209         }
210         intent.putExtra(EXTRA_PHONE_ID, mSlotId);
211         mContext.sendBroadcast(intent);
212     }
213 
214     /**
215      * Called when the feature is being removed and must be cleaned up.
216      */
onFeatureRemoved()217     public abstract void onFeatureRemoved();
218 }
219