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.Service; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.os.IBinder; 23 import android.provider.CallLog; 24 import android.support.annotation.Nullable; 25 import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler; 26 import com.android.dialer.common.LogUtil; 27 import com.android.dialer.location.GeoUtil; 28 import com.android.dialer.logging.ContactLookupResult; 29 import com.android.dialer.logging.DialerImpression; 30 import com.android.dialer.logging.Logger; 31 import com.android.dialer.logging.ReportingLocation; 32 import com.android.dialer.notification.DialerNotificationManager; 33 import com.android.dialer.spam.SpamComponent; 34 import com.android.incallui.call.DialerCall; 35 36 /** 37 * This service determines if the device is locked/unlocked and takes an action based on the state. 38 * A service is used to to determine this, as opposed to an activity, because the user must unlock 39 * the device before a notification can start an activity. This is not the case for a service, and 40 * intents can be sent to this service even from the lock screen. This allows users to quickly 41 * report a number as spam or not spam from their lock screen. 42 */ 43 public class SpamNotificationService extends Service { 44 45 private static final String TAG = "SpamNotificationSvc"; 46 47 private static final String EXTRA_PHONE_NUMBER = "service_phone_number"; 48 private static final String EXTRA_CALL_ID = "service_call_id"; 49 private static final String EXTRA_CALL_START_TIME_MILLIS = "service_call_start_time_millis"; 50 private static final String EXTRA_NOTIFICATION_TAG = "service_notification_tag"; 51 private static final String EXTRA_NOTIFICATION_ID = "service_notification_id"; 52 private static final String EXTRA_CONTACT_LOOKUP_RESULT_TYPE = 53 "service_contact_lookup_result_type"; 54 /** Creates an intent to start this service. */ createServiceIntent( Context context, DialerCall call, String action, String notificationTag, int notificationId)55 public static Intent createServiceIntent( 56 Context context, DialerCall call, String action, String notificationTag, int notificationId) { 57 Intent intent = new Intent(context, SpamNotificationService.class); 58 intent.setAction(action); 59 intent.putExtra(EXTRA_PHONE_NUMBER, call.getNumber()); 60 intent.putExtra(EXTRA_CALL_ID, call.getUniqueCallId()); 61 intent.putExtra(EXTRA_CALL_START_TIME_MILLIS, call.getTimeAddedMs()); 62 intent.putExtra(EXTRA_NOTIFICATION_TAG, notificationTag); 63 intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId); 64 intent.putExtra( 65 EXTRA_CONTACT_LOOKUP_RESULT_TYPE, call.getLogState().contactLookupResult.getNumber()); 66 return intent; 67 } 68 69 @Nullable 70 @Override onBind(Intent intent)71 public IBinder onBind(Intent intent) { 72 // Return null because clients cannot bind to this service 73 return null; 74 } 75 76 @Override onStartCommand(Intent intent, int flags, int startId)77 public int onStartCommand(Intent intent, int flags, int startId) { 78 LogUtil.d(TAG, "onStartCommand"); 79 if (intent == null) { 80 LogUtil.d(TAG, "Null intent"); 81 stopSelf(); 82 // Return {@link #START_NOT_STICKY} so service is not restarted. 83 return START_NOT_STICKY; 84 } 85 String number = intent.getStringExtra(EXTRA_PHONE_NUMBER); 86 String notificationTag = intent.getStringExtra(EXTRA_NOTIFICATION_TAG); 87 int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, 1); 88 String countryIso = GeoUtil.getCurrentCountryIso(this); 89 ContactLookupResult.Type contactLookupResultType = 90 ContactLookupResult.Type.forNumber(intent.getIntExtra(EXTRA_CONTACT_LOOKUP_RESULT_TYPE, 0)); 91 92 DialerNotificationManager.cancel(this, notificationTag, notificationId); 93 94 switch (intent.getAction()) { 95 case SpamNotificationActivity.ACTION_MARK_NUMBER_AS_SPAM: 96 logCallImpression( 97 intent, DialerImpression.Type.SPAM_NOTIFICATION_SERVICE_ACTION_MARK_NUMBER_AS_SPAM); 98 SpamComponent.get(this) 99 .spam() 100 .reportSpamFromAfterCallNotification( 101 number, 102 countryIso, 103 CallLog.Calls.INCOMING_TYPE, 104 ReportingLocation.Type.FEEDBACK_PROMPT, 105 contactLookupResultType); 106 new FilteredNumberAsyncQueryHandler(this).blockNumber(null, number, countryIso); 107 break; 108 case SpamNotificationActivity.ACTION_MARK_NUMBER_AS_NOT_SPAM: 109 logCallImpression( 110 intent, DialerImpression.Type.SPAM_NOTIFICATION_SERVICE_ACTION_MARK_NUMBER_AS_NOT_SPAM); 111 SpamComponent.get(this) 112 .spam() 113 .reportNotSpamFromAfterCallNotification( 114 number, 115 countryIso, 116 CallLog.Calls.INCOMING_TYPE, 117 ReportingLocation.Type.FEEDBACK_PROMPT, 118 contactLookupResultType); 119 break; 120 default: // fall out 121 } 122 // TODO: call stopSelf() after async tasks complete (a bug) 123 stopSelf(); 124 return START_NOT_STICKY; 125 } 126 127 @Override onDestroy()128 public void onDestroy() { 129 super.onDestroy(); 130 LogUtil.d(TAG, "onDestroy"); 131 } 132 logCallImpression(Intent intent, DialerImpression.Type impression)133 private void logCallImpression(Intent intent, DialerImpression.Type impression) { 134 Logger.get(this) 135 .logCallImpression( 136 impression, 137 intent.getStringExtra(EXTRA_CALL_ID), 138 intent.getLongExtra(EXTRA_CALL_START_TIME_MILLIS, 0)); 139 } 140 } 141