1 /*
2  * Copyright (C) 2019 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.phone;
18 
19 import android.content.Context;
20 import android.net.Uri;
21 import android.os.Binder;
22 import android.os.RemoteException;
23 import android.os.ServiceManager;
24 import android.os.ServiceSpecificException;
25 import android.telephony.SubscriptionManager;
26 import android.telephony.ims.ImsException;
27 import android.telephony.ims.RegistrationManager;
28 import android.telephony.ims.aidl.IImsCapabilityCallback;
29 import android.telephony.ims.aidl.IImsRcsController;
30 import android.telephony.ims.aidl.IImsRegistrationCallback;
31 import android.telephony.ims.aidl.IRcsUceControllerCallback;
32 import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
33 import android.telephony.ims.feature.RcsFeature;
34 import android.telephony.ims.stub.ImsRegistrationImplBase;
35 import android.util.Log;
36 
37 import com.android.ims.ImsManager;
38 import com.android.internal.telephony.IIntegerConsumer;
39 import com.android.internal.telephony.Phone;
40 import com.android.internal.telephony.TelephonyPermissions;
41 import com.android.internal.telephony.imsphone.ImsPhone;
42 import com.android.services.telephony.rcs.RcsFeatureController;
43 import com.android.services.telephony.rcs.TelephonyRcsService;
44 import com.android.services.telephony.rcs.UserCapabilityExchangeImpl;
45 
46 import java.util.List;
47 
48 /**
49  * Implementation of the IImsRcsController interface.
50  */
51 public class ImsRcsController extends IImsRcsController.Stub {
52     private static final String TAG = "ImsRcsController";
53 
54     /** The singleton instance. */
55     private static ImsRcsController sInstance;
56 
57     private PhoneGlobals mApp;
58     private TelephonyRcsService mRcsService;
59 
60     /**
61      * Initialize the singleton ImsRcsController instance.
62      * This is only done once, at startup, from PhoneApp.onCreate().
63      */
init(PhoneGlobals app)64     static ImsRcsController init(PhoneGlobals app) {
65         synchronized (ImsRcsController.class) {
66             if (sInstance == null) {
67                 sInstance = new ImsRcsController(app);
68             } else {
69                 Log.wtf(TAG, "init() called multiple times!  sInstance = " + sInstance);
70             }
71             return sInstance;
72         }
73     }
74 
75     /** Private constructor; @see init() */
ImsRcsController(PhoneGlobals app)76     private ImsRcsController(PhoneGlobals app) {
77         Log.i(TAG, "ImsRcsController");
78         mApp = app;
79         ServiceManager.addService(Context.TELEPHONY_IMS_SERVICE, this);
80     }
81 
82     /**
83      * Register a {@link RegistrationManager.RegistrationCallback} to receive IMS network
84      * registration state.
85      */
86     @Override
registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback)87     public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
88         enforceReadPrivilegedPermission("registerImsRegistrationCallback");
89         final long token = Binder.clearCallingIdentity();
90         try {
91             getRcsFeatureController(subId).registerImsRegistrationCallback(subId, callback);
92         } catch (ImsException e) {
93             Log.e(TAG, "registerImsRegistrationCallback: sudId=" + subId + ", " + e.getMessage());
94             throw new ServiceSpecificException(e.getCode());
95         } finally {
96             Binder.restoreCallingIdentity(token);
97         }
98     }
99 
100     /**
101      * Removes an existing {@link RegistrationManager.RegistrationCallback}.
102      */
103     @Override
unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback)104     public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
105         enforceReadPrivilegedPermission("unregisterImsRegistrationCallback");
106         final long token = Binder.clearCallingIdentity();
107         try {
108             getRcsFeatureController(subId).unregisterImsRegistrationCallback(subId, callback);
109         } catch (ServiceSpecificException e) {
110             Log.e(TAG, "unregisterImsRegistrationCallback: error=" + e.errorCode);
111         } finally {
112             Binder.restoreCallingIdentity(token);
113         }
114     }
115 
116     /**
117      * Get the IMS service registration state for the RcsFeature associated with this sub id.
118      */
119     @Override
getImsRcsRegistrationState(int subId, IIntegerConsumer consumer)120     public void getImsRcsRegistrationState(int subId, IIntegerConsumer consumer) {
121         enforceReadPrivilegedPermission("getImsRcsRegistrationState");
122         final long token = Binder.clearCallingIdentity();
123         try {
124             getRcsFeatureController(subId).getRegistrationState(regState -> {
125                 try {
126                     consumer.accept((regState == null)
127                             ? RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED : regState);
128                 } catch (RemoteException e) {
129                     Log.w(TAG, "getImsRcsRegistrationState: callback is not available.");
130                 }
131             });
132         } finally {
133             Binder.restoreCallingIdentity(token);
134         }
135     }
136 
137     /**
138      * Gets the Transport Type associated with the current IMS RCS registration.
139      */
140     @Override
getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer)141     public void getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer) {
142         enforceReadPrivilegedPermission("getImsRcsRegistrationTransportType");
143         final long token = Binder.clearCallingIdentity();
144         try {
145             getRcsFeatureController(subId).getRegistrationTech(regTech -> {
146                 // Convert registration tech from ImsRegistrationImplBase -> RegistrationManager
147                 int regTechConverted = (regTech == null)
148                         ? ImsRegistrationImplBase.REGISTRATION_TECH_NONE : regTech;
149                 regTechConverted = RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(
150                         regTechConverted);
151                 try {
152                     consumer.accept(regTechConverted);
153                 } catch (RemoteException e) {
154                     Log.w(TAG, "getImsRcsRegistrationTransportType: callback is not available.");
155                 }
156             });
157         } finally {
158             Binder.restoreCallingIdentity(token);
159         }
160     }
161 
162     /**
163      * Register a capability callback which will provide RCS availability updates for the
164      * subscription specified.
165      *
166      * @param subId the subscription ID
167      * @param callback The ImsCapabilityCallback to be registered.
168      */
169     @Override
registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)170     public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
171         enforceReadPrivilegedPermission("registerRcsAvailabilityCallback");
172         final long token = Binder.clearCallingIdentity();
173         try {
174             getRcsFeatureController(subId).registerRcsAvailabilityCallback(subId, callback);
175         } catch (ImsException e) {
176             Log.e(TAG, "registerRcsAvailabilityCallback: sudId=" + subId + ", " + e.getMessage());
177             throw new ServiceSpecificException(e.getCode());
178         } finally {
179             Binder.restoreCallingIdentity(token);
180         }
181     }
182 
183     /**
184      * Remove the registered capability callback.
185      *
186      * @param subId the subscription ID
187      * @param callback The ImsCapabilityCallback to be removed.
188      */
189     @Override
unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)190     public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
191         enforceReadPrivilegedPermission("unregisterRcsAvailabilityCallback");
192         final long token = Binder.clearCallingIdentity();
193         try {
194             getRcsFeatureController(subId).unregisterRcsAvailabilityCallback(subId, callback);
195         } finally {
196             Binder.restoreCallingIdentity(token);
197         }
198     }
199 
200     @Override
registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c)201     public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
202         enforceReadPrivilegedPermission("registerUcePublishStateCallback");
203         final long token = Binder.clearCallingIdentity();
204         try {
205             UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
206                     UserCapabilityExchangeImpl.class);
207             if (uce == null) {
208                 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
209                     "This subscription does not support UCE.");
210             }
211             uce.registerPublishStateCallback(c);
212         } finally {
213             Binder.restoreCallingIdentity(token);
214         }
215     }
216 
217     @Override
unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c)218     public void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
219         enforceReadPrivilegedPermission("unregisterUcePublishStateCallback");
220         final long token = Binder.clearCallingIdentity();
221         try {
222             UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
223                     UserCapabilityExchangeImpl.class);
224             if (uce == null) {
225                 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
226                     "This subscription does not support UCE.");
227             }
228             uce.unregisterUcePublishStateCallback(c);
229         } finally {
230             Binder.restoreCallingIdentity(token);
231         }
232     }
233 
234     /**
235      * Query for the capability of an IMS RCS service
236      *
237      * @param subId the subscription ID
238      * @param capability the RCS capability to query.
239      * @param radioTech the radio tech that this capability failed for
240      * @return true if the RCS capability is capable for this subscription, false otherwise.
241      */
242     @Override
isCapable(int subId, @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)243     public boolean isCapable(int subId,
244             @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
245             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
246         enforceReadPrivilegedPermission("isCapable");
247         final long token = Binder.clearCallingIdentity();
248         try {
249             return getRcsFeatureController(subId).isCapable(capability, radioTech);
250         } catch (ImsException e) {
251             Log.e(TAG, "isCapable: sudId=" + subId
252                     + ", capability=" + capability + ", " + e.getMessage());
253             return false;
254         } finally {
255             Binder.restoreCallingIdentity(token);
256         }
257     }
258 
259     /**
260      * Query the availability of an IMS RCS capability.
261      *
262      * @param subId the subscription ID
263      * @param capability the RCS capability to query.
264      * @return true if the RCS capability is currently available for the associated subscription,
265      * false otherwise.
266      */
267     @Override
isAvailable(int subId, @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability)268     public boolean isAvailable(int subId,
269             @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
270         enforceReadPrivilegedPermission("isAvailable");
271         final long token = Binder.clearCallingIdentity();
272         try {
273             return getRcsFeatureController(subId).isAvailable(capability);
274         } catch (ImsException e) {
275             Log.e(TAG, "isAvailable: sudId=" + subId
276                     + ", capability=" + capability + ", " + e.getMessage());
277             return false;
278         } finally {
279             Binder.restoreCallingIdentity(token);
280         }
281     }
282 
283     @Override
requestCapabilities(int subId, String callingPackage, String callingFeatureId, List<Uri> contactNumbers, IRcsUceControllerCallback c)284     public void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
285             List<Uri> contactNumbers, IRcsUceControllerCallback c) {
286         enforceReadPrivilegedPermission("requestCapabilities");
287         if (!isUceSettingEnabled(subId, callingPackage, callingFeatureId)) {
288             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
289                     "The user has not enabled UCE for this subscription.");
290         }
291         final long token = Binder.clearCallingIdentity();
292         try {
293             UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
294                     UserCapabilityExchangeImpl.class);
295             if (uce == null) {
296                 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
297                         "This subscription does not support UCE.");
298             }
299             uce.requestCapabilities(contactNumbers, c);
300         } finally {
301             Binder.restoreCallingIdentity(token);
302         }
303     }
304 
305     @Override
getUcePublishState(int subId)306     public int getUcePublishState(int subId) {
307         enforceReadPrivilegedPermission("getUcePublishState");
308         final long token = Binder.clearCallingIdentity();
309         try {
310             UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
311                     UserCapabilityExchangeImpl.class);
312             if (uce == null) {
313                 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
314                         "This subscription does not support UCE.");
315             }
316             return uce.getUcePublishState();
317         } finally {
318             Binder.restoreCallingIdentity(token);
319         }
320     }
321 
322     @Override
isUceSettingEnabled(int subId, String callingPackage, String callingFeatureId)323     public boolean isUceSettingEnabled(int subId, String callingPackage, String callingFeatureId) {
324         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
325                 mApp, subId, callingPackage, callingFeatureId, "isUceSettingEnabled")) {
326             Log.w(TAG, "isUceSettingEnabled: READ_PHONE_STATE app op disabled when accessing "
327                     + "isUceSettingEnabled");
328             return false;
329         }
330         final long token = Binder.clearCallingIdentity();
331         try {
332             return SubscriptionManager.getBooleanSubscriptionProperty(subId,
333                     SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp);
334         } finally {
335             Binder.restoreCallingIdentity(token);
336         }
337     }
338 
339     @Override
setUceSettingEnabled(int subId, boolean isEnabled)340     public void setUceSettingEnabled(int subId, boolean isEnabled) {
341         enforceModifyPermission();
342         final long token = Binder.clearCallingIdentity();
343         try {
344             SubscriptionManager.setSubscriptionProperty(subId,
345                     SubscriptionManager.IMS_RCS_UCE_ENABLED, (isEnabled ? "1" : "0"));
346         } finally {
347             Binder.restoreCallingIdentity(token);
348         }
349     }
350 
351     /**
352      * Make sure either called from same process as self (phone) or IPC caller has read privilege.
353      *
354      * @throws SecurityException if the caller does not have the required permission
355      */
enforceReadPrivilegedPermission(String message)356     private void enforceReadPrivilegedPermission(String message) {
357         mApp.enforceCallingOrSelfPermission(
358                 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
359     }
360 
361     /**
362      * Make sure the caller has the MODIFY_PHONE_STATE permission.
363      *
364      * @throws SecurityException if the caller does not have the required permission
365      */
enforceModifyPermission()366     private void enforceModifyPermission() {
367         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
368     }
369 
370     /**
371      * Retrieve ImsPhone instance.
372      *
373      * @param subId the subscription ID
374      * @return The ImsPhone instance
375      * @throws ServiceSpecificException if getting ImsPhone instance failed.
376      */
getImsPhone(int subId)377     private ImsPhone getImsPhone(int subId) {
378         if (!ImsManager.isImsSupportedOnDevice(mApp)) {
379             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
380                     "IMS is not available on device.");
381         }
382         Phone phone = PhoneGlobals.getPhone(subId);
383         if (phone == null) {
384             throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
385                     "Invalid subscription Id: " + subId);
386         }
387         ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
388         if (imsPhone == null) {
389             throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
390                     "Cannot find ImsPhone instance: " + subId);
391         }
392         return imsPhone;
393     }
394 
395     /**
396      * Retrieve RcsFeatureManager instance.
397      *
398      * @param subId the subscription ID
399      * @return The RcsFeatureManager instance
400      * @throws ServiceSpecificException if getting RcsFeatureManager instance failed.
401      */
getRcsFeatureController(int subId)402     private RcsFeatureController getRcsFeatureController(int subId) {
403         if (!ImsManager.isImsSupportedOnDevice(mApp)) {
404             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
405                     "IMS is not available on device.");
406         }
407         if (mRcsService == null) {
408             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
409                     "IMS is not available on device.");
410         }
411         Phone phone = PhoneGlobals.getPhone(subId);
412         if (phone == null) {
413             throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
414                     "Invalid subscription Id: " + subId);
415         }
416         int slotId = phone.getPhoneId();
417         RcsFeatureController c = mRcsService.getFeatureController(slotId);
418         if (c == null) {
419             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
420                     "The requested operation is not supported for subId " + subId);
421         }
422         return c;
423     }
424 
setRcsService(TelephonyRcsService rcsService)425     void setRcsService(TelephonyRcsService rcsService) {
426         mRcsService = rcsService;
427     }
428 }
429