• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
CancellationSignal()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      */
isCanceled()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      */
throwIfCanceled()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      */
cancel()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      */
setOnCancelListener(OnCancelListener listener)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      */
setRemote(ICancellationSignal remote)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 
waitForCancelFinishedLocked()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      */
createTransport()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      */
fromTransport(ICancellationSignal transport)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          */
onCancel()199         void onCancel();
200     }
201 
202     private static final class Transport extends ICancellationSignal.Stub {
203         final CancellationSignal mCancellationSignal = new CancellationSignal();
204 
205         @Override
cancel()206         public void cancel() throws RemoteException {
207             mCancellationSignal.cancel();
208         }
209     }
210 }
211