diff options
author | 2025-02-20 14:12:44 -0800 | |
---|---|---|
committer | 2025-03-05 09:15:11 -0800 | |
commit | 8595dc01e81d1d2c4a3357c58e27710330aa994f (patch) | |
tree | 8bc3b7c53eb1ca4da8489d632e1102c88de805c6 /service | |
parent | 1e6bca7ffcac0f3401a1788e833139844984e258 (diff) |
Add logging to ECM in call checks
These will help guage the success of the feature. Also refactors the
call tracking to its own class.
Bug: 364535720
Test: manual
Flag: android.permission.flags.unknown_call_setting_blocked_logging_enabled
Relnote: none
LOW_COVERAGE_REASON=FLAG_NOT_ENABLED
Change-Id: I5b23564b7cb75d8ddeaaed38307d2bb2c19bab0a
Diffstat (limited to 'service')
-rw-r--r-- | service/java/com/android/ecm/EnhancedConfirmationService.java | 523 |
1 files changed, 350 insertions, 173 deletions
diff --git a/service/java/com/android/ecm/EnhancedConfirmationService.java b/service/java/com/android/ecm/EnhancedConfirmationService.java index 2e83a802b..0566a8285 100644 --- a/service/java/com/android/ecm/EnhancedConfirmationService.java +++ b/service/java/com/android/ecm/EnhancedConfirmationService.java @@ -19,6 +19,9 @@ package com.android.ecm; import static android.app.ecm.EnhancedConfirmationManager.REASON_PACKAGE_RESTRICTED; import static android.app.ecm.EnhancedConfirmationManager.REASON_PHONE_STATE; +import static com.android.permission.PermissionStatsLog.CALL_WITH_ECM_INTERACTION_REPORTED; +import static com.android.permissioncontroller.PermissionControllerStatsLog.ECM_RESTRICTION_QUERY_IN_CALL_REPORTED; + import android.Manifest; import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.FlaggedApi; @@ -47,6 +50,7 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Looper; +import android.os.SystemClock; import android.os.SystemConfigManager; import android.os.UserHandle; import android.permission.flags.Flags; @@ -67,6 +71,7 @@ import androidx.annotation.RequiresApi; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.permission.util.UserUtils; +import com.android.permissioncontroller.PermissionControllerStatsLog; import com.android.server.LocalManagerRegistry; import com.android.server.SystemService; @@ -75,8 +80,10 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; /** @@ -93,12 +100,58 @@ import java.util.concurrent.ConcurrentHashMap; public class EnhancedConfirmationService extends SystemService { private static final String LOG_TAG = EnhancedConfirmationService.class.getSimpleName(); + /** A map of ECM states to their corresponding app op states */ + @Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @IntDef(prefix = {"ECM_STATE_"}, value = {EcmState.ECM_STATE_NOT_GUARDED, + EcmState.ECM_STATE_GUARDED, EcmState.ECM_STATE_GUARDED_AND_ACKNOWLEDGED, + EcmState.ECM_STATE_IMPLICIT}) + private @interface EcmState { + int ECM_STATE_NOT_GUARDED = AppOpsManager.MODE_ALLOWED; + int ECM_STATE_GUARDED = AppOpsManager.MODE_ERRORED; + int ECM_STATE_GUARDED_AND_ACKNOWLEDGED = AppOpsManager.MODE_IGNORED; + int ECM_STATE_IMPLICIT = AppOpsManager.MODE_DEFAULT; + } + + private static final ArraySet<String> PER_PACKAGE_PROTECTED_SETTINGS = new ArraySet<>(); + + // Settings restricted when an untrusted call is ongoing. These must also be added to + // PROTECTED_SETTINGS + private static final ArraySet<String> UNTRUSTED_CALL_RESTRICTED_SETTINGS = new ArraySet<>(); + + static { + // Runtime permissions + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.SEND_SMS); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_SMS); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.READ_SMS); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_MMS); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_WAP_PUSH); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.READ_CELL_BROADCASTS); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission_group.SMS); + + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.BIND_DEVICE_ADMIN); + // App ops + PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE); + PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS); + PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW); + PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_GET_USAGE_STATS); + PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS); + // Default application roles. + PER_PACKAGE_PROTECTED_SETTINGS.add(RoleManager.ROLE_DIALER); + PER_PACKAGE_PROTECTED_SETTINGS.add(RoleManager.ROLE_SMS); + + if (Flags.unknownCallPackageInstallBlockingEnabled()) { + // Requesting package installs, limited during phone calls + UNTRUSTED_CALL_RESTRICTED_SETTINGS.add( + AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES); + UNTRUSTED_CALL_RESTRICTED_SETTINGS.add( + AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE); + } + } + private Map<String, List<byte[]>> mTrustedPackageCertDigests; private Map<String, List<byte[]>> mTrustedInstallerCertDigests; - // A map of call ID to call type. Thread safe because it is queried on the main thread, but - // added/removed on a background thread. - private final Map<String, Integer> mOngoingCalls = new ConcurrentHashMap<>(); + private static final long UNTRUSTED_CALL_STORAGE_TIME_MS = TimeUnit.HOURS.toMillis(1); private static final int CALL_TYPE_UNTRUSTED = 0; private static final int CALL_TYPE_TRUSTED = 1; private static final int CALL_TYPE_EMERGENCY = 1 << 1; @@ -116,7 +169,10 @@ public class EnhancedConfirmationService extends SystemService { new EnhancedConfirmationManagerLocalImpl(this)); } - private TelephonyManager mTelephonyManager; + private PackageManager mPackageManager; + + // A helper which tracks the calls received by the system, and information about them. + private CallTracker mCallTracker; @GuardedBy("mUserAccessibilityManagers") private final Map<Integer, AccessibilityManager> mUserAccessibilityManagers = @@ -133,7 +189,11 @@ public class EnhancedConfirmationService extends SystemService { systemConfigManager.getEnhancedConfirmationTrustedInstallers()); publishBinderService(Context.ECM_ENHANCED_CONFIRMATION_SERVICE, new Stub()); - mTelephonyManager = getContext().getSystemService(TelephonyManager.class); + + if (Flags.unknownCallPackageInstallBlockingEnabled()) { + mCallTracker = new CallTracker(getContext()); + } + mPackageManager = getContext().getPackageManager(); } private Map<String, List<byte[]>> toTrustedPackageMap(Set<SignedPackage> signedPackages) { @@ -148,115 +208,31 @@ public class EnhancedConfirmationService extends SystemService { void addOngoingCall(Call call) { assertNotMainThread(); - if (!Flags.unknownCallPackageInstallBlockingEnabled()) { - return; + if (mCallTracker != null) { + mCallTracker.addCall(call); } - if (call.getDetails() == null) { - return; - } - mOngoingCalls.put(call.getDetails().getId(), getCallType(call)); } @WorkerThread void removeOngoingCall(String callId) { assertNotMainThread(); - if (!Flags.unknownCallPackageInstallBlockingEnabled()) { - return; - } - Integer returned = mOngoingCalls.remove(callId); - if (returned == null) { - // TODO b/379941144: Capture a bug report whenever this happens. + if (mCallTracker != null) { + mCallTracker.endCall(callId); } } @WorkerThread void clearOngoingCalls() { assertNotMainThread(); - mOngoingCalls.clear(); - } - @WorkerThread - private @CallType int getCallType(Call call) { - assertNotMainThread(); - String number = getPhoneNumber(call); - try { - if (number != null && mTelephonyManager.isEmergencyNumber(number)) { - return CALL_TYPE_EMERGENCY; - } - } catch (IllegalStateException | UnsupportedOperationException e) { - // If either of these are thrown, the telephony service is not available on the current - // device, either because the device lacks telephony calling, or the telephony service - // is unavailable. - } - UserHandle user = getContext().getUser(); - Bundle extras = call.getDetails().getExtras(); - if (extras != null) { - user = extras.getParcelable(Intent.EXTRA_USER_HANDLE, UserHandle.class); - } - if (number != null) { - return hasContactWithPhoneNumber(number, user) - ? CALL_TYPE_TRUSTED : CALL_TYPE_UNTRUSTED; - } else { - return hasContactWithDisplayName(call.getDetails().getCallerDisplayName(), user) - ? CALL_TYPE_TRUSTED : CALL_TYPE_UNTRUSTED; + if (mCallTracker != null) { + mCallTracker.endAllCalls(); } } - private String getPhoneNumber(Call call) { - Uri handle = call.getDetails().getHandle(); - if (handle == null || handle.getScheme() == null) { - return null; - } - if (!handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) { - return null; - } - return handle.getSchemeSpecificPart(); - } - - @WorkerThread - private boolean hasContactWithPhoneNumber(String phoneNumber, UserHandle user) { - assertNotMainThread(); - if (phoneNumber == null) { - return false; - } - Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, - Uri.encode(phoneNumber)); - String[] projection = new String[]{ - PhoneLookup.DISPLAY_NAME, - ContactsContract.PhoneLookup._ID - }; - try (Cursor res = getUserContentResolver(user).query(uri, projection, null, null)) { - return res != null && res.getCount() > 0; - } - } - - @WorkerThread - private boolean hasContactWithDisplayName(String displayName, UserHandle user) { - assertNotMainThread(); - if (displayName == null) { - return false; - } - Uri uri = ContactsContract.Data.CONTENT_URI; - String[] projection = new String[]{PhoneLookup._ID}; - String selection = StructuredName.DISPLAY_NAME + " = ?"; - String[] selectionArgs = new String[]{displayName}; - try (Cursor res = getUserContentResolver(user) - .query(uri, projection, selection, selectionArgs, null)) { - return res != null && res.getCount() > 0; - } - } - - private ContentResolver getUserContentResolver(UserHandle user) { - return getContext().createContextAsUser(user, 0).getContentResolver(); - } - - private boolean hasCallOfType(@CallType int callType) { - for (int ongoingCallType : mOngoingCalls.values()) { - if (ongoingCallType == callType) { - return true; - } - } - return false; + static int getPackageUid(PackageManager pm, String packageName, + int userId) throws NameNotFoundException { + return pm.getPackageUidAsUser(packageName, PackageManager.PackageInfoFlags.of(0), userId); } private void assertNotMainThread() throws IllegalStateException { @@ -267,65 +243,15 @@ public class EnhancedConfirmationService extends SystemService { private class Stub extends IEnhancedConfirmationManager.Stub { - /** A map of ECM states to their corresponding app op states */ - @Retention(java.lang.annotation.RetentionPolicy.SOURCE) - @IntDef(prefix = {"ECM_STATE_"}, value = {EcmState.ECM_STATE_NOT_GUARDED, - EcmState.ECM_STATE_GUARDED, EcmState.ECM_STATE_GUARDED_AND_ACKNOWLEDGED, - EcmState.ECM_STATE_IMPLICIT}) - private @interface EcmState { - int ECM_STATE_NOT_GUARDED = AppOpsManager.MODE_ALLOWED; - int ECM_STATE_GUARDED = AppOpsManager.MODE_ERRORED; - int ECM_STATE_GUARDED_AND_ACKNOWLEDGED = AppOpsManager.MODE_IGNORED; - int ECM_STATE_IMPLICIT = AppOpsManager.MODE_DEFAULT; - } - - private static final ArraySet<String> PER_PACKAGE_PROTECTED_SETTINGS = new ArraySet<>(); - - // Settings restricted when an untrusted call is ongoing. These must also be added to - // PROTECTED_SETTINGS - private static final ArraySet<String> UNTRUSTED_CALL_RESTRICTED_SETTINGS = new ArraySet<>(); - - static { - // Runtime permissions - PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.SEND_SMS); - PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_SMS); - PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.READ_SMS); - PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_MMS); - PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_WAP_PUSH); - PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.READ_CELL_BROADCASTS); - PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission_group.SMS); - - PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.BIND_DEVICE_ADMIN); - // App ops - PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE); - PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS); - PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW); - PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_GET_USAGE_STATS); - PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS); - // Default application roles. - PER_PACKAGE_PROTECTED_SETTINGS.add(RoleManager.ROLE_DIALER); - PER_PACKAGE_PROTECTED_SETTINGS.add(RoleManager.ROLE_SMS); - - if (Flags.unknownCallPackageInstallBlockingEnabled()) { - // Requesting package installs, limited during phone calls - UNTRUSTED_CALL_RESTRICTED_SETTINGS.add( - AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES); - UNTRUSTED_CALL_RESTRICTED_SETTINGS.add( - AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE); - } - } - private final @NonNull Context mContext; private final String mAttributionTag; private final AppOpsManager mAppOpsManager; - private final PackageManager mPackageManager; Stub() { Context context = getContext(); mContext = context; mAttributionTag = context.getAttributionTag(); mAppOpsManager = context.getSystemService(AppOpsManager.class); - mPackageManager = context.getPackageManager(); } public boolean isRestricted(@NonNull String packageName, @NonNull String settingIdentifier, @@ -380,7 +306,7 @@ public class EnhancedConfirmationService extends SystemService { } setAppEcmState(packageName, EcmState.ECM_STATE_NOT_GUARDED, userId); EnhancedConfirmationStatsLogUtils.INSTANCE.logRestrictionCleared( - getPackageUid(packageName, userId)); + getPackageUid(mPackageManager, packageName, userId)); } catch (NameNotFoundException e) { throw new IllegalArgumentException(e); } @@ -421,18 +347,6 @@ public class EnhancedConfirmationService extends SystemService { } } - private boolean isUntrustedCallOngoing() { - if (!Flags.unknownCallPackageInstallBlockingEnabled()) { - return false; - } - - if (hasCallOfType(CALL_TYPE_EMERGENCY)) { - // If we have an emergency call, return false always. - return false; - } - return hasCallOfType(CALL_TYPE_UNTRUSTED); - } - private void enforcePermissions(@NonNull String methodName, @UserIdInt int userId) { UserUtils.enforceCrossUserPermission(userId, /* allowAll= */ false, /* enforceForProfileGroup= */ false, methodName, mContext); @@ -540,7 +454,7 @@ public class EnhancedConfirmationService extends SystemService { @SuppressLint("WrongConstant") private void setAppEcmState(@NonNull String packageName, @EcmState int ecmState, @UserIdInt int userId) throws NameNotFoundException { - int packageUid = getPackageUid(packageName, userId); + int packageUid = getPackageUid(mPackageManager, packageName, userId); final long identityToken = Binder.clearCallingIdentity(); try { mAppOpsManager.setMode(AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS, packageUid, @@ -552,7 +466,7 @@ public class EnhancedConfirmationService extends SystemService { private @EcmState int getAppEcmState(@NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException { - int packageUid = getPackageUid(packageName, userId); + int packageUid = getPackageUid(mPackageManager, packageName, userId); final long identityToken = Binder.clearCallingIdentity(); try { return mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS, @@ -578,19 +492,28 @@ public class EnhancedConfirmationService extends SystemService { return false; } + // Generate a global protection reason for why the setting may be blocked. Note, this + // method will result in a metric being logged, representing a blocked/allowed setting private String getGlobalProtectionReason(@NonNull String settingIdentifier, @NonNull String packageName, @UserIdInt int userId) { - if (UNTRUSTED_CALL_RESTRICTED_SETTINGS.contains(settingIdentifier) - && isUntrustedCallOngoing()) { + if (!UNTRUSTED_CALL_RESTRICTED_SETTINGS.contains(settingIdentifier)) { + return null; + } + if (mCallTracker == null) { + return null; + } + String reason = null; + if (mCallTracker.isUntrustedCallOngoing()) { if (!AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE.equals(settingIdentifier)) { - return REASON_PHONE_STATE; + reason = REASON_PHONE_STATE; } if (!isAccessibilityTool(packageName, userId)) { - return REASON_PHONE_STATE; + reason = REASON_PHONE_STATE; } - return null; } - return null; + mCallTracker.onEcmInteraction(packageName, userId, settingIdentifier, reason == null); + + return reason; } private boolean isAccessibilityTool(@NonNull String packageName, @UserIdInt int userId) { @@ -633,11 +556,265 @@ public class EnhancedConfirmationService extends SystemService { return null; } } + } - private int getPackageUid(@NonNull String packageName, @UserIdInt int userId) - throws NameNotFoundException { - return mPackageManager.getApplicationInfoAsUser(packageName, /* flags */ 0, - UserHandle.of(userId)).uid; + private static class CallTracker { + // A map of call ID to ongoing or recently removed calls. Concurrent because + // additions/removals happen on background threads, but queries on main thread. + private final Map<String, TrackedCall> mCalls = new ConcurrentHashMap<>(); + + private class TrackedCall { + public @CallType Integer callType; + public String caller; + + public long startTime = SystemClock.elapsedRealtime(); + + public long endTime = -1; + + public boolean incoming; + + public boolean blockedDuringCall = false; + + public boolean ecmInteractionDuringCall = false; + + public boolean isFinished() { + return endTime > 0; + } + + TrackedCall(@NonNull Call call) { + caller = getPhoneNumber(call); + if (caller == null) { + caller = getDisplayName(call); + } + callType = getCallType(call); + incoming = call.getDetails().getCallDirection() == Call.Details.DIRECTION_INCOMING; + } + } + + private Context mContext; + private TelephonyManager mTelephonyManager; + private PackageManager mPackageManager; + + CallTracker(Context context) { + mContext = context; + mTelephonyManager = context.getSystemService(TelephonyManager.class); + mPackageManager = context.getPackageManager(); + } + + public void addCall(@NonNull Call call) { + if (call.getDetails() == null) { + return; + } + pruneOldFinishedCalls(); + mCalls.put(call.getDetails().getId(), new TrackedCall(call)); + } + + public void endCall(@NonNull String callId) { + TrackedCall trackedCall = mCalls.get(callId); + if (trackedCall == null) { + // TODO b/379941144: Capture a bug report whenever this happens. + return; + } + if (trackedCall.isFinished()) { + return; + } + if (!Flags.unknownCallSettingBlockedLoggingEnabled()) { + mCalls.remove(callId); + return; + } + + trackedCall.endTime = SystemClock.elapsedRealtime(); + if (trackedCall.callType != CALL_TYPE_UNTRUSTED) { + // We only hang onto a finished call if the call was untrusted + mCalls.remove(callId); + } + + if (trackedCall.ecmInteractionDuringCall) { + long duration = TimeUnit.MILLISECONDS.toSeconds( + trackedCall.endTime - trackedCall.startTime); + int durationInt = (int) Math.min(duration, Integer.MAX_VALUE); + PermissionControllerStatsLog.write(CALL_WITH_ECM_INTERACTION_REPORTED, + trackedCall.blockedDuringCall, durationInt); + } + + pruneOldFinishedCalls(); + } + + public void endAllCalls() { + for (String callId: mCalls.keySet()) { + endCall(callId); + } + } + + public void onEcmInteraction(@NonNull String packageName, int userId, + @NonNull String settingIdentifier, boolean allowed) { + if (!Flags.unknownCallSettingBlockedLoggingEnabled()) { + return; + } + + boolean hasOngoingCall = false; + for (TrackedCall current: mCalls.values()) { + if (current.isFinished()) { + // We only care about ongoing calls + continue; + } + hasOngoingCall = true; + // Mark that the current call had a setting interaction during it + current.ecmInteractionDuringCall = true; + current.blockedDuringCall = !allowed; + logInCallRestrictionEvent(packageName, userId, settingIdentifier, allowed, current); + } + if (!hasOngoingCall) { + logInCallRestrictionEvent(packageName, userId, settingIdentifier, allowed, null); + } + + } + + private @CallType int getCallType(@NonNull Call call) { + String number = getPhoneNumber(call); + try { + if (number != null && mTelephonyManager.isEmergencyNumber(number)) { + return CALL_TYPE_EMERGENCY; + } + } catch (IllegalStateException | UnsupportedOperationException e) { + // If either of these are thrown, the telephony service is not available on the + // current device, either because the device lacks telephony calling, or the + // telephony service is unavailable. + } + UserHandle user = mContext.getUser(); + Bundle extras = call.getDetails().getExtras(); + if (extras != null) { + user = extras.getParcelable(Intent.EXTRA_USER_HANDLE, UserHandle.class); + } + if (number != null) { + return hasContactWithPhoneNumber(number, user) + ? CALL_TYPE_TRUSTED : CALL_TYPE_UNTRUSTED; + } else { + return hasContactWithDisplayName(getDisplayName(call), user) + ? CALL_TYPE_TRUSTED : CALL_TYPE_UNTRUSTED; + } + } + + private static String getPhoneNumber(@NonNull Call call) { + Uri handle = call.getDetails().getHandle(); + if (handle == null || handle.getScheme() == null) { + return null; + } + if (!handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) { + return null; + } + return handle.getSchemeSpecificPart(); + } + + private static String getDisplayName(@NonNull Call call) { + return call.getDetails().getCallerDisplayName(); + } + + private boolean hasContactWithPhoneNumber(@Nullable String phoneNumber, UserHandle user) { + if (phoneNumber == null) { + return false; + } + Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, + Uri.encode(phoneNumber)); + String[] projection = new String[]{ + PhoneLookup.DISPLAY_NAME, + ContactsContract.PhoneLookup._ID + }; + try (Cursor res = getUserContentResolver(user).query(uri, projection, null, null)) { + return res != null && res.getCount() > 0; + } + } + + private boolean hasContactWithDisplayName(@Nullable String displayName, UserHandle user) { + if (displayName == null) { + return false; + } + Uri uri = ContactsContract.Data.CONTENT_URI; + String[] projection = new String[]{PhoneLookup._ID}; + String selection = StructuredName.DISPLAY_NAME + " = ?"; + String[] selectionArgs = new String[]{displayName}; + try (Cursor res = getUserContentResolver(user) + .query(uri, projection, selection, selectionArgs, null)) { + return res != null && res.getCount() > 0; + } + } + + private ContentResolver getUserContentResolver(UserHandle user) { + return mContext.createContextAsUser(user, 0).getContentResolver(); + } + + private TrackedCall getOngoingCallOfType(@CallType int callType) { + for (TrackedCall call : mCalls.values()) { + if (!call.isFinished() && call.callType == callType) { + return call; + } + } + return null; + } + + public boolean isUntrustedCallOngoing() { + if (!Flags.unknownCallPackageInstallBlockingEnabled()) { + return false; + } + + if (getOngoingCallOfType(CALL_TYPE_EMERGENCY) != null) { + // If we have an emergency call, return false always. + return false; + } + return getOngoingCallOfType(CALL_TYPE_UNTRUSTED) != null; + } + + void pruneOldFinishedCalls() { + if (!Flags.unknownCallSettingBlockedLoggingEnabled()) { + return; + } + long cutoff = SystemClock.elapsedRealtime() - UNTRUSTED_CALL_STORAGE_TIME_MS; + mCalls.entrySet().removeIf( + e -> e.getValue().isFinished() && e.getValue().endTime < cutoff); + } + private void logInCallRestrictionEvent(@NonNull String packageName, int userId, + @NonNull String settingIdentifier, boolean allowed, @Nullable TrackedCall call) { + if (!Flags.unknownCallSettingBlockedLoggingEnabled()) { + return; + } + + if (!UNTRUSTED_CALL_RESTRICTED_SETTINGS.contains(settingIdentifier)) { + return; + } + + int uid; + try { + uid = mPackageManager.getPackageUid(packageName, userId); + } catch (NameNotFoundException e) { + return; + } + + boolean callInProgress = call != null && !call.isFinished(); + boolean trusted = true; + boolean incoming = false; + boolean callBackAfterBlock = false; + if (callInProgress) { + trusted = call.callType != CALL_TYPE_UNTRUSTED; + incoming = call.incoming; + + // Look for a previous call from the same caller, that had a blocked ecm interaction + for (TrackedCall otherCall : mCalls.values()) { + if (!otherCall.isFinished()) { + continue; + } + if (!Objects.equals(otherCall.caller, call.caller)) { + continue; + } + if (!otherCall.blockedDuringCall) { + continue; + } + callBackAfterBlock = true; + } + } + + PermissionControllerStatsLog.write(ECM_RESTRICTION_QUERY_IN_CALL_REPORTED, uid, + settingIdentifier, allowed, callInProgress, incoming, trusted, + callBackAfterBlock); } } } |