| Index: third_party/cacheinvalidation/src/java/com/google/ipc/invalidation/ticl/android2/AndroidStorage.java
|
| diff --git a/third_party/cacheinvalidation/src/java/com/google/ipc/invalidation/ticl/android2/AndroidStorage.java b/third_party/cacheinvalidation/src/java/com/google/ipc/invalidation/ticl/android2/AndroidStorage.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a98ec2a6f63e135d387c5b5e32fc5053760a7f45
|
| --- /dev/null
|
| +++ b/third_party/cacheinvalidation/src/java/com/google/ipc/invalidation/ticl/android2/AndroidStorage.java
|
| @@ -0,0 +1,164 @@
|
| +/*
|
| + * Copyright 2011 Google Inc.
|
| + *
|
| + * Licensed under the Apache License, Version 2.0 (the "License");
|
| + * you may not use this file except in compliance with the License.
|
| + * You may obtain a copy of the License at
|
| + *
|
| + * http://www.apache.org/licenses/LICENSE-2.0
|
| + *
|
| + * Unless required by applicable law or agreed to in writing, software
|
| + * distributed under the License is distributed on an "AS IS" BASIS,
|
| + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| + * See the License for the specific language governing permissions and
|
| + * limitations under the License.
|
| + */
|
| +package com.google.ipc.invalidation.ticl.android2;
|
| +
|
| +import com.google.ipc.invalidation.external.client.SystemResources;
|
| +import com.google.ipc.invalidation.external.client.SystemResources.Storage;
|
| +import com.google.ipc.invalidation.external.client.types.Callback;
|
| +import com.google.ipc.invalidation.external.client.types.SimplePair;
|
| +import com.google.ipc.invalidation.external.client.types.Status;
|
| +import com.google.ipc.invalidation.ticl.InvalidationClientCore;
|
| +import com.google.ipc.invalidation.util.Preconditions;
|
| +
|
| +import android.content.Context;
|
| +
|
| +import java.io.DataInputStream;
|
| +import java.io.FileInputStream;
|
| +import java.io.FileNotFoundException;
|
| +import java.io.FileOutputStream;
|
| +import java.io.IOException;
|
| +
|
| +/**
|
| + * Implementation of {@link Storage} for the Android Ticl. This implementation supports only
|
| + * the {@link InvalidationClientCore#CLIENT_TOKEN_KEY}. As required by Android storage
|
| + * implementations, it executes all callbacks synchronously.
|
| + *
|
| + */
|
| +public class AndroidStorage implements Storage {
|
| + /** Name of the file in which state will be stored. */
|
| + private static final String STATE_FILENAME = "ticl_storage.bin";
|
| +
|
| + /** Maximum size of the file which we are willing to read. */
|
| + private static final int MAX_STATE_FILE_SIZE_BYTES = 4096;
|
| +
|
| + private final Context context;
|
| +
|
| + public AndroidStorage(Context context) {
|
| + this.context = Preconditions.checkNotNull(context);
|
| + }
|
| +
|
| + @Override
|
| + public void setSystemResources(SystemResources resources) {
|
| + }
|
| +
|
| + @Override
|
| + public void writeKey(String key, byte[] value, Callback<Status> done) {
|
| + // We only support the CLIENT_TOKEN_KEY.
|
| + if (!key.equals(InvalidationClientCore.CLIENT_TOKEN_KEY)) {
|
| + done.accept(Status.newInstance(Status.Code.PERMANENT_FAILURE, "Key unsupported: " + key));
|
| + return;
|
| + }
|
| + // Write the data.
|
| + FileOutputStream outstream = null;
|
| + Status status = null;
|
| + try {
|
| + outstream = context.openFileOutput(STATE_FILENAME, Context.MODE_PRIVATE);
|
| + outstream.write(value);
|
| + status = Status.newInstance(Status.Code.SUCCESS, "");
|
| + } catch (FileNotFoundException exception) {
|
| + status = Status.newInstance(Status.Code.PERMANENT_FAILURE, "File not found: " + exception);
|
| + } catch (IOException exception) {
|
| + status = Status.newInstance(Status.Code.PERMANENT_FAILURE, "File not found: " + exception);
|
| + } finally {
|
| + if (outstream != null) {
|
| + try {
|
| + outstream.close();
|
| + } catch (IOException exception) {
|
| + status = Status.newInstance(
|
| + Status.Code.PERMANENT_FAILURE, "Failed to close file: " + exception);
|
| + }
|
| + }
|
| + }
|
| + done.accept(status);
|
| + }
|
| +
|
| + @Override
|
| + public void readKey(String key, Callback<SimplePair<Status, byte[]>> done) {
|
| + // We only support the CLIENT_TOKEN_KEY.
|
| + if (!key.equals(InvalidationClientCore.CLIENT_TOKEN_KEY)) {
|
| + Status status = Status.newInstance(Status.Code.PERMANENT_FAILURE, "Key unsupported: " + key);
|
| + done.accept(SimplePair.of(status, (byte[]) null));
|
| + return;
|
| + }
|
| + // Read and return the data.
|
| + FileInputStream instream = null;
|
| + SimplePair<Status, byte[]> result = null;
|
| + try {
|
| + instream = context.openFileInput(STATE_FILENAME);
|
| + long fileSizeBytes = instream.getChannel().size();
|
| + if (fileSizeBytes > MAX_STATE_FILE_SIZE_BYTES) {
|
| + Status status =
|
| + Status.newInstance(Status.Code.PERMANENT_FAILURE, "File too big: " + fileSizeBytes);
|
| + result = SimplePair.of(status, (byte[]) null);
|
| + }
|
| + // Cast to int must be safe due to the above size check.
|
| + DataInputStream input = new DataInputStream(instream);
|
| + byte[] fileData = new byte[(int) fileSizeBytes];
|
| + input.readFully(fileData);
|
| + result = SimplePair.of(Status.newInstance(Status.Code.SUCCESS, ""), fileData);
|
| + } catch (FileNotFoundException exception) {
|
| + Status status =
|
| + Status.newInstance(Status.Code.PERMANENT_FAILURE, "File not found: " + exception);
|
| + result = SimplePair.of(status, (byte[]) null);
|
| + } catch (IOException exception) {
|
| + Status status =
|
| + Status.newInstance(Status.Code.TRANSIENT_FAILURE, "IO exception: " + exception);
|
| + result = SimplePair.of(status, (byte[]) null);
|
| + } finally {
|
| + if (instream != null) {
|
| + try {
|
| + instream.close();
|
| + } catch (IOException exception) {
|
| + Status status =
|
| + Status.newInstance(
|
| + Status.Code.TRANSIENT_FAILURE, "Failed to close file: " + exception);
|
| + result = SimplePair.of(status, (byte[]) null);
|
| + }
|
| + }
|
| + }
|
| + done.accept(result);
|
| + }
|
| +
|
| + @Override
|
| + public void deleteKey(String key, Callback<Boolean> done) {
|
| + // We only support the CLIENT_TOKEN_KEY.
|
| + if (!key.equals(InvalidationClientCore.CLIENT_TOKEN_KEY)) {
|
| + done.accept(false);
|
| + return;
|
| + }
|
| + if (!context.getFileStreamPath(STATE_FILENAME).exists()) {
|
| + // Deletion "succeeds" if the key didn't exist.
|
| + done.accept(true);
|
| + } else {
|
| + // Otherwise it succeeds based on whether the IO operation succeeded.
|
| + done.accept(context.deleteFile(STATE_FILENAME));
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void readAllKeys(Callback<SimplePair<Status, String>> keyCallback) {
|
| + // If the state file exists, supply the CLIENT_TOKEN_KEY as a present key.
|
| + if (context.getFileStreamPath(STATE_FILENAME).exists()) {
|
| + Status status = Status.newInstance(Status.Code.SUCCESS, "");
|
| + keyCallback.accept(SimplePair.of(status, InvalidationClientCore.CLIENT_TOKEN_KEY));
|
| + }
|
| + keyCallback.accept(null);
|
| + }
|
| +
|
| + static void deleteStateForTest(Context context) {
|
| + context.deleteFile(STATE_FILENAME);
|
| + }
|
| +}
|
|
|