1 /* 2 * Copyright (C) 2018 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.phonelookup.emergency; 18 19 import android.content.Context; 20 import com.android.dialer.DialerPhoneNumber; 21 import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; 22 import com.android.dialer.inject.ApplicationContext; 23 import com.android.dialer.phonelookup.PhoneLookup; 24 import com.android.dialer.phonelookup.PhoneLookupInfo; 25 import com.android.dialer.phonelookup.PhoneLookupInfo.EmergencyInfo; 26 import com.android.dialer.phonenumberutil.PhoneNumberHelper; 27 import com.google.common.collect.ImmutableMap; 28 import com.google.common.collect.ImmutableSet; 29 import com.google.common.util.concurrent.Futures; 30 import com.google.common.util.concurrent.ListenableFuture; 31 import com.google.common.util.concurrent.ListeningExecutorService; 32 import javax.inject.Inject; 33 34 /** 35 * PhoneLookup implementation for checking if a number is an emergency number. 36 * 37 * <p>The check has to be done in a PhoneLookup as it involves detecting the user's location and 38 * obtaining SIM info, which are expensive operations. Doing it in the main thread will make the UI 39 * super janky. 40 */ 41 public class EmergencyPhoneLookup implements PhoneLookup<EmergencyInfo> { 42 43 private final Context appContext; 44 private final ListeningExecutorService backgroundExecutorService; 45 46 @Inject EmergencyPhoneLookup( @pplicationContext Context appContext, @BackgroundExecutor ListeningExecutorService backgroundExecutorService)47 EmergencyPhoneLookup( 48 @ApplicationContext Context appContext, 49 @BackgroundExecutor ListeningExecutorService backgroundExecutorService) { 50 this.appContext = appContext; 51 this.backgroundExecutorService = backgroundExecutorService; 52 } 53 54 @Override lookup(DialerPhoneNumber dialerPhoneNumber)55 public ListenableFuture<EmergencyInfo> lookup(DialerPhoneNumber dialerPhoneNumber) { 56 return backgroundExecutorService.submit( 57 () -> 58 EmergencyInfo.newBuilder() 59 .setIsEmergencyNumber( 60 PhoneNumberHelper.isLocalEmergencyNumber( 61 appContext, dialerPhoneNumber.getNormalizedNumber())) 62 .build()); 63 } 64 65 @Override isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers)66 public ListenableFuture<Boolean> isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers) { 67 return Futures.immediateFuture(false); 68 } 69 70 @Override getMostRecentInfo( ImmutableMap<DialerPhoneNumber, EmergencyInfo> existingInfoMap)71 public ListenableFuture<ImmutableMap<DialerPhoneNumber, EmergencyInfo>> getMostRecentInfo( 72 ImmutableMap<DialerPhoneNumber, EmergencyInfo> existingInfoMap) { 73 // We can update EmergencyInfo for all numbers in the provided map, but the negative impact on 74 // performance is intolerable as checking a single number involves detecting the user's location 75 // and obtaining SIM info, which will take more than 100ms (see 76 // android.telephony.PhoneNumberUtils#isLocalEmergencyNumber(Context, int, String) for details). 77 // 78 // As emergency numbers won't change in a country, the only case we will miss is that 79 // (1) a number is an emergency number in country A but not in country B, 80 // (2) a user has an emergency call entry when they are in country A, and 81 // (3) they travel from A to B, 82 // which is a rare event. 83 // 84 // We can update the implementation if telecom supports batch check in the future. 85 return Futures.immediateFuture(existingInfoMap); 86 } 87 88 @Override setSubMessage(PhoneLookupInfo.Builder destination, EmergencyInfo subMessage)89 public void setSubMessage(PhoneLookupInfo.Builder destination, EmergencyInfo subMessage) { 90 destination.setEmergencyInfo(subMessage); 91 } 92 93 @Override getSubMessage(PhoneLookupInfo phoneLookupInfo)94 public EmergencyInfo getSubMessage(PhoneLookupInfo phoneLookupInfo) { 95 return phoneLookupInfo.getEmergencyInfo(); 96 } 97 98 @Override onSuccessfulBulkUpdate()99 public ListenableFuture<Void> onSuccessfulBulkUpdate() { 100 return Futures.immediateFuture(null); 101 } 102 103 @Override registerContentObservers()104 public void registerContentObservers() { 105 // No content observer to register. 106 } 107 108 @Override unregisterContentObservers()109 public void unregisterContentObservers() { 110 // Nothing to be done as no content observer is registered. 111 } 112 113 @Override clearData()114 public ListenableFuture<Void> clearData() { 115 return Futures.immediateFuture(null); 116 } 117 118 @Override getLoggingName()119 public String getLoggingName() { 120 return "EmergencyPhoneLookup"; 121 } 122 } 123