| 1 | package codewhale.pet; |
| 2 | |
| 3 | import android.content.ContentProvider; |
| 4 | import android.content.ContentValues; |
| 5 | import android.database.Cursor; |
| 6 | import android.net.Uri; |
| 7 | import android.os.Bundle; |
| 8 | import android.os.ParcelFileDescriptor; |
| 9 | import java.io.File; |
| 10 | import java.io.FileNotFoundException; |
| 11 | import java.io.IOException; |
| 12 | import java.nio.charset.StandardCharsets; |
| 13 | import java.nio.file.Files; |
| 14 | import java.nio.file.StandardOpenOption; |
| 15 | import java.util.concurrent.atomic.AtomicInteger; |
| 16 | |
| 17 | /** Test APK only. Uses framework/Java APIs because this separate provider |
| 18 | * process does not load the target app's Kotlin or QuickJS dependencies. */ |
| 19 | public class PetLiveTestProvider extends ContentProvider { |
| 20 | private final AtomicInteger reads = new AtomicInteger(); |
| 21 | private File tape() { return new File(getContext().getCacheDir(), "synthetic-live.jsonl"); } |
| 22 | @Override public boolean onCreate() { return true; } |
| 23 | @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { |
| 24 | if (!"/tape".equals(uri.getPath()) || !"r".equals(mode)) throw new FileNotFoundException(); |
| 25 | reads.incrementAndGet(); |
| 26 | return ParcelFileDescriptor.open(tape(), ParcelFileDescriptor.MODE_READ_ONLY); |
| 27 | } |
| 28 | @Override public Bundle call(String method, String arg, Bundle extras) { |
| 29 | try { |
| 30 | switch (method) { |
| 31 | case "replace": case "append": |
| 32 | if (arg == null || arg.length() > 65_536) throw new IllegalArgumentException(); |
| 33 | if (method.equals("append")) Files.write(tape().toPath(), arg.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.APPEND); |
| 34 | else Files.write(tape().toPath(), arg.getBytes(StandardCharsets.UTF_8)); |
| 35 | break; |
| 36 | case "delete": Files.deleteIfExists(tape().toPath()); break; |
| 37 | case "stats": break; |
| 38 | default: throw new IllegalArgumentException("Unknown fixture action"); |
| 39 | } |
| 40 | } catch (IOException error) { throw new IllegalStateException(error); } |
| 41 | Bundle result = new Bundle(); result.putInt("reads", reads.get()); return result; |
| 42 | } |
| 43 | @Override public String getType(Uri uri) { return "text/plain"; } |
| 44 | @Override public Cursor query(Uri uri, String[] projection, String selection, String[] args, String sort) { return null; } |
| 45 | @Override public Uri insert(Uri uri, ContentValues values) { throw new UnsupportedOperationException(); } |
| 46 | @Override public int update(Uri uri, ContentValues values, String selection, String[] args) { throw new UnsupportedOperationException(); } |
| 47 | @Override public int delete(Uri uri, String selection, String[] args) { throw new UnsupportedOperationException(); } |
| 48 | } |
| 49 |