InShot(com.camerasideas.instashot)变现与分析技术实现深度分析报告

分析对象:反编译源码 /Users/ccc/Downloads/app/out/jadx_src/sources/,解码清单 /Users/ccc/Downloads/app/out/jadx_src/resources/AndroidManifest.xml 包名:com.camerasideas.instashot(杭州影笑科技 / InShot Inc.)


0. 总体架构:商业化代码如何组织

核心前提:这家公司的商业化代码是"自研 SDK(代号 Shantanu)+ R8 强混淆"组织起来的。

三层抽象:

  1. 基础设施层(自研 Shantanu SDK,强混淆)
    • com.shantanu.mobileads → 混淆为 De(MobileAds/SdkConfiguration/SdkSettings/CmpService)、Ge(日志/常量)、Ee(Waterfall)、Fe(插屏)、He(收益适配器)、Ie(原生/MREC/横幅)、Je(激励/AdView)、Le(字符串池/全局上下文)
    • com.shantanu.iap(服务器端 IAP 验证 + 账户绑定,1484 特性)
    • com.shantanu.mmkv / com.shantanu.storage / com.shantanu.network(MMKV、存储、网络)
  2. 配置/投放层(未混淆,com.camerasideas)
    • V5.c(AdDeploy:设备定向、placement 开关)、N.a(最终广告位 ID)、X3.C1246g(AppCapabilities)+ com.camerasideas.instashot.remote.d(AppRemoteConfig)+ Firebase RC
    • 一切可远程下发,本地 assets 有完整回退(local_ad_waterfall.json / local_ad_deploy_list.json / popup_config.json)
  3. 业务接入层
    • 各 Activity/Fragment 只调 V5.g(Banner)/V5.n(Interstitial)/V5.p(MREC)/V5.r(Reward)单例
    • 展示前统一过 S.d(ctx).k()/w()(订阅判定)+ V5.c.h(placement)(投放开关)+ 频控 JSON

关键点速览:

主题 结论
广告聚合 AppLovin MAX + 自研 Shantanu MobileAds;自定义 Waterfall 全部直连 AdMob(GoogleMediationAdapter),非标准 mediation
订阅 Google Play Billing + 4 渠道 PurchaseManager(含华为);服务器端 Shantanu IAP 账户绑定;Play/Huawei Integrity
分析 Firebase Analytics + Crashlytics(经 p348ri.M 门面 + 协程 Channel 队列);无 AppsFlyer/Adjust
远程配置 FirebaseRemoteConfigWrapper → remote.d → X3.C1246g 静态门面 + V5.c 投放
APM 自定义 CrashHandler(吞第三方 SDK 崩溃)+ 微信 xlog + Crashlytics 日志镜像
隐私 Google UMP(混淆在 De 包)+ 自研隐私页 PolicyFragment;AppLovin setDoNotSell(true),TCF 禁用

1. 广告聚合:AppLovin MAX + 自定义 Waterfall(AdMob 直连)

1.1 初始化点

类:com/camerasideas/startup/MobileAdsInitializer.java(实现 Application.ActivityLifecycleCallbacks)

初始化模型 = "延迟到主线程空闲 + 单线程执行器 + 订阅用户跳过":

// MobileAdsInitializer.a(Context)
if (S.d(cVarC.f12059a).k()) {          // k() = 未购买 remove.ads → 才初始化广告
    Iterator it = Arrays.asList("I_VIDEO_AFTER_SAVE","I_PHOTO_AFTER_SAVE",
        "R_REWARDED_UNLOCK_","M_VIDEO_RESULT","M_PHOTO_RESULT",
        "B_VIDEO_EDITING","B_PHOTO_EDITING").iterator();   // 7 个广告位,任一开启才初始化
    ...
    strC = C1246g.f14024b.h("ad_waterfall_list_v_max");     // 远程 waterfall JSON
    if (TextUtils.isEmpty(strC)) strC = B.c(openRawResource(R.raw.local_ad_waterfall)); // 本地回退
    ... // 硬编码 AppLovin SDK Key: kBiFuolh9J-eNFCitXmimSOs_Gt4IuV04CGy_0e8Kdoht_PkW9QGcp6c4zgMrANvNdsMdUVy6HezG2gnC62pv8
    AppLovinSdk.getInstance(context2).initialize(
        AppLovinSdkInitializationConfiguration.builder(sdkKey)
            .setMediationProvider("max").setAdUnitIds(adUnitIds).build(), new f());
}

类:De/g.java(MobileAds,混淆后的 Shantanu 广告 SDK 单例)

public final class g {
    public static final a f2363a = new a();   // 初始化完成回调持有者
    public static boolean f2364b;             // isInitialized
    public static boolean f2365c;             // isInitializing
    public static AppLovinSdkConfiguration f2366d;
}

类:De/o.java(SdkConfiguration)+ De/p.java(SdkSettings)

