1/*
2 * Copyright (C) 2008 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
17package com.android.phone;
18
19import android.app.Service;
20import android.content.Intent;
21import com.android.internal.telephony.gsm.NetworkInfo;
22import android.os.AsyncResult;
23import android.os.Binder;
24import android.os.Handler;
25import android.os.IBinder;
26import android.os.Message;
27import android.os.RemoteCallbackList;
28import android.os.RemoteException;
29import com.android.internal.telephony.Phone;
30import com.android.internal.telephony.PhoneFactory;
31import android.util.Log;
32
33import java.util.ArrayList;
34
35/**
36 * Service code used to assist in querying the network for service
37 * availability.
38 */
39public class NetworkQueryService extends Service {
40    // debug data
41    private static final String LOG_TAG = "NetworkQuery";
42    private static final boolean DBG = false;
43
44    // static events
45    private static final int EVENT_NETWORK_SCAN_COMPLETED = 100;
46
47    // static states indicating the query status of the service
48    private static final int QUERY_READY = -1;
49    private static final int QUERY_IS_RUNNING = -2;
50
51    // error statuses that will be retured in the callback.
52    public static final int QUERY_OK = 0;
53    public static final int QUERY_EXCEPTION = 1;
54
55    /** state of the query service */
56    private int mState;
57
58    /** local handle to the phone object */
59    private Phone mPhone;
60
61    /**
62     * Class for clients to access.  Because we know this service always
63     * runs in the same process as its clients, we don't need to deal with
64     * IPC.
65     */
66    public class LocalBinder extends Binder {
67        INetworkQueryService getService() {
68            return mBinder;
69        }
70    }
71    private final IBinder mLocalBinder = new LocalBinder();
72
73    /**
74     * Local handler to receive the network query compete callback
75     * from the RIL.
76     */
77    Handler mHandler = new Handler() {
78        @Override
79        public void handleMessage(Message msg) {
80            switch (msg.what) {
81                // if the scan is complete, broadcast the results.
82                // to all registerd callbacks.
83                case EVENT_NETWORK_SCAN_COMPLETED:
84                    if (DBG) log("scan completed, broadcasting results");
85                    broadcastQueryResults((AsyncResult) msg.obj);
86                    break;
87            }
88        }
89    };
90
91    /**
92     * List of callback objects, also used to synchronize access to
93     * itself and to changes in state.
94     */
95    final RemoteCallbackList<INetworkQueryServiceCallback> mCallbacks =
96        new RemoteCallbackList<INetworkQueryServiceCallback> ();
97
98    /**
99     * Implementation of the INetworkQueryService interface.
100     */
101    private final INetworkQueryService.Stub mBinder = new INetworkQueryService.Stub() {
102
103        /**
104         * Starts a query with a INetworkQueryServiceCallback object if
105         * one has not been started yet.  Ignore the new query request
106         * if the query has been started already.  Either way, place the
107         * callback object in the queue to be notified upon request
108         * completion.
109         */
110        public void startNetworkQuery(INetworkQueryServiceCallback cb) {
111            if (cb != null) {
112                // register the callback to the list of callbacks.
113                synchronized (mCallbacks) {
114                    mCallbacks.register(cb);
115                    if (DBG) log("registering callback " + cb.getClass().toString());
116
117                    switch (mState) {
118                        case QUERY_READY:
119                            // TODO: we may want to install a timeout here in case we
120                            // do not get a timely response from the RIL.
121                            mPhone.getAvailableNetworks(
122                                    mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED));
123                            mState = QUERY_IS_RUNNING;
124                            if (DBG) log("starting new query");
125                            break;
126
127                        // do nothing if we're currently busy.
128                        case QUERY_IS_RUNNING:
129                            if (DBG) log("query already in progress");
130                            break;
131                        default:
132                    }
133                }
134            }
135        }
136
137        /**
138         * Stops a query with a INetworkQueryServiceCallback object as
139         * a token.
140         */
141        public void stopNetworkQuery(INetworkQueryServiceCallback cb) {
142            // currently we just unregister the callback, since there is
143            // no way to tell the RIL to terminate the query request.
144            // This means that the RIL may still be busy after the stop
145            // request was made, but the state tracking logic ensures
146            // that the delay will only last for 1 request even with
147            // repeated button presses in the NetworkSetting activity.
148            if (cb != null) {
149                synchronized (mCallbacks) {
150                    if (DBG) log("unregistering callback " + cb.getClass().toString());
151                    mCallbacks.unregister(cb);
152                }
153            }
154        }
155    };
156
157    @Override
158    public void onCreate() {
159        mState = QUERY_READY;
160        mPhone = PhoneFactory.getDefaultPhone();
161    }
162
163    /**
164     * Required for service implementation.
165     */
166    @Override
167    public void onStart(Intent intent, int startId) {
168    }
169
170    /**
171     * Handle the bind request.
172     */
173    @Override
174    public IBinder onBind(Intent intent) {
175        // TODO: Currently, return only the LocalBinder instance.  If we
176        // end up requiring support for a remote binder, we will need to
177        // return mBinder as well, depending upon the intent.
178        if (DBG) log("binding service implementation");
179        return mLocalBinder;
180    }
181
182    /**
183     * Broadcast the results from the query to all registered callback
184     * objects.
185     */
186    private void broadcastQueryResults (AsyncResult ar) {
187        // reset the state.
188        synchronized (mCallbacks) {
189            mState = QUERY_READY;
190
191            // see if we need to do any work.
192            if (ar == null) {
193                if (DBG) log("AsyncResult is null.");
194                return;
195            }
196
197            // TODO: we may need greater accuracy here, but for now, just a
198            // simple status integer will suffice.
199            int exception = (ar.exception == null) ? QUERY_OK : QUERY_EXCEPTION;
200            if (DBG) log("AsyncResult has exception " + exception);
201
202            // Make the calls to all the registered callbacks.
203            for (int i = (mCallbacks.beginBroadcast() - 1); i >= 0; i--) {
204                INetworkQueryServiceCallback cb = mCallbacks.getBroadcastItem(i);
205                if (DBG) log("broadcasting results to " + cb.getClass().toString());
206                try {
207                    cb.onQueryComplete((ArrayList<NetworkInfo>) ar.result, exception);
208                } catch (RemoteException e) {
209                }
210            }
211
212            // finish up.
213            mCallbacks.finishBroadcast();
214        }
215    }
216
217    private static void log(String msg) {
218        Log.d(LOG_TAG, msg);
219    }
220}
221