1a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann/* 2a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * Copyright (C) 2016 The Android Open Source Project 3a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * 4a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * Licensed under the Apache License, Version 2.0 (the "License"); 5a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * you may not use this file except in compliance with the License. 6a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * You may obtain a copy of the License at 7a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * 8a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * http://www.apache.org/licenses/LICENSE-2.0 9a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * 10a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * Unless required by applicable law or agreed to in writing, software 11a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * distributed under the License is distributed on an "AS IS" BASIS, 12a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * See the License for the specific language governing permissions and 14a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * limitations under the License. 15a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann */ 16a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 17a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannpackage com.android.packageinstaller; 18a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 19a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport android.content.Context; 20a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport android.content.Intent; 21a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport android.content.pm.PackageInstaller; 22a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport android.os.AsyncTask; 2374fa089b8c39d84b737607a3e3d2cde4d3b42d24Philip P. Moltmannimport android.support.annotation.NonNull; 2474fa089b8c39d84b737607a3e3d2cde4d3b42d24Philip P. Moltmannimport android.support.annotation.Nullable; 25a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport android.util.AtomicFile; 26a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport android.util.Log; 27a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport android.util.SparseArray; 28a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport android.util.Xml; 29a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 30a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport org.xmlpull.v1.XmlPullParser; 31fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmannimport org.xmlpull.v1.XmlPullParserException; 32fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmannimport org.xmlpull.v1.XmlSerializer; 33a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 34a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport java.io.File; 35a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport java.io.FileInputStream; 36a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport java.io.FileOutputStream; 37a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport java.io.IOException; 38a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannimport java.nio.charset.StandardCharsets; 39a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 40a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann/** 41a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * Persists results of events and calls back observers when a matching result arrives. 42a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann */ 43a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmannclass EventResultPersister { 44a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann private static final String LOG_TAG = EventResultPersister.class.getSimpleName(); 45a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 46a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** Id passed to {@link #addObserver(int, EventResultObserver)} to generate new id */ 47a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann static final int GENERATE_NEW_ID = Integer.MIN_VALUE; 48a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 49a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** 50a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * The extra with the id to set in the intent delivered to 51a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * {@link #onEventReceived(Context, Intent)} 52a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann */ 53a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann static final String EXTRA_ID = "EventResultPersister.EXTRA_ID"; 54a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 55a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** Persisted state of this object */ 56a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann private final AtomicFile mResultsFile; 57a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 58a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann private final Object mLock = new Object(); 59a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 60a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** Currently stored but not yet called back results (install id -> status, status message) */ 61a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann private final SparseArray<EventResult> mResults = new SparseArray<>(); 62a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 63a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** Currently registered, not called back observers (install id -> observer) */ 64a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann private final SparseArray<EventResultObserver> mObservers = new SparseArray<>(); 65a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 66a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** Always increasing counter for install event ids */ 67a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann private int mCounter; 68a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 69a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** If a write that will persist the state is scheduled */ 70a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann private boolean mIsPersistScheduled; 71a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 72a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** If the state was changed while the data was being persisted */ 73a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann private boolean mIsPersistingStateValid; 74a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 754f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann /** 764f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann * @return a new event id. 774f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann */ 784f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann public int getNewId() throws OutOfIdsException { 794f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann synchronized (mLock) { 804f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann if (mCounter == Integer.MAX_VALUE) { 814f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann throw new OutOfIdsException(); 824f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann } 834f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann 844f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann mCounter++; 854f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann writeState(); 864f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann 874f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann return mCounter - 1; 884f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann } 894f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann } 904f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann 91a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** Call back when a result is received. Observer is removed when onResult it called. */ 92a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann interface EventResultObserver { 93ac9155b2b20164da52cdc0d82a6ab5c3a9d7ec38Philip P. Moltmann void onResult(int status, int legacyStatus, @Nullable String message); 94a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 95a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 96a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** 97fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * Progress parser to the next element. 98fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * 99fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * @param parser The parser to progress 100fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann */ 101fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann private static void nextElement(@NonNull XmlPullParser parser) 102fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann throws XmlPullParserException, IOException { 103fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann int type; 104fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann do { 105fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann type = parser.next(); 106fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann } while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT); 107fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann } 108fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann 109fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann /** 110fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * Read an int attribute from the current element 111fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * 112fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * @param parser The parser to read from 113fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * @param name The attribute name to read 114fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * 115fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * @return The value of the attribute 116fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann */ 117fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann private static int readIntAttribute(@NonNull XmlPullParser parser, @NonNull String name) { 118fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann return Integer.parseInt(parser.getAttributeValue(null, name)); 119fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann } 120fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann 121fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann /** 122fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * Read an String attribute from the current element 123fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * 124fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * @param parser The parser to read from 125fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * @param name The attribute name to read 126fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * 127fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann * @return The value of the attribute or null if the attribute is not set 128fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann */ 129fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann private static String readStringAttribute(@NonNull XmlPullParser parser, @NonNull String name) { 130fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann return parser.getAttributeValue(null, name); 131fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann } 132fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann 133fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann /** 134a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * Read persisted state. 135a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * 136a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * @param resultFile The file the results are persisted in 137a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann */ 138a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann EventResultPersister(@NonNull File resultFile) { 139a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mResultsFile = new AtomicFile(resultFile); 140a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mCounter = GENERATE_NEW_ID + 1; 141a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 142a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann try (FileInputStream stream = mResultsFile.openRead()) { 143a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann XmlPullParser parser = Xml.newPullParser(); 144a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann parser.setInput(stream, StandardCharsets.UTF_8.name()); 145a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 146fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann nextElement(parser); 147a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { 148a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann String tagName = parser.getName(); 149a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann if ("results".equals(tagName)) { 150fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann mCounter = readIntAttribute(parser, "counter"); 151a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } else if ("result".equals(tagName)) { 152fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann int id = readIntAttribute(parser, "id"); 153fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann int status = readIntAttribute(parser, "status"); 154fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann int legacyStatus = readIntAttribute(parser, "legacyStatus"); 155fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann String statusMessage = readStringAttribute(parser, "statusMessage"); 156a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 157a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann if (mResults.get(id) != null) { 158a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann throw new Exception("id " + id + " has two results"); 159a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 160a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 161ac9155b2b20164da52cdc0d82a6ab5c3a9d7ec38Philip P. Moltmann mResults.put(id, new EventResult(status, legacyStatus, statusMessage)); 162a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } else { 163a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann throw new Exception("unexpected tag"); 164a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 165a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 166fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann nextElement(parser); 167a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 168a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } catch (Exception e) { 169a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mResults.clear(); 170a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann writeState(); 171a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 172a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 173a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 174a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** 175a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * Add a result. If the result is an pending user action, execute the pending user action 176a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * directly and do not queue a result. 177a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * 178a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * @param context The context the event was received in 179a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * @param intent The intent the activity received 180a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann */ 181a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann void onEventReceived(@NonNull Context context, @NonNull Intent intent) { 182a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0); 183a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 184a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) { 185a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT)); 186a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 187a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann return; 188a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 189a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 190a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann int id = intent.getIntExtra(EXTRA_ID, 0); 191a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann String statusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE); 192ac9155b2b20164da52cdc0d82a6ab5c3a9d7ec38Philip P. Moltmann int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 0); 193a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 194a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann EventResultObserver observerToCall = null; 195a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann synchronized (mLock) { 196a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann int numObservers = mObservers.size(); 197a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann for (int i = 0; i < numObservers; i++) { 198a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann if (mObservers.keyAt(i) == id) { 199a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann observerToCall = mObservers.valueAt(i); 200a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mObservers.removeAt(i); 201a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 202a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann break; 203a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 204a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 205a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 206a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann if (observerToCall != null) { 207ac9155b2b20164da52cdc0d82a6ab5c3a9d7ec38Philip P. Moltmann observerToCall.onResult(status, legacyStatus, statusMessage); 208a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } else { 209ac9155b2b20164da52cdc0d82a6ab5c3a9d7ec38Philip P. Moltmann mResults.put(id, new EventResult(status, legacyStatus, statusMessage)); 210a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann writeState(); 211a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 212a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 213a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 214a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 215a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** 216a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * Persist current state. The persistence might be delayed. 217a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann */ 218a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann private void writeState() { 219a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann synchronized (mLock) { 220a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mIsPersistingStateValid = false; 221a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 222a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann if (!mIsPersistScheduled) { 223a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mIsPersistScheduled = true; 224a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 225a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann AsyncTask.execute(() -> { 226a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann int counter; 227a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann SparseArray<EventResult> results; 228a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 229a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann while (true) { 230a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann // Take snapshot of state 231a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann synchronized (mLock) { 232a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann counter = mCounter; 233a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann results = mResults.clone(); 234a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mIsPersistingStateValid = true; 235a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 236a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 237a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann FileOutputStream stream = null; 238a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann try { 239a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann stream = mResultsFile.startWrite(); 240fcf8ab8aadbf907b69f28d457243e2fb830da023Philip P. Moltmann XmlSerializer serializer = Xml.newSerializer(); 241a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann serializer.setOutput(stream, StandardCharsets.UTF_8.name()); 242a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann serializer.startDocument(null, true); 243a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann serializer.setFeature( 244a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann "http://xmlpull.org/v1/doc/features.html#indent-output", true); 245a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann serializer.startTag(null, "results"); 246a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann serializer.attribute(null, "counter", Integer.toString(counter)); 247a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 248a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann int numResults = results.size(); 249a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann for (int i = 0; i < numResults; i++) { 250a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann serializer.startTag(null, "result"); 251a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann serializer.attribute(null, "id", 252a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann Integer.toString(results.keyAt(i))); 253a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann serializer.attribute(null, "status", 254a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann Integer.toString(results.valueAt(i).status)); 255ac9155b2b20164da52cdc0d82a6ab5c3a9d7ec38Philip P. Moltmann serializer.attribute(null, "legacyStatus", 256ac9155b2b20164da52cdc0d82a6ab5c3a9d7ec38Philip P. Moltmann Integer.toString(results.valueAt(i).legacyStatus)); 257a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann if (results.valueAt(i).message != null) { 258a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann serializer.attribute(null, "statusMessage", 259a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann results.valueAt(i).message); 260a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 261a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann serializer.endTag(null, "result"); 262a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 263a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 264a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann serializer.endTag(null, "results"); 265a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann serializer.endDocument(); 266a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 267a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mResultsFile.finishWrite(stream); 268a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } catch (IOException e) { 269a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann if (stream != null) { 270a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mResultsFile.failWrite(stream); 271a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 272a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 273a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann Log.e(LOG_TAG, "error writing results", e); 274a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mResultsFile.delete(); 275a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 276a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 277a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann // Check if there was changed state since we persisted. If so, we need to 278a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann // persist again. 279a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann synchronized (mLock) { 280a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann if (mIsPersistingStateValid) { 281a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mIsPersistScheduled = false; 282a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann break; 283a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 284a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 285a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 286a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann }); 287a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 288a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 289a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 290a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 291a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** 292a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * Add an observer. If there is already an event for this id, call back inside of this call. 293a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * 294a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * @param id The id the observer is for or {@code GENERATE_NEW_ID} to generate a new one. 295a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * @param observer The observer to call back. 296a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * 297a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * @return The id for this event 298a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann */ 299a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann int addObserver(int id, @NonNull EventResultObserver observer) 300a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann throws OutOfIdsException { 301a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann synchronized (mLock) { 3029e9d3a368eebb82dac197ac4c92e356fc342af75Philip P. Moltmann int resultIndex = -1; 303a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 304a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann if (id == GENERATE_NEW_ID) { 3054f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann id = getNewId(); 306a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } else { 307a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann resultIndex = mResults.indexOfKey(id); 308a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 309a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 310a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann // Check if we can instantly call back 3119e9d3a368eebb82dac197ac4c92e356fc342af75Philip P. Moltmann if (resultIndex >= 0) { 3129e9d3a368eebb82dac197ac4c92e356fc342af75Philip P. Moltmann EventResult result = mResults.valueAt(resultIndex); 3139e9d3a368eebb82dac197ac4c92e356fc342af75Philip P. Moltmann 314ac9155b2b20164da52cdc0d82a6ab5c3a9d7ec38Philip P. Moltmann observer.onResult(result.status, result.legacyStatus, result.message); 315a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mResults.removeAt(resultIndex); 3164f78e1fa55255a261247e9a112bc4433cd1a9ab0Philip P. Moltmann writeState(); 317a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } else { 318a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mObservers.put(id, observer); 319a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 320a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 321a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 322a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 323a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann return id; 324a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 325a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 326a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** 327a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * Remove a observer. 328a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * 329a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * @param id The id the observer was added for 330a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann */ 331a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann void removeObserver(int id) { 332a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann synchronized (mLock) { 333a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann mObservers.delete(id); 334a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 335a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 336a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 337a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann /** 338a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann * The status from an event. 339a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann */ 340a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann private class EventResult { 341a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann public final int status; 342ac9155b2b20164da52cdc0d82a6ab5c3a9d7ec38Philip P. Moltmann public final int legacyStatus; 343a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann @Nullable public final String message; 344a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 345ac9155b2b20164da52cdc0d82a6ab5c3a9d7ec38Philip P. Moltmann private EventResult(int status, int legacyStatus, @Nullable String message) { 346a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann this.status = status; 347ac9155b2b20164da52cdc0d82a6ab5c3a9d7ec38Philip P. Moltmann this.legacyStatus = legacyStatus; 348a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann this.message = message; 349a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 350a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann } 351a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann 352a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann class OutOfIdsException extends Exception {} 353a35df07e28e94f78fefc88d24ebfa7643b6df7e6Philip P. Moltmann} 354