1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
|
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.telephony.ims.internal;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.CarrierConfigManager;
import android.telephony.ims.internal.aidl.IImsConfig;
import android.telephony.ims.internal.aidl.IImsMmTelFeature;
import android.telephony.ims.internal.aidl.IImsRcsFeature;
import android.telephony.ims.internal.aidl.IImsServiceController;
import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
import android.telephony.ims.internal.feature.ImsFeature;
import android.telephony.ims.internal.feature.MmTelFeature;
import android.telephony.ims.internal.feature.RcsFeature;
import android.telephony.ims.internal.stub.ImsConfigImplBase;
import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import android.util.SparseArray;
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsRegistration;
import com.android.internal.annotations.VisibleForTesting;
/**
* Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
* ImsService must register the service in their AndroidManifest to be detected by the framework.
* First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
* permission. Then, the ImsService definition in the manifest must follow the following format:
*
* ...
* <service android:name=".EgImsService"
* android:permission="android.permission.BIND_IMS_SERVICE" >
* <!-- Apps must declare which features they support as metadata. The different categories are
* defined below. In this example, the RCS_FEATURE feature is supported. -->
* <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
* <intent-filter>
* <action android:name="android.telephony.ims.ImsService" />
* </intent-filter>
* </service>
* ...
*
* The telephony framework will then bind to the ImsService you have defined in your manifest
* if you are either:
* 1) Defined as the default ImsService for the device in the device overlay using
* "config_ims_package".
* 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
* {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
*
* The features that are currently supported in an ImsService are:
* - RCS_FEATURE: This ImsService implements the RcsFeature class.
* - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
* @hide
*/
public class ImsService extends Service {
private static final String LOG_TAG = "ImsService";
/**
* The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
* @hide
*/
public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
// A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
// slot.
// We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
// call ImsFeature#onFeatureRemoved.
private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
private IImsServiceControllerListener mListener;
/**
* Listener that notifies the framework of ImsService changes.
*/
public static class Listener extends IImsServiceControllerListener.Stub {
/**
* The IMS features that this ImsService supports has changed.
* @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
* that this ImsService supports. This may trigger the addition/removal of feature
* in this service.
*/
public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
}
}
/**
* @hide
*/
protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
@Override
public void setListener(IImsServiceControllerListener l) {
mListener = l;
}
@Override
public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
return createMmTelFeatureInternal(slotId, c);
}
@Override
public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
return createRcsFeatureInternal(slotId, c);
}
@Override
public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
throws RemoteException {
ImsService.this.removeImsFeature(slotId, featureType, c);
}
@Override
public ImsFeatureConfiguration querySupportedImsFeatures() {
return ImsService.this.querySupportedImsFeatures();
}
@Override
public void notifyImsServiceReadyForFeatureCreation() {
ImsService.this.readyForFeatureCreation();
}
@Override
public void notifyImsFeatureReady(int slotId, int featureType)
throws RemoteException {
ImsService.this.notifyImsFeatureReady(slotId, featureType);
}
@Override
public IImsConfig getConfig(int slotId) throws RemoteException {
ImsConfigImplBase c = ImsService.this.getConfig(slotId);
return c != null ? c.getBinder() : null;
}
@Override
public IImsRegistration getRegistration(int slotId) throws RemoteException {
ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
return r != null ? r.getBinder() : null;
}
};
/**
* @hide
*/
@Override
public IBinder onBind(Intent intent) {
if(SERVICE_INTERFACE.equals(intent.getAction())) {
Log.i(LOG_TAG, "ImsService Bound.");
return mImsServiceController;
}
return null;
}
/**
* @hide
*/
@VisibleForTesting
public SparseArray<ImsFeature> getFeatures(int slotId) {
return mFeaturesBySlot.get(slotId);
}
private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
IImsFeatureStatusCallback c) {
MmTelFeature f = createMmTelFeature(slotId);
if (f != null) {
setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
return f.getBinder();
} else {
Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
return null;
}
}
private IImsRcsFeature createRcsFeatureInternal(int slotId,
IImsFeatureStatusCallback c) {
RcsFeature f = createRcsFeature(slotId);
if (f != null) {
setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
return f.getBinder();
} else {
Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
return null;
}
}
private void setupFeature(ImsFeature f, int slotId, int featureType,
IImsFeatureStatusCallback c) {
f.addImsFeatureStatusCallback(c);
f.initialize(this, slotId);
addImsFeature(slotId, featureType, f);
}
private void addImsFeature(int slotId, int featureType, ImsFeature f) {
synchronized (mFeaturesBySlot) {
// Get SparseArray for Features, by querying slot Id
SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
if (features == null) {
// Populate new SparseArray of features if it doesn't exist for this slot yet.
features = new SparseArray<>();
mFeaturesBySlot.put(slotId, features);
}
features.put(featureType, f);
}
}
private void removeImsFeature(int slotId, int featureType,
IImsFeatureStatusCallback c) {
synchronized (mFeaturesBySlot) {
// get ImsFeature associated with the slot/feature
SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
if (features == null) {
Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
+ slotId);
return;
}
ImsFeature f = features.get(featureType);
if (f == null) {
Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
+ featureType + " exists on slot " + slotId);
return;
}
f.removeImsFeatureStatusCallback(c);
f.onFeatureRemoved();
features.remove(featureType);
}
}
private void notifyImsFeatureReady(int slotId, int featureType) {
synchronized (mFeaturesBySlot) {
// get ImsFeature associated with the slot/feature
SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
if (features == null) {
Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " +
"slot " + slotId);
return;
}
ImsFeature f = features.get(featureType);
if (f == null) {
Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type "
+ featureType + " exists on slot " + slotId);
return;
}
f.onFeatureReady();
}
}
/**
* When called, provide the {@link ImsFeatureConfiguration} that this ImsService currently
* supports. This will trigger the framework to set up the {@link ImsFeature}s that correspond
* to the {@link ImsFeature.FeatureType}s configured here.
* @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports,
* defined in {@link ImsFeature.FeatureType}.
*/
public ImsFeatureConfiguration querySupportedImsFeatures() {
// Return empty for base implementation
return new ImsFeatureConfiguration();
}
/**
* Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
* features, defined in {@link ImsFeature.FeatureType} that this ImsService supports. This may
* trigger the framework to add/remove new ImsFeatures, depending on the configuration.
*/
public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
throws RemoteException {
if (mListener == null) {
throw new IllegalStateException("Framework is not ready");
}
mListener.onUpdateSupportedImsFeatures(c);
}
/**
* The ImsService has been bound and is ready for ImsFeature creation based on the Features that
* the ImsService has registered for with the framework, either in the manifest or via
* The ImsService should use this signal instead of onCreate/onBind or similar to perform
* feature initialization because the framework may bind to this service multiple times to
* query the ImsService's {@link ImsFeatureConfiguration} via
* {@link #querySupportedImsFeatures()}before creating features.
*/
public void readyForFeatureCreation() {
}
/**
* When called, the framework is requesting that a new MmTelFeature is created for the specified
* slot.
*
* @param slotId The slot ID that the MMTel Feature is being created for.
* @return The newly created MmTelFeature associated with the slot or null if the feature is not
* supported.
*/
public MmTelFeature createMmTelFeature(int slotId) {
return null;
}
/**
* When called, the framework is requesting that a new RcsFeature is created for the specified
* slot
*
* @param slotId The slot ID that the RCS Feature is being created for.
* @return The newly created RcsFeature associated with the slot or null if the feature is not
* supported.
*/
public RcsFeature createRcsFeature(int slotId) {
return null;
}
/**
* @param slotId The slot that the IMS configuration is associated with.
* @return ImsConfig implementation that is associated with the specified slot.
*/
public ImsConfigImplBase getConfig(int slotId) {
return new ImsConfigImplBase();
}
/**
* @param slotId The slot that is associated with the IMS Registration.
* @return the ImsRegistration implementation associated with the slot.
*/
public ImsRegistrationImplBase getRegistration(int slotId) {
return new ImsRegistrationImplBase();
}
}
|