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