Summary of problems when upgrading Android app targetSdk from 28 to 33

Article directory

  • 1. TelephonyManager#listen(PhoneStateListener listener, int events)
    • error log
    • 1. Call the entrance
    • 2.~~TelephonyRegistryManager of binder service~~ TelephonyRegistryManager (not binder server!!!)
      • register
      • use
    • 3.TelephonyRegistry of binder service
      • register
        • Note:~~You can see that the registration locations of the two binder services are different~~ It has been confirmed that the former runs in the calling process (same jvm), and the latter runs in the binder server process. Here is the system server process, registered through ServiceManager
      • use
        • 1. First look at the permission check TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage, callingFeatureId, message)
        • 2. See under what circumstances permission is required
          • Note: the annotation @EnabledSince should be used together with the annotation @ChangeId
    • Why is it required that the running system is no less than 12?
  • 2. TelephonyManager#getCallState()
    • error log
    • 1. Call the entrance
    • 2.TelecomManager
      • register
      • use
    • 3.ITelecomService of binder service
      • register
      • use

一.TelephonyManager#listen(PhoneStateListener listener, int events)

Description of the problem: If targetsdkversion is upgraded to 12 or above, and the device runs at least 12, if the READ PHONE STATE permission is not dynamically applied, a SecurityException error will be reported.

Error log

java.lang.SecurityException: listen
   at android.os.Parcel.createExceptionOrNull(Parcel.java:2442)
   at android.os.Parcel.createException(Parcel.java:2426)
   at android.os.Parcel.readException(Parcel.java:2409)
   at android.os.Parcel.readException(Parcel.java:2351)
   at com.android.internal.telephony.ITelephonyRegistry$Stub$Proxy.listenWithEventList(ITelephonyRegistry.java:1036)
   at android.telephony.TelephonyRegistryManager.listenFromListener(TelephonyRegistryManager.java:250)
   at android.telephony.TelephonyManager.listen(TelephonyManager.java:6064)

android12
Process overview:

1. Call entry

5956 /**
5957 * Registers a listener object to receive notification of changes
5958 * in specified telephony states.
5959 * <p>
5960 * To register a listener, pass a {<!-- -->@link PhoneStateListener} and specify at least one telephony
5961 * state of interest in the events argument.
5962*
5963 * At registration, and when a specified telephony state changes, the telephony manager invokes
5964 * the appropriate callback method on the listener object and passes the current (updated)
5965 * values.
5966 * <p>
5967 * To un-register a listener, pass the listener object and set the events argument to
5968 * {<!-- -->@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
5969*
5970 * If this TelephonyManager object has been created with {<!-- -->@link #createForSubscriptionId},
5971 * applies to the given subId. Otherwise, applies to
5972 * {<!-- -->@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds,
5973 * pass a separate listener object to each TelephonyManager object created with
5974 * {<!-- -->@link #createForSubscriptionId}.
5975*
5976 * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
5977 * call {<!-- -->@link android.os.Binder#clearCallingIdentity()} before calling this method. A
5978 * {<!-- -->@link SecurityException} will be thrown otherwise.
5979 *
5980 * This API should be used sparingly -- large numbers of listeners will cause system
5981 * instability. If a process has registered too many listeners without unregistering them, it
5982 * may encounter an {<!-- -->@link IllegalStateException} when trying to register more listeners.
5983 *
5984 * @param listener The {<!-- -->@link PhoneStateListener} object to register
5985 * (or unregister)
5986 * @param events The telephony state(s) of interest to the listener,
5987 * as a bitwise-OR combination of {<!-- -->@link PhoneStateListener}
5988 * LISTEN_ flags.
5989 * @deprecated Use {<!-- -->@link #registerTelephonyCallback(Executor, TelephonyCallback)}.
5990 */
5991 @Deprecated
5992 public void listen(PhoneStateListener listener, int events) {<!-- -->
5993 if (mContext == null) return;
5994 boolean notifyNow = (getITelephony() != null);
5995 TelephonyRegistryManager telephonyRegistry =
5996 (TelephonyRegistryManager)
5997 mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
5998 if (telephonyRegistry != null) {<!-- -->
5999 telephonyRegistry.listenFromListener(mSubId, getOpPackageName(),
6000 getAttributionTag(), listener, events, notifyNow);
6001 } else {<!-- -->
6002 Rlog.w(TAG, "telephony registry not ready.");
6003 }
6004 }

