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 private StatCacheEntry
443 cacheFileStatus (String documentId, CacheToplevel toplevel,
444 Cursor cursor)
445 {
446 StatCacheEntry entry;
447 int flagsIndex, columnIndex, typeIndex;
448 int sizeIndex, mtimeIndex;
449 String type;
450
451
452 flagsIndex = cursor.getColumnIndex (Document.COLUMN_FLAGS);
453 sizeIndex = cursor.getColumnIndex (Document.COLUMN_SIZE);
454 typeIndex = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
455 mtimeIndex = cursor.getColumnIndex (Document.COLUMN_LAST_MODIFIED);
456
457
458
459 if (flagsIndex < 0 || sizeIndex < 0 || typeIndex < 0)
460 return null;
461
462
463 entry = new StatCacheEntry ();
464 entry.flags = cursor.getInt (flagsIndex);
465 type = cursor.getString (typeIndex);
466
467 if (type == null)
468 return null;
469
470 entry.isDirectory = type.equals (Document.MIME_TYPE_DIR);
471
472 if (cursor.isNull (sizeIndex))
473
474 entry.size = -1;
475 else
476 entry.size = cursor.getLong (sizeIndex);
477
478
479
480
481 if (mtimeIndex >= 0 && !cursor.isNull (mtimeIndex))
482 entry.mtime = cursor.getLong (mtimeIndex);
483
484
485 toplevel.statCache.put (documentId, entry);
486 return entry;
487 }
488
489
490
491
492
493
494
495
496
497
498
499 private void
500 cacheDirectoryFromCursor (CacheToplevel toplevel, String documentId,
501 Cursor cursor)
502 {
503 CacheEntry entry, constitutent;
504 int nameColumn, idColumn, typeColumn;
505 String id, name, type;
506 DocIdEntry idEntry;
507
508
509
510 nameColumn
511 = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME);
512 idColumn
513 = cursor.getColumnIndex (Document.COLUMN_DOCUMENT_ID);
514 typeColumn
515 = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
516
517 if (nameColumn < 0 || idColumn < 0 || typeColumn < 0)
518 return;
519
520 entry = new CacheEntry ();
521
522
523 entry.type = Document.MIME_TYPE_DIR;
524 toplevel.idCache.put (documentId, entry);
525
526
527
528 while (cursor.moveToNext ())
529 {
530 try
531 {
532 name = cursor.getString (nameColumn);
533 id = cursor.getString (idColumn);
534 type = cursor.getString (typeColumn);
535
536 if (name == null || id == null || type == null)
537 continue;
538
539
540
541 idEntry = new DocIdEntry ();
542 idEntry.documentId = id;
543 entry.children.put (id, idEntry);
544
545
546
547
548
549 cacheFileStatus (id, toplevel, cursor);
550
551
552
553
554
555 if (type.equals (Document.MIME_TYPE_DIR))
556 continue;
557
558
559
560 constitutent = new CacheEntry ();
561 constitutent.type = type;
562 toplevel.idCache.put (documentId, entry);
563 }
564 catch (Exception e)
565 {
566 e.printStackTrace ();
567 continue;
568 }
569 }
570
571
572 cursor.moveToPosition (-1);
573 }
574
575
576
577
578 private void
579 postPruneMessage ()
580 {
581 handler.postDelayed (new Runnable () {
582 @Override
583 public void
584 run ()
585 {
586 pruneCache ();
587 }
588 }, CACHE_PRUNE_TIME * 1000);
589 }
590
591
592
593
594
595
596
597
598 public void
599 postInvalidateCache (final Uri uri, final String documentId,
600 final String cacheName)
601 {
602 handler.post (new Runnable () {
603 @Override
604 public void
605 run ()
606 {
607 CacheToplevel toplevel;
608 HashMap<String, DocIdEntry> children;
609 String[] components;
610 CacheEntry entry;
611 DocIdEntry idEntry;
612
613 toplevel = getCache (uri);
614 toplevel.idCache.remove (documentId);
615 toplevel.statCache.remove (documentId);
616
617
618
619 children = toplevel.children;
620 components = cacheName.split ("/");
621
622 for (String component : components)
623 {
624
625
626 if (component.isEmpty ())
627 continue;
628
629 if (component == components[components.length - 1])
630 {
631
632
633 children.remove (component);
634 return;
635 }
636 else
637 {
638
639
640
641 idEntry = children.get (component);
642
643 if (idEntry == null)
644
645 return;
646
647 entry = toplevel.idCache.get (idEntry.documentId);
648
649 if (entry == null)
650
651 return;
652
653
654
655 children = entry.children;
656 }
657 }
658 }
659 });
660 }
661
662
663
664
665
666
667
668
669 public void
670 postInvalidateCacheDir (final Uri uri, final String documentId,
671 final String cacheName)
672 {
673 handler.post (new Runnable () {
674 @Override
675 public void
676 run ()
677 {
678 CacheToplevel toplevel;
679 HashMap<String, DocIdEntry> children;
680 String[] components;
681 CacheEntry entry;
682 DocIdEntry idEntry;
683 Iterator<DocIdEntry> iter;
684
685 toplevel = getCache (uri);
686 toplevel.idCache.remove (documentId);
687 toplevel.statCache.remove (documentId);
688
689
690
691
692 children = toplevel.children;
693 components = cacheName.split ("/");
694
695 for (String component : components)
696 {
697
698
699 if (component.isEmpty ())
700 continue;
701
702
703
704
705 idEntry = children.get (component);
706
707 if (idEntry == null)
708
709 return;
710
711 entry = toplevel.idCache.get (idEntry.documentId);
712
713 if (entry == null)
714
715 return;
716
717
718
719 children = entry.children;
720 }
721
722 iter = children.values ().iterator ();
723 while (iter.hasNext ())
724 {
725 idEntry = iter.next ();
726
727 if (idEntry.documentId.equals (documentId))
728 {
729 iter.remove ();
730 break;
731 }
732 }
733 }
734 });
735 }
736
737
738
739
740
741
742 public void
743 postInvalidateStat (final Uri uri, final String documentId)
744 {
745 handler.post (new Runnable () {
746 @Override
747 public void
748 run ()
749 {
750 CacheToplevel toplevel;
751
752 toplevel = getCache (uri);
753 toplevel.statCache.remove (documentId);
754 }
755 });
756 }
757
758
759
760
761
762
763
764 private abstract class SafIntFunction
765 {
766
767
768
769
770
771
772
773
774
775 public abstract int runInt (CancellationSignal signal)
776 throws Throwable;
777 };
778
779 private abstract class SafObjectFunction
780 {
781
782
783
784
785
786
787
788
789
790 public abstract Object runObject (CancellationSignal signal)
791 throws Throwable;
792 };
793
794
795
796
797
798
799
800
801
802
803
804
805
806 @SuppressWarnings("unchecked")
807 private static <T extends Throwable> void
808 throwException (Throwable exception)
809 throws T
810 {
811 throw (T) exception;
812 }
813
814
815
816
817
818
819
820
821
822
823 private int
824 runIntFunction (final SafIntFunction function)
825 {
826 final EmacsHolder<Object> result;
827 final CancellationSignal signal;
828 Throwable throwable;
829
830 result = new EmacsHolder<Object> ();
831 signal = new CancellationSignal ();
832
833 handler.post (new Runnable () {
834 @Override
835 public void
836 run ()
837 {
838 try
839 {
840 result.thing
841 = Integer.valueOf (function.runInt (signal));
842 }
843 catch (Throwable throwable)
844 {
845 result.thing = throwable;
846 }
847
848 EmacsNative.safPostRequest ();
849 }
850 });
851
852 if (EmacsNative.safSyncAndReadInput () != 0)
853 {
854 signal.cancel ();
855
856
857
858
859
860
861 EmacsNative.safSync ();
862 }
863
864 if (result.thing instanceof Throwable)
865 {
866 throwable = (Throwable) result.thing;
867 EmacsSafThread.<RuntimeException>throwException (throwable);
868 }
869
870 return (Integer) result.thing;
871 }
872
873
874
875
876
877
878
879
880
881
882 private Object
883 runObjectFunction (final SafObjectFunction function)
884 {
885 final EmacsHolder<Object> result;
886 final CancellationSignal signal;
887 Throwable throwable;
888
889 result = new EmacsHolder<Object> ();
890 signal = new CancellationSignal ();
891
892 handler.post (new Runnable () {
893 @Override
894 public void
895 run ()
896 {
897 try
898 {
899 result.thing = function.runObject (signal);
900 }
901 catch (Throwable throwable)
902 {
903 result.thing = throwable;
904 }
905
906 EmacsNative.safPostRequest ();
907 }
908 });
909
910 if (EmacsNative.safSyncAndReadInput () != 0)
911 {
912 signal.cancel ();
913
914
915
916
917
918
919 EmacsNative.safSync ();
920 }
921
922 if (result.thing instanceof Throwable)
923 {
924 throwable = (Throwable) result.thing;
925 EmacsSafThread.<RuntimeException>throwException (throwable);
926 }
927
928 return result.thing;
929 }
930
931
932
933
934 private int
935 documentIdFromName1 (String tree_uri, String name,
936 String[] id_return, CancellationSignal signal)
937 {
938 Uri uri, treeUri;
939 String id, type, newId, newType;
940 String[] components, projection;
941 Cursor cursor;
942 int nameColumn, idColumn, typeColumn;
943 CacheToplevel toplevel;
944 DocIdEntry idEntry;
945 HashMap<String, DocIdEntry> children, next;
946 CacheEntry cache;
947
948 projection = new String[] {
949 Document.COLUMN_DISPLAY_NAME,
950 Document.COLUMN_DOCUMENT_ID,
951 Document.COLUMN_MIME_TYPE,
952 };
953
954
955 uri = Uri.parse (tree_uri);
956
957
958 components = name.split ("/");
959
960
961 type = id = null;
962 cursor = null;
963
964
965 toplevel = getCache (uri);
966
967
968 children = toplevel.children;
969
970
971
972 try
973 {
974 for (String component : components)
975 {
976
977
978 if (component.isEmpty ())
979 continue;
980
981
982
983
984 idEntry = children.get (component);
985
986 if (idEntry != null)
987 {
988
989
990
991 cache = toplevel.idCache.get (idEntry.documentId);
992
993
994
995 if (cache == null)
996 cache = idEntry.getCacheEntry (resolver, uri, toplevel,
997 signal);
998
999 if (cache == null)
1000 {
1001
1002
1003
1004
1005 children.remove (component);
1006
1007 if (id == null)
1008 id = DocumentsContract.getTreeDocumentId (uri);
1009
1010 id_return[0] = id;
1011
1012 if ((type == null
1013 || type.equals (Document.MIME_TYPE_DIR))
1014
1015
1016 && component == components[components.length - 1])
1017 return -2;
1018
1019 return -1;
1020 }
1021
1022
1023 id = idEntry.documentId;
1024 type = cache.type;
1025 children = cache.children;
1026 continue;
1027 }
1028
1029
1030
1031
1032 if (id == null)
1033 id = DocumentsContract.getTreeDocumentId (uri);
1034
1035 treeUri
1036 = DocumentsContract.buildChildDocumentsUriUsingTree (uri, id);
1037
1038
1039
1040
1041 cursor = resolver.query (treeUri, projection,
1042 (Document.COLUMN_DISPLAY_NAME
1043 + " = ?"),
1044 new String[] { component, },
1045 null, signal);
1046
1047 if (cursor == null)
1048 return -1;
1049
1050
1051
1052
1053 nameColumn
1054 = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME);
1055 idColumn
1056 = cursor.getColumnIndex (Document.COLUMN_DOCUMENT_ID);
1057 typeColumn
1058 = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
1059
1060 if (nameColumn < 0 || idColumn < 0 || typeColumn < 0)
1061 return -1;
1062
1063 next = null;
1064
1065 while (true)
1066 {
1067
1068
1069
1070
1071 if (!cursor.moveToNext ())
1072 {
1073
1074
1075
1076 if (next != null)
1077 break;
1078
1079
1080
1081 if ((type == null
1082 || type.equals (Document.MIME_TYPE_DIR))
1083
1084
1085 && component == components[components.length - 1])
1086 {
1087
1088
1089
1090
1091
1092 id_return[0] = id;
1093
1094
1095
1096
1097 if (id == null)
1098 return -1;
1099
1100 return -2;
1101 }
1102
1103
1104
1105 return -1;
1106 }
1107
1108
1109
1110
1111 name = cursor.getString (nameColumn);
1112 newId = cursor.getString (idColumn);
1113 newType = cursor.getString (typeColumn);
1114
1115
1116
1117
1118
1119 if (name == null || newId == null || newType == null)
1120 return -1;
1121
1122
1123
1124
1125 cache = cacheChild (toplevel, children, name,
1126 newId, newType,
1127 idEntry != null);
1128
1129
1130
1131
1132
1133 if (name.equals (component))
1134 {
1135 id = newId;
1136 next = cache.children;
1137 type = newType;
1138 }
1139 }
1140
1141 children = next;
1142
1143
1144 cursor.close ();
1145 cursor = null;
1146
1147
1148
1149 if (id == null)
1150 return -1;
1151 }
1152 }
1153 finally
1154 {
1155
1156
1157
1158
1159 if (cursor != null)
1160 cursor.close ();
1161 }
1162
1163
1164
1165
1166
1167
1168
1169 if (id == null)
1170 id_return[0] = DocumentsContract.getTreeDocumentId (uri);
1171 else
1172 id_return[0] = id;
1173
1174
1175 if (type == null || type.equals (Document.MIME_TYPE_DIR))
1176 return 1;
1177
1178 return 0;
1179 }
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200 public int
1201 documentIdFromName (final String tree_uri, final String name,
1202 final String[] id_return)
1203 {
1204 return runIntFunction (new SafIntFunction () {
1205 @Override
1206 public int
1207 runInt (CancellationSignal signal)
1208 {
1209 return documentIdFromName1 (tree_uri, name, id_return,
1210 signal);
1211 }
1212 });
1213 }
1214
1215
1216
1217
1218 private long[]
1219 statDocument1 (String uri, String documentId,
1220 CancellationSignal signal)
1221 {
1222 Uri uriObject, tree;
1223 String[] projection;
1224 long[] stat;
1225 Cursor cursor;
1226 CacheToplevel toplevel;
1227 StatCacheEntry cache;
1228
1229 tree = Uri.parse (uri);
1230
1231 if (documentId == null)
1232 documentId = DocumentsContract.getTreeDocumentId (tree);
1233
1234
1235
1236
1237 uriObject
1238 = DocumentsContract.buildDocumentUriUsingTree (tree, documentId);
1239
1240
1241
1242
1243 toplevel = getCache (tree);
1244 cache = toplevel.statCache.get (documentId);
1245
1246 if (cache == null || !cache.isValid ())
1247 {
1248
1249
1250
1251 projection = new String[] {
1252 Document.COLUMN_FLAGS,
1253 Document.COLUMN_LAST_MODIFIED,
1254 Document.COLUMN_MIME_TYPE,
1255 Document.COLUMN_SIZE,
1256 };
1257
1258 cursor = resolver.query (uriObject, projection, null,
1259 null, null, signal);
1260
1261 if (cursor == null)
1262 return null;
1263
1264 try
1265 {
1266 if (!cursor.moveToFirst ())
1267 return null;
1268
1269 cache = cacheFileStatus (documentId, toplevel, cursor);
1270 }
1271 finally
1272 {
1273 cursor.close ();
1274 }
1275
1276
1277
1278 if (cache == null)
1279 return null;
1280 }
1281
1282
1283
1284 stat = new long[3];
1285
1286 stat[0] |= S_IRUSR;
1287 if ((cache.flags & Document.FLAG_SUPPORTS_WRITE) != 0)
1288 stat[0] |= S_IWUSR;
1289
1290 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
1291 && (cache.flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0)
1292 stat[0] |= S_IFCHR;
1293
1294 stat[1] = cache.size;
1295
1296
1297 if (cache.isDirectory
1298
1299
1300
1301 && (stat[0] & S_IFCHR) == 0)
1302 {
1303
1304
1305 stat[0] |= S_IFDIR | S_IWUSR | S_IXUSR;
1306
1307
1308
1309
1310 if ((cache.flags & Document.FLAG_DIR_SUPPORTS_CREATE) == 0)
1311 stat[0] &= ~S_IWUSR;
1312 }
1313
1314
1315
1316
1317 if ((stat[0] & (S_IFDIR | S_IFCHR)) == 0)
1318 stat[0] |= S_IFREG;
1319
1320 stat[2] = cache.mtime;
1321 return stat;
1322 }
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338 public long[]
1339 statDocument (final String uri, final String documentId)
1340 {
1341 return (long[]) runObjectFunction (new SafObjectFunction () {
1342 @Override
1343 public Object
1344 runObject (CancellationSignal signal)
1345 {
1346 return statDocument1 (uri, documentId, signal);
1347 }
1348 });
1349 }
1350
1351
1352
1353
1354 private int
1355 accessDocument1 (String uri, String documentId, boolean writable,
1356 CancellationSignal signal)
1357 {
1358 Uri uriObject;
1359 String[] projection;
1360 int tem, index;
1361 String tem1;
1362 Cursor cursor;
1363 CacheToplevel toplevel;
1364 CacheEntry entry;
1365
1366 uriObject = Uri.parse (uri);
1367
1368 if (documentId == null)
1369 documentId = DocumentsContract.getTreeDocumentId (uriObject);
1370
1371
1372
1373
1374
1375 if (!writable)
1376 {
1377 toplevel = getCache (uriObject);
1378 entry = toplevel.idCache.get (documentId);
1379
1380 if (entry != null)
1381 return 0;
1382 }
1383
1384
1385
1386
1387 uriObject
1388 = DocumentsContract.buildDocumentUriUsingTree (uriObject, documentId);
1389
1390
1391
1392 projection = new String[] {
1393 Document.COLUMN_FLAGS,
1394 Document.COLUMN_MIME_TYPE,
1395 };
1396
1397 cursor = resolver.query (uriObject, projection, null,
1398 null, null, signal);
1399
1400 if (cursor == null)
1401 return -1;
1402
1403 try
1404 {
1405 if (!cursor.moveToFirst ())
1406 return -1;
1407
1408 if (!writable)
1409 return 0;
1410
1411 index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE);
1412 if (index < 0)
1413 return -3;
1414
1415
1416 tem1 = cursor.getString (index);
1417
1418
1419 if (tem1.equals (Document.MIME_TYPE_DIR))
1420 {
1421
1422
1423
1424 if (!writable)
1425 return 0;
1426
1427 index = cursor.getColumnIndex (Document.COLUMN_FLAGS);
1428 if (index < 0)
1429 return -3;
1430
1431 tem = cursor.getInt (index);
1432 if ((tem & Document.FLAG_DIR_SUPPORTS_CREATE) == 0)
1433 return -3;
1434
1435 return 0;
1436 }
1437
1438 index = cursor.getColumnIndex (Document.COLUMN_FLAGS);
1439 if (index < 0)
1440 return -3;
1441
1442 tem = cursor.getInt (index);
1443 if (writable && (tem & Document.FLAG_SUPPORTS_WRITE) == 0)
1444 return -3;
1445 }
1446 finally
1447 {
1448
1449 cursor.close ();
1450 }
1451
1452 return 0;
1453 }
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474 public int
1475 accessDocument (final String uri, final String documentId,
1476 final boolean writable)
1477 {
1478 return runIntFunction (new SafIntFunction () {
1479 @Override
1480 public int
1481 runInt (CancellationSignal signal)
1482 {
1483 return accessDocument1 (uri, documentId, writable,
1484 signal);
1485 }
1486 });
1487 }
1488
1489
1490
1491
1492 private Cursor
1493 openDocumentDirectory1 (String uri, String documentId,
1494 CancellationSignal signal)
1495 {
1496 Uri uriObject, tree;
1497 Cursor cursor;
1498 String projection[];
1499 CacheToplevel toplevel;
1500
1501 tree = uriObject = Uri.parse (uri);
1502
1503
1504
1505
1506 if (documentId == null)
1507 documentId = DocumentsContract.getTreeDocumentId (uriObject);
1508
1509
1510
1511
1512 uriObject
1513 = DocumentsContract.buildChildDocumentsUriUsingTree (uriObject,
1514 documentId);
1515
1516 projection = new String [] {
1517 Document.COLUMN_DISPLAY_NAME,
1518 Document.COLUMN_DOCUMENT_ID,
1519 Document.COLUMN_MIME_TYPE,
1520 Document.COLUMN_FLAGS,
1521 Document.COLUMN_LAST_MODIFIED,
1522 Document.COLUMN_SIZE,
1523 };
1524
1525 cursor = resolver.query (uriObject, projection, null, null,
1526 null, signal);
1527
1528
1529
1530 if (cursor != null)
1531 {
1532 toplevel = getCache (tree);
1533 cacheDirectoryFromCursor (toplevel, documentId,
1534 cursor);
1535 }
1536
1537
1538 return cursor;
1539 }
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551 public Cursor
1552 openDocumentDirectory (final String uri, final String documentId)
1553 {
1554 return (Cursor) runObjectFunction (new SafObjectFunction () {
1555 @Override
1556 public Object
1557 runObject (CancellationSignal signal)
1558 {
1559 return openDocumentDirectory1 (uri, documentId, signal);
1560 }
1561 });
1562 }
1563
1564
1565
1566
1567 public ParcelFileDescriptor
1568 openDocument1 (String uri, String documentId, boolean write,
1569 boolean truncate, CancellationSignal signal)
1570 throws Throwable
1571 {
1572 Uri treeUri, documentUri;
1573 String mode;
1574 ParcelFileDescriptor fileDescriptor;
1575 CacheToplevel toplevel;
1576
1577 treeUri = Uri.parse (uri);
1578
1579
1580
1581
1582 documentUri
1583 = DocumentsContract.buildDocumentUriUsingTree (treeUri, documentId);
1584
1585
1586
1587 if (write)
1588 {
1589 if (truncate)
1590 mode = "rwt";
1591 else
1592 mode = "rw";
1593 }
1594 else
1595 mode = "r";
1596
1597 fileDescriptor
1598 = resolver.openFileDescriptor (documentUri, mode,
1599 signal);
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615 if (write && truncate && fileDescriptor != null
1616 && !EmacsNative.ftruncate (fileDescriptor.getFd ()))
1617 {
1618 try
1619 {
1620 fileDescriptor.closeWithError ("File descriptor requested"
1621 + " is not writable");
1622 }
1623 catch (IOException e)
1624 {
1625 Log.w (TAG, "Leaking unclosed file descriptor " + e);
1626 }
1627
1628 fileDescriptor
1629 = resolver.openFileDescriptor (documentUri, "rw", signal);
1630
1631
1632
1633 if (fileDescriptor != null)
1634 EmacsNative.ftruncate (fileDescriptor.getFd ());
1635 }
1636
1637
1638
1639 toplevel = getCache (treeUri);
1640 toplevel.statCache.remove (documentId);
1641
1642 return fileDescriptor;
1643 }
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668 public ParcelFileDescriptor
1669 openDocument (final String uri, final String documentId,
1670 final boolean write, final boolean truncate)
1671 {
1672 Object tem;
1673
1674 tem = runObjectFunction (new SafObjectFunction () {
1675 @Override
1676 public Object
1677 runObject (CancellationSignal signal)
1678 throws Throwable
1679 {
1680 return openDocument1 (uri, documentId, write, truncate,
1681 signal);
1682 }
1683 });
1684
1685 return (ParcelFileDescriptor) tem;
1686 }
1687 };