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