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