1bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan/*
2bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * Copyright (C) 2010 The Android Open Source Project
3bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan *
4bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * Licensed under the Apache License, Version 2.0 (the "License");
5bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * you may not use this file except in compliance with the License.
6bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * You may obtain a copy of the License at
7bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan *
8bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan *      http://www.apache.org/licenses/LICENSE-2.0
9bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan *
10bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * Unless required by applicable law or agreed to in writing, software
11bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * distributed under the License is distributed on an "AS IS" BASIS,
12bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * See the License for the specific language governing permissions and
14bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * limitations under the License.
15bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan */
16bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
17bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanpackage com.android.calendar;
18bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
19bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanimport com.android.calendar.AsyncQueryServiceHelper.OperationInfo;
20bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
21bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanimport android.content.ContentProviderOperation;
22bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanimport android.content.ContentProviderResult;
23bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanimport android.content.ContentResolver;
24bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanimport android.content.ContentValues;
25bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanimport android.content.Context;
26bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanimport android.database.Cursor;
27bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanimport android.net.Uri;
28bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanimport android.os.Handler;
29bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanimport android.os.Message;
30bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanimport android.util.Log;
31bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
32bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanimport java.util.ArrayList;
3337960c091b5912dd45beb561a85a9064ea42e616Erikimport java.util.concurrent.atomic.AtomicInteger;
34bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
35bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan/**
36bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * A helper class that executes {@link ContentResolver} calls in a background
37bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * {@link android.app.Service}. This minimizes the chance of the call getting
38bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * lost because the caller ({@link android.app.Activity}) is killed. It is
39bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * designed for easy migration from {@link android.content.AsyncQueryHandler}
40bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * which calls the {@link ContentResolver} in a background thread. This supports
41bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * query/insert/update/delete and also batch mode i.e.
42bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * {@link ContentProviderOperation}. It also supports delay execution and cancel
43bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * which allows for time-limited undo. Note that there's one queue per
44bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan * application which serializes all the calls.
45bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan */
46bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chanpublic class AsyncQueryService extends Handler {
47bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    private static final String TAG = "AsyncQuery";
480847dbf80788392f1e6e2a25f6f0a91e1f9cda04Michael Chan    static final boolean localLOGV = false;
49bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
5037960c091b5912dd45beb561a85a9064ea42e616Erik    // Used for generating unique tokens for calls to this service
5137960c091b5912dd45beb561a85a9064ea42e616Erik    private static AtomicInteger mUniqueToken = new AtomicInteger(0);
5237960c091b5912dd45beb561a85a9064ea42e616Erik
53bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    private Context mContext;
54bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    private Handler mHandler = this; // can be overridden for testing
55bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
56bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
57bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * Data class which holds into info of the queued operation
58bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
59bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    public static class Operation {
60bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        static final int EVENT_ARG_QUERY = 1;
61bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        static final int EVENT_ARG_INSERT = 2;
62bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        static final int EVENT_ARG_UPDATE = 3;
63bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        static final int EVENT_ARG_DELETE = 4;
64bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        static final int EVENT_ARG_BATCH = 5;
65bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
66bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        /**
67bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan         * unique identify for cancellation purpose
68bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan         */
69bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        public int token;
70bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
71bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        /**
72bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan         * One of the EVENT_ARG_ constants in the class describing the operation
73bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan         */
74bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        public int op;
75bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
76bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        /**
77bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan         * {@link SystemClock.elapsedRealtime()} based
78bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan         */
79bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        public long scheduledExecutionTime;
80bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
81bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        protected static char opToChar(int op) {
82bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            switch (op) {
83bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                case Operation.EVENT_ARG_QUERY:
84bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                    return 'Q';
85bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                case Operation.EVENT_ARG_INSERT:
86bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                    return 'I';
87bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                case Operation.EVENT_ARG_UPDATE:
88bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                    return 'U';
89bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                case Operation.EVENT_ARG_DELETE:
90bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                    return 'D';
91bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                case Operation.EVENT_ARG_BATCH:
92bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                    return 'B';
93bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                default:
94bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                    return '?';
95bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            }
96bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        }
97bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
98bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        @Override
99bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        public String toString() {
100bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            StringBuilder builder = new StringBuilder();
101bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            builder.append("Operation [op=");
102bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            builder.append(op);
103bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            builder.append(", token=");
104bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            builder.append(token);
105bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            builder.append(", scheduledExecutionTime=");
106bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            builder.append(scheduledExecutionTime);
107bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            builder.append("]");
108bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            return builder.toString();
109bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        }
110bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
111bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
112bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    public AsyncQueryService(Context context) {
113bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        mContext = context;
114bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
115bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
116bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
11737960c091b5912dd45beb561a85a9064ea42e616Erik     * returns a practically unique token for db operations
11837960c091b5912dd45beb561a85a9064ea42e616Erik     */
11937960c091b5912dd45beb561a85a9064ea42e616Erik    public final int getNextToken() {
12037960c091b5912dd45beb561a85a9064ea42e616Erik        return mUniqueToken.getAndIncrement();
12137960c091b5912dd45beb561a85a9064ea42e616Erik    }
12237960c091b5912dd45beb561a85a9064ea42e616Erik
12337960c091b5912dd45beb561a85a9064ea42e616Erik    /**
124bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * Gets the last delayed operation. It is typically used for canceling.
125bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *
126bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @return Operation object which contains of the last cancelable operation
127bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
128bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    public final Operation getLastCancelableOperation() {
129bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        return AsyncQueryServiceHelper.getLastCancelableOperation();
130bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
131bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
132bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
133bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * Attempts to cancel operation that has not already started. Note that
134bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * there is no guarantee that the operation will be canceled. They still may
135bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * result in a call to on[Query/Insert/Update/Delete/Batch]Complete after
136bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * this call has completed.
137bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *
138bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param token The token representing the operation to be canceled. If
139bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            multiple operations have the same token they will all be
140bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            canceled.
141bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
142bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    public final int cancelOperation(int token) {
143bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        return AsyncQueryServiceHelper.cancelOperation(token);
144bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
145bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
146bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
147bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * This method begins an asynchronous query. When the query is done
148bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * {@link #onQueryComplete} is called.
149bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *
150bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param token A token passed into {@link #onQueryComplete} to identify the
151bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            query.
152bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param cookie An object that gets passed into {@link #onQueryComplete}
153bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param uri The URI, using the content:// scheme, for the content to
154bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            retrieve.
155bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param projection A list of which columns to return. Passing null will
156bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            return all columns, which is discouraged to prevent reading
157bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            data from storage that isn't going to be used.
158bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param selection A filter declaring which rows to return, formatted as an
159bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            SQL WHERE clause (excluding the WHERE itself). Passing null
160bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            will return all rows for the given URI.
161bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param selectionArgs You may include ?s in selection, which will be
162bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            replaced by the values from selectionArgs, in the order that
163bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            they appear in the selection. The values will be bound as
164bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            Strings.
165bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
166bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            (excluding the ORDER BY itself). Passing null will use the
167bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            default sort order, which may be unordered.
168bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
169bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    public void startQuery(int token, Object cookie, Uri uri, String[] projection,
170bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            String selection, String[] selectionArgs, String orderBy) {
171bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        OperationInfo info = new OperationInfo();
172bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.op = Operation.EVENT_ARG_QUERY;
173bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.resolver = mContext.getContentResolver();
174bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
175bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.handler = mHandler;
176bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.token = token;
177bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.cookie = cookie;
178bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.uri = uri;
179bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.projection = projection;
180bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.selection = selection;
181bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.selectionArgs = selectionArgs;
182bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.orderBy = orderBy;
183bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
184bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        AsyncQueryServiceHelper.queueOperation(mContext, info);
185bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
186bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
187bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
188bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * This method begins an asynchronous insert. When the insert operation is
189bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * done {@link #onInsertComplete} is called.
190bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *
191bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param token A token passed into {@link #onInsertComplete} to identify
192bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            the insert operation.
193bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param cookie An object that gets passed into {@link #onInsertComplete}
194bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param uri the Uri passed to the insert operation.
195bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param initialValues the ContentValues parameter passed to the insert
196bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            operation.
197bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param delayMillis delay in executing the operation. This operation will
198bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            execute before the delayed time when another operation is
199bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            added. Useful for implementing single level undo.
200bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
20137960c091b5912dd45beb561a85a9064ea42e616Erik    public void startInsert(int token, Object cookie, Uri uri, ContentValues initialValues,
202bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            long delayMillis) {
203bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        OperationInfo info = new OperationInfo();
204bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.op = Operation.EVENT_ARG_INSERT;
205bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.resolver = mContext.getContentResolver();
206bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.handler = mHandler;
207bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
208bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.token = token;
209bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.cookie = cookie;
210bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.uri = uri;
211bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.values = initialValues;
212bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.delayMillis = delayMillis;
213bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
214bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        AsyncQueryServiceHelper.queueOperation(mContext, info);
215bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
216bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
217bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
218bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * This method begins an asynchronous update. When the update operation is
219bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * done {@link #onUpdateComplete} is called.
220bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *
221bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param token A token passed into {@link #onUpdateComplete} to identify
222bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            the update operation.
223bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param cookie An object that gets passed into {@link #onUpdateComplete}
224bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param uri the Uri passed to the update operation.
225bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param values the ContentValues parameter passed to the update operation.
226bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param selection A filter declaring which rows to update, formatted as an
227bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            SQL WHERE clause (excluding the WHERE itself). Passing null
228bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            will update all rows for the given URI.
229bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param selectionArgs You may include ?s in selection, which will be
230bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            replaced by the values from selectionArgs, in the order that
231bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            they appear in the selection. The values will be bound as
232bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            Strings.
233bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param delayMillis delay in executing the operation. This operation will
234bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            execute before the delayed time when another operation is
235bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            added. Useful for implementing single level undo.
236bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
23737960c091b5912dd45beb561a85a9064ea42e616Erik    public void startUpdate(int token, Object cookie, Uri uri, ContentValues values,
238bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            String selection, String[] selectionArgs, long delayMillis) {
239bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        OperationInfo info = new OperationInfo();
240bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.op = Operation.EVENT_ARG_UPDATE;
241bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.resolver = mContext.getContentResolver();
242bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.handler = mHandler;
243bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
244bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.token = token;
245bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.cookie = cookie;
246bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.uri = uri;
247bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.values = values;
248bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.selection = selection;
249bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.selectionArgs = selectionArgs;
250bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.delayMillis = delayMillis;
251bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
252bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        AsyncQueryServiceHelper.queueOperation(mContext, info);
253bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
254bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
255bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
256bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * This method begins an asynchronous delete. When the delete operation is
257bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * done {@link #onDeleteComplete} is called.
258bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *
259bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param token A token passed into {@link #onDeleteComplete} to identify
260bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            the delete operation.
261bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param cookie An object that gets passed into {@link #onDeleteComplete}
262bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param uri the Uri passed to the delete operation.
263bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param selection A filter declaring which rows to delete, formatted as an
264bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            SQL WHERE clause (excluding the WHERE itself). Passing null
265bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            will delete all rows for the given URI.
266bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param selectionArgs You may include ?s in selection, which will be
267bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            replaced by the values from selectionArgs, in the order that
268bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            they appear in the selection. The values will be bound as
269bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            Strings.
270bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param delayMillis delay in executing the operation. This operation will
271bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            execute before the delayed time when another operation is
272bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            added. Useful for implementing single level undo.
273bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
27437960c091b5912dd45beb561a85a9064ea42e616Erik    public void startDelete(int token, Object cookie, Uri uri, String selection,
275bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            String[] selectionArgs, long delayMillis) {
276bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        OperationInfo info = new OperationInfo();
277bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.op = Operation.EVENT_ARG_DELETE;
278bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.resolver = mContext.getContentResolver();
279bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.handler = mHandler;
280bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
281bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.token = token;
282bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.cookie = cookie;
283bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.uri = uri;
284bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.selection = selection;
285bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.selectionArgs = selectionArgs;
286bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.delayMillis = delayMillis;
287bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
288bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        AsyncQueryServiceHelper.queueOperation(mContext, info);
289bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
290bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
291bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
292bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * This method begins an asynchronous {@link ContentProviderOperation}. When
293bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * the operation is done {@link #onBatchComplete} is called.
294bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *
295bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param token A token passed into {@link #onDeleteComplete} to identify
296bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            the delete operation.
297bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param cookie An object that gets passed into {@link #onDeleteComplete}
298bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param authority the authority used for the
299bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            {@link ContentProviderOperation}.
300bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param cpo the {@link ContentProviderOperation} to be executed.
301bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param delayMillis delay in executing the operation. This operation will
302bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            execute before the delayed time when another operation is
303bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            added. Useful for implementing single level undo.
304bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
30537960c091b5912dd45beb561a85a9064ea42e616Erik    public void startBatch(int token, Object cookie, String authority,
306bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            ArrayList<ContentProviderOperation> cpo, long delayMillis) {
307bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        OperationInfo info = new OperationInfo();
308bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.op = Operation.EVENT_ARG_BATCH;
309bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.resolver = mContext.getContentResolver();
310bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.handler = mHandler;
311bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
312bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.token = token;
313bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.cookie = cookie;
314bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.authority = authority;
315bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.cpo = cpo;
316bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        info.delayMillis = delayMillis;
317bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
318bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        AsyncQueryServiceHelper.queueOperation(mContext, info);
319bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
320bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
321bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
322bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * Called when an asynchronous query is completed.
323bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *
324bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param token the token to identify the query, passed in from
325bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            {@link #startQuery}.
326bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param cookie the cookie object passed in from {@link #startQuery}.
327bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param cursor The cursor holding the results from the query.
328bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
329bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
330bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        if (localLOGV) {
331bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            Log.d(TAG, "########## default onQueryComplete");
332bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        }
333bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
334bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
335bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
336bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * Called when an asynchronous insert is completed.
337bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *
338bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param token the token to identify the query, passed in from
339bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            {@link #startInsert}.
340bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param cookie the cookie object that's passed in from
341bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            {@link #startInsert}.
342bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param uri the uri returned from the insert operation.
343bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
344bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    protected void onInsertComplete(int token, Object cookie, Uri uri) {
345bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        if (localLOGV) {
346bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            Log.d(TAG, "########## default onInsertComplete");
347bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        }
348bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
349bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
350bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
351bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * Called when an asynchronous update is completed.
352bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *
353bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param token the token to identify the query, passed in from
354bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            {@link #startUpdate}.
355bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param cookie the cookie object that's passed in from
356bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            {@link #startUpdate}.
357bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param result the result returned from the update operation
358bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
359bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    protected void onUpdateComplete(int token, Object cookie, int result) {
360bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        if (localLOGV) {
361bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            Log.d(TAG, "########## default onUpdateComplete");
362bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        }
363bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
364bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
365bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
366bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * Called when an asynchronous delete is completed.
367bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *
368bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param token the token to identify the query, passed in from
369bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            {@link #startDelete}.
370bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param cookie the cookie object that's passed in from
371bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            {@link #startDelete}.
372bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param result the result returned from the delete operation
373bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
374bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    protected void onDeleteComplete(int token, Object cookie, int result) {
375bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        if (localLOGV) {
376bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            Log.d(TAG, "########## default onDeleteComplete");
377bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        }
378bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
379bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
380bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    /**
381bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * Called when an asynchronous {@link ContentProviderOperation} is
382bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * completed.
383bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *
384bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param token the token to identify the query, passed in from
385bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            {@link #startDelete}.
386bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param cookie the cookie object that's passed in from
387bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            {@link #startDelete}.
388bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     * @param results the result returned from executing the
389bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     *            {@link ContentProviderOperation}
390bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan     */
391bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    protected void onBatchComplete(int token, Object cookie, ContentProviderResult[] results) {
392bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        if (localLOGV) {
393bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            Log.d(TAG, "########## default onBatchComplete");
394bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        }
395bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
396bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
397bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    @Override
398bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    public void handleMessage(Message msg) {
399bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        OperationInfo info = (OperationInfo) msg.obj;
400bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
401bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        int token = msg.what;
402bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        int op = msg.arg1;
403bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
404bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        if (localLOGV) {
405bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            Log.d(TAG, "AsyncQueryService.handleMessage: token=" + token + ", op=" + op
406bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                    + ", result=" + info.result);
407bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        }
408bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
409bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        // pass token back to caller on each callback.
410bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        switch (op) {
411bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            case Operation.EVENT_ARG_QUERY:
412bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                onQueryComplete(token, info.cookie, (Cursor) info.result);
413bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                break;
414bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
415bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            case Operation.EVENT_ARG_INSERT:
416bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                onInsertComplete(token, info.cookie, (Uri) info.result);
417bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                break;
418bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
419bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            case Operation.EVENT_ARG_UPDATE:
420bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                onUpdateComplete(token, info.cookie, (Integer) info.result);
421bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                break;
422bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
423bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            case Operation.EVENT_ARG_DELETE:
424bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                onDeleteComplete(token, info.cookie, (Integer) info.result);
425bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                break;
426bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
427bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan            case Operation.EVENT_ARG_BATCH:
428bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                onBatchComplete(token, info.cookie, (ContentProviderResult[]) info.result);
429bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan                break;
430bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        }
431bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
432bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan
433bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan//    @VisibleForTesting
434bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    protected void setTestHandler(Handler handler) {
435bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan        mHandler = handler;
436bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan    }
437bed0275111ecc6c4a3a638f90a9bac13bee594f4Michael Chan}
438