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 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 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 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 114 private static boolean doesChannelExist(@NonNull Context context, @NonNull String channelId) { 115 return context.getSystemService(NotificationManager.class).getNotificationChannel(channelId) 116 != null; 117 } 118 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 */ 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 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 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 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 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 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 212 private static boolean isSingleSimDevice(@NonNull Context context) { 213 return context.getSystemService(TelephonyManager.class).getPhoneCount() <= 1; 214 } 215 216 private VoicemailChannelUtils() {} 217 } 218