blob: dcae24bc70e81a25193d0039a4053011ce516128 [file] [log] [blame]
James.cf Linaf3183c2019-10-24 00:59:00 +08001/*
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
17package com.android.phone;
18
James.cf Linaf3183c2019-10-24 00:59:00 +080019import android.net.Uri;
James.cf Lincad981c2019-12-10 20:37:56 +080020import android.os.Binder;
21import android.os.RemoteException;
James.cf Lincad981c2019-12-10 20:37:56 +080022import android.os.ServiceSpecificException;
Brad Ebingerb7a866a2020-01-22 17:51:55 -080023import android.telephony.SubscriptionManager;
Peter Wangc035ce42020-01-08 21:00:22 -080024import android.telephony.TelephonyFrameworkInitializer;
James.cf Lincad981c2019-12-10 20:37:56 +080025import android.telephony.ims.ImsException;
James.cf Lindc2d5422019-12-31 14:40:25 +080026import android.telephony.ims.RegistrationManager;
James.cf Linaf3183c2019-10-24 00:59:00 +080027import android.telephony.ims.aidl.IImsCapabilityCallback;
28import android.telephony.ims.aidl.IImsRcsController;
James.cf Lindc2d5422019-12-31 14:40:25 +080029import android.telephony.ims.aidl.IImsRegistrationCallback;
James.cf Linaf3183c2019-10-24 00:59:00 +080030import android.telephony.ims.aidl.IRcsUceControllerCallback;
31import android.telephony.ims.feature.RcsFeature;
James.cf Lincad981c2019-12-10 20:37:56 +080032import android.telephony.ims.stub.ImsRegistrationImplBase;
James.cf Linaf3183c2019-10-24 00:59:00 +080033import android.util.Log;
34
James.cf Lindc2d5422019-12-31 14:40:25 +080035import com.android.ims.ImsManager;
James.cf Lindc2d5422019-12-31 14:40:25 +080036import com.android.internal.telephony.IIntegerConsumer;
James.cf Lincad981c2019-12-10 20:37:56 +080037import com.android.internal.telephony.Phone;
Brad Ebinger8b79edc2020-02-27 19:13:24 -080038import com.android.internal.telephony.TelephonyPermissions;
James.cf Lincad981c2019-12-10 20:37:56 +080039import com.android.internal.telephony.imsphone.ImsPhone;
Brad Ebingera68a4972020-01-30 17:31:23 -080040import com.android.services.telephony.rcs.RcsFeatureController;
James.cf Linc9f35a42020-01-15 02:35:22 +080041import com.android.services.telephony.rcs.TelephonyRcsService;
Brad Ebingera68a4972020-01-30 17:31:23 -080042import com.android.services.telephony.rcs.UserCapabilityExchangeImpl;
James.cf Lincad981c2019-12-10 20:37:56 +080043
James.cf Linaf3183c2019-10-24 00:59:00 +080044import java.util.List;
45
46/**
47 * Implementation of the IImsRcsController interface.
48 */
49public 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;
James.cf Linc9f35a42020-01-15 02:35:22 +080056 private TelephonyRcsService mRcsService;
James.cf Linaf3183c2019-10-24 00:59:00 +080057
58 /**
59 * Initialize the singleton ImsRcsController instance.
60 * This is only done once, at startup, from PhoneApp.onCreate().
61 */
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() */
74 private ImsRcsController(PhoneGlobals app) {
75 Log.i(TAG, "ImsRcsController");
76 mApp = app;
Peter Wangc035ce42020-01-08 21:00:22 -080077 TelephonyFrameworkInitializer
78 .getTelephonyServiceManager().getTelephonyImsServiceRegisterer().register(this);
James.cf Linaf3183c2019-10-24 00:59:00 +080079 }
80
James.cf Lincad981c2019-12-10 20:37:56 +080081 /**
Brad Ebingera68a4972020-01-30 17:31:23 -080082 * Register a {@link RegistrationManager.RegistrationCallback} to receive IMS network
83 * registration state.
James.cf Lindc2d5422019-12-31 14:40:25 +080084 */
85 @Override
Brad Ebingera68a4972020-01-30 17:31:23 -080086 public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
James.cf Lindc2d5422019-12-31 14:40:25 +080087 enforceReadPrivilegedPermission("registerImsRegistrationCallback");
88 final long token = Binder.clearCallingIdentity();
89 try {
Brad Ebingera68a4972020-01-30 17:31:23 -080090 getRcsFeatureController(subId).registerImsRegistrationCallback(subId, callback);
91 } catch (ImsException e) {
James.cf Lindc2d5422019-12-31 14:40:25 +080092 Log.e(TAG, "registerImsRegistrationCallback: sudId=" + subId + ", " + e.getMessage());
93 throw new ServiceSpecificException(e.getCode());
94 } finally {
95 Binder.restoreCallingIdentity(token);
96 }
97 }
98
99 /**
Brad Ebingera68a4972020-01-30 17:31:23 -0800100 * Removes an existing {@link RegistrationManager.RegistrationCallback}.
James.cf Lindc2d5422019-12-31 14:40:25 +0800101 */
102 @Override
103 public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
104 enforceReadPrivilegedPermission("unregisterImsRegistrationCallback");
105 final long token = Binder.clearCallingIdentity();
106 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800107 getRcsFeatureController(subId).unregisterImsRegistrationCallback(subId, callback);
James.cf Lindc2d5422019-12-31 14:40:25 +0800108 } 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
119 public void getImsRcsRegistrationState(int subId, IIntegerConsumer consumer) {
120 enforceReadPrivilegedPermission("getImsRcsRegistrationState");
121 final long token = Binder.clearCallingIdentity();
122 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800123 getRcsFeatureController(subId).getRegistrationState(regState -> {
James.cf Lindc2d5422019-12-31 14:40:25 +0800124 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
140 public void getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer) {
141 enforceReadPrivilegedPermission("getImsRcsRegistrationTransportType");
142 final long token = Binder.clearCallingIdentity();
143 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800144 getRcsFeatureController(subId).getRegistrationTech(regTech -> {
James.cf Lindc2d5422019-12-31 14:40:25 +0800145 // 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 /**
James.cf Lincad981c2019-12-10 20:37:56 +0800162 * 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 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800168 @Override
Brad Ebingera68a4972020-01-30 17:31:23 -0800169 public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
James.cf Linaf3183c2019-10-24 00:59:00 +0800170 enforceReadPrivilegedPermission("registerRcsAvailabilityCallback");
James.cf Lincad981c2019-12-10 20:37:56 +0800171 final long token = Binder.clearCallingIdentity();
172 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800173 getRcsFeatureController(subId).registerRcsAvailabilityCallback(subId, callback);
174 } catch (ImsException e) {
James.cf Lincad981c2019-12-10 20:37:56 +0800175 Log.e(TAG, "registerRcsAvailabilityCallback: sudId=" + subId + ", " + e.getMessage());
176 throw new ServiceSpecificException(e.getCode());
177 } finally {
178 Binder.restoreCallingIdentity(token);
179 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800180 }
181
James.cf Lincad981c2019-12-10 20:37:56 +0800182 /**
183 * Remove the registered capability callback.
184 *
185 * @param subId the subscription ID
186 * @param callback The ImsCapabilityCallback to be removed.
187 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800188 @Override
James.cf Lincad981c2019-12-10 20:37:56 +0800189 public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
James.cf Linaf3183c2019-10-24 00:59:00 +0800190 enforceReadPrivilegedPermission("unregisterRcsAvailabilityCallback");
James.cf Lincad981c2019-12-10 20:37:56 +0800191 final long token = Binder.clearCallingIdentity();
192 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800193 getRcsFeatureController(subId).unregisterRcsAvailabilityCallback(subId, callback);
James.cf Lincad981c2019-12-10 20:37:56 +0800194 } finally {
195 Binder.restoreCallingIdentity(token);
196 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800197 }
198
James.cf Lincad981c2019-12-10 20:37:56 +0800199 /**
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 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800207 @Override
208 public boolean isCapable(int subId,
James.cf Lincad981c2019-12-10 20:37:56 +0800209 @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
210 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
James.cf Linaf3183c2019-10-24 00:59:00 +0800211 enforceReadPrivilegedPermission("isCapable");
James.cf Lincad981c2019-12-10 20:37:56 +0800212 final long token = Binder.clearCallingIdentity();
213 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800214 return getRcsFeatureController(subId).isCapable(capability, radioTech);
215 } catch (ImsException e) {
James.cf Lincad981c2019-12-10 20:37:56 +0800216 Log.e(TAG, "isCapable: sudId=" + subId
217 + ", capability=" + capability + ", " + e.getMessage());
218 return false;
219 } finally {
220 Binder.restoreCallingIdentity(token);
221 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800222 }
223
James.cf Lincad981c2019-12-10 20:37:56 +0800224 /**
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 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800232 @Override
233 public boolean isAvailable(int subId,
234 @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
235 enforceReadPrivilegedPermission("isAvailable");
James.cf Lincad981c2019-12-10 20:37:56 +0800236 final long token = Binder.clearCallingIdentity();
237 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800238 return getRcsFeatureController(subId).isAvailable(capability);
239 } catch (ImsException e) {
James.cf Lincad981c2019-12-10 20:37:56 +0800240 Log.e(TAG, "isAvailable: sudId=" + subId
241 + ", capability=" + capability + ", " + e.getMessage());
242 return false;
243 } finally {
244 Binder.restoreCallingIdentity(token);
245 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800246 }
247
248 @Override
Brad Ebinger8b79edc2020-02-27 19:13:24 -0800249 public void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
250 List<Uri> contactNumbers, IRcsUceControllerCallback c) {
James.cf Linaf3183c2019-10-24 00:59:00 +0800251 enforceReadPrivilegedPermission("requestCapabilities");
Brad Ebinger8b79edc2020-02-27 19:13:24 -0800252 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 }
Brad Ebingera68a4972020-01-30 17:31:23 -0800256 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);
Brad Ebinger1aa94992020-01-22 14:17:23 -0800267 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800268 }
269
270 @Override
271 public int getUcePublishState(int subId) {
272 enforceReadPrivilegedPermission("getUcePublishState");
Brad Ebingera68a4972020-01-30 17:31:23 -0800273 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);
Brad Ebinger1aa94992020-01-22 14:17:23 -0800284 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800285 }
286
287 @Override
Brad Ebinger8b79edc2020-02-27 19:13:24 -0800288 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 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800302 }
303
304 @Override
305 public void setUceSettingEnabled(int subId, boolean isEnabled) {
306 enforceModifyPermission();
Brad Ebinger8b79edc2020-02-27 19:13:24 -0800307 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 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800314 }
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 */
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 */
331 private void enforceModifyPermission() {
332 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
333 }
James.cf Lincad981c2019-12-10 20:37:56 +0800334
335 /**
James.cf Lindc2d5422019-12-31 14:40:25 +0800336 * Retrieve ImsPhone instance.
James.cf Lincad981c2019-12-10 20:37:56 +0800337 *
338 * @param subId the subscription ID
James.cf Lindc2d5422019-12-31 14:40:25 +0800339 * @return The ImsPhone instance
340 * @throws ServiceSpecificException if getting ImsPhone instance failed.
James.cf Lincad981c2019-12-10 20:37:56 +0800341 */
James.cf Lindc2d5422019-12-31 14:40:25 +0800342 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 }
James.cf Lincad981c2019-12-10 20:37:56 +0800347 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) {
James.cf Lindc2d5422019-12-31 14:40:25 +0800354 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 */
Brad Ebingera68a4972020-01-30 17:31:23 -0800367 private RcsFeatureController getRcsFeatureController(int subId) {
James.cf Lindc2d5422019-12-31 14:40:25 +0800368 if (!ImsManager.isImsSupportedOnDevice(mApp)) {
James.cf Lincad981c2019-12-10 20:37:56 +0800369 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
James.cf Lindc2d5422019-12-31 14:40:25 +0800370 "IMS is not available on device.");
371 }
Brad Ebingera68a4972020-01-30 17:31:23 -0800372 if (mRcsService == null) {
373 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
374 "IMS is not available on device.");
375 }
James.cf Lindc2d5422019-12-31 14:40:25 +0800376 Phone phone = PhoneGlobals.getPhone(subId);
377 if (phone == null) {
378 throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
379 "Invalid subscription Id: " + subId);
380 }
Brad Ebingera68a4972020-01-30 17:31:23 -0800381 int slotId = phone.getPhoneId();
382 RcsFeatureController c = mRcsService.getFeatureController(slotId);
383 if (c == null) {
Brad Ebinger036dc9e2020-02-06 15:49:06 -0800384 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
385 "The requested operation is not supported for subId " + subId);
James.cf Lincad981c2019-12-10 20:37:56 +0800386 }
Brad Ebingera68a4972020-01-30 17:31:23 -0800387 return c;
James.cf Lincad981c2019-12-10 20:37:56 +0800388 }
James.cf Linc9f35a42020-01-15 02:35:22 +0800389
390 void setRcsService(TelephonyRcsService rcsService) {
391 mRcsService = rcsService;
392 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800393}