您的位置:首页 > 移动开发 > Android开发

[置顶] Android 应用内禁止截屏功能的实现

2017-05-23 13:57 1506 查看

截图介绍

  Android的调试工具DDMS提供有截屏功能,很多软件也会有截屏功能,在做支付等安全类应用的时候,为了保证用户的资产和系统安全,往往会禁止应用内截屏,禁止之后,在此应用处于前台的情况下,截屏功能将不能使用,如下图所示



截图的原理

DDMS的实现方式

  DDMS是通过adb调用设备端的adbd(ADBdaemon)提供的framebufferservice进行截屏(源码在system/core/adb/framebuffer_service.c),在较早版本的Android中,framebufferservice通过直接读framebuffer设备(/dev/graphics/fb0)来截屏,但是读framebuffer设备(/dev/graphics/fb0)的方式在某些使用硬件overlay显示的设备上可能无法截取到某些画面(例如videoplayback和camerapreview画面)。

  在较新版本的Android中,framebufferservice则调用截屏工具screencap来截屏。

screencap工具

  screencap是Android原生自带的工具,是一个C写的可执行文件,在设备上的/system/bin/下面可以找到它,screencap截屏后可保存为PNG格式文件或RGBRAW文件。screencap的源码
frameworks/base/cmds/screencap/
,它调用SurfaceFlinger提供的截屏接口ScreenshotClient,其源码在
frameworks/native/libs/gui/SurfaceComposerClient.cpp
(该路径在不同版本的Android源码中可能略有差别),ScreenshotClient通过进程间通信调用SurfaceFlingerservice的截屏功能,源码在
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
中的函数
SurfaceFlinger::captureScreen


SurfaceFlinger提供的上述截屏接口则可以完美截取任何屏幕画面,因此相对来说是Android上最正规最完善的截屏方法,使用起来也非常简单。

关于SurfaceFlinger和framebuffer的内容不再做赘述,因为描述起来非常复杂,不是本文的重点,有兴趣的同学可以参考相关资料学习。

截图的实现

shell命令实现

screencap命令用法如下

screencap[-hp][FILENAME]
-h:thismessage
-p:savethefileasapng.
IfFILENAMEendswith.pngitwillbesavedasapng.
IfFILENAMEisnotgiven,theresultswillbeprintedtostdout.

如果文件名以.png结尾时,它将保存为png文件

如果文件名没有给出,则结果被会被输出到stdout

例如以下命令会将屏幕截图保存在sd卡路径下,文件名为screen.png

$adbshellscreencap-p/sdcard/screen.png

代码实现

截图工具类ScreenUtils

publicclassScreenUtils{
privateScreenUtils(){
/*cannotbeinstantiated*/
thrownewUnsupportedOperationException("cannotbeinstantiated");
}

/**
*获得屏幕高度
*
*@paramcontext
*@return
*/
publicstaticintgetScreenWidth(Contextcontext){
WindowManagerwm=(WindowManager)context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetricsoutMetrics=newDisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
returnoutMetrics.widthPixels;
}

/**
*获得屏幕宽度
*
*@paramcontext
*@return
*/
publicstaticintgetScreenHeight(Contextcontext){
WindowManagerwm=(WindowManager)context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetricsoutMetrics=newDisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
returnoutMetrics.heightPixels;
}

/**
*获得状态栏的高度
*
*@paramcontext
*@return
*/
publicstaticintgetStatusHeight(Contextcontext){

intstatusHeight=-1;
try{
Class<?>clazz=Class.forName("com.android.internal.R$dimen");
Objectobject=clazz.newInstance();
intheight=Integer.parseInt(clazz.getField("status_bar_height")
.get(object).toString());
statusHeight=context.getResources().getDimensionPixelSize(height);
}catch(Exceptione){
e.printStackTrace();
}
returnstatusHeight;
}

/**
*获取当前屏幕截图,包含状态栏
*
*@paramactivity
*@return
*/
publicstaticBitmapsnapShotWithStatusBar(Activityactivity){
Viewview=activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmapbmp=view.getDrawingCache();
intwidth=getScreenWidth(activity);
intheight=getScreenHeight(activity);
Bitmapbp=null;
bp=Bitmap.createBitmap(bmp,0,0,width,height);
view.destroyDrawingCache();
returnbp;

}

/**
*获取当前屏幕截图,不包含状态栏
*
*@paramactivity
*@return
*/
publicstaticBitmapsnapShotWithoutStatusBar(Activityactivity){
Viewview=activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmapbmp=view.getDrawingCache();
Rectframe=newRect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
intstatusBarHeight=frame.top;

intwidth=getScreenWidth(activity);
intheight=getScreenHeight(activity);
Bitmapbp=null;
bp=Bitmap.createBitmap(bmp,0,statusBarHeight,width,height
-statusBarHeight);
view.destroyDrawingCache();
returnbp;

}

}


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

通过调用以上工具类的截图方法就可以拿到图片的bitmap然后就可以随心所欲的进行进一步操作了。

禁止截图的实现

禁止截图通过对window对象加标志位FLAG_SECURE实现,此标识位的注释如下,

/**Windowflag:treatthecontentofthewindowassecure,preventing
*itfromappearinginscreenshotsorfrombeingviewedonnon-secure
*displays.
*
*<p>See{@linkandroid.view.Display#FLAG_SECURE}formoredetailsabout
*securesurfacesandsecuredisplays.
*/
publicstaticfinalintFLAG_SECURE=0x00002000;

使用方式如下:

@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);

}

privatevoidinitializeScreenshotSecurity(){
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.ICE_CREAM_SANDWICH&&
TextSecurePreferences.isScreenSecurityEnabled(this))
{
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
}else{
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
}


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: