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