1/*
2 * Copyright (C) 2014 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
17package com.android.services.telephony;
18
19import android.content.BroadcastReceiver;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.content.res.Resources;
25import android.graphics.Bitmap;
26import android.graphics.Canvas;
27import android.graphics.PorterDuff;
28import android.graphics.drawable.Drawable;
29import android.graphics.drawable.Icon;
30import android.net.Uri;
31import android.os.Bundle;
32import android.os.PersistableBundle;
33import android.os.UserHandle;
34import android.os.UserManager;
35import android.telecom.PhoneAccount;
36import android.telecom.PhoneAccountHandle;
37import android.telecom.TelecomManager;
38import android.telephony.CarrierConfigManager;
39import android.telephony.PhoneStateListener;
40import android.telephony.ServiceState;
41import android.telephony.SubscriptionInfo;
42import android.telephony.SubscriptionManager;
43import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
44import android.telephony.TelephonyManager;
45import android.text.TextUtils;
46
47import com.android.internal.telephony.Phone;
48import com.android.internal.telephony.PhoneFactory;
49import com.android.phone.PhoneGlobals;
50import com.android.phone.PhoneUtils;
51import com.android.phone.R;
52
53import java.util.Arrays;
54import java.util.LinkedList;
55import java.util.List;
56import java.util.Optional;
57
58/**
59 * Owns all data we have registered with Telecom including handling dynamic addition and
60 * removal of SIMs and SIP accounts.
61 */
62final class TelecomAccountRegistry {
63    private static final boolean DBG = false; /* STOP SHIP if true */
64
65    // This icon is the one that is used when the Slot ID that we have for a particular SIM
66    // is not supported, i.e. SubscriptionManager.INVALID_SLOT_ID or the 5th SIM in a phone.
67    private final static int DEFAULT_SIM_ICON =  R.drawable.ic_multi_sim;
68    private final static String GROUP_PREFIX = "group_";
69
70    final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
71        private final Phone mPhone;
72        private PhoneAccount mAccount;
73        private final PstnIncomingCallNotifier mIncomingCallNotifier;
74        private final PstnPhoneCapabilitiesNotifier mPhoneCapabilitiesNotifier;
75        private boolean mIsEmergency;
76        private boolean mIsDummy;
77        private boolean mIsVideoCapable;
78        private boolean mIsVideoPresenceSupported;
79        private boolean mIsVideoPauseSupported;
80        private boolean mIsMergeCallSupported;
81        private boolean mIsVideoConferencingSupported;
82        private boolean mIsMergeOfWifiCallsAllowedWhenVoWifiOff;
83
84        AccountEntry(Phone phone, boolean isEmergency, boolean isDummy) {
85            mPhone = phone;
86            mIsEmergency = isEmergency;
87            mIsDummy = isDummy;
88            mAccount = registerPstnPhoneAccount(isEmergency, isDummy);
89            Log.i(this, "Registered phoneAccount: %s with handle: %s",
90                    mAccount, mAccount.getAccountHandle());
91            mIncomingCallNotifier = new PstnIncomingCallNotifier((Phone) mPhone);
92            mPhoneCapabilitiesNotifier = new PstnPhoneCapabilitiesNotifier((Phone) mPhone,
93                    this);
94        }
95
96        void teardown() {
97            mIncomingCallNotifier.teardown();
98            mPhoneCapabilitiesNotifier.teardown();
99        }
100
101        /**
102         * Registers the specified account with Telecom as a PhoneAccountHandle.
103         */
104        private PhoneAccount registerPstnPhoneAccount(boolean isEmergency, boolean isDummyAccount) {
105            String dummyPrefix = isDummyAccount ? "Dummy " : "";
106
107            // Build the Phone account handle.
108            PhoneAccountHandle phoneAccountHandle =
109                    PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
110                            mPhone, dummyPrefix, isEmergency);
111
112            // Populate the phone account data.
113            int subId = mPhone.getSubId();
114            String subscriberId = mPhone.getSubscriberId();
115            int color = PhoneAccount.NO_HIGHLIGHT_COLOR;
116            int slotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
117            String line1Number = mTelephonyManager.getLine1Number(subId);
118            if (line1Number == null) {
119                line1Number = "";
120            }
121            String subNumber = mPhone.getLine1Number();
122            if (subNumber == null) {
123                subNumber = "";
124            }
125
126            String label;
127            String description;
128            Icon icon = null;
129
130            // We can only get the real slotId from the SubInfoRecord, we can't calculate the
131            // slotId from the subId or the phoneId in all instances.
132            SubscriptionInfo record =
133                    mSubscriptionManager.getActiveSubscriptionInfo(subId);
134
135            if (isEmergency) {
136                label = mContext.getResources().getString(R.string.sim_label_emergency_calls);
137                description =
138                        mContext.getResources().getString(R.string.sim_description_emergency_calls);
139            } else if (mTelephonyManager.getPhoneCount() == 1) {
140                // For single-SIM devices, we show the label and description as whatever the name of
141                // the network is.
142                description = label = mTelephonyManager.getNetworkOperatorName();
143            } else {
144                CharSequence subDisplayName = null;
145
146                if (record != null) {
147                    subDisplayName = record.getDisplayName();
148                    slotId = record.getSimSlotIndex();
149                    color = record.getIconTint();
150                    icon = Icon.createWithBitmap(record.createIconBitmap(mContext));
151                }
152
153                String slotIdString;
154                if (SubscriptionManager.isValidSlotId(slotId)) {
155                    slotIdString = Integer.toString(slotId);
156                } else {
157                    slotIdString = mContext.getResources().getString(R.string.unknown);
158                }
159
160                if (TextUtils.isEmpty(subDisplayName)) {
161                    // Either the sub record is not there or it has an empty display name.
162                    Log.w(this, "Could not get a display name for subid: %d", subId);
163                    subDisplayName = mContext.getResources().getString(
164                            R.string.sim_description_default, slotIdString);
165                }
166
167                // The label is user-visible so let's use the display name that the user may
168                // have set in Settings->Sim cards.
169                label = dummyPrefix + subDisplayName;
170                description = dummyPrefix + mContext.getResources().getString(
171                                R.string.sim_description_default, slotIdString);
172            }
173
174            // By default all SIM phone accounts can place emergency calls.
175            int capabilities = PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
176                    PhoneAccount.CAPABILITY_CALL_PROVIDER |
177                    PhoneAccount.CAPABILITY_MULTI_USER;
178
179            if (mContext.getResources().getBoolean(R.bool.config_pstnCanPlaceEmergencyCalls)) {
180                capabilities |= PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS;
181            }
182
183            mIsVideoCapable = mPhone.isVideoEnabled();
184
185            if (!mIsPrimaryUser) {
186                Log.i(this, "Disabling video calling for secondary user.");
187                mIsVideoCapable = false;
188            }
189
190            if (mIsVideoCapable) {
191                capabilities |= PhoneAccount.CAPABILITY_VIDEO_CALLING;
192            }
193
194            mIsVideoPresenceSupported = isCarrierVideoPresenceSupported();
195            if (mIsVideoCapable && mIsVideoPresenceSupported) {
196                capabilities |= PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE;
197            }
198
199            if (mIsVideoCapable && isCarrierEmergencyVideoCallsAllowed()) {
200                capabilities |= PhoneAccount.CAPABILITY_EMERGENCY_VIDEO_CALLING;
201            }
202
203            mIsVideoPauseSupported = isCarrierVideoPauseSupported();
204            Bundle instantLetteringExtras = null;
205            if (isCarrierInstantLetteringSupported()) {
206                capabilities |= PhoneAccount.CAPABILITY_CALL_SUBJECT;
207                instantLetteringExtras = getPhoneAccountExtras();
208            }
209            mIsMergeCallSupported = isCarrierMergeCallSupported();
210            mIsVideoConferencingSupported = isCarrierVideoConferencingSupported();
211            mIsMergeOfWifiCallsAllowedWhenVoWifiOff =
212                    isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff();
213
214            if (isEmergency && mContext.getResources().getBoolean(
215                    R.bool.config_emergency_account_emergency_calls_only)) {
216                capabilities |= PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY;
217            }
218
219            if (icon == null) {
220                // TODO: Switch to using Icon.createWithResource() once that supports tinting.
221                Resources res = mContext.getResources();
222                Drawable drawable = res.getDrawable(DEFAULT_SIM_ICON, null);
223                drawable.setTint(res.getColor(R.color.default_sim_icon_tint_color, null));
224                drawable.setTintMode(PorterDuff.Mode.SRC_ATOP);
225
226                int width = drawable.getIntrinsicWidth();
227                int height = drawable.getIntrinsicHeight();
228                Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
229                Canvas canvas = new Canvas(bitmap);
230                drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
231                drawable.draw(canvas);
232
233                icon = Icon.createWithBitmap(bitmap);
234            }
235
236            // Check to see if the newly registered account should replace the old account.
237            String groupId = "";
238            String[] mergedImsis = mTelephonyManager.getMergedSubscriberIds();
239            boolean isMergedSim = false;
240            if (mergedImsis != null && subscriberId != null && !isEmergency) {
241                for (String imsi : mergedImsis) {
242                    if (imsi.equals(subscriberId)) {
243                        isMergedSim = true;
244                        break;
245                    }
246                }
247            }
248            if(isMergedSim) {
249                groupId = GROUP_PREFIX + line1Number;
250                Log.i(this, "Adding Merged Account with group: " + Log.pii(groupId));
251            }
252
253            PhoneAccount account = PhoneAccount.builder(phoneAccountHandle, label)
254                    .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, line1Number, null))
255                    .setSubscriptionAddress(
256                            Uri.fromParts(PhoneAccount.SCHEME_TEL, subNumber, null))
257                    .setCapabilities(capabilities)
258                    .setIcon(icon)
259                    .setHighlightColor(color)
260                    .setShortDescription(description)
261                    .setSupportedUriSchemes(Arrays.asList(
262                            PhoneAccount.SCHEME_TEL, PhoneAccount.SCHEME_VOICEMAIL))
263                    .setExtras(instantLetteringExtras)
264                    .setGroupId(groupId)
265                    .build();
266
267            // Register with Telecom and put into the account entry.
268            mTelecomManager.registerPhoneAccount(account);
269
270            return account;
271        }
272
273        public PhoneAccountHandle getPhoneAccountHandle() {
274            return mAccount != null ? mAccount.getAccountHandle() : null;
275        }
276
277        /**
278         * Determines from carrier configuration whether pausing of IMS video calls is supported.
279         *
280         * @return {@code true} if pausing IMS video calls is supported.
281         */
282        private boolean isCarrierVideoPauseSupported() {
283            // Check if IMS video pause is supported.
284            PersistableBundle b =
285                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
286            return b != null &&
287                    b.getBoolean(CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
288        }
289
290        /**
291         * Determines from carrier configuration whether RCS presence indication for video calls is
292         * supported.
293         *
294         * @return {@code true} if RCS presence indication for video calls is supported.
295         */
296        private boolean isCarrierVideoPresenceSupported() {
297            PersistableBundle b =
298                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
299            return b != null &&
300                    b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
301        }
302
303        /**
304         * Determines from carrier config whether instant lettering is supported.
305         *
306         * @return {@code true} if instant lettering is supported, {@code false} otherwise.
307         */
308        private boolean isCarrierInstantLetteringSupported() {
309            PersistableBundle b =
310                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
311            return b != null &&
312                    b.getBoolean(CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL);
313        }
314
315        /**
316         * Determines from carrier config whether merging calls is supported.
317         *
318         * @return {@code true} if merging calls is supported, {@code false} otherwise.
319         */
320        private boolean isCarrierMergeCallSupported() {
321            PersistableBundle b =
322                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
323            return b != null &&
324                    b.getBoolean(CarrierConfigManager.KEY_SUPPORT_CONFERENCE_CALL_BOOL);
325        }
326
327        /**
328         * Determines from carrier config whether emergency video calls are supported.
329         *
330         * @return {@code true} if emergency video calls are allowed, {@code false} otherwise.
331         */
332        private boolean isCarrierEmergencyVideoCallsAllowed() {
333            PersistableBundle b =
334                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
335            return b != null &&
336                    b.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL);
337        }
338
339        /**
340         * Determines from carrier config whether video conferencing is supported.
341         *
342         * @return {@code true} if video conferencing is supported, {@code false} otherwise.
343         */
344        private boolean isCarrierVideoConferencingSupported() {
345            PersistableBundle b =
346                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
347            return b != null &&
348                    b.getBoolean(CarrierConfigManager.KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL);
349        }
350
351        /**
352         * Determines from carrier config whether merging of wifi calls is allowed when VoWIFI is
353         * turned off.
354         *
355         * @return {@code true} merging of wifi calls when VoWIFI is disabled should be prevented,
356         *      {@code false} otherwise.
357         */
358        private boolean isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff() {
359            PersistableBundle b =
360                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
361            return b != null && b.getBoolean(
362                    CarrierConfigManager.KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL);
363        }
364
365        /**
366         * @return The {@link PhoneAccount} extras associated with the current subscription.
367         */
368        private Bundle getPhoneAccountExtras() {
369            PersistableBundle b =
370                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
371
372            int instantLetteringMaxLength = b.getInt(
373                    CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT);
374            String instantLetteringEncoding = b.getString(
375                    CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING);
376
377            Bundle phoneAccountExtras = new Bundle();
378            phoneAccountExtras.putInt(PhoneAccount.EXTRA_CALL_SUBJECT_MAX_LENGTH,
379                    instantLetteringMaxLength);
380            phoneAccountExtras.putString(PhoneAccount.EXTRA_CALL_SUBJECT_CHARACTER_ENCODING,
381                    instantLetteringEncoding);
382            return phoneAccountExtras;
383        }
384
385        /**
386         * Receives callback from {@link PstnPhoneCapabilitiesNotifier} when the video capabilities
387         * have changed.
388         *
389         * @param isVideoCapable {@code true} if video is capable.
390         */
391        @Override
392        public void onVideoCapabilitiesChanged(boolean isVideoCapable) {
393            mIsVideoCapable = isVideoCapable;
394            synchronized (mAccountsLock) {
395                if (!mAccounts.contains(this)) {
396                    // Account has already been torn down, don't try to register it again.
397                    // This handles the case where teardown has already happened, and we got a video
398                    // update that lost the race for the mAccountsLock.  In such a scenario by the
399                    // time we get here, the original phone account could have been torn down.
400                    return;
401                }
402                mAccount = registerPstnPhoneAccount(mIsEmergency, mIsDummy);
403            }
404        }
405
406        /**
407         * Indicates whether this account supports pausing video calls.
408         * @return {@code true} if the account supports pausing video calls, {@code false}
409         * otherwise.
410         */
411        public boolean isVideoPauseSupported() {
412            return mIsVideoCapable && mIsVideoPauseSupported;
413        }
414
415        /**
416         * Indicates whether this account supports merging calls (i.e. conferencing).
417         * @return {@code true} if the account supports merging calls, {@code false} otherwise.
418         */
419        public boolean isMergeCallSupported() {
420            return mIsMergeCallSupported;
421        }
422
423        /**
424         * Indicates whether this account supports video conferencing.
425         * @return {@code true} if the account supports video conferencing, {@code false} otherwise.
426         */
427        public boolean isVideoConferencingSupported() {
428            return mIsVideoConferencingSupported;
429        }
430
431        /**
432         * Indicate whether this account allow merging of wifi calls when VoWIFI is off.
433         * @return {@code true} if allowed, {@code false} otherwise.
434         */
435        public boolean isMergeOfWifiCallsAllowedWhenVoWifiOff() {
436            return mIsMergeOfWifiCallsAllowedWhenVoWifiOff;
437        }
438    }
439
440    private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
441            new OnSubscriptionsChangedListener() {
442        @Override
443        public void onSubscriptionsChanged() {
444            // Any time the SubscriptionInfo changes...rerun the setup
445            tearDownAccounts();
446            setupAccounts();
447        }
448    };
449
450    private final BroadcastReceiver mUserSwitchedReceiver = new BroadcastReceiver() {
451        @Override
452        public void onReceive(Context context, Intent intent) {
453            Log.i(this, "User changed, re-registering phone accounts.");
454
455            int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
456            UserHandle currentUserHandle = new UserHandle(userHandleId);
457            mIsPrimaryUser = UserManager.get(mContext).getPrimaryUser().getUserHandle()
458                    .equals(currentUserHandle);
459
460            // Any time the user changes, re-register the accounts.
461            tearDownAccounts();
462            setupAccounts();
463        }
464    };
465
466    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
467        @Override
468        public void onServiceStateChanged(ServiceState serviceState) {
469            int newState = serviceState.getState();
470            if (newState == ServiceState.STATE_IN_SERVICE && mServiceState != newState) {
471                tearDownAccounts();
472                setupAccounts();
473            }
474            mServiceState = newState;
475        }
476    };
477
478    private static TelecomAccountRegistry sInstance;
479    private final Context mContext;
480    private final TelecomManager mTelecomManager;
481    private final TelephonyManager mTelephonyManager;
482    private final SubscriptionManager mSubscriptionManager;
483    private List<AccountEntry> mAccounts = new LinkedList<AccountEntry>();
484    private Object mAccountsLock = new Object();
485    private int mServiceState = ServiceState.STATE_POWER_OFF;
486    private boolean mIsPrimaryUser = true;
487
488    // TODO: Remove back-pointer from app singleton to Service, since this is not a preferred
489    // pattern; redesign. This was added to fix a late release bug.
490    private TelephonyConnectionService mTelephonyConnectionService;
491
492    TelecomAccountRegistry(Context context) {
493        mContext = context;
494        mTelecomManager = TelecomManager.from(context);
495        mTelephonyManager = TelephonyManager.from(context);
496        mSubscriptionManager = SubscriptionManager.from(context);
497    }
498
499    static synchronized final TelecomAccountRegistry getInstance(Context context) {
500        if (sInstance == null && context != null) {
501            sInstance = new TelecomAccountRegistry(context);
502        }
503        return sInstance;
504    }
505
506    void setTelephonyConnectionService(TelephonyConnectionService telephonyConnectionService) {
507        this.mTelephonyConnectionService = telephonyConnectionService;
508    }
509
510    TelephonyConnectionService getTelephonyConnectionService() {
511        return mTelephonyConnectionService;
512    }
513
514    /**
515     * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} supports
516     * pausing video calls.
517     *
518     * @param handle The {@link PhoneAccountHandle}.
519     * @return {@code True} if video pausing is supported.
520     */
521    boolean isVideoPauseSupported(PhoneAccountHandle handle) {
522        synchronized (mAccountsLock) {
523            for (AccountEntry entry : mAccounts) {
524                if (entry.getPhoneAccountHandle().equals(handle)) {
525                    return entry.isVideoPauseSupported();
526                }
527            }
528        }
529        return false;
530    }
531
532    /**
533     * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} supports
534     * merging calls.
535     *
536     * @param handle The {@link PhoneAccountHandle}.
537     * @return {@code True} if merging calls is supported.
538     */
539    boolean isMergeCallSupported(PhoneAccountHandle handle) {
540        synchronized (mAccountsLock) {
541            for (AccountEntry entry : mAccounts) {
542                if (entry.getPhoneAccountHandle().equals(handle)) {
543                    return entry.isMergeCallSupported();
544                }
545            }
546        }
547        return false;
548    }
549
550    /**
551     * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} supports
552     * video conferencing.
553     *
554     * @param handle The {@link PhoneAccountHandle}.
555     * @return {@code True} if video conferencing is supported.
556     */
557    boolean isVideoConferencingSupported(PhoneAccountHandle handle) {
558        synchronized (mAccountsLock) {
559            for (AccountEntry entry : mAccounts) {
560                if (entry.getPhoneAccountHandle().equals(handle)) {
561                    return entry.isVideoConferencingSupported();
562                }
563            }
564        }
565        return false;
566    }
567
568    /**
569     * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} allows
570     * merging of wifi calls when VoWIFI is disabled.
571     *
572     * @param handle The {@link PhoneAccountHandle}.
573     * @return {@code True} if merging of wifi calls is allowed when VoWIFI is disabled.
574     */
575    boolean isMergeOfWifiCallsAllowedWhenVoWifiOff(final PhoneAccountHandle handle) {
576        synchronized (mAccountsLock) {
577            Optional<AccountEntry> result = mAccounts.stream().filter(
578                    entry -> entry.getPhoneAccountHandle().equals(handle)).findFirst();
579
580            if (result.isPresent()) {
581                return result.get().isMergeOfWifiCallsAllowedWhenVoWifiOff();
582            } else {
583                return false;
584            }
585        }
586    }
587
588    /**
589     * @return Reference to the {@code TelecomAccountRegistry}'s subscription manager.
590     */
591    SubscriptionManager getSubscriptionManager() {
592        return mSubscriptionManager;
593    }
594
595    /**
596     * Returns the address (e.g. the phone number) associated with a subscription.
597     *
598     * @param handle The phone account handle to find the subscription address for.
599     * @return The address.
600     */
601    Uri getAddress(PhoneAccountHandle handle) {
602        synchronized (mAccountsLock) {
603            for (AccountEntry entry : mAccounts) {
604                if (entry.getPhoneAccountHandle().equals(handle)) {
605                    return entry.mAccount.getAddress();
606                }
607            }
608        }
609        return null;
610    }
611
612    /**
613     * Sets up all the phone accounts for SIMs on first boot.
614     */
615    void setupOnBoot() {
616        // TODO: When this object "finishes" we should unregister by invoking
617        // SubscriptionManager.getInstance(mContext).unregister(mOnSubscriptionsChangedListener);
618        // This is not strictly necessary because it will be unregistered if the
619        // notification fails but it is good form.
620
621        // Register for SubscriptionInfo list changes which is guaranteed
622        // to invoke onSubscriptionsChanged the first time.
623        SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
624                mOnSubscriptionsChangedListener);
625
626        // We also need to listen for changes to the service state (e.g. emergency -> in service)
627        // because this could signal a removal or addition of a SIM in a single SIM phone.
628        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
629
630        // Listen for user switches.  When the user switches, we need to ensure that if the current
631        // use is not the primary user we disable video calling.
632        mContext.registerReceiver(mUserSwitchedReceiver,
633                new IntentFilter(Intent.ACTION_USER_SWITCHED));
634    }
635
636    /**
637     * Determines if the list of {@link AccountEntry}(s) contains an {@link AccountEntry} with a
638     * specified {@link PhoneAccountHandle}.
639     *
640     * @param handle The {@link PhoneAccountHandle}.
641     * @return {@code True} if an entry exists.
642     */
643    boolean hasAccountEntryForPhoneAccount(PhoneAccountHandle handle) {
644        synchronized (mAccountsLock) {
645            for (AccountEntry entry : mAccounts) {
646                if (entry.getPhoneAccountHandle().equals(handle)) {
647                    return true;
648                }
649            }
650        }
651        return false;
652    }
653
654    /**
655     * Un-registers any {@link PhoneAccount}s which are no longer present in the list
656     * {@code AccountEntry}(s).
657     */
658    private void cleanupPhoneAccounts() {
659        ComponentName telephonyComponentName =
660                new ComponentName(mContext, TelephonyConnectionService.class);
661        // This config indicates whether the emergency account was flagged as emergency calls only
662        // in which case we need to consider all phone accounts, not just the call capable ones.
663        final boolean emergencyCallsOnlyEmergencyAccount = mContext.getResources().getBoolean(
664                R.bool.config_emergency_account_emergency_calls_only);
665        List<PhoneAccountHandle> accountHandles = emergencyCallsOnlyEmergencyAccount
666                ? mTelecomManager.getAllPhoneAccountHandles()
667                : mTelecomManager.getCallCapablePhoneAccounts(true /* includeDisabled */);
668
669        for (PhoneAccountHandle handle : accountHandles) {
670            if (telephonyComponentName.equals(handle.getComponentName()) &&
671                    !hasAccountEntryForPhoneAccount(handle)) {
672                Log.i(this, "Unregistering phone account %s.", handle);
673                mTelecomManager.unregisterPhoneAccount(handle);
674            }
675        }
676    }
677
678    private void setupAccounts() {
679        // Go through SIM-based phones and register ourselves -- registering an existing account
680        // will cause the existing entry to be replaced.
681        Phone[] phones = PhoneFactory.getPhones();
682        Log.d(this, "Found %d phones.  Attempting to register.", phones.length);
683
684        final boolean phoneAccountsEnabled = mContext.getResources().getBoolean(
685                R.bool.config_pstn_phone_accounts_enabled);
686
687        synchronized (mAccountsLock) {
688            if (phoneAccountsEnabled) {
689                for (Phone phone : phones) {
690                    int subscriptionId = phone.getSubId();
691                    Log.d(this, "Phone with subscription id %d", subscriptionId);
692                    // setupAccounts can be called multiple times during service changes. Don't add an
693                    // account if the Icc has not been set yet.
694                    if (subscriptionId >= 0 && phone.getFullIccSerialNumber() != null) {
695                        mAccounts.add(new AccountEntry(phone, false /* emergency */,
696                                false /* isDummy */));
697                    }
698                }
699            }
700
701            // If we did not list ANY accounts, we need to provide a "default" SIM account
702            // for emergency numbers since no actual SIM is needed for dialing emergency
703            // numbers but a phone account is.
704            if (mAccounts.isEmpty()) {
705                mAccounts.add(new AccountEntry(PhoneFactory.getDefaultPhone(), true /* emergency */,
706                        false /* isDummy */));
707            }
708
709            // Add a fake account entry.
710            if (DBG && phones.length > 0 && "TRUE".equals(System.getProperty("dummy_sim"))) {
711                mAccounts.add(new AccountEntry(phones[0], false /* emergency */,
712                        true /* isDummy */));
713            }
714        }
715
716        // Clean up any PhoneAccounts that are no longer relevant
717        cleanupPhoneAccounts();
718
719        // At some point, the phone account ID was switched from the subId to the iccId.
720        // If there is a default account, check if this is the case, and upgrade the default account
721        // from using the subId to iccId if so.
722        PhoneAccountHandle defaultPhoneAccount =
723                mTelecomManager.getUserSelectedOutgoingPhoneAccount();
724        ComponentName telephonyComponentName =
725                new ComponentName(mContext, TelephonyConnectionService.class);
726
727        if (defaultPhoneAccount != null &&
728                telephonyComponentName.equals(defaultPhoneAccount.getComponentName()) &&
729                !hasAccountEntryForPhoneAccount(defaultPhoneAccount)) {
730
731            String phoneAccountId = defaultPhoneAccount.getId();
732            if (!TextUtils.isEmpty(phoneAccountId) && TextUtils.isDigitsOnly(phoneAccountId)) {
733                PhoneAccountHandle upgradedPhoneAccount =
734                        PhoneUtils.makePstnPhoneAccountHandle(
735                                PhoneGlobals.getPhone(Integer.parseInt(phoneAccountId)));
736
737                if (hasAccountEntryForPhoneAccount(upgradedPhoneAccount)) {
738                    mTelecomManager.setUserSelectedOutgoingPhoneAccount(upgradedPhoneAccount);
739                }
740            }
741        }
742    }
743
744    private void tearDownAccounts() {
745        synchronized (mAccountsLock) {
746            for (AccountEntry entry : mAccounts) {
747                entry.teardown();
748            }
749            mAccounts.clear();
750        }
751    }
752}
753