敞开成长之旅!这是我参加「日新方案 12 月更文挑战」的第11天,点击检查活动详情

背景

上一年11月,咱们团队就现已宣告解散,可是因为隐私监管的问题,咱们还得保护最终一个版别,满足隐私监管的要求。 咱们团队的隐私问题主要是由我来负责,回想起这一年,真是被各种隐私问题折腾得死去活来, 所幸最终一个版别总算上线了,现针对隐私问题总结一下各种应对的办法。

隐私检测的流程

流程 1.0

在刚开始被要求隐私检测的时候,咱们的流程是这样的:

应对隐私检测的各种姿势

在定包前提交隐私检测,然后根据检测成果进行相应的更改,可是在实际操作的过程中发现有以下问题:

  1. 提交隐私检测之后有新的代码提交,可能会形成新隐私问题;
  2. 检测出问题之后再改,测验时间短,风险大,可能会形成定包延期;
  3. 在定包前进行修正,测验没有足够的人力来支持;

因此在和 PM 评论之后,咱们将流程进行了一些变更。

流程 2.0

咱们在每个版别定包之后系统测验的时候提交隐私检测,然后将检测出来的问题转换成下个版别的需求。

应对隐私检测的各种姿势

然而在实施一段时间之后,发现依然有问题。 咱们团队分为北京和深圳两个团队,一般状况下咱们是每两周发一个版别,深圳和北京替换发布,比方深圳发布了 4.7.0,接下来北京发布 4.7.5。 一般状况下,咱们在等隐私检测成果出来后,下一个版别的需求早就排好。如下图所示,隐私检测大概需求三天左右,当咱们针对 4.7.0 版别提交隐私检测,拿到检测成果之后,4.7.5 版别的需求早就现已排好,此时想要跟 4.7.5 版别就比较困难,只能将需求排进 4.8.0 版别。

应对隐私检测的各种姿势

所以第二个流程依然有以下问题:

  1. 发版较为频频,470 检测出来的问题,475 无法处理,需延迟到 480 才干处理;
  2. 470 检测出来的问题,475 检测出来依然有相同的问题,糟蹋检测资源;
  3. 在系统测验期间,需求手动提交隐私检测,添加测验工作量;

在和隐私检测的同事评论之后,咱们对流程又进行了一些变更。

流程 3.0

既然隐私检测无法防止,与其被迫的接受检测,不如自动的拥抱改变;

应对隐私检测的各种姿势

详细包含以下几项:

  1. 针对存量的隐私问题,咱们依然运用流程 2.0 的方式处理;
  2. 针对新的需求,咱们和 PM 评论之后,在产品内审需求评定阶段要求添加隐私自查,尽可能将隐私问题提早暴露,包含以下几项:
检查项
1 该需求是否需求调用权限,是已有权限仍是之前没有标示写明过的权限,同时需求在需求文档陈述若用户回绝授权后的流程
2 该需求会不会接入新的 sdk,需求进行 sdk 明示
3 该需求是否有其他的数据分享/同享状况,需求文字补充
  1. 流水线引进隐私检测 咱们在release版的流水线添加提交隐私检测的过程, 这一步会在首次自动化测验经过之后履行,会首要判断该版别是否现已提交检测,若现已提交,则不再提交,若未提交,则会将对应的 apk 上传至检测平台。
    应对隐私检测的各种姿势

除了流程上的问题,还有各种技术层面的问题。

应对隐私检测的各种姿势

1. 提早获取数据和获取频率

针对提早获取数据的这种状况,我创建了一个类 PrivacyInit, 在用户未授权时,先将需求初始化的代码放到 PrivacyCallBack 里面,等用户授权之后再初始化; 若用户现已授权,则直接履行。

//用户授权之后调用
public static void privacyAllInit() {
    for (PrivacyCallBack callBack : callBackList) {
        try {
            callBack.call();
        } catch (Exception e) {
            e.printStackTrace();
            ABCLog.e(TAG, e.getMessage());
        }
    }
}
//判断是否授权,若未授权则先不初始化
public static void privacyInit(PrivacyCallBack callBack) {
    if (isAuthorized()) {
        try {
            if (callBack != null) {
                callBack.call();
            }
        } catch (Exception e) {
            e.printStackTrace();
            ABCLog.e(TAG, e.getMessage());
        }
    } else {
        callBackList.add(callBack);
    }
}
//隐私回调接口
public interface PrivacyCallBack {
    void call();
}

针对获取频率的问题就比较简单,直接运用一个大局的静态变量或许单例保存相关的隐私数据即可,防止多次获取。

2. 权限问题

