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 androidx.core.os;
18 
19 import android.os.Build;
20 
21 /**
22  * Static library support version of the framework's {@link android.os.CancellationSignal}.
23  * Used to write apps that run on platforms prior to Android 4.1.  See the framework SDK
24  * documentation for a class overview.
25  */
26 public final class CancellationSignal {
27     private boolean mIsCanceled;
28     private OnCancelListener mOnCancelListener;
29     private Object mCancellationSignalObj;
30     private boolean mCancelInProgress;
31 
32     /**
33      * Creates a cancellation signal, initially not canceled.
34      */
CancellationSignal()35     public CancellationSignal() {
36     }
37 
38     /**
39      * Returns true if the operation has been canceled.
40      *
41      * @return True if the operation has been canceled.
42      */
isCanceled()43     public boolean isCanceled() {
44         synchronized (this) {
45             return mIsCanceled;
46         }
47     }
48 
49     /**
50      * Throws {@link OperationCanceledException} if the operation has been canceled.
51      *
52      * @throws OperationCanceledException if the operation has been canceled.
53      */
throwIfCanceled()54     public void throwIfCanceled() {
55         if (isCanceled()) {
56             throw new OperationCanceledException();
57         }
58     }
59 
60     /**
61      * Cancels the operation and signals the cancellation listener.
62      * If the operation has not yet started, then it will be canceled as soon as it does.
63      */
cancel()64     public void cancel() {
65         final OnCancelListener listener;
66         final Object obj;
67         synchronized (this) {
68             if (mIsCanceled) {
69                 return;
70             }
71             mIsCanceled = true;
72             mCancelInProgress = true;
73             listener = mOnCancelListener;
74             obj = mCancellationSignalObj;
75         }
76 
77         try {
78             if (listener != null) {
79                 listener.onCancel();
80             }
81             if (obj != null && Build.VERSION.SDK_INT >= 16) {
82                 ((android.os.CancellationSignal) obj).cancel();
83             }
84         } finally {
85             synchronized (this) {
86                 mCancelInProgress = false;
87                 notifyAll();
88             }
89         }
90     }
91 
92     /**
93      * Sets the cancellation listener to be called when canceled.
94      *
95      * This method is intended to be used by the recipient of a cancellation signal
96      * such as a database or a content provider to handle cancellation requests
97      * while performing a long-running operation.  This method is not intended to be
98      * used by applications themselves.
99      *
100      * If {@link CancellationSignal#cancel} has already been called, then the provided
101      * listener is invoked immediately.
102      *
103      * This method is guaranteed that the listener will not be called after it
104      * has been removed.
105      *
106      * @param listener The cancellation listener, or null to remove the current listener.
107      */
setOnCancelListener(OnCancelListener listener)108     public void setOnCancelListener(OnCancelListener listener) {
109         synchronized (this) {
110             waitForCancelFinishedLocked();
111 
112             if (mOnCancelListener == listener) {
113                 return;
114             }
115             mOnCancelListener = listener;
116             if (!mIsCanceled || listener == null) {
117                 return;
118             }
119         }
120         listener.onCancel();
121     }
122 
123     /**
124      * Gets the framework {@link android.os.CancellationSignal} associated with this object.
125      * <p>
126      * Framework support for cancellation signals was added in
127      * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} so this method will always
128      * return null on older versions of the platform.
129      * </p>
130      *
131      * @return A framework cancellation signal object, or null on platform versions
132      * prior to Jellybean.
133      */
getCancellationSignalObject()134     public Object getCancellationSignalObject() {
135         if (Build.VERSION.SDK_INT < 16) {
136             return null;
137         }
138         synchronized (this) {
139             if (mCancellationSignalObj == null) {
140                 mCancellationSignalObj = new android.os.CancellationSignal();
141                 if (mIsCanceled) {
142                     ((android.os.CancellationSignal) mCancellationSignalObj).cancel();
143                 }
144             }
145             return mCancellationSignalObj;
146         }
147     }
148 
waitForCancelFinishedLocked()149     private void waitForCancelFinishedLocked() {
150         while (mCancelInProgress) {
151             try {
152                 wait();
153             } catch (InterruptedException ex) {
154             }
155         }
156     }
157 
158      /**
159      * Listens for cancellation.
160      */
161     public interface OnCancelListener {
162         /**
163          * Called when {@link CancellationSignal#cancel} is invoked.
164          */
onCancel()165         void onCancel();
166     }
167 }
168