关于 Android P Media 的后台录音录像 UID 保护

google 放出 Android Pie 已经是8月初的事了, 国内的各镜像站上, 也陆续加上了 9.0.0_r3 的代码;
起初特别关注 Android 9.0, 是因为当前一个和车载有关的项目, 会使用多摄像头和 UVC 摄像头,因为 google 官网早就有 feature 简介, 参考 https://developer.android.google.cn/about/versions/pie/android-9.0  ------
关于 camera 部分有:  // MAGIC1.  DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu

Multi-camera support and camera updates
On devices running Android 9, you can access streams simultaneously from two or more physical cameras. On devices with either dual-front or dual-back cameras, you can create innovative features not possible
with just a single camera, such as seamless zoom, bokeh, and stereo vision. The API also lets you call a logical or fused camera stream that automatically switches between two or more cameras.
Android 9 also enables support for external USB/UVC cameras on supported devices.

当时想着如果 android Pie 原生就支持多路 Cam 的预览与录制, 那项目上就不必另作一套框架了;
但是拉了 9.0 的代码看了,才发现还远不是那么回事, 与本文主题无关,这个不多说了 ;

当时另一个主要的关注点是 AudioRecord/Camear 的 UID 保护,  我当时截了图 : 

 

关于 Android P Media 的后台录音录像 UID 保护_第1张图片

这个之前很早就已经放出风了,比如:  // MAGIC2.  DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu

<保护隐私,安卓9.0将禁止后台应用调用摄像头和麦克风权限>     见 https://www.sohu.com/a/224103414_472558

呵呵,这个比较敏感;

我们知道, 在经历了 IOT, VR/AR, 区块链之后, 现在大火的是机器学习 ~   语音识别,推荐算法, 都是最接地气的 AI 应用没错吧, 给大家的印象是什么,牛X ~   尤其是对于参观了 2018 CES 的我,现场感受了讯飞的实时系统演示,确实牛X ...
河东,河西, 芯片厂就不说了, 互联网厂, 有哪个不往这上面蹭的?   随之而来的是什么呢, 某头条,某东,某度输入法, 都被怀疑使用后台录音, 采集用户语音...  不贴链接了,都能搜到; 随后世界杯期间 OPPO 发布 FindX, 升降式摄像头, 揪出了偷调摄像头的 QQ 浏览器  ...... // MAGIC3.  DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu

后台拍照的嫌疑已洗不清了,后台录音呢?  大家可能确实有类似的印象, 平日闲聊的内容,过两天在手机通知栏就看到了某头条/某东对应产品的推送, 好似被监听了...  作为技术人员来说, 我必须承认, 就算这些 APP 开了后台 service, 但是毕竟手机大部分时间是休眠状态, 开后台录音的耗电就离谱了,还得一直耗流量上传语音数据, 而且装兜里的手机的录音质量识别起来可能也够呛......    所以我宁可相信某头条,某输入法它们是清白的, 不是说它们高尚, 只是说技术层面暂时很难做到;

但为啥我还觉得敏感呢?  因为互联网厂做不了的事情,  会有更深不可测的角色去做!
我们不说 20年前的 <国家公敌>和 10年前的 <鹰眼> ,  单下面这张图了解一下 :

关于 Android P Media 的后台录音录像 UID 保护_第2张图片

 

我年初看到这张图的时候,哆嗦了一下,然后去买了一堆口罩 ...   说啥好呢?  BIG BROTHER IS WATCHING YOU !  
奥威尔说:  你猜 BIG BROTHER 会不会关心你的耗电和流量?

所以我对  Android P 的  UID 隐私保护很上心 ......    // MAGIC4.  DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu

背景介绍结束, 现在看代码吧,总共没几行  ----

commit f4ddfefc8ba59a8486d91826154cc9447821409e
Author: Svet Ganov
Date:   Tue Jan 16 07:37:58 2018 -0800

    Don't record audio if UID is idle - media framework

    To protect user's privacy if a UID is in an idle state we allow
    recording but report silence (all zeros in the byte array) and once
    the process goes in an active state we report the real mic data.
    This avoids the race between the app being notified aboout its
    lifecycle and the audio system being notified about the state
    of a UID.


这里是说, UID idle 的话, 采集的数据就会全部填零;

