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