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 
     21 /**
     22  * Provides the ability to cancel an operation in progress.
     23  */
     24 public final class CancellationSignal {
     25     private boolean mIsCanceled;
     26     private OnCancelListener mOnCancelListener;
     27     private ICancellationSignal mRemote;
     28     private boolean mCancelInProgress;
     29 
     30     /**
     31      * Creates a cancellation signal, initially not canceled.
     32      */
     33     public CancellationSignal() {
     34     }
     35 
     36     /**
     37      * Returns true if the operation has been canceled.
     38      *
     39      * @return True if the operation has been canceled.
     40      */
     41     public boolean isCanceled() {
     42         synchronized (this) {
     43             return mIsCanceled;
     44         }
     45     }
     46 
     47     /**
     48      * Throws {@link OperationCanceledException} if the operation has been canceled.
     49      *
     50      * @throws OperationCanceledException if the operation has been canceled.
     51      */
     52     public void throwIfCanceled() {
     53         if (isCanceled()) {
     54             throw new OperationCanceledException();
     55         }
     56     }
     57 
     58     /**
     59      * Cancels the operation and signals the cancellation listener.
     60      * If the operation has not yet started, then it will be canceled as soon as it does.
     61      */
     62     public void cancel() {
     63         final OnCancelListener listener;
     64         final ICancellationSignal remote;
     65         synchronized (this) {
     66             if (mIsCanceled) {
     67                 return;
     68             }
     69             mIsCanceled = true;
     70             mCancelInProgress = true;
     71             listener = mOnCancelListener;
     72             remote = mRemote;
     73         }
     74 
     75         try {
     76             if (listener != null) {
     77                 listener.onCancel();
     78             }
     79             if (remote != null) {
     80                 try {
     81                     remote.cancel();
     82                 } catch (RemoteException ex) {
     83                 }
     84             }
     85         } finally {
     86             synchronized (this) {
     87                 mCancelInProgress = false;
     88                 notifyAll();
     89             }
     90         }
     91     }
     92 
     93     /**
     94      * Sets the cancellation listener to be called when canceled.
     95      *
     96      * This method is intended to be used by the recipient of a cancellation signal
     97      * such as a database or a content provider to handle cancellation requests
     98      * while performing a long-running operation.  This method is not intended to be
     99      * used by applications themselves.
    100      *
    101      * If {@link CancellationSignal#cancel} has already been called, then the provided
    102      * listener is invoked immediately.
    103      *
    104      * This method is guaranteed that the listener will not be called after it
    105      * has been removed.
    106      *
    107      * @param listener The cancellation listener, or null to remove the current listener.
    108      */
    109     public void setOnCancelListener(OnCancelListener listener) {
    110         synchronized (this) {
    111             waitForCancelFinishedLocked();
    112 
    113             if (mOnCancelListener == listener) {
    114                 return;
    115             }
    116             mOnCancelListener = listener;
    117             if (!mIsCanceled || listener == null) {
    118                 return;
    119             }
    120         }
    121         listener.onCancel();
    122     }
    123 
    124     /**
    125      * Sets the remote transport.
    126      *
    127      * If {@link CancellationSignal#cancel} has already been called, then the provided
    128      * remote transport is canceled immediately.
    129      *
    130      * This method is guaranteed that the remote transport will not be called after it
    131      * has been removed.
    132      *
    133      * @param remote The remote transport, or null to remove.
    134      *
    135      * @hide
    136      */
    137     public void setRemote(ICancellationSignal remote) {
    138         synchronized (this) {
    139             waitForCancelFinishedLocked();
    140 
    141             if (mRemote == remote) {
    142                 return;
    143             }
    144             mRemote = remote;
    145             if (!mIsCanceled || remote == null) {
    146                 return;
    147             }
    148         }
    149         try {
    150             remote.cancel();
    151         } catch (RemoteException ex) {
    152         }
    153     }
    154 
    155     private void waitForCancelFinishedLocked() {
    156         while (mCancelInProgress) {
    157             try {
    158                 wait();
    159             } catch (InterruptedException ex) {
    160             }
    161         }
    162     }
    163 
    164     /**
    165      * Creates a transport that can be returned back to the caller of
    166      * a Binder function and subsequently used to dispatch a cancellation signal.
    167      *
    168      * @return The new cancellation signal transport.
    169      *
    170      * @hide
    171      */
    172     public static ICancellationSignal createTransport() {
    173         return new Transport();
    174     }
    175 
    176     /**
    177      * Given a locally created transport, returns its associated cancellation signal.
    178      *
    179      * @param transport The locally created transport, or null if none.
    180      * @return The associated cancellation signal, or null if none.
    181      *
    182      * @hide
    183      */
    184     public static CancellationSignal fromTransport(ICancellationSignal transport) {
    185         if (transport instanceof Transport) {
    186             return ((Transport)transport).mCancellationSignal;
    187         }
    188         return null;
    189     }
    190 
    191     /**
    192      * Listens for cancellation.
    193      */
    194     public interface OnCancelListener {
    195         /**
    196          * Called when {@link CancellationSignal#cancel} is invoked.
    197          */
    198         void onCancel();
    199     }
    200 
    201     private static final class Transport extends ICancellationSignal.Stub {
    202         final CancellationSignal mCancellationSignal = new CancellationSignal();
    203 
    204         @Override
    205         public void cancel() throws RemoteException {
    206             mCancellationSignal.cancel();
    207         }
    208     }
    209 }
    210