Introduce CarrierConfigChangeListener API to monitor CC change

The new interface is used for system components and 3rd party apps
to monitor carrier config changes, replacing the current carrier config
change broadcast receivers which has known performance issue.

Bug: 244087782
Test: atest CarrierConfigManagerTest

Change-Id: I782e3aa2312886279cc7f449fa3a8238b4115407
This commit is contained in:
rambowang
2022-10-02 11:19:21 -05:00
parent 365825e2c5
commit c5e36e1c1f
6 changed files with 298 additions and 0 deletions

View File

@@ -42009,6 +42009,8 @@ package android.telephony {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, "carrier privileges"}) public android.os.PersistableBundle getConfigForSubId(int, @NonNull java.lang.String...);
method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyConfigChangedForSubId(int);
method public void registerCarrierConfigChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.CarrierConfigManager.CarrierConfigChangeListener);
method public void unregisterCarrierConfigChangeListener(@NonNull android.telephony.CarrierConfigManager.CarrierConfigChangeListener);
field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final int CARRIER_NR_AVAILABILITY_NSA = 1; // 0x1
field public static final int CARRIER_NR_AVAILABILITY_SA = 2; // 0x2
@@ -42338,6 +42340,10 @@ package android.telephony {
field public static final String KEY_PREFIX = "bsf.";
}
public static interface CarrierConfigManager.CarrierConfigChangeListener {
method public void onCarrierConfigChanged(int, int, int, int);
}
public static final class CarrierConfigManager.Gps {
field public static final String KEY_PERSIST_LPP_MODE_BOOL = "gps.persist_lpp_mode_bool";
field public static final String KEY_PREFIX = "gps.";

View File

@@ -45,6 +45,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.listeners.ListenerExecutor;
import com.android.internal.telephony.ICarrierConfigChangeListener;
import com.android.internal.telephony.ICarrierPrivilegesCallback;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -54,8 +55,10 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@@ -89,6 +92,14 @@ public class TelephonyRegistryManager {
IOnSubscriptionsChangedListener> mOpportunisticSubscriptionChangedListenerMap
= new HashMap<>();
/**
* A mapping between {@link CarrierConfigManager.CarrierConfigChangeListener} and its callback
* ICarrierConfigChangeListener.
*/
private final ConcurrentHashMap<CarrierConfigManager.CarrierConfigChangeListener,
ICarrierConfigChangeListener>
mCarrierConfigChangeListenerMap = new ConcurrentHashMap<>();
/** @hide **/
public TelephonyRegistryManager(@NonNull Context context) {
@@ -1412,4 +1423,94 @@ public class TelephonyRegistryManager {
throw e.rethrowFromSystemServer();
}
}
/**
* Register a {@link android.telephony.CarrierConfigManager.CarrierConfigChangeListener} to get
* notification when carrier configurations have changed.
*
* @param executor The executor on which the callback will be executed.
* @param listener The CarrierConfigChangeListener to be registered with.
*/
public void addCarrierConfigChangedListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull CarrierConfigManager.CarrierConfigChangeListener listener) {
Objects.requireNonNull(executor, "Executor should be non-null.");
Objects.requireNonNull(listener, "Listener should be non-null.");
if (mCarrierConfigChangeListenerMap.get(listener) != null) {
Log.e(TAG, "registerCarrierConfigChangeListener: listener already present");
return;
}
ICarrierConfigChangeListener callback = new ICarrierConfigChangeListener.Stub() {
@Override
public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId,
int specificCarrierId) {
Log.d(TAG, "onCarrierConfigChanged call in ICarrierConfigChangeListener callback");
final long identify = Binder.clearCallingIdentity();
try {
executor.execute(() -> listener.onCarrierConfigChanged(slotIndex, subId,
carrierId, specificCarrierId));
} finally {
Binder.restoreCallingIdentity(identify);
}
}
};
try {
sRegistry.addCarrierConfigChangeListener(callback,
mContext.getOpPackageName(), mContext.getAttributionTag());
mCarrierConfigChangeListenerMap.put(listener, callback);
} catch (RemoteException re) {
// system server crashes
throw re.rethrowFromSystemServer();
}
}
/**
* Unregister to stop the notification when carrier configurations changed.
*
* @param listener The CarrierConfigChangeListener to be unregistered with.
*/
public void removeCarrierConfigChangedListener(
@NonNull CarrierConfigManager.CarrierConfigChangeListener listener) {
Objects.requireNonNull(listener, "Listener should be non-null.");
if (mCarrierConfigChangeListenerMap.get(listener) == null) {
Log.e(TAG, "removeCarrierConfigChangedListener: listener was not present");
return;
}
try {
sRegistry.removeCarrierConfigChangeListener(
mCarrierConfigChangeListenerMap.get(listener), mContext.getOpPackageName());
mCarrierConfigChangeListenerMap.remove(listener);
} catch (RemoteException re) {
// System sever crashes
throw re.rethrowFromSystemServer();
}
}
/**
* Notify the registrants the carrier configurations have changed.
*
* @param slotIndex The SIM slot index on which to monitor and get notification.
* @param subId The subscription on the SIM slot. May be
* {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
* @param carrierId The optional carrier Id, may be
* {@link TelephonyManager#UNKNOWN_CARRIER_ID}.
* @param specificCarrierId The optional specific carrier Id, may be {@link
* TelephonyManager#UNKNOWN_CARRIER_ID}.
*/
public void notifyCarrierConfigChanged(int slotIndex, int subId, int carrierId,
int specificCarrierId) {
// Only validate slotIndex, all others are optional and allowed to be invalid
if (!SubscriptionManager.isValidPhoneId(slotIndex)) {
Log.e(TAG, "notifyCarrierConfigChanged, ignored: invalid slotIndex " + slotIndex);
return;
}
try {
sRegistry.notifyCarrierConfigChanged(slotIndex, subId, carrierId, specificCarrierId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2022 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;
oneway interface ICarrierConfigChangeListener {
void onCarrierConfigChanged(int slotIndex, int subId, int carrierId, int specificCarrierId);
}

View File

@@ -32,6 +32,7 @@ import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.emergency.EmergencyNumber;
import com.android.internal.telephony.ICarrierConfigChangeListener;
import com.android.internal.telephony.ICarrierPrivilegesCallback;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -109,4 +110,8 @@ interface ITelephonyRegistry {
int phoneId, in List<String> privilegedPackageNames, in int[] privilegedUids);
void notifyCarrierServiceChanged(int phoneId, in String packageName, int uid);
void addCarrierConfigChangeListener(ICarrierConfigChangeListener listener,
String pkg, String featureId);
void removeCarrierConfigChangeListener(ICarrierConfigChangeListener listener, String pkg);
void notifyCarrierConfigChanged(int phoneId, int subId, int carrierId, int specificCarrierId);
}

View File

@@ -92,6 +92,7 @@ import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.ICarrierConfigChangeListener;
import com.android.internal.telephony.ICarrierPrivilegesCallback;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.IPhoneStateListener;
@@ -155,6 +156,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback;
ICarrierPrivilegesCallback carrierPrivilegesCallback;
ICarrierConfigChangeListener carrierConfigChangeListener;
int callerUid;
int callerPid;
@@ -183,6 +185,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
return carrierPrivilegesCallback != null;
}
boolean matchCarrierConfigChangeListener() {
return carrierConfigChangeListener != null;
}
boolean canReadCallLog() {
try {
return TelephonyPermissions.checkReadCallLog(
@@ -201,6 +207,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ " onOpportunisticSubscriptionsChangedListenererCallback="
+ onOpportunisticSubscriptionsChangedListenerCallback
+ " carrierPrivilegesCallback=" + carrierPrivilegesCallback
+ " carrierConfigChangeListener=" + carrierConfigChangeListener
+ " subId=" + subId + " phoneId=" + phoneId + " events=" + eventList + "}";
}
}
@@ -3044,6 +3051,82 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
@Override
public void addCarrierConfigChangeListener(ICarrierConfigChangeListener listener,
String pkg, String featureId) {
final int callerUserId = UserHandle.getCallingUserId();
mAppOps.checkPackage(Binder.getCallingUid(), pkg);
if (VDBG) {
log("addCarrierConfigChangeListener pkg=" + pii(pkg) + " uid=" + Binder.getCallingUid()
+ " myUserId=" + UserHandle.myUserId() + " callerUerId" + callerUserId
+ " listener=" + listener + " listener.asBinder=" + listener.asBinder());
}
synchronized (mRecords) {
IBinder b = listener.asBinder();
boolean doesLimitApply = doesLimitApplyForListeners(Binder.getCallingUid(),
Process.myUid());
Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply);
if (r == null) {
loge("Can not create Record instance!");
return;
}
r.context = mContext;
r.carrierConfigChangeListener = listener;
r.callingPackage = pkg;
r.callingFeatureId = featureId;
r.callerUid = Binder.getCallingUid();
r.callerPid = Binder.getCallingPid();
r.eventList = new ArraySet<>();
if (DBG) {
log("addCarrierConfigChangeListener: Register r=" + r);
}
}
}
@Override
public void removeCarrierConfigChangeListener(ICarrierConfigChangeListener listener,
String pkg) {
if (DBG) log("removeCarrierConfigChangeListener listener=" + listener + ", pkg=" + pkg);
mAppOps.checkPackage(Binder.getCallingUid(), pkg);
remove(listener.asBinder());
}
@Override
public void notifyCarrierConfigChanged(int phoneId, int subId, int carrierId,
int specificCarrierId) {
if (!validatePhoneId(phoneId)) {
throw new IllegalArgumentException("Invalid phoneId: " + phoneId);
}
if (!checkNotifyPermission("notifyCarrierConfigChanged")) {
loge("Caller has no notify permission!");
return;
}
if (VDBG) {
log("notifyCarrierConfigChanged: phoneId=" + phoneId + ", subId=" + subId
+ ", carrierId=" + carrierId + ", specificCarrierId=" + specificCarrierId);
}
synchronized (mRecords) {
mRemoveList.clear();
for (Record r : mRecords) {
// Listeners are "global", neither per-slot nor per-sub, so no idMatch check here
if (!r.matchCarrierConfigChangeListener()) {
continue;
}
try {
r.carrierConfigChangeListener.onCarrierConfigChanged(phoneId, subId, carrierId,
specificCarrierId);
} catch (RemoteException re) {
mRemoveList.add(r.binder);
}
}
handleRemoveListLocked();
}
}
@NeverCompile // Avoid size overhead of debugging code.
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {

View File

@@ -17,6 +17,7 @@
package android.telephony;
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -10337,4 +10338,85 @@ public class CarrierConfigManager {
configs.putPersistableBundle(key, (PersistableBundle) value);
}
}
/**
* Listener interface to get a notification when the carrier configurations have changed.
*
* Use this listener to receive timely updates when the carrier configuration changes. System
* components should prefer this listener over {@link #ACTION_CARRIER_CONFIG_CHANGED}
* whenever possible.
*
* To register the listener, call
* {@link #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)}.
* To unregister, call
* {@link #unregisterCarrierConfigChangeListener(CarrierConfigChangeListener)}.
*
* Note that on registration, registrants will NOT receive a notification on last carrier config
* change. Only carrier configs change AFTER the registration will be sent to registrants. And
* unlike {@link #ACTION_CARRIER_CONFIG_CHANGED}, notification wouldn't send when the device is
* unlocked. Registrants only receive the notification when there has been real carrier config
* changes.
*
* @see #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)
* @see #unregisterCarrierConfigChangeListener(CarrierConfigChangeListener)
* @see #ACTION_CARRIER_CONFIG_CHANGED
* @see #getConfig(String...)
* @see #getConfigForSubId(int, String...)
*/
public interface CarrierConfigChangeListener {
/**
* Called when carrier configurations have changed.
*
* @param logicalSlotIndex The logical SIM slot index on which to monitor and get
* notification. It is guaranteed to be valid.
* @param subscriptionId The subscription on the SIM slot. May be
* {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
* @param carrierId The optional carrier Id, may be
* {@link TelephonyManager#UNKNOWN_CARRIER_ID}.
* See {@link TelephonyManager#getSimCarrierId()}.
* @param specificCarrierId The optional fine-grained carrierId, may be {@link
* TelephonyManager#UNKNOWN_CARRIER_ID}. A specific carrierId may
* be different from the carrierId above in a MVNO scenario. See
* detail in {@link TelephonyManager#getSimSpecificCarrierId()}.
*/
void onCarrierConfigChanged(int logicalSlotIndex, int subscriptionId, int carrierId,
int specificCarrierId);
}
/**
* Register a {@link CarrierConfigChangeListener} to get a notification when carrier
* configurations have changed.
*
* @param executor The executor on which the listener will be called.
* @param listener The CarrierConfigChangeListener called when carrier configs has changed.
*/
public void registerCarrierConfigChangeListener(@NonNull @CallbackExecutor Executor executor,
@NonNull CarrierConfigChangeListener listener) {
Objects.requireNonNull(executor, "Executor should be non-null.");
Objects.requireNonNull(listener, "Listener should be non-null.");
TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class);
if (trm == null) {
throw new IllegalStateException("Telephony registry service is null");
}
trm.addCarrierConfigChangedListener(executor, listener);
}
/**
* Unregister the {@link CarrierConfigChangeListener} to stop notification on carrier
* configurations change.
*
* @param listener The CarrierConfigChangeListener which was registered with method
* {@link #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)}.
*/
public void unregisterCarrierConfigChangeListener(
@NonNull CarrierConfigChangeListener listener) {
Objects.requireNonNull(listener, "Listener should be non-null.");
TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class);
if (trm == null) {
throw new IllegalStateException("Telephony registry service is null");
}
trm.removeCarrierConfigChangedListener(listener);
}
}