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