• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 com.android.services.telephony.rcs;
18 
19 import android.annotation.AnyThread;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.os.AsyncResult;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.os.Looper;
28 import android.telephony.CarrierConfigManager;
29 import android.telephony.SubscriptionManager;
30 import android.util.Log;
31 import android.util.SparseArray;
32 
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.internal.telephony.PhoneConfigurationManager;
35 import com.android.internal.telephony.metrics.RcsStats;
36 import com.android.internal.util.IndentingPrintWriter;
37 import com.android.phone.ImsStateCallbackController;
38 import com.android.phone.R;
39 
40 import java.io.FileDescriptor;
41 import java.io.PrintWriter;
42 
43 /**
44  * Singleton service setup to manage RCS related services that the platform provides such as User
45  * Capability Exchange.
46  */
47 @AnyThread
48 public class TelephonyRcsService {
49 
50     private static final String LOG_TAG = "TelephonyRcsService";
51 
52     /**
53      * Used to inject RcsFeatureController and UceController instances for testing.
54      */
55     @VisibleForTesting
56     public interface FeatureFactory {
57         /**
58          * @return an {@link RcsFeatureController} associated with the slot specified.
59          */
createController(Context context, int slotId, int subId)60         RcsFeatureController createController(Context context, int slotId, int subId);
61 
62         /**
63          * @return an instance of {@link UceControllerManager} associated with the slot specified.
64          */
createUceControllerManager(Context context, int slotId, int subId)65         UceControllerManager createUceControllerManager(Context context, int slotId, int subId);
66 
67         /**
68          * @return an instance of {@link SipTransportController} for the slot and subscription
69          * specified.
70          */
createSipTransportController(Context context, int slotId, int subId)71         SipTransportController createSipTransportController(Context context, int slotId, int subId);
72     }
73 
74     private FeatureFactory mFeatureFactory = new FeatureFactory() {
75         @Override
76         public RcsFeatureController createController(Context context, int slotId, int subId) {
77             return new RcsFeatureController(context, slotId, subId);
78         }
79 
80         @Override
81         public UceControllerManager createUceControllerManager(Context context, int slotId,
82                 int subId) {
83             return new UceControllerManager(context, slotId, subId);
84         }
85 
86         @Override
87         public SipTransportController createSipTransportController(Context context, int slotId,
88                 int subId) {
89             return new SipTransportController(context, slotId, subId);
90         }
91     };
92 
93     /**
94      * Used to inject device resource for testing.
95      */
96     @VisibleForTesting
97     public interface ResourceProxy {
98         /**
99          * @return an whether the device supports User Capability Exchange.
100          */
getDeviceUceEnabled(Context context)101         boolean getDeviceUceEnabled(Context context);
102     }
103 
104     private static ResourceProxy sResourceProxy = context -> {
105         return context.getResources().getBoolean(
106                 R.bool.config_rcs_user_capability_exchange_enabled);
107     };
108 
109     // Notifies this service that there has been a change in available slots.
110     private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 1;
111 
112     private final Context mContext;
113     private final Object mLock = new Object();
114     private int mNumSlots;
115 
116     // Maps slot ID -> RcsFeatureController.
117     private SparseArray<RcsFeatureController> mFeatureControllers;
118     // Maps slotId -> associatedSubIds
119     private SparseArray<Integer> mSlotToAssociatedSubIds;
120 
121     // Whether the device supports User Capability Exchange
122     private boolean mRcsUceEnabled;
123 
124     private BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() {
125         @Override
126         public void onReceive(Context context, Intent intent) {
127             if (intent == null) {
128                 return;
129             }
130             if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
131                 Bundle bundle = intent.getExtras();
132                 if (bundle == null) {
133                     return;
134                 }
135                 int slotId = bundle.getInt(CarrierConfigManager.EXTRA_SLOT_INDEX,
136                         SubscriptionManager.INVALID_PHONE_INDEX);
137                 int subId = bundle.getInt(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
138                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
139                 onCarrierConfigChangedForSlot(slotId, subId);
140             }
141         }
142     };
143 
144     private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> {
145         switch (msg.what) {
146             case HANDLER_MSIM_CONFIGURATION_CHANGE: {
147                 AsyncResult result = (AsyncResult) msg.obj;
148                 Integer numSlots = (Integer) result.result;
149                 if (numSlots == null) {
150                     Log.w(LOG_TAG, "msim config change with null num slots.");
151                     break;
152                 }
153                 updateFeatureControllerSize(numSlots);
154                 break;
155             }
156             default:
157                 return false;
158         }
159         return true;
160     });
161 
TelephonyRcsService(Context context, int numSlots)162     public TelephonyRcsService(Context context, int numSlots) {
163         mContext = context;
164         mNumSlots = numSlots;
165         mFeatureControllers = new SparseArray<>(numSlots);
166         mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
167         mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
168         RcsStats.getInstance().registerUceCallback();
169     }
170 
171     @VisibleForTesting
TelephonyRcsService(Context context, int numSlots, ResourceProxy resourceProxy)172     public TelephonyRcsService(Context context, int numSlots, ResourceProxy resourceProxy) {
173         mContext = context;
174         mNumSlots = numSlots;
175         mFeatureControllers = new SparseArray<>(numSlots);
176         mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
177         sResourceProxy = resourceProxy;
178         mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
179         RcsStats.getInstance().registerUceCallback();
180     }
181 
182     /**
183      * @return the {@link RcsFeatureController} associated with the given slot.
184      */
getFeatureController(int slotId)185     public RcsFeatureController getFeatureController(int slotId) {
186         synchronized (mLock) {
187             return mFeatureControllers.get(slotId);
188         }
189     }
190 
191     /**
192      * Called after instance creation to initialize internal structures as well as register for
193      * system callbacks.
194      */
initialize()195     public void initialize() {
196         updateFeatureControllerSize(mNumSlots);
197 
198         PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
199                 HANDLER_MSIM_CONFIGURATION_CHANGE, null);
200         mContext.registerReceiver(mCarrierConfigChangedReceiver, new IntentFilter(
201                 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
202     }
203 
204     @VisibleForTesting
setFeatureFactory(FeatureFactory f)205     public void setFeatureFactory(FeatureFactory f) {
206         mFeatureFactory = f;
207     }
208 
209     /**
210      * Update the number of {@link RcsFeatureController}s that are created based on the number of
211      * active slots on the device.
212      */
213     @VisibleForTesting
updateFeatureControllerSize(int newNumSlots)214     public void updateFeatureControllerSize(int newNumSlots) {
215         synchronized (mLock) {
216             int oldNumSlots = mFeatureControllers.size();
217             if (oldNumSlots == newNumSlots) {
218                 return;
219             }
220             Log.i(LOG_TAG, "updateFeatureControllers: oldSlots=" + oldNumSlots + ", newNumSlots="
221                     + newNumSlots);
222             mNumSlots = newNumSlots;
223             if (oldNumSlots < newNumSlots) {
224                 for (int i = oldNumSlots; i < newNumSlots; i++) {
225                     RcsFeatureController c = constructFeatureController(i);
226                     // Do not add feature controllers for inactive subscriptions
227                     if (c.hasActiveFeatures()) {
228                         mFeatureControllers.put(i, c);
229                         // Do not change mSlotToAssociatedSubIds, it will be updated upon carrier
230                         // config change.
231                     }
232                 }
233             } else {
234                 for (int i = (oldNumSlots - 1); i > (newNumSlots - 1); i--) {
235                     RcsFeatureController c = mFeatureControllers.get(i);
236                     if (c != null) {
237                         mFeatureControllers.remove(i);
238                         mSlotToAssociatedSubIds.remove(i);
239                         c.destroy();
240                     }
241                 }
242             }
243         }
244     }
245 
246     /**
247      * ACTION_CARRIER_CONFIG_CHANGED was received by this service for a specific slot.
248      * @param slotId The slotId associated with the event.
249      * @param subId The subId associated with the event. May cause the subId associated with the
250      *              RcsFeatureController to change if the subscription itself has changed.
251      */
onCarrierConfigChangedForSlot(int slotId, int subId)252     private void onCarrierConfigChangedForSlot(int slotId, int subId) {
253         synchronized (mLock) {
254             RcsFeatureController f = mFeatureControllers.get(slotId);
255             final int oldSubId = mSlotToAssociatedSubIds.get(slotId,
256                     SubscriptionManager.INVALID_SUBSCRIPTION_ID);
257             mSlotToAssociatedSubIds.put(slotId, subId);
258             Log.i(LOG_TAG, "updateFeatureControllerSubscription: slotId=" + slotId
259                     + ", oldSubId= " + oldSubId + ", subId=" + subId + ", existing feature="
260                     + (f != null));
261             if (SubscriptionManager.isValidSubscriptionId(subId)) {
262                 if (f == null) {
263                     // A controller doesn't exist for this slot yet.
264                     f = mFeatureFactory.createController(mContext, slotId, subId);
265                     updateSupportedFeatures(f, slotId, subId);
266                     if (f.hasActiveFeatures()) mFeatureControllers.put(slotId, f);
267                 } else {
268                     updateSupportedFeatures(f, slotId, subId);
269                     // Do not keep an empty container around.
270                     if (!f.hasActiveFeatures()) {
271                         f.destroy();
272                         mFeatureControllers.remove(slotId);
273                     }
274                 }
275             }
276             if (f != null) {
277                 if (oldSubId == subId) {
278                     f.onCarrierConfigChangedForSubscription();
279                 } else {
280                     f.updateAssociatedSubscription(subId);
281                 }
282             }
283         }
284     }
285 
constructFeatureController(int slotId)286     private RcsFeatureController constructFeatureController(int slotId) {
287         int subId = getSubscriptionFromSlot(slotId);
288         RcsFeatureController c = mFeatureFactory.createController(mContext, slotId, subId);
289         updateSupportedFeatures(c, slotId, subId);
290         return c;
291     }
292 
updateSupportedFeatures(RcsFeatureController c, int slotId, int subId)293     private void updateSupportedFeatures(RcsFeatureController c, int slotId, int subId) {
294         if (isDeviceUceEnabled() && doesSubscriptionSupportPresence(subId)) {
295             if (c.getFeature(UceControllerManager.class) == null) {
296                 c.addFeature(mFeatureFactory.createUceControllerManager(mContext, slotId, subId),
297                         UceControllerManager.class);
298             }
299         } else {
300             if (c.getFeature(UceControllerManager.class) != null) {
301                 c.removeFeature(UceControllerManager.class);
302             }
303         }
304 
305         if (doesSubscriptionSupportSingleRegistration(subId)) {
306             if (c.getFeature(SipTransportController.class) == null) {
307                 c.addFeature(mFeatureFactory.createSipTransportController(mContext, slotId, subId),
308                         SipTransportController.class);
309             }
310         } else {
311             if (c.getFeature(SipTransportController.class) != null) {
312                 c.removeFeature(SipTransportController.class);
313             }
314         }
315         // Only start the connection procedure if we have active features.
316         if (c.hasActiveFeatures()) c.connect();
317 
318         ImsStateCallbackController.getInstance()
319                 .notifyExternalRcsStateChanged(slotId, false, c.hasActiveFeatures());
320     }
321 
322     /**
323      * Get whether the device supports RCS User Capability Exchange or not.
324      */
isDeviceUceEnabled()325     public boolean isDeviceUceEnabled() {
326         return mRcsUceEnabled;
327     }
328 
329     /**
330      * Set the device supports RCS User Capability Exchange.
331      */
setDeviceUceEnabled(boolean isEnabled)332     public void setDeviceUceEnabled(boolean isEnabled) {
333         mRcsUceEnabled = isEnabled;
334     }
335 
doesSubscriptionSupportPresence(int subId)336     private boolean doesSubscriptionSupportPresence(int subId) {
337         if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
338         CarrierConfigManager carrierConfigManager =
339                 mContext.getSystemService(CarrierConfigManager.class);
340         if (carrierConfigManager == null) return false;
341         boolean supportsUce = carrierConfigManager.getConfigForSubId(subId).getBoolean(
342                 CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL);
343         supportsUce |= carrierConfigManager.getConfigForSubId(subId).getBoolean(
344                 CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL);
345         return supportsUce;
346     }
347 
doesSubscriptionSupportSingleRegistration(int subId)348     private boolean doesSubscriptionSupportSingleRegistration(int subId) {
349         if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
350         CarrierConfigManager carrierConfigManager =
351                 mContext.getSystemService(CarrierConfigManager.class);
352         if (carrierConfigManager == null) return false;
353         return carrierConfigManager.getConfigForSubId(subId).getBoolean(
354                 CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL);
355     }
356 
getSubscriptionFromSlot(int slotId)357     private int getSubscriptionFromSlot(int slotId) {
358         SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
359         if (manager == null) {
360             Log.w(LOG_TAG, "Couldn't find SubscriptionManager for slotId=" + slotId);
361             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
362         }
363         int[] subIds = manager.getSubscriptionIds(slotId);
364         if (subIds != null && subIds.length > 0) {
365             return subIds[0];
366         }
367         return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
368     }
369 
370     /**
371      * Dump this instance into a readable format for dumpsys usage.
372      */
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)373     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
374         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
375         pw.println("RcsFeatureControllers:");
376         pw.increaseIndent();
377         synchronized (mLock) {
378             for (int i = 0; i < mNumSlots; i++) {
379                 RcsFeatureController f = mFeatureControllers.get(i);
380                 if (f == null) continue;
381                 pw.increaseIndent();
382                 f.dump(fd, printWriter, args);
383                 pw.decreaseIndent();
384             }
385         }
386         pw.decreaseIndent();
387     }
388 }
389