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.telephony.Rlog; 35 import android.util.SparseArray; 36 37 import com.android.internal.annotations.VisibleForTesting; 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 * @param slotIndex SIM slot id the data service associated with. 462 * @return Data service object. Null if failed to create the provider (e.g. invalid slot index) 463 */ 464 @Nullable onCreateDataServiceProvider(int slotIndex)465 public abstract DataServiceProvider onCreateDataServiceProvider(int slotIndex); 466 467 @Override onBind(Intent intent)468 public IBinder onBind(Intent intent) { 469 if (intent == null || !SERVICE_INTERFACE.equals(intent.getAction())) { 470 loge("Unexpected intent " + intent); 471 return null; 472 } 473 return mBinder; 474 } 475 476 @Override onUnbind(Intent intent)477 public boolean onUnbind(Intent intent) { 478 mHandler.obtainMessage(DATA_SERVICE_REMOVE_ALL_DATA_SERVICE_PROVIDERS).sendToTarget(); 479 return false; 480 } 481 482 @Override onDestroy()483 public void onDestroy() { 484 mHandlerThread.quit(); 485 super.onDestroy(); 486 } 487 488 /** 489 * A wrapper around IDataService that forwards calls to implementations of {@link DataService}. 490 */ 491 private class IDataServiceWrapper extends IDataService.Stub { 492 @Override createDataServiceProvider(int slotIndex)493 public void createDataServiceProvider(int slotIndex) { 494 mHandler.obtainMessage(DATA_SERVICE_CREATE_DATA_SERVICE_PROVIDER, slotIndex, 0) 495 .sendToTarget(); 496 } 497 498 @Override removeDataServiceProvider(int slotIndex)499 public void removeDataServiceProvider(int slotIndex) { 500 mHandler.obtainMessage(DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER, slotIndex, 0) 501 .sendToTarget(); 502 } 503 504 @Override setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties, IDataServiceCallback callback)505 public void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile, 506 boolean isRoaming, boolean allowRoaming, int reason, 507 LinkProperties linkProperties, IDataServiceCallback callback) { 508 mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0, 509 new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming, 510 allowRoaming, reason, linkProperties, callback)) 511 .sendToTarget(); 512 } 513 514 @Override deactivateDataCall(int slotIndex, int cid, int reason, IDataServiceCallback callback)515 public void deactivateDataCall(int slotIndex, int cid, int reason, 516 IDataServiceCallback callback) { 517 mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, slotIndex, 0, 518 new DeactivateDataCallRequest(cid, reason, callback)) 519 .sendToTarget(); 520 } 521 522 @Override setInitialAttachApn(int slotIndex, DataProfile dataProfile, boolean isRoaming, IDataServiceCallback callback)523 public void setInitialAttachApn(int slotIndex, DataProfile dataProfile, boolean isRoaming, 524 IDataServiceCallback callback) { 525 mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN, slotIndex, 0, 526 new SetInitialAttachApnRequest(dataProfile, isRoaming, callback)) 527 .sendToTarget(); 528 } 529 530 @Override setDataProfile(int slotIndex, List<DataProfile> dps, boolean isRoaming, IDataServiceCallback callback)531 public void setDataProfile(int slotIndex, List<DataProfile> dps, boolean isRoaming, 532 IDataServiceCallback callback) { 533 mHandler.obtainMessage(DATA_SERVICE_REQUEST_SET_DATA_PROFILE, slotIndex, 0, 534 new SetDataProfileRequest(dps, isRoaming, callback)).sendToTarget(); 535 } 536 537 @Override requestDataCallList(int slotIndex, IDataServiceCallback callback)538 public void requestDataCallList(int slotIndex, IDataServiceCallback callback) { 539 if (callback == null) { 540 loge("requestDataCallList: callback is null"); 541 return; 542 } 543 mHandler.obtainMessage(DATA_SERVICE_REQUEST_REQUEST_DATA_CALL_LIST, slotIndex, 0, 544 callback).sendToTarget(); 545 } 546 547 @Override registerForDataCallListChanged(int slotIndex, IDataServiceCallback callback)548 public void registerForDataCallListChanged(int slotIndex, IDataServiceCallback callback) { 549 if (callback == null) { 550 loge("registerForDataCallListChanged: callback is null"); 551 return; 552 } 553 mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_DATA_CALL_LIST_CHANGED, slotIndex, 554 0, callback).sendToTarget(); 555 } 556 557 @Override unregisterForDataCallListChanged(int slotIndex, IDataServiceCallback callback)558 public void unregisterForDataCallListChanged(int slotIndex, IDataServiceCallback callback) { 559 if (callback == null) { 560 loge("unregisterForDataCallListChanged: callback is null"); 561 return; 562 } 563 mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_DATA_CALL_LIST_CHANGED, 564 slotIndex, 0, callback).sendToTarget(); 565 } 566 } 567 log(String s)568 private void log(String s) { 569 Rlog.d(TAG, s); 570 } 571 loge(String s)572 private void loge(String s) { 573 Rlog.e(TAG, s); 574 } 575 } 576