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