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