summaryrefslogtreecommitdiff
path: root/src/com/android/server/telecom/CallLogManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/server/telecom/CallLogManager.java')
-rw-r--r--src/com/android/server/telecom/CallLogManager.java131
1 files changed, 118 insertions, 13 deletions
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 3005656b3..fc4e05d69 100644
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -19,9 +19,13 @@ package com.android.server.telecom;
import static android.provider.CallLog.Calls.BLOCK_REASON_NOT_BLOCKED;
import static android.telephony.CarrierConfigManager.KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
import android.location.Country;
import android.location.CountryDetector;
import android.location.Location;
@@ -30,6 +34,7 @@ import android.os.AsyncTask;
import android.os.Looper;
import android.os.UserHandle;
import android.os.PersistableBundle;
+import android.os.UserManager;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.telecom.Connection;
@@ -42,13 +47,16 @@ import android.telecom.VideoProfile;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
import android.telephony.SubscriptionManager;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.telecom.callfiltering.CallFilteringResult;
+import com.android.server.telecom.flags.FeatureFlags;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
+import java.util.UUID;
import java.util.stream.Stream;
/**
@@ -68,16 +76,19 @@ public final class CallLogManager extends CallsManagerListenerBase {
*/
private static class AddCallArgs {
public AddCallArgs(Context context, CallLog.AddCallParams params,
- @Nullable LogCallCompletedListener logCallCompletedListener) {
+ @Nullable LogCallCompletedListener logCallCompletedListener,
+ @NonNull Call call) {
this.context = context;
this.params = params;
this.logCallCompletedListener = logCallCompletedListener;
+ this.call = call;
}
// Since the members are accessed directly, we don't use the
// mXxxx notation.
public final Context context;
public final CallLog.AddCallParams params;
+ public final Call call;
@Nullable
public final LogCallCompletedListener logCallCompletedListener;
}
@@ -88,29 +99,39 @@ public final class CallLogManager extends CallsManagerListenerBase {
// TODO: come up with a better way to indicate in a android.telecom.DisconnectCause that
// a conference was merged successfully
private static final String REASON_IMS_MERGED_SUCCESSFULLY = "IMS_MERGED_SUCCESSFULLY";
+ private static final UUID LOG_CALL_FAILED_ANOMALY_ID =
+ UUID.fromString("d9b38771-ff36-417b-8723-2363a870c702");
+ private static final String LOG_CALL_FAILED_ANOMALY_DESC =
+ "Based on the current user, Telecom detected failure to record a call to the call log.";
private final Context mContext;
private final CarrierConfigManager mCarrierConfigManager;
private final PhoneAccountRegistrar mPhoneAccountRegistrar;
private final MissedCallNotifier mMissedCallNotifier;
+ private AnomalyReporterAdapter mAnomalyReporterAdapter;
private static final String ACTION_CALLS_TABLE_ADD_ENTRY =
- "com.android.server.telecom.intent.action.CALLS_ADD_ENTRY";
+ "com.android.server.telecom.intent.action.CALLS_ADD_ENTRY";
private static final String PERMISSION_PROCESS_CALLLOG_INFO =
- "android.permission.PROCESS_CALLLOG_INFO";
+ "android.permission.PROCESS_CALLLOG_INFO";
private static final String CALL_TYPE = "callType";
private static final String CALL_DURATION = "duration";
private Object mLock;
private String mCurrentCountryIso;
+ private final FeatureFlags mFeatureFlags;
+
public CallLogManager(Context context, PhoneAccountRegistrar phoneAccountRegistrar,
- MissedCallNotifier missedCallNotifier) {
+ MissedCallNotifier missedCallNotifier, AnomalyReporterAdapter anomalyReporterAdapter,
+ FeatureFlags featureFlags) {
mContext = context;
mCarrierConfigManager = (CarrierConfigManager) mContext
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
mPhoneAccountRegistrar = phoneAccountRegistrar;
mMissedCallNotifier = missedCallNotifier;
+ mAnomalyReporterAdapter = anomalyReporterAdapter;
mLock = new Object();
+ mFeatureFlags = featureFlags;
}
@Override
@@ -149,9 +170,10 @@ public final class CallLogManager extends CallsManagerListenerBase {
* Call was NOT in the "choose account" phase when disconnected
* Call is NOT a conference call which had children (unless it was remotely hosted).
* Call is NOT a child call from a conference which was remotely hosted.
+ * Call has NOT indicated it should be skipped for logging in its extras
* Call is NOT simulating a single party conference.
* Call was NOT explicitly canceled, except for disconnecting from a conference.
- * Call is NOT an external call
+ * Call is NOT an external call or an external call on watch.
* Call is NOT disconnected because of merging into a conference.
* Call is NOT a self-managed call OR call is a self-managed call which has indicated it
* should be logged in its PhoneAccount
@@ -180,6 +202,11 @@ public final class CallLogManager extends CallsManagerListenerBase {
return false;
}
+ if (mFeatureFlags.telecomSkipLogBasedOnExtra() && call.getExtras() != null
+ && call.getExtras().containsKey(TelecomManager.EXTRA_DO_NOT_LOG_CALL)) {
+ return false;
+ }
+
// A child call of a conference which was remotely hosted; these didn't originate on this
// device and should not be logged.
if (call.getParentCall() != null && call.hasProperty(Connection.PROPERTY_REMOTELY_HOSTED)) {
@@ -200,8 +227,10 @@ public final class CallLogManager extends CallsManagerListenerBase {
& Connection.CAPABILITY_DISCONNECT_FROM_CONFERENCE)
== Connection.CAPABILITY_DISCONNECT_FROM_CONFERENCE;
}
- // An external call
- if (call.isExternalCall()) {
+ // An external and non-watch call
+ if (call.isExternalCall() && (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WATCH)
+ || !mFeatureFlags.telecomLogExternalWearableCalls())) {
return false;
}
@@ -240,8 +269,13 @@ public final class CallLogManager extends CallsManagerListenerBase {
logCall(call, type, new LogCallCompletedListener() {
@Override
public void onLogCompleted(@Nullable Uri uri) {
- mMissedCallNotifier.showMissedCallNotification(
- new MissedCallNotifier.CallInfo(call));
+ if (mFeatureFlags.addCallUriForMissedCalls()){
+ mMissedCallNotifier.showMissedCallNotification(
+ new MissedCallNotifier.CallInfo(call), uri);
+ } else {
+ mMissedCallNotifier.showMissedCallNotification(
+ new MissedCallNotifier.CallInfo(call), /* uri= */ null);
+ }
}
}, result);
} else {
@@ -263,7 +297,7 @@ public final class CallLogManager extends CallsManagerListenerBase {
* {@link android.provider.CallLog.Calls#BLOCKED_TYPE}.
*/
void logCall(Call call, int callLogType,
- @Nullable LogCallCompletedListener logCallCompletedListener, CallFilteringResult result) {
+ @Nullable LogCallCompletedListener logCallCompletedListener, CallFilteringResult result) {
CallLog.AddCallParams.AddCallParametersBuilder paramBuilder =
new CallLog.AddCallParams.AddCallParametersBuilder();
@@ -385,7 +419,7 @@ public final class CallLogManager extends CallsManagerListenerBase {
okayToLogCall(accountHandle, logNumber, call.isEmergencyCall());
if (okayToLog) {
AddCallArgs args = new AddCallArgs(mContext, paramBuilder.build(),
- logCallCompletedListener);
+ logCallCompletedListener, call);
Log.addEvent(call, LogUtils.Events.LOG_CALL, "number=" + Log.piiHandle(logNumber)
+ ",postDial=" + Log.piiHandle(call.getPostDialDigits()) + ",pres="
+ call.getHandlePresentation());
@@ -516,8 +550,25 @@ public final class CallLogManager extends CallsManagerListenerBase {
AddCallArgs c = callList[i];
mListeners[i] = c.logCallCompletedListener;
try {
- // May block.
+ Pair<Integer, Integer> startStats = getCallLogStats(c.call);
+ Log.i(TAG, "LogCall; about to log callId=%s, "
+ + "startCount=%d, startMaxId=%d",
+ c.call.getId(), startStats.first, startStats.second);
+
result[i] = Calls.addCall(c.context, c.params);
+ Pair<Integer, Integer> endStats = getCallLogStats(c.call);
+ Log.i(TAG, "LogCall; logged callId=%s, uri=%s, "
+ + "endCount=%d, endMaxId=%s",
+ c.call.getId(), result, endStats.first, endStats.second);
+ if ((endStats.second - startStats.second) <= 0) {
+ // No call was added or even worse we lost a call in the log. Trigger an
+ // anomaly report. Note: it technically possible that an app modified the
+ // call log while we were writing to it here; that is pretty unlikely, and
+ // the goal here is to try and identify potential anomalous conditions with
+ // logging calls.
+ mAnomalyReporterAdapter.reportAnomaly(LOG_CALL_FAILED_ANOMALY_ID,
+ LOG_CALL_FAILED_ANOMALY_DESC);
+ }
} catch (Exception e) {
// This is very rare but may happen in legitimate cases.
// E.g. If the phone is encrypted and thus write request fails, it may cause
@@ -526,8 +577,10 @@ public final class CallLogManager extends CallsManagerListenerBase {
//
// We don't want to crash the whole process just because of that, so just log
// it instead.
- Log.e(TAG, e, "Exception raised during adding CallLog entry.");
+ Log.e(TAG, e, "LogCall: Exception raised adding callId=%s", c.call.getId());
result[i] = null;
+ mAnomalyReporterAdapter.reportAnomaly(LOG_CALL_FAILED_ANOMALY_ID,
+ LOG_CALL_FAILED_ANOMALY_DESC);
}
}
return result;
@@ -602,4 +655,56 @@ public final class CallLogManager extends CallsManagerListenerBase {
return mCurrentCountryIso;
}
}
+
+
+ /**
+ * Returns a pair containing the number of rows in the call log, as well as the maximum call log
+ * ID. There is a limit of 500 entries in the call log for a phone account, so once we hit 500
+ * we can reasonably expect that number to not change before and after logging a call.
+ * We determine the maximum ID in the call log since this is a way we can objectively check if
+ * the provider did record a call log entry or not. Ideally there should be more call log
+ * entries after logging than before, and certainly not less.
+ * @return pair with number of rows in the call log and max id.
+ */
+ private Pair<Integer, Integer> getCallLogStats(@NonNull Call call) {
+ try {
+ // Ensure we query the call log based on the current user.
+ final Context currentUserContext = mContext.createContextAsUser(
+ call.getAssociatedUser(), /* flags= */ 0);
+ final ContentResolver currentUserResolver = currentUserContext.getContentResolver();
+ final UserManager userManager = currentUserContext.getSystemService(UserManager.class);
+ final int currentUserId = userManager.getProcessUserId();
+
+ // Use shadow provider based on current user unlock state.
+ Uri providerUri;
+ if (userManager.isUserUnlocked(currentUserId)) {
+ providerUri = Calls.CONTENT_URI;
+ } else {
+ providerUri = Calls.SHADOW_CONTENT_URI;
+ }
+ int maxCallId = -1;
+ int numFound;
+ try (Cursor countCursor = currentUserResolver.query(providerUri,
+ new String[]{Calls._ID},
+ null,
+ null,
+ Calls._ID + " DESC")) {
+ numFound = countCursor.getCount();
+ if (numFound > 0) {
+ countCursor.moveToFirst();
+ maxCallId = countCursor.getInt(0);
+ }
+ }
+ return new Pair<>(numFound, maxCallId);
+ } catch (Exception e) {
+ // Oh jeepers, we crashed getting the call count.
+ Log.e(TAG, e, "getCountOfCallLogRows: failed");
+ return new Pair<>(-1, -1);
+ }
+ }
+
+ @VisibleForTesting
+ public void setAnomalyReporterAdapter(AnomalyReporterAdapter anomalyReporterAdapter){
+ mAnomalyReporterAdapter = anomalyReporterAdapter;
+ }
}