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.content.Context; 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.os.ServiceManager; 32 import android.util.SparseArray; 33 34 import com.android.internal.telephony.ITelephony; 35 36 import java.util.Arrays; 37 import java.util.List; 38 import java.util.concurrent.Executor; 39 40 /** 41 * Manages the radio access network scan requests and callbacks. 42 */ 43 public final class TelephonyScanManager { 44 45 private static final String TAG = "TelephonyScanManager"; 46 47 /** @hide */ 48 public static final String SCAN_RESULT_KEY = "scanResult"; 49 50 /** @hide */ 51 public static final int CALLBACK_SCAN_RESULTS = 1; 52 /** @hide */ 53 public static final int CALLBACK_SCAN_ERROR = 2; 54 /** @hide */ 55 public static final int CALLBACK_SCAN_COMPLETE = 3; 56 /** @hide */ 57 public static final int CALLBACK_RESTRICTED_SCAN_RESULTS = 4; 58 59 /** @hide */ 60 public static final int INVALID_SCAN_ID = -1; 61 62 /** 63 * The caller of 64 * {@link 65 * TelephonyManager#requestNetworkScan(NetworkScanRequest, Executor, NetworkScanCallback)} 66 * should implement and provide this callback so that the scan results or errors can be 67 * returned. 68 */ 69 public static abstract class NetworkScanCallback { 70 /** Returns the scan results to the user, this callback will be called multiple times. */ onResults(List<CellInfo> results)71 public void onResults(List<CellInfo> results) {} 72 73 /** 74 * Informs the user that the scan has stopped. 75 * 76 * This callback will be called when the scan is finished or cancelled by the user. 77 * The related NetworkScanRequest will be deleted after this callback. 78 */ onComplete()79 public void onComplete() {} 80 81 /** 82 * Informs the user that there is some error about the scan. 83 * 84 * This callback will be called whenever there is any error about the scan, and the scan 85 * will be terminated. onComplete() will NOT be called. 86 * 87 * @param error Error code when the scan is failed, as defined in {@link NetworkScan}. 88 */ onError(@etworkScan.ScanErrorCode int error)89 public void onError(@NetworkScan.ScanErrorCode int error) {} 90 } 91 92 private static class NetworkScanInfo { 93 private final NetworkScanRequest mRequest; 94 private final Executor mExecutor; 95 private final NetworkScanCallback mCallback; 96 NetworkScanInfo( NetworkScanRequest request, Executor executor, NetworkScanCallback callback)97 NetworkScanInfo( 98 NetworkScanRequest request, Executor executor, NetworkScanCallback callback) { 99 mRequest = request; 100 mExecutor = executor; 101 mCallback = callback; 102 } 103 } 104 105 private final Looper mLooper; 106 private final Messenger mMessenger; 107 private SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>(); 108 TelephonyScanManager()109 public TelephonyScanManager() { 110 HandlerThread thread = new HandlerThread(TAG); 111 thread.start(); 112 mLooper = thread.getLooper(); 113 mMessenger = new Messenger(new Handler(mLooper) { 114 @Override 115 public void handleMessage(Message message) { 116 checkNotNull(message, "message cannot be null"); 117 NetworkScanInfo nsi; 118 synchronized (mScanInfo) { 119 nsi = mScanInfo.get(message.arg2); 120 } 121 if (nsi == null) { 122 throw new RuntimeException( 123 "Failed to find NetworkScanInfo with id " + message.arg2); 124 } 125 NetworkScanCallback callback = nsi.mCallback; 126 Executor executor = nsi.mExecutor; 127 if (callback == null) { 128 throw new RuntimeException( 129 "Failed to find NetworkScanCallback with id " + message.arg2); 130 } 131 if (executor == null) { 132 throw new RuntimeException( 133 "Failed to find Executor with id " + message.arg2); 134 } 135 136 switch (message.what) { 137 case CALLBACK_RESTRICTED_SCAN_RESULTS: 138 case CALLBACK_SCAN_RESULTS: 139 try { 140 final Bundle b = message.getData(); 141 final Parcelable[] parcelables = b.getParcelableArray(SCAN_RESULT_KEY); 142 CellInfo[] ci = new CellInfo[parcelables.length]; 143 for (int i = 0; i < parcelables.length; i++) { 144 ci[i] = (CellInfo) parcelables[i]; 145 } 146 executor.execute(() -> { 147 Rlog.d(TAG, "onResults: " + ci.toString()); 148 callback.onResults(Arrays.asList(ci)); 149 }); 150 } catch (Exception e) { 151 Rlog.e(TAG, "Exception in networkscan callback onResults", e); 152 } 153 break; 154 case CALLBACK_SCAN_ERROR: 155 try { 156 final int errorCode = message.arg1; 157 executor.execute(() -> { 158 Rlog.d(TAG, "onError: " + errorCode); 159 callback.onError(errorCode); 160 }); 161 } catch (Exception e) { 162 Rlog.e(TAG, "Exception in networkscan callback onError", e); 163 } 164 break; 165 case CALLBACK_SCAN_COMPLETE: 166 try { 167 executor.execute(() -> { 168 Rlog.d(TAG, "onComplete"); 169 callback.onComplete(); 170 }); 171 mScanInfo.remove(message.arg2); 172 } catch (Exception e) { 173 Rlog.e(TAG, "Exception in networkscan callback onComplete", e); 174 } 175 break; 176 default: 177 Rlog.e(TAG, "Unhandled message " + Integer.toHexString(message.what)); 178 break; 179 } 180 } 181 }); 182 } 183 184 /** 185 * Request a network scan. 186 * 187 * This method is asynchronous, so the network scan results will be returned by callback. 188 * The returned NetworkScan will contain a callback method which can be used to stop the scan. 189 * 190 * <p> 191 * Requires Permission: 192 * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and 193 * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} 194 * Or the calling app has carrier privileges. @see #hasCarrierPrivileges 195 * 196 * @param request Contains all the RAT with bands/channels that need to be scanned. 197 * @param callback Returns network scan results or errors. 198 * @return A NetworkScan obj which contains a callback which can stop the scan. 199 * @hide 200 */ requestNetworkScan(int subId, NetworkScanRequest request, Executor executor, NetworkScanCallback callback, String callingPackage)201 public NetworkScan requestNetworkScan(int subId, 202 NetworkScanRequest request, Executor executor, NetworkScanCallback callback, 203 String callingPackage) { 204 try { 205 ITelephony telephony = getITelephony(); 206 if (telephony != null) { 207 int scanId = telephony.requestNetworkScan( 208 subId, request, mMessenger, new Binder(), callingPackage); 209 if (scanId == INVALID_SCAN_ID) { 210 Rlog.e(TAG, "Failed to initiate network scan"); 211 return null; 212 } 213 saveScanInfo(scanId, request, executor, callback); 214 return new NetworkScan(scanId, subId); 215 } 216 } catch (RemoteException ex) { 217 Rlog.e(TAG, "requestNetworkScan RemoteException", ex); 218 } catch (NullPointerException ex) { 219 Rlog.e(TAG, "requestNetworkScan NPE", ex); 220 } 221 return null; 222 } 223 saveScanInfo( int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback)224 private void saveScanInfo( 225 int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) { 226 synchronized (mScanInfo) { 227 mScanInfo.put(id, new NetworkScanInfo(request, executor, callback)); 228 } 229 } 230 getITelephony()231 private ITelephony getITelephony() { 232 return ITelephony.Stub.asInterface( 233 ServiceManager.getService(Context.TELEPHONY_SERVICE)); 234 } 235 } 236