diff options
Diffstat (limited to 'core/java/android/content')
-rw-r--r-- | core/java/android/content/AsyncTaskLoader.java | 20 | ||||
-rw-r--r-- | core/java/android/content/CancelationSignal.java | 164 | ||||
-rw-r--r-- | core/java/android/content/ContentProvider.java | 93 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderClient.java | 15 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderNative.java | 41 | ||||
-rw-r--r-- | core/java/android/content/ContentResolver.java | 52 | ||||
-rw-r--r-- | core/java/android/content/CursorLoader.java | 39 | ||||
-rw-r--r-- | core/java/android/content/ICancelationSignal.aidl | 24 | ||||
-rw-r--r-- | core/java/android/content/IContentProvider.java | 5 | ||||
-rw-r--r-- | core/java/android/content/OperationCanceledException.java | 32 |
10 files changed, 465 insertions, 20 deletions
diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java index 0b54396..944ca6b 100644 --- a/core/java/android/content/AsyncTaskLoader.java +++ b/core/java/android/content/AsyncTaskLoader.java @@ -173,6 +173,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { if (DEBUG) Slog.v(TAG, "cancelLoad: cancelled=" + cancelled); if (cancelled) { mCancellingTask = mTask; + onCancelLoadInBackground(); } mTask = null; return cancelled; @@ -256,6 +257,25 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> { } /** + * Override this method to try to abort the computation currently taking + * place on a background thread. + * + * Note that when this method is called, it is possible that {@link #loadInBackground} + * has not started yet or has already completed. + */ + protected void onCancelLoadInBackground() { + } + + /** + * Returns true if the current execution of {@link #loadInBackground()} is being canceled. + * + * @return True if the current execution of {@link #loadInBackground()} is being canceled. + */ + protected boolean isLoadInBackgroundCanceled() { + return mCancellingTask != null; + } + + /** * Locks the current thread until the loader completes the current load * operation. Returns immediately if there is no load operation running. * Should not be called from the UI thread: calling it from the UI diff --git a/core/java/android/content/CancelationSignal.java b/core/java/android/content/CancelationSignal.java new file mode 100644 index 0000000..58cf59d --- /dev/null +++ b/core/java/android/content/CancelationSignal.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2012 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.content; + +import android.os.RemoteException; + +/** + * Provides the ability to cancel an operation in progress. + */ +public final class CancelationSignal { + private boolean mIsCanceled; + private OnCancelListener mOnCancelListener; + private ICancelationSignal mRemote; + + /** + * Creates a cancelation signal, initially not canceled. + */ + public CancelationSignal() { + } + + /** + * Returns true if the operation has been canceled. + * + * @return True if the operation has been canceled. + */ + public boolean isCanceled() { + synchronized (this) { + return mIsCanceled; + } + } + + /** + * Throws {@link OperationCanceledException} if the operation has been canceled. + * + * @throws OperationCanceledException if the operation has been canceled. + */ + public void throwIfCanceled() { + if (isCanceled()) { + throw new OperationCanceledException(); + } + } + + /** + * Cancels the operation and signals the cancelation listener. + * If the operation has not yet started, then it will be canceled as soon as it does. + */ + public void cancel() { + synchronized (this) { + if (!mIsCanceled) { + mIsCanceled = true; + if (mOnCancelListener != null) { + mOnCancelListener.onCancel(); + } + if (mRemote != null) { + try { + mRemote.cancel(); + } catch (RemoteException ex) { + } + } + } + } + } + + /** + * Sets the cancelation listener to be called when canceled. + * If {@link CancelationSignal#cancel} has already been called, then the provided + * listener is invoked immediately. + * + * The listener is called while holding the cancelation signal's lock which is + * also held while registering or unregistering the listener. Because of the lock, + * it is not possible for the listener to run after it has been unregistered. + * This design choice makes it easier for clients of {@link CancelationSignal} to + * prevent race conditions related to listener registration and unregistration. + * + * @param listener The cancelation listener, or null to remove the current listener. + */ + public void setOnCancelListener(OnCancelListener listener) { + synchronized (this) { + mOnCancelListener = listener; + if (mIsCanceled && listener != null) { + listener.onCancel(); + } + } + } + + /** + * Sets the remote transport. + * + * @param remote The remote transport, or null to remove. + * + * @hide + */ + public void setRemote(ICancelationSignal remote) { + synchronized (this) { + mRemote = remote; + if (mIsCanceled && remote != null) { + try { + remote.cancel(); + } catch (RemoteException ex) { + } + } + } + } + + /** + * Creates a transport that can be returned back to the caller of + * a Binder function and subsequently used to dispatch a cancelation signal. + * + * @return The new cancelation signal transport. + * + * @hide + */ + public static ICancelationSignal createTransport() { + return new Transport(); + } + + /** + * Given a locally created transport, returns its associated cancelation signal. + * + * @param transport The locally created transport, or null if none. + * @return The associated cancelation signal, or null if none. + * + * @hide + */ + public static CancelationSignal fromTransport(ICancelationSignal transport) { + if (transport instanceof Transport) { + return ((Transport)transport).mCancelationSignal; + } + return null; + } + + /** + * Listens for cancelation. + */ + public interface OnCancelListener { + /** + * Called when {@link CancelationSignal#cancel} is invoked. + */ + void onCancel(); + } + + private static final class Transport extends ICancelationSignal.Stub { + final CancelationSignal mCancelationSignal = new CancelationSignal(); + + @Override + public void cancel() throws RemoteException { + mCancelationSignal.cancel(); + } + } +} diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 116ca48..adbeb6a 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -29,6 +29,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.os.RemoteException; import android.util.Log; import java.io.File; @@ -174,28 +175,33 @@ public abstract class ContentProvider implements ComponentCallbacks2 { return getContentProvider().getClass().getName(); } + @Override public Cursor query(Uri uri, String[] projection, - String selection, String[] selectionArgs, String sortOrder) { + String selection, String[] selectionArgs, String sortOrder, + ICancelationSignal cancelationSignal) { enforceReadPermission(uri); - return ContentProvider.this.query(uri, projection, selection, - selectionArgs, sortOrder); + return ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder, + CancelationSignal.fromTransport(cancelationSignal)); } + @Override public String getType(Uri uri) { return ContentProvider.this.getType(uri); } - + @Override public Uri insert(Uri uri, ContentValues initialValues) { enforceWritePermission(uri); return ContentProvider.this.insert(uri, initialValues); } + @Override public int bulkInsert(Uri uri, ContentValues[] initialValues) { enforceWritePermission(uri); return ContentProvider.this.bulkInsert(uri, initialValues); } + @Override public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { for (ContentProviderOperation operation : operations) { @@ -210,17 +216,20 @@ public abstract class ContentProvider implements ComponentCallbacks2 { return ContentProvider.this.applyBatch(operations); } + @Override public int delete(Uri uri, String selection, String[] selectionArgs) { enforceWritePermission(uri); return ContentProvider.this.delete(uri, selection, selectionArgs); } + @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { enforceWritePermission(uri); return ContentProvider.this.update(uri, values, selection, selectionArgs); } + @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri); @@ -228,6 +237,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { return ContentProvider.this.openFile(uri, mode); } + @Override public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri); @@ -235,6 +245,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { return ContentProvider.this.openAssetFile(uri, mode); } + @Override public Bundle call(String method, String arg, Bundle extras) { return ContentProvider.this.call(method, arg, extras); } @@ -251,6 +262,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 { return ContentProvider.this.openTypedAssetFile(uri, mimeType, opts); } + @Override + public ICancelationSignal createCancelationSignal() throws RemoteException { + return CancelationSignal.createTransport(); + } + private void enforceReadPermission(Uri uri) { final int uid = Binder.getCallingUid(); if (uid == mMyUid) { @@ -541,6 +557,75 @@ public abstract class ContentProvider implements ComponentCallbacks2 { String selection, String[] selectionArgs, String sortOrder); /** + * Implement this to handle query requests from clients with support for cancelation. + * This method can be called from multiple threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes + * and Threads</a>. + * <p> + * Example client call:<p> + * <pre>// Request a specific record. + * Cursor managedCursor = managedQuery( + ContentUris.withAppendedId(Contacts.People.CONTENT_URI, 2), + projection, // Which columns to return. + null, // WHERE clause. + null, // WHERE clause value substitution + People.NAME + " ASC"); // Sort order.</pre> + * Example implementation:<p> + * <pre>// SQLiteQueryBuilder is a helper class that creates the + // proper SQL syntax for us. + SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder(); + + // Set the table we're querying. + qBuilder.setTables(DATABASE_TABLE_NAME); + + // If the query ends in a specific record number, we're + // being asked for a specific record, so set the + // WHERE clause in our query. + if((URI_MATCHER.match(uri)) == SPECIFIC_MESSAGE){ + qBuilder.appendWhere("_id=" + uri.getPathLeafId()); + } + + // Make the query. + Cursor c = qBuilder.query(mDb, + projection, + selection, + selectionArgs, + groupBy, + having, + sortOrder); + c.setNotificationUri(getContext().getContentResolver(), uri); + return c;</pre> + * <p> + * If you implement this method then you must also implement the version of + * {@link #query(Uri, String[], String, String[], String)} that does not take a cancelation + * provider to ensure correct operation on older versions of the Android Framework in + * which the cancelation signal overload was not available. + * + * @param uri The URI to query. This will be the full URI sent by the client; + * if the client is requesting a specific record, the URI will end in a record number + * that the implementation should parse and add to a WHERE or HAVING clause, specifying + * that _id value. + * @param projection The list of columns to put into the cursor. If + * null all columns are included. + * @param selection A selection criteria to apply when filtering rows. + * If null then all rows are included. + * @param selectionArgs You may include ?s in selection, which will be replaced by + * the values from selectionArgs, in order that they appear in the selection. + * The values will be bound as Strings. + * @param sortOrder How the rows in the cursor should be sorted. + * If null then the provider is free to define the sort order. + * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * If the operation is canceled, then {@link OperationCanceledException} will be thrown + * when the query is executed. + * @return a Cursor or null. + */ + public Cursor query(Uri uri, String[] projection, + String selection, String[] selectionArgs, String sortOrder, + CancelationSignal cancelationSignal) { + return query(uri, projection, selection, selectionArgs, sortOrder); + } + + /** * Implement this to handle requests for the MIME type of the data at the * given URI. The returned MIME type should start with * <code>vnd.android.cursor.item</code> for a single record, diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index 0540109..9a1fa65 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -47,7 +47,20 @@ public class ContentProviderClient { /** See {@link ContentProvider#query ContentProvider.query} */ public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder) throws RemoteException { - return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder); + return query(url, projection, selection, selectionArgs, sortOrder, null); + } + + /** See {@link ContentProvider#query ContentProvider.query} */ + public Cursor query(Uri url, String[] projection, String selection, + String[] selectionArgs, String sortOrder, CancelationSignal cancelationSignal) + throws RemoteException { + ICancelationSignal remoteCancelationSignal = null; + if (cancelationSignal != null) { + remoteCancelationSignal = mContentProvider.createCancelationSignal(); + cancelationSignal.setRemote(remoteCancelationSignal); + } + return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder, + remoteCancelationSignal); } /** See {@link ContentProvider#getType ContentProvider.getType} */ diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index b089bf2..e0e277a 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -21,7 +21,6 @@ import android.database.BulkCursorNative; import android.database.BulkCursorToCursorAdaptor; import android.database.Cursor; import android.database.CursorToBulkCursorAdaptor; -import android.database.CursorWindow; import android.database.DatabaseUtils; import android.database.IBulkCursor; import android.database.IContentObserver; @@ -41,8 +40,6 @@ import java.util.ArrayList; * {@hide} */ abstract public class ContentProviderNative extends Binder implements IContentProvider { - private static final String TAG = "ContentProvider"; - public ContentProviderNative() { attachInterface(this, descriptor); @@ -108,8 +105,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr String sortOrder = data.readString(); IContentObserver observer = IContentObserver.Stub.asInterface( data.readStrongBinder()); + ICancelationSignal cancelationSignal = ICancelationSignal.Stub.asInterface( + data.readStrongBinder()); - Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder); + Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder, + cancelationSignal); if (cursor != null) { CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor( cursor, observer, getProviderName()); @@ -295,6 +295,16 @@ abstract public class ContentProviderNative extends Binder implements IContentPr } return true; } + + case CREATE_CANCELATION_SIGNAL_TRANSACTION: + { + data.enforceInterface(IContentProvider.descriptor); + + ICancelationSignal cancelationSignal = createCancelationSignal(); + reply.writeNoException(); + reply.writeStrongBinder(cancelationSignal.asBinder()); + return true; + } } } catch (Exception e) { DatabaseUtils.writeExceptionToParcel(reply, e); @@ -324,7 +334,8 @@ final class ContentProviderProxy implements IContentProvider } public Cursor query(Uri url, String[] projection, String selection, - String[] selectionArgs, String sortOrder) throws RemoteException { + String[] selectionArgs, String sortOrder, ICancelationSignal cancelationSignal) + throws RemoteException { BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -352,6 +363,7 @@ final class ContentProviderProxy implements IContentProvider } data.writeString(sortOrder); data.writeStrongBinder(adaptor.getObserver().asBinder()); + data.writeStrongBinder(cancelationSignal != null ? cancelationSignal.asBinder() : null); mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); @@ -620,5 +632,24 @@ final class ContentProviderProxy implements IContentProvider } } + public ICancelationSignal createCancelationSignal() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + try { + data.writeInterfaceToken(IContentProvider.descriptor); + + mRemote.transact(IContentProvider.CREATE_CANCELATION_SIGNAL_TRANSACTION, + data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + ICancelationSignal cancelationSignal = ICancelationSignal.Stub.asInterface( + reply.readStrongBinder()); + return cancelationSignal; + } finally { + data.recycle(); + reply.recycle(); + } + } + private IBinder mRemote; } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 0debb84..e79475a 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -22,6 +22,7 @@ import android.accounts.Account; import android.app.ActivityManagerNative; import android.app.ActivityThread; import android.app.AppGlobals; +import android.content.ContentProvider.Transport; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; @@ -302,13 +303,62 @@ public abstract class ContentResolver { */ public final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + return query(uri, projection, selection, selectionArgs, sortOrder, null); + } + + /** + * <p> + * Query the given URI, returning a {@link Cursor} over the result set. + * </p> + * <p> + * For best performance, the caller should follow these guidelines: + * <ul> + * <li>Provide an explicit projection, to prevent + * reading data from storage that aren't going to be used.</li> + * <li>Use question mark parameter markers such as 'phone=?' instead of + * explicit values in the {@code selection} parameter, so that queries + * that differ only by those values will be recognized as the same + * for caching purposes.</li> + * </ul> + * </p> + * + * @param uri The URI, using the content:// scheme, for the content to + * retrieve. + * @param projection A list of which columns to return. Passing null will + * return all columns, which is inefficient. + * @param selection A filter declaring which rows to return, formatted as an + * SQL WHERE clause (excluding the WHERE itself). Passing null will + * return all rows for the given URI. + * @param selectionArgs You may include ?s in selection, which will be + * replaced by the values from selectionArgs, in the order that they + * appear in the selection. The values will be bound as Strings. + * @param sortOrder How to order the rows, formatted as an SQL ORDER BY + * clause (excluding the ORDER BY itself). Passing null will use the + * default sort order, which may be unordered. + * @param cancelationSignal A signal to cancel the operation in progress, or null if none. + * If the operation is canceled, then {@link OperationCanceledException} will be thrown + * when the query is executed. + * @return A Cursor object, which is positioned before the first entry, or null + * @see Cursor + */ + public final Cursor query(final Uri uri, String[] projection, + String selection, String[] selectionArgs, String sortOrder, + CancelationSignal cancelationSignal) { IContentProvider provider = acquireProvider(uri); if (provider == null) { return null; } try { long startTime = SystemClock.uptimeMillis(); - Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder); + + ICancelationSignal remoteCancelationSignal = null; + if (cancelationSignal != null) { + cancelationSignal.throwIfCanceled(); + remoteCancelationSignal = provider.createCancelationSignal(); + cancelationSignal.setRemote(remoteCancelationSignal); + } + Cursor qCursor = provider.query(uri, projection, + selection, selectionArgs, sortOrder, remoteCancelationSignal); if (qCursor == null) { releaseProvider(provider); return null; diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java index 7af535b..6e4aca8 100644 --- a/core/java/android/content/CursorLoader.java +++ b/core/java/android/content/CursorLoader.java @@ -19,7 +19,6 @@ package android.content; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; -import android.os.AsyncTask; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -49,18 +48,42 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> { String mSortOrder; Cursor mCursor; + CancelationSignal mCancelationSignal; /* Runs on a worker thread */ @Override public Cursor loadInBackground() { - Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection, - mSelectionArgs, mSortOrder); - if (cursor != null) { - // Ensure the cursor window is filled - cursor.getCount(); - registerContentObserver(cursor, mObserver); + synchronized (this) { + if (isLoadInBackgroundCanceled()) { + throw new OperationCanceledException(); + } + mCancelationSignal = new CancelationSignal(); + } + try { + Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection, + mSelectionArgs, mSortOrder, mCancelationSignal); + if (cursor != null) { + // Ensure the cursor window is filled + cursor.getCount(); + registerContentObserver(cursor, mObserver); + } + return cursor; + } finally { + synchronized (this) { + mCancelationSignal = null; + } + } + } + + @Override + protected void onCancelLoadInBackground() { + super.onCancelLoadInBackground(); + + synchronized (this) { + if (mCancelationSignal != null) { + mCancelationSignal.cancel(); + } } - return cursor; } /** diff --git a/core/java/android/content/ICancelationSignal.aidl b/core/java/android/content/ICancelationSignal.aidl new file mode 100644 index 0000000..3f5a24d --- /dev/null +++ b/core/java/android/content/ICancelationSignal.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 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.content; + +/** + * @hide + */ +interface ICancelationSignal { + oneway void cancel(); +} diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index 2a67ff8..f52157f 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -34,7 +34,8 @@ import java.util.ArrayList; */ public interface IContentProvider extends IInterface { public Cursor query(Uri url, String[] projection, String selection, - String[] selectionArgs, String sortOrder) throws RemoteException; + String[] selectionArgs, String sortOrder, ICancelationSignal cancelationSignal) + throws RemoteException; public String getType(Uri url) throws RemoteException; public Uri insert(Uri url, ContentValues initialValues) throws RemoteException; @@ -50,6 +51,7 @@ public interface IContentProvider extends IInterface { public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException; public Bundle call(String method, String arg, Bundle extras) throws RemoteException; + public ICancelationSignal createCancelationSignal() throws RemoteException; // Data interchange. public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException; @@ -71,4 +73,5 @@ public interface IContentProvider extends IInterface { static final int CALL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 20; static final int GET_STREAM_TYPES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 21; static final int OPEN_TYPED_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 22; + static final int CREATE_CANCELATION_SIGNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 23; } diff --git a/core/java/android/content/OperationCanceledException.java b/core/java/android/content/OperationCanceledException.java new file mode 100644 index 0000000..24afcfa --- /dev/null +++ b/core/java/android/content/OperationCanceledException.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 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.content; + +/** + * An exception type that is thrown when an operation in progress is canceled. + * + * @see CancelationSignal + */ +public class OperationCanceledException extends RuntimeException { + public OperationCanceledException() { + this(null); + } + + public OperationCanceledException(String message) { + super(message != null ? message : "The operation has been canceled."); + } +} |