Android app targetSdk从28升级到33问题汇总

这篇具有很好参考价值的文章主要介绍了Android app targetSdk从28升级到33问题汇总。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

问题说明:targetsdkversion升级到12或者以上,设备运行系统至少12的话,如果不动态申请READ PHONE STATE权限则报错SecurityException

错误日志

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
过程概述:

1.调用入口

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      }

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

2.binder service之TelephonyRegistryManager TelephonyRegistryManager(不是binder服务端!!!)

注册

源码位置/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                  }});

使用

源码位置 /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 & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
1029              eventList.add(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED);
1030          }
1031  

最后转换成EVENT_LEGACY_CALL_STATE_CHANGED

至此走到ITelephonyRegistry#listenWithEventList(int subId, String callingPackage, String callingFeatureId,IPhoneStateListener callback, int[] events, boolean notifyNow)

3.binder service之TelephonyRegistry

注册

源码位置 /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();
注意:可以看到两个binder service注册位置不一样 经确认,前者运行在调用进程(同一个jvm),后者运行在binder服务端进程,这里是system server进程,通过ServiceManager注册

使用

源码位置: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          }//关键权限校验代码
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.先看权限检查TelephonyPermissions.checkCallingOrSelfReadPhoneState( mContext, subId, callingPackage, callingFeatureId, message)

可以看到先校验READ_PRIVILEGED_PHONE_STATE,如果没有该权限,则校验READ_PHONE_STATE,如果都没有,则抛出异常SecurityException

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.再看什么情况下需要权限

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                  && mConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(
476                          callingPackage, userHandle)) {
477              return true;
478          }
。。。
504      }

因为events中包含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          }

源码位置: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;

到了这里,就可以确定targetsdk版本从android 12开始并且运行系统版本不低于12则这个权限READ PHONE STATE必须动态申请,否则binder通信会报错SecurityException

注意:注解@EnabledSince 要和注解@ChangeId搭配使用

源码位置 /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  

为什么同时要求运行系统不低于12

答:低于12的系统版本中没有这些逻辑要求,可以对照12以下系统源码

二.TelephonyManager#getCallState()

问题说明:targetsdkversion升级到12或者以上,设备运行系统至少12的话,如果不动态申请READ PHONE STATE权限则报错SecurityException

跟上一个问题类似

错误日志

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)

这里我们可以看到是android.os.Parcel.readException抛出的,因为涉及到binder跨进程通信,属于binder服务端权限校验异常,会发送异常数据到binder客户端,所以binder客户端才会通过Parcel读取到exception

还是跟android12源码
过程概述:

1.调用入口

光看这段代码是看不出有任何权限要求的

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

注册

源码位置/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              }});

注意:这里注册逻辑是在同一个进程注册的,没有binder跨进程

源码位置: /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) && !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      }

使用

源码位置: /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  

这里只是看到

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

但是其实并没有对targetsdk有要求,也不是硬性要求必须有READ_PHONE_STATE权限,没有则不能运行

这里继续看getTelecomService(),这时候才是真正有binder通信

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 && 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

注册

这里注册逻辑比较深!!!

源码位置: /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      }

抽丝剥茧,真正的binder服务端是在

源码位置: /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 && !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          }

使用

继续看到
源码位置: /packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java

getCallStateUsingPackage这个方法,关键校验逻辑

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

源码位置: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;

又回到注解EnabledSince

跟TelephonyManager#listen(PhoneStateListener listener, int events)
殊途同归文章来源地址https://www.toymoban.com/news/detail-772133.html

到了这里,关于Android app targetSdk从28升级到33问题汇总的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android/iOS APP备案:遇到的问题汇总指南!

    APP备案经常有的朋友会问对以下问题,今天小编给大家总结下,希望对有app备案疑问的朋友有所帮助,好了话不多说,现在开始整理,给大家简单说下一些常见的问题。 问题1:app和小程序由谁来备案? 答:app由网络接入服务者代为备案,小程序由小程序运营平台代为备案。

    2024年03月15日
    浏览(58)
  • android 33 适配升级踩坑

    工程添加参考官网: 设置 Android 13 SDK  |  Android 开发者  |  Android Developers   在 Android Studio 中,您可以按如下方式安装 Android 13 SDK: 依次点击  Tools SDK Manager 。 在  SDK Platforms  标签页中,选择  Android Tiramisu Preview 。 在  SDK Tools  标签页中,选择  Android SDK Build-Tools 33 。

    2023年04月12日
    浏览(59)
  • Android 编译版本升级target 33

    应用编译版本: 环境编译版本: kotlin版本未改变,仅升级了,gradle编译版本。 build.gradle配置: gradle-wrapper.properties 配置: 升级步骤二:解决library编译问题 1、修改 library 的 build.gradle 文件 编译版本修改 kotlin配置修改,在build.gradle增加配置 viewBinding 不过可以全部配置。 2、

    2024年02月05日
    浏览(280)
  • Android Studio升级到Android API 33版本后,XML布局输入没有提示

      低版本的Android Studio升级到Android API 33版本后,XML布局输入没有提示。查一下我目前使用的Android Studio 是2021年发布,而Android API 33是2022年发布的,这是由低版本升级到高版本造成不兼容的问题。解决方法有两种: 第一种方法: 降低compileSdk的版本,但每次新建项目都要修改

    2024年02月10日
    浏览(72)
  • Android APP逆向分析工具和方法汇总

    受益于移动设备的广泛普及,移动应用近年来得到了蓬勃发展。基于移动设备集成的各类传感器,众多功能丰富的移动应用被开发出来,聚集了大量高价值用户隐私数据,包括用户身份信息、地理位置信息、账户资料信息等。用户在享受移动应用带来便利的同时,其隐私安全

    2024年02月12日
    浏览(51)
  • android App内下载apk 并升级

    主要代码 1.下载apk代码;安装APP权限申请;3.文件存储; 兼容Android 12 版本 存储权限处理: 1.新建xml文件; 2.manifest配置文件: 注,可运行下载链接 APP内升级代码源码

    2024年02月13日
    浏览(38)
  • Android 系统级APP 升级方案 OTA全流程

    支持原创,请关注专栏: 高质量文章导航 一.Android ota固件编译 OTA 介绍 OTA ( over the air )升级是 Android 系统提供的标准软件升级方式。它功能强大,提供了 完全升级(完整包)、增量升级模式(差异包),可以通过本地升级,也可以通过网络升级 1.完整包 完整包所包含内容

    2024年02月02日
    浏览(86)
  • 【Android】app应用内版本更新升级(DownloadManager下载,适配Android6.0以上所有版本)

    版本的升级和更新是一个线上App所必备的功能,App的升级安装包主要通过 应用商店 或者 应用内下载 两种方式获得,大部分app这两种方式都会具备,应用商店只需要上传对应平台审核通过即可,而应用内更新一般是通过以下几种方式: 1.集成第三方库如 appupdateX、bugly 的更新

    2024年02月11日
    浏览(115)
  • Android问题笔记 - 使用SDK33导致xml布局代码没有任何提示了

    专栏分享 点击跳转=Unity3D特效百例 点击跳转=案例项目实战源码 点击跳转=游戏脚本-辅助自动化 点击跳转=Android控件全解手册 点击跳转=Scratch编程案例 点击跳转=软考全系列 众所周知,人生是一个漫长的流程,不断 克服困难 ,不断反思前进的过程。在这个过程中会产生很多对

    2024年02月11日
    浏览(53)
  • Android实现App内自动升级,适配了安卓7、8及以上版本

            应用发布后,要实现灰度升级控制,如果只依赖各家应用市场是不够的,还需要自己在应用中控制升级逻辑。并且每家应用市场上新审核也是一件很麻烦的事情,尤其像至简网格这样的应用,甚至没在应用市场上架,更不可能依赖它们了。所以必须要在应用中实现自

    2024年02月10日
    浏览(70)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包