summaryrefslogtreecommitdiff
path: root/telephony/java/android/telephony/ims/compat/ImsService.java
blob: 97a8517afea99d368cb76304a11b68f9925143b0 (plain)
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
/*
 * Copyright (C) 2018 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.compat;

import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.CarrierConfigManager;
import android.telephony.ims.compat.feature.ImsFeature;
import android.telephony.ims.compat.feature.MMTelFeature;
import android.telephony.ims.compat.feature.RcsFeature;
import android.util.Log;
import android.util.SparseArray;

import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsMMTelFeature;
import com.android.ims.internal.IImsRcsFeature;
import com.android.ims.internal.IImsServiceController;
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.compat.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.
 * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the MMTelFeature class and will be
 *   available to place emergency calls at all times. This MUST be implemented by the default
 *   ImsService provided in the device overlay.
 *   @hide
 */
public class ImsService extends Service {

    private static final String LOG_TAG = "ImsService(Compat)";

    /**
     * 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.compat.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<>();

    /**
     * @hide
     */
    @UnsupportedAppUsage
    protected final IBinder mImsServiceController = new IImsServiceController.Stub() {

        @Override
        public IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
                IImsFeatureStatusCallback c) {
            return createEmergencyMMTelFeatureInternal(slotId, c);
        }

        @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);
        }
    };

    @UnsupportedAppUsage
    public ImsService() {
    }

    /**
     * @hide
     */
    @Override
    public IBinder onBind(Intent intent) {
        if(SERVICE_INTERFACE.equals(intent.getAction())) {
            Log.i(LOG_TAG, "ImsService(Compat) Bound.");
            return mImsServiceController;
        }
        return null;
    }

    /**
     * @hide
     */
    @VisibleForTesting
    public SparseArray<ImsFeature> getFeatures(int slotId) {
        return mFeaturesBySlot.get(slotId);
    }

    private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId,
            IImsFeatureStatusCallback c) {
        MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
        if (f != null) {
            setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
            return f.getBinder();
        } else {
            return null;
        }
    }

    private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
            IImsFeatureStatusCallback c) {
        MMTelFeature f = onCreateMMTelImsFeature(slotId);
        if (f != null) {
            setupFeature(f, slotId, ImsFeature.MMTEL, c);
            return f.getBinder();
        } else {
            return null;
        }
    }

    private IImsRcsFeature createRcsFeatureInternal(int slotId,
            IImsFeatureStatusCallback c) {
        RcsFeature f = onCreateRcsFeature(slotId);
        if (f != null) {
            setupFeature(f, slotId, ImsFeature.RCS, c);
            return f.getBinder();
        } else {
            return null;
        }
    }

    private void setupFeature(ImsFeature f, int slotId, int featureType,
            IImsFeatureStatusCallback c) {
        f.setContext(this);
        f.setSlotId(slotId);
        f.addImsFeatureStatusCallback(c);
        addImsFeature(slotId, featureType, f);
        // TODO: Remove once new onFeatureReady AIDL is merged in.
        f.onFeatureReady();
    }

    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);
        }
    }

    /**
     * @return An implementation of MMTelFeature that will be used by the system for MMTel
     * functionality. Must be able to handle emergency calls at any time as well.
     * @hide
     */
    public @Nullable MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
        return null;
    }

    /**
     * @return An implementation of MMTelFeature that will be used by the system for MMTel
     * functionality.
     * @hide
     */
    public @Nullable MMTelFeature onCreateMMTelImsFeature(int slotId) {
        return null;
    }

    /**
     * @return An implementation of RcsFeature that will be used by the system for RCS.
     * @hide
     */
    public @Nullable RcsFeature onCreateRcsFeature(int slotId) {
        return null;
    }
}