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