Add API to listen to opportunistic subscriptions change.
Add a new set of APIs so that caller can listen to
opportunistic subscriptions changes.
Bug: 92796390
Test: build
Change-Id: Iaa741141bf1bd04c4b1618d5bfeac733a0690ed8
Merged-In: Iaa741141bf1bd04c4b1618d5bfeac733a0690ed8
diff --git a/api/current.txt b/api/current.txt
index eed925d..fe8ee50 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -42440,6 +42440,7 @@
}
public class SubscriptionManager {
+ method public void addOnOpportunisticSubscriptionsChangedListener(java.util.concurrent.Executor, android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void addOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method public boolean canManageSubscription(android.telephony.SubscriptionInfo);
method public static deprecated android.telephony.SubscriptionManager from(android.content.Context);
@@ -42456,6 +42457,7 @@
method public static int[] getSubscriptionIds(int);
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public boolean isNetworkRoaming(int);
+ method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method public void setSubscriptionOverrideCongested(int, boolean, long);
method public void setSubscriptionOverrideUnmetered(int, boolean, long);
@@ -42471,6 +42473,11 @@
field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
}
+ public static class SubscriptionManager.OnOpportunisticSubscriptionsChangedListener {
+ ctor public SubscriptionManager.OnOpportunisticSubscriptionsChangedListener();
+ method public void onOpportunisticSubscriptionsChanged();
+ }
+
public static class SubscriptionManager.OnSubscriptionsChangedListener {
ctor public SubscriptionManager.OnSubscriptionsChangedListener();
method public void onSubscriptionsChanged();
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 566ce4f..0955bed 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -99,6 +99,7 @@
IPhoneStateListener callback;
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
+ IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback;
int callerUid;
int callerPid;
@@ -117,6 +118,10 @@
return (onSubscriptionsChangedListenerCallback != null);
}
+ boolean matchOnOpportunisticSubscriptionsChangedListener() {
+ return (onOpportunisticSubscriptionsChangedListenerCallback != null);
+ }
+
boolean canReadCallLog() {
try {
return TelephonyPermissions.checkReadCallLog(
@@ -131,7 +136,9 @@
return "{callingPackage=" + callingPackage + " binder=" + binder
+ " callback=" + callback
+ " onSubscriptionsChangedListenererCallback="
- + onSubscriptionsChangedListenerCallback
+ + onSubscriptionsChangedListenerCallback
+ + " onOpportunisticSubscriptionsChangedListenererCallback="
+ + onOpportunisticSubscriptionsChangedListenerCallback
+ " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId
+ " events=" + Integer.toHexString(events) + "}";
}
@@ -147,7 +154,9 @@
private final AppOpsManager mAppOps;
- private boolean hasNotifySubscriptionInfoChangedOccurred = false;
+ private boolean mHasNotifySubscriptionInfoChangedOccurred = false;
+
+ private boolean mHasNotifyOpportunisticSubscriptionInfoChangedOccurred = false;
private int mNumPhones;
@@ -407,7 +416,7 @@
log("listen oscl: Register r=" + r);
}
// Always notify when registration occurs if there has been a notification.
- if (hasNotifySubscriptionInfoChangedOccurred) {
+ if (mHasNotifySubscriptionInfoChangedOccurred) {
try {
if (VDBG) log("listen oscl: send to r=" + r);
r.onSubscriptionsChangedListenerCallback.onSubscriptionsChanged();
@@ -417,7 +426,7 @@
remove(r.binder);
}
} else {
- log("listen oscl: hasNotifySubscriptionInfoChangedOccurred==false no callback");
+ log("listen oscl: mHasNotifySubscriptionInfoChangedOccurred==false no callback");
}
}
}
@@ -429,15 +438,61 @@
remove(callback.asBinder());
}
+
+ @Override
+ public void addOnOpportunisticSubscriptionsChangedListener(String callingPackage,
+ IOnSubscriptionsChangedListener callback) {
+ int callerUserId = UserHandle.getCallingUserId();
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ if (VDBG) {
+ log("listen ooscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
+ + " callerUserId=" + callerUserId + " callback=" + callback
+ + " callback.asBinder=" + callback.asBinder());
+ }
+
+ synchronized (mRecords) {
+ // register
+ IBinder b = callback.asBinder();
+ Record r = add(b);
+
+ if (r == null) {
+ return;
+ }
+
+ r.context = mContext;
+ r.onOpportunisticSubscriptionsChangedListenerCallback = callback;
+ r.callingPackage = callingPackage;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
+ r.events = 0;
+ if (DBG) {
+ log("listen ooscl: Register r=" + r);
+ }
+ // Always notify when registration occurs if there has been a notification.
+ if (mHasNotifyOpportunisticSubscriptionInfoChangedOccurred) {
+ try {
+ if (VDBG) log("listen ooscl: send to r=" + r);
+ r.onOpportunisticSubscriptionsChangedListenerCallback.onSubscriptionsChanged();
+ if (VDBG) log("listen ooscl: sent to r=" + r);
+ } catch (RemoteException e) {
+ if (VDBG) log("listen ooscl: remote exception sending to r=" + r + " e=" + e);
+ remove(r.binder);
+ }
+ } else {
+ log("listen ooscl: hasNotifyOpptSubInfoChangedOccurred==false no callback");
+ }
+ }
+ }
+
@Override
public void notifySubscriptionInfoChanged() {
if (VDBG) log("notifySubscriptionInfoChanged:");
synchronized (mRecords) {
- if (!hasNotifySubscriptionInfoChangedOccurred) {
+ if (!mHasNotifySubscriptionInfoChangedOccurred) {
log("notifySubscriptionInfoChanged: first invocation mRecords.size="
+ mRecords.size());
}
- hasNotifySubscriptionInfoChangedOccurred = true;
+ mHasNotifySubscriptionInfoChangedOccurred = true;
mRemoveList.clear();
for (Record r : mRecords) {
if (r.matchOnSubscriptionsChangedListener()) {
@@ -456,6 +511,33 @@
}
@Override
+ public void notifyOpportunisticSubscriptionInfoChanged() {
+ if (VDBG) log("notifyOpptSubscriptionInfoChanged:");
+ synchronized (mRecords) {
+ if (!mHasNotifyOpportunisticSubscriptionInfoChangedOccurred) {
+ log("notifyOpptSubscriptionInfoChanged: first invocation mRecords.size="
+ + mRecords.size());
+ }
+ mHasNotifyOpportunisticSubscriptionInfoChangedOccurred = true;
+ mRemoveList.clear();
+ for (Record r : mRecords) {
+ if (r.matchOnOpportunisticSubscriptionsChangedListener()) {
+ try {
+ if (VDBG) log("notifyOpptSubChanged: call oosc to r=" + r);
+ r.onOpportunisticSubscriptionsChangedListenerCallback
+ .onSubscriptionsChanged();
+ if (VDBG) log("notifyOpptSubChanged: done oosc to r=" + r);
+ } catch (RemoteException ex) {
+ if (VDBG) log("notifyOpptSubChanged: RemoteException r=" + r);
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ @Override
public void listen(String pkgForDebug, IPhoneStateListener callback, int events,
boolean notifyNow) {
listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, pkgForDebug, callback,
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d78f492..f7c8e0e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -19,6 +19,7 @@
import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
+import android.annotation.CallbackExecutor;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -47,6 +48,7 @@
import android.os.ServiceManager;
import android.telephony.euicc.EuiccManager;
import android.util.DisplayMetrics;
+import android.util.Log;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.ISub;
@@ -57,6 +59,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
@@ -666,7 +669,7 @@
tr.addOnSubscriptionsChangedListener(pkgName, listener.callback);
}
} catch (RemoteException ex) {
- // Should not happen
+ Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex);
}
}
@@ -684,7 +687,7 @@
+ " listener=" + listener);
}
try {
- // We use the TelephonyRegistry as its runs in the system and thus is always
+ // We use the TelephonyRegistry as it runs in the system and thus is always
// available where as SubscriptionController could crash and not be available
ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));
@@ -692,7 +695,116 @@
tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
}
} catch (RemoteException ex) {
- // Should not happen
+ Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex);
+ }
+ }
+
+ /**
+ * A listener class for monitoring changes to {@link SubscriptionInfo} records of opportunistic
+ * subscriptions.
+ * <p>
+ * Override the onOpportunisticSubscriptionsChanged method in the object that extends this
+ * or {@link #addOnOpportunisticSubscriptionsChangedListener(
+ * Executor, OnOpportunisticSubscriptionsChangedListener)}
+ * to register your listener and to unregister invoke
+ * {@link #removeOnOpportunisticSubscriptionsChangedListener(
+ * OnOpportunisticSubscriptionsChangedListener)}
+ * <p>
+ * Permissions android.Manifest.permission.READ_PHONE_STATE is required
+ * for #onOpportunisticSubscriptionsChanged to be invoked.
+ */
+ public static class OnOpportunisticSubscriptionsChangedListener {
+ private Executor mExecutor;
+ /**
+ * Callback invoked when there is any change to any SubscriptionInfo. Typically
+ * this method would invoke {@link #getActiveSubscriptionInfoList}
+ */
+ public void onOpportunisticSubscriptionsChanged() {
+ if (DBG) log("onOpportunisticSubscriptionsChanged: NOT OVERRIDDEN");
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+
+ /**
+ * The callback methods need to be called on the handler thread where
+ * this object was created. If the binder did that for us it'd be nice.
+ */
+ IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
+ @Override
+ public void onSubscriptionsChanged() {
+ if (DBG) log("onOpportunisticSubscriptionsChanged callback received.");
+ mExecutor.execute(() -> onOpportunisticSubscriptionsChanged());
+ }
+ };
+
+ private void log(String s) {
+ Rlog.d(LOG_TAG, s);
+ }
+ }
+
+ /**
+ * Register for changes to the list of opportunistic subscription records or to the
+ * individual records themselves. When a change occurs the onOpportunisticSubscriptionsChanged
+ * method of the listener will be invoked immediately if there has been a notification.
+ *
+ * @param listener an instance of {@link OnOpportunisticSubscriptionsChangedListener} with
+ * onOpportunisticSubscriptionsChanged overridden.
+ */
+ public void addOnOpportunisticSubscriptionsChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnOpportunisticSubscriptionsChangedListener listener) {
+ if (executor == null || listener == null) {
+ return;
+ }
+
+ String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ if (DBG) {
+ logd("register addOnOpportunisticSubscriptionsChangedListener pkgName=" + pkgName
+ + " listener=" + listener);
+ }
+
+ listener.setExecutor(executor);
+
+ try {
+ // We use the TelephonyRegistry as it runs in the system and thus is always
+ // available. Where as SubscriptionController could crash and not be available
+ ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
+ "telephony.registry"));
+ if (tr != null) {
+ tr.addOnOpportunisticSubscriptionsChangedListener(pkgName, listener.callback);
+ }
+ } catch (RemoteException ex) {
+ Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex);
+ }
+ }
+
+ /**
+ * Unregister the {@link OnOpportunisticSubscriptionsChangedListener} that is currently
+ * listening opportunistic subscriptions change. This is not strictly necessary
+ * as the listener will automatically be unregistered if an attempt to invoke the listener
+ * fails.
+ *
+ * @param listener that is to be unregistered.
+ */
+ public void removeOnOpportunisticSubscriptionsChangedListener(
+ OnOpportunisticSubscriptionsChangedListener listener) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ if (DBG) {
+ logd("unregister OnOpportunisticSubscriptionsChangedListener pkgForDebug="
+ + pkgForDebug + " listener=" + listener);
+ }
+ try {
+ // We use the TelephonyRegistry as it runs in the system and thus is always
+ // available where as SubscriptionController could crash and not be available
+ ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
+ "telephony.registry"));
+ if (tr != null) {
+ tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
+ }
+ } catch (RemoteException ex) {
+ Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex);
}
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index e0e1a7b..43d56b3 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -32,6 +32,8 @@
interface ITelephonyRegistry {
void addOnSubscriptionsChangedListener(String pkg,
IOnSubscriptionsChangedListener callback);
+ void addOnOpportunisticSubscriptionsChangedListener(String pkg,
+ IOnSubscriptionsChangedListener callback);
void removeOnSubscriptionsChangedListener(String pkg,
IOnSubscriptionsChangedListener callback);
void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow);
@@ -73,6 +75,7 @@
int activationState, int activationType);
void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
void notifySubscriptionInfoChanged();
+ void notifyOpportunisticSubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
void notifyPhoneCapabilityChanged(in PhoneCapability capability);