1 /* 2 * Copyright (C) 2017 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 android.telephony; 18 19 import static com.android.internal.util.Preconditions.checkNotNull; 20 21 import android.annotation.Nullable; 22 import android.os.Binder; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.os.Messenger; 29 import android.os.Parcelable; 30 import android.os.RemoteException; 31 import android.util.SparseArray; 32 33 import com.android.internal.annotations.GuardedBy; 34 import com.android.internal.telephony.ITelephony; 35 import com.android.telephony.Rlog; 36 37 import java.util.Arrays; 38 import java.util.List; 39 import java.util.concurrent.Executor; 40 41 /** 42 * Manages the radio access network scan requests and callbacks. 43 */ 44 public final class TelephonyScanManager { 45 46 private static final String TAG = "TelephonyScanManager"; 47 48 /** @hide */ 49 public static final String SCAN_RESULT_KEY = "scanResult"; 50 51 /** @hide */ 52 public static final int CALLBACK_SCAN_RESULTS = 1; 53 /** @hide */ 54 public static final int CALLBACK_SCAN_ERROR = 2; 55 /** @hide */ 56 public static final int CALLBACK_SCAN_COMPLETE = 3; 57 /** @hide */ 58 public static final int CALLBACK_RESTRICTED_SCAN_RESULTS = 4; 59 /** @hide */ 60 public static final int CALLBACK_TELEPHONY_DIED = 5; 61 62 /** @hide */ 63 public static final int INVALID_SCAN_ID = -1; 64 65 /** 66 * The caller of 67 * {@link 68 * TelephonyManager#requestNetworkScan(NetworkScanRequest, Executor, NetworkScanCallback)} 69 * should implement and provide this callback so that the scan results or errors can be 70 * returned. 71 */ 72 public static abstract class NetworkScanCallback { 73 /** Returns the scan results to the user, this callback will be called multiple times. */ onResults(List<CellInfo> results)74 public void onResults(List<CellInfo> results) {} 75 76 /** 77 * Informs the user that the scan has stopped. 78 * 79 * This callback will be called when the scan is finished or cancelled by the user. 80 * The related NetworkScanRequest will be deleted after this callback. 81 */ onComplete()82 public void onComplete() {} 83 84 /** 85 * Informs the user that there is some error about the scan. 86 * 87 * This callback will be called whenever there is any error about the scan, and the scan 88 * will be terminated. onComplete() will NOT be called. 89 * 90 * @param error Error code when the scan is failed, as defined in {@link NetworkScan}. 91 */ onError(@etworkScan.ScanErrorCode int error)92 public void onError(@NetworkScan.ScanErrorCode int error) {} 93 } 94 95 private static class NetworkScanInfo { 96 private final NetworkScanRequest mRequest; 97 private final Executor mExecutor; 98 private final NetworkScanCallback mCallback; 99 NetworkScanInfo( NetworkScanRequest request, Executor executor, NetworkScanCallback callback)100 NetworkScanInfo( 101 NetworkScanRequest request, Executor executor, NetworkScanCallback callback) { 102 mRequest = request; 103 mExecutor = executor; 104 mCallback = callback; 105 } 106 } 107 108 private final Looper mLooper; 109 private final Handler mHandler; 110 private final Messenger mMessenger; 111 private final SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>(); 112 private final Binder.DeathRecipient mDeathRecipient; 113 TelephonyScanManager()114 public TelephonyScanManager() { 115 HandlerThread thread = new HandlerThread(TAG); 116 thread.start(); 117 mLooper = thread.getLooper(); 118 mHandler = new Handler(mLooper) { 119 @Override 120 public void handleMessage(Message message) { 121 checkNotNull(message, "message cannot be null"); 122 if (message.what == CALLBACK_TELEPHONY_DIED) { 123 // If there are no objects in mScanInfo then binder death will simply return. 124 synchronized (mScanInfo) { 125 for (int i = 0; i < mScanInfo.size(); i++) { 126 NetworkScanInfo nsi = mScanInfo.valueAt(i); 127 // At this point we go into panic mode and ignore errors that would 128 // normally stop the show in order to try and clean up as gracefully 129 // as possible. 130 if (nsi == null) continue; // shouldn't be possible 131 Executor e = nsi.mExecutor; 132 NetworkScanCallback cb = nsi.mCallback; 133 if (e == null || cb == null) continue; 134 try { 135 e.execute( 136 () -> cb.onError(NetworkScan.ERROR_MODEM_UNAVAILABLE)); 137 } catch (java.util.concurrent.RejectedExecutionException ignore) { 138 // ignore so that we can continue 139 } 140 } 141 142 mScanInfo.clear(); 143 } 144 return; 145 } 146 147 NetworkScanInfo nsi; 148 synchronized (mScanInfo) { 149 nsi = mScanInfo.get(message.arg2); 150 } 151 if (nsi == null) { 152 throw new RuntimeException( 153 "Failed to find NetworkScanInfo with id " + message.arg2); 154 } 155 NetworkScanCallback callback = nsi.mCallback; 156 Executor executor = nsi.mExecutor; 157 if (callback == null) { 158 throw new RuntimeException( 159 "Failed to find NetworkScanCallback with id " + message.arg2); 160 } 161 if (executor == null) { 162 throw new RuntimeException( 163 "Failed to find Executor with id " + message.arg2); 164 } 165 166 switch (message.what) { 167 case CALLBACK_RESTRICTED_SCAN_RESULTS: 168 case CALLBACK_SCAN_RESULTS: 169 try { 170 final Bundle b = message.getData(); 171 final Parcelable[] parcelables = b.getParcelableArray(SCAN_RESULT_KEY); 172 CellInfo[] ci = new CellInfo[parcelables.length]; 173 for (int i = 0; i < parcelables.length; i++) { 174 ci[i] = (CellInfo) parcelables[i]; 175 } 176 executor.execute(() -> { 177 Rlog.d(TAG, "onResults: " + ci.toString()); 178 callback.onResults(Arrays.asList(ci)); 179 }); 180 } catch (Exception e) { 181 Rlog.e(TAG, "Exception in networkscan callback onResults", e); 182 } 183 break; 184 case CALLBACK_SCAN_ERROR: 185 try { 186 final int errorCode = message.arg1; 187 executor.execute(() -> { 188 Rlog.d(TAG, "onError: " + errorCode); 189 callback.onError(errorCode); 190 }); 191 synchronized (mScanInfo) { 192 mScanInfo.remove(message.arg2); 193 } 194 } catch (Exception e) { 195 Rlog.e(TAG, "Exception in networkscan callback onError", e); 196 } 197 break; 198 case CALLBACK_SCAN_COMPLETE: 199 try { 200 executor.execute(() -> { 201 Rlog.d(TAG, "onComplete"); 202 callback.onComplete(); 203 }); 204 synchronized (mScanInfo) { 205 mScanInfo.remove(message.arg2); 206 } 207 } catch (Exception e) { 208 Rlog.e(TAG, "Exception in networkscan callback onComplete", e); 209 } 210 break; 211 default: 212 Rlog.e(TAG, "Unhandled message " + Integer.toHexString(message.what)); 213 break; 214 } 215 } 216 }; 217 mMessenger = new Messenger(mHandler); 218 mDeathRecipient = new Binder.DeathRecipient() { 219 @Override 220 public void binderDied() { 221 mHandler.obtainMessage(CALLBACK_TELEPHONY_DIED).sendToTarget(); 222 } 223 }; 224 } 225 226 /** 227 * Request a network scan. 228 * 229 * This method is asynchronous, so the network scan results will be returned by callback. 230 * The returned NetworkScan will contain a callback method which can be used to stop the scan. 231 * 232 * <p> 233 * Requires Permission: 234 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and 235 * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} 236 * Or the calling app has carrier privileges. @see #hasCarrierPrivileges 237 * 238 * @param request Contains all the RAT with bands/channels that need to be scanned. 239 * @param callback Returns network scan results or errors. 240 * @param callingPackage The package name of the caller 241 * @param callingFeatureId The feature id inside of the calling package 242 * @return A NetworkScan obj which contains a callback which can stop the scan. 243 * @hide 244 */ requestNetworkScan(int subId, NetworkScanRequest request, Executor executor, NetworkScanCallback callback, String callingPackage, @Nullable String callingFeatureId)245 public NetworkScan requestNetworkScan(int subId, 246 NetworkScanRequest request, Executor executor, NetworkScanCallback callback, 247 String callingPackage, @Nullable String callingFeatureId) { 248 try { 249 final ITelephony telephony = getITelephony(); 250 if (telephony == null) return null; 251 252 int scanId = telephony.requestNetworkScan( 253 subId, request, mMessenger, new Binder(), callingPackage, 254 callingFeatureId); 255 if (scanId == INVALID_SCAN_ID) { 256 Rlog.e(TAG, "Failed to initiate network scan"); 257 return null; 258 } 259 synchronized (mScanInfo) { 260 // We link to death whenever a scan is started to ensure that we are linked 261 // at the point that phone process death might matter. 262 // We never unlink because: 263 // - Duplicate links to death with the same callback do not result in 264 // extraneous callbacks (the tracking de-dupes). 265 // - Receiving binderDeath() when no scans are active is a no-op. 266 telephony.asBinder().linkToDeath(mDeathRecipient, 0); 267 saveScanInfo(scanId, request, executor, callback); 268 return new NetworkScan(scanId, subId); 269 } 270 } catch (RemoteException ex) { 271 Rlog.e(TAG, "requestNetworkScan RemoteException", ex); 272 } catch (NullPointerException ex) { 273 Rlog.e(TAG, "requestNetworkScan NPE", ex); 274 } 275 return null; 276 } 277 278 @GuardedBy("mScanInfo") saveScanInfo( int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback)279 private void saveScanInfo( 280 int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) { 281 mScanInfo.put(id, new NetworkScanInfo(request, executor, callback)); 282 } 283 getITelephony()284 private ITelephony getITelephony() { 285 return ITelephony.Stub.asInterface( 286 TelephonyFrameworkInitializer 287 .getTelephonyServiceManager() 288 .getTelephonyServiceRegisterer() 289 .get()); 290 } 291 } 292