blob: e890acb36b48f361af5cd4d52438157b4ca68d67 [file] [log] [blame]
yinxufdfb6f42017-04-12 16:09:54 -07001/*
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
17package android.telephony;
18
19import static com.android.internal.util.Preconditions.checkNotNull;
20
Philip P. Moltmann95a41b82019-10-04 08:14:38 -070021import android.annotation.Nullable;
yinxufdfb6f42017-04-12 16:09:54 -070022import android.os.Binder;
yinxu56049432017-06-27 14:05:17 -070023import android.os.Bundle;
yinxufdfb6f42017-04-12 16:09:54 -070024import android.os.Handler;
25import android.os.HandlerThread;
26import android.os.Looper;
27import android.os.Message;
28import android.os.Messenger;
yinxu56049432017-06-27 14:05:17 -070029import android.os.Parcelable;
yinxufdfb6f42017-04-12 16:09:54 -070030import android.os.RemoteException;
yinxufdfb6f42017-04-12 16:09:54 -070031import android.util.SparseArray;
Hall Liuee313732018-11-27 14:36:51 -080032
Nathan Harold5a653282020-05-15 15:25:47 -070033import com.android.internal.annotations.GuardedBy;
Hall Liuee313732018-11-27 14:36:51 -080034import com.android.internal.telephony.ITelephony;
Meng Wang1dbea2f2020-01-30 12:25:08 -080035import com.android.telephony.Rlog;
Hall Liuee313732018-11-27 14:36:51 -080036
yinxu56049432017-06-27 14:05:17 -070037import java.util.Arrays;
yinxufdfb6f42017-04-12 16:09:54 -070038import java.util.List;
yinxu1abe9b12018-02-28 15:43:42 -080039import java.util.concurrent.Executor;
yinxufdfb6f42017-04-12 16:09:54 -070040
yinxufdfb6f42017-04-12 16:09:54 -070041/**
42 * Manages the radio access network scan requests and callbacks.
yinxufdfb6f42017-04-12 16:09:54 -070043 */
44public final class TelephonyScanManager {
45
46 private static final String TAG = "TelephonyScanManager";
47
48 /** @hide */
yinxu56049432017-06-27 14:05:17 -070049 public static final String SCAN_RESULT_KEY = "scanResult";
50
51 /** @hide */
yinxufdfb6f42017-04-12 16:09:54 -070052 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;
Hall Liuc9d74302019-02-28 15:29:19 -080057 /** @hide */
58 public static final int CALLBACK_RESTRICTED_SCAN_RESULTS = 4;
Nathan Harold5a653282020-05-15 15:25:47 -070059 /** @hide */
60 public static final int CALLBACK_TELEPHONY_DIED = 5;
Hall Liuc9d74302019-02-28 15:29:19 -080061
62 /** @hide */
63 public static final int INVALID_SCAN_ID = -1;
yinxufdfb6f42017-04-12 16:09:54 -070064
65 /**
yinxubbc3d362017-11-30 14:58:36 -080066 * The caller of
yinxu1abe9b12018-02-28 15:43:42 -080067 * {@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.
yinxufdfb6f42017-04-12 16:09:54 -070071 */
72 public static abstract class NetworkScanCallback {
73 /** Returns the scan results to the user, this callback will be called multiple times. */
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 */
82 public void onComplete() {}
83
84 /**
85 * Informs the user that there is some error about the scan.
86 *
yinxu392b0f12017-11-08 16:10:16 -080087 * 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.
yinxubbc3d362017-11-30 14:58:36 -080089 *
90 * @param error Error code when the scan is failed, as defined in {@link NetworkScan}.
yinxufdfb6f42017-04-12 16:09:54 -070091 */
yinxubbc3d362017-11-30 14:58:36 -080092 public void onError(@NetworkScan.ScanErrorCode int error) {}
yinxufdfb6f42017-04-12 16:09:54 -070093 }
94
95 private static class NetworkScanInfo {
96 private final NetworkScanRequest mRequest;
yinxu1abe9b12018-02-28 15:43:42 -080097 private final Executor mExecutor;
yinxufdfb6f42017-04-12 16:09:54 -070098 private final NetworkScanCallback mCallback;
99
yinxu1abe9b12018-02-28 15:43:42 -0800100 NetworkScanInfo(
101 NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
yinxufdfb6f42017-04-12 16:09:54 -0700102 mRequest = request;
yinxu1abe9b12018-02-28 15:43:42 -0800103 mExecutor = executor;
yinxufdfb6f42017-04-12 16:09:54 -0700104 mCallback = callback;
105 }
106 }
107
108 private final Looper mLooper;
Nathan Harold5a653282020-05-15 15:25:47 -0700109 private final Handler mHandler;
yinxufdfb6f42017-04-12 16:09:54 -0700110 private final Messenger mMessenger;
Hall Liu418e7792019-10-03 11:48:31 -0700111 private final SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>();
Nathan Harold5a653282020-05-15 15:25:47 -0700112 private final Binder.DeathRecipient mDeathRecipient;
yinxufdfb6f42017-04-12 16:09:54 -0700113
114 public TelephonyScanManager() {
115 HandlerThread thread = new HandlerThread(TAG);
116 thread.start();
117 mLooper = thread.getLooper();
Nathan Harold5a653282020-05-15 15:25:47 -0700118 mHandler = new Handler(mLooper) {
yinxufdfb6f42017-04-12 16:09:54 -0700119 @Override
120 public void handleMessage(Message message) {
121 checkNotNull(message, "message cannot be null");
Nathan Harold5a653282020-05-15 15:25:47 -0700122 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
yinxufdfb6f42017-04-12 16:09:54 -0700147 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;
yinxu1abe9b12018-02-28 15:43:42 -0800156 Executor executor = nsi.mExecutor;
yinxufdfb6f42017-04-12 16:09:54 -0700157 if (callback == null) {
158 throw new RuntimeException(
159 "Failed to find NetworkScanCallback with id " + message.arg2);
160 }
yinxu1abe9b12018-02-28 15:43:42 -0800161 if (executor == null) {
162 throw new RuntimeException(
163 "Failed to find Executor with id " + message.arg2);
164 }
yinxufdfb6f42017-04-12 16:09:54 -0700165
166 switch (message.what) {
Hall Liuc9d74302019-02-28 15:29:19 -0800167 case CALLBACK_RESTRICTED_SCAN_RESULTS:
yinxufdfb6f42017-04-12 16:09:54 -0700168 case CALLBACK_SCAN_RESULTS:
169 try {
yinxu56049432017-06-27 14:05:17 -0700170 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 }
Hall Liuc9d74302019-02-28 15:29:19 -0800176 executor.execute(() -> {
yinxu2e8d5ed2018-03-15 11:39:15 -0700177 Rlog.d(TAG, "onResults: " + ci.toString());
Hall Liuc9d74302019-02-28 15:29:19 -0800178 callback.onResults(Arrays.asList(ci));
yinxu2e8d5ed2018-03-15 11:39:15 -0700179 });
yinxufdfb6f42017-04-12 16:09:54 -0700180 } catch (Exception e) {
181 Rlog.e(TAG, "Exception in networkscan callback onResults", e);
182 }
183 break;
184 case CALLBACK_SCAN_ERROR:
185 try {
yinxu17dbd5b2018-03-12 14:07:49 -0700186 final int errorCode = message.arg1;
yinxu2e8d5ed2018-03-15 11:39:15 -0700187 executor.execute(() -> {
188 Rlog.d(TAG, "onError: " + errorCode);
189 callback.onError(errorCode);
190 });
Nathan Harold5a653282020-05-15 15:25:47 -0700191 synchronized (mScanInfo) {
192 mScanInfo.remove(message.arg2);
193 }
yinxufdfb6f42017-04-12 16:09:54 -0700194 } catch (Exception e) {
195 Rlog.e(TAG, "Exception in networkscan callback onError", e);
196 }
197 break;
198 case CALLBACK_SCAN_COMPLETE:
199 try {
yinxu2e8d5ed2018-03-15 11:39:15 -0700200 executor.execute(() -> {
201 Rlog.d(TAG, "onComplete");
202 callback.onComplete();
203 });
Nathan Harold5a653282020-05-15 15:25:47 -0700204 synchronized (mScanInfo) {
205 mScanInfo.remove(message.arg2);
206 }
yinxufdfb6f42017-04-12 16:09:54 -0700207 } 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 }
Nathan Harold5a653282020-05-15 15:25:47 -0700216 };
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 };
yinxufdfb6f42017-04-12 16:09:54 -0700224 }
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:
Nathan Harold5a653282020-05-15 15:25:47 -0700234 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and
yinxufdfb6f42017-04-12 16:09:54 -0700235 * {@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.
Philip P. Moltmann95a41b82019-10-04 08:14:38 -0700240 * @param callingPackage The package name of the caller
241 * @param callingFeatureId The feature id inside of the calling package
yinxufdfb6f42017-04-12 16:09:54 -0700242 * @return A NetworkScan obj which contains a callback which can stop the scan.
243 * @hide
244 */
245 public NetworkScan requestNetworkScan(int subId,
Hall Liuee313732018-11-27 14:36:51 -0800246 NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
Philip P. Moltmann95a41b82019-10-04 08:14:38 -0700247 String callingPackage, @Nullable String callingFeatureId) {
yinxufdfb6f42017-04-12 16:09:54 -0700248 try {
Nathan Harold5a653282020-05-15 15:25:47 -0700249 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);
yinxufdfb6f42017-04-12 16:09:54 -0700269 }
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
Nathan Harold5a653282020-05-15 15:25:47 -0700278 @GuardedBy("mScanInfo")
yinxu1abe9b12018-02-28 15:43:42 -0800279 private void saveScanInfo(
280 int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
Hall Liu418e7792019-10-03 11:48:31 -0700281 mScanInfo.put(id, new NetworkScanInfo(request, executor, callback));
yinxufdfb6f42017-04-12 16:09:54 -0700282 }
283
284 private ITelephony getITelephony() {
285 return ITelephony.Stub.asInterface(
Peter Wangd9eebca2019-12-30 16:14:01 -0800286 TelephonyFrameworkInitializer
287 .getTelephonyServiceManager()
288 .getTelephonyServiceRegisterer()
289 .get());
yinxufdfb6f42017-04-12 16:09:54 -0700290 }
291}