Go to TelephonyRegistryManager#listenFromListener(int subId, @NonNull String pkg, @NonNull String featureId, @NonNull PhoneStateListener listener, @NonNull int events, boolean notifyNow)

2.TelephonyRegistryManager of binder service TelephonyRegistryManager (not binder server!!!)

Register

Source code location/frameworks/base/core/java/android/app/SystemServiceRegistry.java

678 registerService(Context.TELEPHONY_REGISTRY_SERVICE, TelephonyRegistryManager.class,
679 new CachedServiceFetcher<TelephonyRegistryManager>() {<!-- -->
680 @Override
681 public TelephonyRegistryManager createService(ContextImpl ctx) {<!-- -->
682 return new TelephonyRegistryManager(ctx);
683 }});

Use

Source code location /frameworks/base/core/java/android/telephony/TelephonyRegistryManager.java

216 /**
217 * To check the SDK version for {<!-- -->@link #listenFromListener}.
218 */
219 @ChangeId
220 @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
221 private static final long LISTEN_CODE_CHANGE = 147600208L;
222
223/**
224 * Listen for incoming subscriptions
225 * @param subId Subscription ID
226 * @param pkg Package name
227 * @param featureId Feature ID
228 * @param listener Listener providing callback
229 * @param events Events
230 * @param notifyNow Whether to notify instantly
231 */
232 public void listenFromListener(int subId, @NonNull String pkg, @NonNull String featureId,
233 @NonNull PhoneStateListener listener, @NonNull int events, boolean notifyNow) {<!-- -->
234 if (listener == null) {<!-- -->
235 throw new IllegalStateException("telephony service is null.");
236 }
237
238 try {<!-- -->
239 int[] eventsList = getEventsFromBitmask(events).stream().mapToInt(i -> i).toArray();
240 // subId from PhoneStateListener is deprecated Q on forward, use the subId from
241 // TelephonyManager instance. Keep using subId from PhoneStateListener for pre-Q.
242 if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {<!-- -->
243 // Since mSubId in PhoneStateListener is deprecated from Q on forward, this is
244 // the only place to set mSubId and its for "informational" only.
245 listener.mSubId = (eventsList.length == 0)
246 ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId;
247 } else if (listener.mSubId != null) {<!-- -->
248 subId = listener.mSubId;
249 }
250 sRegistry.listenWithEventList(
251 subId, pkg, featureId, listener.callback, eventsList, notifyNow);
252 } catch (RemoteException e) {<!-- -->
253 throw e.rethrowFromSystemServer();
254 }
255 }
private @NonNull Set<Integer> getEventsFromBitmask(int eventMask) {<!-- -->
1003
1004 Set<Integer> eventList = new ArraySet<>();
. . .
1026 // Note: Legacy call state listeners can get the phone number which is not provided in the
1027 // new version in TelephonyCallback.
1028 if ((eventMask & amp; PhoneStateListener.LISTEN_CALL_STATE) != 0) {<!-- -->
1029 eventList.add(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED);
1030 }
1031

Finally converted to EVENT_LEGACY_CALL_STATE_CHANGED

So far we have gone to ITelephonyRegistry#listenWithEventList(int subId, String callingPackage, String callingFeatureId,IPhoneStateListener callback, int[] events, boolean notifyNow)

3.binder service TelephonyRegistry

Register

Source code location /frameworks/base/services/java/com/android/server/SystemServer.java

1410 t.traceBegin("StartTelephonyRegistry");
1411 telephonyRegistry = new TelephonyRegistry(
1412 context, new TelephonyRegistry.ConfigurationProvider());
1413 ServiceManager.addService("telephony.registry", telephonyRegistry);
1414 t.traceEnd();
Note: You can see that the registration locations of the two binder services are different It was confirmed that the former is running in the calling process (same jvm), and the latter is running in the binder server process, here is the system server Process, registered through ServiceManager

Use

Source code location: frameworks/base/services/core/java/com/android/server/TelephonyRegistry.java

992 @Override
993 public void listenWithEventList(int subId, String callingPackage, String callingFeatureId,
994 IPhoneStateListener callback, int[] events, boolean notifyNow) {<!-- -->
995 Set<Integer> eventList = Arrays.stream(events).boxed().collect(Collectors.toSet());
996 listen(callingPackage, callingFeatureId, callback, eventList, notifyNow, subId);
997 }



private void listen(String callingPackage, @Nullable String callingFeatureId,
1000 IPhoneStateListener callback, Set<Integer> events, boolean notifyNow, int subId) {<!-- -->
1001 int callerUserId = UserHandle.getCallingUserId();
1002 mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
1003 String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid()
1004 + " events=" + events + " notifyNow=" + notifyNow
1005 + " subId=" + subId + " myUserId=" + UserHandle.myUserId()
1006 + " callerUserId=" + callerUserId;
1007 mListenLog.log(str);
1008 if (VDBG) {<!-- -->
1009 log(str);
1010 }
1011
1012 if (events.isEmpty()) {<!-- -->
1013 if (DBG) {<!-- -->
1014 log("listen: Unregister");
1015 }
1016 events.clear();
1017 remove(callback.asBinder());
1018 return;
1019 }
1020
1021 // Checks permission and throws SecurityException for disallowed operations. For pre-M
1022 // apps whose runtime permission has been revoked, we return immediately to skip sending
1023 // events to the app without crashing it.
1024 if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId, "listen")) {<!-- -->
1025 return;
1026 }//Key permission verification code
1027

