• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.voicemail.impl.sync;
17 
18 import android.annotation.TargetApi;
19 import android.content.Context;
20 import android.os.Build.VERSION_CODES;
21 import android.os.UserManager;
22 import android.support.annotation.MainThread;
23 import android.support.annotation.NonNull;
24 import android.support.annotation.VisibleForTesting;
25 import android.telecom.PhoneAccountHandle;
26 import android.telecom.TelecomManager;
27 import android.util.ArraySet;
28 import com.android.dialer.common.Assert;
29 import com.android.dialer.common.PerAccountSharedPreferences;
30 import com.android.dialer.common.concurrent.ThreadUtil;
31 import com.android.dialer.util.DialerUtils;
32 import com.android.voicemail.impl.OmtpConstants;
33 import com.android.voicemail.impl.VisualVoicemailPreferences;
34 import com.android.voicemail.impl.VoicemailStatus;
35 import com.android.voicemail.impl.sms.StatusMessage;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Set;
39 
40 /**
41  * Tracks the activation state of a visual voicemail phone account. An account is considered
42  * activated if it has valid connection information from the {@link StatusMessage} stored on the
43  * device. Once activation/provisioning is completed, {@link #addAccount(Context,
44  * PhoneAccountHandle, StatusMessage)} should be called to store the connection information. When an
45  * account is removed or if the connection information is deemed invalid, {@link
46  * #removeAccount(Context, PhoneAccountHandle)} should be called to clear the connection information
47  * and allow reactivation.
48  */
49 @TargetApi(VERSION_CODES.O)
50 public class VvmAccountManager {
51   public static final String TAG = "VvmAccountManager";
52 
53   /** Listener for activation state changes. Will be called on the main thread. */
54   public interface Listener {
55     @MainThread
onActivationStateChanged(PhoneAccountHandle phoneAccountHandle, boolean isActivated)56     void onActivationStateChanged(PhoneAccountHandle phoneAccountHandle, boolean isActivated);
57   }
58 
59   @VisibleForTesting static final String IS_ACCOUNT_ACTIVATED = "is_account_activated";
60 
61   private static Set<Listener> listeners = new ArraySet<>();
62 
addAccount( Context context, PhoneAccountHandle phoneAccountHandle, StatusMessage statusMessage)63   public static void addAccount(
64       Context context, PhoneAccountHandle phoneAccountHandle, StatusMessage statusMessage) {
65     VisualVoicemailPreferences preferences =
66         new VisualVoicemailPreferences(context, phoneAccountHandle);
67     statusMessage.putStatus(preferences.edit()).apply();
68     setAccountActivated(context, phoneAccountHandle, true);
69 
70     ThreadUtil.postOnUiThread(
71         () -> {
72           for (Listener listener : listeners) {
73             listener.onActivationStateChanged(phoneAccountHandle, true);
74           }
75         });
76   }
77 
removeAccount(Context context, PhoneAccountHandle phoneAccount)78   public static void removeAccount(Context context, PhoneAccountHandle phoneAccount) {
79     VoicemailStatus.disable(context, phoneAccount);
80     setAccountActivated(context, phoneAccount, false);
81     VisualVoicemailPreferences preferences = new VisualVoicemailPreferences(context, phoneAccount);
82     preferences
83         .edit()
84         .putString(OmtpConstants.IMAP_USER_NAME, null)
85         .putString(OmtpConstants.IMAP_PASSWORD, null)
86         .apply();
87     ThreadUtil.postOnUiThread(
88         () -> {
89           for (Listener listener : listeners) {
90             listener.onActivationStateChanged(phoneAccount, false);
91           }
92         });
93   }
94 
isAccountActivated(Context context, PhoneAccountHandle phoneAccount)95   public static boolean isAccountActivated(Context context, PhoneAccountHandle phoneAccount) {
96     Assert.isNotNull(phoneAccount);
97     PerAccountSharedPreferences preferences =
98         getPreferenceForActivationState(context, phoneAccount);
99     migrateActivationState(context, preferences, phoneAccount);
100     return preferences.getBoolean(IS_ACCOUNT_ACTIVATED, false);
101   }
102 
103   @NonNull
getActiveAccounts(Context context)104   public static List<PhoneAccountHandle> getActiveAccounts(Context context) {
105     List<PhoneAccountHandle> results = new ArrayList<>();
106     for (PhoneAccountHandle phoneAccountHandle :
107         context.getSystemService(TelecomManager.class).getCallCapablePhoneAccounts()) {
108       if (isAccountActivated(context, phoneAccountHandle)) {
109         results.add(phoneAccountHandle);
110       }
111     }
112     return results;
113   }
114 
115   @MainThread
addListener(Listener listener)116   public static void addListener(Listener listener) {
117     Assert.isMainThread();
118     listeners.add(listener);
119   }
120 
121   @MainThread
removeListener(Listener listener)122   public static void removeListener(Listener listener) {
123     Assert.isMainThread();
124     listeners.remove(listener);
125   }
126 
127   /**
128    * The activation state is moved from credential protected storage to device protected storage
129    * after v10, so it can be checked under FBE. The state should be migrated to avoid reactivation.
130    */
migrateActivationState( Context context, PerAccountSharedPreferences deviceProtectedPreference, PhoneAccountHandle phoneAccountHandle)131   private static void migrateActivationState(
132       Context context,
133       PerAccountSharedPreferences deviceProtectedPreference,
134       PhoneAccountHandle phoneAccountHandle) {
135     if (!context.getSystemService(UserManager.class).isUserUnlocked()) {
136       return;
137     }
138     if (deviceProtectedPreference.contains(IS_ACCOUNT_ACTIVATED)) {
139       return;
140     }
141 
142     PerAccountSharedPreferences credentialProtectedPreference =
143         new VisualVoicemailPreferences(context, phoneAccountHandle);
144 
145     deviceProtectedPreference
146         .edit()
147         .putBoolean(
148             IS_ACCOUNT_ACTIVATED,
149             credentialProtectedPreference.getBoolean(IS_ACCOUNT_ACTIVATED, false))
150         .apply();
151   }
152 
setAccountActivated( Context context, PhoneAccountHandle phoneAccountHandle, boolean activated)153   private static void setAccountActivated(
154       Context context, PhoneAccountHandle phoneAccountHandle, boolean activated) {
155     Assert.isNotNull(phoneAccountHandle);
156     getPreferenceForActivationState(context, phoneAccountHandle)
157         .edit()
158         .putBoolean(IS_ACCOUNT_ACTIVATED, activated)
159         .apply();
160   }
161 
getPreferenceForActivationState( Context context, PhoneAccountHandle phoneAccountHandle)162   private static PerAccountSharedPreferences getPreferenceForActivationState(
163       Context context, PhoneAccountHandle phoneAccountHandle) {
164     return new PerAccountSharedPreferences(
165         context,
166         phoneAccountHandle,
167         DialerUtils.getDefaultSharedPreferenceForDeviceProtectedStorageContext(context));
168   }
169 }
170