blob: f00572caa80508012a8ab1ef829c4ec09b02c37f [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
19import android.content.Context;
20import android.net.Uri;
James.cf Lincad981c2019-12-10 20:37:56 +080021import android.os.Binder;
22import android.os.RemoteException;
James.cf Linaf3183c2019-10-24 00:59:00 +080023import android.os.ServiceManager;
James.cf Lincad981c2019-12-10 20:37:56 +080024import android.os.ServiceSpecificException;
Brad Ebinger6e3543b2020-01-22 17:51:55 -080025import android.telephony.SubscriptionManager;
James.cf Lincad981c2019-12-10 20:37:56 +080026import android.telephony.ims.ImsException;
James.cf Lindc2d5422019-12-31 14:40:25 +080027import android.telephony.ims.RegistrationManager;
James.cf Linaf3183c2019-10-24 00:59:00 +080028import android.telephony.ims.aidl.IImsCapabilityCallback;
29import android.telephony.ims.aidl.IImsRcsController;
James.cf Lindc2d5422019-12-31 14:40:25 +080030import android.telephony.ims.aidl.IImsRegistrationCallback;
James.cf Linaf3183c2019-10-24 00:59:00 +080031import android.telephony.ims.aidl.IRcsUceControllerCallback;
James.cf Lincdad3862020-02-25 15:55:03 +080032import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
James.cf Linaf3183c2019-10-24 00:59:00 +080033import android.telephony.ims.feature.RcsFeature;
James.cf Lincad981c2019-12-10 20:37:56 +080034import android.telephony.ims.stub.ImsRegistrationImplBase;
James.cf Linaf3183c2019-10-24 00:59:00 +080035import android.util.Log;
36
James.cf Lindc2d5422019-12-31 14:40:25 +080037import com.android.ims.ImsManager;
James.cf Lindc2d5422019-12-31 14:40:25 +080038import com.android.internal.telephony.IIntegerConsumer;
James.cf Lincad981c2019-12-10 20:37:56 +080039import com.android.internal.telephony.Phone;
40import com.android.internal.telephony.imsphone.ImsPhone;
Brad Ebingera68a4972020-01-30 17:31:23 -080041import com.android.services.telephony.rcs.RcsFeatureController;
James.cf Linc9f35a42020-01-15 02:35:22 +080042import com.android.services.telephony.rcs.TelephonyRcsService;
Brad Ebingera68a4972020-01-30 17:31:23 -080043import com.android.services.telephony.rcs.UserCapabilityExchangeImpl;
James.cf Lincad981c2019-12-10 20:37:56 +080044
James.cf Linaf3183c2019-10-24 00:59:00 +080045import java.util.List;
46
47/**
48 * Implementation of the IImsRcsController interface.
49 */
50public class ImsRcsController extends IImsRcsController.Stub {
51 private static final String TAG = "ImsRcsController";
52
53 /** The singleton instance. */
54 private static ImsRcsController sInstance;
55
56 private PhoneGlobals mApp;
James.cf Linc9f35a42020-01-15 02:35:22 +080057 private TelephonyRcsService mRcsService;
James.cf Linaf3183c2019-10-24 00:59:00 +080058
59 /**
60 * Initialize the singleton ImsRcsController instance.
61 * This is only done once, at startup, from PhoneApp.onCreate().
62 */
63 static ImsRcsController init(PhoneGlobals app) {
64 synchronized (ImsRcsController.class) {
65 if (sInstance == null) {
66 sInstance = new ImsRcsController(app);
67 } else {
68 Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance);
69 }
70 return sInstance;
71 }
72 }
73
74 /** Private constructor; @see init() */
75 private ImsRcsController(PhoneGlobals app) {
76 Log.i(TAG, "ImsRcsController");
77 mApp = app;
78 ServiceManager.addService(Context.TELEPHONY_IMS_SERVICE, this);
79 }
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 Lincdad3862020-02-25 15:55:03 +0800199 @Override
200 public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
201 enforceReadPrivilegedPermission("registerUcePublishStateCallback");
202 final long token = Binder.clearCallingIdentity();
203 try {
204 UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
205 UserCapabilityExchangeImpl.class);
206 if (uce == null) {
207 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
208 "This subscription does not support UCE.");
209 }
210 uce.registerPublishStateCallback(c);
211 } finally {
212 Binder.restoreCallingIdentity(token);
213 }
214 }
215
216 @Override
217 public void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
218 enforceReadPrivilegedPermission("unregisterUcePublishStateCallback");
219 final long token = Binder.clearCallingIdentity();
220 try {
221 UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
222 UserCapabilityExchangeImpl.class);
223 if (uce == null) {
224 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
225 "This subscription does not support UCE.");
226 }
227 uce.unregisterUcePublishStateCallback(c);
228 } finally {
229 Binder.restoreCallingIdentity(token);
230 }
231 }
232
James.cf Lincad981c2019-12-10 20:37:56 +0800233 /**
234 * Query for the capability of an IMS RCS service
235 *
236 * @param subId the subscription ID
237 * @param capability the RCS capability to query.
238 * @param radioTech the radio tech that this capability failed for
239 * @return true if the RCS capability is capable for this subscription, false otherwise.
240 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800241 @Override
242 public boolean isCapable(int subId,
James.cf Lincad981c2019-12-10 20:37:56 +0800243 @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
244 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
James.cf Linaf3183c2019-10-24 00:59:00 +0800245 enforceReadPrivilegedPermission("isCapable");
James.cf Lincad981c2019-12-10 20:37:56 +0800246 final long token = Binder.clearCallingIdentity();
247 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800248 return getRcsFeatureController(subId).isCapable(capability, radioTech);
249 } catch (ImsException e) {
James.cf Lincad981c2019-12-10 20:37:56 +0800250 Log.e(TAG, "isCapable: sudId=" + subId
251 + ", capability=" + capability + ", " + e.getMessage());
252 return false;
253 } finally {
254 Binder.restoreCallingIdentity(token);
255 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800256 }
257
James.cf Lincad981c2019-12-10 20:37:56 +0800258 /**
259 * Query the availability of an IMS RCS capability.
260 *
261 * @param subId the subscription ID
262 * @param capability the RCS capability to query.
263 * @return true if the RCS capability is currently available for the associated subscription,
264 * false otherwise.
265 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800266 @Override
267 public boolean isAvailable(int subId,
268 @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
269 enforceReadPrivilegedPermission("isAvailable");
James.cf Lincad981c2019-12-10 20:37:56 +0800270 final long token = Binder.clearCallingIdentity();
271 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800272 return getRcsFeatureController(subId).isAvailable(capability);
273 } catch (ImsException e) {
James.cf Lincad981c2019-12-10 20:37:56 +0800274 Log.e(TAG, "isAvailable: sudId=" + subId
275 + ", capability=" + capability + ", " + e.getMessage());
276 return false;
277 } finally {
278 Binder.restoreCallingIdentity(token);
279 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800280 }
281
282 @Override
283 public void requestCapabilities(int subId, List<Uri> contactNumbers,
284 IRcsUceControllerCallback c) {
285 enforceReadPrivilegedPermission("requestCapabilities");
Brad Ebingera68a4972020-01-30 17:31:23 -0800286 final long token = Binder.clearCallingIdentity();
287 try {
288 UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
289 UserCapabilityExchangeImpl.class);
290 if (uce == null) {
291 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
292 "This subscription does not support UCE.");
293 }
294 uce.requestCapabilities(contactNumbers, c);
295 } finally {
296 Binder.restoreCallingIdentity(token);
Brad Ebinger278e9bf2020-01-22 14:17:23 -0800297 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800298 }
299
300 @Override
301 public int getUcePublishState(int subId) {
302 enforceReadPrivilegedPermission("getUcePublishState");
Brad Ebingera68a4972020-01-30 17:31:23 -0800303 final long token = Binder.clearCallingIdentity();
304 try {
305 UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
306 UserCapabilityExchangeImpl.class);
307 if (uce == null) {
308 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
309 "This subscription does not support UCE.");
310 }
311 return uce.getUcePublishState();
312 } finally {
313 Binder.restoreCallingIdentity(token);
Brad Ebinger278e9bf2020-01-22 14:17:23 -0800314 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800315 }
316
317 @Override
318 public boolean isUceSettingEnabled(int subId) {
319 enforceReadPrivilegedPermission("isUceSettingEnabled");
Brad Ebinger6e3543b2020-01-22 17:51:55 -0800320 return SubscriptionManager.getBooleanSubscriptionProperty(subId,
321 SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp);
James.cf Linaf3183c2019-10-24 00:59:00 +0800322 }
323
324 @Override
325 public void setUceSettingEnabled(int subId, boolean isEnabled) {
326 enforceModifyPermission();
Brad Ebinger6e3543b2020-01-22 17:51:55 -0800327 SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.IMS_RCS_UCE_ENABLED,
328 (isEnabled ? "1" : "0"));
James.cf Linaf3183c2019-10-24 00:59:00 +0800329 }
330
331 /**
332 * Make sure either called from same process as self (phone) or IPC caller has read privilege.
333 *
334 * @throws SecurityException if the caller does not have the required permission
335 */
336 private void enforceReadPrivilegedPermission(String message) {
337 mApp.enforceCallingOrSelfPermission(
338 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
339 }
340
341 /**
342 * Make sure the caller has the MODIFY_PHONE_STATE permission.
343 *
344 * @throws SecurityException if the caller does not have the required permission
345 */
346 private void enforceModifyPermission() {
347 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
348 }
James.cf Lincad981c2019-12-10 20:37:56 +0800349
350 /**
James.cf Lindc2d5422019-12-31 14:40:25 +0800351 * Retrieve ImsPhone instance.
James.cf Lincad981c2019-12-10 20:37:56 +0800352 *
353 * @param subId the subscription ID
James.cf Lindc2d5422019-12-31 14:40:25 +0800354 * @return The ImsPhone instance
355 * @throws ServiceSpecificException if getting ImsPhone instance failed.
James.cf Lincad981c2019-12-10 20:37:56 +0800356 */
James.cf Lindc2d5422019-12-31 14:40:25 +0800357 private ImsPhone getImsPhone(int subId) {
358 if (!ImsManager.isImsSupportedOnDevice(mApp)) {
359 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
360 "IMS is not available on device.");
361 }
James.cf Lincad981c2019-12-10 20:37:56 +0800362 Phone phone = PhoneGlobals.getPhone(subId);
363 if (phone == null) {
364 throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
365 "Invalid subscription Id: " + subId);
366 }
367 ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
368 if (imsPhone == null) {
James.cf Lindc2d5422019-12-31 14:40:25 +0800369 throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
370 "Cannot find ImsPhone instance: " + subId);
371 }
372 return imsPhone;
373 }
374
375 /**
376 * Retrieve RcsFeatureManager instance.
377 *
378 * @param subId the subscription ID
379 * @return The RcsFeatureManager instance
380 * @throws ServiceSpecificException if getting RcsFeatureManager instance failed.
381 */
Brad Ebingera68a4972020-01-30 17:31:23 -0800382 private RcsFeatureController getRcsFeatureController(int subId) {
James.cf Lindc2d5422019-12-31 14:40:25 +0800383 if (!ImsManager.isImsSupportedOnDevice(mApp)) {
James.cf Lincad981c2019-12-10 20:37:56 +0800384 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
James.cf Lindc2d5422019-12-31 14:40:25 +0800385 "IMS is not available on device.");
386 }
Brad Ebingera68a4972020-01-30 17:31:23 -0800387 if (mRcsService == null) {
388 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
389 "IMS is not available on device.");
390 }
James.cf Lindc2d5422019-12-31 14:40:25 +0800391 Phone phone = PhoneGlobals.getPhone(subId);
392 if (phone == null) {
393 throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
394 "Invalid subscription Id: " + subId);
395 }
Brad Ebingera68a4972020-01-30 17:31:23 -0800396 int slotId = phone.getPhoneId();
397 RcsFeatureController c = mRcsService.getFeatureController(slotId);
398 if (c == null) {
Brad Ebinger036dc9e2020-02-06 15:49:06 -0800399 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
400 "The requested operation is not supported for subId " + subId);
James.cf Lincad981c2019-12-10 20:37:56 +0800401 }
Brad Ebingera68a4972020-01-30 17:31:23 -0800402 return c;
James.cf Lincad981c2019-12-10 20:37:56 +0800403 }
James.cf Linc9f35a42020-01-15 02:35:22 +0800404
405 void setRcsService(TelephonyRcsService rcsService) {
406 mRcsService = rcsService;
407 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800408}