• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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