This source file includes following definitions.
- start
- isValid
- getCacheEntry
- isValid
- isValid
- getCache
- pruneCache1
- pruneCache
- cacheChild
- cacheFileStatus
- cacheDirectoryFromCursor
- postPruneMessage
- postInvalidateCache
- postInvalidateCacheDir
- postInvalidateStat
- runInt
- runObject
- throwException
- runIntFunction
- runObjectFunction
- documentIdFromName1
- documentIdFromName
- statDocument1
- statDocument
- accessDocument1
- accessDocument
- openDocumentDirectory1
- openDocumentDirectory
- openDocument1
- openDocument
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.gnu.emacs;
21
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.Iterator;
25
26 import java.io.FileNotFoundException;
27 import java.io.IOException;
28
29 import android.content.ContentResolver;
30 import android.database.Cursor;
31 import android.net.Uri;
32
33 import android.os.Build;
34 import android.os.CancellationSignal;
35 import android.os.Handler;
36 import android.os.HandlerThread;
37 import android.os.OperationCanceledException;
38 import android.os.ParcelFileDescriptor;
39 import android.os.SystemClock;
40
41 import android.util.Log;
42
43 import android.provider.DocumentsContract;
44 import android.provider.DocumentsContract.Document;
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 public final class EmacsSafThread extends HandlerThread
85 {
86 private static final String TAG = "EmacsSafThread";
87
88
89 private final ContentResolver resolver;
90
91
92
93 private final HashMap<Uri, CacheToplevel> cacheToplevels;
94
95
96 private Handler handler;
97
98
99 public static final int S_IRUSR = 0000400;
100 public static final int S_IWUSR = 0000200;
101 public static final int S_IXUSR = 0000100;
102 public static final int S_IFCHR = 0020000;
103 public static final int S_IFDIR = 0040000;
104 public static final int S_IFREG = 0100000;
105
106
107
108 public static final int CACHE_PRUNE_TIME = 10;
109
110
111
112 public static final int CACHE_INVALID_TIME = 10;
113
114 public
115 EmacsSafThread (ContentResolver resolver)
116 {
117 super ("Document provider access thread");
118 this.resolver = resolver;
119 this.cacheToplevels = new HashMap<Uri, CacheToplevel> ();
120 }
121
122
123
124 @Override
125 public void
126 start ()
127 {
128 super.start ();
129
130
131 handler = new Handler (getLooper ());
132
133
134 postPruneMessage ();
135 }
136
137
138 private static final class CacheToplevel
139 {
140
141 HashMap<String, DocIdEntry> children;
142
143
144 HashMap<String, StatCacheEntry> statCache;
145
146
147 HashMap<String, CacheEntry> idCache;
148 };
149
150 private static final class StatCacheEntry
151 {
152
153 long time;
154
155
156 long flags, size, mtime;
157
158
159 boolean isDirectory;
160
161 public
162 StatCacheEntry ()
163 {
164 time = SystemClock.uptimeMillis ();
165 }
166
167 public boolean
168 isValid ()
169 {
170 return ((SystemClock.uptimeMillis () - time)
171 < CACHE_INVALID_TIME * 1000);
172 }
173 };
174
175 private static final class DocIdEntry
176 {
177
178 String documentId;
179
180
181 long time;
182
183 public
184 DocIdEntry ()
185 {
186 time = SystemClock.uptimeMillis ();
187 }
188
189
190
191
192
193
194
195
196
197
198
199 public CacheEntry
200 getCacheEntry (ContentResolver resolver, Uri tree,
201 CacheToplevel toplevel,
202 CancellationSignal signal)
203 {
204 Uri uri;
205 String[] projection;
206 String type;
207 Cursor cursor;
208 int column;
209 CacheEntry entry;
210
211
212
213
214 uri = DocumentsContract.buildDocumentUriUsingTree (tree,
215 documentId);
216 projection = new String[] {
217 Document.COLUMN_MIME_TYPE,
218 };
219
220 cursor = null;
221
222 try
223 {
224 cursor = resolver.query (uri, projection, null,
225 null, null, signal);
226
227 if (!cursor.moveToFirst ())
228 return null;
229
230 column = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
231
232 if (column < 0)
233 return null;
234
235 type = cursor.getString (column);
236
237 if (type == null)
238 return null;
239
240 entry = new CacheEntry ();
241 entry.type = type;
242 toplevel.idCache.put (documentId, entry);
243 return entry;
244 }
245 catch (OperationCanceledException e)
246 {
247 throw e;
248 }
249 catch (Throwable e)
250 {
251 return null;
252 }
253 finally
254 {
255 if (cursor != null)
256 cursor.close ();
257 }
258 }
259
260 public boolean
261 isValid ()
262 {
263 return ((SystemClock.uptimeMillis () - time)
264 < CACHE_INVALID_TIME * 1000);
265 }
266 };
267
268 private static final class CacheEntry
269 {
270
271 String type;
272
273
274 HashMap<String, DocIdEntry> children;
275
276
277 long time;
278
279 public
280 CacheEntry ()
281 {
282 children = new HashMap<String, DocIdEntry> ();
283 time = SystemClock.uptimeMillis ();
284 }
285
286 public boolean
287 isValid ()
288 {
289 return ((SystemClock.uptimeMillis () - time)
290 < CACHE_INVALID_TIME * 1000);
291 }
292 };
293
294
295
296 private CacheToplevel
297 getCache (Uri uri)
298 {
299 CacheToplevel toplevel;
300
301 toplevel = cacheToplevels.get (uri);
302
303 if (toplevel != null)
304 return toplevel;
305
306 toplevel = new CacheToplevel ();
307 toplevel.children = new HashMap<String, DocIdEntry> ();
308 toplevel.statCache = new HashMap<String, StatCacheEntry> ();
309 toplevel.idCache = new HashMap<String, CacheEntry> ();
310 cacheToplevels.put (uri, toplevel);
311 return toplevel;
312 }
313
314
315
316
317 private void
318 pruneCache1 (Collection<DocIdEntry> collection)
319 {
320 Iterator<DocIdEntry> iter;
321 DocIdEntry tem;
322
323 iter = collection.iterator ();
324 while (iter.hasNext ())
325 {
326
327 tem = iter.next ();
328
329
330
331
332
333
334 if (tem.isValid ())
335 continue;
336
337 iter.remove ();
338 }
339 }
340
341
342
343
344 private void
345 pruneCache ()
346 {
347 Iterator<CacheEntry> iter;
348 Iterator<StatCacheEntry> statIter;
349 CacheEntry tem;
350 StatCacheEntry stat;
351
352 for (CacheToplevel toplevel : cacheToplevels.values ())
353 {
354
355 iter = toplevel.idCache.values ().iterator ();
356
357 while (iter.hasNext ())
358 {
359
360 tem = iter.next ();
361
362
363
364
365
366
367
368 if (tem.isValid ())
369 {
370
371
372 pruneCache1 (tem.children.values ());
373 continue;
374 }
375
376 iter.remove ();
377 }
378
379 statIter = toplevel.statCache.values ().iterator ();
380
381 while (statIter.hasNext ())
382 {
383
384 stat = statIter.next ();
385
386
387
388
389
390
391
392 if (stat.isValid ())
393 continue;
394
395 statIter.remove ();
396 }
397 }
398
399 postPruneMessage ();
400 }
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415 private CacheEntry
416 cacheChild (CacheToplevel toplevel,
417 HashMap<String, DocIdEntry> children,
418 String name, String id, String type,
419 boolean id_entry_exists)
420 {
421 DocIdEntry idEntry;
422 CacheEntry cacheEntry;
423
424 if (!id_entry_exists)
425 {
426 idEntry = new DocIdEntry ();
427 idEntry.documentId = id;
428 children.put (name, idEntry);
429 }
430
431 cacheEntry = new CacheEntry ();
432 cacheEntry.type = type;
433 toplevel.idCache.put (id, cacheEntry);
434 return cacheEntry;
435 }
436
437
438
439
440
441
442
443
444
445 private StatCacheEntry
446 cacheFileStatus (String documentId, CacheToplevel toplevel,
447 Cursor cursor, boolean no_cache)
448 {
449 StatCacheEntry entry;
450 int flagsIndex, columnIndex, typeIndex;
451 int sizeIndex, mtimeIndex;
452 String type;
453
454
455 flagsIndex = cursor.getColumnIndex (Document.COLUMN_FLAGS);
456 sizeIndex = cursor.getColumnIndex (Document.COLUMN_SIZE);
457 typeIndex = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
458 mtimeIndex = cursor.getColumnIndex (Document.COLUMN_LAST_MODIFIED);
459
460
461
462 if (flagsIndex < 0 || sizeIndex < 0 || typeIndex < 0)
463 return null;
464
465
466 entry = new StatCacheEntry ();
467 entry.flags = cursor.getInt (flagsIndex);
468 type = cursor.getString (typeIndex);
469
470 if (type == null)
471 return null;
472
473 entry.isDirectory = type.equals (Document.MIME_TYPE_DIR);
474
475 if (cursor.isNull (sizeIndex))
476
477 entry.size = -1;
478 else
479 entry.size = cursor.getLong (sizeIndex);
480
481
482
483
484 if (mtimeIndex >= 0 && !cursor.isNull (mtimeIndex))
485 entry.mtime = cursor.getLong (mtimeIndex);
486
487
488 if (!no_cache)
489 toplevel.statCache.put (documentId, entry);
490 return entry;
491 }
492
493
494
495
496
497
498
499
500
501
502
503 private void
504 cacheDirectoryFromCursor (CacheToplevel toplevel, String documentId,
505 Cursor cursor)
506 {
507 CacheEntry entry, constitutent;
508 int nameColumn, idColumn, typeColumn;
509 String id, name, type;
510 DocIdEntry idEntry;
511
512
513
514 nameColumn
515 = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME);
516 idColumn
517 = cursor.getColumnIndex (Document.COLUMN_DOCUMENT_ID);
518 typeColumn
519 = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
520
521 if (nameColumn < 0 || idColumn < 0 || typeColumn < 0)
522 return;
523
524 entry = new CacheEntry ();
525
526
527 entry.type = Document.MIME_TYPE_DIR;
528 toplevel.idCache.put (documentId, entry);
529
530
531
532 while (cursor.moveToNext ())
533 {
534 try
535 {
536 name = cursor.getString (nameColumn);
537 id = cursor.getString (idColumn);
538 type = cursor.getString (typeColumn);
539
540 if (name == null || id == null || type == null)
541 continue;
542
543
544
545 idEntry = new DocIdEntry ();
546 idEntry.documentId = id;
547 entry.children.put (id, idEntry);
548
549
550
551
552
553 cacheFileStatus (id, toplevel, cursor, false);
554
555
556
557
558
559 if (type.equals (Document.MIME_TYPE_DIR))
560 continue;
561
562
563
564 constitutent = new CacheEntry ();
565 constitutent.type = type;
566 toplevel.idCache.put (documentId, entry);
567 }
568 catch (Exception e)
569 {
570 e.printStackTrace ();
571 continue;
572 }
573 }
574
575
576 cursor.moveToPosition (-1);
577 }
578
579
580
581
582 private void
583 postPruneMessage ()
584 {
585 handler.postDelayed (new Runnable () {
586 @Override
587 public void
588 run ()
589 {
590 pruneCache ();
591 }
592 }, CACHE_PRUNE_TIME * 1000);
593 }
594
595
596
597
598
599
600
601
602 public void
603 postInvalidateCache (final Uri uri, final String documentId,
604 final String cacheName)
605 {
606 handler.post (new Runnable () {
607 @Override
608 public void
609 run ()
610 {
611 CacheToplevel toplevel;
612 HashMap<String, DocIdEntry> children;
613 String[] components;
614 CacheEntry entry;
615 DocIdEntry idEntry;
616
617 toplevel = getCache (uri);
618 toplevel.idCache.remove (documentId);
619 toplevel.statCache.remove (documentId);
620
621
622
623 children = toplevel.children;
624 components = cacheName.split ("/");
625
626 for (String component : components)
627 {
628
629
630 if (component.isEmpty ())
631 continue;
632
633 if (component == components[components.length - 1])
634 {
635
636
637 children.remove (component);
638 return;
639 }
640 else
641 {
642
643
644
645 idEntry = children.get (component);
646
647 if (idEntry == null)
648
649 return;
650
651 entry = toplevel.idCache.get (idEntry.documentId);
652
653 if (entry == null)
654
655 return;
656
657
658
659 children = entry.children;
660 }
661 }
662 }
663 });
664 }
665
666
667
668
669
670
671
672
673 public void
674 postInvalidateCacheDir (final Uri uri, final String documentId,
675 final String cacheName)
676 {
677 handler.post (new Runnable () {
678 @Override
679 public void
680 run ()
681 {
682 CacheToplevel toplevel;
683 HashMap<String, DocIdEntry> children;
684 String[] components;
685 CacheEntry entry;
686 DocIdEntry idEntry;
687 Iterator<DocIdEntry> iter;
688
689 toplevel = getCache (uri);
690 toplevel.idCache.remove (documentId);
691 toplevel.statCache.remove (documentId);
692
693
694
695
696 children = toplevel.children;
697 components = cacheName.split ("/");
698
699 for (String component : components)
700 {
701
702
703 if (component.isEmpty ())
704 continue;
705
706
707
708
709 idEntry = children.get (component);
710
711 if (idEntry == null)
712
713 return;
714
715 entry = toplevel.idCache.get (idEntry.documentId);
716
717 if (entry == null)
718
719 return;
720
721
722
723 children = entry.children;
724 }
725
726 iter = children.values ().iterator ();
727 while (iter.hasNext ())
728 {
729 idEntry = iter.next ();
730
731 if (idEntry.documentId.equals (documentId))
732 {
733 iter.remove ();
734 break;
735 }
736 }
737 }
738 });
739 }
740
741
742
743
744
745
746 public void
747 postInvalidateStat (final Uri uri, final String documentId)
748 {
749 handler.post (new Runnable () {
750 @Override
751 public void
752 run ()
753 {
754 CacheToplevel toplevel;
755
756 toplevel = getCache (uri);
757 toplevel.statCache.remove (documentId);
758 }
759 });
760 }
761
762
763
764
765
766
767
768 private abstract class SafIntFunction
769 {
770
771
772
773
774
775
776
777
778
779 public abstract int runInt (CancellationSignal signal)
780 throws Throwable;
781 };
782
783 private abstract class SafObjectFunction
784 {
785
786
787
788
789
790
791
792
793
794 public abstract Object runObject (CancellationSignal signal)
795 throws Throwable;
796 };
797
798
799
800
801
802
803
804
805
806
807
808
809
810 @SuppressWarnings("unchecked")
811 private static <T extends Throwable> void
812 throwException (Throwable exception)
813 throws T
814 {
815 throw (T) exception;
816 }
817
818
819
820
821
822
823
824
825
826
827 private int
828 runIntFunction (final SafIntFunction function)
829 {
830 final EmacsHolder<Object> result;
831 final CancellationSignal signal;
832 Throwable throwable;
833
834 result = new EmacsHolder<Object> ();
835 signal = new CancellationSignal ();
836
837 handler.post (new Runnable () {
838 @Override
839 public void
840 run ()
841 {
842 try
843 {
844 result.thing
845 = Integer.valueOf (function.runInt (signal));
846 }
847 catch (Throwable throwable)
848 {
849 result.thing = throwable;
850 }
851
852 EmacsNative.safPostRequest ();
853 }
854 });
855
856 if (EmacsNative.safSyncAndReadInput () != 0)
857 {
858 signal.cancel ();
859
860
861
862
863
864
865 EmacsNative.safSync ();
866 }
867
868 if (result.thing instanceof Throwable)
869 {
870 throwable = (Throwable) result.thing;
871 EmacsSafThread.<RuntimeException>throwException (throwable);
872 }
873
874 return (Integer) result.thing;
875 }
876
877
878
879
880
881
882
883
884
885
886 private Object
887 runObjectFunction (final SafObjectFunction function)
888 {
889 final EmacsHolder<Object> result;
890 final CancellationSignal signal;
891 Throwable throwable;
892
893 result = new EmacsHolder<Object> ();
894 signal = new CancellationSignal ();
895
896 handler.post (new Runnable () {
897 @Override
898 public void
899 run ()
900 {
901 try
902 {
903 result.thing = function.runObject (signal);
904 }
905 catch (Throwable throwable)
906 {
907 result.thing = throwable;
908 }
909
910 EmacsNative.safPostRequest ();
911 }
912 });
913
914 if (EmacsNative.safSyncAndReadInput () != 0)
915 {
916 signal.cancel ();
917
918
919
920
921
922
923 EmacsNative.safSync ();
924 }
925
926 if (result.thing instanceof Throwable)
927 {
928 throwable = (Throwable) result.thing;
929 EmacsSafThread.<RuntimeException>throwException (throwable);
930 }
931
932 return result.thing;
933 }
934
935
936
937
938 private int
939 documentIdFromName1 (String tree_uri, String name,
940 String[] id_return, CancellationSignal signal)
941 {
942 Uri uri, treeUri;
943 String id, type, newId, newType;
944 String[] components, projection;
945 Cursor cursor;
946 int nameColumn, idColumn, typeColumn;
947 CacheToplevel toplevel;
948 DocIdEntry idEntry;
949 HashMap<String, DocIdEntry> children, next;
950 CacheEntry cache;
951
952 projection = new String[] {
953 Document.COLUMN_DISPLAY_NAME,
954 Document.COLUMN_DOCUMENT_ID,
955 Document.COLUMN_MIME_TYPE,
956 };
957
958
959 uri = Uri.parse (tree_uri);
960
961
962 components = name.split ("/");
963
964
965 type = id = null;
966 cursor = null;
967
968
969 toplevel = getCache (uri);
970
971
972 children = toplevel.children;
973
974
975
976 try
977 {
978 for (String component : components)
979 {
980
981
982 if (component.isEmpty ())
983 continue;
984
985
986
987
988 idEntry = children.get (component);
989
990 if (idEntry != null)
991 {
992
993
994
995 cache = toplevel.idCache.get (idEntry.documentId);
996
997
998
999 if (cache == null)
1000 cache = idEntry.getCacheEntry (resolver, uri, toplevel,
1001 signal);
1002
1003 if (cache == null)
1004 {
1005
1006
1007
1008
1009 children.remove (component);
1010
1011 if (id == null)
1012 id = DocumentsContract.getTreeDocumentId (uri);
1013
1014 id_return[0] = id;
1015
1016 if ((type == null
1017 || type.equals (Document.MIME_TYPE_DIR))
1018
1019
1020 && component == components[components.length - 1])
1021 return -2;
1022
1023 return -1;
1024 }
1025
1026
1027 id = idEntry.documentId;
1028 type = cache.type;
1029 children = cache.children;
1030 continue;
1031 }
1032
1033
1034
1035
1036 if (id == null)
1037 id = DocumentsContract.getTreeDocumentId (uri);
1038
1039 treeUri
1040 = DocumentsContract.buildChildDocumentsUriUsingTree (uri, id);
1041
1042
1043
1044
1045 cursor = resolver.query (treeUri, projection,
1046 (Document.COLUMN_DISPLAY_NAME
1047 + " = ?"),
1048 new String[] { component, },
1049 null, signal);
1050
1051 if (cursor == null)
1052 return -1;
1053
1054
1055
1056
1057 nameColumn
1058 = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME);
1059 idColumn
1060 = cursor.getColumnIndex (Document.COLUMN_DOCUMENT_ID);
1061 typeColumn
1062 = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
1063
1064 if (nameColumn < 0 || idColumn < 0 || typeColumn < 0)
1065 return -1;
1066
1067 next = null;
1068
1069 while (true)
1070 {
1071
1072
1073
1074
1075 if (!cursor.moveToNext ())
1076 {
1077
1078
1079
1080 if (next != null)
1081 break;
1082
1083
1084
1085 if ((type == null
1086 || type.equals (Document.MIME_TYPE_DIR))
1087
1088
1089 && component == components[components.length - 1])
1090 {
1091
1092
1093
1094
1095
1096 id_return[0] = id;
1097
1098
1099
1100
1101 if (id == null)
1102 return -1;
1103
1104 return -2;
1105 }
1106
1107
1108
1109 return -1;
1110 }
1111
1112
1113
1114
1115 name = cursor.getString (nameColumn);
1116 newId = cursor.getString (idColumn);
1117 newType = cursor.getString (typeColumn);
1118
1119
1120
1121
1122
1123 if (name == null || newId == null || newType == null)
1124 return -1;
1125
1126
1127
1128
1129 cache = cacheChild (toplevel, children, name,
1130 newId, newType,
1131 idEntry != null);
1132
1133
1134
1135
1136
1137 if (name.equals (component))
1138 {
1139 id = newId;
1140 next = cache.children;
1141 type = newType;
1142 }
1143 }
1144
1145 children = next;
1146
1147
1148 cursor.close ();
1149 cursor = null;
1150
1151
1152
1153 if (id == null)
1154 return -1;
1155 }
1156 }
1157 finally
1158 {
1159
1160
1161
1162
1163 if (cursor != null)
1164 cursor.close ();
1165 }
1166
1167
1168
1169
1170
1171
1172
1173 if (id == null)
1174 id_return[0] = DocumentsContract.getTreeDocumentId (uri);
1175 else
1176 id_return[0] = id;
1177
1178
1179 if (type == null || type.equals (Document.MIME_TYPE_DIR))
1180 return 1;
1181
1182 return 0;
1183 }
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204 public int
1205 documentIdFromName (final String tree_uri, final String name,
1206 final String[] id_return)
1207 {
1208 return runIntFunction (new SafIntFunction () {
1209 @Override
1210 public int
1211 runInt (CancellationSignal signal)
1212 {
1213 return documentIdFromName1 (tree_uri, name, id_return,
1214 signal);
1215 }
1216 });
1217 }
1218
1219
1220
1221
1222 private long[]
1223 statDocument1 (String uri, String documentId,
1224 CancellationSignal signal, boolean noCache)
1225 {
1226 Uri uriObject, tree;
1227 String[] projection;
1228 long[] stat;
1229 Cursor cursor;
1230 CacheToplevel toplevel;
1231 StatCacheEntry cache;
1232
1233 tree = Uri.parse (uri);
1234
1235 if (documentId == null)
1236 documentId = DocumentsContract.getTreeDocumentId (tree);
1237
1238
1239
1240
1241 uriObject
1242 = DocumentsContract.buildDocumentUriUsingTree (tree, documentId);
1243
1244
1245
1246
1247 toplevel = getCache (tree);
1248 cache = toplevel.statCache.get (documentId);
1249
1250 if (cache == null || !cache.isValid ())
1251 {
1252
1253
1254
1255 projection = new String[] {
1256 Document.COLUMN_FLAGS,
1257 Document.COLUMN_LAST_MODIFIED,
1258 Document.COLUMN_MIME_TYPE,
1259 Document.COLUMN_SIZE,
1260 };
1261
1262 cursor = resolver.query (uriObject, projection, null,
1263 null, null, signal);
1264
1265 if (cursor == null)
1266 return null;
1267
1268 try
1269 {
1270 if (!cursor.moveToFirst ())
1271 return null;
1272
1273 cache = cacheFileStatus (documentId, toplevel, cursor,
1274 noCache);
1275 }
1276 finally
1277 {
1278 cursor.close ();
1279 }
1280
1281
1282
1283 if (cache == null)
1284 return null;
1285 }
1286
1287
1288
1289 stat = new long[3];
1290
1291 stat[0] |= S_IRUSR;
1292 if ((cache.flags & Document.FLAG_SUPPORTS_WRITE) != 0)
1293 stat[0] |= S_IWUSR;
1294
1295 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
1296 && (cache.flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0)
1297 stat[0] |= S_IFCHR;
1298
1299 stat[1] = cache.size;
1300
1301
1302 if (cache.isDirectory
1303
1304
1305
1306 && (stat[0] & S_IFCHR) == 0)
1307 {
1308
1309
1310 stat[0] |= S_IFDIR | S_IWUSR | S_IXUSR;
1311
1312
1313
1314
1315 if ((cache.flags & Document.FLAG_DIR_SUPPORTS_CREATE) == 0)
1316 stat[0] &= ~S_IWUSR;
1317 }
1318
1319
1320
1321
1322 if ((stat[0] & (S_IFDIR | S_IFCHR)) == 0)
1323 stat[0] |= S_IFREG;
1324
1325 stat[2] = cache.mtime;
1326 return stat;
1327 }
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346 public long[]
1347 statDocument (final String uri, final String documentId,
1348 final boolean noCache)
1349 {
1350 return (long[]) runObjectFunction (new SafObjectFunction () {
1351 @Override
1352 public Object
1353 runObject (CancellationSignal signal)
1354 {
1355 return statDocument1 (uri, documentId, signal, noCache);
1356 }
1357 });
1358 }
1359
1360
1361
1362
1363 private int
1364 accessDocument1 (String uri, String documentId, boolean writable,
1365 CancellationSignal signal)
1366 {
1367 Uri uriObject;
1368 String[] projection;
1369 int tem, index;
1370 String tem1;
1371 Cursor cursor;
1372 CacheToplevel toplevel;
1373 CacheEntry entry;
1374
1375 uriObject = Uri.parse (uri);
1376
1377 if (documentId == null)
1378 documentId = DocumentsContract.getTreeDocumentId (uriObject);
1379
1380
1381
1382
1383
1384 if (!writable)
1385 {
1386 toplevel = getCache (uriObject);
1387 entry = toplevel.idCache.get (documentId);
1388
1389 if (entry != null)
1390 return 0;
1391 }
1392
1393
1394
1395
1396 uriObject
1397 = DocumentsContract.buildDocumentUriUsingTree (uriObject, documentId);
1398
1399
1400
1401 projection = new String[] {
1402 Document.COLUMN_FLAGS,
1403 Document.COLUMN_MIME_TYPE,
1404 };
1405
1406 cursor = resolver.query (uriObject, projection, null,
1407 null, null, signal);
1408
1409 if (cursor == null)
1410 return -1;
1411
1412 try
1413 {
1414 if (!cursor.moveToFirst ())
1415 return -1;
1416
1417 if (!writable)
1418 return 0;
1419
1420 index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
1421 if (index < 0)
1422 return -3;
1423
1424
1425 tem1 = cursor.getString (index);
1426
1427
1428 if (tem1.equals (Document.MIME_TYPE_DIR))
1429 {
1430
1431
1432
1433 if (!writable)
1434 return 0;
1435
1436 index = cursor.getColumnIndex (Document.COLUMN_FLAGS);
1437 if (index < 0)
1438 return -3;
1439
1440 tem = cursor.getInt (index);
1441 if ((tem & Document.FLAG_DIR_SUPPORTS_CREATE) == 0)
1442 return -3;
1443
1444 return 0;
1445 }
1446
1447 index = cursor.getColumnIndex (Document.COLUMN_FLAGS);
1448 if (index < 0)
1449 return -3;
1450
1451 tem = cursor.getInt (index);
1452 if (writable && (tem & Document.FLAG_SUPPORTS_WRITE) == 0)
1453 return -3;
1454 }
1455 finally
1456 {
1457
1458 cursor.close ();
1459 }
1460
1461 return 0;
1462 }
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483 public int
1484 accessDocument (final String uri, final String documentId,
1485 final boolean writable)
1486 {
1487 return runIntFunction (new SafIntFunction () {
1488 @Override
1489 public int
1490 runInt (CancellationSignal signal)
1491 {
1492 return accessDocument1 (uri, documentId, writable,
1493 signal);
1494 }
1495 });
1496 }
1497
1498
1499
1500
1501 private Cursor
1502 openDocumentDirectory1 (String uri, String documentId,
1503 CancellationSignal signal)
1504 {
1505 Uri uriObject, tree;
1506 Cursor cursor;
1507 String projection[];
1508 CacheToplevel toplevel;
1509
1510 tree = uriObject = Uri.parse (uri);
1511
1512
1513
1514
1515 if (documentId == null)
1516 documentId = DocumentsContract.getTreeDocumentId (uriObject);
1517
1518
1519
1520
1521 uriObject
1522 = DocumentsContract.buildChildDocumentsUriUsingTree (uriObject,
1523 documentId);
1524
1525 projection = new String [] {
1526 Document.COLUMN_DISPLAY_NAME,
1527 Document.COLUMN_DOCUMENT_ID,
1528 Document.COLUMN_MIME_TYPE,
1529 Document.COLUMN_FLAGS,
1530 Document.COLUMN_LAST_MODIFIED,
1531 Document.COLUMN_SIZE,
1532 };
1533
1534 cursor = resolver.query (uriObject, projection, null, null,
1535 null, signal);
1536
1537
1538
1539 if (cursor != null)
1540 {
1541 toplevel = getCache (tree);
1542 cacheDirectoryFromCursor (toplevel, documentId,
1543 cursor);
1544 }
1545
1546
1547 return cursor;
1548 }
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560 public Cursor
1561 openDocumentDirectory (final String uri, final String documentId)
1562 {
1563 return (Cursor) runObjectFunction (new SafObjectFunction () {
1564 @Override
1565 public Object
1566 runObject (CancellationSignal signal)
1567 {
1568 return openDocumentDirectory1 (uri, documentId, signal);
1569 }
1570 });
1571 }
1572
1573
1574
1575
1576 public ParcelFileDescriptor
1577 openDocument1 (String uri, String documentId, boolean read,
1578 boolean write, boolean truncate,
1579 CancellationSignal signal)
1580 throws Throwable
1581 {
1582 Uri treeUri, documentUri;
1583 String mode;
1584 ParcelFileDescriptor fileDescriptor;
1585 CacheToplevel toplevel;
1586
1587 treeUri = Uri.parse (uri);
1588
1589
1590
1591
1592 documentUri
1593 = DocumentsContract.buildDocumentUriUsingTree (treeUri, documentId);
1594
1595
1596
1597 if (write)
1598 {
1599 if (read)
1600 {
1601 if (truncate)
1602 mode = "rwt";
1603 else
1604 mode = "rw";
1605 }
1606 else
1607
1608
1609
1610
1611 mode = "w";
1612 }
1613 else
1614 mode = "r";
1615
1616 fileDescriptor
1617 = resolver.openFileDescriptor (documentUri, mode,
1618 signal);
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634 if (read && write && truncate && fileDescriptor != null
1635 && !EmacsNative.ftruncate (fileDescriptor.getFd ()))
1636 {
1637 try
1638 {
1639 fileDescriptor.closeWithError ("File descriptor requested"
1640 + " is not writable");
1641 }
1642 catch (IOException e)
1643 {
1644 Log.w (TAG, "Leaking unclosed file descriptor " + e);
1645 }
1646
1647 fileDescriptor
1648 = resolver.openFileDescriptor (documentUri, "rw", signal);
1649
1650
1651
1652 if (fileDescriptor != null)
1653 EmacsNative.ftruncate (fileDescriptor.getFd ());
1654 }
1655 else if (!read && write && truncate && fileDescriptor != null)
1656
1657
1658
1659
1660 EmacsNative.ftruncate (fileDescriptor.getFd ());
1661
1662
1663
1664 toplevel = getCache (treeUri);
1665 toplevel.statCache.remove (documentId);
1666
1667 return fileDescriptor;
1668 }
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691 public ParcelFileDescriptor
1692 openDocument (final String uri, final String documentId,
1693 final boolean read, final boolean write,
1694 final boolean truncate)
1695 {
1696 Object tem;
1697
1698 tem = runObjectFunction (new SafObjectFunction () {
1699 @Override
1700 public Object
1701 runObject (CancellationSignal signal)
1702 throws Throwable
1703 {
1704 return openDocument1 (uri, documentId, read,
1705 write, truncate, signal);
1706 }
1707 });
1708
1709 return (ParcelFileDescriptor) tem;
1710 }
1711 };