系列文章
- android使用ContentProvider初始化sdk,初始化时机
- Android ContentProvider初始化流程简化分析
- Android-Firebase快速解决合规问题第1篇,汇总篇,一步解决问题
- Android-Firebase快速解决合规问题第2篇,解决FirebasePerformance库获取软件安装列表的行为
- Android-Firebase快速解决合规问题第3篇,解决FirebaseCrashlytics库违规网络请求、获取AndroidId问题
- Android-Firebase快速解决合规问题第4篇,解决FirebaseAnalytics库违规获取应用列表问题
背景
2022年9月,小米应用商店上架审核,提示存在违规行为。
违规行为:未经许可读取个人信息 | 获取应用列表
依赖环境
demo的环境如下,只是为了演示firebase出现的问题,本篇文章基于Flutter作为开发语言,实现了demo演示问题,原生库、RN库同理可以解决问题。
android版本:
build.gradle
compileSdkVersion 31
minSdkVersion 21
targetSdkVersion 31
futter版本:Flutter 2.10.5
pubspec.yaml
firebase_core: 1.10.0
firebase_messaging: 10.0.0
firebase_crashlytics: 2.2.0
firebase_analytics: 9.1.0
firebase_performance: 0.7.0+3
dio_firebase_performance: ^0.3.0
解决方案
修改firebase_analytics插件,
修改的源码,分支firebase_analytics-v9.1.0-20220913。
等到用户同意后再执行,flutter调用以下方法,就会初始化FirebaseAnalytics。
// flutter
FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(true)
堆栈信息
callstack:android.app.ApplicationPackageManager.getInstallerPackageName:2044;
com.google.android.gms.measurement.internal.e3.k:8;
com.google.android.gms.measurement.internal.c4.i:2;
com.google.android.gms.measurement.internal.y4.c:13;
com.google.android.gms.measurement.internal.x4.run:1;
java.util.concurrent.Executors$RunnableAdapter.call:458;
java.util.concurrent.FutureTask.run:266;
com.google.android.gms.measurement.internal.u4.run:6;
官方反馈的堆栈信息,看得莫名其妙。只好根据之前的定位问题的方式,找到栈顶方法,进行debug调试。
分析问题
从堆栈信息和debug断点模式来看,应用启动后就会执行到这,是gms(谷歌移动服务)发生的问题。那现在就要找到什么地方调用起来。
尝试方案一
根据之前的猜测可能是引入了gms相关的provider,自动初始化了。找到debug包中的AndroidManifest文件,查看有关的gms配置,我给每一项都加入tools:node=“remove”,移除该项配置,结果并没有用。
<receiver
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
android:enabled="true"
android:exported="false" >
</receiver>
<service
android:name="com.google.android.gms.measurement.AppMeasurementService"
android:enabled="true"
android:exported="false" />
<service
android:name="com.google.android.gms.measurement.AppMeasurementJobService"
android:enabled="true"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name="com.google.android.gms.auth.api.signin.RevocationBoundService"
android:permission="com.google.android.gms.auth.api.signin.permission.REVOCATION_NOTIFICATION"
android:exported="true" />
<activity
android:theme="@ref/0x01030010"
android:name="com.google.android.gms.common.api.GoogleApiActivity"
android:exported="false" />
尝试方案二
既然与gms,查看堆栈,找到对应的库,发现是play-services-measurement-impl中触发的,那就想办法移除这个库。
在app的gradle中,移除com.google.android.gms相关的库,我发现两个都有触发的可能性,所以都移除了。
configurations {
all {
// // 重点是这个
exclude group: "com.google.android.gms", module: "play-services-measurement-sdk"
exclude group: "com.google.android.gms", module: "play-services-measurement-impl"
}
}
再次运行后,发现不会再出现由这两个库引起调用getInstallerPackageName方法了。
但在继续debug调试的过程,其他的gms库依然存在违规获取应用列表问题。如下图
我也play-services-measurement-basement库给移除掉,但移除后发现,app跑不起来。
继续debug调试的过程,又又又发现其他gms库也存在问题,在移除 play-services-measurement-sdk-api或play-services-measurement-api时,会白屏,并且提示错误,FirebaseAnalytics.getInstance初始化失败。实在无力吐槽了。
// 出现错误
Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/android/gms/internal/measurement/zzee;
at com.google.firebase.analytics.FirebaseAnalytics.getInstance(com.google.android.gms:play-services-measurement-api@@20.0.0:1)
从这个错误,发现一点问题,好像与FirebaseAnalytics.getInstance的初始化有关,又去搜了一下google。android.gms.internal.measurement这个包有啥用,firebase官方文档还有真有这个类的介绍。
包含配置 Firebase Analytics 核心服务
顺着FirebaseAnalytics.getInstance去找问题,找到该方法的位置。
发现FirebaseAnalytics类是在com.google.android.gms:play-services-measurement-api库中引入,那也证实了FirebaseAnalytics与gms某些库有关联。
解决问题
找到FirebaseAnalytics.getInstance调用时机。
问题发生在FirebaseAnalytics初始化,FirebaseAnalytics.getInstance(context);
经过排查,发现在Flutter的插件FlutterFirebaseAnalyticsPlugin类中,在FlutterFirebaseAnalyticsPlugin被Flutter加载后,就执行了FirebaseAnalytics初始化方法,然后导致gms服务被加载,然后违规获取应用列表的行为。
只要修改firebase_analytics插件,等到用户同意后再执行。
github下载源码 基于v9.1.0分支
public class FlutterFirebaseAnalyticsPlugin
implements FlutterFirebasePlugin, MethodCallHandler, FlutterPlugin {
private FirebaseAnalytics analytics;
private MethodChannel channel;
private Context context;
private void initInstance(BinaryMessenger messenger, Context context) {
// 第1步:注释掉这个位置
// analytics = FirebaseAnalytics.getInstance(context);
this.context = context;
String channelName = "plugins.flutter.io/firebase_analytics";
channel = new MethodChannel(messenger, channelName);
channel.setMethodCallHandler(this);
FlutterFirebasePluginRegistry.registerPlugin(channelName, this);
}
/**
* 第2步
* 20220913自己加入一个方法,初始化
*/
private void initFirebaseAnalytics(){
if(this.context != null && this.analytics == null){
Log.i("zzb", "初始化initFirebaseAnalytics");
analytics = FirebaseAnalytics.getInstance(this.context);
}
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
Task<?> methodCallTask;
switch (call.method) {
// 第3步,加入初始化方法,给flutter调用
case "Analytics#initFirebaseAnalytics":
initFirebaseAnalytics();
return;
}
}
private Task<Void> handleSetAnalyticsCollectionEnabled(final Map<String, Object> arguments) {
return Tasks.call(
cachedThreadPool,
() -> {
// 第4步,我偷懒,在handleSetAnalyticsCollectionEnabled方法进行初始化
initFirebaseAnalytics();
final Boolean enabled =
(Boolean) Objects.requireNonNull(arguments.get(Constants.ENABLED));
if(analytics != null){
Log.i("zzb", "执行analytics handleSetAnalyticsCollectionEnabled");
analytics.setAnalyticsCollectionEnabled(enabled);
}
return null;
});
}
}
由于Flutter与原生进行通信使用firebase_analytics_platform_interface库,如果还要修改firebase_analytics_platform_interface库,就要维护两个库,firebase_analytics和firebase_analytics_platform_interface,不想修改这个库的内容。
所以偷懒的方式,把初始化方法,放在了现有的方法FlutterFirebaseAnalyticsPlugin.java的handleSetAnalyticsCollectionEnabled()中。
Flutter层需要调用一次FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(true)来初始化analytics。
最后
Firebase实在太恶心了,在国内强烈不建议使用!!!firebase生态库相互唤起,出问题了很难定位。