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