Add support for reporting nuisance calls to CallScreeningService.
Add new API to report nuisance calls from the default dialer.
This API will relay the nuisance report on to the current
call screening service if it has previously provided call id
information for calls.
Test: Manual using test app
Test: Added CTS coverage
Test: Added unit tests
Bug: 63966743
Merged-In: I43c05ec9fa20f2df31da07c19090335c4d0154ca
Change-Id: I43c05ec9fa20f2df31da07c19090335c4d0154ca
diff --git a/src/com/android/server/telecom/NuisanceCallReporter.java b/src/com/android/server/telecom/NuisanceCallReporter.java
new file mode 100644
index 0000000..064527b
--- /dev/null
+++ b/src/com/android/server/telecom/NuisanceCallReporter.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Process;
+import android.os.UserHandle;
+import android.provider.CallLog;
+import android.telecom.CallScreeningService;
+import android.telecom.Log;
+import android.telecom.PhoneAccountHandle;
+
+import java.util.Arrays;
+
+/**
+ * Responsible for handling reports via
+ * {@link android.telecom.TelecomManager#reportNuisanceCallStatus(Uri, boolean)} as to whether the
+ * user has indicated a call is a nuisance call.
+ *
+ * Since nuisance reports can be initiated from the call log, potentially long after a call has
+ * completed, calls are identified by the {@link Call#getHandle()}. A nuisance report for a call is
+ * only accepted if:
+ * <ul>
+ * <li>A missed, incoming, or rejected call to that number exists in the call log. We want to
+ * avoid a scenario where a user reports a single outgoing call as a nuisance call.</li>
+ * <li>The call occurred via a sim-based phone account; we do not want to report nuisance calls
+ * which only ever took place via a self-managed ConnectionService. It is, however, valid for
+ * a number to be contacted both via a sim-based phone account and a self-managed one.</li>
+ * <li>The {@link CallScreeningService} has provided call identification for calls in the past.
+ * This provides an incentive for {@link CallScreeningService} implementations to use the caller
+ * ID APIs appropriately if they are going to benefit from use reports of nuisance and
+ * non-nuisance calls.</li>
+ * </ul>
+ */
+public class NuisanceCallReporter {
+ /**
+ * Columns we want to retrieve from the call log.
+ */
+ private static final String[] CALL_LOG_PROJECTION = new String[] {
+ CallLog.Calls._ID,
+ CallLog.Calls.DURATION,
+ CallLog.Calls.TYPE,
+ CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME,
+ CallLog.Calls.PHONE_ACCOUNT_ID
+ };
+
+ public static final int CALL_LOG_COLUMN_ID =
+ Arrays.asList(CALL_LOG_PROJECTION).indexOf(CallLog.Calls._ID);
+ public static final int CALL_LOG_COLUMN_DURATION =
+ Arrays.asList(CALL_LOG_PROJECTION).indexOf(CallLog.Calls.DURATION);
+ public static final int CALL_LOG_COLUMN_TYPE =
+ Arrays.asList(CALL_LOG_PROJECTION).indexOf(CallLog.Calls.TYPE);
+ public static final int CALL_LOG_COLUMN_PHONE_ACCOUNT_COMPONENT_NAME =
+ Arrays.asList(CALL_LOG_PROJECTION).indexOf(CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME);
+ public static final int CALL_LOG_COLUMN_PHONE_ACCOUNT_ID =
+ Arrays.asList(CALL_LOG_PROJECTION).indexOf(CallLog.Calls.PHONE_ACCOUNT_ID);
+
+ /**
+ * Represents information about a nuisance report via
+ * {@link android.telecom.TelecomManager#reportNuisanceCallStatus(Uri, boolean)}.
+ */
+ private static class NuisanceReport {
+ public String callScreeningPackageName;
+ public Uri handle;
+ public boolean isNuisance;
+ public NuisanceReport(String packageName, Uri handle, boolean isNuisance) {
+ this.callScreeningPackageName = packageName;
+ this.handle = handle;
+ this.isNuisance = isNuisance;
+ }
+ }
+
+ /**
+ * Proxy interface to abstract calls to
+ * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}.
+ * Facilitates testing.
+ */
+ public interface PhoneNumberUtilsProxy {
+ String formatNumberToE164(String number);
+ }
+
+ /**
+ * Proxy interface to abstract queries to the package manager to determine if a
+ * {@link PhoneAccountHandle} is for a self-managed connection service.
+ */
+ public interface PhoneAccountRegistrarProxy {
+ boolean isSelfManagedConnectionService(PhoneAccountHandle handle);
+ }
+
+ /**
+ * Restrict to call log entries for the specified number where its an incoming, missed, blocked
+ * or rejected call.
+ */
+ private static final String NUMBER_WHERE_CLAUSE =
+ CallLog.Calls.CACHED_NORMALIZED_NUMBER + " = ? AND " + CallLog.Calls.TYPE
+ + " IN (" + CallLog.Calls.INCOMING_TYPE + "," + CallLog.Calls.MISSED_TYPE + ","
+ + CallLog.Calls.BLOCKED_TYPE + "," + CallLog.Calls.REJECTED_TYPE + ")";
+
+ /**
+ * Call log where clause to find entries with call identification reported by a specified call
+ * screening service.
+ */
+ private static final String CALL_ID_PACKAGE_WHERE_CLAUSE =
+ CallLog.Calls.CALL_ID_PACKAGE_NAME + " = ? ";
+
+ private final Context mContext;
+ private final PhoneNumberUtilsProxy mPhoneNumberUtilsProxy;
+ private final PhoneAccountRegistrarProxy mPhoneAccountRegistrarProxy;
+ private UserHandle mCurrentUserHandle;
+
+ public NuisanceCallReporter(Context context, PhoneNumberUtilsProxy phoneNumberUtilsProxy,
+ PhoneAccountRegistrarProxy phoneAccountRegistrarProxy) {
+ mContext = context;
+ mPhoneNumberUtilsProxy = phoneNumberUtilsProxy;
+ mPhoneAccountRegistrarProxy = phoneAccountRegistrarProxy;
+ }
+
+ public void setCurrentUserHandle(UserHandle userHandle) {
+ if (userHandle == null) {
+ Log.d(this, "setCurrentUserHandle, userHandle = null");
+ userHandle = Process.myUserHandle();
+ }
+ Log.d(this, "setCurrentUserHandle, %s", userHandle);
+ mCurrentUserHandle = userHandle;
+ }
+
+ /**
+ * Given a call handle reported by the default dialer, inform the
+ * {@link android.telecom.CallScreeningService} of whether the user has indicated a call is
+ * or is not a nuisance call.
+ *
+ * @param callScreeningPackageName the package name of the call screening service.
+ * @param handle the handle of the call to report nuisance status on.
+ * @param isNuisance {@code true} if the call is a nuisance call, {@code false} otherwise.
+ */
+ public void reportNuisanceCallStatus(@NonNull String callScreeningPackageName,
+ @NonNull Uri handle, boolean isNuisance) {
+
+ // Don't report the nuisance status to a call screening app if it has not provided any
+ // caller id info in the past.
+ if (!hasCallScreeningServiceProvidedCallId(callScreeningPackageName)) {
+ Log.i(this, "reportNuisanceCallStatus: app %s has not provided caller ID; skipping.",
+ callScreeningPackageName);
+ return;
+ }
+
+ maybeSendNuisanceReport(new NuisanceReport(callScreeningPackageName, handle, isNuisance));
+ }
+
+ private void maybeSendNuisanceReport(@NonNull NuisanceReport nuisanceReport) {
+ Uri callsUri = CallLog.Calls.CONTENT_URI;
+ if (mCurrentUserHandle == null) {
+ return;
+ }
+
+ ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI,
+ mCurrentUserHandle.getIdentifier());
+
+ String normalizedNumber = mPhoneNumberUtilsProxy.formatNumberToE164(
+ nuisanceReport.handle.getSchemeSpecificPart());
+
+ // Query the call log for the most recent information about this call.
+ Cursor cursor = mContext.getContentResolver().query(callsUri, CALL_LOG_PROJECTION,
+ NUMBER_WHERE_CLAUSE, new String[] { normalizedNumber },
+ CallLog.Calls.DEFAULT_SORT_ORDER);
+ Log.d(this, "maybeSendNuisanceReport: number=%s, isNuisance=%b",
+ Log.piiHandle(normalizedNumber), nuisanceReport.isNuisance);
+ if (cursor != null) {
+ try {
+ while (cursor.moveToNext()) {
+ final long duration = cursor.getLong(CALL_LOG_COLUMN_DURATION);
+ final int callType = cursor.getInt(CALL_LOG_COLUMN_TYPE);
+ final String phoneAccountComponentName = cursor.getString(
+ CALL_LOG_COLUMN_PHONE_ACCOUNT_COMPONENT_NAME);
+ final String phoneAccountId = cursor.getString(
+ CALL_LOG_COLUMN_PHONE_ACCOUNT_ID);
+
+ PhoneAccountHandle handle = new PhoneAccountHandle(
+ ComponentName.unflattenFromString(phoneAccountComponentName),
+ phoneAccountId);
+
+ if (mPhoneAccountRegistrarProxy.isSelfManagedConnectionService(handle)) {
+ // Skip this call log entry; it was made via a self-managed CS.
+ Log.d(this, "maybeSendNuisanceReport: skip self-mgd CS %s",
+ phoneAccountComponentName);
+ continue;
+ }
+
+ sendNuisanceReportIntent(nuisanceReport, duration, callType);
+ // Stop when we send a nuisance report.
+ break;
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+
+ /**
+ * Determines if a {@link CallScreeningService} has provided
+ * {@link android.telecom.CallIdentification} for calls in the past.
+ * @param packageName The package name of the {@link CallScreeningService}.
+ * @return {@code true} if the app has provided call identification, {@code false} otherwise.
+ */
+ private boolean hasCallScreeningServiceProvidedCallId(@NonNull String packageName) {
+ // Query the call log for any entries which have call identification provided by the call
+ // screening package.
+ Cursor cursor = mContext.getContentResolver().query(CallLog.Calls.CONTENT_URI,
+ CALL_LOG_PROJECTION, CALL_ID_PACKAGE_WHERE_CLAUSE, new String[] { packageName },
+ CallLog.Calls.DEFAULT_SORT_ORDER + " LIMIT 1");
+
+ return cursor.getCount() > 0;
+ }
+
+ private void sendNuisanceReportIntent(@NonNull NuisanceReport nuisanceReport, long duration,
+ int callType) {
+ Log.i(this, "handleCallLogResult: number=%s, duration=%d, type=%d",
+ Log.piiHandle(nuisanceReport.handle), duration, callType);
+
+ Intent intent = new Intent(CallScreeningService.ACTION_NUISANCE_CALL_STATUS_CHANGED);
+ intent.setPackage(nuisanceReport.callScreeningPackageName);
+ intent.putExtra(CallScreeningService.EXTRA_CALL_HANDLE, nuisanceReport.handle);
+ intent.putExtra(CallScreeningService.EXTRA_IS_NUISANCE, nuisanceReport.isNuisance);
+ intent.putExtra(CallScreeningService.EXTRA_CALL_TYPE, callType);
+ intent.putExtra(CallScreeningService.EXTRA_CALL_DURATION, getCallDurationBucket(duration));
+ mContext.sendBroadcastAsUser(intent, mCurrentUserHandle);
+ }
+
+ /**
+ * Maps a call duration in milliseconds to a call duration bucket.
+ * @param callDuration Call duration, in milliseconds.
+ * @return The call duration bucket
+ */
+ public @CallScreeningService.CallDuration int getCallDurationBucket(long callDuration) {
+ if (callDuration < 3000L) {
+ return CallScreeningService.CALL_DURATION_VERY_SHORT;
+ } else if (callDuration >= 3000L && callDuration < 60000L) {
+ return CallScreeningService.CALL_DURATION_SHORT;
+ } else if (callDuration >= 6000L && callDuration < 120000L) {
+ return CallScreeningService.CALL_DURATION_MEDIUM;
+ } else {
+ return CallScreeningService.CALL_DURATION_LONG;
+ }
+ }
+}
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index 9010913..afe5609 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -17,6 +17,7 @@
package com.android.server.telecom;
import android.Manifest;
+import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -650,6 +651,21 @@
return getPhoneAccountHandles(0, null, packageName, false, userHandle);
}
+ /**
+ * Determines if a {@link PhoneAccountHandle} is for a self-managed {@link ConnectionService}.
+ * @param handle The handle.
+ * @return {@code true} if for a self-managed {@link ConnectionService}, {@code false}
+ * otherwise.
+ */
+ public boolean isSelfManagedPhoneAccount(@NonNull PhoneAccountHandle handle) {
+ PhoneAccount account = getPhoneAccountUnchecked(handle);
+ if (account == null) {
+ return false;
+ }
+
+ return account.isSelfManaged();
+ }
+
// TODO: Should we implement an artificial limit for # of accounts associated with a single
// ComponentName?
public void registerPhoneAccount(PhoneAccount account) {
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index f472421..291f9d0 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -1506,6 +1506,36 @@
}
/**
+ * See {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}
+ */
+ @Override
+ public void reportNuisanceCallStatus(Uri handle, boolean isNuisance,
+ String callingPackage) {
+ try {
+ Log.startSession("TSI.rNCS");
+ if (!isPrivilegedDialerCalling(callingPackage)) {
+ throw new SecurityException(
+ "Only the default dialer can report nuisance call status");
+ }
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ String callScreeningPackageName =
+ mCallsManager.getRoleManagerAdapter().getDefaultCallScreeningApp();
+
+ if (!TextUtils.isEmpty(callScreeningPackageName)) {
+ mNuisanceCallReporter.reportNuisanceCallStatus(callScreeningPackageName,
+ handle, isNuisance);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ /**
* See {@link TelecomManager#handleCallIntent(Intent)} ()}
*/
@Override
@@ -1678,6 +1708,7 @@
private AppOpsManager mAppOpsManager;
private PackageManager mPackageManager;
private CallsManager mCallsManager;
+ private final NuisanceCallReporter mNuisanceCallReporter;
private final PhoneAccountRegistrar mPhoneAccountRegistrar;
private final CallIntentProcessor.Adapter mCallIntentProcessorAdapter;
private final UserCallIntentProcessorFactory mUserCallIntentProcessorFactory;
@@ -1695,6 +1726,7 @@
DefaultDialerCache defaultDialerCache,
SubscriptionManagerAdapter subscriptionManagerAdapter,
SettingsSecureAdapter settingsSecureAdapter,
+ NuisanceCallReporter nuisanceCallReporter,
TelecomSystem.SyncRoot lock) {
mContext = context;
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -1709,6 +1741,7 @@
mCallIntentProcessorAdapter = callIntentProcessorAdapter;
mSubscriptionManagerAdapter = subscriptionManagerAdapter;
mSettingsSecureAdapter = settingsSecureAdapter;
+ mNuisanceCallReporter = nuisanceCallReporter;
}
public ITelecomService.Stub getBinder() {
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 2daba61..0e3234e 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -35,10 +35,15 @@
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.location.Country;
+import android.location.CountryDetector;
import android.net.Uri;
+import android.os.Process;
import android.os.UserHandle;
+import android.os.UserManager;
import android.telecom.Log;
import android.telecom.PhoneAccountHandle;
+import android.telephony.PhoneNumberUtils;
import java.io.FileNotFoundException;
import java.io.InputStream;
@@ -115,6 +120,7 @@
private final TelecomServiceImpl mTelecomServiceImpl;
private final ContactsAsyncHelper mContactsAsyncHelper;
private final DialerCodeReceiver mDialerCodeReceiver;
+ private final NuisanceCallReporter mNuisanceCallReporter;
private boolean mIsBootComplete = false;
@@ -128,6 +134,7 @@
UserHandle currentUserHandle = new UserHandle(userHandleId);
mPhoneAccountRegistrar.setCurrentUserHandle(currentUserHandle);
mCallsManager.onUserSwitch(currentUserHandle);
+ mNuisanceCallReporter.setCurrentUserHandle(currentUserHandle);
}
} finally {
Log.endSession();
@@ -326,6 +333,41 @@
mContext.registerReceiver(mDialerCodeReceiver, DIALER_SECRET_CODE_FILTER,
Manifest.permission.CONTROL_INCALL_EXPERIENCE, null);
+ mNuisanceCallReporter = new NuisanceCallReporter(mContext,
+ new NuisanceCallReporter.PhoneNumberUtilsProxy() {
+ @Override
+ public String formatNumberToE164(String number) {
+ final CountryDetector detector =
+ (CountryDetector) context.getSystemService(
+ Context.COUNTRY_DETECTOR);
+ if (detector != null) {
+ final Country country = detector.detectCountry();
+ if (country != null) {
+ String countryIso = country.getCountryIso();
+ return PhoneNumberUtils.formatNumberToE164(number,
+ countryIso);
+ }
+ }
+ return number;
+ }
+ },
+ new NuisanceCallReporter.PhoneAccountRegistrarProxy() {
+ @Override
+ public boolean isSelfManagedConnectionService(
+ PhoneAccountHandle handle) {
+ // Sync access to the PhoneAccountRegistrar on Telecom lock.
+ synchronized (mLock) {
+ return mPhoneAccountRegistrar.isSelfManagedPhoneAccount(handle);
+ }
+ }
+ });
+
+ // There is no USER_SWITCHED broadcast for user 0, handle it here explicitly.
+ final UserManager userManager = UserManager.get(mContext);
+ if (userManager.isPrimaryUser()) {
+ mNuisanceCallReporter.setCurrentUserHandle(Process.myUserHandle());
+ }
+
mTelecomServiceImpl = new TelecomServiceImpl(
mContext, mCallsManager, mPhoneAccountRegistrar,
new CallIntentProcessor.AdapterImpl(),
@@ -338,6 +380,7 @@
defaultDialerCache,
new TelecomServiceImpl.SubscriptionManagerAdapterImpl(),
new TelecomServiceImpl.SettingsSecureAdapterImpl(),
+ mNuisanceCallReporter,
mLock);
Log.endSession();
}
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index 9cb1992..1373906 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -239,6 +239,13 @@
android:process="com.android.server.telecom.testapps.SelfMangingCallingApp"
android:name="com.android.server.telecom.testapps.SelfManagedCallNotificationReceiver" />
+ <receiver android:exported="true"
+ android:name="com.android.server.telecom.testapps.NuisanceReportReceiver">
+ <intent-filter>
+ <action android:name="android.telecom.action.NUISANCE_CALL_STATUS_CHANGED" />
+ </intent-filter>
+ </receiver>
+
<service
android:name=".TestCallScreeningService"
android:permission="android.permission.BIND_SCREENING_SERVICE">
diff --git a/testapps/res/layout/testdialer_main.xml b/testapps/res/layout/testdialer_main.xml
index e6c56b7..9da3789 100644
--- a/testapps/res/layout/testdialer_main.xml
+++ b/testapps/res/layout/testdialer_main.xml
@@ -30,6 +30,16 @@
android:layout_height="wrap_content"
android:text="@string/placeCallButton" />
<Button
+ android:id="@+id/report_nuisance_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Report Nuisance" />
+ <Button
+ android:id="@+id/report_not_nuisance_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Report Not Nuisance" />
+ <Button
android:id="@+id/set_default_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/testapps/src/com/android/server/telecom/testapps/NuisanceReportReceiver.java b/testapps/src/com/android/server/telecom/testapps/NuisanceReportReceiver.java
new file mode 100644
index 0000000..76fff66
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/NuisanceReportReceiver.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.testapps;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.telecom.CallScreeningService;
+import android.widget.Toast;
+
+/**
+ * Example receiver for nuisance call reports from Telecom.
+ */
+public class NuisanceReportReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(CallScreeningService.ACTION_NUISANCE_CALL_STATUS_CHANGED)) {
+ Uri handle = intent.getParcelableExtra(CallScreeningService.EXTRA_CALL_HANDLE);
+ boolean isNuisance = intent.getBooleanExtra(CallScreeningService.EXTRA_IS_NUISANCE,
+ false);
+ int durationBucket = intent.getIntExtra(CallScreeningService.EXTRA_CALL_DURATION, 0);
+ int callType = intent.getIntExtra(CallScreeningService.EXTRA_CALL_TYPE, 0);
+ handleNuisanceReport(context, handle, isNuisance, durationBucket, callType);
+ }
+ }
+
+ private void handleNuisanceReport(Context context, Uri handle, boolean isNuisance,
+ int durationBucket, int callType) {
+
+ String message = "Nuisance report for: " + handle + " isNuisance=" + isNuisance
+ + " duration=" + durationBucket + " callType=" + callType;
+ Toast.makeText(context,
+ (CharSequence) message,
+ Toast.LENGTH_LONG)
+ .show();
+ }
+}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java b/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
index c7eccf7..66a6944 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
@@ -54,6 +54,20 @@
}
});
+ findViewById(R.id.report_nuisance_button).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ reportNuisance(true);
+ }
+ });
+
+ findViewById(R.id.report_not_nuisance_button).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ reportNuisance(false);
+ }
+ });
+
mNumberView = (EditText) findViewById(R.id.number);
mRttCheckbox = (CheckBox) findViewById(R.id.call_with_rtt_checkbox);
updateMutableUi();
@@ -140,4 +154,11 @@
Log.i("Santos xtr", intentExtras.toString());
return intentExtras;
}
+
+ private void reportNuisance(boolean isNuisance) {
+ final TelecomManager telecomManager =
+ (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
+ telecomManager.reportNuisanceCallStatus(Uri.fromParts(PhoneAccount.SCHEME_TEL,
+ mNumberView.getText().toString(), null), isNuisance);
+ }
}
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index b779ce2..512dbe0 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -51,6 +51,7 @@
import com.android.server.telecom.CallState;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.DefaultDialerCache;
+import com.android.server.telecom.NuisanceCallReporter;
import com.android.server.telecom.PhoneAccountRegistrar;
import com.android.server.telecom.TelecomServiceImpl;
import com.android.server.telecom.TelecomSystem;
@@ -166,6 +167,7 @@
private TelecomServiceImpl.SettingsSecureAdapter mSettingsSecureAdapter =
spy(new SettingsSecureAdapterFake());
@Mock private UserCallIntentProcessor mUserCallIntentProcessor;
+ @Mock private NuisanceCallReporter mNuisanceCallReporter;
private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
@@ -208,6 +210,7 @@
mDefaultDialerCache,
mSubscriptionManagerAdapter,
mSettingsSecureAdapter,
+ mNuisanceCallReporter,
mLock);
mTSIBinder = telecomServiceImpl.getBinder();
mComponentContextFixture.setTelecomManager(mTelecomManager);