+    // If recording we need to make sure the UID is allowed to do that. If the UID is idle
+    // then it cannot record and gets buffers with zeros - silence. As soon as the UID
+    // transitions to an active state we will start reporting buffers with data. This approach
+    // transparently handles recording while the UID transitions between idle/active state
+    // avoiding to get stuck in a state receiving non-empty buffers while idle or in a state
+    // receiving empty buffers while active.
+    class UidPolicy : public BnUidObserver {
+    public:
+        void registerSelf();
+        void unregisterSelf();
+        bool isUidActive(uid_t uid);
+        void onUidGone(uid_t uid, bool disabled);
+        void onUidActive(uid_t uid);
+        void onUidIdle(uid_t uid, bool disabled);

+void AudioPolicyService::UidPolicy::registerSelf() {
+    ActivityManager am;
+    am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
+            | ActivityManager::UID_OBSERVER_IDLE
+            | ActivityManager::UID_OBSERVER_ACTIVE,
+            ActivityManager::PROCESS_STATE_UNKNOWN,
+            String16("audioserver"));
+}
+void AudioPolicyService::UidPolicy::onUidActive(uid_t uid) {
+    {
+        Mutex::Autolock _l(mUidLock);
+        mActiveUids.insert(uid);
+    }
+    sp service = mService.promote();
+    if (service != nullptr) {
+        service->setRecordSilenced(uid, false);
+    }
+}
+void AudioPolicyService::UidPolicy::onUidIdle(uid_t uid, __unused bool disabled) {
+    bool deleted = false;
+    {
+        Mutex::Autolock _l(mUidLock);
+        if (mActiveUids.erase(uid) > 0) {
+            deleted = true;
+        }
+    }
+    if (deleted) {
+        sp service = mService.promote();
+        if (service != nullptr) {
+            service->setRecordSilenced(uid, true);
+        }
+    }
+}

具体就是向 ActivityManagerService 注册 UidObserver, 把 AudioPolicyService 的 onUidActive/ onUidIdle 注册过去,这样 Uid State 变化的时候, 执行对应的 setRecordSilenced , 很清晰 ;

Camera 也同样加了 Uid 的保护: // MAGIC5.  DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu

commit a453d0d278ee916bf68c98093fcfd9fa924ca454
Author: Svet Ganov
Date:   Thu Jan 11 15:37:58 2018 -0800

    No camera for idle uids - av framework

    If a UID is idle (being in the background for more than
    cartain amount of time) it should not be able to use the
    camera. If the UID becomes idle we generate an eror and
    close the cameras for this UID. If an app in an idle UID
    tries to use the camera we immediately generate an error.
    Since apps already should handle these errors it is safe
    to apply this policy to all apps to protect user privacy.

+void CameraService::UidPolicy::onUidActive(uid_t uid) {
+    Mutex::Autolock _l(mUidLock);
+    mActiveUids.insert(uid);
+}
+
+void CameraService::UidPolicy::onUidIdle(uid_t uid, bool /* disabled */) {
+    bool deleted = false;
+    {
+        Mutex::Autolock _l(mUidLock);
+        if (mActiveUids.erase(uid) > 0) {
+            deleted = true;
+        }
+    }
+    if (deleted) {
+        sp service = mService.promote();
+        if (service != nullptr) {
+            service->blockClientsForUid(uid);
+        }
+    }
+}

Audio/Camera 方面的代码很简单, 但 ActivityManagerService  里面就复杂了, 我看了眼 updateOomAdjLocked / computeOomAdjLocked 觉得头疼...   没法贴代码了, 但至少有一条,foregroundServices 是被看作 active 的 !


当时想到的问题: 如果一个 APP 包含有一个 activity 是个浏览器, 一个 service 可以录音, 二者会是不同的 PID, 但应该是相同的 UID ; 那么我用 APP 的浏览器 activity 的时候, 录音 service 的 UID 应该不会按 Idle 来处理, 就是仍然可以后台录音? 
其实可能想多了,还没到考虑这个的时候...   // MAGIC6.  DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu

实地验证一下吧:

关于 Android P Media 的后台录音录像 UID 保护_第3张图片

在 Android P 样机上安装一个可以后台录音的 APK :  com.QHhuaw.Recorder
启动录音, 然后 HOME 返回桌面, 此时 dumpsys activity processes 可以看到 UID 状态 :


CTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)
  UID states:
    UID 1000: UidRecord{55cbc01 1000 PER  change:active|uncached procs:7 seq(0,0,0)}
    UID 1001: UidRecord{38a8aa6 1001 PER  change:active|uncached procs:7 seq(0,0,0)}
    UID 1002: UidRecord{a9fcce7 1002 PER  change:active|uncached procs:1 seq(0,0,0)}
    UID 1027: UidRecord{822d094 1027 PER  change:active|uncached procs:1 seq(0,0,0)}
    UID 1068: UidRecord{8d8173d 1068 PER  change:active|uncached procs:1 seq(0,0,0)}
    UID u0a11: UidRecord{144ab32 u0a11 BFGS procs:1 seq(0,0,0)}
    UID u0a13: UidRecord{1125083 u0a13 CEM  bg:+1m5s172ms idle change:idle procs:1 seq(0,0,0)}
    UID u0a15: UidRecord{1a66200 u0a15 CEM  bg:+1m7s836ms idle change:idle procs:1 seq(0,0,0)}
    UID u0a16: UidRecord{1b3ea39 u0a16 CEM  bg:+2m16s792ms idle change:idle procs:1 seq(0,0,0)}
    UID u0a23: UidRecord{c0e487e u0a23 SVC  idle procs:1 seq(0,0,0)}
    UID u0a26: UidRecord{b9f71df u0a26 CEM  idle change:cached procs:1 seq(0,0,0)}
    UID u0a28: UidRecord{90f9e2c u0a28 CEM  idle change:cached procs:1 seq(0,0,0)}
    UID u0a30: UidRecord{fdff0f5 u0a30 CRE  bg:+2m2s216ms idle change:idle procs:1 seq(0,0,0)}
    UID u0a31: UidRecord{426e8a u0a31 CEM  idle change:cached procs:1 seq(0,0,0)}
    UID u0a32: UidRecord{2b34cfb u0a32 PER  procs:1 seq(0,0,0)}
    UID u0a40: UidRecord{cee7118 u0a40 CEM  idle procs:1 seq(0,0,0)}
    UID u0a43: UidRecord{88ba771 u0a43 TOP  procs:1 seq(0,0,0)}
    UID u0a51: UidRecord{2e0e956 u0a51 IMPF change:active|uncached procs:1 seq(0,0,0)}
    UID u0a55: UidRecord{f5fbdd7 u0a55 CEM  idle change:cached procs:1 seq(0,0,0)}
    UID u0a62: UidRecord{2e586c4 u0a62 SVC  idle procs:1 seq(0,0,0)}
    UID u0a69: UidRecord{a6249ad u0a69 CEM  idle procs:1 seq(0,0,0)}
    UID u0a77: UidRecord{71a44e2 u0a77 IMPB procs:1 seq(0,0,0)}
    UID u0a82: UidRecord{ff86073 u0a82 CEM  bg:+16m39s874ms idle change:idle procs:1 seq(0,0,0)}
    UID u0a85: UidRecord{6164b30 u0a85 CEM  idle change:cached procs:1 seq(0,0,0)}
    UID u0a87: UidRecord{986d3a9 u0a87 CEM  idle change:cached procs:1 seq(0,0,0)}
    UID u0a89: UidRecord{9bcd2e u0a89 FGS  change:active procs:1 seq(0,0,0)}
    UID u0a90: UidRecord{a6e90cf u0a90 FGS  fgServices change:active procs:2 seq(0,0,0)}
    UID u0a91: UidRecord{4cea5c u0a91 FGS  fgServices procs:1 seq(0,0,0)}

对照看看 UID : // MAGIC7.  DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu

Process LRU list (sorted by oom_adj, 41 total, non-act at 4, non-svc at 4):
    PERS #40: sys   F/ /PER  trm: 0 1196:system/1000 (fixed)
    PERS #39: pers  F/ /PER  trm: 0 1509:com.android.systemui/u0a32 (fixed)
    PERS #38: pers  F/ /PER  trm: 0 1683:.dataservices/1000 (fixed)
    PERS #36: pers  F/ /PER  trm: 0 1713:com.android.phone/1001 (fixed)
    PERS #35: pers  F/ /PER  trm: 0 2042:com.android.nfc/1027 (fixed)
    PERS #34: pers  F/ /PER  trm: 0 2065:com.android.se/1068 (fixed)
    Proc #18: psvc  F/ /PER  trm: 0 1444:com.android.bluetooth/1002 (service)
        com.android.bluetooth/.btservice.AdapterService<=Proc{1196:system/1000}
    Proc # 1: fore  T/A/TOP  trm: 0 2112:com.android.launcher3/u0a43 (top-activity)
    Proc #13: vis   F/ /BFGS trm: 0 1781:android.ext.services/u0a11 (service)
        android.ext.services/.notification.Assistant<=Proc{1196:system/1000}
    Proc #32: vis   F/ /IMPF trm: 0 2131:com.android.smspush/u0a51 (service)
        com.android.smspush/.WapPushManager<=Proc{1713:com.android.phone/1001}
    Proc #10: prcp  F/S/FGS  trm: 0 2816:com.wandoujia.phoenix2:channel/u0a90 (fg-service)
    Proc # 6: prcp  F/ /FGS  trm: 0 2912:com.wandoujia.phoenix2.usbproxy/u0a89 (service)
    Proc # 4: prcp  F/S/FGS  trm: 0 2864:com.wandoujia.phoenix2/u0a90 (fg-service)
    Proc # 2: prcp  F/S/FGS  trm: 0 5727:com.QHhuaw.Recorder/u0a91 (fg-service)
    Proc # 3: prcp  B/ /IMPB trm: 0 1485:com.android.inputmethod.latin/u0a77 (service)
        com.android.inputmethod.latin/.LatinIME<=Proc{1196:system/1000}
    Proc # 0: cch   B/ /CRE  trm: 0 5338:com.android.soundrecorder/u0a30 (cch-rec)
    Proc #11: cch   B/ /CEM  trm: 0 2419:com.android.providers.calendar/u0a40 (cch-empty)
    Proc # 9: cch   B/ /CEM  trm: 0 2584:com.android.packageinstaller/u0a16 (cch-empty)
    Proc # 7: cch   B/ /CEM  trm: 0 5612:com.android.defcontainer/u0a15 (cch-empty)
    Proc # 5: cch   B/ /CEM  trm: 0 2096:android.process.media/u0a13 (cch-empty)
    Proc #16: cch+2 B/ /CEM  trm: 0 3043:com.android.quicksearchbox/u0a55 (cch-empty)
    Proc #15: cch+2 B/ /CEM  trm: 0 2471:com.android.email/u0a69 (cch-empty)
    Proc #14: cch+2 B/ /CEM  trm: 0 2533:com.android.mms/u0a28 (cch-empty)
    Proc #12: cch+2 B/ /CEM  trm: 0 3095:com.android.keychain/1000 (cch-empty)
 


