blob: be8c78e4c1a39891379c5469439787c2ddcae520 [file] [log] [blame]
/*
* 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);
}
}