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