• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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.database;
18 
19 import android.os.Bundle;
20 import android.os.RemoteException;
21 import android.util.Log;
22 
23 /**
24  * Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local process.
25  *
26  * {@hide}
27  */
28 public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
29     private static final String TAG = "BulkCursor";
30 
31     private SelfContentObserver mObserverBridge = new SelfContentObserver(this);
32     private IBulkCursor mBulkCursor;
33     private String[] mColumns;
34     private boolean mWantsAllOnMoveCalls;
35     private int mCount;
36 
37     /**
38      * Initializes the adaptor.
39      * Must be called before first use.
40      */
initialize(BulkCursorDescriptor d)41     public void initialize(BulkCursorDescriptor d) {
42         mBulkCursor = d.cursor;
43         mColumns = d.columnNames;
44         mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns);
45         mWantsAllOnMoveCalls = d.wantsAllOnMoveCalls;
46         mCount = d.count;
47         if (d.window != null) {
48             setWindow(d.window);
49         }
50     }
51 
52     /**
53      * Gets a SelfDataChangeOberserver that can be sent to a remote
54      * process to receive change notifications over IPC.
55      *
56      * @return A SelfContentObserver hooked up to this Cursor
57      */
getObserver()58     public IContentObserver getObserver() {
59         return mObserverBridge.getContentObserver();
60     }
61 
throwIfCursorIsClosed()62     private void throwIfCursorIsClosed() {
63         if (mBulkCursor == null) {
64             throw new StaleDataException("Attempted to access a cursor after it has been closed.");
65         }
66     }
67 
68     @Override
getCount()69     public int getCount() {
70         throwIfCursorIsClosed();
71         return mCount;
72     }
73 
74     @Override
onMove(int oldPosition, int newPosition)75     public boolean onMove(int oldPosition, int newPosition) {
76         throwIfCursorIsClosed();
77 
78         try {
79             // Make sure we have the proper window
80             if (mWindow == null
81                     || newPosition < mWindow.getStartPosition()
82                     || newPosition >= mWindow.getStartPosition() + mWindow.getNumRows()) {
83                 setWindow(mBulkCursor.getWindow(newPosition));
84             } else if (mWantsAllOnMoveCalls) {
85                 mBulkCursor.onMove(newPosition);
86             }
87         } catch (RemoteException ex) {
88             // We tried to get a window and failed
89             Log.e(TAG, "Unable to get window because the remote process is dead");
90             return false;
91         }
92 
93         // Couldn't obtain a window, something is wrong
94         if (mWindow == null) {
95             return false;
96         }
97 
98         return true;
99     }
100 
101     @Override
deactivate()102     public void deactivate() {
103         // This will call onInvalidated(), so make sure to do it before calling release,
104         // which is what actually makes the data set invalid.
105         super.deactivate();
106 
107         if (mBulkCursor != null) {
108             try {
109                 mBulkCursor.deactivate();
110             } catch (RemoteException ex) {
111                 Log.w(TAG, "Remote process exception when deactivating");
112             }
113         }
114     }
115 
116     @Override
close()117     public void close() {
118         super.close();
119 
120         if (mBulkCursor != null) {
121             try {
122                 mBulkCursor.close();
123             } catch (RemoteException ex) {
124                 Log.w(TAG, "Remote process exception when closing");
125             } finally {
126                 mBulkCursor = null;
127             }
128         }
129     }
130 
131     @Override
requery()132     public boolean requery() {
133         throwIfCursorIsClosed();
134 
135         try {
136             mCount = mBulkCursor.requery(getObserver());
137             if (mCount != -1) {
138                 mPos = -1;
139                 closeWindow();
140 
141                 // super.requery() will call onChanged. Do it here instead of relying on the
142                 // observer from the far side so that observers can see a correct value for mCount
143                 // when responding to onChanged.
144                 super.requery();
145                 return true;
146             } else {
147                 deactivate();
148                 return false;
149             }
150         } catch (Exception ex) {
151             Log.e(TAG, "Unable to requery because the remote process exception " + ex.getMessage());
152             deactivate();
153             return false;
154         }
155     }
156 
157     @Override
getColumnNames()158     public String[] getColumnNames() {
159         throwIfCursorIsClosed();
160 
161         return mColumns;
162     }
163 
164     @Override
getExtras()165     public Bundle getExtras() {
166         throwIfCursorIsClosed();
167 
168         try {
169             return mBulkCursor.getExtras();
170         } catch (RemoteException e) {
171             // This should never happen because the system kills processes that are using remote
172             // cursors when the provider process is killed.
173             throw new RuntimeException(e);
174         }
175     }
176 
177     @Override
respond(Bundle extras)178     public Bundle respond(Bundle extras) {
179         throwIfCursorIsClosed();
180 
181         try {
182             return mBulkCursor.respond(extras);
183         } catch (RemoteException e) {
184             // the system kills processes that are using remote cursors when the provider process
185             // is killed, but this can still happen if this is being called from the system process,
186             // so, better to log and return an empty bundle.
187             Log.w(TAG, "respond() threw RemoteException, returning an empty bundle.", e);
188             return Bundle.EMPTY;
189         }
190     }
191 }
192