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 17 package com.android.phone; 18 19 import android.app.Service; 20 import android.content.Intent; 21 import com.android.internal.telephony.OperatorInfo; 22 import android.os.AsyncResult; 23 import android.os.Binder; 24 import android.os.Handler; 25 import android.os.IBinder; 26 import android.os.Message; 27 import android.os.RemoteCallbackList; 28 import android.os.RemoteException; 29 import com.android.internal.telephony.Phone; 30 import com.android.internal.telephony.PhoneFactory; 31 import android.util.Log; 32 33 import java.util.ArrayList; 34 35 /** 36 * Service code used to assist in querying the network for service 37 * availability. 38 */ 39 public 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<OperatorInfo>) 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