網路熱傳App鑑定 |「得物」疑私刪使用者影片?從技術角度還原事件始末

語言: CN / TW / HK

本文正在參加「金石計劃 . 瓜分6萬現金大獎」

宣告:本文更注重於原理知識的普及,因此文中不會有大量實際程式碼的展示,如果想從程式碼層面上了解「應用儲存分割槽」的內容,歡迎閱讀我兩年前寫過的技術文章《Android 10 應用分割槽儲存適配實踐》


近日,有網友爆料,稱其發現得物App有疑似偷偷呼叫手機許可權刪除使用者影片的行為。

事件的起因,是該網友在得物App上購買到的商品有問題,於是按流程向平臺方反饋,並上傳了相關的影片證據。

但沒過多久,其手機上就收到了一條系統推送提醒,內容是檢測到“得物”刪除了影片,已成功攔截,據該網友推測,被刪除的正是作為重要維權證據的那條影片。

此事一經曝光,立即在網上掀起了軒然大波。從大量評論跟進的內容上看,網友們最關注的問題集中在:

得物App到底有沒有未經使用者同意,就刪除了使用者手機相簿中的影片?

隨著事情的逐漸發酵,得物App也緊急連發了兩條宣告,最新的一條宣告迴應稱:刪除的是編輯、處理、上傳過程中產生的臨時快取檔案,是對該臨時快取檔案的處理觸發了系統攔截通知

聲明發出後,有相當一部分網友採納了這個說法,畢竟類似的誤報之前也已經在其他App上發生過不少了。到這裡,事情似乎告一段落了。

但是,這真的可以簡單歸結為系統攔截通知的誤報嗎?多年從事Android開發的直覺告訴我,這件事情的背後,肯定還有更加深層的原因。

也是基於這種直覺的驅使,經過了大半個晚上的資訊收集、情景模擬以及測試驗證,我大致上已經能夠梳理出這整件事情的來龍去脈了。

先丟擲結論,這其實是一場由「Android系統的歷史遺留問題」,「得物App對於適配工作的不作為」以及「系統攔截App刪除操作的判定規則」三者共同作用下所引發的「烏龍事件」

故事,還得先從Android系統的歷史遺留問題開始講起。

Android系統的歷史遺留問題

Android 10之前:亂象叢生

在Android 10之前,Android系統對於App的檔案儲存,既沒有強硬的儲存規範,也沒有可參考的儲存建議

在此混沌的背景下,只要能申請到必要的檔案讀寫許可權,每一個App就都可以在手機儲存空間的任意位置建立屬於自己的目錄,訪問系統目錄或其他App的目錄也是完全沒有任何限制。

開啟你手機上的「檔案管理器」,你認得出以上是哪幾個App建立的目錄嗎?

這樣做直接導致的結果就是,使用者根本無法區分和管理不同App的檔案,一開啟檔案管理器,能看到的除了混亂無序,還是混亂無序。

Android 10:應用分割槽儲存開始實施

也許是Android系統也意識到了這個問題的嚴重性,所以在Android 10之後大刀闊斧地引入了應用分割槽儲存的概念,也即為每個App劃分了一個專有目錄,預設情況下,App只能訪問這個專有目錄下的檔案。

如果App嘗試訪問此目錄之外的檔案,就會發生錯誤,即使已經申請了檔案讀取(READ_EXTERNAL_STORAGE)許可權

而如果App需要訪問公有目錄(比如系統相簿)下的照片、影片、音訊等媒體檔案,則需要使用另外的MediaStore API來訪問。但是對於這個API的使用,Android系統也同樣收緊了許可權。

比如現在你想要刪除公有目錄下的某個媒體檔案,系統就會向你丟擲一個RecoverableSecurityException異常,中斷你的刪除行為,以告知你正在進行危險的操作,為此你需要再次請求彈出一個系統級的彈窗,告知使用者你正在進行刪除操作(彈窗上的文字描述不可自定義),在徵求使用者的同意之後方能刪除媒體檔案。

立意雖然是好的,但是這個巨大的行為變更,相當於要求App把之前經過數個版本甚至數十個版本才建立的檔案目錄結構完全推翻,不僅要把之前可能分散各處的檔案重新聚攏到App專有目錄下,還要成片成片地修改之前的程式碼實現。如果短期內強硬要求執行,那必然將是怨聲載道,哀鴻遍野。

所以,針對那些短期內無法完成遷移工作的App,Android系統又給留了一個後門。開發者們可以通過新增requestLegacyExternalStorage清單屬性,允許App適配Android 10的其他變更,但暫時停用分割槽儲存

表現出來的樣子就是App可以沿用之前檔案目錄結構,暫時不需要做任何改變。

Android 11:強制執行分割槽儲存

但當將App更新到以Android 11為目標平臺後,Android系統會忽略該屬性,即會強制執行分割槽儲存

