1 /* 2 * Copyright (C) 2013 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 com.android.internal.telephony; 18 19 import static java.util.Arrays.copyOf; 20 21 import android.annotation.NonNull; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.os.AsyncResult; 26 import android.os.Build; 27 import android.os.Handler; 28 import android.os.Message; 29 import android.os.PowerManager; 30 import android.os.PowerManager.WakeLock; 31 import android.telephony.RadioAccessFamily; 32 import android.telephony.TelephonyManager; 33 import android.util.Log; 34 35 import com.android.internal.annotations.VisibleForTesting; 36 import com.android.internal.telephony.data.PhoneSwitcher; 37 import com.android.internal.telephony.flags.FeatureFlags; 38 import com.android.telephony.Rlog; 39 40 import java.util.ArrayList; 41 import java.util.HashSet; 42 import java.util.Random; 43 import java.util.concurrent.atomic.AtomicInteger; 44 45 public class ProxyController { 46 static final String LOG_TAG = "ProxyController"; 47 48 private static final int EVENT_NOTIFICATION_RC_CHANGED = 1; 49 @VisibleForTesting 50 static final int EVENT_START_RC_RESPONSE = 2; 51 private static final int EVENT_APPLY_RC_RESPONSE = 3; 52 @VisibleForTesting 53 public static final int EVENT_FINISH_RC_RESPONSE = 4; 54 @VisibleForTesting 55 public static final int EVENT_TIMEOUT = 5; 56 @VisibleForTesting 57 public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 6; 58 59 private static final int SET_RC_STATUS_IDLE = 0; 60 private static final int SET_RC_STATUS_STARTING = 1; 61 private static final int SET_RC_STATUS_STARTED = 2; 62 private static final int SET_RC_STATUS_APPLYING = 3; 63 private static final int SET_RC_STATUS_SUCCESS = 4; 64 private static final int SET_RC_STATUS_FAIL = 5; 65 66 // The entire transaction must complete within this amount of time 67 // or a FINISH will be issued to each Logical Modem with the old 68 // Radio Access Family. 69 private static final int SET_RC_TIMEOUT_WAITING_MSEC = (45 * 1000); 70 71 //***** Class Variables 72 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 73 private static ProxyController sProxyController; 74 75 private Phone[] mPhones; 76 77 private Context mContext; 78 79 private PhoneSwitcher mPhoneSwitcher; 80 81 //UiccPhoneBookController to use proper IccPhoneBookInterfaceManagerProxy object 82 private UiccPhoneBookController mUiccPhoneBookController; 83 84 //PhoneSubInfoController to use proper PhoneSubInfoProxy object 85 private PhoneSubInfoController mPhoneSubInfoController; 86 87 //SmsController to use proper IccSmsInterfaceManager object 88 private SmsController mSmsController; 89 90 WakeLock mWakeLock; 91 92 // record each phone's set radio capability status 93 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 94 private int[] mSetRadioAccessFamilyStatus; 95 private int mRadioAccessFamilyStatusCounter; 96 private boolean mTransactionFailed = false; 97 98 private String[] mCurrentLogicalModemIds; 99 private String[] mNewLogicalModemIds; 100 101 // Allows the generation of unique Id's for radio capability request session id 102 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 103 private AtomicInteger mUniqueIdGenerator = new AtomicInteger(new Random().nextInt()); 104 105 // on-going radio capability request session id 106 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 107 private int mRadioCapabilitySessionId; 108 109 // Record new and old Radio Access Family (raf) configuration. 110 // The old raf configuration is used to restore each logical modem raf when FINISH is 111 // issued if any requests fail. 112 private int[] mNewRadioAccessFamily; 113 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 114 private int[] mOldRadioAccessFamily; 115 116 @NonNull 117 private final FeatureFlags mFlags; 118 getInstance(Context context, FeatureFlags flags)119 public static ProxyController getInstance(Context context, FeatureFlags flags) { 120 if (sProxyController == null) { 121 sProxyController = new ProxyController(context, flags); 122 } 123 return sProxyController; 124 } 125 126 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getInstance()127 public static ProxyController getInstance() { 128 return sProxyController; 129 } 130 131 /** 132 * Constructor 133 * 134 * @param context The context 135 * @param featureFlags Feature flags 136 */ ProxyController(@onNull Context context, @NonNull FeatureFlags featureFlags)137 public ProxyController(@NonNull Context context, @NonNull FeatureFlags featureFlags) { 138 logd("Constructor - Enter"); 139 140 mContext = context; 141 mFlags = featureFlags; 142 mPhones = PhoneFactory.getPhones(); 143 mPhoneSwitcher = PhoneSwitcher.getInstance(); 144 145 mUiccPhoneBookController = new UiccPhoneBookController(); 146 mPhoneSubInfoController = new PhoneSubInfoController(mContext); 147 mSmsController = new SmsController(mContext, featureFlags); 148 mSetRadioAccessFamilyStatus = new int[mPhones.length]; 149 mNewRadioAccessFamily = new int[mPhones.length]; 150 mOldRadioAccessFamily = new int[mPhones.length]; 151 mCurrentLogicalModemIds = new String[mPhones.length]; 152 mNewLogicalModemIds = new String[mPhones.length]; 153 154 // wake lock for set radio capability 155 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 156 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); 157 mWakeLock.setReferenceCounted(false); 158 159 // Clear to be sure we're in the initial state 160 clearTransaction(); 161 for (int i = 0; i < mPhones.length; i++) { 162 mPhones[i].registerForRadioCapabilityChanged( 163 mHandler, EVENT_NOTIFICATION_RC_CHANGED, null); 164 } 165 166 PhoneConfigurationManager.registerForMultiSimConfigChange( 167 mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null); 168 logd("Constructor - Exit"); 169 } 170 171 /** 172 * Get phone radio type and access technology. 173 * 174 * @param phoneId which phone you want to get 175 * @return phone radio type and access technology for input phone ID 176 */ getRadioAccessFamily(int phoneId)177 public int getRadioAccessFamily(int phoneId) { 178 if (phoneId >= mPhones.length) { 179 return RadioAccessFamily.RAF_UNKNOWN; 180 } else { 181 return mPhones[phoneId].getRadioAccessFamily(); 182 } 183 } 184 185 /** 186 * Set phone radio type and access technology for each phone. 187 * 188 * @param rafs an RadioAccessFamily array to indicate all phone's 189 * new radio access family. The length of RadioAccessFamily 190 * must equal to phone count. 191 * @return false if another session is already active and the request is rejected. 192 */ setRadioCapability(RadioAccessFamily[] rafs)193 public boolean setRadioCapability(RadioAccessFamily[] rafs) { 194 if (rafs.length != mPhones.length) { 195 return false; 196 } 197 // Check if there is any ongoing transaction and throw an exception if there 198 // is one as this is a programming error. 199 synchronized (mSetRadioAccessFamilyStatus) { 200 for (int i = 0; i < mPhones.length; i++) { 201 if (mSetRadioAccessFamilyStatus[i] != SET_RC_STATUS_IDLE) { 202 // TODO: The right behaviour is to cancel previous request and send this. 203 loge("setRadioCapability: Phone[" + i + "] is not idle. Rejecting request."); 204 return false; 205 } 206 } 207 } 208 209 // Check we actually need to do anything 210 boolean same = true; 211 for (int i = 0; i < mPhones.length; i++) { 212 if (mPhones[i].getRadioAccessFamily() != rafs[i].getRadioAccessFamily()) { 213 same = false; 214 } 215 } 216 if (same) { 217 // All phones are already set to the requested raf 218 logd("setRadioCapability: Already in requested configuration, nothing to do."); 219 // It isn't really an error, so return true - everything is OK. 220 return true; 221 } 222 223 // Clear to be sure we're in the initial state 224 clearTransaction(); 225 226 // Keep a wake lock until we finish radio capability changed 227 logd("Acquiring wake lock for setting radio capability"); 228 mWakeLock.acquire(); 229 230 return doSetRadioCapabilities(rafs); 231 } 232 233 /** 234 * Get the SmsController. 235 * @return the SmsController object. 236 */ getSmsController()237 public SmsController getSmsController() { 238 return mSmsController; 239 } 240 doSetRadioCapabilities(RadioAccessFamily[] rafs)241 private boolean doSetRadioCapabilities(RadioAccessFamily[] rafs) { 242 // A new sessionId for this transaction 243 mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement(); 244 245 // Start timer to make sure all phones respond within a specific time interval. 246 // Will send FINISH if a timeout occurs. 247 Message msg = mHandler.obtainMessage(EVENT_TIMEOUT, mRadioCapabilitySessionId, 0); 248 mHandler.sendMessageDelayed(msg, SET_RC_TIMEOUT_WAITING_MSEC); 249 250 synchronized (mSetRadioAccessFamilyStatus) { 251 logd("setRadioCapability: new request session id=" + mRadioCapabilitySessionId); 252 resetRadioAccessFamilyStatusCounter(); 253 for (int i = 0; i < rafs.length; i++) { 254 int phoneId = rafs[i].getPhoneId(); 255 logd("setRadioCapability: phoneId=" + phoneId + " status=STARTING"); 256 mSetRadioAccessFamilyStatus[phoneId] = SET_RC_STATUS_STARTING; 257 mOldRadioAccessFamily[phoneId] = mPhones[phoneId].getRadioAccessFamily(); 258 int requestedRaf = rafs[i].getRadioAccessFamily(); 259 // TODO Set the new radio access family to the maximum of the requested & supported 260 // int supportedRaf = mPhones[i].getRadioAccessFamily(); 261 // mNewRadioAccessFamily[phoneId] = requestedRaf & supportedRaf; 262 mNewRadioAccessFamily[phoneId] = requestedRaf; 263 264 mCurrentLogicalModemIds[phoneId] = mPhones[phoneId].getModemUuId(); 265 // get the logical mode corresponds to new raf requested and pass the 266 // same as part of SET_RADIO_CAP APPLY phase 267 mNewLogicalModemIds[phoneId] = getLogicalModemIdFromRaf(requestedRaf); 268 logd("setRadioCapability: mOldRadioAccessFamily[" + phoneId + "]=" 269 + mOldRadioAccessFamily[phoneId]); 270 logd("setRadioCapability: mNewRadioAccessFamily[" + phoneId + "]=" 271 + mNewRadioAccessFamily[phoneId]); 272 sendRadioCapabilityRequest( 273 phoneId, 274 mRadioCapabilitySessionId, 275 RadioCapability.RC_PHASE_START, 276 mOldRadioAccessFamily[phoneId], 277 mCurrentLogicalModemIds[phoneId], 278 RadioCapability.RC_STATUS_NONE, 279 EVENT_START_RC_RESPONSE); 280 } 281 } 282 283 return true; 284 } 285 286 @VisibleForTesting 287 public final Handler mHandler = new Handler() { 288 @Override 289 public void handleMessage(Message msg) { 290 logd("handleMessage msg.what=" + msg.what); 291 switch (msg.what) { 292 case EVENT_START_RC_RESPONSE: 293 onStartRadioCapabilityResponse(msg); 294 break; 295 296 case EVENT_APPLY_RC_RESPONSE: 297 onApplyRadioCapabilityResponse(msg); 298 break; 299 300 case EVENT_NOTIFICATION_RC_CHANGED: 301 onNotificationRadioCapabilityChanged(msg); 302 break; 303 304 case EVENT_FINISH_RC_RESPONSE: 305 onFinishRadioCapabilityResponse(msg); 306 break; 307 308 case EVENT_TIMEOUT: 309 onTimeoutRadioCapability(msg); 310 break; 311 312 case EVENT_MULTI_SIM_CONFIG_CHANGED: 313 onMultiSimConfigChanged(); 314 break; 315 316 default: 317 break; 318 } 319 } 320 }; 321 onMultiSimConfigChanged()322 private void onMultiSimConfigChanged() { 323 int oldPhoneCount = mPhones.length; 324 mPhones = PhoneFactory.getPhones(); 325 326 // Re-size arrays. 327 mSetRadioAccessFamilyStatus = copyOf(mSetRadioAccessFamilyStatus, mPhones.length); 328 mNewRadioAccessFamily = copyOf(mNewRadioAccessFamily, mPhones.length); 329 mOldRadioAccessFamily = copyOf(mOldRadioAccessFamily, mPhones.length); 330 mCurrentLogicalModemIds = copyOf(mCurrentLogicalModemIds, mPhones.length); 331 mNewLogicalModemIds = copyOf(mNewLogicalModemIds, mPhones.length); 332 333 // Clear to be sure we're in the initial state 334 clearTransaction(); 335 336 // Register radio cap change for new phones. 337 for (int i = oldPhoneCount; i < mPhones.length; i++) { 338 mPhones[i].registerForRadioCapabilityChanged( 339 mHandler, EVENT_NOTIFICATION_RC_CHANGED, null); 340 } 341 } 342 343 /** 344 * Handle START response 345 * @param msg obj field isa RadioCapability 346 */ onStartRadioCapabilityResponse(Message msg)347 private void onStartRadioCapabilityResponse(Message msg) { 348 synchronized (mSetRadioAccessFamilyStatus) { 349 AsyncResult ar = (AsyncResult)msg.obj; 350 // Abort here only in Single SIM case, in Multi SIM cases 351 // send FINISH with failure so that below layers can re-bind 352 // old logical modems. 353 if (ar.exception != null) { 354 boolean isPermanaentFailure = false; 355 if (ar.exception instanceof CommandException) { 356 CommandException.Error error = 357 ((CommandException) (ar.exception)).getCommandError(); 358 if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) { 359 isPermanaentFailure = true; 360 } 361 } 362 if (TelephonyManager.getDefault().getPhoneCount() == 1 || isPermanaentFailure) { 363 // just abort now. They didn't take our start so we don't have to revert 364 logd("onStartRadioCapabilityResponse got exception=" + ar.exception); 365 mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement(); 366 Intent intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_FAILED); 367 mContext.sendBroadcast(intent); 368 clearTransaction(); 369 return; 370 } 371 } 372 RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result; 373 // Added exception condition to continue to mark as transaction fail case. 374 // Checking session validity during exception is not valid 375 if (ar.exception == null 376 && ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId))) { 377 logd("onStartRadioCapabilityResponse: Ignore session=" + mRadioCapabilitySessionId 378 + " rc=" + rc); 379 return; 380 } 381 mRadioAccessFamilyStatusCounter--; 382 //rc.getPhoneId() moved to avoid Null Pointer Exception, since when exception occurs 383 //its expected rc is null. 384 if (ar.exception != null) { 385 logd("onStartRadioCapabilityResponse got exception=" + ar.exception); 386 //mSetRadioAccessFamilyStatus will be set anyway to SET_RC_STATUS_FAIL 387 // if either of them fail at issueFinish() method below,i.e. both phone id count 388 // is set to SET_RC_STATUS_FAIL. 389 mTransactionFailed = true; 390 } else { 391 int id = rc.getPhoneId(); 392 logd("onStartRadioCapabilityResponse: phoneId=" + id + " status=STARTED"); 393 mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_STARTED; 394 } 395 396 if (mRadioAccessFamilyStatusCounter == 0) { 397 HashSet<String> modemsInUse = new HashSet<String>(mNewLogicalModemIds.length); 398 for (String modemId : mNewLogicalModemIds) { 399 if (!modemsInUse.add(modemId)) { 400 mTransactionFailed = true; 401 Log.wtf(LOG_TAG, "ERROR: sending down the same id for different phones"); 402 } 403 } 404 logd("onStartRadioCapabilityResponse: success=" + !mTransactionFailed); 405 if (mTransactionFailed) { 406 // Sends a variable number of requests, so don't resetRadioAccessFamilyCounter 407 // here. 408 issueFinish(mRadioCapabilitySessionId); 409 } else { 410 // All logical modem accepted the new radio access family, issue the APPLY 411 resetRadioAccessFamilyStatusCounter(); 412 for (int i = 0; i < mPhones.length; i++) { 413 sendRadioCapabilityRequest( 414 i, 415 mRadioCapabilitySessionId, 416 RadioCapability.RC_PHASE_APPLY, 417 mNewRadioAccessFamily[i], 418 mNewLogicalModemIds[i], 419 RadioCapability.RC_STATUS_NONE, 420 EVENT_APPLY_RC_RESPONSE); 421 422 logd("onStartRadioCapabilityResponse: phoneId=" + i + " status=APPLYING"); 423 mSetRadioAccessFamilyStatus[i] = SET_RC_STATUS_APPLYING; 424 } 425 } 426 } 427 } 428 } 429 430 /** 431 * Handle APPLY response 432 * @param msg obj field isa RadioCapability 433 */ onApplyRadioCapabilityResponse(Message msg)434 private void onApplyRadioCapabilityResponse(Message msg) { 435 RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result; 436 if ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId)) { 437 logd("onApplyRadioCapabilityResponse: Ignore session=" + mRadioCapabilitySessionId 438 + " rc=" + rc); 439 return; 440 } 441 logd("onApplyRadioCapabilityResponse: rc=" + rc); 442 if (((AsyncResult) msg.obj).exception != null) { 443 synchronized (mSetRadioAccessFamilyStatus) { 444 logd("onApplyRadioCapabilityResponse: Error response session=" + rc.getSession()); 445 int id = rc.getPhoneId(); 446 logd("onApplyRadioCapabilityResponse: phoneId=" + id + " status=FAIL"); 447 mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_FAIL; 448 mTransactionFailed = true; 449 } 450 } else { 451 logd("onApplyRadioCapabilityResponse: Valid start expecting notification rc=" + rc); 452 } 453 } 454 455 /** 456 * Handle the notification unsolicited response associated with the APPLY 457 * @param msg obj field isa RadioCapability 458 */ onNotificationRadioCapabilityChanged(Message msg)459 private void onNotificationRadioCapabilityChanged(Message msg) { 460 RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result; 461 if ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId)) { 462 logd("onNotificationRadioCapabilityChanged: Ignore session=" + mRadioCapabilitySessionId 463 + " rc=" + rc); 464 return; 465 } 466 synchronized (mSetRadioAccessFamilyStatus) { 467 logd("onNotificationRadioCapabilityChanged: rc=" + rc); 468 // skip the overdue response by checking sessionId 469 if (rc.getSession() != mRadioCapabilitySessionId) { 470 logd("onNotificationRadioCapabilityChanged: Ignore session=" 471 + mRadioCapabilitySessionId + " rc=" + rc); 472 return; 473 } 474 475 int id = rc.getPhoneId(); 476 if ((((AsyncResult) msg.obj).exception != null) || 477 (rc.getStatus() == RadioCapability.RC_STATUS_FAIL)) { 478 logd("onNotificationRadioCapabilityChanged: phoneId=" + id + " status=FAIL"); 479 mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_FAIL; 480 mTransactionFailed = true; 481 } else { 482 logd("onNotificationRadioCapabilityChanged: phoneId=" + id + " status=SUCCESS"); 483 mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_SUCCESS; 484 // The modems may have been restarted and forgotten this 485 mPhoneSwitcher.onRadioCapChanged(id); 486 mPhones[id].radioCapabilityUpdated(rc, true); 487 } 488 489 mRadioAccessFamilyStatusCounter--; 490 if (mRadioAccessFamilyStatusCounter == 0) { 491 logd("onNotificationRadioCapabilityChanged: APPLY URC success=" + 492 mTransactionFailed); 493 issueFinish(mRadioCapabilitySessionId); 494 } 495 } 496 } 497 498 /** 499 * Handle the FINISH Phase response 500 * @param msg obj field isa RadioCapability 501 */ onFinishRadioCapabilityResponse(Message msg)502 void onFinishRadioCapabilityResponse(Message msg) { 503 synchronized (mSetRadioAccessFamilyStatus) { 504 AsyncResult ar = (AsyncResult) msg.obj; 505 RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result; 506 // Added exception condition on finish to continue to revert if exception occurred. 507 // Checking session validity during exception is not valid 508 if (ar.exception == null 509 && ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId))) { 510 logd("onFinishRadioCapabilityResponse: Ignore session=" 511 + mRadioCapabilitySessionId + " rc=" + rc); 512 return; 513 } 514 if (ar.exception != null) { 515 logd("onFinishRadioCapabilityResponse got exception=" + ar.exception); 516 } 517 logd(" onFinishRadioCapabilityResponse mRadioAccessFamilyStatusCounter=" 518 + mRadioAccessFamilyStatusCounter); 519 mRadioAccessFamilyStatusCounter--; 520 if (mRadioAccessFamilyStatusCounter == 0) { 521 completeRadioCapabilityTransaction(); 522 } 523 } 524 } 525 onTimeoutRadioCapability(Message msg)526 private void onTimeoutRadioCapability(Message msg) { 527 if (msg.arg1 != mRadioCapabilitySessionId) { 528 logd("RadioCapability timeout: Ignore msg.arg1=" + msg.arg1 + 529 "!= mRadioCapabilitySessionId=" + mRadioCapabilitySessionId); 530 return; 531 } 532 533 synchronized(mSetRadioAccessFamilyStatus) { 534 // timed-out. Clean up as best we can 535 for (int i = 0; i < mPhones.length; i++) { 536 logd("RadioCapability timeout: mSetRadioAccessFamilyStatus[" + i + "]=" + 537 mSetRadioAccessFamilyStatus[i]); 538 } 539 540 // Increment the sessionId as we are completing the transaction below 541 // so we don't want it completed when the FINISH phase is done. 542 mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement(); 543 544 // Reset the status counter as existing session failed 545 mRadioAccessFamilyStatusCounter = 0; 546 547 // send FINISH request with fail status and then uniqueDifferentId 548 mTransactionFailed = true; 549 issueFinish(mRadioCapabilitySessionId); 550 } 551 } 552 issueFinish(int sessionId)553 private void issueFinish(int sessionId) { 554 // Issue FINISH 555 synchronized(mSetRadioAccessFamilyStatus) { 556 for (int i = 0; i < mPhones.length; i++) { 557 logd("issueFinish: phoneId=" + i + " sessionId=" + sessionId 558 + " mTransactionFailed=" + mTransactionFailed); 559 mRadioAccessFamilyStatusCounter++; 560 sendRadioCapabilityRequest( 561 i, 562 sessionId, 563 RadioCapability.RC_PHASE_FINISH, 564 (mTransactionFailed ? mOldRadioAccessFamily[i] : 565 mNewRadioAccessFamily[i]), 566 (mTransactionFailed ? mCurrentLogicalModemIds[i] : 567 mNewLogicalModemIds[i]), 568 (mTransactionFailed ? RadioCapability.RC_STATUS_FAIL : 569 RadioCapability.RC_STATUS_SUCCESS), 570 EVENT_FINISH_RC_RESPONSE); 571 if (mTransactionFailed) { 572 logd("issueFinish: phoneId: " + i + " status: FAIL"); 573 // At least one failed, mark them all failed. 574 mSetRadioAccessFamilyStatus[i] = SET_RC_STATUS_FAIL; 575 } 576 } 577 } 578 } 579 580 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) completeRadioCapabilityTransaction()581 private void completeRadioCapabilityTransaction() { 582 // Create the intent to broadcast 583 Intent intent; 584 logd("onFinishRadioCapabilityResponse: success=" + !mTransactionFailed); 585 if (!mTransactionFailed) { 586 ArrayList<RadioAccessFamily> phoneRAFList = new ArrayList<RadioAccessFamily>(); 587 for (int i = 0; i < mPhones.length; i++) { 588 int raf = mPhones[i].getRadioAccessFamily(); 589 logd("radioAccessFamily[" + i + "]=" + raf); 590 RadioAccessFamily phoneRC = new RadioAccessFamily(i, raf); 591 phoneRAFList.add(phoneRC); 592 } 593 intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_DONE); 594 intent.putParcelableArrayListExtra(TelephonyIntents.EXTRA_RADIO_ACCESS_FAMILY, 595 phoneRAFList); 596 597 // make messages about the old transaction obsolete (specifically the timeout) 598 mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement(); 599 600 // Reinitialize 601 clearTransaction(); 602 } else { 603 intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_FAILED); 604 // now revert. 605 mTransactionFailed = false; 606 RadioAccessFamily[] rafs = new RadioAccessFamily[mPhones.length]; 607 for (int phoneId = 0; phoneId < mPhones.length; phoneId++) { 608 rafs[phoneId] = new RadioAccessFamily(phoneId, mOldRadioAccessFamily[phoneId]); 609 } 610 doSetRadioCapabilities(rafs); 611 } 612 613 // Broadcast that we're done 614 mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE); 615 } 616 617 // Clear this transaction clearTransaction()618 private void clearTransaction() { 619 logd("clearTransaction"); 620 synchronized(mSetRadioAccessFamilyStatus) { 621 for (int i = 0; i < mPhones.length; i++) { 622 logd("clearTransaction: phoneId=" + i + " status=IDLE"); 623 mSetRadioAccessFamilyStatus[i] = SET_RC_STATUS_IDLE; 624 mOldRadioAccessFamily[i] = 0; 625 mNewRadioAccessFamily[i] = 0; 626 mTransactionFailed = false; 627 } 628 629 if (isWakeLockHeld()) { 630 logd("clearTransaction:checking wakelock held and releasing"); 631 mWakeLock.release(); 632 } 633 } 634 } 635 636 /** 637 * check if wakelock is held. 638 * 639 * @return true if wakelock is held else false. 640 */ 641 @VisibleForTesting isWakeLockHeld()642 public boolean isWakeLockHeld() { 643 synchronized (mSetRadioAccessFamilyStatus) { 644 return mWakeLock.isHeld(); 645 } 646 } 647 resetRadioAccessFamilyStatusCounter()648 private void resetRadioAccessFamilyStatusCounter() { 649 mRadioAccessFamilyStatusCounter = mPhones.length; 650 } 651 652 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) sendRadioCapabilityRequest(int phoneId, int sessionId, int rcPhase, int radioFamily, String logicalModemId, int status, int eventId)653 private void sendRadioCapabilityRequest(int phoneId, int sessionId, int rcPhase, 654 int radioFamily, String logicalModemId, int status, int eventId) { 655 RadioCapability requestRC = new RadioCapability( 656 phoneId, sessionId, rcPhase, radioFamily, logicalModemId, status); 657 mPhones[phoneId].setRadioCapability( 658 requestRC, mHandler.obtainMessage(eventId)); 659 } 660 661 // This method will return max number of raf bits supported from the raf 662 // values currently stored in all phone objects getMaxRafSupported()663 public int getMaxRafSupported() { 664 int[] numRafSupported = new int[mPhones.length]; 665 int maxNumRafBit = 0; 666 int maxRaf = RadioAccessFamily.RAF_UNKNOWN; 667 668 for (int len = 0; len < mPhones.length; len++) { 669 numRafSupported[len] = Integer.bitCount(mPhones[len].getRadioAccessFamily()); 670 if (maxNumRafBit < numRafSupported[len]) { 671 maxNumRafBit = numRafSupported[len]; 672 maxRaf = mPhones[len].getRadioAccessFamily(); 673 } 674 } 675 676 return maxRaf; 677 } 678 679 // This method will return minimum number of raf bits supported from the raf 680 // values currently stored in all phone objects getMinRafSupported()681 public int getMinRafSupported() { 682 int[] numRafSupported = new int[mPhones.length]; 683 int minNumRafBit = 0; 684 int minRaf = RadioAccessFamily.RAF_UNKNOWN; 685 686 for (int len = 0; len < mPhones.length; len++) { 687 numRafSupported[len] = Integer.bitCount(mPhones[len].getRadioAccessFamily()); 688 if ((minNumRafBit == 0) || (minNumRafBit > numRafSupported[len])) { 689 minNumRafBit = numRafSupported[len]; 690 minRaf = mPhones[len].getRadioAccessFamily(); 691 } 692 } 693 return minRaf; 694 } 695 696 // This method checks current raf values stored in all phones and 697 // whicheve phone raf matches with input raf, returns modemId from that phone getLogicalModemIdFromRaf(int raf)698 private String getLogicalModemIdFromRaf(int raf) { 699 String modemUuid = null; 700 701 for (int phoneId = 0; phoneId < mPhones.length; phoneId++) { 702 if (mPhones[phoneId].getRadioAccessFamily() == raf) { 703 modemUuid = mPhones[phoneId].getModemUuId(); 704 break; 705 } 706 } 707 return modemUuid; 708 } 709 710 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) logd(String string)711 private void logd(String string) { 712 Rlog.d(LOG_TAG, string); 713 } 714 loge(String string)715 private void loge(String string) { 716 Rlog.e(LOG_TAG, string); 717 } 718 } 719