Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.os;
     18 
     19 import android.os.ICancellationSignal;
     20 import android.os.ICancellationSignal.Stub;
     21 
     22 /**
     23  * Provides the ability to cancel an operation in progress.
     24  */
     25 public final class CancellationSignal {
     26     private boolean mIsCanceled;
     27     private OnCancelListener mOnCancelListener;
     28     private ICancellationSignal mRemote;
     29     private boolean mCancelInProgress;
     30 
     31     /**
     32      * Creates a cancellation signal, initially not canceled.
     33      */
     34     public CancellationSignal() {
     35     }
     36 
     37     /**
     38      * Returns true if the operation has been canceled.
     39      *
     40      * @return True if the operation has been canceled.
     41      */
     42     public boolean isCanceled() {
     43         synchronized (this) {
     44             return mIsCanceled;
     45         }
     46     }
     47 
     48     /**
     49      * Throws {@link OperationCanceledException} if the operation has been canceled.
     50      *
     51      * @throws OperationCanceledException if the operation has been canceled.
     52      */
     53     public void throwIfCanceled() {
     54         if (isCanceled()) {
     55             throw new OperationCanceledException();
     56         }
     57     }
     58 
     59     /**
     60      * Cancels the operation and signals the cancellation listener.
     61      * If the operation has not yet started, then it will be canceled as soon as it does.
     62      */
     63     public void cancel() {
     64         final OnCancelListener listener;
     65         final ICancellationSignal remote;
     66         synchronized (this) {
     67             if (mIsCanceled) {
     68                 return;
     69             }
     70             mIsCanceled = true;
     71             mCancelInProgress = true;
     72             listener = mOnCancelListener;
     73             remote = mRemote;
     74         }
     75 
     76         try {
     77             if (listener != null) {
     78                 listener.onCancel();
     79             }
     80             if (remote != null) {
     81                 try {
     82                     remote.cancel();
     83                 } catch (RemoteException ex) {
     84                 }
     85             }
     86         } finally {
     87             synchronized (this) {
     88                 mCancelInProgress = false;
     89                 notifyAll();
     90             }
     91         }
     92     }
     93 
     94     /**
     95      * Sets the cancellation listener to be called when canceled.
     96      *
     97      * This method is intended to be used by the recipient of a cancellation signal
     98      * such as a database or a content provider to handle cancellation requests
     99      * while performing a long-running operation.  This method is not intended to be
    100      * used by applications themselves.
    101      *
    102      * If {@link CancellationSignal#cancel} has already been called, then the provided
    103      * listener is invoked immediately.
    104      *
    105      * This method is guaranteed that the listener will not be called after it
    106      * has been removed.
    107      *
    108      * @param listener The cancellation listener, or null to remove the current listener.
    109      */
    110     public void setOnCancelListener(OnCancelListener listener) {
    111         synchronized (this) {
    112             waitForCancelFinishedLocked();
    113 
    114             if (mOnCancelListener == listener) {
    115                 return;
    116             }
    117             mOnCancelListener = listener;
    118             if (!mIsCanceled || listener == null) {
    119                 return;
    120             }
    121         }
    122         listener.onCancel();
    123     }
    124 
    125     /**
    126      * Sets the remote transport.
    127      *
    128      * If {@link CancellationSignal#cancel} has already been called, then the provided
    129      * remote transport is canceled immediately.
    130      *
    131      * This method is guaranteed that the remote transport will not be called after it
    132      * has been removed.
    133      *
    134      * @param remote The remote transport, or null to remove.
    135      *
    136      * @hide
    137      */
    138     public void setRemote(ICancellationSignal remote) {
    139         synchronized (this) {
    140             waitForCancelFinishedLocked();
    141 
    142             if (mRemote == remote) {
    143                 return;
    144             }
    145             mRemote = remote;
    146             if (!mIsCanceled || remote == null) {
    147                 return;
    148             }
    149         }
    150         try {
    151             remote.cancel();
    152         } catch (RemoteException ex) {
    153         }
    154     }
    155 
    156     private void waitForCancelFinishedLocked() {
    157         while (mCancelInProgress) {
    158             try {
    159                 wait();
    160             } catch (InterruptedException ex) {
    161             }
    162         }
    163     }
    164 
    165     /**
    166      * Creates a transport that can be returned back to the caller of
    167      * a Binder function and subsequently used to dispatch a cancellation signal.
    168      *
    169      * @return The new cancellation signal transport.
    170      *
    171      * @hide
    172      */
    173     public static ICancellationSignal createTransport() {
    174         return new Transport();
    175     }
    176 
    177     /**
    178      * Given a locally created transport, returns its associated cancellation signal.
    179      *
    180      * @param transport The locally created transport, or null if none.
    181      * @return The associated cancellation signal, or null if none.
    182      *
    183      * @hide
    184      */
    185     public static CancellationSignal fromTransport(ICancellationSignal transport) {
    186         if (transport instanceof Transport) {
    187             return ((Transport)transport).mCancellationSignal;
    188         }
    189         return null;
    190     }
    191 
    192     /**
    193      * Listens for cancellation.
    194      */
    195     public interface OnCancelListener {
    196         /**
    197          * Called when {@link CancellationSignal#cancel} is invoked.
    198          */
    199         void onCancel();
    200     }
    201 
    202     private static final class Transport extends ICancellationSignal.Stub {
    203         final CancellationSignal mCancellationSignal = new CancellationSignal();
    204 
    205         @Override
    206         public void cancel() throws RemoteException {
    207             mCancellationSignal.cancel();
    208         }
    209     }
    210 }
    211