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