script-astra/Android/Sdk/sources/android-35/com/android/internal/telephony/CarrierPrivilegesTracker.java
localadmin 4380f00a78 init
2025-01-20 18:15:20 +03:00

1051 lines
46 KiB
Java

/*
* Copyright (C) 2020 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.internal.telephony;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
import static android.telephony.TelephonyManager.EXTRA_SIM_STATE;
import static android.telephony.TelephonyManager.SIM_STATE_ABSENT;
import static android.telephony.TelephonyManager.SIM_STATE_LOADED;
import static android.telephony.TelephonyManager.SIM_STATE_NOT_READY;
import static android.telephony.TelephonyManager.SIM_STATE_READY;
import static android.telephony.TelephonyManager.SIM_STATE_UNKNOWN;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.carrier.CarrierService;
import android.telephony.Annotation.CarrierPrivilegeStatus;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyRegistryManager;
import android.telephony.UiccAccessRule;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccPort;
import com.android.internal.telephony.uicc.UiccProfile;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
/**
* CarrierPrivilegesTracker will track the Carrier Privileges for a specific {@link Phone}.
* Registered Telephony entities will receive notifications when the UIDs with these privileges
* change.
*/
public class CarrierPrivilegesTracker extends Handler {
private static final String TAG = CarrierPrivilegesTracker.class.getSimpleName();
private static final boolean VDBG = false;
private static final String SHA_1 = "SHA-1";
private static final String SHA_256 = "SHA-256";
private static final int PACKAGE_NOT_PRIVILEGED = 0;
private static final int PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG = 1;
private static final int PACKAGE_PRIVILEGED_FROM_SIM = 2;
private static final int PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE = 3;
// TODO(b/232273884): Turn feature on when find solution to handle the inter-carriers switching
/**
* Time delay to clear UICC rules after UICC is gone.
* This introduces the grace period to retain carrier privileges when SIM is removed.
*
* This feature is off by default due to the security concern during inter-carriers switching.
*/
private static final long CLEAR_UICC_RULES_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(0);
/**
* PackageManager flags used to query installed packages.
* Include DISABLED_UNTIL_USED components. This facilitates cases where a carrier app
* is disabled by default, and some other component wants to enable it when it has
* gained carrier privileges (as an indication that a matching SIM has been inserted).
*/
private static final int INSTALLED_PACKAGES_QUERY_FLAGS =
PackageManager.GET_SIGNING_CERTIFICATES
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
| PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS;
/**
* Action for tracking when the Phone's SIM state changes.
* arg1: slotId that this Action applies to
* arg2: simState reported by this Broadcast
*/
private static final int ACTION_SIM_STATE_UPDATED = 4;
/**
* Action for tracking when a package is installed, replaced or changed (exclude the case
* disabled by user) on the device.
* obj: String package name that was installed, replaced or changed on the device.
*/
private static final int ACTION_PACKAGE_ADDED_REPLACED_OR_CHANGED = 5;
/**
* Action for tracking when a package is uninstalled or disabled by user on the device.
* obj: String package name that was installed or disabled by user on the device.
*/
private static final int ACTION_PACKAGE_REMOVED_OR_DISABLED_BY_USER = 6;
/**
* Action used to initialize the state of the Tracker.
*/
private static final int ACTION_INITIALIZE_TRACKER = 7;
/**
* Action to set the test override rule through {@link TelephonyManager#setCarrierTestOverride}.
* obj: String of the carrierPrivilegeRules from method setCarrierTestOverride.
*/
private static final int ACTION_SET_TEST_OVERRIDE_RULE = 8;
/**
* Action to clear UICC rules.
*/
private static final int ACTION_CLEAR_UICC_RULES = 9;
/**
* Action to handle the case when UiccAccessRules has been loaded.
*/
private static final int ACTION_UICC_ACCESS_RULES_LOADED = 10;
/**
* Action to set the test override rule through {@link
* TelephonyManager#setCarrierServicePackageOverride}.
*
* <p>obj: String of the carrierServicePackage from method setCarrierServicePackageOverride.
*/
private static final int ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE = 11;
private final Context mContext;
private final Phone mPhone;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
private final CarrierConfigManager mCarrierConfigManager;
private final TelephonyManager mTelephonyManager;
private final TelephonyRegistryManager mTelephonyRegistryManager;
@NonNull private final LocalLog mLocalLog = new LocalLog(64);
// Stores rules for Carrier Config-loaded rules
@NonNull private final List<UiccAccessRule> mCarrierConfigRules = new ArrayList<>();
// Stores rules for SIM-loaded rules.
@NonNull private final List<UiccAccessRule> mUiccRules = new ArrayList<>();
// Stores rule from test override (through TelephonyManager#setCarrierTestOverride).
// - Null list indicates no test override (CC and UICC rules are respected)
// - Empty list indicates test override to simulate no rules (CC and UICC rules are ignored)
// - Non-empty list indicates test override with specific rules (CC and UICC rules are ignored)
@Nullable private List<UiccAccessRule> mTestOverrideRules = null;
@Nullable private String mTestOverrideCarrierServicePackage = null;
// Map of PackageName -> Certificate hashes for that Package
@NonNull private final Map<String, Set<String>> mInstalledPackageCerts = new ArrayMap<>();
// Map of PackageName -> UIDs for that Package
@NonNull private final Map<String, Set<Integer>> mCachedUids = new ArrayMap<>();
// This should be used to guard critical section either with
// mPrivilegedPackageInfoLock.readLock() or mPrivilegedPackageInfoLock.writeLock(), but never
// with the mPrivilegedPackageInfoLock object itself.
@NonNull private final ReadWriteLock mPrivilegedPackageInfoLock = new ReentrantReadWriteLock();
// Package names and UIDs of apps that currently hold carrier privileges.
@GuardedBy(anyOf = {"mPrivilegedPackageInfoLock.readLock()",
"mPrivilegedPackageInfoLock.writeLock()"})
@NonNull private PrivilegedPackageInfo mPrivilegedPackageInfo = new PrivilegedPackageInfo();
// Uptime in millis on when the NEXT clear-up of UiccRules are scheduled
@ElapsedRealtimeLong
private long mClearUiccRulesUptimeMillis = CLEAR_UICC_RULE_NOT_SCHEDULED;
// Constant indicates no schedule to clear UiccRules
private static final long CLEAR_UICC_RULE_NOT_SCHEDULED = -1;
// Indicates SIM has reached SIM_STATE_READY but not SIM_STATE_LOADED yet. During this transient
// state, all the information previously loaded from SIM may be updated soon later and thus
// unreliable. For security's concern, any carrier privileges check should return
// CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED (instead of neither HAS_ACCESS nor NO_ACCESS) until
// SIM becomes LOADED again, or grace period specified by CLEAR_UICC_RULES_DELAY_MILLIS expires.
@GuardedBy(anyOf = {"mPrivilegedPackageInfoLock.readLock()",
"mPrivilegedPackageInfoLock.writeLock()"})
private boolean mSimIsReadyButNotLoaded = false;
/** Small snapshot to hold package names and UIDs of privileged packages. */
private static final class PrivilegedPackageInfo {
@NonNull final Set<String> mPackageNames;
@NonNull final Set<Integer> mUids;
// The carrier service (packageName, UID) pair
@NonNull final Pair<String, Integer> mCarrierService;
PrivilegedPackageInfo() {
mPackageNames = Collections.emptySet();
mUids = Collections.emptySet();
mCarrierService = new Pair<>(null, Process.INVALID_UID);
}
PrivilegedPackageInfo(@NonNull Set<String> packageNames, @NonNull Set<Integer> uids,
@NonNull Pair<String, Integer> carrierService) {
mPackageNames = packageNames;
mUids = uids;
mCarrierService = carrierService;
}
@Override
public String toString() {
return "{packageNames="
+ getObfuscatedPackages(mPackageNames, pkg -> Rlog.pii(TAG, pkg))
+ ", uids="
+ mUids
+ ", carrierServicePackageName="
+ Rlog.pii(TAG, mCarrierService.first)
+ ", carrierServiceUid="
+ mCarrierService.second
+ "}";
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof PrivilegedPackageInfo)) {
return false;
}
PrivilegedPackageInfo other = (PrivilegedPackageInfo) o;
return mPackageNames.equals(other.mPackageNames) && mUids.equals(other.mUids)
&& mCarrierService.equals(other.mCarrierService);
}
@Override
public int hashCode() {
return Objects.hash(mPackageNames, mUids, mCarrierService);
}
}
private final BroadcastReceiver mIntentReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null) return;
switch (action) {
case TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED: // fall through
case TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED: {
Bundle extras = intent.getExtras();
int simState = extras.getInt(EXTRA_SIM_STATE, SIM_STATE_UNKNOWN);
int slotId =
extras.getInt(PhoneConstants.PHONE_KEY, INVALID_SIM_SLOT_INDEX);
if (simState != SIM_STATE_ABSENT
&& simState != SIM_STATE_NOT_READY
&& simState != SIM_STATE_READY
&& simState != SIM_STATE_LOADED) {
return;
}
sendMessage(obtainMessage(ACTION_SIM_STATE_UPDATED, slotId, simState));
break;
}
case Intent.ACTION_PACKAGE_ADDED: // fall through
case Intent.ACTION_PACKAGE_REPLACED: // fall through
case Intent.ACTION_PACKAGE_REMOVED: // fall through
case Intent.ACTION_PACKAGE_CHANGED: {
Uri uri = intent.getData();
String pkgName = (uri != null) ? uri.getSchemeSpecificPart() : null;
if (TextUtils.isEmpty(pkgName)) {
Rlog.e(TAG, "Failed to get package from Intent");
return;
}
boolean removed = action.equals(Intent.ACTION_PACKAGE_REMOVED);
boolean disabledByUser = false;
boolean notExist = false;
try {
disabledByUser = action.equals(Intent.ACTION_PACKAGE_CHANGED)
&& mPackageManager.getApplicationEnabledSetting(pkgName)
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
} catch (IllegalArgumentException iae) {
// Very rare case when package changed race with package removed
Rlog.w(TAG, "Package does not exist: " + pkgName);
notExist = true;
}
// When a package is explicitly disabled by the user or does not exist,
// treat them as if it was removed: clear it from the cache
int what = (removed || disabledByUser || notExist)
? ACTION_PACKAGE_REMOVED_OR_DISABLED_BY_USER
: ACTION_PACKAGE_ADDED_REPLACED_OR_CHANGED;
sendMessage(obtainMessage(what, pkgName));
break;
}
}
}
};
public CarrierPrivilegesTracker(
@NonNull Looper looper, @NonNull Phone phone, @NonNull Context context) {
super(looper);
mContext = context;
mPhone = phone;
mPackageManager = mContext.getPackageManager();
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mCarrierConfigManager =
(CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
// Callback is executed in handler thread and directly handles carrier config update
mCarrierConfigManager.registerCarrierConfigChangeListener(this::post,
(slotIndex, subId, carrierId, specificCarrierId) -> handleCarrierConfigUpdated(
subId, slotIndex));
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mTelephonyRegistryManager =
(TelephonyRegistryManager)
mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
IntentFilter certFilter = new IntentFilter();
certFilter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
certFilter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
mContext.registerReceiver(mIntentReceiver, certFilter);
IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
// For package-related broadcasts, specify the data scheme for "package" to receive the
// package name along with the broadcast
packageFilter.addDataScheme("package");
mContext.registerReceiver(mIntentReceiver, packageFilter);
sendMessage(obtainMessage(ACTION_INITIALIZE_TRACKER));
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case ACTION_SIM_STATE_UPDATED: {
handleSimStateChanged(msg.arg1, msg.arg2);
break;
}
case ACTION_PACKAGE_ADDED_REPLACED_OR_CHANGED: {
String pkgName = (String) msg.obj;
handlePackageAddedReplacedOrChanged(pkgName);
break;
}
case ACTION_PACKAGE_REMOVED_OR_DISABLED_BY_USER: {
String pkgName = (String) msg.obj;
handlePackageRemovedOrDisabledByUser(pkgName);
break;
}
case ACTION_INITIALIZE_TRACKER: {
handleInitializeTracker();
break;
}
case ACTION_SET_TEST_OVERRIDE_RULE: {
String carrierPrivilegeRules = (String) msg.obj;
handleSetTestOverrideRules(carrierPrivilegeRules);
break;
}
case ACTION_CLEAR_UICC_RULES: {
handleClearUiccRules();
break;
}
case ACTION_UICC_ACCESS_RULES_LOADED: {
handleUiccAccessRulesLoaded();
break;
}
case ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE: {
String carrierServicePackage = (String) msg.obj;
handleSetTestOverrideCarrierServicePackage(carrierServicePackage);
break;
}
default: {
Rlog.e(TAG, "Received unknown msg type: " + msg.what);
break;
}
}
}
private void handleCarrierConfigUpdated(int subId, int slotIndex) {
if (slotIndex != mPhone.getPhoneId()) return;
List<UiccAccessRule> updatedCarrierConfigRules = Collections.EMPTY_LIST;
// Carrier Config broadcasts with INVALID_SUBSCRIPTION_ID when the SIM is removed. This is
// an expected event. When this happens, clear the certificates from the previous configs.
// The rules will be cleared in maybeUpdateRulesAndNotifyRegistrants() below.
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
updatedCarrierConfigRules = getCarrierConfigRules(subId);
}
mLocalLog.log("CarrierConfigUpdated:"
+ " subId=" + subId
+ " slotIndex=" + slotIndex
+ " updated CarrierConfig rules=" + updatedCarrierConfigRules);
maybeUpdateRulesAndNotifyRegistrants(mCarrierConfigRules, updatedCarrierConfigRules);
}
@NonNull
private List<UiccAccessRule> getCarrierConfigRules(int subId) {
PersistableBundle carrierConfigs =
CarrierConfigManager.getCarrierConfigSubset(
mContext, subId, KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
// CarrierConfigManager#isConfigForIdentifiedCarrier can handle null or empty bundle
if (!mCarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfigs)) {
return Collections.EMPTY_LIST;
}
String[] carrierConfigRules =
carrierConfigs.getStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
if (carrierConfigRules == null) {
return Collections.EMPTY_LIST;
}
return Arrays.asList(UiccAccessRule.decodeRulesFromCarrierConfig(carrierConfigRules));
}
private void handleSimStateChanged(int slotId, int simState) {
if (slotId != mPhone.getPhoneId()) return;
List<UiccAccessRule> updatedUiccRules = Collections.EMPTY_LIST;
mPrivilegedPackageInfoLock.writeLock().lock();
try {
mSimIsReadyButNotLoaded = simState == SIM_STATE_READY;
} finally {
mPrivilegedPackageInfoLock.writeLock().unlock();
}
// Only include the UICC rules if the SIM is fully loaded
if (simState == SIM_STATE_LOADED) {
mLocalLog.log("SIM fully loaded, handleUiccAccessRulesLoaded.");
handleUiccAccessRulesLoaded();
} else {
if (!mUiccRules.isEmpty()
&& mClearUiccRulesUptimeMillis == CLEAR_UICC_RULE_NOT_SCHEDULED) {
mClearUiccRulesUptimeMillis =
SystemClock.uptimeMillis() + CLEAR_UICC_RULES_DELAY_MILLIS;
sendMessageAtTime(obtainMessage(ACTION_CLEAR_UICC_RULES),
mClearUiccRulesUptimeMillis);
mLocalLog.log("SIM is gone, simState=" + TelephonyManager.simStateToString(simState)
+ ". Delay " + TimeUnit.MILLISECONDS.toSeconds(
CLEAR_UICC_RULES_DELAY_MILLIS) + " seconds to clear UICC rules.");
} else {
mLocalLog.log(
"Ignore SIM gone event while UiccRules is empty or waiting to be emptied.");
}
}
}
private void handleUiccAccessRulesLoaded() {
mClearUiccRulesUptimeMillis = CLEAR_UICC_RULE_NOT_SCHEDULED;
removeMessages(ACTION_CLEAR_UICC_RULES);
List<UiccAccessRule> updatedUiccRules = getSimRules();
mLocalLog.log("UiccAccessRules loaded:"
+ " updated SIM-loaded rules=" + updatedUiccRules);
maybeUpdateRulesAndNotifyRegistrants(mUiccRules, updatedUiccRules);
}
/** Called when UiccAccessRules has been loaded */
public void onUiccAccessRulesLoaded() {
sendEmptyMessage(ACTION_UICC_ACCESS_RULES_LOADED);
}
private void handleClearUiccRules() {
mClearUiccRulesUptimeMillis = CLEAR_UICC_RULE_NOT_SCHEDULED;
removeMessages(ACTION_CLEAR_UICC_RULES);
maybeUpdateRulesAndNotifyRegistrants(mUiccRules, Collections.EMPTY_LIST);
}
@NonNull
private List<UiccAccessRule> getSimRules() {
if (!mTelephonyManager.hasIccCard(mPhone.getPhoneId())) {
return Collections.EMPTY_LIST;
}
UiccPort uiccPort = mPhone.getUiccPort();
if (uiccPort == null) {
Rlog.w(
TAG,
"Null UiccPort, but hasIccCard was present for phoneId " + mPhone.getPhoneId());
return Collections.EMPTY_LIST;
}
UiccProfile uiccProfile = uiccPort.getUiccProfile();
if (uiccProfile == null) {
Rlog.w(
TAG,
"Null UiccProfile, but hasIccCard was true for phoneId " + mPhone.getPhoneId());
return Collections.EMPTY_LIST;
}
return uiccProfile.getCarrierPrivilegeAccessRules();
}
private void handlePackageAddedReplacedOrChanged(@Nullable String pkgName) {
if (pkgName == null) return;
PackageInfo pkg;
try {
pkg = mPackageManager.getPackageInfo(pkgName, INSTALLED_PACKAGES_QUERY_FLAGS);
} catch (NameNotFoundException e) {
Rlog.e(TAG, "Error getting installed package: " + pkgName, e);
return;
}
updateCertsForPackage(pkg);
// Invalidate cache because this may be a package already on the device but getting
// installed for a user it wasn't installed in before, which means there will be an
// additional UID.
getUidsForPackage(pkg.packageName, /* invalidateCache= */ true);
if (VDBG) {
Rlog.d(TAG, "Package added/replaced/changed:"
+ " pkg=" + Rlog.pii(TAG, pkgName)
+ " cert hashes=" + mInstalledPackageCerts.get(pkgName));
}
maybeUpdatePrivilegedPackagesAndNotifyRegistrants();
}
private void updateCertsForPackage(@NonNull PackageInfo pkg) {
Set<String> certs = new ArraySet<>(1);
List<Signature> signatures = UiccAccessRule.getSignatures(pkg);
for (Signature signature : signatures) {
byte[] sha1 = UiccAccessRule.getCertHash(signature, SHA_1);
certs.add(IccUtils.bytesToHexString(sha1).toUpperCase(Locale.ROOT));
byte[] sha256 = UiccAccessRule.getCertHash(signature, SHA_256);
certs.add(IccUtils.bytesToHexString(sha256).toUpperCase(Locale.ROOT));
}
mInstalledPackageCerts.put(pkg.packageName, certs);
}
private void handlePackageRemovedOrDisabledByUser(@Nullable String pkgName) {
if (pkgName == null) return;
if (mInstalledPackageCerts.remove(pkgName) == null || mCachedUids.remove(pkgName) == null) {
Rlog.e(TAG, "Unknown package was uninstalled or disabled by user: " + pkgName);
return;
}
if (VDBG) {
Rlog.d(TAG, "Package removed or disabled by user: pkg=" + Rlog.pii(TAG, pkgName));
}
maybeUpdatePrivilegedPackagesAndNotifyRegistrants();
}
private void handleInitializeTracker() {
// Cache CarrierConfig rules
mCarrierConfigRules.addAll(getCarrierConfigRules(mPhone.getSubId()));
// Cache SIM rules
mUiccRules.addAll(getSimRules());
// Cache all installed packages and their certs
refreshInstalledPackageCache();
// Okay because no registrants exist yet
maybeUpdatePrivilegedPackagesAndNotifyRegistrants();
String msg = "Initializing state:"
+ " CarrierConfig rules=" + mCarrierConfigRules
+ " SIM-loaded rules=" + mUiccRules;
if (VDBG) {
msg +=
" installed pkgs="
+ getObfuscatedPackages(
mInstalledPackageCerts.entrySet(),
e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue());
}
mLocalLog.log(msg);
}
private void refreshInstalledPackageCache() {
List<PackageInfo> installedPackages =
mPackageManager.getInstalledPackagesAsUser(
INSTALLED_PACKAGES_QUERY_FLAGS, UserHandle.SYSTEM.getIdentifier());
for (PackageInfo pkg : installedPackages) {
updateCertsForPackage(pkg);
// This may be unnecessary before initialization, but invalidate the cache all the time
// just in case to ensure consistency.
getUidsForPackage(pkg.packageName, /* invalidateCache= */ true);
}
}
@NonNull
private static <T> String getObfuscatedPackages(
@NonNull Collection<T> packageNames, @NonNull Function<T, String> obfuscator) {
StringJoiner obfuscated = new StringJoiner(", ", "{", "}");
for (T packageName : packageNames) {
obfuscated.add(obfuscator.apply(packageName));
}
return obfuscated.toString();
}
private void maybeUpdateRulesAndNotifyRegistrants(@NonNull List<UiccAccessRule> currentRules,
@NonNull List<UiccAccessRule> updatedRules) {
if (currentRules.equals(updatedRules)) return;
currentRules.clear();
currentRules.addAll(updatedRules);
maybeUpdatePrivilegedPackagesAndNotifyRegistrants();
}
private void maybeUpdatePrivilegedPackagesAndNotifyRegistrants() {
PrivilegedPackageInfo currentPrivilegedPackageInfo =
getCurrentPrivilegedPackagesForAllUsers();
boolean carrierPrivilegesPackageNamesChanged;
boolean carrierPrivilegesUidsChanged;
boolean carrierServiceChanged;
mPrivilegedPackageInfoLock.readLock().lock();
try {
if (mPrivilegedPackageInfo.equals(currentPrivilegedPackageInfo)) return;
mLocalLog.log("Privileged packages info changed. New state = "
+ currentPrivilegedPackageInfo);
carrierPrivilegesPackageNamesChanged =
!currentPrivilegedPackageInfo.mPackageNames.equals(
mPrivilegedPackageInfo.mPackageNames);
carrierPrivilegesUidsChanged =
!currentPrivilegedPackageInfo.mUids.equals(mPrivilegedPackageInfo.mUids);
carrierServiceChanged = !currentPrivilegedPackageInfo.mCarrierService.equals(
mPrivilegedPackageInfo.mCarrierService);
} finally {
mPrivilegedPackageInfoLock.readLock().unlock();
}
mPrivilegedPackageInfoLock.writeLock().lock();
try {
mPrivilegedPackageInfo = currentPrivilegedPackageInfo;
} finally {
mPrivilegedPackageInfoLock.writeLock().unlock();
}
mPrivilegedPackageInfoLock.readLock().lock();
try {
if (carrierPrivilegesPackageNamesChanged || carrierPrivilegesUidsChanged) {
mTelephonyRegistryManager.notifyCarrierPrivilegesChanged(
mPhone.getPhoneId(),
Collections.unmodifiableSet(mPrivilegedPackageInfo.mPackageNames),
Collections.unmodifiableSet(mPrivilegedPackageInfo.mUids));
}
if (carrierServiceChanged) {
mTelephonyRegistryManager.notifyCarrierServiceChanged(mPhone.getPhoneId(),
mPrivilegedPackageInfo.mCarrierService.first,
mPrivilegedPackageInfo.mCarrierService.second);
}
} finally {
mPrivilegedPackageInfoLock.readLock().unlock();
}
// Update set of enabled carrier apps now that the privilege rules may have changed.
ActivityManager am = mContext.getSystemService(ActivityManager.class);
CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
mTelephonyManager, am.getCurrentUser(), mContext);
}
@NonNull
private PrivilegedPackageInfo getCurrentPrivilegedPackagesForAllUsers() {
Set<String> carrierServiceEligiblePackages = new ArraySet<>();
Set<String> privilegedPackageNames = new ArraySet<>();
Set<Integer> privilegedUids = new ArraySet<>();
for (Map.Entry<String, Set<String>> e : mInstalledPackageCerts.entrySet()) {
final int priv = getPackagePrivilegedStatus(e.getKey(), e.getValue());
switch (priv) {
case PACKAGE_PRIVILEGED_FROM_SIM:
case PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE: // fallthrough
carrierServiceEligiblePackages.add(e.getKey());
// fallthrough
case PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG:
privilegedPackageNames.add(e.getKey());
privilegedUids.addAll(
getUidsForPackage(e.getKey(), /* invalidateCache= */ false));
}
}
return new PrivilegedPackageInfo(
privilegedPackageNames,
privilegedUids,
getCarrierService(carrierServiceEligiblePackages));
}
/**
* Returns the privilege status of the provided package.
*
* <p>Returned privilege status depends on whether a package matches the certificates from
* carrier config, from test overrides or from certificates stored on the SIM.
*/
private int getPackagePrivilegedStatus(@NonNull String pkgName, @NonNull Set<String> certs) {
// Double-nested for loops, but each collection should contain at most 2 elements in nearly
// every case.
// TODO(b/184382310) find a way to speed this up
for (String cert : certs) {
// Non-null (whether empty or not) test override rule will ignore the UICC and CC rules
if (mTestOverrideRules != null) {
for (UiccAccessRule rule : mTestOverrideRules) {
if (rule.matches(cert, pkgName)) {
return PACKAGE_PRIVILEGED_FROM_SIM;
}
}
} else {
for (UiccAccessRule rule : mUiccRules) {
if (rule.matches(cert, pkgName)) {
return PACKAGE_PRIVILEGED_FROM_SIM;
}
}
for (UiccAccessRule rule : mCarrierConfigRules) {
if (rule.matches(cert, pkgName)) {
return pkgName.equals(mTestOverrideCarrierServicePackage)
? PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE
: PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG;
}
}
}
}
return PACKAGE_NOT_PRIVILEGED;
}
@NonNull
private Set<Integer> getUidsForPackage(@NonNull String pkgName, boolean invalidateCache) {
if (invalidateCache) {
mCachedUids.remove(pkgName);
}
if (mCachedUids.containsKey(pkgName)) {
return mCachedUids.get(pkgName);
}
Set<Integer> uids = new ArraySet<>(1);
List<UserInfo> users = mUserManager.getUsers();
for (UserInfo user : users) {
int userId = user.getUserHandle().getIdentifier();
try {
uids.add(mPackageManager.getPackageUidAsUser(pkgName, userId));
} catch (NameNotFoundException exception) {
// Didn't find package. Continue looking at other packages
Rlog.e(TAG, "Unable to find uid for package " + pkgName + " and user " + userId);
}
}
mCachedUids.put(pkgName, uids);
return uids;
}
private int getPackageUid(@Nullable String pkgName) {
int uid = Process.INVALID_UID;
try {
uid = mPackageManager.getPackageUid(pkgName, /* flags= */0);
} catch (NameNotFoundException e) {
Rlog.e(TAG, "Unable to find uid for package " + pkgName);
}
return uid;
}
/**
* Dump the local log buffer and other internal state of CarrierPrivilegesTracker.
*/
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("CarrierPrivilegesTracker - phoneId: " + mPhone.getPhoneId());
pw.println("CarrierPrivilegesTracker - Log Begin ----");
mLocalLog.dump(fd, pw, args);
pw.println("CarrierPrivilegesTracker - Log End ----");
mPrivilegedPackageInfoLock.readLock().lock();
try {
pw.println(
"CarrierPrivilegesTracker - Privileged package info: "
+ mPrivilegedPackageInfo);
pw.println("mSimIsReadyButNotLoaded: " + mSimIsReadyButNotLoaded);
} finally {
mPrivilegedPackageInfoLock.readLock().unlock();
}
pw.println("CarrierPrivilegesTracker - Test-override rules: " + mTestOverrideRules);
pw.println("CarrierPrivilegesTracker - SIM-loaded rules: " + mUiccRules);
pw.println("CarrierPrivilegesTracker - Carrier config rules: " + mCarrierConfigRules);
if (VDBG) {
pw.println(
"CarrierPrivilegesTracker - Obfuscated Pkgs + Certs: "
+ getObfuscatedPackages(
mInstalledPackageCerts.entrySet(),
e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue()));
}
pw.println("mClearUiccRulesUptimeMillis: " + mClearUiccRulesUptimeMillis);
}
/**
* Set test carrier privilege rules which will override the actual rules on both Carrier Config
* and SIM.
*
* <p>{@code carrierPrivilegeRules} can be null, in which case the rules on the Carrier Config
* and SIM will be used and any previous overrides will be cleared.
*
* @see TelephonyManager#setCarrierTestOverride
*/
public void setTestOverrideCarrierPrivilegeRules(@Nullable String carrierPrivilegeRules) {
sendMessage(obtainMessage(ACTION_SET_TEST_OVERRIDE_RULE, carrierPrivilegeRules));
}
/**
* Override the carrier provisioning package, if it exists.
*
* <p>This API is to be used ONLY for testing, and requires the provided package to be carrier
* privileged. While this override is set, ONLY the specified package will be considered
* eligible to be bound as the carrier provisioning package, and any existing bindings will be
* terminated.
*
* @param carrierServicePackage the package to be used as the overridden carrier service
* package, or {@code null} to reset override
* @see TelephonyManager#setCarrierServicePackageOverride
*/
public void setTestOverrideCarrierServicePackage(@Nullable String carrierServicePackage) {
sendMessage(obtainMessage(
ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE, carrierServicePackage));
}
private void handleSetTestOverrideCarrierServicePackage(
@Nullable String carrierServicePackage) {
mTestOverrideCarrierServicePackage = carrierServicePackage;
refreshInstalledPackageCache();
maybeUpdatePrivilegedPackagesAndNotifyRegistrants();
}
private void handleSetTestOverrideRules(@Nullable String carrierPrivilegeRules) {
if (carrierPrivilegeRules == null) {
mTestOverrideRules = null;
} else if (carrierPrivilegeRules.isEmpty()) {
mTestOverrideRules = Collections.emptyList();
} else {
mTestOverrideRules = Arrays.asList(UiccAccessRule.decodeRulesFromCarrierConfig(
new String[]{carrierPrivilegeRules}));
// TODO(b/215239409): remove the additional cache refresh for test override cases.
// Test override doesn't respect if the package for the specified cert has been removed
// or hidden since initialization. Refresh the cache again to get the pkg/uid with the
// best effort.
refreshInstalledPackageCache();
}
maybeUpdatePrivilegedPackagesAndNotifyRegistrants();
}
/** Backing of {@link TelephonyManager#checkCarrierPrivilegesForPackage}. */
public @CarrierPrivilegeStatus int getCarrierPrivilegeStatusForPackage(
@Nullable String packageName) {
if (packageName == null) return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
// TODO(b/205736323) consider if/how we want to account for the RULES_NOT_LOADED and
// ERROR_LOADING_RULES constants. Technically those will never be returned today since those
// results are only from the SIM rules, but the CC rules' result (which never has these
// errors) always supersede them unless something goes super wrong when getting CC.
mPrivilegedPackageInfoLock.readLock().lock();
try {
if (mSimIsReadyButNotLoaded) {
return CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
} else if (mPrivilegedPackageInfo.mPackageNames.contains(packageName)) {
return CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
} else {
return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
}
} finally {
mPrivilegedPackageInfoLock.readLock().unlock();
}
}
/** Backing of {@link TelephonyManager#getPackagesWithCarrierPrivileges}. */
@NonNull
public Set<String> getPackagesWithCarrierPrivileges() {
mPrivilegedPackageInfoLock.readLock().lock();
try {
return mSimIsReadyButNotLoaded ? Collections.emptySet() :
Collections.unmodifiableSet(mPrivilegedPackageInfo.mPackageNames);
} finally {
mPrivilegedPackageInfoLock.readLock().unlock();
}
}
/**
* Backing of {@link TelephonyManager#hasCarrierPrivileges} and {@link
* TelephonyManager#getCarrierPrivilegeStatus(int)}.
*/
public @CarrierPrivilegeStatus int getCarrierPrivilegeStatusForUid(int uid) {
// TODO(b/205736323) consider if/how we want to account for the RULES_NOT_LOADED and
// ERROR_LOADING_RULES constants. Technically those will never be returned today since those
// results are only from the SIM rules, but the CC rules' result (which never has these
// errors) always supersede them unless something goes super wrong when getting CC.
mPrivilegedPackageInfoLock.readLock().lock();
try {
if (mSimIsReadyButNotLoaded) {
return CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
} else if (mPrivilegedPackageInfo.mUids.contains(uid)) {
return CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
} else {
return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
}
} finally {
mPrivilegedPackageInfoLock.readLock().unlock();
}
}
/**
* Backing of {@link TelephonyManager#getCarrierServicePackageName()} and
* {@link TelephonyManager#getCarrierServicePackageNameForLogicalSlot(int)}
*/
@Nullable
public String getCarrierServicePackageName() {
// Return the cached one if present, it is fast and safe (no IPC call to PackageManager)
mPrivilegedPackageInfoLock.readLock().lock();
try {
// If SIM is READY but not LOADED, neither the cache nor the queries below are reliable,
// we should return null for this transient state for security/privacy's concern.
if (mSimIsReadyButNotLoaded) return null;
return mPrivilegedPackageInfo.mCarrierService.first;
} finally {
mPrivilegedPackageInfoLock.readLock().unlock();
}
// Do NOT query package manager, mPrivilegedPackageInfo.mCarrierService has maintained the
// latest CarrierService info. Querying PM will not get better result.
}
/**
* @return The UID of carrier service package. {@link Process#INVALID_UID} if not found.
*/
public int getCarrierServicePackageUid() {
mPrivilegedPackageInfoLock.readLock().lock();
try {
if (mSimIsReadyButNotLoaded) return Process.INVALID_UID;
return mPrivilegedPackageInfo.mCarrierService.second;
} finally {
mPrivilegedPackageInfoLock.readLock().unlock();
}
}
/**
* Backing of {@link TelephonyManager#getCarrierPackageNamesForIntent} and {@link
* TelephonyManager#getCarrierPackageNamesForIntentAndPhone}.
*/
@NonNull
public List<String> getCarrierPackageNamesForIntent(@NonNull Intent intent) {
mPrivilegedPackageInfoLock.readLock().lock();
try {
if (mSimIsReadyButNotLoaded) return Collections.emptyList();
} finally {
mPrivilegedPackageInfoLock.readLock().unlock();
}
// Do the PackageManager queries before we take the lock, as these are the longest-running
// pieces of this method and don't depend on the set of carrier apps.
List<ResolveInfo> resolveInfos = new ArrayList<>();
resolveInfos.addAll(mPackageManager.queryBroadcastReceivers(intent, 0));
resolveInfos.addAll(mPackageManager.queryIntentActivities(intent, 0));
resolveInfos.addAll(mPackageManager.queryIntentServices(intent, 0));
resolveInfos.addAll(mPackageManager.queryIntentContentProviders(intent, 0));
// Now actually check which of the resolved packages have carrier privileges.
mPrivilegedPackageInfoLock.readLock().lock();
try {
// Check mSimIsReadyButNotLoaded again here since the PackageManager queries above are
// pretty time-consuming, mSimIsReadyButNotLoaded state may change since last check
if (mSimIsReadyButNotLoaded) return Collections.emptyList();
Set<String> packageNames = new ArraySet<>(); // For deduping purposes
for (ResolveInfo resolveInfo : resolveInfos) {
String packageName = getPackageName(resolveInfo);
if (packageName != null && CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
== getCarrierPrivilegeStatusForPackage(packageName)) {
packageNames.add(packageName);
}
}
return new ArrayList<>(packageNames);
} finally {
mPrivilegedPackageInfoLock.readLock().unlock();
}
}
@Nullable
private static String getPackageName(@NonNull ResolveInfo resolveInfo) {
// Note: activityInfo covers both activities + broadcast receivers
if (resolveInfo.activityInfo != null) return resolveInfo.activityInfo.packageName;
if (resolveInfo.serviceInfo != null) return resolveInfo.serviceInfo.packageName;
if (resolveInfo.providerInfo != null) return resolveInfo.providerInfo.packageName;
return null;
}
@NonNull
private Pair<String, Integer> getCarrierService(@NonNull Set<String> simPrivilegedPackages) {
List<ResolveInfo> carrierServiceResolveInfos = mPackageManager.queryIntentServices(
new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), /* flags= */ 0);
String carrierServicePackageName = null;
for (ResolveInfo resolveInfo : carrierServiceResolveInfos) {
String packageName = getPackageName(resolveInfo);
if (mTestOverrideCarrierServicePackage != null
&& !mTestOverrideCarrierServicePackage.equals(packageName)) {
continue;
}
if (simPrivilegedPackages.contains(packageName)) {
carrierServicePackageName = packageName;
break;
}
}
return carrierServicePackageName == null
? new Pair<>(null, Process.INVALID_UID)
: new Pair<>(carrierServicePackageName, getPackageUid(carrierServicePackageName));
}
}