• 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(boolean isSupportPublishingState)209     public @PublishState int getUcePublishState(boolean isSupportPublishingState)
210             throws ImsException {
211         Future<Integer> future = mExecutorService.submit(() -> {
212             checkUceControllerState();
213             return mUceController.getUcePublishState(isSupportPublishingState);
214         });
215 
216         try {
217             return future.get();
218         } catch (ExecutionException | InterruptedException e) {
219             Log.w(LOG_TAG, "getUcePublishState exception: " + e);
220             Throwable cause = e.getCause();
221             if (cause instanceof ImsException) {
222                 throw (ImsException) cause;
223             }
224             return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
225         }
226     }
227 
228     /**
229      * Add new feature tags to the Set used to calculate the capabilities in PUBLISH.
230      */
addUceRegistrationOverride( Set<String> featureTags)231     public RcsContactUceCapability addUceRegistrationOverride(
232             Set<String> featureTags) throws ImsException {
233         Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
234             checkUceControllerState();
235             return mUceController.addRegistrationOverrideCapabilities(featureTags);
236         });
237 
238         try {
239             return future.get();
240         } catch (ExecutionException | InterruptedException e) {
241             Log.w(LOG_TAG, "addUceRegistrationOverride exception: " + e);
242             Throwable cause = e.getCause();
243             if (cause instanceof ImsException) {
244                 throw (ImsException) cause;
245             }
246             return null;
247         }
248     }
249 
250     /**
251      * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH.
252      */
removeUceRegistrationOverride( Set<String> featureTags)253     public RcsContactUceCapability removeUceRegistrationOverride(
254             Set<String> featureTags) throws ImsException {
255         Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
256             checkUceControllerState();
257             return mUceController.removeRegistrationOverrideCapabilities(featureTags);
258         });
259 
260         try {
261             return future.get();
262         } catch (ExecutionException | InterruptedException e) {
263             Log.w(LOG_TAG, "removeUceRegistrationOverride exception: " + e);
264             Throwable cause = e.getCause();
265             if (cause instanceof ImsException) {
266                 throw (ImsException) cause;
267             }
268             return null;
269         }
270     }
271 
272     /**
273      * Clear all overrides in the Set used to calculate the capabilities in PUBLISH.
274      */
clearUceRegistrationOverride()275     public RcsContactUceCapability clearUceRegistrationOverride() throws ImsException {
276         Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
277             checkUceControllerState();
278             return mUceController.clearRegistrationOverrideCapabilities();
279         });
280 
281         try {
282             return future.get();
283         } catch (ExecutionException | InterruptedException e) {
284             Log.w(LOG_TAG, "clearUceRegistrationOverride exception: " + e);
285             Throwable cause = e.getCause();
286             if (cause instanceof ImsException) {
287                 throw (ImsException) cause;
288             }
289             return null;
290         }
291     }
292 
293     /**
294      * @return current RcsContactUceCapability instance that will be used for PUBLISH.
295      */
getLatestRcsContactUceCapability()296     public RcsContactUceCapability getLatestRcsContactUceCapability() throws ImsException {
297         Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
298             checkUceControllerState();
299             return mUceController.getLatestRcsContactUceCapability();
300         });
301 
302         try {
303             return future.get();
304         } catch (ExecutionException | InterruptedException e) {
305             Log.w(LOG_TAG, "getLatestRcsContactUceCapability exception: " + e);
306             Throwable cause = e.getCause();
307             if (cause instanceof ImsException) {
308                 throw (ImsException) cause;
309             }
310             return null;
311         }
312     }
313 
314     /**
315      *
316      * @return The last PIDF XML sent to the IMS stack to be published.
317      */
getLastPidfXml()318     public String getLastPidfXml() throws ImsException {
319         Future<String> future = mExecutorService.submit(() -> {
320             checkUceControllerState();
321             return mUceController.getLastPidfXml();
322         });
323 
324         try {
325             return future.get();
326         } catch (ExecutionException | InterruptedException e) {
327             Log.w(LOG_TAG, "getLastPidfXml exception: " + e);
328             Throwable cause = e.getCause();
329             if (cause instanceof ImsException) {
330                 throw (ImsException) cause;
331             }
332             return null;
333         }
334     }
335 
336     /**
337      * Remove UCE requests cannot be sent to the network status.
338      * @return true if this command is successful.
339      */
removeUceRequestDisallowedStatus()340     public boolean removeUceRequestDisallowedStatus() throws ImsException {
341         Future<Boolean> future = mExecutorService.submit(() -> {
342             if (mUceController == null) {
343                 throw new ImsException("UCE controller is null",
344                         ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
345             }
346             mUceController.removeRequestDisallowedStatus();
347             return true;
348         });
349 
350         try {
351             return future.get();
352         } catch (ExecutionException | InterruptedException e) {
353             Log.w(LOG_TAG, "removeUceRequestDisallowedStatus exception: " + e);
354             Throwable cause = e.getCause();
355             if (cause instanceof ImsException) {
356                 throw (ImsException) cause;
357             }
358             return false;
359         }
360     }
361 
362     /**
363      * Set the timeout for contact capabilities request.
364      * @param timeoutAfterMs How long when the capabilities request will time up.
365      * @return true if this command is successful.
366      */
setCapabilitiesRequestTimeout(long timeoutAfterMs)367     public boolean setCapabilitiesRequestTimeout(long timeoutAfterMs)  throws ImsException {
368         Future<Boolean> future = mExecutorService.submit(() -> {
369             if (mUceController == null) {
370                 throw new ImsException("UCE controller is null",
371                         ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
372             }
373             mUceController.setCapabilitiesRequestTimeout(timeoutAfterMs);
374             return true;
375         });
376 
377         try {
378             return future.get();
379         } catch (ExecutionException | InterruptedException e) {
380             Log.w(LOG_TAG, "setCapabilitiesRequestTimeout exception: " + e);
381             Throwable cause = e.getCause();
382             if (cause instanceof ImsException) {
383                 throw (ImsException) cause;
384             }
385             return false;
386         }
387     }
388 
389     /**
390      * Register the Publish state changed callback.
391      *
392      * @throws ImsException if the ImsService connected to this controller is currently down.
393      */
registerPublishStateCallback(IRcsUcePublishStateCallback c, boolean supportPublishingState)394     public void registerPublishStateCallback(IRcsUcePublishStateCallback c,
395             boolean supportPublishingState) throws ImsException {
396         Future future = mExecutorService.submit(() -> {
397             checkUceControllerState();
398             mUceController.registerPublishStateCallback(c, supportPublishingState);
399             return true;
400         });
401 
402         try {
403             future.get();
404         } catch (ExecutionException | InterruptedException e) {
405             Log.w(LOG_TAG, "registerPublishStateCallback exception: " + e);
406             Throwable cause = e.getCause();
407             if (cause instanceof ImsException) {
408                 throw (ImsException) cause;
409             }
410         }
411     }
412 
413     /**
414      * Unregister the existing publish state changed callback.
415      */
unregisterPublishStateCallback(IRcsUcePublishStateCallback c)416     public void unregisterPublishStateCallback(IRcsUcePublishStateCallback c) {
417         Future future = mExecutorService.submit(() -> {
418             if (checkUceControllerState()) {
419                 mUceController.unregisterPublishStateCallback(c);
420             }
421             return true;
422         });
423 
424         try {
425             future.get();
426         } catch (ExecutionException | InterruptedException e) {
427             Log.w(LOG_TAG, "unregisterPublishStateCallback exception: " + e);
428         }
429     }
430 
431     /**
432      * Initialize the UceController instance associated with the given subscription ID.
433      * The existing UceController will be destroyed if the original subscription ID is different
434      * from the new subscription ID.
435      * If the new subscription ID is invalid, the UceController instance will be null.
436      */
initUceController(int newSubId)437     private void initUceController(int newSubId) {
438         Log.d(LOG_TAG, "initUceController: newSubId=" + newSubId + ", current UceController subId="
439                 + ((mUceController == null) ? "null" : mUceController.getSubId()));
440         if (mUceController == null) {
441             // Create new UceController only when the subscription ID is valid.
442             if (SubscriptionManager.isValidSubscriptionId(newSubId)) {
443                 mUceController = new UceController(mContext, newSubId);
444             }
445         } else if (mUceController.getSubId() != newSubId) {
446             // The subscription ID is updated. Remove the old UceController instance.
447             mUceController.onDestroy();
448             mUceController = null;
449             // Create new UceController only when the subscription ID is valid.
450             if (SubscriptionManager.isValidSubscriptionId(newSubId)) {
451                 mUceController = new UceController(mContext, newSubId);
452             }
453         }
454     }
455 
checkUceControllerState()456     private boolean checkUceControllerState() throws ImsException {
457         if (mUceController == null || mUceController.isUnavailable()) {
458             throw new ImsException("UCE controller is unavailable",
459                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
460         }
461         return true;
462     }
463 
464     /**
465      * Get the UceController instance.
466      * <p>
467      * Used for testing ONLY.
468      */
469     @VisibleForTesting
getUceController()470     public UceController getUceController() {
471         return mUceController;
472     }
473 
474     @Override
dump(PrintWriter printWriter)475     public void dump(PrintWriter printWriter) {
476         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
477         pw.println("UceControllerManager" + "[" + mSlotId + "]:");
478         pw.increaseIndent();
479         if (mUceController != null) {
480             mUceController.dump(pw);
481         } else {
482             pw.println("UceController is null.");
483         }
484         pw.decreaseIndent();
485     }
486 }
487