blob: 6b1b5e37c07aaaaabac1cd9fb2efafa8b38a9a7f [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;
32import android.telephony.ims.feature.RcsFeature;
James.cf Lincad981c2019-12-10 20:37:56 +080033import android.telephony.ims.stub.ImsRegistrationImplBase;
James.cf Linaf3183c2019-10-24 00:59:00 +080034import android.util.Log;
35
James.cf Lindc2d5422019-12-31 14:40:25 +080036import com.android.ims.ImsManager;
James.cf Lindc2d5422019-12-31 14:40:25 +080037import com.android.internal.telephony.IIntegerConsumer;
James.cf Lincad981c2019-12-10 20:37:56 +080038import com.android.internal.telephony.Phone;
39import 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;
77 ServiceManager.addService(Context.TELEPHONY_IMS_SERVICE, this);
78 }
79
James.cf Lincad981c2019-12-10 20:37:56 +080080 /**
Brad Ebingera68a4972020-01-30 17:31:23 -080081 * Register a {@link RegistrationManager.RegistrationCallback} to receive IMS network
82 * registration state.
James.cf Lindc2d5422019-12-31 14:40:25 +080083 */
84 @Override
Brad Ebingera68a4972020-01-30 17:31:23 -080085 public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
James.cf Lindc2d5422019-12-31 14:40:25 +080086 enforceReadPrivilegedPermission("registerImsRegistrationCallback");
87 final long token = Binder.clearCallingIdentity();
88 try {
Brad Ebingera68a4972020-01-30 17:31:23 -080089 getRcsFeatureController(subId).registerImsRegistrationCallback(subId, callback);
90 } catch (ImsException e) {
James.cf Lindc2d5422019-12-31 14:40:25 +080091 Log.e(TAG, "registerImsRegistrationCallback: sudId=" + subId + ", " + e.getMessage());
92 throw new ServiceSpecificException(e.getCode());
93 } finally {
94 Binder.restoreCallingIdentity(token);
95 }
96 }
97
98 /**
Brad Ebingera68a4972020-01-30 17:31:23 -080099 * Removes an existing {@link RegistrationManager.RegistrationCallback}.
James.cf Lindc2d5422019-12-31 14:40:25 +0800100 */
101 @Override
102 public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
103 enforceReadPrivilegedPermission("unregisterImsRegistrationCallback");
104 final long token = Binder.clearCallingIdentity();
105 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800106 getRcsFeatureController(subId).unregisterImsRegistrationCallback(subId, callback);
James.cf Lindc2d5422019-12-31 14:40:25 +0800107 } catch (ServiceSpecificException e) {
108 Log.e(TAG, "unregisterImsRegistrationCallback: error=" + e.errorCode);
109 } finally {
110 Binder.restoreCallingIdentity(token);
111 }
112 }
113
114 /**
115 * Get the IMS service registration state for the RcsFeature associated with this sub id.
116 */
117 @Override
118 public void getImsRcsRegistrationState(int subId, IIntegerConsumer consumer) {
119 enforceReadPrivilegedPermission("getImsRcsRegistrationState");
120 final long token = Binder.clearCallingIdentity();
121 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800122 getRcsFeatureController(subId).getRegistrationState(regState -> {
James.cf Lindc2d5422019-12-31 14:40:25 +0800123 try {
124 consumer.accept((regState == null)
125 ? RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED : regState);
126 } catch (RemoteException e) {
127 Log.w(TAG, "getImsRcsRegistrationState: callback is not available.");
128 }
129 });
130 } finally {
131 Binder.restoreCallingIdentity(token);
132 }
133 }
134
135 /**
136 * Gets the Transport Type associated with the current IMS RCS registration.
137 */
138 @Override
139 public void getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer) {
140 enforceReadPrivilegedPermission("getImsRcsRegistrationTransportType");
141 final long token = Binder.clearCallingIdentity();
142 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800143 getRcsFeatureController(subId).getRegistrationTech(regTech -> {
James.cf Lindc2d5422019-12-31 14:40:25 +0800144 // Convert registration tech from ImsRegistrationImplBase -> RegistrationManager
145 int regTechConverted = (regTech == null)
146 ? ImsRegistrationImplBase.REGISTRATION_TECH_NONE : regTech;
147 regTechConverted = RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(
148 regTechConverted);
149 try {
150 consumer.accept(regTechConverted);
151 } catch (RemoteException e) {
152 Log.w(TAG, "getImsRcsRegistrationTransportType: callback is not available.");
153 }
154 });
155 } finally {
156 Binder.restoreCallingIdentity(token);
157 }
158 }
159
160 /**
James.cf Lincad981c2019-12-10 20:37:56 +0800161 * Register a capability callback which will provide RCS availability updates for the
162 * subscription specified.
163 *
164 * @param subId the subscription ID
165 * @param callback The ImsCapabilityCallback to be registered.
166 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800167 @Override
Brad Ebingera68a4972020-01-30 17:31:23 -0800168 public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
James.cf Linaf3183c2019-10-24 00:59:00 +0800169 enforceReadPrivilegedPermission("registerRcsAvailabilityCallback");
James.cf Lincad981c2019-12-10 20:37:56 +0800170 final long token = Binder.clearCallingIdentity();
171 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800172 getRcsFeatureController(subId).registerRcsAvailabilityCallback(subId, callback);
173 } catch (ImsException e) {
James.cf Lincad981c2019-12-10 20:37:56 +0800174 Log.e(TAG, "registerRcsAvailabilityCallback: sudId=" + subId + ", " + e.getMessage());
175 throw new ServiceSpecificException(e.getCode());
176 } finally {
177 Binder.restoreCallingIdentity(token);
178 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800179 }
180
James.cf Lincad981c2019-12-10 20:37:56 +0800181 /**
182 * Remove the registered capability callback.
183 *
184 * @param subId the subscription ID
185 * @param callback The ImsCapabilityCallback to be removed.
186 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800187 @Override
James.cf Lincad981c2019-12-10 20:37:56 +0800188 public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
James.cf Linaf3183c2019-10-24 00:59:00 +0800189 enforceReadPrivilegedPermission("unregisterRcsAvailabilityCallback");
James.cf Lincad981c2019-12-10 20:37:56 +0800190 final long token = Binder.clearCallingIdentity();
191 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800192 getRcsFeatureController(subId).unregisterRcsAvailabilityCallback(subId, callback);
James.cf Lincad981c2019-12-10 20:37:56 +0800193 } finally {
194 Binder.restoreCallingIdentity(token);
195 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800196 }
197
James.cf Lincad981c2019-12-10 20:37:56 +0800198 /**
199 * Query for the capability of an IMS RCS service
200 *
201 * @param subId the subscription ID
202 * @param capability the RCS capability to query.
203 * @param radioTech the radio tech that this capability failed for
204 * @return true if the RCS capability is capable for this subscription, false otherwise.
205 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800206 @Override
207 public boolean isCapable(int subId,
James.cf Lincad981c2019-12-10 20:37:56 +0800208 @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
209 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
James.cf Linaf3183c2019-10-24 00:59:00 +0800210 enforceReadPrivilegedPermission("isCapable");
James.cf Lincad981c2019-12-10 20:37:56 +0800211 final long token = Binder.clearCallingIdentity();
212 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800213 return getRcsFeatureController(subId).isCapable(capability, radioTech);
214 } catch (ImsException e) {
James.cf Lincad981c2019-12-10 20:37:56 +0800215 Log.e(TAG, "isCapable: sudId=" + subId
216 + ", capability=" + capability + ", " + e.getMessage());
217 return false;
218 } finally {
219 Binder.restoreCallingIdentity(token);
220 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800221 }
222
James.cf Lincad981c2019-12-10 20:37:56 +0800223 /**
224 * Query the availability of an IMS RCS capability.
225 *
226 * @param subId the subscription ID
227 * @param capability the RCS capability to query.
228 * @return true if the RCS capability is currently available for the associated subscription,
229 * false otherwise.
230 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800231 @Override
232 public boolean isAvailable(int subId,
233 @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
234 enforceReadPrivilegedPermission("isAvailable");
James.cf Lincad981c2019-12-10 20:37:56 +0800235 final long token = Binder.clearCallingIdentity();
236 try {
Brad Ebingera68a4972020-01-30 17:31:23 -0800237 return getRcsFeatureController(subId).isAvailable(capability);
238 } catch (ImsException e) {
James.cf Lincad981c2019-12-10 20:37:56 +0800239 Log.e(TAG, "isAvailable: sudId=" + subId
240 + ", capability=" + capability + ", " + e.getMessage());
241 return false;
242 } finally {
243 Binder.restoreCallingIdentity(token);
244 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800245 }
246
247 @Override
248 public void requestCapabilities(int subId, List<Uri> contactNumbers,
249 IRcsUceControllerCallback c) {
250 enforceReadPrivilegedPermission("requestCapabilities");
Brad Ebingera68a4972020-01-30 17:31:23 -0800251 final long token = Binder.clearCallingIdentity();
252 try {
253 UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
254 UserCapabilityExchangeImpl.class);
255 if (uce == null) {
256 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
257 "This subscription does not support UCE.");
258 }
259 uce.requestCapabilities(contactNumbers, c);
260 } finally {
261 Binder.restoreCallingIdentity(token);
Brad Ebinger278e9bf2020-01-22 14:17:23 -0800262 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800263 }
264
265 @Override
266 public int getUcePublishState(int subId) {
267 enforceReadPrivilegedPermission("getUcePublishState");
Brad Ebingera68a4972020-01-30 17:31:23 -0800268 final long token = Binder.clearCallingIdentity();
269 try {
270 UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
271 UserCapabilityExchangeImpl.class);
272 if (uce == null) {
273 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
274 "This subscription does not support UCE.");
275 }
276 return uce.getUcePublishState();
277 } finally {
278 Binder.restoreCallingIdentity(token);
Brad Ebinger278e9bf2020-01-22 14:17:23 -0800279 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800280 }
281
282 @Override
283 public boolean isUceSettingEnabled(int subId) {
284 enforceReadPrivilegedPermission("isUceSettingEnabled");
Brad Ebinger6e3543b2020-01-22 17:51:55 -0800285 return SubscriptionManager.getBooleanSubscriptionProperty(subId,
286 SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp);
James.cf Linaf3183c2019-10-24 00:59:00 +0800287 }
288
289 @Override
290 public void setUceSettingEnabled(int subId, boolean isEnabled) {
291 enforceModifyPermission();
Brad Ebinger6e3543b2020-01-22 17:51:55 -0800292 SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.IMS_RCS_UCE_ENABLED,
293 (isEnabled ? "1" : "0"));
James.cf Linaf3183c2019-10-24 00:59:00 +0800294 }
295
296 /**
297 * Make sure either called from same process as self (phone) or IPC caller has read privilege.
298 *
299 * @throws SecurityException if the caller does not have the required permission
300 */
301 private void enforceReadPrivilegedPermission(String message) {
302 mApp.enforceCallingOrSelfPermission(
303 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
304 }
305
306 /**
307 * Make sure the caller has the MODIFY_PHONE_STATE permission.
308 *
309 * @throws SecurityException if the caller does not have the required permission
310 */
311 private void enforceModifyPermission() {
312 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
313 }
James.cf Lincad981c2019-12-10 20:37:56 +0800314
315 /**
James.cf Lindc2d5422019-12-31 14:40:25 +0800316 * Retrieve ImsPhone instance.
James.cf Lincad981c2019-12-10 20:37:56 +0800317 *
318 * @param subId the subscription ID
James.cf Lindc2d5422019-12-31 14:40:25 +0800319 * @return The ImsPhone instance
320 * @throws ServiceSpecificException if getting ImsPhone instance failed.
James.cf Lincad981c2019-12-10 20:37:56 +0800321 */
James.cf Lindc2d5422019-12-31 14:40:25 +0800322 private ImsPhone getImsPhone(int subId) {
323 if (!ImsManager.isImsSupportedOnDevice(mApp)) {
324 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
325 "IMS is not available on device.");
326 }
James.cf Lincad981c2019-12-10 20:37:56 +0800327 Phone phone = PhoneGlobals.getPhone(subId);
328 if (phone == null) {
329 throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
330 "Invalid subscription Id: " + subId);
331 }
332 ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
333 if (imsPhone == null) {
James.cf Lindc2d5422019-12-31 14:40:25 +0800334 throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
335 "Cannot find ImsPhone instance: " + subId);
336 }
337 return imsPhone;
338 }
339
340 /**
341 * Retrieve RcsFeatureManager instance.
342 *
343 * @param subId the subscription ID
344 * @return The RcsFeatureManager instance
345 * @throws ServiceSpecificException if getting RcsFeatureManager instance failed.
346 */
Brad Ebingera68a4972020-01-30 17:31:23 -0800347 private RcsFeatureController getRcsFeatureController(int subId) {
James.cf Lindc2d5422019-12-31 14:40:25 +0800348 if (!ImsManager.isImsSupportedOnDevice(mApp)) {
James.cf Lincad981c2019-12-10 20:37:56 +0800349 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
James.cf Lindc2d5422019-12-31 14:40:25 +0800350 "IMS is not available on device.");
351 }
Brad Ebingera68a4972020-01-30 17:31:23 -0800352 if (mRcsService == null) {
353 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
354 "IMS is not available on device.");
355 }
James.cf Lindc2d5422019-12-31 14:40:25 +0800356 Phone phone = PhoneGlobals.getPhone(subId);
357 if (phone == null) {
358 throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
359 "Invalid subscription Id: " + subId);
360 }
Brad Ebingera68a4972020-01-30 17:31:23 -0800361 int slotId = phone.getPhoneId();
362 RcsFeatureController c = mRcsService.getFeatureController(slotId);
363 if (c == null) {
James.cf Lindc2d5422019-12-31 14:40:25 +0800364 throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
Brad Ebingera68a4972020-01-30 17:31:23 -0800365 "Cannot find RcsFeatureController instance for sub: " + subId);
James.cf Lincad981c2019-12-10 20:37:56 +0800366 }
Brad Ebingera68a4972020-01-30 17:31:23 -0800367 return c;
James.cf Lincad981c2019-12-10 20:37:56 +0800368 }
James.cf Linc9f35a42020-01-15 02:35:22 +0800369
370 void setRcsService(TelephonyRcsService rcsService) {
371 mRcsService = rcsService;
372 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800373}