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