1 /* 2 * Copyright (C) 2019 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.cts.locationaccessingapp; 18 19 import android.app.Service; 20 import android.content.Intent; 21 import android.os.Bundle; 22 import android.os.Handler; 23 import android.os.HandlerThread; 24 import android.os.IBinder; 25 import android.os.RemoteException; 26 import android.telephony.CellInfo; 27 import android.telephony.CellLocation; 28 import android.telephony.PhoneStateListener; 29 import android.telephony.ServiceState; 30 import android.telephony.TelephonyManager; 31 import android.telephony.cdma.CdmaCellLocation; 32 import android.telephony.gsm.GsmCellLocation; 33 34 import java.util.Collections; 35 import java.util.List; 36 import java.util.concurrent.Executor; 37 import java.util.concurrent.LinkedBlockingQueue; 38 import java.util.concurrent.TimeUnit; 39 40 public class CtsLocationAccessService extends Service { 41 public static final String CONTROL_ACTION = 42 "android.telephony.cts.locationaccessingapp.ACTION_CONTROL"; 43 44 public static final String COMMAND_GET_SERVICE_STATE = "get_service_state"; 45 public static final String COMMAND_GET_CELL_INFO = "get_cell_info"; 46 public static final String COMMAND_GET_CELL_LOCATION = "get_cell_location"; 47 public static final String COMMAND_GET_SERVICE_STATE_FROM_LISTENER = 48 "get_service_state_from_listener"; 49 public static final String COMMAND_LISTEN_CELL_LOCATION = "listen_cell_location"; 50 public static final String COMMAND_LISTEN_CELL_INFO = "listen_cell_info"; 51 public static final String COMMAND_REQUEST_CELL_INFO_UPDATE = "request_cell_info_update"; 52 53 private static final long LISTENER_TIMEOUT = 1000; 54 55 private ICtsLocationAccessControl mBinder = new ICtsLocationAccessControl.Stub() { 56 @Override 57 public List performCommand(String command) throws RemoteException { 58 Object result = null; 59 switch (command) { 60 case COMMAND_GET_SERVICE_STATE: 61 result = mTelephonyManager.getServiceState(); 62 break; 63 case COMMAND_GET_CELL_INFO: 64 result = mTelephonyManager.getAllCellInfo(); 65 break; 66 case COMMAND_GET_CELL_LOCATION: 67 result = new Bundle(); 68 CellLocation cellLocation = mTelephonyManager.getCellLocation(); 69 if (cellLocation instanceof GsmCellLocation) { 70 ((GsmCellLocation) cellLocation).fillInNotifierBundle((Bundle) result); 71 } else if (cellLocation instanceof CdmaCellLocation) { 72 ((CdmaCellLocation) cellLocation).fillInNotifierBundle((Bundle) result); 73 } else if (cellLocation == null) { 74 result = null; 75 } else { 76 throw new RuntimeException("Unexpected celllocation type"); 77 } 78 break; 79 case COMMAND_GET_SERVICE_STATE_FROM_LISTENER: 80 result = listenForServiceState(mTelephonyManager); 81 break; 82 case COMMAND_LISTEN_CELL_INFO: 83 result = listenForCellInfo(); 84 break; 85 case COMMAND_LISTEN_CELL_LOCATION: 86 result = listenForCellLocation(); 87 break; 88 case COMMAND_REQUEST_CELL_INFO_UPDATE: 89 result = requestCellInfoUpdate(); 90 } 91 return Collections.singletonList(result); 92 } 93 }; 94 private TelephonyManager mTelephonyManager; 95 96 @Override onBind(Intent intent)97 public IBinder onBind(Intent intent) { 98 mTelephonyManager = getSystemService(TelephonyManager.class); 99 return mBinder.asBinder(); 100 } 101 listenForCellInfo()102 private List<CellInfo> listenForCellInfo() { 103 LinkedBlockingQueue<List<CellInfo>> queue = new LinkedBlockingQueue<>(); 104 HandlerThread handlerThread = new HandlerThread("Telephony location CTS"); 105 handlerThread.start(); 106 Executor executor = new Handler(handlerThread.getLooper())::post; 107 PhoneStateListener listener = new PhoneStateListener(executor) { 108 @Override 109 public void onCellInfoChanged(List<CellInfo> cis) { 110 if (cis == null) cis = Collections.emptyList(); 111 queue.offer(cis); 112 } 113 }; 114 115 mTelephonyManager.listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE); 116 117 try { 118 return queue.poll(LISTENER_TIMEOUT, TimeUnit.MILLISECONDS); 119 } catch (InterruptedException e) { 120 throw new RuntimeException("interrupted"); 121 } finally { 122 handlerThread.quit(); 123 mTelephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE); 124 } 125 } 126 listenForCellLocation()127 private CellLocation listenForCellLocation() { 128 LinkedBlockingQueue<CellLocation> queue = new LinkedBlockingQueue<>(); 129 HandlerThread handlerThread = new HandlerThread("Telephony location CTS"); 130 handlerThread.start(); 131 Executor executor = new Handler(handlerThread.getLooper())::post; 132 PhoneStateListener listener = new PhoneStateListener(executor) { 133 @Override 134 public void onCellLocationChanged(CellLocation cellLocation) { 135 if (cellLocation == null) return; 136 queue.offer(cellLocation); 137 } 138 }; 139 140 mTelephonyManager.listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE); 141 142 try { 143 return queue.poll(LISTENER_TIMEOUT, TimeUnit.MILLISECONDS); 144 } catch (InterruptedException e) { 145 throw new RuntimeException("interrupted"); 146 } finally { 147 handlerThread.quit(); 148 mTelephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE); 149 } 150 } 151 listenForServiceState(TelephonyManager telephonyManager)152 public static ServiceState listenForServiceState(TelephonyManager telephonyManager) { 153 LinkedBlockingQueue<ServiceState> queue = new LinkedBlockingQueue<>(); 154 HandlerThread handlerThread = new HandlerThread("Telephony location CTS"); 155 handlerThread.start(); 156 Executor executor = new Handler(handlerThread.getLooper())::post; 157 PhoneStateListener listener = new PhoneStateListener(executor) { 158 @Override 159 public void onServiceStateChanged(ServiceState ss) { 160 if (ss == null) ss = new ServiceState(); 161 queue.offer(ss); 162 } 163 }; 164 165 telephonyManager.listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE); 166 167 try { 168 ServiceState ss = queue.poll(LISTENER_TIMEOUT, TimeUnit.MILLISECONDS); 169 if (ss == null) { 170 throw new RuntimeException("Timed out waiting for service state"); 171 } 172 return ss; 173 } catch (InterruptedException e) { 174 throw new RuntimeException("interrupted"); 175 } finally { 176 handlerThread.quit(); 177 telephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE); 178 } 179 } 180 requestCellInfoUpdate()181 private List<CellInfo> requestCellInfoUpdate() { 182 LinkedBlockingQueue<List<CellInfo>> queue = new LinkedBlockingQueue<>(); 183 HandlerThread handlerThread = new HandlerThread("Telephony location CTS"); 184 handlerThread.start(); 185 Executor executor = new Handler(handlerThread.getLooper())::post; 186 TelephonyManager.CellInfoCallback cb = new TelephonyManager.CellInfoCallback() { 187 @Override 188 public void onCellInfo(List<CellInfo> cellInfo) { 189 queue.offer(cellInfo); 190 } 191 }; 192 193 mTelephonyManager.requestCellInfoUpdate(executor, cb); 194 195 try { 196 List<CellInfo> ci = queue.poll(LISTENER_TIMEOUT, TimeUnit.MILLISECONDS); 197 if (ci == null) { 198 throw new RuntimeException("Timed out waiting for cell info"); 199 } 200 return ci; 201 } catch (InterruptedException e) { 202 throw new RuntimeException("interrupted"); 203 } finally { 204 handlerThread.quit(); 205 } 206 } 207 } 208