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