// De/p: 解析 waterfall JSON → map[ad_unit_id] → Ee.e(Waterfall 迭代器)
public static Ee.e a(String str) {
    if (map.isEmpty() && f2391b != null) {
        JSONArray jSONArray = new JSONArray(f2391b);   // f2391b = waterfall JSON 字符串
        for (...) map.put(jSONObject.optString("ad_unit_id"), jSONObject);
    }
    ...
}
public static boolean b(String str) {   // 该 ad unit 是否走自定义 waterfall
    if (f2392c) return true;            // preferCustomWaterfall
    return f2394e != null && f2394e.contains(str);   // customWaterfallAdUnitIds
}

1.2 Waterfall 结构(关键证据)

资源:resources/res/raw/local_ad_waterfall.json(201 行)

12 个 MAX ad unit,共 32 步 waterfall,全部指向 AdMob(com.applovin.mediation.adapters.GoogleMediationAdapter custom event):

{ "ad_unit_id": "820327943890bc87", "ad_unit_name": "b_photo_editing",
  "waterfall": [
    { "x-custom-event-class-name": "com.applovin.mediation.adapters.GoogleMediationAdapter",
      "x-custom-event-class-data": "{\"placementId\":\"ca-app-pub-4033211073363733/8382207922\",\"format\":\"BANNER\"}" },
    { "x-custom-event-class-name": "com.applovin.mediation.adapters.GoogleMediationAdapter",
      "x-custom-event-class-data": "{\"placementId\":\"ca-app-pub-4033211073363733/7069126255\",\"format\":\"BANNER\"}" },
    ...

广告位 ↔ 格式映射:

MAX ad_unit_id(示例) 广告位名 格式
820327943890bc87 b_photo_editing BANNER(3 个 AdMob 位级联)
7279f670086be16e / d9b7838b717e63f3 b_video_editing BANNER
2d17c059ef2e19ba m_photo_result MREC 320×250
40f525f9e584b412 / c9a8747dce6a96bd m_video_result MREC
c3d648d209900d60 i_photo_after_save INTER
ad9961595904b039 / 4b00eed1bfa9f67d i_video_after_save INTER
57f5a64e43ded401 i_material_unlock INTER
c415933b89159953 / ef9b460767d45f7e reward_ad REWARDED

结论:自定义 waterfall 只填 AdMob(同一 AdMob 位 id 可出现在多个 MAX 位下,同一位下 2-3 个 AdMob 位级联做兜底)。远程配置 ad_waterfall_list_v_max 可整体替换此 JSON。

解析器:

其他打包的广告 SDK(未进自定义 waterfall,仅随 MAX 适配器/独立库存在):

1.3 广告位 ID 解析:设备定向投放

类:V5/c.java(AdDeploy)+ com/camerasideas/instashot/remote/a.java(AdConfig)

// V5/c.d(): 远程 JSON → 解析,回退本地 raw/local_ad_deploy_list.json
strH = dVar.h("ad_supported_info_android");      // AdConfig: banner 支持、默认 ad unit id
strH2 = dVar.h("ad_deploy_list_v_1530");         // deployNodes: 按 OS/RAM/机型 换 ad unit
this.f12064f = (b) new Gson().fromJson(strH2, ...);
N.a.f7641a = a("I_VIDEO_AFTER_SAVE", this.f12063e.f30980d);  // 最终 ad unit id 落盘到 N.a
N.a.f7642b = a("I_PHOTO_AFTER_SAVE", ...);
N.a.f7643c = a("R_REWARDED_UNLOCK_", ...);
N.a.f7644d = a("M_VIDEO_RESULT", ...);
N.a.f7645e = a("M_PHOTO_RESULT", ...);
N.a.f7646f = a("B_VIDEO_EDITING", ...);
N.a.f7647g = a("B_PHOTO_EDITING", ...);

类:N/a.java(AdUnitIdManager,静态最终广告位 ID)

public static String f7641a = "ad9961595904b039";  // I_VIDEO_AFTER_SAVE
public static String f7642b = "ad9961595904b039";  // I_PHOTO_AFTER_SAVE
public static String f7643c = "c415933b89159953";  // R_REWARDED_UNLOCK_
public static String f7644d = "40f525f9e584b412";  // M_VIDEO_RESULT
public static String f7645e = "2d17c059ef2e19ba";  // M_PHOTO_RESULT
public static String f7646f = "7279f670086be16e";  // B_VIDEO_EDITING
public static String f7647g = "820327943890bc87";  // B_PHOTO_EDITING

1.4 展示策略(插屏/激励/横幅/MREC)

插屏(After-Save Interstitial)

// VideoResultActivity.onResume() 片段
if (this.f13770I.getPopupType() == com.camerasideas.instashot.udpate.h.AD && !this.f13771J && !this.f13772K) {
    this.f13771J = true;
    if (!getIntent().getBooleanExtra("Key.Retry.Save.Video", false)) {
        if (V5.l.c(this) && V5.n.f12115b.b(this, N.a.f7641a, "I_VIDEO_AFTER_SAVE")) {
            p202k4.o.p0(this, "isFirstVideoInterstitialFinished", true);
            p202k4.o.r0(this, 0, "VideoSaveTimesSinceLastInterstitial");
        } else {
            // 计数 +1
        }
    }
}
// V5/n.java: 加载/展示,统一过 AdDeploy 开关
public final void a(Context context, String str, String str2) {   // 预加载
    if (!c.c(contextD).h(str2)) { ... return; }                    // AdDeploy 不支持 → 放弃
    ... // Fe.g(ShantanuInterstitialAd) 组装,按 ad unit id 去重缓存
}
public final boolean b(B b10, String str, String str2) {          // 展示
    if (!c.c(b10).h(str2)) return false;
    Fe.g gVar = (Fe.g) this.f12116a.get(str);
    ... return gVar.f3570f.c(b10, str2);                          // Fe.a.isReady() + showAd()
}
// Fe/g.java: 加载失败指数退避重试
public static void b(g gVar, Ee.a aVar) {
    int i10 = gVar.f3568d + 1; gVar.f3568d = i10;
    long millis = TimeUnit.SECONDS.toMillis((long) Math.pow(2.0d, Math.min(5, i10)));
    ... gVar.f3567c.postDelayed(new i(gVar), millis);
}
// 双路径: De.p.b(adUnitId) ? 自定义 waterfall 加载(Fe.q) : MAX 加载(Fe.f)
if (De.p.b(str)) { e(); return; }   // "Use custom waterfall mediation directly"
... fVar.eVar.setListener(dVar); eVar.setRevenueListener(new He.d(...)); eVar.loadAd();

激励视频(Rewarded)

// V5/r.java: 请求展示入口
public final void g(String str, q qVar, Runnable runnable) {
    M.g(InstashotApplication.f24926a, "unlock_ad_source", g0.j(g0.f("R_REWARDED_UNLOCK_", str)), ...);
    this.f12123a = str;
    f fVar = new f(); fVar.f12092a = runnable; this.f12124b = fVar;   // 奖励回调
    ...
    boolean zA = s.f12133d.a(this.f12130h);      // 是否已 loaded
    if (zA && !s.f12133d.b(str)) {
        this.f12129g.sh();
        b bVar = new b(); this.f12125c = bVar;
        this.f12126d.postDelayed(bVar, this.f12127e);   // f12127e = waiting_reward_timeout_millis(默认10000)
    }
}
// 完成 → 发放奖励并加载下一条
@Override public final void g(String str, Ee.d dVar) { ... r.a(r.this); }  // onRewardedAdCompleted
// Je/a.java: MAX 激励封装
public final class a extends c {
    public a(Context context, String str) {
        super(context, str);
        AppLovinContextFix.ensureContext(context);
        this.f6297f = MaxRewardedAd.getInstance(str);
    }
    public final boolean b() { return this.f6297f.isReady(); }
    public final boolean c(Activity activity, String str) {
        if (!this.f6297f.isReady()) return false;
        this.f6297f.showAd(str, activity); return true;
    }
}

横幅(Banner)

MREC(Medium Rectangle)

设备/订阅联动门控(S.javas(boolean)):

public final boolean s(boolean z10) {   // z10=true 视频横幅 / false 图片横幅
    V5.c cVarC = V5.c.c(context);
    boolean z11 = z10 ? cVarC.f12063e.f30979c : cVarC.f12063e.f30978b;  // video/photo_banner_supported
    if (!cVarC.f12063e.f30977a && !Rf.e.g(context)) z11 = false;        // 平板专属开关
    if (iA == 5 || iA == 4) z11 = false;                                 // 设备分级
    return z11 && cVarC.h(z10 ? "B_VIDEO_EDITING" : "B_PHOTO_EDITING");
}

2. 订阅/内购

2.1 启动流程

类:com/camerasideas/startup/InitializeBillingTask.java(@Keep,启动链 Env → Billing → State → Lib → Resource,见 InstashotApplication.onCreatec0010a.a(InitializeBillingTask.class.getName()))

// updateProInfo()
p321qa.o oVarA = p321qa.o.d(this.mContext).a();
Future futureF = oVarA.f();                        // queryPurchaseHistory
Future<z> futureG = oVarA.g(null);                 // queryPurchases
Future<x> futureE = oVarA.e("subs", C2460y.f31208b, null);  // queryProductDetails("subs" 组)
updatePurchaseHistoryRecord(this.mContext, futureF);
updateProInfo(futureG);
updateProductDetails(futureE);
p321qa.o.f60295b = Integer.parseInt(C1246g.f14024b.h("key_subscription_replacement_mode")); // 订阅替换模式,默认3

2.2 计费抽象层

类:p321qa/o.java(BillingService,抽象基类)

public abstract class o {
    public static boolean f60294a = false;   // forcePlayBilling(SettingFragment 调试开关可开)
    public static int f60295b = 3;           // 订阅替换模式(远程下发)
    public static a d(Context context) { ... }
    // a() → 两个实现:
    //   z10 ? new p321qa.a(context, mode)   // BillingFakePaymentFailure(调试)
    //        : new g(context, mode)         // BillingManager(真实 Google Play Billing)
    public abstract Future<i> a(String sku, InterfaceC1513m listener);   // launchPurchaseFlow
    public abstract void c(j jVar, ...);                                 // consumeAsync
    public abstract Future<x> e(String productType, List<String> skus, ...); // queryProductDetails
    public abstract Future f();                                          // queryPurchaseHistory
    public abstract Future<z> g(InterfaceC1538z);                        // queryPurchases
    public abstract void h(InterfaceC1503h);                             // 购买回调
}

2.3 SKU 组织(订阅组)

类:com/camerasideas/instashot/store/billing/C2460y.java(SkuDefinition)

public static final List<String> f31207a = Arrays.asList(
    "com.camerasideas.instashot.pro.permanent",
    "com.camerasideas.instashot.vip.yearly",
    "com.camerasideas.instashot.vip.yearly.freetrail",
    "com.camerasideas.instashot.vip.yearly.freetrail.introductory",
    "com.camerasideas.instashot.vip.monthly",
    "com.camerasideas.instashot.vip.monthly.introductory",
    "com.camerasideas.instashot.vip.monthly.introductory.hw",   // hw = 华为渠道变体
    "com.camerasideas.instashot.vip.monthly.introductory.hw2",
    "com.camerasideas.instashot.fup");                           // fup = 限时解锁功能

public static final List<String> f31208b = /* 订阅(subs)类型,用于 queryProductDetails:
    vip.monthly(+introductory)、vip.yearly(+freetrail+introductory) */
public static final List<String> f31209c = /* pro.permanent —— 永久组 */
public static final List<String> f31210d = /* vip.monthly(+intro.hw/hw2) —— 月度组 */
public static final List<String> f31212f = /* vip.yearly(+freetrail+introductory) —— 年度组 */

// 免费试用映射
public static String a(String str) { /* vip.monthly → "p1m"; vip.yearly.freetrail → "p1y"(basePlanId) */ }
public static String b(String str, boolean z10) { /* vip.yearly.freetrail && z10 → "freetrial"(offerId) */ }
public static String c(String str) { /* 订阅 → "subs"; 其他 → "inapp" */ }

2.4 权益判定(核心)

类:com/camerasideas/instashot/store/billing/S.java(UserManager,单例,S.d(context) 获取)

public final boolean k() { return !m("com.camerasideas.instashot.remove.ads"); }  // 是否展示广告
public final boolean w() {   // 是否 Pro(最高级判定)
    if (com.google.gson.internal.s.g(context) || u() || v()) return true;  // gson 钩子 / 内购 / 华为
    return !A1.j0(context) && Ce.c.a(context, p325qe.b.f60328a).getBoolean("DebugPro", false);
}
public final boolean u() { return E.c(context) || com.google.gson.internal.s.g(context) || x("...fup"); }
public final boolean v() { return prefs.getBoolean("SubscribeProOfHw", false) || gson.s.g(context); }
public final boolean m(String sku) {   // 单 SKU 解锁判定:订阅态 || 本地解锁标记 || 时间窗内(fup)
    I(context, str);                    // fup 过期检查:UnlockTimeMillis_<sku> + 有效时长(g())
    return w() || E.d(context, str) || prefs.getBoolean(str, false);
}
public final boolean z() { /* 遍历 f31209c(permanent) */ }
public final boolean y() { /* 遍历 f31210d(monthly) */ }
public final boolean A() { /* 遍历 f31212f(yearly,含 freetrail) */ }
public final boolean t() { /* 购买有效标记 */ }
static InterfaceC2448l c(Context context, int i) {   // 0/1/2/3 四渠道
    if (i == 0) return new C2440d(context);   // Google Play
    if (i == 1) return new C2441e(context);   // 华为?
    if (i == 2) return new C2442f(context);
    return i != 3 ? new C2440d(context) : new G(context);   // 3 = Shantanu IAP 渠道
}
// 每渠道提供: a() 聚合有效 PurchaseToken、b() PurchaseInfo 列表、d(sku) 单 SKU token

2.5 服务器端验证 + 账户绑定(Shantanu IAP)

类:com/shantanu/iap/v.java(IapServiceImpl,OkHttp 服务端客户端)

public final class v extends AbstractC3588c {
    public final Future<BindResult> bindAsync(String accountId, List<PurchaseInfo> list);
    public final Future<BindResult> bindByOrderIdAsync(String accountId, String orderId, String ...);
    public final Future<QueryAccountInfoResult> queryAccountInfoAsync(String accountId);
    public final Future<Result> queryAccountStateAsync(String accountId);
    public final Future<QueryOrderStateResult> queryOrderStateAsync(boolean, List<PurchaseInfo>);
    public final Future<QueryPurchaseResult> queryPurchasesAsync(String accountId, C);
    public final Future<QueryReviewStateResult> queryReviewStateAsync(...);   // 审核中状态
    public final Future<Result> removeAccountAsync(String accountId);
    public final Future<Result> feedbackAsync(...);
}

2.6 Play Integrity(注意:不在 com.camerasideas.safe,而在 com.android.auth.integrity)

类:com/android/auth/integrity/IntegrityInitializer.java

private static final String META_KEY_HUAWEI_APP_ID = "com.android.auth.HuaweiAppId";
private static final String META_KEY_INTEGRITY_TYPE = "com.android.auth.IntegrityType";
private static final String META_KEY_PROJECT_ID = "com.android.auth.IntegrityProjectNumber";  // ← 用户提到的键
...
String string = bundle.getString(META_KEY_INTEGRITY_TYPE, "google");  // google | huawei
String string2 = bundle.getString(META_KEY_PROJECT_ID, "0");          // "number-xxx"
string2 = string2.replace("number-", "");
gVar.init(context, new h(Long.parseLong(string2), Long.parseLong(string3), string));

类:g.java(IntegrityManager)+ GoogleIntegrityManager.java / HuaweiIntegrityManager.java(反射选择)

// g.init(): 按 type 反射实例化
Class.forName(type.equalsIgnoreCase("huawei") ? "com.android.auth.integrity.HuaweiIntegrityManager"
                                              : "com.android.auth.integrity.GoogleIntegrityManager");
// 接口: requestStandardToken / requestTraditionToken / syncRequestStandardToken / showIntegrityLicenseDialog

3. 分析/埋点

重要更正:com/camerasideas/track/ 目录的 43 个文件是视频编辑时间轴 UI(TimelineDelegateAudiolineDelegateFilterlineDelegateseekbar* 等),不是埋点框架。真正的埋点框架如下。

3.1 门面与实现

类:p348ri/M.java(分析门面,编译名 DebugStrings.kt)

public final class M implements com.google.gson.internal.q {   // ← Gson 实例创建钩子(见 3.3)
    public static X3.Q f60737a;   // 由 InitializeApmTask 注入: new Q(context, isMainProcess)
    public static void c(Context context, String str) {        // 自定义事件(select_content)
        if (f60737a == null || TextUtils.isEmpty(str)) return;
        f60737a.a(context, str, new String[]{"content_type","item_id"}, new Object[]{"",""});
    }
    public static void d(Context context, String str, String[] keys, Object[] vals);  // 自定义事件
    public static void e(Throwable th2) { ... new H(th2, null) ... }   // → FirebaseCrashlytics.recordException
    public static void f(Activity activity, String str) {              // screen_view
        q7.a(activity, "screen_view", new String[]{"screen_name"}, new Object[]{str});
    }
    public static void g(Context context, String str, String str2, Object... kv) { // select_content 变体
        ... "content_type"=str, "item_id"=str2, "item_name"=str2 + 附加 kv ...
    }
    @Override public Object construct() { return new LinkedHashSet(); }  // Gson InstanceCreator 钩子
}

类:X3/Q.java(ISFirebaseListener)+ X3/F.java(DefaultFirebaseAnalytics)+ X3/G.java(logEvent 协程)+ X3/H.java(logException 协程)

// X3/F$LogConsumer: 事件队列(Channel,容量 MAX_VALUE,溢出 DROP_OLDEST)
public final p386ti.b f13838a = p386ti.j.a(Integer.MAX_VALUE, 6, null);

// X3/G.invokeSuspend: 发送前富化
if (!z10) {   // 未被 firebase_event_filter_config 过滤
    bundle.putString("uuid", aVar2.f14092a);
    bundle.putLong("sample_number", aVar2.f14095d);
    bundle.putString("user_id", aVar2.f14093b);
    bundle.putString("user_identity", aVar2.f14094c);   // "default_vip"/"fake_vip"/"normal"
    FirebaseAnalytics.getInstance(context).zzh(event, bundle);
}

// X3/H: 异常 → Crashlytics
FirebaseCrashlytics.getInstance().recordException(th2);

类:X3/AbstractC1284t.java(BaseFirebaseAnalytics)——采样与用户身份

// 启动时初始化:
if (Ce.c.a(context, f60328a).getLong("sample_number", -1L) < 0)
    Ce.c.a(context, f60328a).putLong("sample_number", Cf.X.a(0L, 10000L));  // 0-10000 随机采样号
String strI = com.camerasideas.instashot.store.billing.S.i(context);        // uuid
if (sD.w()) str2 = sD.f31149b.a().a() ? "default_vip" : "fake_vip"; else str2 = "normal";
this.f14090c = new a(sampleNumber, uuid, uuid, userIdentity);
// 之后: FirebaseCrashlytics.getInstance().setUserId(uuid);
//       读取远程 "firebase_event_filter_config"(Ig.c) 决定事件是否上送

3.2 日志管道(队列 + 批量镜像)

3.3 Gson 钩子(用户提到的"脱敏"疑点,实为锚定类技巧)

// InstashotApplication.onCreate():
com.google.gson.internal.s.f42708a = new J7.k(this);   // J7.k = Suppliers(包一个 LinkedHashSet)

注入后该字段被用作特性开关:s.f(OwnedPurchase)f42708a != null ? C2460y.f31207a(新 SKU 列表) : Th.t.f11116a(旧列表);s.j()support_bind_1484 决定是否启用 1484 绑定。同时该类提供 accountId/appUserId/isInReview/isPro 的本地存取(p382te.s MMKV 偏好)与 MD5、网络判断。目的:借用 gson 内部类不被 R8 删除的特性,把应用逻辑"锚"在稳定包内,并兼作跨混淆边界的全局注册表。

3.4 第三方归因 SDK


4. 远程配置 / A/B 测试

4.1 配置链

FirebaseRemoteConfigWrapper(拉取,60s 超时/1h 最小间隔)
   → com.camerasideas.instashot.remote.d (AppRemoteConfig,本地 debug 覆盖 + RC 合并)
      → X3.C1246g (AppCapabilities 静态门面,全 app 引用)
      → V5.c (AdDeploy 投放) / 各业务模块

类:com/camerasideas/instashot/FirebaseRemoteConfigWrapper.java

aVar2.f62906a = 3600L;                                   // minimum_fetch_interval
editorEdit.putLong("fetch_timeout_in_seconds", 60L)      // fetch timeout 60s
... .putLong("minimum_fetch_interval_in_seconds", 3600)  // commit
// 完成回调: postTaskResultInfo → dispatchComplete(this, success, activated)
// getString/getBoolean/getLong/getDouble: 读 activated 值,回退 defaults

类:com/camerasideas/instashot/remote/d.java(AppRemoteConfig):h()/b()/g()/c()/e() 读合并后的 HashMap(f30986b,本地 prefs 覆盖 + Firebase RC);X3/C1246g.java(AppCapabilities)以静态方法暴露,如:

public static C2437a b() {  // 订阅弹窗自动拉起配置
    return Gson.fromJson(f14024b.h("key_subscribe_auto_launch"), C2437a.class);
}
public static com.camerasideas.instashot.remote.t h() {  // 订阅页 A/B
    return Gson.fromJson(f14024b.h("key_subscribe_ab_test_info"), ...);
}
public static com.camerasideas.instashot.remote.t i() {  // A/B debug 变体
    return Gson.fromJson(f14024b.h("key_subscribe_ab_test_info_debug"), ...);
}
public static List f(B b10) {  // 弹窗序列控制
    strH = f14024b.h("popup_items_control");
    if (empty) strH = B.c(b10.getResources().openRawResource(R.raw.popup_config));
}
public static boolean l(Context context) {  // 水印展示黑名单
    return f14024b.h("disallow_show_watermark_list") 机型匹配;
}

4.2 典型远程键清单

用途
广告 ad_waterfall_list_v_max 自定义 waterfall JSON(回退 raw/local_ad_waterfall.json)
广告 ad_deploy_list_v_1530 广告位部署(OS/RAM/机型定向,回退 raw/local_ad_deploy_list.json)
广告 ad_supported_info_android AdConfig(banner 支持/默认 ad unit)
广告 popup_interstitial_ads 插屏频控 JSON
广告 waiting_reward_timeout_millis / is_ad_load_failure_can_rewarded / reward_ad_load_position 激励广告策略
广告 banner_refresh_time_millis / mrec_refresh_time_millis / ad_request_time_millis / ad_expiration_time_millis 横幅/MREC 刷新、超时、过期
广告 app_ad_info_list 自推 house ad 列表
广告 remove_banner_ad / remove_card_ad 广告关闭按钮开关
订阅 key_subscribe_auto_launch 订阅页自动拉起配置
订阅 key_subscribe_ab_test_info(+_debug) 订阅页 A/B 测试
订阅 key_subscription_replacement_mode Play 订阅替换模式
功能 ai_cut_free_tries_count / feature_score_style / transition_filter_versions / giphy_types / latest_version_update_info / notification_permission_requested_parameter 功能与 UI 开关
稳定性 glesv2_mtk_crash_list / lottie_engine_render_black_screen 机型黑名单降级
分析 firebase_event_filter_config Firebase 事件过滤 JSON

4.3 设备能力分级

类:com/camerasideas/instashot/SdkInitializer.java(实现 S0.b<h2>,依赖注入初始化)

if (!((ActivityManager) context.getSystemService("activity")).isLowRamDevice()
        && Build.VERSION.SDK_INT > 28
        && reqGlEsVersion >= 196608                // GLES 3.0
        && memoryInfo.totalMem >= 2.147483648E9d) {  // 2GB
    i = 1;   // 高端设备
}
if (i == 0) cVar.f63725a = "1";   // 低端设备标记 → 关闭部分特效

5. 崩溃与监控(APM)

5.1 初始化

类:com/camerasideas/startup/InitializeApmTask.java(@Keep,优先级最高,其它任务 dependOn 它;InstashotApplicationinitializeApmTask.start() 后调度器阻塞等待)

private void initializeApp(Context context) {
    InstashotApplication.a(context);
    Thread.setDefaultUncaughtExceptionHandler(new C2871v());   // 自定义 CrashHandler
    setRxJavaErrorHandler();                                     // RxJava 错误 → M.e 上报
    if (this.isMainProcess) {
        z.b(context);                                            // ...
        ((ArrayList) Dd.e.f2349a.f2351c).add(new l());           // 追加 LogPrinter
        // WebView 多进程 data-dir 锁清理(webview_data.lock 文件锁修复)
        // Kf.g(AiTaskDI) 注入: f6709l = startup.a(CommonRequestHook), f6708k = startup.k(IntegrityExtensions)
    }
    M.f60737a = new X3.Q(context, this.isMainProcess);           // Firebase 分析初始化
    p305pc.d.f59680k.a(context).f59691h = new J7.i();
}

5.2 崩溃过滤(关键:第三方 SDK 白名单静默)

类:com/camerasideas/utils/C2871v.java(CrashHandler)

对下列来源的崩溃直接吞掉不上报(既是防误报,也侧面暴露了广告 SDK 清单):

// 过滤: com.google.android.gms.ads.omid / nonagon(AdMob)
//       org.chromium(WebView)、com.vungle.warren(Vungle)
//       com.applovin.impl.sdk(AppLovin)、com.unity3d.services(Unity Ads)
//       com.inmobi.media(InMobi)、com.yandex.mobile(Yandex)
//       com.google.firebase.installations、androidx.work、GoogleSignatureVerifier/chimera/internal
// 例如:
if ("java.lang.NullPointerException".equals(name2)) {
    for (...) if ((stackTraceElement.getClassName()+"").contains("com.vungle.warren.VungleApiClient")) return;
}
if ("java.util.ConcurrentModificationException".equals(name2) || ...) {
    for (...) if ((...).contains("com.applovin.impl.sdk")) return;
}

5.3 微信 mars xlog

p192je.a.e(p107f5.b.A(this.mContext), "instashot");   // xlog 目录 + 文件前缀

// p192je.a.e():
System.loadLibrary("marsxlog");
Xlog xlog = new Xlog();
xlog.appenderOpen(2, 0, "", str, str2, 2);   // 异步模式,level=2(Debug)
com.tencent.mars.xlog.Log.setLogImp(xlog);
com.tencent.mars.xlog.Log.setConsoleLogOpen(false);

5.4 启动编排(AnchorsManager)

类:com/camerasideas/instashot/InstashotApplication.java + p434w7/d.java(调度器)+ B7/a.java

// onCreate: 仅主进程 + videoprocess 进程初始化
p434w7.d dVar = new p434w7.d();
dVar.f(initializeApmTask, {initializeApmTask});   // 锚点任务
initializeApmTask.start();
while (dVar.c()) { ... 按优先级排序执行任务 ... }  // 主线程阻塞等待启动链完成

任务链:InitializeApmTask → InitializeEnvTask → InitializeBillingTask → InitializeStateTask → InitializeLibTask → InitializeResourceTask

5.5 其他监控点


6. 广告 ID / 隐私 / UMP 同意流

6.1 Google UMP(混淆在广告 SDK 内)

类:De/d.java(CmpService)+ De/m.java(SdkCmpService 实现)+ De/h.java(OnConsentFormLoadListener)+ De/i.java(OnConsentInfoUpdateListener)+ De/j/k.java(SdkCmpService 业务回调)

// De/m:
public final ConsentInformation f2378a = UserMessagingPlatform.getConsentInformation(context);
public final ConsentRequestParameters f2379b = new ConsentRequestParameters.Builder()
    .setTagForUnderAgeOfConsent(false).setConsentDebugSettings(null).build();

// 触发点 SettingFragment.java:408:
mVar.f2378a.requestConsentInfoUpdate(dVar3, mVar.f2379b, lVar, lVar);

// De/j.onConsentFormLoadSuccess:
int consentStatus = this.f2371d.f2378a.getConsentStatus();
if (z10 || consentStatus == 2) consentForm.show(this.f2370c, new a());   // REQUIRED 或老用户
// onConsentFormDismissed:
boolean hasConsent = mVar.f2378a.getConsentStatus() == 3;               // OBTAINED
SettingFragment.zj((SettingFragment) ..., hasConsent);

6.2 应用内隐私页与 AppLovin 同步

类:com/camerasideas/instashot/PolicyFragment.java

// 同意状态 ↔ AppLovinPrivacySettings 双向同步
AppLovinPrivacySettings.setHasUserConsent(!"disagree".equals(new JSONObject(str).getString("status")));
jSONObject.put("status", AppLovinPrivacySettings.hasUserConsent() ? "agree" : "disagree");
// EU 用户切换 EU 隐私页
int consentStatus = ((De.m) De.d.a.a(this.mContext)).f2378a.getConsentStatus();
this.mWebView.loadUrl((consentStatus == 3 || consentStatus == 2)
    ? C1264m.h("https://inshot.cc/website/InShotAndroid/privacypolicy_eu.html")
    : A1.S(this.mContext));

类:com/camerasideas/instashot/fragment/common/C1897w.java:设置页同意开关 → AppLovinPrivacySettings.setHasUserConsent(true)

类:com/camerasideas/startup/MobileAdsInitializer.java:初始化时 AppLovinPrivacySettings.setDoNotSell(true)(CCPA)、setExtraParameter("user_agent_collection_enabled","false")、禁用 MAX TCF flow——同意完全由自研 UMP 流程驱动,并通过 AppLovinPrivacySettings 透传给 MAX。

6.3 广告 ID 与设备标识


7. 结论:这家公司的商业化工程特征

  1. 自研商业化 SDK(Shantanu)+ 全远程化:广告(waterfall/deploy/频控/自推 house ad)与订阅(弹窗/A-B/替换模式)几乎全部由远程配置驱动,本地 assets 均有回退 JSON——线上可热切换广告主、广告位、价格页与 A-B 实验。
  2. 广告聚合 = AppLovin MAX + 自定义 AdMob waterfall:不是标准 mediation;MAX 只做兜底调度,真实填充是自定义 event 直连 AdMob(32 步全部 GoogleMediationAdapter),多网络(Vungle/Unity/InMobi/Fyber/Yandex/TradPlus/PubMatic)仅随包存在。
  3. 反分析手段密集:R8 单字母混淆、\u200b 零宽空格包名水印、Le.n 字符串池解密、应用类编入 com.google.gson.internal 包做静态钩子(s.f42708a 特性开关、M implements q 实例创建钩子)、广告异常类刻意 keep 原包名。
  4. 订阅-广告强耦合:remove.ads 即去广告 SKU;广告初始化/展示处处以 UserManager.k() 为前提;Pro 判定走 w()(gson 钩子 + 内购 + 华为 + DebugPro)。
  5. 分析收敛:全事件经 p348ri.M 门面 → Firebase Analytics/Crashlytics;协程 Channel 队列 + sample_number 采样 + firebase_event_filter_config 过滤;日志三路(Android Log / xlog / Crashlytics.log 镜像);无 AppsFlyer/Adjust。
  6. 服务器侧商业闭环:Shantanu IAP(账户绑定/订单查询/审核态)+ Play/Huawei Integrity(反欺诈)+ 盗版签名检测(pirate_app)+ 崩溃白名单,形成"商业化-安全-稳定性"一体的工程体系。

附:关键类路径速查表

主题 类路径
广告初始化 com/camerasideas/startup/MobileAdsInitializer.javaDe/g.javaDe/o.javaDe/p.javacom/shantanu/mobileads/SdkInitializer.java
广告位解析 V5/c.java(AdDeploy)、N/a.javacom/camerasideas/instashot/remote/a.java(AdConfig)
插屏 V5/n.javaV5/l.javaFe/g.javaFe/e.javaFe/f.javaFe/q.java
激励 V5/r.javaV5/s.javaV5/t.javaJe/a.javaJe/c.javaJe/q.java
横幅/MREC V5/g.javaV5/p.javacom/camerasideas/mobileads/BannerContainer.javaIe/d.javaJe/o.java
收益/适配器 He/e.java(NetworkAdRevenueAdapter)、He/d.javaDe/a.java(AdRevenue)、De/b.java
订阅入口 com/camerasideas/startup/InitializeBillingTask.java
计费抽象 p321qa/o.javap321qa/g.java(BillingManager)、p321qa/d.java(BillingHelper)、p321qa/a.java(Fake)
SKU/权益 com/camerasideas/instashot/store/billing/C2460y.javaS.java(UserManager)、InterfaceC2448l.javaC2449m.javaC2450n.javaP.javaC2458w.javaD.java
服务端 IAP com/shantanu/iap/v.javacom/shantanu/iap/OwnedPurchase.javacom/shantanu/iap/bind/*
Play Integrity com/android/auth/integrity/IntegrityInitializer.javag.javaGoogleIntegrityManager.javacom/camerasideas/startup/j.java
埋点 p348ri/M.javaX3/Q.javaX3/F.javaX3/G.javaX3/H.javaX3/AbstractC1284t.javaX3/E.java
日志管道 Dd/e.javaDd/f.javap192je/a.javacom/tencent/mars/xlog/Xlog.java
远程配置 com/camerasideas/instashot/FirebaseRemoteConfigWrapper.javaremote/d.javaX3/C1246g.javaX3/U0.java
APM/崩溃 com/camerasideas/startup/InitializeApmTask.javacom/camerasideas/utils/C2871v.javacom/camerasideas/startup/InitializeEnvTask.javastartup/a.java(CommonRequestHook)
隐私/同意 De/d.javaDe/m.javaDe/h.javaDe/i.javaDe/j.javaDe/k.javaPolicyFragment.javafragment/common/C1897w.java
启动编排 com/camerasideas/instashot/InstashotApplication.javap434w7/d.javaB7/a.java
Gson 钩子 com/google/gson/internal/s.javaJ7/k.javap348ri/M.java