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