Merge changes from topic "phonestatelistener-limit-rvc-dev" into rvc-dev am: 952697b886 am: db8281ff75

Change-Id: Idc2c2537f2fd175f1d3fbba62935b463917dc420
This commit is contained in:
Hall Liu
2020-03-28 07:10:24 +00:00
committed by Automerger Merge Worker
5 changed files with 116 additions and 6 deletions

View File

@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Binder;
import android.os.Build;
@@ -64,6 +65,43 @@ public class PhoneStateListener {
private static final String LOG_TAG = "PhoneStateListener";
private static final boolean DBG = false; // STOPSHIP if true
/**
* Experiment flag to set the per-pid registration limit for PhoneStateListeners
*
* Limit on registrations of {@link PhoneStateListener}s on a per-pid
* basis. When this limit is exceeded, any calls to {@link TelephonyManager#listen} will fail
* with an {@link IllegalStateException}.
*
* {@link android.os.Process#PHONE_UID}, {@link android.os.Process#SYSTEM_UID}, and the uid that
* TelephonyRegistry runs under are exempt from this limit.
*
* If the value of the flag is less than 1, enforcement of the limit will be disabled.
* @hide
*/
public static final String FLAG_PER_PID_REGISTRATION_LIMIT =
"phone_state_listener_per_pid_registration_limit";
/**
* Default value for the per-pid registation limit.
* See {@link #FLAG_PER_PID_REGISTRATION_LIMIT}.
* @hide
*/
public static final int DEFAULT_PER_PID_REGISTRATION_LIMIT = 50;
/**
* This change enables a limit on the number of {@link PhoneStateListener} objects any process
* may register via {@link TelephonyManager#listen}. The default limit is 50, which may change
* via remote device config updates.
*
* This limit is enforced via an {@link IllegalStateException} thrown from
* {@link TelephonyManager#listen} when the offending process attempts to register one too many
* listeners.
*
* @hide
*/
@ChangeId
public static final long PHONE_STATE_LISTENER_LIMIT_CHANGE_ID = 150880553L;
/**
* Stop listening for updates.
*

View File

@@ -27,6 +27,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -39,8 +40,10 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.telephony.Annotation;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.DataFailureCause;
@@ -177,8 +180,38 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
/**
* Wrapper class to facilitate testing -- encapsulates bits of configuration that are
* normally fetched from static methods with many dependencies.
*/
public static class ConfigurationProvider {
/**
* @return The per-pid registration limit for PhoneStateListeners, as set from DeviceConfig
* @noinspection ConstantConditions
*/
public int getRegistrationLimit() {
return Binder.withCleanCallingIdentity(() ->
DeviceConfig.getInt(DeviceConfig.NAMESPACE_TELEPHONY,
PhoneStateListener.FLAG_PER_PID_REGISTRATION_LIMIT,
PhoneStateListener.DEFAULT_PER_PID_REGISTRATION_LIMIT));
}
/**
* @param uid uid to check
* @return Whether enforcement of the per-pid registation limit for PhoneStateListeners is
* enabled in PlatformCompat for the given uid.
* @noinspection ConstantConditions
*/
public boolean isRegistrationLimitEnabledInPlatformCompat(int uid) {
return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
PhoneStateListener.PHONE_STATE_LISTENER_LIMIT_CHANGE_ID, uid));
}
}
private final Context mContext;
private ConfigurationProvider mConfigurationProvider;
// access should be inside synchronized (mRecords) for these two fields
private final ArrayList<IBinder> mRemoveList = new ArrayList<IBinder>();
private final ArrayList<Record> mRecords = new ArrayList<Record>();
@@ -506,10 +539,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
// handler before they get to app code.
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public TelephonyRegistry(Context context) {
public TelephonyRegistry(Context context, ConfigurationProvider configurationProvider) {
CellLocation location = CellLocation.getEmpty();
mContext = context;
mConfigurationProvider = configurationProvider;
mBatteryStats = BatteryStatsService.getService();
int numPhones = getTelephonyManager().getActiveModemCount();
@@ -605,7 +639,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
synchronized (mRecords) {
// register
IBinder b = callback.asBinder();
Record r = add(b);
Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), false);
if (r == null) {
return;
@@ -659,7 +693,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
synchronized (mRecords) {
// register
IBinder b = callback.asBinder();
Record r = add(b);
Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), false);
if (r == null) {
return;
@@ -789,7 +823,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
synchronized (mRecords) {
// register
IBinder b = callback.asBinder();
Record r = add(b);
boolean doesLimitApply =
Binder.getCallingUid() != Process.SYSTEM_UID
&& Binder.getCallingUid() != Process.PHONE_UID
&& Binder.getCallingUid() != Process.myUid();
Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply);
if (r == null) {
return;
@@ -1084,18 +1122,44 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
return record.canReadCallLog() ? mCallIncomingNumber[phoneId] : "";
}
private Record add(IBinder binder) {
private Record add(IBinder binder, int callingUid, int callingPid, boolean doesLimitApply) {
Record r;
synchronized (mRecords) {
final int N = mRecords.size();
// While iterating through the records, keep track of how many we have from this pid.
int numRecordsForPid = 0;
for (int i = 0; i < N; i++) {
r = mRecords.get(i);
if (binder == r.binder) {
// Already existed.
return r;
}
if (r.callerPid == callingPid) {
numRecordsForPid++;
}
}
// If we've exceeded the limit for registrations, log an error and quit.
int registrationLimit = mConfigurationProvider.getRegistrationLimit();
if (doesLimitApply
&& registrationLimit >= 1
&& numRecordsForPid >= registrationLimit) {
String errorMsg = "Pid " + callingPid + " has exceeded the number of permissible"
+ "registered listeners. Ignoring request to add.";
loge(errorMsg);
if (mConfigurationProvider
.isRegistrationLimitEnabledInPlatformCompat(callingUid)) {
throw new IllegalStateException(errorMsg);
}
} else if (doesLimitApply && numRecordsForPid
>= PhoneStateListener.DEFAULT_PER_PID_REGISTRATION_LIMIT / 2) {
// Log the warning independently of the dynamically set limit -- apps shouldn't be
// doing this regardless of whether we're throwing them an exception for it.
Rlog.w(TAG, "Pid " + callingPid + " has exceeded half the number of permissible"
+ "registered listeners. Now at " + numRecordsForPid);
}
r = new Record();
r.binder = binder;
r.deathRecipient = new TelephonyRegistryDeathRecipient(binder);

View File

@@ -1063,7 +1063,8 @@ public final class SystemServer {
t.traceEnd();
t.traceBegin("StartTelephonyRegistry");
telephonyRegistry = new TelephonyRegistry(context);
telephonyRegistry = new TelephonyRegistry(
context, new TelephonyRegistry.ConfigurationProvider());
ServiceManager.addService("telephony.registry", telephonyRegistry);
t.traceEnd();

View File

@@ -5577,6 +5577,10 @@ public class TelephonyManager {
* call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
* {@link SecurityException} will be thrown otherwise.
*
* This API should be used sparingly -- large numbers of listeners will cause system
* instability. If a process has registered too many listeners without unregistering them, it
* may encounter an {@link IllegalStateException} when trying to register more listeners.
*
* @param listener The {@link PhoneStateListener} object to register
* (or unregister)
* @param events The telephony state(s) of interest to the listener,

View File

@@ -205,6 +205,7 @@ import android.os.UserManager;
import android.provider.Settings;
import android.security.KeyStore;
import android.system.Os;
import android.telephony.TelephonyManager;
import android.test.mock.MockContentResolver;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -347,6 +348,7 @@ public class ConnectivityServiceTest {
@Mock IBinder mIBinder;
@Mock LocationManager mLocationManager;
@Mock AppOpsManager mAppOpsManager;
@Mock TelephonyManager mTelephonyManager;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -433,6 +435,7 @@ public class ConnectivityServiceTest {
if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
return super.getSystemService(name);
}