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.cts.numberblocking.hostside;
18 
19 import android.content.ComponentName;
20 import android.net.Uri;
21 import android.os.Bundle;
22 import android.os.Process;
23 import android.provider.CallLog;
24 import android.telecom.Connection;
25 import android.telecom.ConnectionRequest;
26 import android.telecom.ConnectionService;
27 import android.telecom.DisconnectCause;
28 import android.telecom.PhoneAccount;
29 import android.telecom.PhoneAccountHandle;
30 import android.telecom.TelecomManager;
31 
32 import java.util.Arrays;
33 import java.util.concurrent.CountDownLatch;
34 import java.util.concurrent.TimeUnit;
35 
36 /**
37  * Tests call blocking in a multi-user environment.
38  */
39 public class CallBlockingTest extends BaseNumberBlockingClientTest {
40     private static final String QUERY_CALL_THROUGH_OUR_CONNECTION_SERVICE = CallLog.Calls.NUMBER
41             + " = ? AND " + CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME + " = ?";
42     public static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_MS = 10000;
43 
44     private static CountDownLatch callRejectionCountDownLatch;
45 
46     @Override
setUp()47     protected void setUp() throws Exception {
48         super.setUp();
49         callRejectionCountDownLatch = new CountDownLatch(1);
50     }
51 
testRegisterPhoneAccount()52     public void testRegisterPhoneAccount() {
53         PhoneAccountHandle phoneAccountHandle = getPhoneAccountHandle();
54         final PhoneAccount phoneAccount = PhoneAccount.builder(
55                 phoneAccountHandle, phoneAccountHandle.getId())
56                 .setAddress(Uri.parse("tel:333-TEST"))
57                 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
58                 .setShortDescription("a short description for the call provider")
59                 .setSupportedUriSchemes(Arrays.asList(PhoneAccount.SCHEME_TEL))
60                 .build();
61         mTelecomManager.registerPhoneAccount(phoneAccount);
62         assertPhoneAccountRegistered(phoneAccountHandle, true);
63     }
64 
testUnregisterPhoneAccount()65     public void testUnregisterPhoneAccount() {
66         PhoneAccountHandle handle = getPhoneAccountHandle();
67         mTelecomManager.unregisterPhoneAccount(handle);
68         assertPhoneAccountRegistered(handle, false);
69     }
70 
testIncomingCallFromBlockedNumberIsRejected()71     public void testIncomingCallFromBlockedNumberIsRejected() throws Exception {
72         // Make sure no lingering values from previous runs.
73         cleanupCall(false /* verifyNoCallLogsWritten */);
74 
75         // Register the phone account.
76         // final PhoneAccountHandle phoneAccountHandle = registerPhoneAccount().getAccountHandle();
77         PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(getPhoneAccountHandle());
78         assertNotNull(phoneAccount);
79         try {
80             // Add a incoming call.
81             final Bundle bundle = new Bundle();
82             final Uri phoneUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, mBlockedPhoneNumber, null);
83             bundle.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, phoneUri);
84             mTelecomManager.addNewIncomingCall(phoneAccount.getAccountHandle(), bundle);
85 
86             // Make sure the call is rejected.
87             assertTrue(callRejectionCountDownLatch.await(10, TimeUnit.SECONDS));
88         } finally {
89             cleanupCall(true /* verifyNoCallLogsWritten */ );
90             unregisterPhoneAccount();
91         }
92     }
93 
cleanupCall(boolean verifyNoCallLogsWritten)94     private void cleanupCall(boolean verifyNoCallLogsWritten) {
95         final String connectionServiceComponentName = new ComponentName(mContext,
96                 DummyConnectionService.class).flattenToString();
97         int numRowDeleted = mContext.getContentResolver()
98                 .delete(CallLog.Calls.CONTENT_URI, QUERY_CALL_THROUGH_OUR_CONNECTION_SERVICE,
99                         new String[]{mBlockedPhoneNumber, connectionServiceComponentName});
100         if (verifyNoCallLogsWritten) {
101             assertEquals(0, numRowDeleted);
102         }
103     }
104 
unregisterPhoneAccount()105     private void unregisterPhoneAccount() {
106         mTelecomManager.unregisterPhoneAccount(getPhoneAccountHandle());
107         assertNull(mTelecomManager.getPhoneAccount(getPhoneAccountHandle()));
108     }
109 
getPhoneAccountHandle()110     private PhoneAccountHandle getPhoneAccountHandle() {
111         return new PhoneAccountHandle(
112                 new ComponentName(
113                         CallBlockingTest.class.getPackage().getName(),
114                         DummyConnectionService.class.getName()),
115                 mPhoneAccountId,
116                 Process.myUserHandle());
117     }
118 
119     public static class DummyConnection extends Connection {
120         @Override
onReject()121         public void onReject() {
122             super.onReject();
123             setDisconnected(new DisconnectCause(DisconnectCause.REJECTED));
124             destroy();
125             callRejectionCountDownLatch.countDown();
126         }
127     }
128 
129     public static class DummyConnectionService extends ConnectionService {
130         @Override
onCreateIncomingConnection( PhoneAccountHandle connectionManagerAccount, ConnectionRequest request)131         public Connection onCreateIncomingConnection(
132                 PhoneAccountHandle connectionManagerAccount, ConnectionRequest request) {
133             final Connection connection = new DummyConnection();
134             connection.setVideoState(request.getVideoState());
135             final Uri address =
136                     request.getExtras().getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
137             connection.setAddress(address, TelecomManager.PRESENTATION_ALLOWED);
138             return connection;
139         }
140     }
141 
assertPhoneAccountRegistered(final PhoneAccountHandle handle, boolean isRegistered)142     private void assertPhoneAccountRegistered(final PhoneAccountHandle handle,
143             boolean isRegistered) {
144         waitUntilConditionIsTrueOrTimeout(
145                 new Condition() {
146                     @Override
147                     public Object expected() {
148                         return true;
149                     }
150 
151                     @Override
152                     public Object actual() {
153                         PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(handle);
154                         return isRegistered ? phoneAccount != null : phoneAccount == null;
155                     }
156                 },
157                 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
158                 "Phone account registered for " + handle
159         );
160     }
161 
waitUntilConditionIsTrueOrTimeout(Condition condition, long timeout, String description)162     private void waitUntilConditionIsTrueOrTimeout(Condition condition, long timeout,
163             String description) {
164         final long start = System.currentTimeMillis();
165         while (!condition.expected().equals(condition.actual())
166                 && System.currentTimeMillis() - start < timeout) {
167             sleep(50);
168         }
169         assertEquals(description, condition.expected(), condition.actual());
170     }
171 
sleep(long ms)172     private void sleep(long ms) {
173         try {
174             Thread.sleep(ms);
175         } catch (InterruptedException e) {
176         }
177     }
178 
179     protected interface Condition {
expected()180         Object expected();
actual()181         Object actual();
182     }
183 }
184