1 /*
2  * Copyright (C) 2017 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.voicemail.impl;
18 
19 import android.annotation.TargetApi;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.os.Build.VERSION_CODES;
23 import android.os.UserManager;
24 import android.preference.PreferenceManager;
25 import android.support.annotation.MainThread;
26 import android.support.annotation.NonNull;
27 import android.telecom.PhoneAccountHandle;
28 import android.telephony.TelephonyManager;
29 import android.telephony.VisualVoicemailService;
30 import android.telephony.VisualVoicemailSms;
31 import com.android.dialer.logging.DialerImpression;
32 import com.android.dialer.logging.Logger;
33 import com.android.voicemail.VoicemailComponent;
34 import com.android.voicemail.impl.settings.VisualVoicemailSettingsUtil;
35 import com.android.voicemail.impl.sms.LegacyModeSmsHandler;
36 import com.android.voicemail.impl.sync.VvmAccountManager;
37 
38 /** Implements {@link VisualVoicemailService} to receive visual voicemail events */
39 @TargetApi(VERSION_CODES.O)
40 public class OmtpService extends VisualVoicemailService {
41 
42   private static final String TAG = "VvmOmtpService";
43 
44   public static final String ACTION_SMS_RECEIVED = "com.android.vociemailomtp.sms.sms_received";
45 
46   public static final String EXTRA_VOICEMAIL_SMS = "extra_voicemail_sms";
47 
48   private static final String IS_SHUTTING_DOWN = "com.android.voicemail.impl.is_shutting_down";
49 
50   @Override
onCellServiceConnected( VisualVoicemailTask task, final PhoneAccountHandle phoneAccountHandle)51   public void onCellServiceConnected(
52       VisualVoicemailTask task, final PhoneAccountHandle phoneAccountHandle) {
53     VvmLog.i(TAG, "onCellServiceConnected");
54     if (!isModuleEnabled()) {
55       VvmLog.e(TAG, "onCellServiceConnected received when module is disabled");
56       task.finish();
57       return;
58     }
59 
60     if (!isUserUnlocked(this)) {
61       VvmLog.i(TAG, "onCellServiceConnected: user locked");
62       task.finish();
63       return;
64     }
65 
66     if (!isServiceEnabled(phoneAccountHandle)) {
67       disableFilter(phoneAccountHandle);
68       task.finish();
69       return;
70     }
71 
72     Logger.get(this).logImpression(DialerImpression.Type.VVM_UNBUNDLED_EVENT_RECEIVED);
73     ActivationTask.start(OmtpService.this, phoneAccountHandle, null);
74     task.finish();
75   }
76 
77   @Override
onSmsReceived(VisualVoicemailTask task, final VisualVoicemailSms sms)78   public void onSmsReceived(VisualVoicemailTask task, final VisualVoicemailSms sms) {
79     VvmLog.i(TAG, "onSmsReceived");
80     if (!isModuleEnabled()) {
81       VvmLog.e(TAG, "onSmsReceived received when module is disabled");
82       task.finish();
83       return;
84     }
85 
86     if (!isUserUnlocked(this)) {
87       LegacyModeSmsHandler.handle(this, sms);
88       return;
89     }
90 
91     if (!isServiceEnabled(sms.getPhoneAccountHandle())) {
92       VvmLog.e(TAG, "onSmsReceived received when service is disabled");
93       disableFilter(sms.getPhoneAccountHandle());
94       task.finish();
95       return;
96     }
97 
98     // isUserUnlocked() is not checked. OmtpMessageReceiver will handle the locked case.
99 
100     Logger.get(this).logImpression(DialerImpression.Type.VVM_UNBUNDLED_EVENT_RECEIVED);
101     Intent intent = new Intent(ACTION_SMS_RECEIVED);
102     intent.setPackage(getPackageName());
103     intent.putExtra(EXTRA_VOICEMAIL_SMS, sms);
104     sendBroadcast(intent);
105     task.finish();
106   }
107 
108   @Override
onSimRemoved( final VisualVoicemailTask task, final PhoneAccountHandle phoneAccountHandle)109   public void onSimRemoved(
110       final VisualVoicemailTask task, final PhoneAccountHandle phoneAccountHandle) {
111     VvmLog.i(TAG, "onSimRemoved");
112     if (!isModuleEnabled()) {
113       VvmLog.e(TAG, "onSimRemoved called when module is disabled");
114       task.finish();
115       return;
116     }
117 
118     if (!isUserUnlocked(this)) {
119       VvmLog.i(TAG, "onSimRemoved: user locked");
120       task.finish();
121       return;
122     }
123 
124     if (isShuttingDown(this)) {
125       VvmLog.i(TAG, "onSimRemoved: system shutting down, ignoring");
126       task.finish();
127       return;
128     }
129 
130     Logger.get(this).logImpression(DialerImpression.Type.VVM_UNBUNDLED_EVENT_RECEIVED);
131     VvmAccountManager.removeAccount(this, phoneAccountHandle);
132     task.finish();
133   }
134 
135   @Override
onStopped(VisualVoicemailTask task)136   public void onStopped(VisualVoicemailTask task) {
137     VvmLog.i(TAG, "onStopped");
138     if (!isModuleEnabled()) {
139       VvmLog.e(TAG, "onStopped called when module is disabled");
140       task.finish();
141       return;
142     }
143     if (!isUserUnlocked(this)) {
144       VvmLog.i(TAG, "onStopped: user locked");
145       task.finish();
146       return;
147     }
148     Logger.get(this).logImpression(DialerImpression.Type.VVM_UNBUNDLED_EVENT_RECEIVED);
149   }
150 
151   @MainThread
onBoot(@onNull Context context)152   static void onBoot(@NonNull Context context) {
153     VvmLog.i(TAG, "onBoot");
154     Assert.isTrue(isUserUnlocked(context));
155     Assert.isMainThread();
156     setShuttingDown(context, false);
157   }
158 
159   @MainThread
onShutdown(@onNull Context context)160   static void onShutdown(@NonNull Context context) {
161     VvmLog.i(TAG, "onShutdown");
162     Assert.isTrue(isUserUnlocked(context));
163     Assert.isMainThread();
164     setShuttingDown(context, true);
165   }
166 
isModuleEnabled()167   private boolean isModuleEnabled() {
168     return VoicemailComponent.get(this).getVoicemailClient().isVoicemailModuleEnabled();
169   }
170 
isServiceEnabled(PhoneAccountHandle phoneAccountHandle)171   private boolean isServiceEnabled(PhoneAccountHandle phoneAccountHandle) {
172     OmtpVvmCarrierConfigHelper config = new OmtpVvmCarrierConfigHelper(this, phoneAccountHandle);
173     if (!config.isValid()) {
174       VvmLog.i(TAG, "VVM not supported on " + phoneAccountHandle);
175       return false;
176     }
177     if (!VisualVoicemailSettingsUtil.isEnabled(this, phoneAccountHandle)
178         && !config.isLegacyModeEnabled()) {
179       VvmLog.i(TAG, "VVM is disabled");
180       return false;
181     }
182     return true;
183   }
184 
disableFilter(PhoneAccountHandle phoneAccountHandle)185   private void disableFilter(PhoneAccountHandle phoneAccountHandle) {
186     TelephonyManager telephonyManager =
187         getSystemService(TelephonyManager.class).createForPhoneAccountHandle(phoneAccountHandle);
188     if (telephonyManager != null) {
189       VvmLog.i(TAG, "disabling SMS filter");
190       telephonyManager.setVisualVoicemailSmsFilterSettings(null);
191     }
192   }
193 
isUserUnlocked(@onNull Context context)194   private static boolean isUserUnlocked(@NonNull Context context) {
195     UserManager userManager = context.getSystemService(UserManager.class);
196     return userManager.isUserUnlocked();
197   }
198 
setShuttingDown(Context context, boolean value)199   private static void setShuttingDown(Context context, boolean value) {
200     PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext())
201         .edit()
202         .putBoolean(IS_SHUTTING_DOWN, value)
203         .apply();
204   }
205 
isShuttingDown(Context context)206   private static boolean isShuttingDown(Context context) {
207     return PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext())
208         .getBoolean(IS_SHUTTING_DOWN, false);
209   }
210 }
211