diff options
| author | 2024-11-01 14:31:42 +0000 | |
|---|---|---|
| committer | 2024-11-01 14:31:42 +0000 | |
| commit | bdaf3afbc2bc36a9266ea477d256425c0b7d29a4 (patch) | |
| tree | 21ea5ac213e392e3c5c09385a553bcf92b83513d | |
| parent | 848bcb0effe4ec261361ebd45a4cffe1a6e24ee1 (diff) | |
| parent | 14a8d903ddbb8ea4393ad095e4bf6650ff2f5869 (diff) | |
Merge changes from topic "satellite-state-change-listener" into main
* changes:
  Add READ_BASIC_PHONE_STATE permission to Shell for CTS
  Implements satellite state change listener APIs
  Introduce satellite state change listener APIs
  Expose SatelliteManager as public manager class
10 files changed, 409 insertions, 6 deletions
| diff --git a/core/api/current.txt b/core/api/current.txt index 4b90f7256628..39d086a8bd0f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -10949,6 +10949,7 @@ package android.content {      field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1      field public static final String RESTRICTIONS_SERVICE = "restrictions";      field public static final String ROLE_SERVICE = "role"; +    field @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") public static final String SATELLITE_SERVICE = "satellite";      field public static final String SEARCH_SERVICE = "search";      field @FlaggedApi("android.os.security_state_service") public static final String SECURITY_STATE_SERVICE = "security_state";      field public static final String SENSOR_SERVICE = "sensor"; @@ -47866,6 +47867,19 @@ package android.telephony.mbms {  } +package android.telephony.satellite { + +  @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") public final class SatelliteManager { +    method @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") @RequiresPermission(anyOf={android.Manifest.permission.READ_BASIC_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE, "carrier privileges"}) public void registerStateChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateChangeListener); +    method @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") @RequiresPermission(anyOf={android.Manifest.permission.READ_BASIC_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE, "carrier privileges"}) public void unregisterStateChangeListener(@NonNull android.telephony.satellite.SatelliteStateChangeListener); +  } + +  @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") public interface SatelliteStateChangeListener { +    method public void onEnabledStateChanged(boolean); +  } + +} +  package android.text {    @Deprecated public class AlteredCharSequence implements java.lang.CharSequence android.text.GetChars { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index e202bfb535e8..e4d55896744c 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -18253,7 +18253,7 @@ package android.telephony.satellite {      method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteDatagramReceived(long, @NonNull android.telephony.satellite.SatelliteDatagram, int, @NonNull java.util.function.Consumer<java.lang.Void>);    } -  public final class SatelliteManager { +  @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") public final class SatelliteManager {      method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void addAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);      method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);      method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getAttachRestrictionReasonsForCarrier(int); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 1d26b77af5c7..186f7b3e111c 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -6677,8 +6677,8 @@ public abstract class Context {       *       * @see #getSystemService(String)       * @see android.telephony.satellite.SatelliteManager -     * @hide       */ +    @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER)      public static final String SATELLITE_SERVICE = "satellite";      /** diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 4d50a450490e..1dab2cf75594 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -47,6 +47,8 @@ import android.telephony.emergency.EmergencyNumber;  import android.telephony.ims.ImsCallSession;  import android.telephony.ims.ImsReasonInfo;  import android.telephony.ims.MediaQualityStatus; +import android.telephony.satellite.SatelliteStateChangeListener; +import android.util.ArrayMap;  import android.util.ArraySet;  import android.util.Log; @@ -55,12 +57,14 @@ 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.ISatelliteStateChangeListener;  import com.android.internal.telephony.ITelephonyRegistry;  import com.android.server.telecom.flags.Flags;  import java.lang.ref.WeakReference;  import java.util.Arrays;  import java.util.List; +import java.util.Map;  import java.util.Objects;  import java.util.Set;  import java.util.WeakHashMap; @@ -1482,6 +1486,111 @@ public class TelephonyRegistryManager {                  pkgName, attributionTag, callback, new int[0], notifyNow);      } +    @NonNull +    @GuardedBy("sSatelliteStateChangeListeners") +    private static final Map<SatelliteStateChangeListener, +                WeakReference<SatelliteStateChangeListenerWrapper>> +            sSatelliteStateChangeListeners = new ArrayMap<>(); + +    /** +     * Register a {@link SatelliteStateChangeListener} to receive notification when Satellite state +     * has changed. +     * +     * @param executor The {@link Executor} where the {@code listener} will be invoked +     * @param listener The listener to monitor the satellite state change +     * @hide +     */ +    public void addSatelliteStateChangeListener(@NonNull @CallbackExecutor Executor executor, +            @NonNull SatelliteStateChangeListener listener) { +        if (listener == null || executor == null) { +            throw new IllegalArgumentException("Listener and executor must be non-null"); +        } + +        synchronized (sSatelliteStateChangeListeners) { +            WeakReference<SatelliteStateChangeListenerWrapper> existing = +                    sSatelliteStateChangeListeners.get(listener); +            if (existing != null && existing.get() != null) { +                Log.d(TAG, "addSatelliteStateChangeListener: listener already registered"); +                return; +            } +            SatelliteStateChangeListenerWrapper wrapper = +                    new SatelliteStateChangeListenerWrapper(executor, listener); +            try { +                sRegistry.addSatelliteStateChangeListener( +                        wrapper, +                        mContext.getOpPackageName(), +                        mContext.getAttributionTag()); +                sSatelliteStateChangeListeners.put(listener, new WeakReference<>(wrapper)); +            } catch (RemoteException e) { +                throw e.rethrowFromSystemServer(); +            } +        } +    } + +    /** +     * Unregister a {@link SatelliteStateChangeListener} to stop receiving notification when +     * satellite state has changed. +     * +     * @param listener The listener previously registered with addSatelliteStateChangeListener. +     * @hide +     */ +    public void removeSatelliteStateChangeListener(@NonNull SatelliteStateChangeListener listener) { +        if (listener == null) { +            throw new IllegalArgumentException("listener must be non-null"); +        } + +        synchronized (sSatelliteStateChangeListeners) { +            WeakReference<SatelliteStateChangeListenerWrapper> ref = +                    sSatelliteStateChangeListeners.get(listener); +            if (ref == null) return; +            SatelliteStateChangeListenerWrapper wrapper = ref.get(); +            if (wrapper == null) return; +            try { +                sRegistry.removeSatelliteStateChangeListener(wrapper, mContext.getOpPackageName()); +                sSatelliteStateChangeListeners.remove(listener); +            } catch (RemoteException e) { +                throw e.rethrowFromSystemServer(); +            } +        } +    } + +    /** +     * Notify the registrants that the satellite state has changed. +     * +     * @param isEnabled True if the satellite modem is enabled, false otherwise +     * @hide +     */ +    public void notifySatelliteStateChanged(boolean isEnabled) { +        try { +            sRegistry.notifySatelliteStateChanged(isEnabled); +        } catch (RemoteException ex) { +            // system process is dead +            throw ex.rethrowFromSystemServer(); +        } +    } + +    private static class SatelliteStateChangeListenerWrapper extends +            ISatelliteStateChangeListener.Stub implements ListenerExecutor { +        @NonNull private final WeakReference<SatelliteStateChangeListener> mListener; +        @NonNull private final Executor mExecutor; + +        SatelliteStateChangeListenerWrapper(@NonNull Executor executor, +                @NonNull SatelliteStateChangeListener listener) { +            mExecutor = executor; +            mListener = new WeakReference<>(listener); +        } + +        @Override +        public void onSatelliteEnabledStateChanged(boolean isEnabled) { +            Binder.withCleanCallingIdentity( +                    () -> +                            executeSafely( +                                    mExecutor, +                                    mListener::get, +                                    sscl -> sscl.onEnabledStateChanged(isEnabled))); +        } +    } +      private static class CarrierPrivilegesCallbackWrapper extends ICarrierPrivilegesCallback.Stub              implements ListenerExecutor {          @NonNull private final WeakReference<CarrierPrivilegesCallback> mCallback; diff --git a/core/java/com/android/internal/telephony/ISatelliteStateChangeListener.aidl b/core/java/com/android/internal/telephony/ISatelliteStateChangeListener.aidl new file mode 100644 index 000000000000..4d195c2028b5 --- /dev/null +++ b/core/java/com/android/internal/telephony/ISatelliteStateChangeListener.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 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 ISatelliteStateChangeListener { +    void onSatelliteEnabledStateChanged(boolean isEnabled); +}
\ 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 ca75abdedfcc..1c76a6cd4bba 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -38,6 +38,7 @@ import com.android.internal.telephony.ICarrierConfigChangeListener;  import com.android.internal.telephony.ICarrierPrivilegesCallback;  import com.android.internal.telephony.IPhoneStateListener;  import com.android.internal.telephony.IOnSubscriptionsChangedListener; +import com.android.internal.telephony.ISatelliteStateChangeListener;  interface ITelephonyRegistry {      void addOnSubscriptionsChangedListener(String pkg, String featureId, @@ -124,4 +125,8 @@ interface ITelephonyRegistry {      void notifyCarrierRoamingNtnModeChanged(int subId, in boolean active);      void notifyCarrierRoamingNtnEligibleStateChanged(int subId, in boolean eligible);      void notifyCarrierRoamingNtnAvailableServicesChanged(int subId, in int[] availableServices); + +    void addSatelliteStateChangeListener(ISatelliteStateChangeListener listener, String pkg, String featureId); +    void removeSatelliteStateChangeListener(ISatelliteStateChangeListener listener, String pkg); +    void notifySatelliteStateChanged(boolean isEnabled);  } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 05c5e5d9f82d..1919572ff571 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -951,6 +951,9 @@      <!-- Permission required for CTS test - CtsAppTestCases -->      <uses-permission android:name="android.permission.KILL_UID" /> +    <!-- Permission required for CTS test - CtsTelephonyTestCases --> +    <uses-permission android:name="android.permission.READ_BASIC_PHONE_STATE" /> +      <application          android:label="@string/app_label"          android:theme="@android:style/Theme.DeviceDefault.DayNight" diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 363807d2aa8c..72a9a2d6de26 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -102,6 +102,7 @@ import com.android.internal.telephony.ICarrierConfigChangeListener;  import com.android.internal.telephony.ICarrierPrivilegesCallback;  import com.android.internal.telephony.IOnSubscriptionsChangedListener;  import com.android.internal.telephony.IPhoneStateListener; +import com.android.internal.telephony.ISatelliteStateChangeListener;  import com.android.internal.telephony.ITelephonyRegistry;  import com.android.internal.telephony.TelephonyPermissions;  import com.android.internal.telephony.flags.Flags; @@ -126,6 +127,7 @@ import java.util.Map;  import java.util.NoSuchElementException;  import java.util.Objects;  import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean;  import java.util.stream.Collectors;  /** @@ -164,6 +166,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {          IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback;          ICarrierPrivilegesCallback carrierPrivilegesCallback;          ICarrierConfigChangeListener carrierConfigChangeListener; +        ISatelliteStateChangeListener satelliteStateChangeListener;          int callerUid;          int callerPid; @@ -196,6 +199,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {              return carrierConfigChangeListener != null;          } +        boolean matchSatelliteStateChangeListener() { +            return satelliteStateChangeListener != null; +        } +          boolean canReadCallLog() {              try {                  return TelephonyPermissions.checkReadCallLog( @@ -215,6 +222,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {                      + onOpportunisticSubscriptionsChangedListenerCallback                      + " carrierPrivilegesCallback=" + carrierPrivilegesCallback                      + " carrierConfigChangeListener=" + carrierConfigChangeListener +                    + " satelliteStateChangeListener=" + satelliteStateChangeListener                      + " subId=" + subId + " phoneId=" + phoneId + " events=" + eventList + "}";          }      } @@ -433,6 +441,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {      private List<IntArray> mCarrierRoamingNtnAvailableServices; +    // Local cache to check if Satellite Modem is enabled +    private AtomicBoolean mIsSatelliteEnabled; +    private AtomicBoolean mWasSatelliteEnabledNotified; +      /**       * Per-phone map of precise data connection state. The key of the map is the pair of transport       * type and APN setting. This is the cache to prevent redundant callbacks to the listeners. @@ -871,6 +883,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {          mCarrierRoamingNtnMode = new boolean[numPhones];          mCarrierRoamingNtnEligible = new boolean[numPhones];          mCarrierRoamingNtnAvailableServices = new ArrayList<>(); +        mIsSatelliteEnabled = new AtomicBoolean(); +        mWasSatelliteEnabledNotified = new AtomicBoolean(); +          for (int i = 0; i < numPhones; i++) {              mCallState[i] =  TelephonyManager.CALL_STATE_IDLE; @@ -3425,6 +3440,94 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {      }      @Override +    public void addSatelliteStateChangeListener(@NonNull ISatelliteStateChangeListener listener, +            @NonNull String pkg, @Nullable String featureId) { +        final int callerUserId = UserHandle.getCallingUserId(); +        mAppOps.checkPackage(Binder.getCallingUid(), pkg); +        enforceCallingOrSelfAtLeastReadBasicPhoneStatePermission(pkg, featureId, +                "addSatelliteStateChangeListener"); +        if (VDBG) { +            log("addSatelliteStateChangeListener pkg=" + pii(pkg) +                    + " uid=" + Binder.getCallingUid() +                    + " myUserId=" + UserHandle.myUserId() + " callerUerId" + callerUserId +                    + " listener=" + listener + " listener.asBinder=" + listener.asBinder()); +        } + +        synchronized (mRecords) { +            final IBinder b = listener.asBinder(); +            boolean doesLimitApply = doesLimitApplyForListeners(Binder.getCallingUid(), +                    Process.myUid()); +            Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply); + +            if (r == null) { +                loge("addSatelliteStateChangeListener: can not create Record instance!"); +                return; +            } + +            r.context = mContext; +            r.satelliteStateChangeListener = listener; +            r.callingPackage = pkg; +            r.callingFeatureId = featureId; +            r.callerUid = Binder.getCallingUid(); +            r.callerPid = Binder.getCallingPid(); +            r.eventList = new ArraySet<>(); +            if (DBG) { +                log("addSatelliteStateChangeListener:  Register r=" + r); +            } + +            // Always notify registrants on registration if it has been notified before +            if (mWasSatelliteEnabledNotified.get() && r.matchSatelliteStateChangeListener()) { +                try { +                    r.satelliteStateChangeListener.onSatelliteEnabledStateChanged( +                            mIsSatelliteEnabled.get()); +                } catch (RemoteException e) { +                    throw new RuntimeException(e); +                } +            } +        } +    } + +    @Override +    public void removeSatelliteStateChangeListener(@NonNull ISatelliteStateChangeListener listener, +            @NonNull String pkg) { +        if (DBG) log("removeSatelliteStateChangeListener listener=" + listener + ", pkg=" + pkg); +        mAppOps.checkPackage(Binder.getCallingUid(), pkg); +        enforceCallingOrSelfAtLeastReadBasicPhoneStatePermission(pkg, null, +                "removeSatelliteStateChangeListener"); +        remove(listener.asBinder()); +    } + +    @Override +    public void notifySatelliteStateChanged(boolean isEnabled) { +        if (!checkNotifyPermission("notifySatelliteStateChanged")) { +            loge("notifySatelliteStateChanged: Caller has no notify permission!"); +            return; +        } +        if (VDBG) { +            log("notifySatelliteStateChanged: isEnabled=" + isEnabled); +        } + +        mWasSatelliteEnabledNotified.set(true); +        mIsSatelliteEnabled.set(isEnabled); + +        synchronized (mRecords) { +            mRemoveList.clear(); +            for (Record r : mRecords) { +                // Listeners are "global", neither per-slot nor per-sub, so no idMatch check here +                if (!r.matchSatelliteStateChangeListener()) { +                    continue; +                } +                try { +                    r.satelliteStateChangeListener.onSatelliteEnabledStateChanged(isEnabled); +                } catch (RemoteException re) { +                    mRemoveList.add(r.binder); +                } +            } +            handleRemoveListLocked(); +        } +    } + +    @Override      public void notifyMediaQualityStatusChanged(int phoneId, int subId, MediaQualityStatus status) {          if (!checkNotifyPermission("notifyMediaQualityStatusChanged()")) {              return; @@ -4622,4 +4725,32 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {          if (packageNames.isEmpty() || Build.IS_DEBUGGABLE) return packageNames.toString();          return "[***, size=" + packageNames.size() + "]";      } + +    /** +     * The method enforces the calling package at least has READ_BASIC_PHONE_STATE permission. +     * That is, calling package either has READ_PRIVILEGED_PHONE_STATE, READ_PHONE_STATE or Carrier +     * Privileges on ANY active subscription, or has READ_BASIC_PHONE_STATE permission. +     */ +    private void enforceCallingOrSelfAtLeastReadBasicPhoneStatePermission(String pkgName, +            String featureId, String message) { +        // Check if calling app has READ_PHONE_STATE on ANY active subscription +        boolean hasReadPhoneState = false; +        SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class); +        if (sm != null) { +            for (int subId : sm.getActiveSubscriptionIdList()) { +                if (TelephonyPermissions.checkCallingOrSelfReadPhoneStateNoThrow(mContext, subId, +                        pkgName, featureId, message)) { +                    hasReadPhoneState = true; +                    break; +                } +            } +        } + +        // If yes, pass. If not, then enforce READ_BASIC_PHONE_STATE permission +        if (!hasReadPhoneState) { +            mContext.enforceCallingOrSelfPermission( +                    Manifest.permission.READ_BASIC_PHONE_STATE, +                    message); +        } +    }  } diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index e332d0f720e8..be02232abe1b 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -26,6 +26,7 @@ import android.annotation.Nullable;  import android.annotation.RequiresFeature;  import android.annotation.RequiresPermission;  import android.annotation.SystemApi; +import android.annotation.SystemService;  import android.content.Context;  import android.content.pm.PackageManager;  import android.os.Binder; @@ -39,6 +40,7 @@ import android.telephony.SubscriptionManager;  import android.telephony.TelephonyCallback;  import android.telephony.TelephonyFrameworkInitializer;  import android.telephony.TelephonyManager; +import android.telephony.TelephonyRegistryManager;  import com.android.internal.telephony.IIntegerConsumer;  import com.android.internal.telephony.ITelephony; @@ -61,13 +63,19 @@ import java.util.function.Consumer;  import java.util.stream.Collectors;  /** - * Manages satellite operations such as provisioning, pointing, messaging, location sharing, etc. - * To get the object, call {@link Context#getSystemService(String)}. + * Manages satellite states such as monitoring enabled state and operations such as provisioning, + * pointing, messaging, location sharing, etc.   * - * @hide + * <p>To get the object, call {@link Context#getSystemService(String)} with + * {@link Context#SATELLITE_SERVICE}. + * + * <p>SatelliteManager is intended for use on devices with feature + * {@link PackageManager#FEATURE_TELEPHONY_SATELLITE}. On devices without the feature, the behavior + * is not reliable.   */ +@SystemService(Context.SATELLITE_SERVICE) +@FlaggedApi(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER)  @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SATELLITE) -@SystemApi  public final class SatelliteManager {      private static final String TAG = "SatelliteManager"; @@ -103,6 +111,8 @@ public final class SatelliteManager {       */      @Nullable private final Context mContext; +    private TelephonyRegistryManager mTelephonyRegistryMgr; +      /**       * Create an instance of the SatelliteManager.       * @@ -736,6 +746,65 @@ public final class SatelliteManager {              "android.telephony.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT";      /** +     * Registers a {@link SatelliteStateChangeListener} to receive callbacks when the satellite +     * state may have changed. +     * +     * <p>The callback method is immediately triggered with latest state on invoking this method if +     * the state change has been notified before. +     * +     * @param executor The {@link Executor} where the {@code listener} will be invoked +     * @param listener The listener to monitor the satellite state change +     * +     * @see SatelliteStateChangeListener +     * @see TelephonyManager#hasCarrierPrivileges() +     */ +    @FlaggedApi(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER) +    @RequiresPermission(anyOf = {android.Manifest.permission.READ_BASIC_PHONE_STATE, +            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, +            android.Manifest.permission.READ_PHONE_STATE, +            "carrier privileges"}) +    public void registerStateChangeListener(@NonNull @CallbackExecutor Executor executor, +            @NonNull SatelliteStateChangeListener listener) { +        if (mContext == null) { +            throw new IllegalStateException("Telephony service is null"); +        } + +        mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); +        if (mTelephonyRegistryMgr == null) { +            throw new IllegalStateException("Telephony registry service is null"); +        } +        mTelephonyRegistryMgr.addSatelliteStateChangeListener(executor, listener); +    } + +    /** +     * Unregisters the {@link SatelliteStateChangeListener} previously registered with +     * {@link #registerStateChangeListener(Executor, SatelliteStateChangeListener)}. +     * +     * <p>It will be a no-op if the {@code listener} is not currently registered. +     * +     * @param listener The listener to unregister +     * +     * @see SatelliteStateChangeListener +     * @see TelephonyManager#hasCarrierPrivileges() +     */ +    @FlaggedApi(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER) +    @RequiresPermission(anyOf = {android.Manifest.permission.READ_BASIC_PHONE_STATE, +            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, +            android.Manifest.permission.READ_PHONE_STATE, +            "carrier privileges"}) +    public void unregisterStateChangeListener(@NonNull SatelliteStateChangeListener listener) { +        if (mContext == null) { +            throw new IllegalStateException("Telephony service is null"); +        } + +        mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); +        if (mTelephonyRegistryMgr == null) { +            throw new IllegalStateException("Telephony registry service is null"); +        } +        mTelephonyRegistryMgr.removeSatelliteStateChangeListener(listener); +    } + +    /**       * Request to enable or disable the satellite modem and demo mode.       * If satellite modem and cellular modem cannot work concurrently,       * then this will disable the cellular modem if satellite modem is enabled, diff --git a/telephony/java/android/telephony/satellite/SatelliteStateChangeListener.java b/telephony/java/android/telephony/satellite/SatelliteStateChangeListener.java new file mode 100644 index 000000000000..3aa910dfcc80 --- /dev/null +++ b/telephony/java/android/telephony/satellite/SatelliteStateChangeListener.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 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 android.telephony.satellite; + +import android.annotation.FlaggedApi; + +import com.android.internal.telephony.flags.Flags; + +import java.util.concurrent.Executor; + +/** + * A listener interface to monitor satellite state change events. + * + * <p>Call + * {@link SatelliteManager#registerStateChangeListener(Executor, SatelliteStateChangeListener)} + * to monitor. Call + * {@link SatelliteManager#unregisterStateChangeListener(SatelliteStateChangeListener)} to cancel. + * + * @see SatelliteManager#registerStateChangeListener(Executor, SatelliteStateChangeListener) + * @see SatelliteManager#unregisterStateChangeListener(SatelliteStateChangeListener) + */ +@FlaggedApi(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER) +public interface SatelliteStateChangeListener { +    /** +     * Called when satellite modem enabled state may have changed. +     * +     * <p>Note:there is no guarantee that this callback will only be invoked upon a change of state. +     * In other word, in some cases, the callback may report with the same enabled states. It is the +     * caller's responsibility to filter uninterested states. +     * +     * <p>Note:satellite enabled state is a device state that is NOT associated with subscription or +     * SIM slot. +     * +     * @param isEnabled {@code true} means satellite modem is enabled. +     */ +    void onEnabledStateChanged(boolean isEnabled); +} |