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