聲明:本文更注重于原理知識的普及,因此文中不會有大量實際代碼的展示,如果想從代碼層面上了解「應用存儲分區」的內容,歡迎閱讀我兩年前寫過的技術文章《Android 10 應用分區存儲適配實踐》
近日,有網友爆料,稱其發現得物App有疑似偷偷調用手機權限刪除用戶視頻的行為。
事件的起因,是該網友在得物App上購買到的商品有問題,于是按流程向平臺方反饋,并上傳了相關的視頻證據。
但沒過多久,其手機上就收到了一條系統推送提醒,內容是檢測到“得物”刪除了視頻,已成功攔截,據該網友推測,被刪除的正是作為重要維權證據的那條視頻。
此事一經曝光,立即在網上掀起了軒然大波。從大量評論跟進的內容上看,網友們最關注的問題集中在:
得物App到底有沒有未經用戶同意,就刪除了用戶手機相冊中的視頻?
隨著事情的逐漸發酵,得物App也緊急連發了兩條聲明,最新的一條聲明回應稱:刪除的是編輯、處理、上傳過程中產生的臨時緩存文件,是對該臨時緩存文件的處理觸發了系統攔截通知。
聲明發出后,有相當一部分網友采納了這個說法,畢竟類似的誤報之前也已經在其他App上發生過不少了。到這里,事情似乎告一段落了。
但是,這真的可以簡單歸結為系統攔截通知的誤報嗎?多年從事Android開發的直覺告訴我,這件事情的背后,肯定還有更加深層的原因。
也是基于這種直覺的驅使,經過了大半個晚上的信息收集、情景模擬以及測試驗證,我大致上已經能夠梳理出這整件事情的來龍去脈了。
先拋出結論,這其實是一場由「Android系統的歷史遺留問題」,「得物App對于適配工作的不作為」以及「系統攔截App刪除操作的判定規則」三者共同作用下所引發的「烏龍事件」。
故事,還得先從Android系統的歷史遺留問題開始講起。
Android系統的歷史遺留問題
Android 10之前:亂象叢生
在Android 10之前,Android系統對于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刪除操作的判定規則:
系統認為,對于公有目錄下的媒體文件,App只應保有讀取的權限,修改、刪除該媒體文件都屬于越權行為,是有可能侵害到用戶重要的個人資產的。
確實有需要刪除某個公有目錄下的媒體文件的,App必須盡到告知用戶的義務,并把刪除的選擇權交給用戶,在得到用戶授權之后才可以刪除。
如果App只是修改、刪除其專有目錄下的媒體文件,系統會認為這是App內部對其緩存文件正常的維護行為,故而不會干涉這類操作。
到這里,我們可以總結一下,這場烏龍事件之所以會發生,根本問題在于,舊系統的遺留問題與新系統的保護措施起了沖突。
得物App使用了不合理的文件緩存管理方案——將臨時緩存文件保存到了公有目錄下。因此命中到了系統攔截App刪除操作的判定規則,進而收到了相關的系統推送提醒。
要真正解決這個問題,需要得物App積極推進對Android 10應用分區存儲的適配,將對文件緩存管理遷移到App專有目錄下進行,就不會引致此類事件的發生。
——這就是得物App疑偷刪用戶視頻整件事情的始末。
少俠,請留步!若本文對你有所幫助或啟發,還請:
- 點贊????,讓更多的人能看到!
- 收藏??,好文值得反復品味!
- 關注?,不錯過每一次更文!
===> 技術號:「星際碼仔」??
你的支持是我繼續創作的動力,感謝!??