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