1 /*
2  * Copyright 2017 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 
17 package android.telephony.data;
18 
19 import android.annotation.CallSuper;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.app.Service;
24 import android.content.Intent;
25 import android.net.LinkProperties;
26 import android.os.Handler;
27 import android.os.HandlerThread;
28 import android.os.IBinder;
29 import android.os.Looper;
30 import android.os.Message;
31 import android.os.RemoteException;
32 import android.telephony.AccessNetworkConstants;
33 import android.telephony.Rlog;
34 import android.util.SparseArray;
35 
36 import com.android.internal.annotations.VisibleForTesting;
37 
38 import java.lang.annotation.Retention;
39 import java.lang.annotation.RetentionPolicy;
40 import java.util.ArrayList;
41 import java.util.List;
42 
43 /**
44  * Base class of data service. Services that extend DataService must register the service in
45  * their AndroidManifest to be detected by the framework. They must be protected by the permission
46  * "android.permission.BIND_DATA_SERVICE". The data service definition in the manifest must follow
47  * the following format:
48  * ...
49  * <service android:name=".xxxDataService"
50  *     android:permission="android.permission.BIND_DATA_SERVICE" >
51  *     <intent-filter>
52  *         <action android:name="android.telephony.data.DataService" />
53  *     </intent-filter>
54  * </service>
55  * @hide
56  */
57 public abstract class DataService extends Service {
58     private static final String TAG = DataService.class.getSimpleName();
59 
60     public static final String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
61     public static final String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
62 
63     /** {@hide} */
64     @IntDef(prefix = "REQUEST_REASON_", value = {
65             REQUEST_REASON_NORMAL,
66             REQUEST_REASON_HANDOVER,
67     })
68     @Retention(RetentionPolicy.SOURCE)
69     public @interface SetupDataReason {}
70 
71     /** {@hide} */
72     @IntDef(prefix = "REQUEST_REASON_", value = {
73             REQUEST_REASON_NORMAL,
74             REQUEST_REASON_SHUTDOWN,
75             REQUEST_REASON_HANDOVER,
76     })
77     @Retention(RetentionPolicy.SOURCE)
78     public @interface DeactivateDataReason {}
79 
80 
81     /** The reason of the data request is normal */
82     public static final int REQUEST_REASON_NORMAL = 1;
83 
84     /** The reason of the data request is device shutdown */
85     public static final int REQUEST_REASON_SHUTDOWN = 2;
86 
87     /** The reason of the data request is IWLAN handover */
88     public static final int REQUEST_REASON_HANDOVER = 3;
89 
90     private static final int DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER                 = 1;
91     private static final int DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER                 = 2;
92     private static final int DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS            = 3;
93     private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL                      = 4;
94     private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL                 = 5;
95     private static final int DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN               = 6;
96     private static final int DATA_SERVICE_REQUEST_SET_DATA_PROFILE                     = 7;
97     private static final int DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST                   = 8;
98     private static final int DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED      = 9;
99     private static final int DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED    = 10;
100     private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED            = 11;
101 
102     private final HandlerThread mHandlerThread;
103 
104     private final DataServiceHandler mHandler;
105 
106     private final SparseArray<DataServiceProvider> mServiceMap = new SparseArray<>();
107 
108     /** @hide */
109     @VisibleForTesting
110     public final IDataServiceWrapper mBinder = new IDataServiceWrapper();
111 
112     /**
113      * The abstract class of the actual data service implementation. The data service provider
114      * must extend this class to support data connection. Note that each instance of data service
115      * provider is associated with one physical SIM slot.
116      */
117     public class DataServiceProvider {
118 
119         private final int mSlotId;
120 
121         private final List<IDataServiceCallback> mDataCallListChangedCallbacks = new ArrayList<>();
122 
123         /**
124          * Constructor
125          * @param slotId SIM slot id the data service provider associated with.
126          */
DataServiceProvider(int slotId)127         public DataServiceProvider(int slotId) {
128             mSlotId = slotId;
129         }
130 
131         /**
132          * @return SIM slot id the data service provider associated with.
133          */
getSlotId()134         public final int getSlotId() {
135             return mSlotId;
136         }
137 
138         /**
139          * Setup a data connection. The data service provider must implement this method to support
140          * establishing a packet data connection. When completed or error, the service must invoke
141          * the provided callback to notify the platform.
142          *
143          * @param accessNetworkType Access network type that the data call will be established on.
144          *        Must be one of {@link AccessNetworkConstants.AccessNetworkType}.
145          * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
146          * @param isRoaming True if the device is data roaming.
147          * @param allowRoaming True if data roaming is allowed by the user.
148          * @param reason The reason for data setup. Must be {@link #REQUEST_REASON_NORMAL} or
149          *        {@link #REQUEST_REASON_HANDOVER}.
150          * @param linkProperties If {@code reason} is {@link #REQUEST_REASON_HANDOVER}, this is the
151          *        link properties of the existing data connection, otherwise null.
152          * @param callback The result callback for this request. Null if the client does not care
153          *        about the result.
154          */
setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, @SetupDataReason int reason, @Nullable LinkProperties linkProperties, @Nullable DataServiceCallback callback)155         public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
156                                   boolean allowRoaming, @SetupDataReason int reason,
157                                   @Nullable LinkProperties linkProperties,
158                                   @Nullable DataServiceCallback callback) {
159             // The default implementation is to return unsupported.
160             callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
161         }
162 
163         /**
164          * Deactivate a data connection. The data service provider must implement this method to
165          * support data connection tear down. When completed or error, the service must invoke the
166          * provided callback to notify the platform.
167          *
168          * @param cid Call id returned in the callback of {@link DataServiceProvider#setupDataCall(
169          *        int, DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)}.
170          * @param reason The reason for data deactivation. Must be {@link #REQUEST_REASON_NORMAL},
171          *        {@link #REQUEST_REASON_SHUTDOWN} or {@link #REQUEST_REASON_HANDOVER}.
172          * @param callback The result callback for this request. Null if the client does not care
173          *        about the result.
174          *
175          */
deactivateDataCall(int cid, @DeactivateDataReason int reason, @Nullable DataServiceCallback callback)176         public void deactivateDataCall(int cid, @DeactivateDataReason int reason,
177                                        @Nullable DataServiceCallback callback) {
178             // The default implementation is to return unsupported.
179             callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
180         }
181 
182         /**
183          * Set an APN to initial attach network.
184          *
185          * @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
186          * @param isRoaming True if the device is data roaming.
187          * @param callback The result callback for this request. Null if the client does not care
188          *        about the result.
189          */
setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, @Nullable DataServiceCallback callback)190         public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
191                                         @Nullable DataServiceCallback callback) {
192             // The default implementation is to return unsupported.
193             callback.onSetInitialAttachApnComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
194         }
195 
196         /**
197          * Send current carrier's data profiles to the data service for data call setup. This is
198          * only for CDMA carrier that can change the profile through OTA. The data service should
199          * always uses the latest data profile sent by the framework.
200          *
201          * @param dps A list of data profiles.
202          * @param isRoaming True if the device is data roaming.
203          * @param callback The result callback for this request. Null if the client does not care
204          *        about the result.
205          */
setDataProfile(List<DataProfile> dps, boolean isRoaming, @Nullable DataServiceCallback callback)206         public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
207                                    @Nullable DataServiceCallback callback) {
208             // The default implementation is to return unsupported.
209             callback.onSetDataProfileComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
210         }
211 
212         /**
213          * Get the active data call list.
214          *
215          * @param callback The result callback for this request.
216          */
getDataCallList(@onNull DataServiceCallback callback)217         public void getDataCallList(@NonNull DataServiceCallback callback) {
218             // The default implementation is to return unsupported.
219             callback.onGetDataCallListComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
220         }
221 
registerForDataCallListChanged(IDataServiceCallback callback)222         private void registerForDataCallListChanged(IDataServiceCallback callback) {
223             synchronized (mDataCallListChangedCallbacks) {
224                 mDataCallListChangedCallbacks.add(callback);
225             }
226         }
227 
unregisterForDataCallListChanged(IDataServiceCallback callback)228         private void unregisterForDataCallListChanged(IDataServiceCallback callback) {
229             synchronized (mDataCallListChangedCallbacks) {
230                 mDataCallListChangedCallbacks.remove(callback);
231             }
232         }
233 
234         /**
235          * Notify the system that current data call list changed. Data service must invoke this
236          * method whenever there is any data call status changed.
237          *
238          * @param dataCallList List of the current active data call.
239          */
notifyDataCallListChanged(List<DataCallResponse> dataCallList)240         public final void notifyDataCallListChanged(List<DataCallResponse> dataCallList) {
241             synchronized (mDataCallListChangedCallbacks) {
242                 for (IDataServiceCallback callback : mDataCallListChangedCallbacks) {
243                     mHandler.obtainMessage(DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED, mSlotId,
244                             0, new DataCallListChangedIndication(dataCallList, callback))
245                             .sendToTarget();
246                 }
247             }
248         }
249 
250         /**
251          * Called when the instance of data service is destroyed (e.g. got unbind or binder died).
252          */
253         @CallSuper
onDestroy()254         protected void onDestroy() {
255             mDataCallListChangedCallbacks.clear();
256         }
257     }
258 
259     private static final class SetupDataCallRequest {
260         public final int accessNetworkType;
261         public final DataProfile dataProfile;
262         public final boolean isRoaming;
263         public final boolean allowRoaming;
264         public final int reason;
265         public final LinkProperties linkProperties;
266         public final IDataServiceCallback callback;
SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties, IDataServiceCallback callback)267         SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
268                              boolean allowRoaming, int reason, LinkProperties linkProperties,
269                              IDataServiceCallback callback) {
270             this.accessNetworkType = accessNetworkType;
271             this.dataProfile = dataProfile;
272             this.isRoaming = isRoaming;
273             this.allowRoaming = allowRoaming;
274             this.linkProperties = linkProperties;
275             this.reason = reason;
276             this.callback = callback;
277         }
278     }
279 
280     private static final class DeactivateDataCallRequest {
281         public final int cid;
282         public final int reason;
283         public final IDataServiceCallback callback;
DeactivateDataCallRequest(int cid, int reason, IDataServiceCallback callback)284         DeactivateDataCallRequest(int cid, int reason, IDataServiceCallback callback) {
285             this.cid = cid;
286             this.reason = reason;
287             this.callback = callback;
288         }
289     }
290 
291     private static final class SetInitialAttachApnRequest {
292         public final DataProfile dataProfile;
293         public final boolean isRoaming;
294         public final IDataServiceCallback callback;
SetInitialAttachApnRequest(DataProfile dataProfile, boolean isRoaming, IDataServiceCallback callback)295         SetInitialAttachApnRequest(DataProfile dataProfile, boolean isRoaming,
296                                    IDataServiceCallback callback) {
297             this.dataProfile = dataProfile;
298             this.isRoaming = isRoaming;
299             this.callback = callback;
300         }
301     }
302 
303     private static final class SetDataProfileRequest {
304         public final List<DataProfile> dps;
305         public final boolean isRoaming;
306         public final IDataServiceCallback callback;
SetDataProfileRequest(List<DataProfile> dps, boolean isRoaming, IDataServiceCallback callback)307         SetDataProfileRequest(List<DataProfile> dps, boolean isRoaming,
308                               IDataServiceCallback callback) {
309             this.dps = dps;
310             this.isRoaming = isRoaming;
311             this.callback = callback;
312         }
313     }
314 
315     private static final class DataCallListChangedIndication {
316         public final List<DataCallResponse> dataCallList;
317         public final IDataServiceCallback callback;
DataCallListChangedIndication(List<DataCallResponse> dataCallList, IDataServiceCallback callback)318         DataCallListChangedIndication(List<DataCallResponse> dataCallList,
319                                       IDataServiceCallback callback) {
320             this.dataCallList = dataCallList;
321             this.callback = callback;
322         }
323     }
324 
325     private class DataServiceHandler extends Handler {
326 
DataServiceHandler(Looper looper)327         DataServiceHandler(Looper looper) {
328             super(looper);
329         }
330 
331         @Override
handleMessage(Message message)332         public void handleMessage(Message message) {
333             IDataServiceCallback callback;
334             final int slotId = message.arg1;
335             DataServiceProvider serviceProvider = mServiceMap.get(slotId);
336 
337             switch (message.what) {
338                 case DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER:
339                     serviceProvider = createDataServiceProvider(message.arg1);
340                     if (serviceProvider != null) {
341                         mServiceMap.put(slotId, serviceProvider);
342                     }
343                     break;
344                 case DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER:
345                     if (serviceProvider != null) {
346                         serviceProvider.onDestroy();
347                         mServiceMap.remove(slotId);
348                     }
349                     break;
350                 case DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS:
351                     for (int i = 0; i < mServiceMap.size(); i++) {
352                         serviceProvider = mServiceMap.get(i);
353                         if (serviceProvider != null) {
354                             serviceProvider.onDestroy();
355                         }
356                     }
357                     mServiceMap.clear();
358                     break;
359                 case DATA_SERVICE_REQUEST_SETUP_DATA_CALL:
360                     if (serviceProvider == null) break;
361                     SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
362                     serviceProvider.setupDataCall(setupDataCallRequest.accessNetworkType,
363                             setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
364                             setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
365                             setupDataCallRequest.linkProperties,
366                             (setupDataCallRequest.callback != null)
367                                     ? new DataServiceCallback(setupDataCallRequest.callback)
368                                     : null);
369 
370                     break;
371                 case DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL:
372                     if (serviceProvider == null) break;
373                     DeactivateDataCallRequest deactivateDataCallRequest =
374                             (DeactivateDataCallRequest) message.obj;
375                     serviceProvider.deactivateDataCall(deactivateDataCallRequest.cid,
376                             deactivateDataCallRequest.reason,
377                             (deactivateDataCallRequest.callback != null)
378                                     ? new DataServiceCallback(deactivateDataCallRequest.callback)
379                                     : null);
380                     break;
381                 case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN:
382                     if (serviceProvider == null) break;
383                     SetInitialAttachApnRequest setInitialAttachApnRequest =
384                             (SetInitialAttachApnRequest) message.obj;
385                     serviceProvider.setInitialAttachApn(setInitialAttachApnRequest.dataProfile,
386                             setInitialAttachApnRequest.isRoaming,
387                             (setInitialAttachApnRequest.callback != null)
388                                     ? new DataServiceCallback(setInitialAttachApnRequest.callback)
389                                     : null);
390                     break;
391                 case DATA_SERVICE_REQUEST_SET_DATA_PROFILE:
392                     if (serviceProvider == null) break;
393                     SetDataProfileRequest setDataProfileRequest =
394                             (SetDataProfileRequest) message.obj;
395                     serviceProvider.setDataProfile(setDataProfileRequest.dps,
396                             setDataProfileRequest.isRoaming,
397                             (setDataProfileRequest.callback != null)
398                                     ? new DataServiceCallback(setDataProfileRequest.callback)
399                                     : null);
400                     break;
401                 case DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST:
402                     if (serviceProvider == null) break;
403 
404                     serviceProvider.getDataCallList(new DataServiceCallback(
405                             (IDataServiceCallback) message.obj));
406                     break;
407                 case DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED:
408                     if (serviceProvider == null) break;
409                     serviceProvider.registerForDataCallListChanged((IDataServiceCallback) message.obj);
410                     break;
411                 case DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED:
412                     if (serviceProvider == null) break;
413                     callback = (IDataServiceCallback) message.obj;
414                     serviceProvider.unregisterForDataCallListChanged(callback);
415                     break;
416                 case DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED:
417                     if (serviceProvider == null) break;
418                     DataCallListChangedIndication indication =
419                             (DataCallListChangedIndication) message.obj;
420                     try {
421                         indication.callback.onDataCallListChanged(indication.dataCallList);
422                     } catch (RemoteException e) {
423                         loge("Failed to call onDataCallListChanged. " + e);
424                     }
425                     break;
426             }
427         }
428     }
429 
430     /**
431      * Default constructor.
432      */
DataService()433     public DataService() {
434         mHandlerThread = new HandlerThread(TAG);
435         mHandlerThread.start();
436 
437         mHandler = new DataServiceHandler(mHandlerThread.getLooper());
438         log("Data service created");
439     }
440 
441     /**
442      * Create the instance of {@link DataServiceProvider}. Data service provider must override
443      * this method to facilitate the creation of {@link DataServiceProvider} instances. The system
444      * will call this method after binding the data service for each active SIM slot id.
445      *
446      * @param slotId SIM slot id the data service associated with.
447      * @return Data service object
448      */
createDataServiceProvider(int slotId)449     public abstract DataServiceProvider createDataServiceProvider(int slotId);
450 
451     /** @hide */
452     @Override
onBind(Intent intent)453     public IBinder onBind(Intent intent) {
454         if (intent == null || !DATA_SERVICE_INTERFACE.equals(intent.getAction())) {
455             loge("Unexpected intent " + intent);
456             return null;
457         }
458         return mBinder;
459     }
460 
461     /** @hide */
462     @Override
onUnbind(Intent intent)463     public boolean onUnbind(Intent intent) {
464         mHandler.obtainMessage(DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS).sendToTarget();
465         return false;
466     }
467 
468     /** @hide */
469     @Override
onDestroy()470     public void onDestroy() {
471         mHandlerThread.quit();
472     }
473 
474     /**
475      * A wrapper around IDataService that forwards calls to implementations of {@link DataService}.
476      */
477     private class IDataServiceWrapper extends IDataService.Stub {
478         @Override
createDataServiceProvider(int slotId)479         public void createDataServiceProvider(int slotId) {
480             mHandler.obtainMessage(DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER, slotId, 0)
481                     .sendToTarget();
482         }
483 
484         @Override
removeDataServiceProvider(int slotId)485         public void removeDataServiceProvider(int slotId) {
486             mHandler.obtainMessage(DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER, slotId, 0)
487                     .sendToTarget();
488         }
489 
490         @Override
setupDataCall(int slotId, int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties, IDataServiceCallback callback)491         public void setupDataCall(int slotId, int accessNetworkType, DataProfile dataProfile,
492                                   boolean isRoaming, boolean allowRoaming, int reason,
493                                   LinkProperties linkProperties, IDataServiceCallback callback) {
494             mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotId, 0,
495                     new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
496                             allowRoaming, reason, linkProperties, callback))
497                     .sendToTarget();
498         }
499 
500         @Override
deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback)501         public void deactivateDataCall(int slotId, int cid, int reason,
502                                        IDataServiceCallback callback) {
503             mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, slotId, 0,
504                     new DeactivateDataCallRequest(cid, reason, callback))
505                     .sendToTarget();
506         }
507 
508         @Override
setInitialAttachApn(int slotId, DataProfile dataProfile, boolean isRoaming, IDataServiceCallback callback)509         public void setInitialAttachApn(int slotId, DataProfile dataProfile, boolean isRoaming,
510                                         IDataServiceCallback callback) {
511             mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, slotId, 0,
512                     new SetInitialAttachApnRequest(dataProfile, isRoaming, callback))
513                     .sendToTarget();
514         }
515 
516         @Override
setDataProfile(int slotId, List<DataProfile> dps, boolean isRoaming, IDataServiceCallback callback)517         public void setDataProfile(int slotId, List<DataProfile> dps, boolean isRoaming,
518                                    IDataServiceCallback callback) {
519             mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, slotId, 0,
520                     new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget();
521         }
522 
523         @Override
getDataCallList(int slotId, IDataServiceCallback callback)524         public void getDataCallList(int slotId, IDataServiceCallback callback) {
525             if (callback == null) {
526                 loge("getDataCallList: callback is null");
527                 return;
528             }
529             mHandler.obtainMessage(DATA_SERVICE_REQUEST_GET_DATA_CALL_LIST, slotId, 0,
530                     callback).sendToTarget();
531         }
532 
533         @Override
registerForDataCallListChanged(int slotId, IDataServiceCallback callback)534         public void registerForDataCallListChanged(int slotId, IDataServiceCallback callback) {
535             if (callback == null) {
536                 loge("registerForDataCallListChanged: callback is null");
537                 return;
538             }
539             mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, slotId,
540                     0, callback).sendToTarget();
541         }
542 
543         @Override
unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback)544         public void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback) {
545             if (callback == null) {
546                 loge("unregisterForDataCallListChanged: callback is null");
547                 return;
548             }
549             mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, slotId,
550                     0, callback).sendToTarget();
551         }
552     }
553 
log(String s)554     private void log(String s) {
555         Rlog.d(TAG, s);
556     }
557 
loge(String s)558     private void loge(String s) {
559         Rlog.e(TAG, s);
560     }
561 }
562