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