• 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.Nullable;
20 import android.content.Context;
21 import android.net.Uri;
22 import android.telephony.SubscriptionManager;
23 import android.telephony.ims.ImsException;
24 import android.telephony.ims.RcsContactUceCapability;
25 import android.telephony.ims.RcsUceAdapter;
26 import android.telephony.ims.RcsUceAdapter.PublishState;
27 import android.telephony.ims.aidl.IRcsUceControllerCallback;
28 import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
29 import android.util.IndentingPrintWriter;
30 import android.util.Log;
31 
32 import com.android.ims.RcsFeatureManager;
33 import com.android.ims.rcs.uce.UceController;
34 import com.android.internal.annotations.VisibleForTesting;
35 
36 import java.io.PrintWriter;
37 import java.util.List;
38 import java.util.Set;
39 import java.util.concurrent.ExecutionException;
40 import java.util.concurrent.ExecutorService;
41 import java.util.concurrent.Executors;
42 import java.util.concurrent.Future;
43 
44 /**
45  * Responsible for managing the creation and destruction of UceController. It also received the
46  * requests from {@link com.android.phone.ImsRcsController} and pass these requests to
47  * {@link UceController}
48  */
49 public class UceControllerManager implements RcsFeatureController.Feature {
50 
51     private static final String LOG_TAG = "UceControllerManager";
52 
53     private final int mSlotId;
54     private final Context mContext;
55     private final ExecutorService mExecutorService;
56 
57     private volatile @Nullable UceController mUceController;
58     private volatile @Nullable RcsFeatureManager mRcsFeatureManager;
59 
UceControllerManager(Context context, int slotId, int subId)60     public UceControllerManager(Context context, int slotId, int subId) {
61         Log.d(LOG_TAG, "create: slotId=" + slotId + ", subId=" + subId);
62         mSlotId = slotId;
63         mContext = context;
64         mExecutorService = Executors.newSingleThreadExecutor();
65         initUceController(subId);
66     }
67 
68     /**
69      * Constructor to inject dependencies for testing.
70      */
71     @VisibleForTesting
UceControllerManager(Context context, int slotId, ExecutorService executor, UceController uceController)72     public UceControllerManager(Context context, int slotId, ExecutorService executor,
73             UceController uceController) {
74         mSlotId = slotId;
75         mContext = context;
76         mExecutorService = executor;
77         mUceController = uceController;
78     }
79 
80     @Override
onRcsConnected(RcsFeatureManager manager)81     public void onRcsConnected(RcsFeatureManager manager) {
82         mExecutorService.submit(() -> {
83             mRcsFeatureManager = manager;
84             if (mUceController != null) {
85                 mUceController.onRcsConnected(manager);
86             } else {
87                 Log.d(LOG_TAG, "onRcsConnected: UceController is null");
88             }
89         });
90     }
91 
92     @Override
onRcsDisconnected()93     public void onRcsDisconnected() {
94         mExecutorService.submit(() -> {
95             mRcsFeatureManager = null;
96             if (mUceController != null) {
97                 mUceController.onRcsDisconnected();
98             } else {
99                 Log.d(LOG_TAG, "onRcsDisconnected: UceController is null");
100             }
101         });
102     }
103 
104     @Override
onDestroy()105     public void onDestroy() {
106         mExecutorService.submit(() -> {
107             Log.d(LOG_TAG, "onDestroy");
108             if (mUceController != null) {
109                 mUceController.onDestroy();
110             }
111         });
112         // When the shutdown is called, it will refuse any new tasks and let existing tasks finish.
113         mExecutorService.shutdown();
114     }
115 
116     /**
117      * This method will be called when the subscription ID associated with the slot has
118      * changed.
119      */
120     @Override
onAssociatedSubscriptionUpdated(int newSubId)121     public void onAssociatedSubscriptionUpdated(int newSubId) {
122         mExecutorService.submit(() -> {
123             Log.i(LOG_TAG, "onAssociatedSubscriptionUpdated: slotId=" + mSlotId
124                     + ", newSubId=" + newSubId);
125 
126             // Check and create the UceController with the new updated subscription ID.
127             initUceController(newSubId);
128 
129             // The RCS should be connected when the mRcsFeatureManager is not null. Set it to the
130             // new UceController instance.
131             if (mUceController != null && mRcsFeatureManager != null) {
132                 mUceController.onRcsConnected(mRcsFeatureManager);
133             }
134         });
135     }
136 
137     /**
138      * This method will be called when the carrier config of the subscription associated with this
139      * manager has changed.
140      */
141     @Override
onCarrierConfigChanged()142     public void onCarrierConfigChanged() {
143         mExecutorService.submit(() -> {
144             Log.i(LOG_TAG, "onCarrierConfigChanged");
145             if (mUceController != null) {
146                 mUceController.onCarrierConfigChanged();
147             } else {
148                 Log.d(LOG_TAG, "onCarrierConfigChanged: UceController is null");
149             }
150         });
151     }
152 
153     /**
154      * Request the capabilities for contacts.
155      *
156      * @param contactNumbers A list of numbers that the capabilities are being requested for.
157      * @param c A callback for when the request for capabilities completes.
158      * @throws ImsException if the ImsService connected to this controller is currently down.
159      */
requestCapabilities(List<Uri> contactNumbers, IRcsUceControllerCallback c)160     public void requestCapabilities(List<Uri> contactNumbers, IRcsUceControllerCallback c)
161             throws ImsException {
162         Future future = mExecutorService.submit(() -> {
163             checkUceControllerState();
164             mUceController.requestCapabilities(contactNumbers, c);
165             return true;
166         });
167 
168         try {
169             future.get();
170         } catch (ExecutionException | InterruptedException e) {
171             Log.w(LOG_TAG, "requestCapabilities: " + e);
172             Throwable cause = e.getCause();
173             if (cause instanceof ImsException) {
174                 throw (ImsException) cause;
175             }
176         }
177     }
178 
179     /**
180      * Request the capabilities for the given contact.
181      * @param contactNumber The contact of the capabilities are being requested for.
182      * @param c A callback for when the request for capabilities completes.
183      * @throws ImsException if the ImsService connected to this controller is currently down.
184      */
requestNetworkAvailability(Uri contactNumber, IRcsUceControllerCallback c)185     public void requestNetworkAvailability(Uri contactNumber, IRcsUceControllerCallback c)
186             throws ImsException {
187         Future future = mExecutorService.submit(() -> {
188             checkUceControllerState();
189             mUceController.requestAvailability(contactNumber, c);
190             return true;
191         });
192 
193         try {
194             future.get();
195         } catch (ExecutionException | InterruptedException e) {
196             Log.w(LOG_TAG, "requestNetworkAvailability exception: " + e);
197             Throwable cause = e.getCause();
198             if (cause instanceof ImsException) {
199                 throw (ImsException) cause;
200             }
201         }
202     }
203 
204     /**
205      * Get the UCE publish state.
206      *
207      * @throws ImsException if the ImsService connected to this controller is currently down.
208      */
getUcePublishState()209     public @PublishState int getUcePublishState() throws ImsException {
210         Future<Integer> future = mExecutorService.submit(() -> {
211             checkUceControllerState();
212             return mUceController.getUcePublishState();
213         });
214 
215         try {
216             return future.get();
217         } catch (ExecutionException | InterruptedException e) {
218             Log.w(LOG_TAG, "getUcePublishState exception: " + e);
219             Throwable cause = e.getCause();
220             if (cause instanceof ImsException) {
221                 throw (ImsException) cause;
222             }
223             return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
224         }
225     }
226 
227     /**
228      * Add new feature tags to the Set used to calculate the capabilities in PUBLISH.
229      */
addUceRegistrationOverride( Set<String> featureTags)230     public RcsContactUceCapability addUceRegistrationOverride(
231             Set<String> featureTags) throws ImsException {
232         Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
233             checkUceControllerState();
234             return mUceController.addRegistrationOverrideCapabilities(featureTags);
235         });
236 
237         try {
238             return future.get();
239         } catch (ExecutionException | InterruptedException e) {
240             Log.w(LOG_TAG, "addUceRegistrationOverride exception: " + e);
241             Throwable cause = e.getCause();
242             if (cause instanceof ImsException) {
243                 throw (ImsException) cause;
244             }
245             return null;
246         }
247     }
248 
249     /**
250      * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH.
251      */
removeUceRegistrationOverride( Set<String> featureTags)252     public RcsContactUceCapability removeUceRegistrationOverride(
253             Set<String> featureTags) throws ImsException {
254         Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
255             checkUceControllerState();
256             return mUceController.removeRegistrationOverrideCapabilities(featureTags);
257         });
258 
259         try {
260             return future.get();
261         } catch (ExecutionException | InterruptedException e) {
262             Log.w(LOG_TAG, "removeUceRegistrationOverride exception: " + e);
263             Throwable cause = e.getCause();
264             if (cause instanceof ImsException) {
265                 throw (ImsException) cause;
266             }
267             return null;
268         }
269     }
270 
271     /**
272      * Clear all overrides in the Set used to calculate the capabilities in PUBLISH.
273      */
clearUceRegistrationOverride()274     public RcsContactUceCapability clearUceRegistrationOverride() throws ImsException {
275         Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
276             checkUceControllerState();
277             return mUceController.clearRegistrationOverrideCapabilities();
278         });
279 
280         try {
281             return future.get();
282         } catch (ExecutionException | InterruptedException e) {
283             Log.w(LOG_TAG, "clearUceRegistrationOverride exception: " + e);
284             Throwable cause = e.getCause();
285             if (cause instanceof ImsException) {
286                 throw (ImsException) cause;
287             }
288             return null;
289         }
290     }
291 
292     /**
293      * @return current RcsContactUceCapability instance that will be used for PUBLISH.
294      */
getLatestRcsContactUceCapability()295     public RcsContactUceCapability getLatestRcsContactUceCapability() throws ImsException {
296         Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
297             checkUceControllerState();
298             return mUceController.getLatestRcsContactUceCapability();
299         });
300 
301         try {
302             return future.get();
303         } catch (ExecutionException | InterruptedException e) {
304             Log.w(LOG_TAG, "getLatestRcsContactUceCapability exception: " + e);
305             Throwable cause = e.getCause();
306             if (cause instanceof ImsException) {
307                 throw (ImsException) cause;
308             }
309             return null;
310         }
311     }
312 
313     /**
314      *
315      * @return The last PIDF XML sent to the IMS stack to be published.
316      */
getLastPidfXml()317     public String getLastPidfXml() throws ImsException {
318         Future<String> future = mExecutorService.submit(() -> {
319             checkUceControllerState();
320             return mUceController.getLastPidfXml();
321         });
322 
323         try {
324             return future.get();
325         } catch (ExecutionException | InterruptedException e) {
326             Log.w(LOG_TAG, "getLastPidfXml exception: " + e);
327             Throwable cause = e.getCause();
328             if (cause instanceof ImsException) {
329                 throw (ImsException) cause;
330             }
331             return null;
332         }
333     }
334 
335     /**
336      * Remove UCE requests cannot be sent to the network status.
337      * @return true if this command is successful.
338      */
removeUceRequestDisallowedStatus()339     public boolean removeUceRequestDisallowedStatus() throws ImsException {
340         Future<Boolean> future = mExecutorService.submit(() -> {
341             if (mUceController == null) {
342                 throw new ImsException("UCE controller is null",
343                         ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
344             }
345             mUceController.removeRequestDisallowedStatus();
346             return true;
347         });
348 
349         try {
350             return future.get();
351         } catch (ExecutionException | InterruptedException e) {
352             Log.w(LOG_TAG, "removeUceRequestDisallowedStatus exception: " + e);
353             Throwable cause = e.getCause();
354             if (cause instanceof ImsException) {
355                 throw (ImsException) cause;
356             }
357             return false;
358         }
359     }
360 
361     /**
362      * Set the timeout for contact capabilities request.
363      * @param timeoutAfterMs How long when the capabilities request will time up.
364      * @return true if this command is successful.
365      */
setCapabilitiesRequestTimeout(long timeoutAfterMs)366     public boolean setCapabilitiesRequestTimeout(long timeoutAfterMs)  throws ImsException {
367         Future<Boolean> future = mExecutorService.submit(() -> {
368             if (mUceController == null) {
369                 throw new ImsException("UCE controller is null",
370                         ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
371             }
372             mUceController.setCapabilitiesRequestTimeout(timeoutAfterMs);
373             return true;
374         });
375 
376         try {
377             return future.get();
378         } catch (ExecutionException | InterruptedException e) {
379             Log.w(LOG_TAG, "setCapabilitiesRequestTimeout exception: " + e);
380             Throwable cause = e.getCause();
381             if (cause instanceof ImsException) {
382                 throw (ImsException) cause;
383             }
384             return false;
385         }
386     }
387 
388     /**
389      * Register the Publish state changed callback.
390      *
391      * @throws ImsException if the ImsService connected to this controller is currently down.
392      */
registerPublishStateCallback(IRcsUcePublishStateCallback c)393     public void registerPublishStateCallback(IRcsUcePublishStateCallback c) throws ImsException {
394         Future future = mExecutorService.submit(() -> {
395             checkUceControllerState();
396             mUceController.registerPublishStateCallback(c);
397             return true;
398         });
399 
400         try {
401             future.get();
402         } catch (ExecutionException | InterruptedException e) {
403             Log.w(LOG_TAG, "registerPublishStateCallback exception: " + e);
404             Throwable cause = e.getCause();
405             if (cause instanceof ImsException) {
406                 throw (ImsException) cause;
407             }
408         }
409     }
410 
411     /**
412      * Unregister the existing publish state changed callback.
413      */
unregisterPublishStateCallback(IRcsUcePublishStateCallback c)414     public void unregisterPublishStateCallback(IRcsUcePublishStateCallback c) {
415         Future future = mExecutorService.submit(() -> {
416             if (checkUceControllerState()) {
417                 mUceController.unregisterPublishStateCallback(c);
418             }
419             return true;
420         });
421 
422         try {
423             future.get();
424         } catch (ExecutionException | InterruptedException e) {
425             Log.w(LOG_TAG, "unregisterPublishStateCallback exception: " + e);
426         }
427     }
428 
429     /**
430      * Initialize the UceController instance associated with the given subscription ID.
431      * The existing UceController will be destroyed if the original subscription ID is different
432      * from the new subscription ID.
433      * If the new subscription ID is invalid, the UceController instance will be null.
434      */
initUceController(int newSubId)435     private void initUceController(int newSubId) {
436         Log.d(LOG_TAG, "initUceController: newSubId=" + newSubId + ", current UceController subId="
437                 + ((mUceController == null) ? "null" : mUceController.getSubId()));
438         if (mUceController == null) {
439             // Create new UceController only when the subscription ID is valid.
440             if (SubscriptionManager.isValidSubscriptionId(newSubId)) {
441                 mUceController = new UceController(mContext, newSubId);
442             }
443         } else if (mUceController.getSubId() != newSubId) {
444             // The subscription ID is updated. Remove the old UceController instance.
445             mUceController.onDestroy();
446             mUceController = null;
447             // Create new UceController only when the subscription ID is valid.
448             if (SubscriptionManager.isValidSubscriptionId(newSubId)) {
449                 mUceController = new UceController(mContext, newSubId);
450             }
451         }
452     }
453 
checkUceControllerState()454     private boolean checkUceControllerState() throws ImsException {
455         if (mUceController == null || mUceController.isUnavailable()) {
456             throw new ImsException("UCE controller is unavailable",
457                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
458         }
459         return true;
460     }
461 
462     /**
463      * Get the UceController instance.
464      * <p>
465      * Used for testing ONLY.
466      */
467     @VisibleForTesting
getUceController()468     public UceController getUceController() {
469         return mUceController;
470     }
471 
472     @Override
dump(PrintWriter printWriter)473     public void dump(PrintWriter printWriter) {
474         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
475         pw.println("UceControllerManager" + "[" + mSlotId + "]:");
476         pw.increaseIndent();
477         if (mUceController != null) {
478             mUceController.dump(pw);
479         } else {
480             pw.println("UceController is null.");
481         }
482         pw.decreaseIndent();
483     }
484 }
485