diff options
4 files changed, 213 insertions, 9 deletions
diff --git a/api/current.txt b/api/current.txt index 88d782586339..6051a265fe1f 100755 --- a/api/current.txt +++ b/api/current.txt @@ -42617,6 +42617,7 @@ package android.telephony { } 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); @@ -42634,6 +42635,7 @@ package android.telephony { 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); @@ -42649,6 +42651,11 @@ package android.telephony { 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 c44a81e2dcd0..98b88cb557af 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -101,6 +101,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { IPhoneStateListener callback; IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback; + IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback; int callerUid; int callerPid; @@ -119,6 +120,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return (onSubscriptionsChangedListenerCallback != null); } + boolean matchOnOpportunisticSubscriptionsChangedListener() { + return (onOpportunisticSubscriptionsChangedListenerCallback != null); + } + boolean canReadCallLog() { try { return TelephonyPermissions.checkReadCallLog( @@ -133,7 +138,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return "{callingPackage=" + callingPackage + " binder=" + binder + " callback=" + callback + " onSubscriptionsChangedListenererCallback=" - + onSubscriptionsChangedListenerCallback + + onSubscriptionsChangedListenerCallback + + " onOpportunisticSubscriptionsChangedListenererCallback=" + + onOpportunisticSubscriptionsChangedListenerCallback + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId + " events=" + Integer.toHexString(events) + "}"; } @@ -149,7 +156,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private final AppOpsManager mAppOps; - private boolean hasNotifySubscriptionInfoChangedOccurred = false; + private boolean mHasNotifySubscriptionInfoChangedOccurred = false; + + private boolean mHasNotifyOpportunisticSubscriptionInfoChangedOccurred = false; private int mNumPhones; @@ -410,7 +419,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { 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(); @@ -420,7 +429,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } else { - log("listen oscl: hasNotifySubscriptionInfoChangedOccurred==false no callback"); + log("listen oscl: mHasNotifySubscriptionInfoChangedOccurred==false no callback"); } } } @@ -432,15 +441,61 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { 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()) { @@ -459,6 +514,33 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } @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 b15bb8512d19..1d9e6052a31c 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -19,6 +19,7 @@ package android.telephony; 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.RemoteException; 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.ArrayList; 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 @@ public class SubscriptionManager { tr.addOnSubscriptionsChangedListener(pkgName, listener.callback); } } catch (RemoteException ex) { - // Should not happen + Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex); } } @@ -684,7 +687,116 @@ public class SubscriptionManager { + " 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")); + if (tr != null) { + tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback); + } + } catch (RemoteException ex) { + 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")); @@ -692,7 +804,7 @@ public class SubscriptionManager { tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback); } } catch (RemoteException ex) { - // Should not happen + 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 e0e1a7b87916..43d56b39e0c4 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -32,6 +32,8 @@ import com.android.internal.telephony.IOnSubscriptionsChangedListener; 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 @@ interface ITelephonyRegistry { 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); |