新的隐私规范要求咱们每次运用某个权限的时候,均需求给出弹窗提示,然后才干真正的进行权限请求。 如果针对每个场景均进行覆盖和修正,显然成本比较大,不过好在之前针对权限请求做过收拢操作。 Android 原生请求权限的方式如下: 一般咱们在某个当地请求了权限,然后在对应的 onRequestPermissionsResult 里面拿到权限请求的成果

public class xxxActivity extends AppCompatActivity {
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //请求权限
        this.requestPermissions(permissions, 100);
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {
            //获取权限请求的成果     
    }
}

这样做的坏处便是请求权限和获取成果分散在不同的当地,别的不同的 Activity 请求权限,不便利统一管理。 因此咱们创建了一个透明的 Activity 专门用于请求权限,然后请求到的成果经过回调的方式回来。

<activity
    android:name="***.***.***.PermissionActivity"
    android:theme="@style/Transparent" >
</activity>

最终实现的成果如下:

//请求权限
PermissionHelper.with(context).permission(Manifest.permission.CAMERA)
        .callBack((int requestCode, String[] permissions, int[] grantResults) -> {
            //得到权限请求的成果
        }).request();

同时,因为一切的权限请求被收拢在一个当地,因此能够很便利的应对权限监管的各种花里胡哨的需求。

3. sdk问题

如果隐私检测出 sdk 有隐私问题,咱们都会直接找到对应的团队,让他们帮忙处理,更新 sdk 的版别。 可是也有一些特殊的状况,比方 sdk 现已不保护了。

反编译

应对隐私检测的各种姿势

遇到这种状况,内心特别溃散,若是去掉 sdk,可能会引发现有功能的反常,若是引进新的 sdk,针对最终一个版别的场景,改动成本会比较大, 如何以最小的成原本处理隐私问题呢? 我想到了反编译。 能够将jar包反编译成 smali,修正 smali,然后将其编译成新的 jar 包,替换掉本来的 jar 包就能够了。 不过这不是一个惯例的手段,慎用!

终极大招

反编译毕竟不是一个惯例的手段,并且需求修正 smali 代码,对应的语法也不熟悉,可是没有关系,咱们还能够运用 Transfrom,直接修正 sdk 里面的代码。 一般状况,咱们遇到 sdk 的隐私问题能够分为两类,拜访了隐私相关的变量或许拜访了隐私相关的办法。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    var btn = findViewById<Button>(R.id.btn)
    btn.setOnClickListener {
        println("getdevice:"+Build.DEVICE)//拜访了隐私相关的变量
        println("getImei:"+getImei())//拜访了隐私相关的办法
    }
}

1. 拜访隐私相关的变量 针对这种状况,咱们能够在 AdviceAdapter 傍边复写 visitFieldInsn 办法,当拜访到对应的字段时能够直接回来咱们自己设定的变量

public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
    if ("android.os.Build".equals(owner) || "DEVICE".equals(name) ) {
        //替换成咱们自己的变量
        super.visitFieldInsn(opcode, "***/***/***/Privacy", "device", "Ljava/lang/String;");
    } else {
        super.visitFieldInsn(opcode, owner, name, descriptor);
    }
}

经过替换,咱们能够发现,拜访 android.os.Build.Device 字段现已被替换成 Privacy.device 了

应对隐私检测的各种姿势

2. 拜访隐私相关办法 假定 MainActivity.getImei() 办法是一个隐私相关的办法,咱们想把它替换成 Privacy.newGetImei() 办法, 咱们能够在 AdviceAdapter 傍边复写 visitMethodInsn 办法,如下所示,能够替换成咱们想要的办法。

@Override
public void visitMethodInsn(int opcode, String owner, String methodName, String descriptor, boolean isInterface) {
    if ("getImei".equals(methodName) && "()Ljava/lang/String;".equals(descriptor)&&""***/"***/"***/MainActivity".equals(owner)) {
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "***/"***/"***/Privacy", "newGetImei", "()Ljava/lang/String;", false);
    } else {
        super.visitMethodInsn(opcode, owner, methodName, descriptor, isInterface);
    }
}

经过替换,咱们发现拜访的办法现已变成 Privacy.newGetImei() 了

应对隐私检测的各种姿势
应对隐私检测的各种姿势

总结

隐私合规现已告一段落,这中间让我感到很溃散的是,隐私的政策一向在变,比方刚开始并未把 AndroidID 纳入隐私字段,后面加上了,再后来拜访频次也加入了检测。 还有便是隐私检测每次检测出来的问题都不相同,比方第一次检测出 ABC 问题,咱们改了之后,又检测出 DE 问题,导致问题很难得到收敛。 不过,最漆黑的时间现已过去了。 别的感谢一向并肩作战的产品同学,尽管都现已活水到了其他部门,可是咱们坚持到了最终,这个需求我感觉咱们现已做出了革新的友谊 。