意思很明確:機會呢,我是給過你了,都過了一個版本了你還不改,那就等著App崩潰吧你。

所以呢,只要是以Android 11為目標平臺的App,基本上都是已經完成了應用分割槽儲存的適配,現在它能自由操作的,只能自身專有目錄下的檔案。而對於公有目錄下的媒體檔案,基本上就只有檢視的權力,如果需要修改或者刪除,則如上面所說,需要使用者授權才能操作,而不能再像之前那樣無感知修改和刪除了。

得物App對於系統適配工作的不作為

那好,現在由你來猜一下,本輪事件中的當事App「得物」可能處於以上描述的哪一個階段?

3…2…1!

保留好你的答案,下面我們先用排除法來排除明顯錯誤的選項。

得物App在釋出的第二條聲明裡,還附上了其App釋出動態時的檔案快取管理方案示意圖,如下:

我們主要關注其步驟2——「複製到臨時目錄」,這裡我們可以看到,得物App是將原影片複製了一份到/pictures/duapp/a.mp4路徑下,也就是在檔案管理器的Pictures目錄下建立了一個名為duapp的臨時目錄。

Pictures目錄是幹什麼用的?這個目錄是用於放置使用者可用圖片的,屬於外部儲存公有目錄下的預定義子目錄之一。

既然得物App可以自由複製檔案到公有目錄,那麼就可以排除第3個選項了,也即得物App並沒有以Android 11為目標平臺

剩下2個選項我們也不欲蓋彌彰了,直接下載本輪事件曝光之前,得物App最後一個版本的安裝包檔案(*.apk)一探究竟即可。

從解壓縮後的安裝包的AndroidManifest.xml清單檔案中我們可以看到,該版本的得物App所適配的目標SDK版本為29,也即Android 10,並且添加了requestLegacyExternalStorage屬性,也即請求暫時停用分割槽儲存。

所以,正確答案是第2個選項,得物App並沒有適配Android 10的應用分割槽儲存變更

話說,連Google Play都要求上傳的App必須適配Android 12了,Android 13也都已經出Beta版了,得物App你這樣一直苟在Android 10裡真的沒問題嗎?

系統攔截App刪除操作的判定規則

最後,我們還有一個問題沒有弄明白,也即:

得物App刪除了其放置在公有目錄下的臨時快取檔案,系統在這個時候攔截了此行為,真的是屬於誤判嗎?

為了還原系統攔截行為的一個合理性,我們需要先理清系統攔截App刪除操作的判定規則,為此,我們設立了3個對照組,均以Android 10為目標平臺

建立對照組

  • 實驗組:添加了requestLegacyExternalStorage屬性,即暫時停用分割槽儲存,直接刪除公有目錄下的媒體檔案

  • 對照組1:不新增requestLegacyExternalStorage屬性,即必須得到使用者的授權之後才能刪除公有目錄下的媒體檔案

  • 對照組2:不新增requestLegacyExternalStorage屬性,只刪除其應用專有目錄下的媒體檔案

用於驗證的示例專案,來源於Android開發者官網提供的MediaStore示例,該示例原本的設計是用於演示如何使用MediaStore API來正確顯示手機相簿中的圖片的。

演示的手機選用是Huawei P30 pro,該型號能穩定重現系統攔截操作的推送通知。

結果展示

實驗組:固定收到了系統攔截App刪除操作的推送提醒。

對照組1:獲得使用者授權之後能正常刪除照片,沒有收到系統攔截App刪除操作的推送提醒。

對照組2:既不需要授權,也沒有收到系統攔截App刪除操作的推送提醒。

通過這個對照結果,我們很容易能倒推出系統攔截App刪除操作的判定規則:

  1. 系統認為,對於公有目錄下的媒體檔案,App只應保有讀取的許可權,修改、刪除該媒體檔案都屬於越權行為,是有可能侵害到使用者重要的個人資產的。

  2. 確實有需要刪除某個公有目錄下的媒體檔案的,App必須盡到告知使用者的義務,並把刪除的選擇權交給使用者,在得到使用者授權之後才可以刪除。

  3. 如果App只是修改、刪除其專有目錄下的媒體檔案,系統會認為這是App內部對其快取檔案正常的維護行為,故而不會干涉這類操作。

到這裡,我們可以總結一下,這場烏龍事件之所以會發生,根本問題在於,舊系統的遺留問題與新系統的保護措施起了衝突

得物App使用了不合理的檔案快取管理方案——將臨時快取檔案儲存到了公有目錄下。因此命中到了系統攔截App刪除操作的判定規則,進而收到了相關的系統推送提醒。

要真正解決這個問題,需要得物App積極推進對Android 10應用分割槽儲存的適配,將對檔案快取管理遷移到App專有目錄下進行,就不會引致此類事件的發生。

——這就是得物App疑偷刪使用者影片整件事情的始末。