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.NotificationManager; 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.spam.Spam; 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_ID = "service_notification_id"; 51 private static final String EXTRA_CONTACT_LOOKUP_RESULT_TYPE = 52 "service_contact_lookup_result_type"; 53 /** Creates an intent to start this service. */ 54 public static Intent createServiceIntent( 55 Context context, DialerCall call, String action, int notificationId) { 56 Intent intent = new Intent(context, SpamNotificationService.class); 57 intent.setAction(action); 58 intent.putExtra(EXTRA_PHONE_NUMBER, call.getNumber()); 59 intent.putExtra(EXTRA_CALL_ID, call.getUniqueCallId()); 60 intent.putExtra(EXTRA_CALL_START_TIME_MILLIS, call.getTimeAddedMs()); 61 intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId); 62 intent.putExtra(EXTRA_CONTACT_LOOKUP_RESULT_TYPE, call.getLogState().contactLookupResult); 63 return intent; 64 } 65 66 @Nullable 67 @Override 68 public IBinder onBind(Intent intent) { 69 // Return null because clients cannot bind to this service 70 return null; 71 } 72 73 @Override 74 public int onStartCommand(Intent intent, int flags, int startId) { 75 LogUtil.d(TAG, "onStartCommand"); 76 if (intent == null) { 77 LogUtil.d(TAG, "Null intent"); 78 stopSelf(); 79 // Return {@link #START_NOT_STICKY} so service is not restarted. 80 return START_NOT_STICKY; 81 } 82 String number = intent.getStringExtra(EXTRA_PHONE_NUMBER); 83 int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, 1); 84 String countryIso = GeoUtil.getCurrentCountryIso(this); 85 ContactLookupResult.Type contactLookupResultType = 86 ContactLookupResult.Type.forNumber(intent.getIntExtra(EXTRA_CONTACT_LOOKUP_RESULT_TYPE, 0)); 87 88 ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)) 89 .cancel(number, notificationId); 90 91 switch (intent.getAction()) { 92 case SpamNotificationActivity.ACTION_MARK_NUMBER_AS_SPAM: 93 logCallImpression( 94 intent, DialerImpression.Type.SPAM_NOTIFICATION_SERVICE_ACTION_MARK_NUMBER_AS_SPAM); 95 Spam.get(this) 96 .reportSpamFromAfterCallNotification( 97 number, 98 countryIso, 99 CallLog.Calls.INCOMING_TYPE, 100 ReportingLocation.Type.FEEDBACK_PROMPT, 101 contactLookupResultType); 102 new FilteredNumberAsyncQueryHandler(this).blockNumber(null, number, countryIso); 103 break; 104 case SpamNotificationActivity.ACTION_MARK_NUMBER_AS_NOT_SPAM: 105 logCallImpression( 106 intent, DialerImpression.Type.SPAM_NOTIFICATION_SERVICE_ACTION_MARK_NUMBER_AS_NOT_SPAM); 107 Spam.get(this) 108 .reportNotSpamFromAfterCallNotification( 109 number, 110 countryIso, 111 CallLog.Calls.INCOMING_TYPE, 112 ReportingLocation.Type.FEEDBACK_PROMPT, 113 contactLookupResultType); 114 break; 115 default: // fall out 116 } 117 // TODO: call stopSelf() after async tasks complete (b/28441936) 118 stopSelf(); 119 return START_NOT_STICKY; 120 } 121 122 @Override 123 public void onDestroy() { 124 super.onDestroy(); 125 LogUtil.d(TAG, "onDestroy"); 126 } 127 128 private void logCallImpression(Intent intent, DialerImpression.Type impression) { 129 Logger.get(this) 130 .logCallImpression( 131 impression, 132 intent.getStringExtra(EXTRA_CALL_ID), 133 intent.getLongExtra(EXTRA_CALL_START_TIME_MILLIS, 0)); 134 } 135 } 136