• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.blockednumber;
18 
19 import android.content.Context;
20 import android.database.Cursor;
21 import android.support.annotation.WorkerThread;
22 import android.util.ArraySet;
23 import com.android.dialer.DialerPhoneNumber;
24 import com.android.dialer.blocking.FilteredNumberCompat;
25 import com.android.dialer.calllog.observer.MarkDirtyObserver;
26 import com.android.dialer.common.Assert;
27 import com.android.dialer.common.LogUtil;
28 import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
29 import com.android.dialer.common.database.Selection;
30 import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
31 import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
32 import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes;
33 import com.android.dialer.inject.ApplicationContext;
34 import com.android.dialer.phonelookup.PhoneLookup;
35 import com.android.dialer.phonelookup.PhoneLookupInfo;
36 import com.android.dialer.phonelookup.PhoneLookupInfo.BlockedState;
37 import com.android.dialer.phonelookup.PhoneLookupInfo.DialerBlockedNumberInfo;
38 import com.android.dialer.phonenumberproto.PartitionedNumbers;
39 import com.google.common.collect.ImmutableMap;
40 import com.google.common.collect.ImmutableSet;
41 import com.google.common.util.concurrent.Futures;
42 import com.google.common.util.concurrent.ListenableFuture;
43 import com.google.common.util.concurrent.ListeningExecutorService;
44 import java.util.Set;
45 import javax.inject.Inject;
46 
47 /**
48  * Lookup blocked numbers in the dialer internal database. This is used when the system database is
49  * not yet available.
50  */
51 public final class DialerBlockedNumberPhoneLookup implements PhoneLookup<DialerBlockedNumberInfo> {
52 
53   private final Context appContext;
54   private final ListeningExecutorService executorService;
55   private final MarkDirtyObserver markDirtyObserver;
56 
57   @Inject
DialerBlockedNumberPhoneLookup( @pplicationContext Context appContext, @BackgroundExecutor ListeningExecutorService executorService, MarkDirtyObserver markDirtyObserver)58   DialerBlockedNumberPhoneLookup(
59       @ApplicationContext Context appContext,
60       @BackgroundExecutor ListeningExecutorService executorService,
61       MarkDirtyObserver markDirtyObserver) {
62     this.appContext = appContext;
63     this.executorService = executorService;
64     this.markDirtyObserver = markDirtyObserver;
65   }
66 
67   @Override
lookup(DialerPhoneNumber dialerPhoneNumber)68   public ListenableFuture<DialerBlockedNumberInfo> lookup(DialerPhoneNumber dialerPhoneNumber) {
69     if (FilteredNumberCompat.useNewFiltering(appContext)) {
70       return Futures.immediateFuture(DialerBlockedNumberInfo.getDefaultInstance());
71     }
72     return executorService.submit(
73         () -> queryNumbers(ImmutableSet.of(dialerPhoneNumber)).get(dialerPhoneNumber));
74   }
75 
76   @Override
isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers)77   public ListenableFuture<Boolean> isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers) {
78     // Dirty state is recorded with PhoneLookupDataSource.markDirtyAndNotify(), which will force
79     // rebuild with the CallLogFramework
80     return Futures.immediateFuture(false);
81   }
82 
83   @Override
84   public ListenableFuture<ImmutableMap<DialerPhoneNumber, DialerBlockedNumberInfo>>
getMostRecentInfo(ImmutableMap<DialerPhoneNumber, DialerBlockedNumberInfo> existingInfoMap)85       getMostRecentInfo(ImmutableMap<DialerPhoneNumber, DialerBlockedNumberInfo> existingInfoMap) {
86     if (FilteredNumberCompat.useNewFiltering(appContext)) {
87       return Futures.immediateFuture(existingInfoMap);
88     }
89     LogUtil.enterBlock("DialerBlockedNumberPhoneLookup.getMostRecentPhoneLookupInfo");
90     return executorService.submit(() -> queryNumbers(existingInfoMap.keySet()));
91   }
92 
93   @Override
setSubMessage( PhoneLookupInfo.Builder phoneLookupInfo, DialerBlockedNumberInfo subMessage)94   public void setSubMessage(
95       PhoneLookupInfo.Builder phoneLookupInfo, DialerBlockedNumberInfo subMessage) {
96     phoneLookupInfo.setDialerBlockedNumberInfo(subMessage);
97   }
98 
99   @Override
getSubMessage(PhoneLookupInfo phoneLookupInfo)100   public DialerBlockedNumberInfo getSubMessage(PhoneLookupInfo phoneLookupInfo) {
101     return phoneLookupInfo.getDialerBlockedNumberInfo();
102   }
103 
104   @Override
onSuccessfulBulkUpdate()105   public ListenableFuture<Void> onSuccessfulBulkUpdate() {
106     return Futures.immediateFuture(null);
107   }
108 
109   @WorkerThread
queryNumbers( ImmutableSet<DialerPhoneNumber> numbers)110   private ImmutableMap<DialerPhoneNumber, DialerBlockedNumberInfo> queryNumbers(
111       ImmutableSet<DialerPhoneNumber> numbers) {
112     Assert.isWorkerThread();
113     PartitionedNumbers partitionedNumbers = new PartitionedNumbers(numbers);
114 
115     Set<DialerPhoneNumber> blockedNumbers = new ArraySet<>();
116 
117     Selection normalizedSelection =
118         Selection.column(FilteredNumberColumns.NORMALIZED_NUMBER)
119             .in(partitionedNumbers.validE164Numbers());
120     try (Cursor cursor =
121         appContext
122             .getContentResolver()
123             .query(
124                 FilteredNumber.CONTENT_URI,
125                 new String[] {FilteredNumberColumns.NORMALIZED_NUMBER, FilteredNumberColumns.TYPE},
126                 normalizedSelection.getSelection(),
127                 normalizedSelection.getSelectionArgs(),
128                 null)) {
129       while (cursor != null && cursor.moveToNext()) {
130         if (cursor.getInt(1) == FilteredNumberTypes.BLOCKED_NUMBER) {
131           blockedNumbers.addAll(
132               partitionedNumbers.dialerPhoneNumbersForValidE164(cursor.getString(0)));
133         }
134       }
135     }
136 
137     Selection rawSelection =
138         Selection.column(FilteredNumberColumns.NUMBER).in(partitionedNumbers.invalidNumbers());
139     try (Cursor cursor =
140         appContext
141             .getContentResolver()
142             .query(
143                 FilteredNumber.CONTENT_URI,
144                 new String[] {FilteredNumberColumns.NUMBER, FilteredNumberColumns.TYPE},
145                 rawSelection.getSelection(),
146                 rawSelection.getSelectionArgs(),
147                 null)) {
148       while (cursor != null && cursor.moveToNext()) {
149         if (cursor.getInt(1) == FilteredNumberTypes.BLOCKED_NUMBER) {
150           blockedNumbers.addAll(
151               partitionedNumbers.dialerPhoneNumbersForInvalid(cursor.getString(0)));
152         }
153       }
154     }
155 
156     ImmutableMap.Builder<DialerPhoneNumber, DialerBlockedNumberInfo> result =
157         ImmutableMap.builder();
158 
159     for (DialerPhoneNumber number : numbers) {
160       result.put(
161           number,
162           DialerBlockedNumberInfo.newBuilder()
163               .setBlockedState(
164                   blockedNumbers.contains(number) ? BlockedState.BLOCKED : BlockedState.NOT_BLOCKED)
165               .build());
166     }
167 
168     return result.build();
169   }
170 
171   @Override
registerContentObservers(Context appContext)172   public void registerContentObservers(Context appContext) {
173     appContext
174         .getContentResolver()
175         .registerContentObserver(
176             FilteredNumber.CONTENT_URI,
177             true, // FilteredNumberProvider notifies on the item
178             markDirtyObserver);
179   }
180 }
181