1/*
2 * Copyright (C) 2015 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 */
16package com.android.phone.vvm.omtp.sync;
17
18import android.content.Context;
19import android.telecom.PhoneAccountHandle;
20import android.telephony.PhoneStateListener;
21import android.telephony.SubscriptionManager;
22import android.telephony.TelephonyManager;
23import com.android.internal.telephony.Phone;
24import com.android.phone.PhoneUtils;
25import com.android.phone.VoicemailStatus;
26import com.android.phone.vvm.omtp.VvmPhoneStateListener;
27import java.util.Collections;
28import java.util.Map;
29import java.util.Set;
30import java.util.concurrent.ConcurrentHashMap;
31
32/**
33 * A singleton class designed to remember the active OMTP visual voicemail sources. Because a
34 * voicemail source is tied 1:1 to a phone account, the phone account handle is used as the key
35 * for each voicemail source and the associated data.
36 */
37public class OmtpVvmSourceManager {
38    public static final String TAG = "OmtpVvmSourceManager";
39
40    private static OmtpVvmSourceManager sInstance = new OmtpVvmSourceManager();
41
42    private Context mContext;
43    private SubscriptionManager mSubscriptionManager;
44    private TelephonyManager mTelephonyManager;
45    // Each phone account is associated with a phone state listener for updates to whether the
46    // device is able to sync.
47    private Set<PhoneAccountHandle> mActiveVvmSources;
48    private Map<PhoneAccountHandle, PhoneStateListener> mPhoneStateListenerMap;
49
50    /**
51     * Private constructor. Instance should only be acquired through getInstance().
52     */
53    private OmtpVvmSourceManager() {}
54
55    public static OmtpVvmSourceManager getInstance(Context context) {
56        sInstance.setup(context);
57        return sInstance;
58    }
59
60    /**
61     * Set the context and system services so they do not need to be retrieved every time.
62     * @param context The context to get the subscription and telephony manager for.
63     */
64    private void setup(Context context) {
65        if (mContext == null) {
66            mContext = context;
67            mSubscriptionManager = SubscriptionManager.from(context);
68            mTelephonyManager = (TelephonyManager)
69                    mContext.getSystemService(Context.TELEPHONY_SERVICE);
70            mActiveVvmSources = Collections.newSetFromMap(
71                    new ConcurrentHashMap<PhoneAccountHandle, Boolean>(8, 0.9f, 1));
72            mPhoneStateListenerMap =
73                    new ConcurrentHashMap<PhoneAccountHandle, PhoneStateListener>(8, 0.9f, 1);
74        }
75    }
76
77    public void addSource(PhoneAccountHandle phoneAccount) {
78        mActiveVvmSources.add(phoneAccount);
79    }
80
81    /**
82     * When a voicemail source is removed, we don't always know which one was removed. Check the
83     * list of registered phone accounts against the active subscriptions list and remove the
84     * inactive sources.
85     */
86    public void removeInactiveSources() {
87        for (PhoneAccountHandle phoneAccount : mActiveVvmSources) {
88            if (!PhoneUtils.isPhoneAccountActive(mSubscriptionManager, phoneAccount)) {
89                removeSource(phoneAccount);
90            }
91        }
92
93        // Remove any orphaned phone state listeners as well.
94        for (PhoneAccountHandle phoneAccount : mPhoneStateListenerMap.keySet()) {
95            if (!PhoneUtils.isPhoneAccountActive(mSubscriptionManager, phoneAccount)) {
96                removePhoneStateListener(phoneAccount);
97            }
98        }
99    }
100
101    public void removeSource(Phone phone) {
102        removeSource(PhoneUtils.makePstnPhoneAccountHandle(phone));
103    }
104
105    public void removeSource(PhoneAccountHandle phoneAccount) {
106        // TODO: should use OmtpVvmCarrierConfigHelper to handle the event. But currently it
107        // couldn't handle events on removed SIMs
108        VoicemailStatus.disable(mContext, phoneAccount);
109        removePhoneStateListener(phoneAccount);
110        mActiveVvmSources.remove(phoneAccount);
111    }
112
113    public void addPhoneStateListener(Phone phone) {
114        addPhoneStateListener(PhoneUtils.makePstnPhoneAccountHandle(phone));
115    }
116
117    public void addPhoneStateListener(PhoneAccountHandle phoneAccount) {
118        if (!mPhoneStateListenerMap.containsKey(phoneAccount)) {
119            VvmPhoneStateListener phoneStateListener = new VvmPhoneStateListener(mContext,
120                    PhoneUtils.makePstnPhoneAccountHandle(phoneAccount.getId()));
121            mPhoneStateListenerMap.put(phoneAccount, phoneStateListener);
122            mTelephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
123        }
124    }
125
126    public void removePhoneStateListener(PhoneAccountHandle phoneAccount) {
127        PhoneStateListener phoneStateListener =
128                mPhoneStateListenerMap.remove(phoneAccount);
129        mTelephonyManager.listen(phoneStateListener, 0);
130    }
131
132    public Set<PhoneAccountHandle> getOmtpVvmSources() {
133        return mActiveVvmSources;
134    }
135
136    /**
137     * Check if a certain account is registered.
138     *
139     * @param phoneAccount The account to look for.
140     * @return {@code true} if the account is in the list of registered OMTP voicemail sources.
141     * {@code false} otherwise.
142     */
143    public boolean isVvmSourceRegistered(PhoneAccountHandle phoneAccount) {
144        if (phoneAccount == null) {
145            return false;
146        }
147
148        return mActiveVvmSources.contains(phoneAccount);
149    }
150}