blob: e09c6af71351d616b417c544fdd6d7dea32e8df9 [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 Lincad981c2019-12-10 20:37:56 +080037import com.android.ims.RcsFeatureManager;
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;
James.cf Linc9f35a42020-01-15 02:35:22 +080041import com.android.services.telephony.rcs.TelephonyRcsService;
James.cf Lincad981c2019-12-10 20:37:56 +080042
James.cf Linaf3183c2019-10-24 00:59:00 +080043import java.util.List;
44
45/**
46 * Implementation of the IImsRcsController interface.
47 */
48public class ImsRcsController extends IImsRcsController.Stub {
49 private static final String TAG = "ImsRcsController";
50
51 /** The singleton instance. */
52 private static ImsRcsController sInstance;
53
54 private PhoneGlobals mApp;
James.cf Linc9f35a42020-01-15 02:35:22 +080055 private TelephonyRcsService mRcsService;
James.cf Linaf3183c2019-10-24 00:59:00 +080056
57 /**
58 * Initialize the singleton ImsRcsController instance.
59 * This is only done once, at startup, from PhoneApp.onCreate().
60 */
61 static ImsRcsController init(PhoneGlobals app) {
62 synchronized (ImsRcsController.class) {
63 if (sInstance == null) {
64 sInstance = new ImsRcsController(app);
65 } else {
66 Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance);
67 }
68 return sInstance;
69 }
70 }
71
72 /** Private constructor; @see init() */
73 private ImsRcsController(PhoneGlobals app) {
74 Log.i(TAG, "ImsRcsController");
75 mApp = app;
76 ServiceManager.addService(Context.TELEPHONY_IMS_SERVICE, this);
77 }
78
James.cf Lincad981c2019-12-10 20:37:56 +080079 /**
James.cf Lindc2d5422019-12-31 14:40:25 +080080 * Register a IImsRegistrationCallback to receive IMS network registration state.
81 */
82 @Override
83 public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback)
84 throws RemoteException {
85 enforceReadPrivilegedPermission("registerImsRegistrationCallback");
86 final long token = Binder.clearCallingIdentity();
87 try {
88 getRcsFeatureManager(subId).registerImsRegistrationCallback(callback);
89 } catch (com.android.ims.ImsException e) {
90 Log.e(TAG, "registerImsRegistrationCallback: sudId=" + subId + ", " + e.getMessage());
91 throw new ServiceSpecificException(e.getCode());
92 } finally {
93 Binder.restoreCallingIdentity(token);
94 }
95 }
96
97 /**
98 * Removes an existing {@link RegistrationCallback}.
99 */
100 @Override
101 public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
102 enforceReadPrivilegedPermission("unregisterImsRegistrationCallback");
103 final long token = Binder.clearCallingIdentity();
104 try {
105 getRcsFeatureManager(subId).unregisterImsRegistrationCallback(callback);
106 } catch (ServiceSpecificException e) {
107 Log.e(TAG, "unregisterImsRegistrationCallback: error=" + e.errorCode);
108 } finally {
109 Binder.restoreCallingIdentity(token);
110 }
111 }
112
113 /**
114 * Get the IMS service registration state for the RcsFeature associated with this sub id.
115 */
116 @Override
117 public void getImsRcsRegistrationState(int subId, IIntegerConsumer consumer) {
118 enforceReadPrivilegedPermission("getImsRcsRegistrationState");
119 final long token = Binder.clearCallingIdentity();
120 try {
121 getImsPhone(subId).getImsRcsRegistrationState(regState -> {
122 try {
123 consumer.accept((regState == null)
124 ? RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED : regState);
125 } catch (RemoteException e) {
126 Log.w(TAG, "getImsRcsRegistrationState: callback is not available.");
127 }
128 });
129 } finally {
130 Binder.restoreCallingIdentity(token);
131 }
132 }
133
134 /**
135 * Gets the Transport Type associated with the current IMS RCS registration.
136 */
137 @Override
138 public void getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer) {
139 enforceReadPrivilegedPermission("getImsRcsRegistrationTransportType");
140 final long token = Binder.clearCallingIdentity();
141 try {
142 getImsPhone(subId).getImsRcsRegistrationTech(regTech -> {
143 // Convert registration tech from ImsRegistrationImplBase -> RegistrationManager
144 int regTechConverted = (regTech == null)
145 ? ImsRegistrationImplBase.REGISTRATION_TECH_NONE : regTech;
146 regTechConverted = RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(
147 regTechConverted);
148 try {
149 consumer.accept(regTechConverted);
150 } catch (RemoteException e) {
151 Log.w(TAG, "getImsRcsRegistrationTransportType: callback is not available.");
152 }
153 });
154 } finally {
155 Binder.restoreCallingIdentity(token);
156 }
157 }
158
159 /**
James.cf Lincad981c2019-12-10 20:37:56 +0800160 * Register a capability callback which will provide RCS availability updates for the
161 * subscription specified.
162 *
163 * @param subId the subscription ID
164 * @param callback The ImsCapabilityCallback to be registered.
165 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800166 @Override
James.cf Lincad981c2019-12-10 20:37:56 +0800167 public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)
168 throws RemoteException {
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 {
172 getRcsFeatureManager(subId).registerRcsAvailabilityCallback(callback);
173 } catch (com.android.ims.ImsException e) {
174 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 {
192 getRcsFeatureManager(subId).unregisterRcsAvailabilityCallback(callback);
193 } catch (com.android.ims.ImsException e) {
194 Log.e(TAG, "unregisterRcsAvailabilityCallback: sudId=" + subId + "," + e.getMessage());
195 } finally {
196 Binder.restoreCallingIdentity(token);
197 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800198 }
199
James.cf Lincad981c2019-12-10 20:37:56 +0800200 /**
201 * Query for the capability of an IMS RCS service
202 *
203 * @param subId the subscription ID
204 * @param capability the RCS capability to query.
205 * @param radioTech the radio tech that this capability failed for
206 * @return true if the RCS capability is capable for this subscription, false otherwise.
207 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800208 @Override
209 public boolean isCapable(int subId,
James.cf Lincad981c2019-12-10 20:37:56 +0800210 @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
211 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
James.cf Linaf3183c2019-10-24 00:59:00 +0800212 enforceReadPrivilegedPermission("isCapable");
James.cf Lincad981c2019-12-10 20:37:56 +0800213 final long token = Binder.clearCallingIdentity();
214 try {
215 return getRcsFeatureManager(subId).isCapable(capability, radioTech);
216 } catch (com.android.ims.ImsException e) {
217 Log.e(TAG, "isCapable: sudId=" + subId
218 + ", capability=" + capability + ", " + e.getMessage());
219 return false;
220 } finally {
221 Binder.restoreCallingIdentity(token);
222 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800223 }
224
James.cf Lincad981c2019-12-10 20:37:56 +0800225 /**
226 * Query the availability of an IMS RCS capability.
227 *
228 * @param subId the subscription ID
229 * @param capability the RCS capability to query.
230 * @return true if the RCS capability is currently available for the associated subscription,
231 * false otherwise.
232 */
James.cf Linaf3183c2019-10-24 00:59:00 +0800233 @Override
234 public boolean isAvailable(int subId,
235 @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
236 enforceReadPrivilegedPermission("isAvailable");
James.cf Lincad981c2019-12-10 20:37:56 +0800237 final long token = Binder.clearCallingIdentity();
238 try {
239 return getRcsFeatureManager(subId).isAvailable(capability);
240 } catch (com.android.ims.ImsException e) {
241 Log.e(TAG, "isAvailable: sudId=" + subId
242 + ", capability=" + capability + ", " + e.getMessage());
243 return false;
244 } finally {
245 Binder.restoreCallingIdentity(token);
246 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800247 }
248
249 @Override
250 public void requestCapabilities(int subId, List<Uri> contactNumbers,
251 IRcsUceControllerCallback c) {
252 enforceReadPrivilegedPermission("requestCapabilities");
Brad Ebinger278e9bf2020-01-22 14:17:23 -0800253 if (mRcsService == null) {
254 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
255 "IMS is not available on device.");
256 }
257 mRcsService.requestCapabilities(getImsPhone(subId).getPhoneId(), contactNumbers, c);
James.cf Linaf3183c2019-10-24 00:59:00 +0800258 }
259
260 @Override
261 public int getUcePublishState(int subId) {
262 enforceReadPrivilegedPermission("getUcePublishState");
Brad Ebinger278e9bf2020-01-22 14:17:23 -0800263 if (mRcsService == null) {
264 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
265 "IMS is not available on device.");
266 }
267 return mRcsService.getUcePublishState(getImsPhone(subId).getPhoneId());
James.cf Linaf3183c2019-10-24 00:59:00 +0800268 }
269
270 @Override
271 public boolean isUceSettingEnabled(int subId) {
272 enforceReadPrivilegedPermission("isUceSettingEnabled");
Brad Ebinger6e3543b2020-01-22 17:51:55 -0800273 return SubscriptionManager.getBooleanSubscriptionProperty(subId,
274 SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp);
James.cf Linaf3183c2019-10-24 00:59:00 +0800275 }
276
277 @Override
278 public void setUceSettingEnabled(int subId, boolean isEnabled) {
279 enforceModifyPermission();
Brad Ebinger6e3543b2020-01-22 17:51:55 -0800280 SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.IMS_RCS_UCE_ENABLED,
281 (isEnabled ? "1" : "0"));
James.cf Linaf3183c2019-10-24 00:59:00 +0800282 }
283
284 /**
285 * Make sure either called from same process as self (phone) or IPC caller has read privilege.
286 *
287 * @throws SecurityException if the caller does not have the required permission
288 */
289 private void enforceReadPrivilegedPermission(String message) {
290 mApp.enforceCallingOrSelfPermission(
291 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
292 }
293
294 /**
295 * Make sure the caller has the MODIFY_PHONE_STATE permission.
296 *
297 * @throws SecurityException if the caller does not have the required permission
298 */
299 private void enforceModifyPermission() {
300 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
301 }
James.cf Lincad981c2019-12-10 20:37:56 +0800302
303 /**
James.cf Lindc2d5422019-12-31 14:40:25 +0800304 * Retrieve ImsPhone instance.
James.cf Lincad981c2019-12-10 20:37:56 +0800305 *
306 * @param subId the subscription ID
James.cf Lindc2d5422019-12-31 14:40:25 +0800307 * @return The ImsPhone instance
308 * @throws ServiceSpecificException if getting ImsPhone instance failed.
James.cf Lincad981c2019-12-10 20:37:56 +0800309 */
James.cf Lindc2d5422019-12-31 14:40:25 +0800310 private ImsPhone getImsPhone(int subId) {
311 if (!ImsManager.isImsSupportedOnDevice(mApp)) {
312 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
313 "IMS is not available on device.");
314 }
James.cf Lincad981c2019-12-10 20:37:56 +0800315 Phone phone = PhoneGlobals.getPhone(subId);
316 if (phone == null) {
317 throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
318 "Invalid subscription Id: " + subId);
319 }
320 ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
321 if (imsPhone == null) {
James.cf Lindc2d5422019-12-31 14:40:25 +0800322 throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
323 "Cannot find ImsPhone instance: " + subId);
324 }
325 return imsPhone;
326 }
327
328 /**
329 * Retrieve RcsFeatureManager instance.
330 *
331 * @param subId the subscription ID
332 * @return The RcsFeatureManager instance
333 * @throws ServiceSpecificException if getting RcsFeatureManager instance failed.
334 */
335 private RcsFeatureManager getRcsFeatureManager(int subId) {
336 if (!ImsManager.isImsSupportedOnDevice(mApp)) {
James.cf Lincad981c2019-12-10 20:37:56 +0800337 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
James.cf Lindc2d5422019-12-31 14:40:25 +0800338 "IMS is not available on device.");
339 }
340 Phone phone = PhoneGlobals.getPhone(subId);
341 if (phone == null) {
342 throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
343 "Invalid subscription Id: " + subId);
344 }
345 ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
346 if (imsPhone == null) {
347 throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
James.cf Lincad981c2019-12-10 20:37:56 +0800348 "Cannot find ImsPhone instance: " + subId);
349 }
350 RcsFeatureManager rcsFeatureManager = imsPhone.getRcsManager();
351 if (rcsFeatureManager == null) {
352 throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
353 "Cannot find RcsFeatureManager instance: " + subId);
354 }
355 return rcsFeatureManager;
356 }
James.cf Linc9f35a42020-01-15 02:35:22 +0800357
358 void setRcsService(TelephonyRcsService rcsService) {
359 mRcsService = rcsService;
360 }
James.cf Linaf3183c2019-10-24 00:59:00 +0800361}