/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.phone; import android.app.Service; import android.content.Intent; import com.android.internal.telephony.OperatorInfo; import android.os.AsyncResult; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneFactory; import android.util.Log; import java.util.ArrayList; /** * Service code used to assist in querying the network for service * availability. */ public class NetworkQueryService extends Service { // debug data private static final String LOG_TAG = "NetworkQuery"; private static final boolean DBG = false; // static events private static final int EVENT_NETWORK_SCAN_COMPLETED = 100; // static states indicating the query status of the service private static final int QUERY_READY = -1; private static final int QUERY_IS_RUNNING = -2; // error statuses that will be retured in the callback. public static final int QUERY_OK = 0; public static final int QUERY_EXCEPTION = 1; /** state of the query service */ private int mState; /** local handle to the phone object */ private Phone mPhone; /** * Class for clients to access. Because we know this service always * runs in the same process as its clients, we don't need to deal with * IPC. */ public class LocalBinder extends Binder { INetworkQueryService getService() { return mBinder; } } private final IBinder mLocalBinder = new LocalBinder(); /** * Local handler to receive the network query compete callback * from the RIL. */ Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { // if the scan is complete, broadcast the results. // to all registerd callbacks. case EVENT_NETWORK_SCAN_COMPLETED: if (DBG) log("scan completed, broadcasting results"); broadcastQueryResults((AsyncResult) msg.obj); break; } } }; /** * List of callback objects, also used to synchronize access to * itself and to changes in state. */ final RemoteCallbackList<INetworkQueryServiceCallback> mCallbacks = new RemoteCallbackList<INetworkQueryServiceCallback> (); /** * Implementation of the INetworkQueryService interface. */ private final INetworkQueryService.Stub mBinder = new INetworkQueryService.Stub() { /** * Starts a query with a INetworkQueryServiceCallback object if * one has not been started yet. Ignore the new query request * if the query has been started already. Either way, place the * callback object in the queue to be notified upon request * completion. */ public void startNetworkQuery(INetworkQueryServiceCallback cb) { if (cb != null) { // register the callback to the list of callbacks. synchronized (mCallbacks) { mCallbacks.register(cb); if (DBG) log("registering callback " + cb.getClass().toString()); switch (mState) { case QUERY_READY: // TODO: we may want to install a timeout here in case we // do not get a timely response from the RIL. mPhone.getAvailableNetworks( mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED)); mState = QUERY_IS_RUNNING; if (DBG) log("starting new query"); break; // do nothing if we're currently busy. case QUERY_IS_RUNNING: if (DBG) log("query already in progress"); break; default: } } } } /** * Stops a query with a INetworkQueryServiceCallback object as * a token. */ public void stopNetworkQuery(INetworkQueryServiceCallback cb) { // currently we just unregister the callback, since there is // no way to tell the RIL to terminate the query request. // This means that the RIL may still be busy after the stop // request was made, but the state tracking logic ensures // that the delay will only last for 1 request even with // repeated button presses in the NetworkSetting activity. if (cb != null) { synchronized (mCallbacks) { if (DBG) log("unregistering callback " + cb.getClass().toString()); mCallbacks.unregister(cb); } } } }; @Override public void onCreate() { mState = QUERY_READY; mPhone = PhoneFactory.getDefaultPhone(); } /** * Required for service implementation. */ @Override public void onStart(Intent intent, int startId) { } /** * Handle the bind request. */ @Override public IBinder onBind(Intent intent) { // TODO: Currently, return only the LocalBinder instance. If we // end up requiring support for a remote binder, we will need to // return mBinder as well, depending upon the intent. if (DBG) log("binding service implementation"); return mLocalBinder; } /** * Broadcast the results from the query to all registered callback * objects. */ private void broadcastQueryResults (AsyncResult ar) { // reset the state. synchronized (mCallbacks) { mState = QUERY_READY; // see if we need to do any work. if (ar == null) { if (DBG) log("AsyncResult is null."); return; } // TODO: we may need greater accuracy here, but for now, just a // simple status integer will suffice. int exception = (ar.exception == null) ? QUERY_OK : QUERY_EXCEPTION; if (DBG) log("AsyncResult has exception " + exception); // Make the calls to all the registered callbacks. for (int i = (mCallbacks.beginBroadcast() - 1); i >= 0; i--) { INetworkQueryServiceCallback cb = mCallbacks.getBroadcastItem(i); if (DBG) log("broadcasting results to " + cb.getClass().toString()); try { cb.onQueryComplete((ArrayList<OperatorInfo>) ar.result, exception); } catch (RemoteException e) { } } // finish up. mCallbacks.finishBroadcast(); } } private static void log(String msg) { Log.d(LOG_TAG, msg); } }