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