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