1 /* 2 * Copyright (C) 2016 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.incallui.spam; 18 19 import android.app.PendingIntent; 20 import android.app.Service; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.os.IBinder; 24 import android.provider.CallLog; 25 import android.support.annotation.Nullable; 26 import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler; 27 import com.android.dialer.common.LogUtil; 28 import com.android.dialer.location.GeoUtil; 29 import com.android.dialer.logging.ContactLookupResult; 30 import com.android.dialer.logging.DialerImpression; 31 import com.android.dialer.logging.Logger; 32 import com.android.dialer.logging.ReportingLocation; 33 import com.android.dialer.notification.DialerNotificationManager; 34 import com.android.dialer.spam.SpamComponent; 35 import com.android.dialer.spam.SpamSettings; 36 import com.android.dialer.spam.promo.SpamBlockingPromoHelper; 37 import com.android.incallui.call.DialerCall; 38 39 /** 40 * This service determines if the device is locked/unlocked and takes an action based on the state. 41 * A service is used to to determine this, as opposed to an activity, because the user must unlock 42 * the device before a notification can start an activity. This is not the case for a service, and 43 * intents can be sent to this service even from the lock screen. This allows users to quickly 44 * report a number as spam or not spam from their lock screen. 45 */ 46 public class SpamNotificationService extends Service { 47 48 private static final String TAG = "SpamNotificationSvc"; 49 50 private static final String EXTRA_PHONE_NUMBER = "service_phone_number"; 51 private static final String EXTRA_CALL_ID = "service_call_id"; 52 private static final String EXTRA_CALL_START_TIME_MILLIS = "service_call_start_time_millis"; 53 private static final String EXTRA_NOTIFICATION_TAG = "service_notification_tag"; 54 private static final String EXTRA_NOTIFICATION_ID = "service_notification_id"; 55 private static final String EXTRA_CONTACT_LOOKUP_RESULT_TYPE = 56 "service_contact_lookup_result_type"; 57 58 private String notificationTag; 59 private int notificationId; 60 61 /** Creates an intent to start this service. */ createServiceIntent( Context context, @Nullable DialerCall call, String action, String notificationTag, int notificationId)62 public static Intent createServiceIntent( 63 Context context, 64 @Nullable DialerCall call, 65 String action, 66 String notificationTag, 67 int notificationId) { 68 Intent intent = new Intent(context, SpamNotificationService.class); 69 intent.setAction(action); 70 intent.putExtra(EXTRA_NOTIFICATION_TAG, notificationTag); 71 intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId); 72 73 if (call != null) { 74 intent.putExtra(EXTRA_PHONE_NUMBER, call.getNumber()); 75 intent.putExtra(EXTRA_CALL_ID, call.getUniqueCallId()); 76 intent.putExtra(EXTRA_CALL_START_TIME_MILLIS, call.getTimeAddedMs()); 77 intent.putExtra( 78 EXTRA_CONTACT_LOOKUP_RESULT_TYPE, call.getLogState().contactLookupResult.getNumber()); 79 } 80 return intent; 81 } 82 83 @Nullable 84 @Override onBind(Intent intent)85 public IBinder onBind(Intent intent) { 86 // Return null because clients cannot bind to this service 87 return null; 88 } 89 90 @Override onStartCommand(Intent intent, int flags, int startId)91 public int onStartCommand(Intent intent, int flags, int startId) { 92 LogUtil.d(TAG, "onStartCommand"); 93 if (intent == null) { 94 LogUtil.d(TAG, "Null intent"); 95 stopSelf(); 96 // Return {@link #START_NOT_STICKY} so service is not restarted. 97 return START_NOT_STICKY; 98 } 99 String number = intent.getStringExtra(EXTRA_PHONE_NUMBER); 100 notificationTag = intent.getStringExtra(EXTRA_NOTIFICATION_TAG); 101 notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, 1); 102 String countryIso = GeoUtil.getCurrentCountryIso(this); 103 ContactLookupResult.Type contactLookupResultType = 104 ContactLookupResult.Type.forNumber(intent.getIntExtra(EXTRA_CONTACT_LOOKUP_RESULT_TYPE, 0)); 105 106 SpamSettings spamSettings = SpamComponent.get(this).spamSettings(); 107 SpamBlockingPromoHelper spamBlockingPromoHelper = 108 new SpamBlockingPromoHelper(this, SpamComponent.get(this).spamSettings()); 109 boolean shouldShowSpamBlockingPromo = 110 SpamNotificationActivity.ACTION_MARK_NUMBER_AS_SPAM.equals(intent.getAction()) 111 && spamBlockingPromoHelper.shouldShowAfterCallSpamBlockingPromo(); 112 113 // Cancel notification only if we are not showing spam blocking promo. Otherwise we will show 114 // spam blocking promo notification in place. 115 if (!shouldShowSpamBlockingPromo) { 116 DialerNotificationManager.cancel(this, notificationTag, notificationId); 117 } 118 119 switch (intent.getAction()) { 120 case SpamNotificationActivity.ACTION_MARK_NUMBER_AS_SPAM: 121 logCallImpression( 122 intent, DialerImpression.Type.SPAM_NOTIFICATION_SERVICE_ACTION_MARK_NUMBER_AS_SPAM); 123 SpamComponent.get(this) 124 .spam() 125 .reportSpamFromAfterCallNotification( 126 number, 127 countryIso, 128 CallLog.Calls.INCOMING_TYPE, 129 ReportingLocation.Type.FEEDBACK_PROMPT, 130 contactLookupResultType); 131 new FilteredNumberAsyncQueryHandler(this).blockNumber(null, number, countryIso); 132 if (shouldShowSpamBlockingPromo) { 133 spamBlockingPromoHelper.showSpamBlockingPromoNotification( 134 notificationTag, 135 notificationId, 136 createPromoActivityPendingIntent(), 137 createEnableSpamBlockingPendingIntent()); 138 } 139 break; 140 case SpamNotificationActivity.ACTION_MARK_NUMBER_AS_NOT_SPAM: 141 logCallImpression( 142 intent, DialerImpression.Type.SPAM_NOTIFICATION_SERVICE_ACTION_MARK_NUMBER_AS_NOT_SPAM); 143 SpamComponent.get(this) 144 .spam() 145 .reportNotSpamFromAfterCallNotification( 146 number, 147 countryIso, 148 CallLog.Calls.INCOMING_TYPE, 149 ReportingLocation.Type.FEEDBACK_PROMPT, 150 contactLookupResultType); 151 break; 152 case SpamNotificationActivity.ACTION_ENABLE_SPAM_BLOCKING: 153 Logger.get(this) 154 .logImpression( 155 DialerImpression.Type.SPAM_BLOCKING_ENABLED_THROUGH_AFTER_CALL_NOTIFICATION_PROMO); 156 spamSettings.modifySpamBlockingSetting( 157 true, 158 success -> { 159 if (!success) { 160 Logger.get(this) 161 .logImpression( 162 DialerImpression.Type 163 .SPAM_BLOCKING_MODIFY_FAILURE_THROUGH_AFTER_CALL_NOTIFICATION_PROMO); 164 } 165 spamBlockingPromoHelper.showModifySettingOnCompleteToast(success); 166 }); 167 break; 168 default: // fall out 169 } 170 // TODO: call stopSelf() after async tasks complete (a bug) 171 stopSelf(); 172 return START_NOT_STICKY; 173 } 174 175 @Override onDestroy()176 public void onDestroy() { 177 super.onDestroy(); 178 LogUtil.d(TAG, "onDestroy"); 179 } 180 logCallImpression(Intent intent, DialerImpression.Type impression)181 private void logCallImpression(Intent intent, DialerImpression.Type impression) { 182 Logger.get(this) 183 .logCallImpression( 184 impression, 185 intent.getStringExtra(EXTRA_CALL_ID), 186 intent.getLongExtra(EXTRA_CALL_START_TIME_MILLIS, 0)); 187 } 188 createPromoActivityPendingIntent()189 private PendingIntent createPromoActivityPendingIntent() { 190 Intent intent = 191 SpamNotificationActivity.createActivityIntent( 192 this, 193 null, 194 SpamNotificationActivity.ACTION_SHOW_SPAM_BLOCKING_PROMO_DIALOG, 195 notificationTag, 196 notificationId); 197 return PendingIntent.getActivity( 198 this, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_ONE_SHOT); 199 } 200 createEnableSpamBlockingPendingIntent()201 private PendingIntent createEnableSpamBlockingPendingIntent() { 202 Intent intent = 203 SpamNotificationService.createServiceIntent( 204 this, 205 null, 206 SpamNotificationActivity.ACTION_ENABLE_SPAM_BLOCKING, 207 notificationTag, 208 notificationId); 209 return PendingIntent.getService( 210 this, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_ONE_SHOT); 211 } 212 } 213