1 /* 2 * Copyright (C) 2014 MediaTek Inc. 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 com.android.internal.telephony.dataconnection; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.os.Handler; 24 import android.os.Message; 25 import android.os.AsyncResult; 26 import android.os.SystemProperties; 27 import android.telephony.ServiceState; 28 import android.telephony.TelephonyManager; 29 import android.telephony.SubscriptionManager; 30 31 import com.android.internal.telephony.IccCardConstants; 32 import com.android.internal.telephony.Phone; 33 import com.android.internal.telephony.PhoneConstants; 34 import com.android.internal.telephony.PhoneBase; 35 import com.android.internal.telephony.PhoneProxy; 36 import com.android.internal.telephony.TelephonyIntents; 37 import com.android.internal.util.AsyncChannel; 38 import com.android.internal.telephony.PhoneFactory; 39 import com.android.internal.telephony.TelephonyProperties; 40 import com.android.internal.telephony.DefaultPhoneNotifier; 41 import com.android.internal.telephony.SubscriptionController; 42 43 import android.util.Log; 44 import java.util.HashSet; 45 import java.util.Iterator; 46 import android.os.Registrant; 47 import android.os.RegistrantList; 48 import android.telephony.Rlog; 49 50 public class DctController extends Handler { 51 private static final String LOG_TAG = "DctController"; 52 private static final boolean DBG = true; 53 54 private static final int EVENT_PHONE1_DETACH = 1; 55 private static final int EVENT_PHONE2_DETACH = 2; 56 private static final int EVENT_PHONE3_DETACH = 3; 57 private static final int EVENT_PHONE4_DETACH = 4; 58 private static final int EVENT_PHONE1_RADIO_OFF = 5; 59 private static final int EVENT_PHONE2_RADIO_OFF = 6; 60 private static final int EVENT_PHONE3_RADIO_OFF = 7; 61 private static final int EVENT_PHONE4_RADIO_OFF = 8; 62 63 private static final int PHONE_NONE = -1; 64 65 private static DctController sDctController; 66 67 private static final int EVENT_ALL_DATA_DISCONNECTED = 1; 68 private static final int EVENT_SET_DATA_ALLOW_DONE = 2; 69 70 private RegistrantList mNotifyDataSwitchInfo = new RegistrantList(); 71 private SubscriptionController mSubController = SubscriptionController.getInstance(); 72 73 private Phone mActivePhone; 74 private int mPhoneNum; 75 private boolean[] mServicePowerOffFlag; 76 private PhoneProxy[] mPhones; 77 private DcSwitchState[] mDcSwitchState; 78 private DcSwitchAsyncChannel[] mDcSwitchAsyncChannel; 79 private Handler[] mDcSwitchStateHandler; 80 81 private HashSet<String> mApnTypes = new HashSet<String>(); 82 83 private BroadcastReceiver mDataStateReceiver; 84 private Context mContext; 85 86 private int mCurrentDataPhone = PHONE_NONE; 87 private int mRequestedDataPhone = PHONE_NONE; 88 89 private Handler mRspHander = new Handler() { 90 public void handleMessage(Message msg){ 91 AsyncResult ar; 92 switch(msg.what) { 93 case EVENT_PHONE1_DETACH: 94 case EVENT_PHONE2_DETACH: 95 case EVENT_PHONE3_DETACH: 96 case EVENT_PHONE4_DETACH: 97 logd("EVENT_PHONE" + msg.what + 98 "_DETACH: mRequestedDataPhone=" + mRequestedDataPhone); 99 mCurrentDataPhone = PHONE_NONE; 100 if (mRequestedDataPhone != PHONE_NONE) { 101 mCurrentDataPhone = mRequestedDataPhone; 102 mRequestedDataPhone = PHONE_NONE; 103 104 Iterator<String> itrType = mApnTypes.iterator(); 105 while (itrType.hasNext()) { 106 mDcSwitchAsyncChannel[mCurrentDataPhone].connectSync(itrType.next()); 107 } 108 mApnTypes.clear(); 109 } 110 break; 111 112 case EVENT_PHONE1_RADIO_OFF: 113 case EVENT_PHONE2_RADIO_OFF: 114 case EVENT_PHONE3_RADIO_OFF: 115 case EVENT_PHONE4_RADIO_OFF: 116 logd("EVENT_PHONE" + (msg.what - EVENT_PHONE1_RADIO_OFF + 1) + "_RADIO_OFF."); 117 mServicePowerOffFlag[msg.what - EVENT_PHONE1_RADIO_OFF] = true; 118 break; 119 120 default: 121 break; 122 } 123 } 124 }; 125 126 private DefaultPhoneNotifier.IDataStateChangedCallback mDataStateChangedCallback = 127 new DefaultPhoneNotifier.IDataStateChangedCallback() { 128 public void onDataStateChanged(long subId, String state, String reason, 129 String apnName, String apnType, boolean unavailable) { 130 logd("[DataStateChanged]:" + "state=" + state + ",reason=" + reason 131 + ",apnName=" + apnName + ",apnType=" + apnType + ",from subId=" + subId); 132 int phoneId = SubscriptionManager.getPhoneId(subId); 133 mDcSwitchState[phoneId].notifyDataConnection(phoneId, state, reason, 134 apnName, apnType, unavailable); 135 } 136 }; 137 138 private class DataStateReceiver extends BroadcastReceiver { 139 public void onReceive(Context context, Intent intent) { 140 synchronized(this) { 141 if (intent.getAction().equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) { 142 ServiceState ss = ServiceState.newFromBundle(intent.getExtras()); 143 144 long subId = intent.getLongExtra(PhoneConstants.SUBSCRIPTION_KEY, PhoneConstants.SUB1); 145 int phoneId = SubscriptionManager.getPhoneId(subId); 146 logd("DataStateReceiver: phoneId= " + phoneId); 147 148 // for the case of network out of service when bootup (ignore dummy values too) 149 if (!SubscriptionManager.isValidSubId(subId) || (subId < 0)) { 150 // FIXME: Maybe add SM.isRealSubId(subId)?? 151 logd("DataStateReceiver: ignore invalid subId=" + subId); 152 return; 153 } 154 if (!SubscriptionManager.isValidPhoneId(phoneId)) { 155 logd("DataStateReceiver: ignore invalid phoneId=" + phoneId); 156 return; 157 } 158 159 boolean prevPowerOff = mServicePowerOffFlag[phoneId]; 160 if (ss != null) { 161 int state = ss.getState(); 162 switch (state) { 163 case ServiceState.STATE_POWER_OFF: 164 mServicePowerOffFlag[phoneId] = true; 165 logd("DataStateReceiver: STATE_POWER_OFF Intent from phoneId=" 166 + phoneId); 167 break; 168 case ServiceState.STATE_IN_SERVICE: 169 mServicePowerOffFlag[phoneId] = false; 170 logd("DataStateReceiver: STATE_IN_SERVICE Intent from phoneId=" 171 + phoneId); 172 break; 173 case ServiceState.STATE_OUT_OF_SERVICE: 174 logd("DataStateReceiver: STATE_OUT_OF_SERVICE Intent from phoneId=" 175 + phoneId); 176 if (mServicePowerOffFlag[phoneId]) { 177 mServicePowerOffFlag[phoneId] = false; 178 } 179 break; 180 case ServiceState.STATE_EMERGENCY_ONLY: 181 logd("DataStateReceiver: STATE_EMERGENCY_ONLY Intent from phoneId=" 182 + phoneId); 183 break; 184 default: 185 logd("DataStateReceiver: SERVICE_STATE_CHANGED invalid state"); 186 break; 187 } 188 189 if (prevPowerOff && mServicePowerOffFlag[phoneId] == false && 190 mCurrentDataPhone == PHONE_NONE && 191 phoneId == getDataConnectionFromSetting()) { 192 logd("DataStateReceiver: Current Phone is none and default phoneId=" 193 + phoneId + ", then enableApnType()"); 194 enableApnType(subId, PhoneConstants.APN_TYPE_DEFAULT); 195 } 196 } 197 } 198 } 199 } 200 } 201 202 public DefaultPhoneNotifier.IDataStateChangedCallback getDataStateChangedCallback() { 203 return mDataStateChangedCallback; 204 } 205 206 public static DctController getInstance() { 207 if (sDctController == null) { 208 throw new RuntimeException( 209 "DCTrackerController.getInstance can't be called before makeDCTController()"); 210 } 211 return sDctController; 212 } 213 214 public static DctController makeDctController(PhoneProxy[] phones) { 215 if (sDctController == null) { 216 sDctController = new DctController(phones); 217 } 218 return sDctController; 219 } 220 221 private DctController(PhoneProxy[] phones) { 222 if (phones == null || phones.length == 0) { 223 if (phones == null) { 224 loge("DctController(phones): UNEXPECTED phones=null, ignore"); 225 } else { 226 loge("DctController(phones): UNEXPECTED phones.length=0, ignore"); 227 } 228 return; 229 } 230 mPhoneNum = phones.length; 231 mServicePowerOffFlag = new boolean[mPhoneNum]; 232 mPhones = phones; 233 234 mDcSwitchState = new DcSwitchState[mPhoneNum]; 235 mDcSwitchAsyncChannel = new DcSwitchAsyncChannel[mPhoneNum]; 236 mDcSwitchStateHandler = new Handler[mPhoneNum]; 237 238 mActivePhone = mPhones[0]; 239 240 for (int i = 0; i < mPhoneNum; ++i) { 241 int phoneId = i; 242 mServicePowerOffFlag[i] = true; 243 mDcSwitchState[i] = new DcSwitchState(mPhones[i], "DcSwitchState-" + phoneId, phoneId); 244 mDcSwitchState[i].start(); 245 mDcSwitchAsyncChannel[i] = new DcSwitchAsyncChannel(mDcSwitchState[i], phoneId); 246 mDcSwitchStateHandler[i] = new Handler(); 247 248 int status = mDcSwitchAsyncChannel[i].fullyConnectSync(mPhones[i].getContext(), 249 mDcSwitchStateHandler[i], mDcSwitchState[i].getHandler()); 250 251 if (status == AsyncChannel.STATUS_SUCCESSFUL) { 252 logd("DctController(phones): Connect success: " + i); 253 } else { 254 loge("DctController(phones): Could not connect to " + i); 255 } 256 257 mDcSwitchState[i].registerForIdle(mRspHander, EVENT_PHONE1_DETACH + i, null); 258 259 // Register for radio state change 260 PhoneBase phoneBase = (PhoneBase)((PhoneProxy)mPhones[i]).getActivePhone(); 261 phoneBase.mCi.registerForOffOrNotAvailable(mRspHander, EVENT_PHONE1_RADIO_OFF + i, null); 262 } 263 264 mContext = mActivePhone.getContext(); 265 266 IntentFilter filter = new IntentFilter(); 267 filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); 268 filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED); 269 270 mDataStateReceiver = new DataStateReceiver(); 271 Intent intent = mContext.registerReceiver(mDataStateReceiver, filter); 272 } 273 274 private IccCardConstants.State getIccCardState(int phoneId) { 275 return mPhones[phoneId].getIccCard().getState(); 276 } 277 278 /** 279 * Enable PDP interface by apn type and phone id 280 * 281 * @param type enable pdp interface by apn type, such as PhoneConstants.APN_TYPE_MMS, etc. 282 * @param subId Indicate which sub to query 283 * @return PhoneConstants.APN_REQUEST_STARTED: action is already started 284 * PhoneConstants.APN_ALREADY_ACTIVE: interface has already active 285 * PhoneConstants.APN_TYPE_NOT_AVAILABLE: invalid APN type 286 * PhoneConstants.APN_REQUEST_FAILED: request failed 287 * PhoneConstants.APN_REQUEST_FAILED_DUE_TO_RADIO_OFF: readio turn off 288 * @see #disableApnType() 289 */ 290 public synchronized int enableApnType(long subId, String type) { 291 int phoneId = SubscriptionManager.getPhoneId(subId); 292 293 if (phoneId == PHONE_NONE || !isValidphoneId(phoneId)) { 294 logw("enableApnType(): with PHONE_NONE or Invalid PHONE ID"); 295 return PhoneConstants.APN_REQUEST_FAILED; 296 } 297 298 logd("enableApnType():type=" + type + ",phoneId=" + phoneId + 299 ",powerOff=" + mServicePowerOffFlag[phoneId]); 300 301 if (!PhoneConstants.APN_TYPE_DEFAULT.equals(type)) { 302 for (int peerphoneId =0; peerphoneId < mPhoneNum; peerphoneId++) { 303 // check peer Phone has non default APN activated as receiving non default APN request. 304 if (phoneId == peerphoneId) { 305 continue; 306 } 307 308 String[] activeApnTypes = mPhones[peerphoneId].getActiveApnTypes(); 309 if (activeApnTypes != null && activeApnTypes.length != 0) { 310 for (int i=0; i<activeApnTypes.length; i++) { 311 if (!PhoneConstants.APN_TYPE_DEFAULT.equals(activeApnTypes[i]) && 312 mPhones[peerphoneId].getDataConnectionState(activeApnTypes[i]) != 313 PhoneConstants.DataState.DISCONNECTED) { 314 logd("enableApnType:Peer Phone still have non-default active APN type:"+ 315 "activeApnTypes[" + i + "]=" + activeApnTypes[i]); 316 return PhoneConstants.APN_REQUEST_FAILED; 317 } 318 } 319 } 320 } 321 } 322 323 logd("enableApnType(): CurrentDataPhone=" + 324 mCurrentDataPhone + ", RequestedDataPhone=" + mRequestedDataPhone); 325 326 if (phoneId == mCurrentDataPhone && 327 !mDcSwitchAsyncChannel[mCurrentDataPhone].isIdleOrDeactingSync()) { 328 mRequestedDataPhone = PHONE_NONE; 329 logd("enableApnType(): mRequestedDataPhone equals request PHONE ID."); 330 return mDcSwitchAsyncChannel[phoneId].connectSync(type); 331 } else { 332 // Only can switch data when mCurrentDataPhone is PHONE_NONE, 333 // it is set to PHONE_NONE only as receiving EVENT_PHONEX_DETACH 334 if (mCurrentDataPhone == PHONE_NONE) { 335 mCurrentDataPhone = phoneId; 336 mRequestedDataPhone = PHONE_NONE; 337 logd("enableApnType(): current PHONE is NONE or IDLE, mCurrentDataPhone=" + 338 mCurrentDataPhone); 339 return mDcSwitchAsyncChannel[phoneId].connectSync(type); 340 } else { 341 logd("enableApnType(): current PHONE:" + mCurrentDataPhone + " is active."); 342 if (phoneId != mRequestedDataPhone) { 343 mApnTypes.clear(); 344 } 345 mApnTypes.add(type); 346 mRequestedDataPhone = phoneId; 347 mDcSwitchState[mCurrentDataPhone].cleanupAllConnection(); 348 } 349 } 350 return PhoneConstants.APN_REQUEST_STARTED; 351 } 352 353 /** 354 * disable PDP interface by apn type and sub id 355 * 356 * @param type enable pdp interface by apn type, such as PhoneConstants.APN_TYPE_MMS, etc. 357 * @param subId Indicate which sub to query 358 * @return PhoneConstants.APN_REQUEST_STARTED: action is already started 359 * PhoneConstants.APN_ALREADY_ACTIVE: interface has already active 360 * PhoneConstants.APN_TYPE_NOT_AVAILABLE: invalid APN type 361 * PhoneConstants.APN_REQUEST_FAILED: request failed 362 * PhoneConstants.APN_REQUEST_FAILED_DUE_TO_RADIO_OFF: readio turn off 363 * @see #enableApnTypeGemini() 364 */ 365 public synchronized int disableApnType(long subId, String type) { 366 367 int phoneId = SubscriptionManager.getPhoneId(subId); 368 369 if (phoneId == PHONE_NONE || !isValidphoneId(phoneId)) { 370 logw("disableApnType(): with PHONE_NONE or Invalid PHONE ID"); 371 return PhoneConstants.APN_REQUEST_FAILED; 372 } 373 logd("disableApnType():type=" + type + ",phoneId=" + phoneId + 374 ",powerOff=" + mServicePowerOffFlag[phoneId]); 375 return mDcSwitchAsyncChannel[phoneId].disconnectSync(type); 376 } 377 378 public boolean isDataConnectivityPossible(String type, int phoneId) { 379 if (phoneId == PHONE_NONE || !isValidphoneId(phoneId)) { 380 logw("isDataConnectivityPossible(): with PHONE_NONE or Invalid PHONE ID"); 381 return false; 382 } else { 383 return mPhones[phoneId].isDataConnectivityPossible(type); 384 } 385 } 386 387 public boolean isIdleOrDeacting(int phoneId) { 388 if (mDcSwitchAsyncChannel[phoneId].isIdleOrDeactingSync()) { 389 return true; 390 } else { 391 return false; 392 } 393 } 394 395 private boolean isValidphoneId(int phoneId) { 396 return phoneId >= 0 && phoneId <= mPhoneNum; 397 } 398 399 private boolean isValidApnType(String apnType) { 400 if (apnType.equals(PhoneConstants.APN_TYPE_DEFAULT) 401 || apnType.equals(PhoneConstants.APN_TYPE_MMS) 402 || apnType.equals(PhoneConstants.APN_TYPE_SUPL) 403 || apnType.equals(PhoneConstants.APN_TYPE_DUN) 404 || apnType.equals(PhoneConstants.APN_TYPE_HIPRI) 405 || apnType.equals(PhoneConstants.APN_TYPE_FOTA) 406 || apnType.equals(PhoneConstants.APN_TYPE_IMS) 407 || apnType.equals(PhoneConstants.APN_TYPE_CBS)) 408 { 409 return true; 410 } else { 411 return false; 412 } 413 } 414 415 private int getDataConnectionFromSetting(){ 416 long [] subId = SubscriptionManager.getSubId(PhoneConstants.SIM_ID_1); 417 int phoneId = SubscriptionManager.getPhoneId(subId[0]); 418 return phoneId; 419 } 420 421 private static void logv(String s) { 422 Log.v(LOG_TAG, "[DctController] " + s); 423 } 424 425 private static void logd(String s) { 426 Log.d(LOG_TAG, "[DctController] " + s); 427 } 428 429 private static void logw(String s) { 430 Log.w(LOG_TAG, "[DctController] " + s); 431 } 432 433 private static void loge(String s) { 434 Log.e(LOG_TAG, "[DctController] " + s); 435 } 436 437 438 public void setDataSubId(long subId) { 439 //FIXME This should rework 440 //FIXME Need to have a StateMachine logic to handle this api considering various clients 441 Rlog.d(LOG_TAG, "setDataAllowed subId :" + subId); 442 int phoneId = mSubController.getPhoneId(subId); 443 int prefPhoneId = mSubController.getPhoneId(mSubController.getDefaultDataSubId()); 444 Phone phone = mPhones[prefPhoneId].getActivePhone(); 445 DcTrackerBase dcTracker =((PhoneBase)phone).mDcTracker; 446 dcTracker.setDataAllowed(false, null); 447 mPhones[prefPhoneId].registerForAllDataDisconnected( 448 this, EVENT_ALL_DATA_DISCONNECTED, new Integer(phoneId)); 449 450 } 451 452 public void registerForDataSwitchInfo(Handler h, int what, Object obj) { 453 //FIXME This should rework 454 Registrant r = new Registrant (h, what, obj); 455 synchronized (mNotifyDataSwitchInfo) { 456 mNotifyDataSwitchInfo.add(r); 457 } 458 } 459 460 @Override 461 public void handleMessage (Message msg) { 462 //FIXME This should rework 463 AsyncResult ar = (AsyncResult)msg.obj; 464 Rlog.d(LOG_TAG, "handleMessage msg=" + msg); 465 466 switch (msg.what) { 467 case EVENT_ALL_DATA_DISCONNECTED: 468 Integer phoneId = (Integer)ar.userObj; 469 int prefPhoneId = mSubController.getPhoneId( 470 mSubController.getDefaultDataSubId()); 471 Rlog.d(LOG_TAG, "EVENT_ALL_DATA_DISCONNECTED phoneId :" + phoneId); 472 mPhones[prefPhoneId].unregisterForAllDataDisconnected(this); 473 Message alllowedDataDone = Message.obtain(this, EVENT_SET_DATA_ALLOW_DONE, 474 new Integer(phoneId)); 475 Phone phone = mPhones[phoneId].getActivePhone(); 476 DcTrackerBase dcTracker =((PhoneBase)phone).mDcTracker; 477 dcTracker.setDataAllowed(true, alllowedDataDone); 478 break; 479 480 case EVENT_SET_DATA_ALLOW_DONE: 481 phoneId = (Integer)ar.userObj; 482 long[] subId = mSubController.getSubId(phoneId); 483 Rlog.d(LOG_TAG, "EVENT_SET_DATA_ALLOWED_DONE phoneId :" + subId[0]); 484 mNotifyDataSwitchInfo.notifyRegistrants(new AsyncResult(null, subId[0], null)); 485 mPhones[phoneId].updateDataConnectionTracker(); 486 break; 487 } 488 } 489 } 490