后台录音的 com.QHhuaw.Recorder 是 u0a91, 它是 fgServices, fgService 就是 active !   
可想而知,切到 HOME 之后, 后台的录音数据没有填零, 录了之后播放, 声音杠杠的 ...  

  *APP* UID 10091 ProcessRecord{f932f00 5727:com.QHhuaw.Recorder/u0a91}
    user #0 uid=10091 gids={50091, 20091, 9997, 3003}
    requiredAbi=armeabi-v7a instructionSet=arm
data=/data/user/0/com.QHhuaw.Recorder
    packageList={com.QHhuaw.Recorder}
    compat={320dpi}
    thread=android.app.IApplicationThread$Stub$Proxy@9df94a2
    pid=5727 starting=false
    lastActivityTime=-1m4s383ms lastPssTime=-1s451ms pssStatType=0 nextPssTime=+9m58s461ms
    adjSeq=4699 lruSeq=0 lastPss=31MB lastSwapPss=0.00 lastCachedPss=0.00 lastCachedSwapPss=0.00
    procStateMemTracker: best=1 (1=1 1.5x, 2=1 1.5x, 3=1 86.49756x)
    cached=false empty=true
    oom: max=1001 curRaw=200 setRaw=200 cur=200 set=200
    curSchedGroup=2 setSchedGroup=2 systemNoUi=false trimMemoryLevel=0
    curProcState=3 repProcState=3 pssProcState=3 setProcState=3 lastStateTime=-42s108ms
    hasShownUi=true pendingUiClean=true hasAboveClient=false treatLikeActivity=false
    foregroundServices=true forcingToImportant=null
    reportedInteraction=false fgInteractionTime=-42s109ms
    hasStartedServices=true
    startSeq=58
    lastRequestedGc=-5h6m9s693ms lastLowMemory=-5h6m9s693ms reportLowMemory=false
    Activities:
      - ActivityRecord{95e6e3b u0 com.QHhuaw.Recorder/com.pjw.atr.AtrActivity t39}
      - ActivityRecord{c4372f u0 com.QHhuaw.Recorder/com.pjw.atr.AtrActivity t39}
    Recent Tasks:
      - TaskRecord{f55dd82 #39 A=com.QHhuaw.Recorder U=0 StackId=2 sz=2}
    Services:
      - ServiceRecord{6eccd94 u0 com.QHhuaw.Recorder/com.pjw.atr.AudioService}


可能 google 后来发觉了, 所以官网上关于 Android P, 已经删除了 UID 隐私保护那个章节...  ( 所以刚才我只能放个 developer 官网先前的截图 ); // MAGIC8.  DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu

那怎么破呢?  终极大招是学 OPPO/VIVO, 升降式摄像头 ...   类似整出一个弹出式麦克风 ... ... 
软件层面嘛, 期待 Android 10 吧 !


写了篇水文, 哎, 其实像我这种家里摆着天猫精灵, 早已全方位无死角的,我还纠结这干嘛 ~ // MAGIC9.  DO NOT TOUCH http://blog.csdn.net/leonxu_sjtu

关于 Android P Media 的后台录音录像 UID 保护_第4张图片

你可能感兴趣的:(Android,吐槽)