1051 lines
46 KiB
Java
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));
|
|
}
|
|
}
|