Home | History | Annotate | Download | only in telephony
      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