summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author rambowang <rambowang@google.com> 2022-10-02 11:19:21 -0500
committer Rambo Wang <rambowang@google.com> 2023-03-03 17:16:30 +0000
commitbc4480ea6ef27a70bf56c3ca678844d886ca0d87 (patch)
tree4e578b6df0d67184bb0bced964b7c4a53d40f897
parent5b326816cbe2a6f64475df569a0fecec033c9e4d (diff)
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 Merged-In: I782e3aa2312886279cc7f449fa3a8238b4115407 Change-Id: I782e3aa2312886279cc7f449fa3a8238b4115407 (cherry picked from commit c5e36e1c1f83cc130a96498f3117090065fa0531)
-rw-r--r--core/api/current.txt6
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java101
-rw-r--r--core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl21
-rw-r--r--core/java/com/android/internal/telephony/ITelephonyRegistry.aidl5
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java83
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java83
6 files changed, 299 insertions, 0 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index c381b16dc414..f2a48c0fd2c2 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -41276,6 +41276,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
@@ -41590,6 +41592,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.";
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index a3696e398668..4bcdf0d0d50d 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -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) {
@@ -1409,4 +1420,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();
+ }
+ }
}
diff --git a/core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl b/core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl
new file mode 100644
index 000000000000..0f7ab0a3d4fb
--- /dev/null
+++ b/core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl
@@ -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);
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index c7fa757ac0b7..747c40df9492 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -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);
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 2652ebec5255..7eb2cf78bb69 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -91,6 +91,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;
@@ -154,6 +155,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback;
ICarrierPrivilegesCallback carrierPrivilegesCallback;
+ ICarrierConfigChangeListener carrierConfigChangeListener;
int callerUid;
int callerPid;
@@ -182,6 +184,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
return carrierPrivilegesCallback != null;
}
+ boolean matchCarrierConfigChangeListener() {
+ return carrierConfigChangeListener != null;
+ }
+
boolean canReadCallLog() {
try {
return TelephonyPermissions.checkReadCallLog(
@@ -200,6 +206,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ " onOpportunisticSubscriptionsChangedListenererCallback="
+ onOpportunisticSubscriptionsChangedListenerCallback
+ " carrierPrivilegesCallback=" + carrierPrivilegesCallback
+ + " carrierConfigChangeListener=" + carrierConfigChangeListener
+ " subId=" + subId + " phoneId=" + phoneId + " events=" + eventList + "}";
}
}
@@ -2956,6 +2963,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) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c98ea7b16c48..fc7298e1e65c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -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;
@@ -53,6 +54,7 @@ import com.android.telephony.Rlog;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
@@ -9715,4 +9717,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);
+ }
}