. . .
}

private boolean checkListenerPermission(Set<Integer> events, int subId, String callingPackage,
3055 @Nullable String callingFeatureId, String message) {<!-- -->
. . .
3090
3091 if (isPhoneStatePermissionRequired(events, callingPackage, Binder.getCallingUserHandle())) {<!-- -->
3092 if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
3093 mContext, subId, callingPackage, callingFeatureId, message)) {<!-- -->
3094 isPermissionCheckSuccessful = false;
3095 }
3096 }

1. First look at the permission check TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage, callingFeatureId, message)

You can see that READ_PRIVILEGED_PHONE_STATE is checked first. If there is no such permission, READ_PHONE_STATE is checked. If neither is available, a SecurityException is thrown.

91 public static boolean checkCallingOrSelfReadPhoneState(
92 Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
93 String message) {<!-- -->
94 return checkReadPhoneState(context, subId, Binder.getCallingPid(), Binder.getCallingUid(),
95 callingPackage, callingFeatureId, message);
96 }
. . .

132 public static boolean checkReadPhoneState(
133 Context context, int subId, int pid, int uid, String callingPackage,
134 @Nullable String callingFeatureId, String message) {<!-- -->
135 try {<!-- -->
136 context.enforcePermission(
137 android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
138
139 // SKIP checking for run-time permission since caller has PRIVILEGED permission
140 return true;
141 } catch (SecurityException privilegedPhoneStateException) {<!-- -->
142 try {<!-- -->
143 context.enforcePermission(
144 android.Manifest.permission.READ_PHONE_STATE, pid, uid, message);
145 } catch (SecurityException phoneStateException) {<!-- -->
146 // If we don't have the runtime permission, but do have carrier privileges, that
147 // suffices for reading phone state.
148 if (SubscriptionManager.isValidSubscriptionId(subId)) {
149 enforceCarrierPrivilege(context, subId, uid, message);
150 return true;
151 }
152 throw phoneStateException;
153 }
154 }
155
156 // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
157 // revoked.
158 AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
159 return appOps.noteOp(AppOpsManager.OPSTR_READ_PHONE_STATE, uid, callingPackage,
160 callingFeatureId, null) == AppOpsManager.MODE_ALLOWED;
161 }
2. Consider under what circumstances permission is required

isPhoneStatePermissionRequired(events, callingPackage, Binder.getCallingUserHandle())

 private boolean isPhoneStatePermissionRequired(Set<Integer> events, String callingPackage,
465 UserHandle userHandle) {<!-- -->
. . .
471
472 // Only check READ_PHONE_STATE for CALL_STATE_CHANGED for Android 12 or above.
473 if ((events.contains(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)
474 || events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED))
475 & amp; & amp; mConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(
476 callingPackage, userHandle)) {<!-- -->
477 return true;
478 }
. . .
504 }

