• 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.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