由來(lái):
最近項(xiàng)目要接入各種支付,其中一個(gè)是銀聯(lián)支付含鳞。于是開(kāi)始搗鼓影锈,之前也沒(méi)接入過(guò),只是做過(guò)微信和支付寶蝉绷。
下載銀聯(lián)的SDK鸭廷、Demo、文檔等等若干東西開(kāi)始啃熔吗,一打開(kāi)文檔辆床,發(fā)現(xiàn)100多頁(yè)。桅狠。讼载。于是瞬間就不想看了,隨便翻了翻中跌,全都是方案規(guī)范什么的咨堤,頭都大了,于是開(kāi)始搗鼓SDK和Demo漩符。
老樣子吱型,先把Demo跑起來(lái)(此處省略若干字)。
跑起來(lái)后的銀聯(lián)demo首頁(yè)如圖所示:
好了現(xiàn)在開(kāi)始測(cè)試一波陨仅,使用銀聯(lián)提供的測(cè)試卡號(hào)和手機(jī)號(hào)以及驗(yàn)證碼津滞,開(kāi)始付款铝侵,上截圖:
WTF?触徐?(黑人問(wèn)號(hào))咪鲜,為何AS的截圖一直菊花,進(jìn)不去截圖界面撞鹉?估計(jì)是ADB歇菜了吧(自從AS2.2正式版+Sieera正式版)發(fā)布后疟丙,adb一直有問(wèn)題。好吧我重啟AS
鸟雏。享郊。。還是不行孝鹊,一直菊花炊琉!
好吧,我用手機(jī)自帶的截圖:
無(wú)法進(jìn)行屏幕截圖又活,原因可能是存儲(chǔ)空間不足苔咪,或者該應(yīng)用或您所屬的單位不允許執(zhí)行此操作。
黑人問(wèn)號(hào)柳骄?团赏?這是什么鬼,還能這樣耐薯?
開(kāi)始探究
對(duì)舔清!前面說(shuō)了這么多廢話,就是為了給這個(gè)東西做鋪墊曲初。
開(kāi)始分析一波:這東西在銀聯(lián)Demo里存在鸠踪,首頁(yè)上面已經(jīng)截圖了,證明沒(méi)問(wèn)題复斥,有問(wèn)題的是開(kāi)始支付之后的所有界面营密,開(kāi)始猜測(cè):因?yàn)橹Ц渡婕暗降臇|西比較復(fù)雜,尤其是隱私和安全性目锭,所以這些東西一般都是被封裝起來(lái)的评汰,對(duì)外提供為混淆后的jar包。
去驗(yàn)證一波痢虹,先找到Demo中支付的按鈕所觸發(fā)的動(dòng)作:
public class JARActivity extends BaseActivity {
@Override
public void doStartUnionPayPlugin(Activity activity, String tn, String mode) {
UPPayAssistEx.startPay(activity, null, null, tn, mode);
}
//省略部分代碼...
}
可以看到被去,發(fā)起支付之后,到了UPPayAssistEx.startPay()
方法去了奖唯,而這個(gè)方法正好是在SDK的jar包內(nèi)惨缆。
也就是說(shuō)導(dǎo)致我們沒(méi)法截屏的代碼在SDK中,有可能是C層面控制的(畢竟有so),也有可能是Android自己控制的坯墨,提供了API寂汇。
C層面的不好找,所以先看看是否是Android的API吧捣染。
組織我們截屏骄瓣,也就是說(shuō)在xxxActivity
下,我們沒(méi)辦法獲得這個(gè)Activity的“信息”耍攘,所以先去看看支付的Activity是怎么寫的吧榕栏。
這里要祭出神器了:TopActivity.apk
我從事Android開(kāi)發(fā)一年,這個(gè)東西也伴隨了我一年蕾各,他可以獲取當(dāng)前運(yùn)行的apk的Activity名字以及包名扒磁,這個(gè)app被作者開(kāi)源在了Github
祭出誅仙劍之后,開(kāi)始嘗試尋找支付界面的類名和包名(快看左上角快看左上角):
可以看到式曲,包名是com.unionpay.uppay
妨托,Activity名字是PayActivity
。
OK检访,有了這些信息,開(kāi)始去SDK中尋找:
public final class PayActivity extends BaseActivity {
private b c = null;
private f d = null;
private n e;
public static String a;
private k f = null;
public PayActivity() {}
public final void onCreate(Bundle var1) {
super.onCreate(var1);
}
//省略部分代碼...
}
找到了這個(gè)支付界面的Activity仔掸,一般來(lái)說(shuō)要對(duì)窗口進(jìn)行操作脆贵,需要在 onCreate()
中,但是這個(gè)Activity的onCreate()
是調(diào)用父類的方法起暮。其實(shí)也很正常卖氨,畢竟是有個(gè)基類的Activity,所以我們看看BaseActivity:
public abstract class BaseActivity extends
Activity implements com.unionpay.mobile.android.plugin.a, b {
//省略部分代碼...
public void onCreate(Bundle var1) {
//省略部分代碼...
UPAgent.LOG_ON = false;
this.requestWindowFeature(1);
super.onCreate(var1);
this.c = (l)this.a(1, (e)null);
this.setContentView(this.c);
this.getWindow().addFlags(8192);
++f;
//省略部分代碼...
}
//省略部分代碼...
}
好了负懦,這個(gè)就是銀聯(lián)支付所有Activity的父類了筒捺,畢竟繼承了Activity。
分析下有沒(méi)有什么有用的線索:
發(fā)現(xiàn)一個(gè)東西:this.getWindow().addFlags(8192);
纸厉。這個(gè)和我們之前的假設(shè)差不多系吭,因?yàn)锳ctivity和window有極大的關(guān)系,很多操作都要依靠getWindow()
來(lái)進(jìn)行颗品,比如去掉標(biāo)題欄之類的肯尺。那么這個(gè)8912是什么鬼?
Android中的這種系統(tǒng)常量一般都是16進(jìn)制的躯枢,所以我們把這個(gè)8192轉(zhuǎn)換成16進(jìn)制看看是多少:
0x2000
因?yàn)檫@個(gè)常量是給window的则吟,回想一下之前我們?cè)O(shè)置全屏:
this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
所以我們進(jìn)入到WindowManager.LayoutParams
去看看,搜索常量先锄蹂。
搜索2000
:
/** Window flag: treat the content of the window as secure, preventing
* it from appearing in screenshots or from being viewed on non-secure
* displays.
*
* <p>See {@link android.view.Display#FLAG_SECURE} for more details about
* secure surfaces and secure displays.
*/
public static final int FLAG_SECURE = 0x00002000;
NICE氓仲!看這個(gè)變量名字就知道了,FLAG_SECURE->安全
。當(dāng)然不能依照名字來(lái)斷定敬扛,還是看看注釋晰洒。這上面說(shuō):這個(gè)標(biāo)志是用來(lái)將窗口內(nèi)容視為安全的,它不會(huì)出現(xiàn)在屏幕截圖里面舔哪。
也就是說(shuō):我們自己的Activity欢顷,只要加上了這個(gè)標(biāo)志,就會(huì)變得“安全”捉蚤,不會(huì)被屏幕截圖捕捉到抬驴,即使是adb命令。
驗(yàn)證之后缆巧,果然如此布持,AS獲取不到屏幕截圖,手機(jī)自帶的截屏也拿不到了陕悬,豌豆莢等第三方客戶端暫時(shí)沒(méi)測(cè)試题暖,電腦上沒(méi)有豌豆莢,感興趣的朋友可以試試看捉超。
結(jié)語(yǔ)
想要像銀聯(lián)一樣胧卤,在某Activity做到手機(jī)無(wú)法截屏,甚至是adb也拿不到拼岳,那么可以在Activity中加入:
getWindow().addFlags(WindowManager.LayoutParams. FLAG_SECURE);