Because events contain TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED

194 /**
195 * Wrapper class to facilitate testing -- encapsulates bits of configuration that are
196 * normally fetched from static methods with many dependencies.
197 */
198 public static class ConfigurationProvider {<!-- -->
...
225 */
226 public boolean isCallStateReadPhoneStateEnforcedInPlatformCompat(String packageName,
227 UserHandle userHandle) {<!-- -->
228 return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
229 TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION, packageName,
230 userHandle));
231 }

Source code location: frameworks/base/telecomm/java/android/telecom/TelecomManager.java

1010 /**
1011 * Enable READ_PHONE_STATE protection on APIs querying and notifying call state, such as
1012 * {<!-- -->@code TelecomManager#getCallState}, {@link TelephonyManager#getCallStateForSubscription()},
1013 * and {<!-- -->@link android.telephony.TelephonyCallback.CallStateListener}.
1014 * @hide
1015 */
1016 @ChangeId
1017 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
1018 // this magic number is a bug ID
1019 public static final long ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION = 157233955L;

At this point, you can confirm that the targetsdk version starts from android 12 and the running system version is not lower than 12. This permission READ PHONE STATE must be applied dynamically, otherwise the binder communication will report an error SecurityException

Note: The annotation @EnabledSince should be used in conjunction with the annotation @ChangeId

Source code location /tools/platform-compat/java/android/compat/annotation/EnabledSince.java

25 /**
26 * Used to indicate that a compatibility {<!-- -->@link ChangeId change} is enabled only for apps with a
27 * {<!-- -->@code targetSdkVersion} <em>greater or equal to</em> the given value.
28*
29 * <p>This annotation should only be applied to change ID constants that are also annotated with
30 * {<!-- -->@link ChangeId}. In any other context, this annotation will have no effect.
31*
32 * @hide
33 */
34 @Retention(SOURCE)
35 @Target({<!-- -->FIELD})
36 public @interface EnabledSince {<!-- -->
37/**
38 * @return Theminimum {<!-- -->@code targetSdkVersion} for which this change is enabled. Apps with
39 * a {<!-- -->@code targetSdkVersion} greater or equal to this value will get the change.
40 */
41 int targetSdkVersion();
42 }
43

Why is it required that the running system is no less than 12

Answer: There are no these logical requirements in system versions lower than 12. You can refer to the source code of systems below 12.

2.TelephonyManager#getCallState()

Description of the problem: If targetsdkversion is upgraded to 12 or above, and the device runs at least 12, if the READ PHONE STATE permission is not dynamically applied, a SecurityException error will be reported.

Similar to the previous question

Error log

java.lang.SecurityException: getCallState: Neither user 10495 nor current process has android.permission.READ_PHONE_STATE.
                                                                                                    at android.os.Parcel.createExceptionOrNull(Parcel.java:2442)
                                                                                                    at android.os.Parcel.createException(Parcel.java:2426)
                                                                                                    at android.os.Parcel.readException(Parcel.java:2409)
                                                                                                    at android.os.Parcel.readException(Parcel.java:2351)
                                                                                                    at com.android.internal.telecom.ITelecomService$Stub$Proxy.getCallStateUsingPackage(ITelecomService.java:2700)
                                                                                                    at android.telecom.TelecomManager.getCallState(TelecomManager.java:1825)

Here we can see that android.os.Parcel.readException is thrown. Because it involves binder cross-process communication, it is a binder server permission verification exception. Abnormal data will be sent to the binder client, so the binder client will pass Parcel reads exception

Or follow the android12 source code
Process overview:

1. Call entry

Just looking at this code, you can’t see any permission requirements.

