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