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.support.v4.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