#由來: 最近專案要接入各種支付,其中一個是銀聯支付。於是開始搗鼓,之前也沒接入過,只是做過微信和支付寶。 下載銀聯的SDK、Demo、文件等等若干東西開始啃,一開啟文件,發現100多頁。。。於是瞬間就不想看了,隨便翻了翻,全都是方案規範什麼的,頭都大了,於是開始搗鼓SDK和Demo。 老樣子,先把Demo跑起來(此處省略若干字)。
跑起來後的銀聯demo首頁如圖所示:
好了現在開始測試一波,使用銀聯提供的測試卡號和手機號以及驗證碼,開始付款,上截圖:
WTF??(黑人問號),為何AS的截圖一直菊花,進不去截圖介面?估計是ADB歇菜了吧(自從AS2.2正式版+Sieera正式版)釋出後,adb一直有問題。好吧我重啟AS
。。。還是不行,一直菊花! 好吧,我用手機自帶的截圖:
無法進行螢幕截圖,原因可能是儲存空間不足,或者該應用或您所屬的單位不允許執行此操作。
黑人問號??這是什麼鬼,還能這樣?
#開始探究 對!前面說了這麼多廢話,就是為了給這個東西做鋪墊。 開始分析一波:這東西在銀聯Demo裡存在,首頁上面已經截圖了,證明沒問題,有問題的是開始支付之後的所有介面,開始猜測:因為支付涉及到的東西比較複雜,尤其是隱私和安全性,所以這些東西一般都是被封裝起來的,對外提供為混淆後的jar包。 去驗證一波,先找到Demo中支付的按鈕所觸發的動作:
public class JARActivity extends BaseActivity {
@Override
public void doStartUnionPayPlugin(Activity activity, String tn, String mode) {
UPPayAssistEx.startPay(activity, null, null, tn, mode);
}
//省略部分程式碼...
}
複製程式碼
可以看到,發起支付之後,到了UPPayAssistEx.startPay()
方法去了,而這個方法正好是在SDK的jar包內。
也就是說導致我們沒法截圖的程式碼在SDK中,有可能是C層面控制的(畢竟有so),也有可能是Android自己控制的,提供了API。
C層面的不好找,所以先看看是否是Android的API吧。
組織我們截圖,也就是說在xxxActivity
下,我們沒辦法獲得這個Activity的“資訊”,所以先去看看支付的Activity是怎麼寫的吧。
這裡要祭出神器了:TopActivity.apk
我從事Android開發一年,這個東西也伴隨了我一年,他可以獲取當前執行的apk的Activity名字以及包名,這個app被作者開源在了Github
祭出誅仙劍之後,開始嘗試尋找支付介面的類名和包名(快看左上角快看左上角):
可以看到,包名是com.unionpay.uppay
,Activity名字是PayActivity
。
OK,有了這些資訊,開始去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);
}
//省略部分程式碼...
}
複製程式碼
找到了這個支付介面的Activity,一般來說要對視窗進行操作,需要在 onCreate()
中,但是這個Activity的onCreate()
是呼叫父類的方法。其實也很正常,畢竟是有個基類的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;
//省略部分程式碼...
}
//省略部分程式碼...
}
複製程式碼
好了,這個就是銀聯支付所有Activity的父類了,畢竟繼承了Activity。
分析下有沒有什麼有用的線索:
發現一個東西:this.getWindow().addFlags(8192);
。這個和我們之前的假設差不多,因為Activity和window有極大的關係,很多操作都要依靠getWindow()
來進行,比如去掉標題欄之類的。那麼這個8912是什麼鬼?
Android中的這種系統常量一般都是16進位制的,所以我們把這個8192轉換成16進位制看看是多少:
0x2000
因為這個常量是給window的,回想一下之前我們設定全屏:
this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
所以我們進入到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!**看這個變數名字就知道了,FLAG_SECURE->安全
。當然不能依照名字來斷定,還是看看註釋。這上面說:這個標誌是用來將視窗內容視為安全的,它不會出現在螢幕截圖裡面。
也就是說:我們自己的Activity,只要加上了這個標誌,就會變得“安全”,不會被螢幕截圖捕捉到,即使是adb命令。
驗證之後,果然如此,AS獲取不到螢幕截圖,手機自帶的截圖也拿不到了,豌豆莢等第三方客戶端暫時沒測試,電腦上沒有豌豆莢,感興趣的朋友可以試試看。
#結語
想要像銀聯一樣,在某Activity做到手機無法截圖,甚至是adb也拿不到,那麼可以在Activity中加入:
getWindow().addFlags(WindowManager.LayoutParams. FLAG_SECURE);