Merge changes from topic "phonestatelistener-limit-rvc-dev" into rvc-dev am: 952697b886 am: db8281ff75 am: dbfecb3dda
Change-Id: I28992094e47ed11f5b29eb9f26732a406b9e11e9
This commit is contained in:
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.RequiresPermission;
|
import android.annotation.RequiresPermission;
|
||||||
import android.annotation.SystemApi;
|
import android.annotation.SystemApi;
|
||||||
import android.annotation.TestApi;
|
import android.annotation.TestApi;
|
||||||
|
import android.compat.annotation.ChangeId;
|
||||||
import android.compat.annotation.UnsupportedAppUsage;
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -64,6 +65,43 @@ public class PhoneStateListener {
|
|||||||
private static final String LOG_TAG = "PhoneStateListener";
|
private static final String LOG_TAG = "PhoneStateListener";
|
||||||
private static final boolean DBG = false; // STOPSHIP if true
|
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.
|
* Stop listening for updates.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.AppOpsManager;
|
import android.app.AppOpsManager;
|
||||||
|
import android.app.compat.CompatChanges;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -39,8 +40,10 @@ import android.os.Bundle;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.provider.DeviceConfig;
|
||||||
import android.telephony.Annotation;
|
import android.telephony.Annotation;
|
||||||
import android.telephony.Annotation.ApnType;
|
import android.telephony.Annotation.ApnType;
|
||||||
import android.telephony.Annotation.DataFailureCause;
|
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 final Context mContext;
|
||||||
|
|
||||||
|
private ConfigurationProvider mConfigurationProvider;
|
||||||
|
|
||||||
// access should be inside synchronized (mRecords) for these two fields
|
// access should be inside synchronized (mRecords) for these two fields
|
||||||
private final ArrayList<IBinder> mRemoveList = new ArrayList<IBinder>();
|
private final ArrayList<IBinder> mRemoveList = new ArrayList<IBinder>();
|
||||||
private final ArrayList<Record> mRecords = new ArrayList<Record>();
|
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.
|
// handler before they get to app code.
|
||||||
|
|
||||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||||
public TelephonyRegistry(Context context) {
|
public TelephonyRegistry(Context context, ConfigurationProvider configurationProvider) {
|
||||||
CellLocation location = CellLocation.getEmpty();
|
CellLocation location = CellLocation.getEmpty();
|
||||||
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
mConfigurationProvider = configurationProvider;
|
||||||
mBatteryStats = BatteryStatsService.getService();
|
mBatteryStats = BatteryStatsService.getService();
|
||||||
|
|
||||||
int numPhones = getTelephonyManager().getActiveModemCount();
|
int numPhones = getTelephonyManager().getActiveModemCount();
|
||||||
@@ -605,7 +639,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
|||||||
synchronized (mRecords) {
|
synchronized (mRecords) {
|
||||||
// register
|
// register
|
||||||
IBinder b = callback.asBinder();
|
IBinder b = callback.asBinder();
|
||||||
Record r = add(b);
|
Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), false);
|
||||||
|
|
||||||
if (r == null) {
|
if (r == null) {
|
||||||
return;
|
return;
|
||||||
@@ -659,7 +693,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
|||||||
synchronized (mRecords) {
|
synchronized (mRecords) {
|
||||||
// register
|
// register
|
||||||
IBinder b = callback.asBinder();
|
IBinder b = callback.asBinder();
|
||||||
Record r = add(b);
|
Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), false);
|
||||||
|
|
||||||
if (r == null) {
|
if (r == null) {
|
||||||
return;
|
return;
|
||||||
@@ -789,7 +823,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
|||||||
synchronized (mRecords) {
|
synchronized (mRecords) {
|
||||||
// register
|
// register
|
||||||
IBinder b = callback.asBinder();
|
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) {
|
if (r == null) {
|
||||||
return;
|
return;
|
||||||
@@ -1084,18 +1122,44 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
|
|||||||
return record.canReadCallLog() ? mCallIncomingNumber[phoneId] : "";
|
return record.canReadCallLog() ? mCallIncomingNumber[phoneId] : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private Record add(IBinder binder) {
|
private Record add(IBinder binder, int callingUid, int callingPid, boolean doesLimitApply) {
|
||||||
Record r;
|
Record r;
|
||||||
|
|
||||||
synchronized (mRecords) {
|
synchronized (mRecords) {
|
||||||
final int N = mRecords.size();
|
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++) {
|
for (int i = 0; i < N; i++) {
|
||||||
r = mRecords.get(i);
|
r = mRecords.get(i);
|
||||||
if (binder == r.binder) {
|
if (binder == r.binder) {
|
||||||
// Already existed.
|
// Already existed.
|
||||||
return r;
|
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 = new Record();
|
||||||
r.binder = binder;
|
r.binder = binder;
|
||||||
r.deathRecipient = new TelephonyRegistryDeathRecipient(binder);
|
r.deathRecipient = new TelephonyRegistryDeathRecipient(binder);
|
||||||
|
|||||||
@@ -1063,7 +1063,8 @@ public final class SystemServer {
|
|||||||
t.traceEnd();
|
t.traceEnd();
|
||||||
|
|
||||||
t.traceBegin("StartTelephonyRegistry");
|
t.traceBegin("StartTelephonyRegistry");
|
||||||
telephonyRegistry = new TelephonyRegistry(context);
|
telephonyRegistry = new TelephonyRegistry(
|
||||||
|
context, new TelephonyRegistry.ConfigurationProvider());
|
||||||
ServiceManager.addService("telephony.registry", telephonyRegistry);
|
ServiceManager.addService("telephony.registry", telephonyRegistry);
|
||||||
t.traceEnd();
|
t.traceEnd();
|
||||||
|
|
||||||
|
|||||||
@@ -5577,6 +5577,10 @@ public class TelephonyManager {
|
|||||||
* call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
|
* call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
|
||||||
* {@link SecurityException} will be thrown otherwise.
|
* {@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
|
* @param listener The {@link PhoneStateListener} object to register
|
||||||
* (or unregister)
|
* (or unregister)
|
||||||
* @param events The telephony state(s) of interest to the listener,
|
* @param events The telephony state(s) of interest to the listener,
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ import android.os.UserManager;
|
|||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.security.KeyStore;
|
import android.security.KeyStore;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
import android.test.mock.MockContentResolver;
|
import android.test.mock.MockContentResolver;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
@@ -347,6 +348,7 @@ public class ConnectivityServiceTest {
|
|||||||
@Mock IBinder mIBinder;
|
@Mock IBinder mIBinder;
|
||||||
@Mock LocationManager mLocationManager;
|
@Mock LocationManager mLocationManager;
|
||||||
@Mock AppOpsManager mAppOpsManager;
|
@Mock AppOpsManager mAppOpsManager;
|
||||||
|
@Mock TelephonyManager mTelephonyManager;
|
||||||
|
|
||||||
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
|
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
|
||||||
ArgumentCaptor.forClass(ResolverParamsParcel.class);
|
ArgumentCaptor.forClass(ResolverParamsParcel.class);
|
||||||
@@ -433,6 +435,7 @@ public class ConnectivityServiceTest {
|
|||||||
if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
|
if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
|
||||||
if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
|
if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
|
||||||
if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
|
if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
|
||||||
|
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
|
||||||
return super.getSystemService(name);
|
return super.getSystemService(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user