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