• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.dialer.notification;
18 
19 import android.annotation.TargetApi;
20 import android.app.NotificationChannel;
21 import android.app.NotificationManager;
22 import android.content.Context;
23 import android.media.AudioAttributes;
24 import android.os.Build.VERSION_CODES;
25 import android.provider.Settings;
26 import android.support.annotation.NonNull;
27 import android.support.annotation.Nullable;
28 import android.support.v4.os.BuildCompat;
29 import android.telecom.PhoneAccount;
30 import android.telecom.PhoneAccountHandle;
31 import android.telecom.TelecomManager;
32 import android.telephony.TelephonyManager;
33 import android.text.TextUtils;
34 import android.util.ArraySet;
35 import com.android.dialer.common.Assert;
36 import com.android.dialer.common.LogUtil;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Set;
40 
41 /** Utilities for working with voicemail channels. */
42 @TargetApi(VERSION_CODES.O)
43 /* package */ final class VoicemailChannelUtils {
44   private static final String GLOBAL_VOICEMAIL_CHANNEL_ID = "phone_voicemail";
45   private static final String PER_ACCOUNT_VOICEMAIL_CHANNEL_ID_PREFIX = "phone_voicemail_account_";
46 
getAllChannelIds(@onNull Context context)47   static Set<String> getAllChannelIds(@NonNull Context context) {
48     Assert.checkArgument(BuildCompat.isAtLeastO());
49     Assert.isNotNull(context);
50 
51     Set<String> result = new ArraySet<>();
52     if (isSingleSimDevice(context)) {
53       result.add(GLOBAL_VOICEMAIL_CHANNEL_ID);
54     } else {
55       for (PhoneAccountHandle handle : getAllEligableAccounts(context)) {
56         result.add(getChannelIdForAccount(handle));
57       }
58     }
59     return result;
60   }
61 
createAllChannels(@onNull Context context)62   static void createAllChannels(@NonNull Context context) {
63     Assert.checkArgument(BuildCompat.isAtLeastO());
64     Assert.isNotNull(context);
65 
66     if (isSingleSimDevice(context)) {
67       createGlobalVoicemailChannel(context);
68     } else {
69       for (PhoneAccountHandle handle : getAllEligableAccounts(context)) {
70         createVoicemailChannelForAccount(context, handle);
71       }
72     }
73   }
74 
75   @NonNull
getChannelId(@onNull Context context, @Nullable PhoneAccountHandle handle)76   static String getChannelId(@NonNull Context context, @Nullable PhoneAccountHandle handle) {
77     Assert.checkArgument(BuildCompat.isAtLeastO());
78     Assert.isNotNull(context);
79 
80     // Most devices we deal with have a single SIM slot. No need to distinguish between phone
81     // accounts.
82     if (isSingleSimDevice(context)) {
83       return GLOBAL_VOICEMAIL_CHANNEL_ID;
84     }
85 
86     // We can get a null phone account at random points (modem reboot, etc...). Gracefully degrade
87     // by using the default channel.
88     if (handle == null) {
89       LogUtil.i(
90           "VoicemailChannelUtils.getChannelId",
91           "no phone account on a multi-SIM device, using default channel");
92       return NotificationChannelId.DEFAULT;
93     }
94 
95     // Voicemail notifications should always be associated with a SIM based phone account.
96     if (!isChannelAllowedForAccount(context, handle)) {
97       LogUtil.i(
98           "VoicemailChannelUtils.getChannelId",
99           "phone account is not for a SIM, using default channel");
100       return NotificationChannelId.DEFAULT;
101     }
102 
103     // Now we're in the multi-SIM case.
104     String channelId = getChannelIdForAccount(handle);
105     if (!doesChannelExist(context, channelId)) {
106       LogUtil.i(
107           "VoicemailChannelUtils.getChannelId",
108           "voicemail channel not found for phone account (possible SIM swap?), creating a new one");
109       createVoicemailChannelForAccount(context, handle);
110     }
111     return channelId;
112   }
113 
doesChannelExist(@onNull Context context, @NonNull String channelId)114   private static boolean doesChannelExist(@NonNull Context context, @NonNull String channelId) {
115     return context.getSystemService(NotificationManager.class).getNotificationChannel(channelId)
116         != null;
117   }
118 
getChannelIdForAccount(@onNull PhoneAccountHandle handle)119   private static String getChannelIdForAccount(@NonNull PhoneAccountHandle handle) {
120     Assert.isNotNull(handle);
121     return PER_ACCOUNT_VOICEMAIL_CHANNEL_ID_PREFIX + ":" + handle.getId();
122   }
123 
124   /**
125    * Creates a voicemail channel but doesn't associate it with a SIM. For devices with only one SIM
126    * slot this is ideal because there won't be duplication in the settings UI.
127    */
createGlobalVoicemailChannel(@onNull Context context)128   private static void createGlobalVoicemailChannel(@NonNull Context context) {
129     NotificationChannel channel = newChannel(context, GLOBAL_VOICEMAIL_CHANNEL_ID, null);
130 
131     TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
132     PhoneAccountHandle handle =
133         telecomManager.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
134     if (handle == null) {
135       LogUtil.i(
136           "VoicemailChannelUtils.createGlobalVoicemailChannel",
137           "phone account is null, not migrating sound settings");
138     } else if (!isChannelAllowedForAccount(context, handle)) {
139       LogUtil.i(
140           "VoicemailChannelUtils.createGlobalVoicemailChannel",
141           "phone account is not eligable, not migrating sound settings");
142     } else {
143       migrateVoicemailSoundSettings(context, channel, handle);
144     }
145     context.getSystemService(NotificationManager.class).createNotificationChannel(channel);
146   }
147 
getAllEligableAccounts(@onNull Context context)148   private static List<PhoneAccountHandle> getAllEligableAccounts(@NonNull Context context) {
149     List<PhoneAccountHandle> handles = new ArrayList<>();
150     TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
151     for (PhoneAccountHandle handle : telecomManager.getCallCapablePhoneAccounts()) {
152       if (isChannelAllowedForAccount(context, handle)) {
153         handles.add(handle);
154       }
155     }
156     return handles;
157   }
158 
createVoicemailChannelForAccount( @onNull Context context, @NonNull PhoneAccountHandle handle)159   private static void createVoicemailChannelForAccount(
160       @NonNull Context context, @NonNull PhoneAccountHandle handle) {
161     PhoneAccount phoneAccount =
162         context.getSystemService(TelecomManager.class).getPhoneAccount(handle);
163     NotificationChannel channel =
164         newChannel(context, getChannelIdForAccount(handle), phoneAccount.getLabel());
165     migrateVoicemailSoundSettings(context, channel, handle);
166     context.getSystemService(NotificationManager.class).createNotificationChannel(channel);
167   }
168 
migrateVoicemailSoundSettings( @onNull Context context, @NonNull NotificationChannel channel, @NonNull PhoneAccountHandle handle)169   private static void migrateVoicemailSoundSettings(
170       @NonNull Context context,
171       @NonNull NotificationChannel channel,
172       @NonNull PhoneAccountHandle handle) {
173     TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
174     channel.enableVibration(telephonyManager.isVoicemailVibrationEnabled(handle));
175     channel.setSound(
176         telephonyManager.getVoicemailRingtoneUri(handle),
177         new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
178   }
179 
isChannelAllowedForAccount( @onNull Context context, @NonNull PhoneAccountHandle handle)180   private static boolean isChannelAllowedForAccount(
181       @NonNull Context context, @NonNull PhoneAccountHandle handle) {
182     PhoneAccount phoneAccount =
183         context.getSystemService(TelecomManager.class).getPhoneAccount(handle);
184     if (phoneAccount == null) {
185       return false;
186     }
187     if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
188       return false;
189     }
190     return true;
191   }
192 
newChannel( @onNull Context context, @NonNull String channelId, @Nullable CharSequence nameSuffix)193   private static NotificationChannel newChannel(
194       @NonNull Context context, @NonNull String channelId, @Nullable CharSequence nameSuffix) {
195     CharSequence name = context.getText(R.string.notification_channel_voicemail);
196     // TODO: Use a string resource template after v10.
197     if (!TextUtils.isEmpty(nameSuffix)) {
198       name = TextUtils.concat(name, ": ", nameSuffix);
199     }
200 
201     NotificationChannel channel =
202         new NotificationChannel(channelId, name, NotificationManager.IMPORTANCE_DEFAULT);
203     channel.setShowBadge(true);
204     channel.enableLights(true);
205     channel.enableVibration(true);
206     channel.setSound(
207         Settings.System.DEFAULT_NOTIFICATION_URI,
208         new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
209     return channel;
210   }
211 
isSingleSimDevice(@onNull Context context)212   private static boolean isSingleSimDevice(@NonNull Context context) {
213     return context.getSystemService(TelephonyManager.class).getPhoneCount() <= 1;
214   }
215 
VoicemailChannelUtils()216   private VoicemailChannelUtils() {}
217 }
218