content: /*
   * Copyright (C) 2009 The Android Open Source Project
   *
   * 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 android.app.backup;
  
  import android.annotation.SystemApi;
  import android.annotation.UnsupportedAppUsage;
  import android.os.ParcelFileDescriptor;
  
  import java.io.FileDescriptor;
  import java.io.IOException;
  
  /**
   * Provides the structured interface through which a {@link BackupAgent} commits
   * information to the backup data set, via its {@link
   * BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
   * onBackup()} method.  Data written for backup is presented
   * as a set of "entities," key/value pairs in which each binary data record "value" is
   * named with a string "key."
   * <p>
   * To commit a data record to the backup transport, the agent's
   * {@link BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
   * onBackup()} method first writes an "entity header" that supplies the key string for the record
   * and the total size of the binary value for the record.  After the header has been
   * written, the agent then writes the binary entity value itself.  The entity value can
   * be written in multiple chunks if desired, as long as the total count of bytes written
   * matches what was supplied to {@link #writeEntityHeader(String, int) writeEntityHeader()}.
   * <p>
   * Entity key strings are considered to be unique within a given application's backup
   * data set. If a backup agent writes a new entity under an existing key string, its value will
   * replace any previous value in the transport's remote data store.  You can remove a record
   * entirely from the remote data set by writing a new entity header using the
   * existing record's key, but supplying a negative <code>dataSize</code> parameter.
   * When you do so, the agent does not need to call {@link #writeEntityData(byte[], int)}.
   * <h3>Example</h3>
   * <p>
   * Here is an example illustrating a way to back up the value of a String variable
   * called <code>mStringToBackUp</code>:
   * <pre>
   * static final String MY_STRING_KEY = "storedstring";
   *
   * public void {@link BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)}
   *         throws IOException {
   *     ...
   *     byte[] stringBytes = mStringToBackUp.getBytes();
   *     data.writeEntityHeader(MY_STRING_KEY, stringBytes.length);
   *     data.writeEntityData(stringBytes, stringBytes.length);
   *     ...
   * }</pre>
   *
   * @see BackupAgent
   */
  public class BackupDataOutput {
  
      private final long mQuota;
      private final int mTransportFlags;
  
      @UnsupportedAppUsage
      long mBackupWriter;
  
      /**
       * Construct a BackupDataOutput purely for data-stream manipulation.  This instance will
       * not report usable quota information.
       * @hide */
      @SystemApi
      public BackupDataOutput(FileDescriptor fd) {
          this(fd, /*quota=*/ -1, /*transportFlags=*/ 0);
      }
  
      /** @hide */
      @SystemApi
      public BackupDataOutput(FileDescriptor fd, long quota) {
          this(fd, quota, /*transportFlags=*/ 0);
      }
  
      /** @hide */
      public BackupDataOutput(FileDescriptor fd, long quota, int transportFlags) {
          if (fd == null) throw new NullPointerException();
          mQuota = quota;
          mTransportFlags = transportFlags;
          mBackupWriter = ctor(fd);
          if (mBackupWriter == 0) {
              throw new RuntimeException("Native initialization failed with fd=" + fd);
          }
      }
  
      /**
       * Returns the quota in bytes for the application's current backup operation.  The
       * value can vary for each operation.
       *
       * @see FullBackupDataOutput#getQuota()
       */
      public long getQuota() {
          return mQuota;
      }
  
      /**
       * Returns flags with additional information about the backup transport. For supported flags see
       * {@link android.app.backup.BackupAgent}
       *
       * @see FullBackupDataOutput#getTransportFlags()
       */
      public int getTransportFlags() {
          return mTransportFlags;
      }
  
      /**
       * Mark the beginning of one record in the backup data stream. This must be called before
       * {@link #writeEntityData}.
       * @param key A string key that uniquely identifies the data record within the application.
       *    Keys whose first character is \uFF00 or higher are not valid.
       * @param dataSize The size in bytes of this record's data.  Passing a dataSize
       *    of -1 indicates that the record under this key should be deleted.
       * @return The number of bytes written to the backup stream
       * @throws IOException if the write failed
       */
      public int writeEntityHeader(String key, int dataSize) throws IOException {
          int result = writeEntityHeader_native(mBackupWriter, key, dataSize);
          if (result >= 0) {
              return result;
          } else {
              throw new IOException("result=0x" + Integer.toHexString(result));
          }
      }
  
      /**
       * Write a chunk of data under the current entity to the backup transport.
       * @param data A raw data buffer to send
       * @param size The number of bytes to be sent in this chunk
       * @return the number of bytes written
       * @throws IOException if the write failed
       */
      public int writeEntityData(byte[] data, int size) throws IOException {
          int result = writeEntityData_native(mBackupWriter, data, size);
          if (result >= 0) {
              return result;
          } else {
              throw new IOException("result=0x" + Integer.toHexString(result));
          }
      }
  
      /** @hide */
      public void setKeyPrefix(String keyPrefix) {
          setKeyPrefix_native(mBackupWriter, keyPrefix);
      }
  
      /** @hide */
      @Override
      protected void finalize() throws Throwable {
          try {
              dtor(mBackupWriter);
          } finally {
              super.finalize();
          }
      }
  
      private native static long ctor(FileDescriptor fd);
      private native static void dtor(long mBackupWriter);
  
      private native static int writeEntityHeader_native(long mBackupWriter, String key, int dataSize);
      private native static int writeEntityData_native(long mBackupWriter, byte[] data, int size);
      private native static void setKeyPrefix_native(long mBackupWriter, String keyPrefix);
  }
  
  