5680 /**
5681 * Returns the state of all calls on the device.
5682 * <p>
5683 * This method considers not only calls in the Telephony stack, but also calls via other
5684 * {<!-- -->@link android.telecom.ConnectionService} implementations.
5685 * <p>
5686 * Note: The call state returned via this method may differ from what is reported by
5687 * {<!-- -->@link PhoneStateListener#onCallStateChanged(int, String)}, as that callback only considers
5688 * Telephony (mobile) calls.
5689 * <p>
5690 * Requires Permission:
5691 * {<!-- -->@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
5692 * targeting API level 31 + .
5693*
5694 * @return the current call state.
5695 * @deprecated Use {<!-- -->@link #getCallStateForSubscription} to retrieve the call state for a
5696 * specific telephony subscription (which allows carrier privileged apps),
5697 * {<!-- -->@link TelephonyCallback.CallStateListener} for real-time call state updates, or
5698 * {<!-- -->@link TelecomManager#isInCall()}, which supplies an aggregate "in call" state for the entire
5699 * device.
5700 */
5701 @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
5702 @Deprecated
5703 public @CallState int getCallState() {<!-- -->
5704 if (mContext != null) {<!-- -->
5705 TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
5706 if (telecomManager != null) {<!-- -->
5707 return telecomManager.getCallState();
5708 }
5709 }
5710 return CALL_STATE_IDLE;
5711 }

2.TelecomManager

Register

Source code location/frameworks/base/core/java/android/app/SystemServiceRegistry.java

685 registerService(Context.TELECOM_SERVICE, TelecomManager.class,
686 new CachedServiceFetcher<TelecomManager>() {<!-- -->
687 @Override
688 public TelecomManager createService(ContextImpl ctx) {<!-- -->
689 return new TelecomManager(ctx.getOuterContext());
690 }});

Note: The registration logic here is registered in the same process, there is no binder across processes

Source code location: /frameworks/base/core/java/android/app/ContextImpl.java

2048 public Object getSystemService(String name) {<!-- -->
2049 if (vmIncorrectContextUseEnabled()) {<!-- -->
2050 // Check incorrect Context usage.
2051 if (WINDOW_SERVICE.equals(name) & amp; & amp; !isUiContext()) {<!-- -->
2052 final String errorMessage = "Tried to access visual service"
2053 + SystemServiceRegistry.getSystemServiceClassName(name)
2054 + " from a non-visual Context:" + getOuterContext();
2055 final String message = "WindowManager should be accessed from Activity or other "
2056 + "visual Context. Use an Activity or a Context created with "
2057 + "Context#createWindowContext(int, Bundle), which are adjusted to "
2058 + "the configuration and visual bounds of an area on screen.";
2059 final Exception exception = new IllegalAccessException(errorMessage);
2060 StrictMode.onIncorrectContextUsed(message, exception);
2061 Log.e(TAG, errorMessage + " " + message, exception);
2062 }
2063 }
2064 return SystemServiceRegistry.getSystemService(this, name);
2065 }

Use

Source code location: /frameworks/base/telecomm/java/android/telecom/TelecomManager.java

1781 /**
1782 * Returns one of the following constants that represents the current state of Telecom:
1783*
1784 * {<!-- -->@link TelephonyManager#CALL_STATE_RINGING}
1785 * {<!-- -->@link TelephonyManager#CALL_STATE_OFFHOOK}
1786 * {<!-- -->@link TelephonyManager#CALL_STATE_IDLE}
1787*
1788 * Takes into consideration both managed and self-managed calls.
1789 * <p>
1790 * Requires Permission:
1791 * {<!-- -->@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} for applications
1792 * targeting API level 31 + .
1793*
1794 * @hide
1795 */
1796 @RequiresPermission(anyOf = {<!-- -->READ_PRIVILEGED_PHONE_STATE,
1797 android.Manifest.permission.READ_PHONE_STATE}, conditional = true)
1798 @SystemApi
1799 public @CallState int getCallState() {<!-- -->
1800 ITelecomService service = getTelecomService();
1801 if (service != null) {<!-- -->
1802 try {<!-- -->
1803 return service.getCallStateUsingPackage(mContext.getPackageName(),
1804 mContext.getAttributionTag());
1805 } catch (RemoteException e) {<!-- -->
1806 Log.d(TAG, "RemoteException calling getCallState().", e);
1807 }
1808 }
1809 return TelephonyManager.CALL_STATE_IDLE;
1810 }
1811

Just see here

@RequiresPermission(anyOf = {<!-- -->READ_PRIVILEGED_PHONE_STATE,android.Manifest.permission.READ_PHONE_STATE}, conditional = true)

But in fact, there is no requirement for targetsdk, nor is it a hard requirement to have READ_PHONE_STATE permission. Without it, it cannot run.

Continue to look at getTelecomService() here. This is when binder communication actually occurs.

2572 private ITelecomService getTelecomService() {<!-- -->
2573 if (mTelecomServiceOverride != null) {<!-- -->
2574 return mTelecomServiceOverride;
2575 }
2576 if (sTelecomService == null) {<!-- -->
2577 ITelecomService temp = ITelecomService.Stub.asInterface(
2578 ServiceManager.getService(Context.TELECOM_SERVICE));
2579 synchronized (CACHE_LOCK) {<!-- -->
2580 if (sTelecomService == null & amp; & amp; temp != null) {<!-- -->
2581 try {<!-- -->
2582 sTelecomService = temp;
2583 sTelecomService.asBinder().linkToDeath(SERVICE_DEATH, 0);
2584 } catch (Exception e) {<!-- -->
2585 sTelecomService = null;
2586 }
2587 }
2588 }
2589 }
2590 return sTelecomService;
2591 }

3.binder service ITelecomService

Register

The registration logic here is quite deep! ! !

Source code location: /frameworks/base/telecomm/java/android/telecom/TelecomManager.java

144 private void connectToTelecom() {<!-- -->
145 synchronized (mLock) {<!-- -->
146 if (mServiceConnection != null) {<!-- -->
147 // TODO: Is unbinding worth doing or wait for system to rebind?
148 mContext.unbindService(mServiceConnection);
149 mServiceConnection = null;
150 }
151
152 TelecomServiceConnection serviceConnection = new TelecomServiceConnection();
153 Intent intent = new Intent(SERVICE_ACTION);
154 intent.setComponent(SERVICE_COMPONENT);
155 int flags = Context.BIND_IMPORTANT | Context.BIND_FOREGROUND_SERVICE
156 | Context.BIND_AUTO_CREATE;
157
158 // Bind to Telecom and register the service
159 if (mContext.bindServiceAsUser(intent, serviceConnection, flags, UserHandle.SYSTEM)) {<!-- -->
160 mServiceConnection = serviceConnection;
161 }
162 }
163 }

After peeling off the cocoon, the real binder server is

Source code location: /packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java

944 /**
945 * @see TelecomManager#getCallState()
946 */
947 @Override
948 public int getCallStateUsingPackage(String callingPackage, String callingFeatureId) {<!-- -->
949 try {<!-- -->
950 Log.startSession("TSI.getCallStateUsingPackage");
951 if (CompatChanges.isChangeEnabled(
952 TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION, callingPackage,
953 Binder.getCallingUserHandle())) {<!-- -->
954 // Bypass canReadPhoneState check if this is being called from SHELL UID
955 if (Binder.getCallingUid() != Process.SHELL_UID & amp; & amp; !canReadPhoneState(
956 callingPackage, callingFeatureId, "getCallState")) {<!-- -->
957 throw new SecurityException("getCallState API requires READ_PHONE_STATE"
958 + " for API version 31 + ");
959 }
960 }
961 synchronized (mLock) {<!-- -->
962 return mCallsManager.getCallState();
963 }
964 } finally {<!-- -->
965 Log.endSession();
966 }
967 }

Use

continue to see
Source code location: /packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java

getCallStateUsingPackage method, key verification logic

CompatChanges.isChangeEnabled(
952 TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION, callingPackage,
953 Binder.getCallingUserHandle())

Source code location: frameworks/base/telecomm/java/android/telecom/TelecomManager.java

1010 /**
1011 * Enable READ_PHONE_STATE protection on APIs querying and notifying call state, such as
1012 * {<!-- -->@code TelecomManager#getCallState}, {@link TelephonyManager#getCallStateForSubscription()},
1013 * and {<!-- -->@link android.telephony.TelephonyCallback.CallStateListener}.
1014 * @hide
1015 */
1016 @ChangeId
1017 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
1018 // this magic number is a bug ID
1019 public static final long ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION = 157233955L;

Back to the annotation EnabledSince

Follow TelephonyManager#listen(PhoneStateListener listener, int events)
Different paths to the same destination