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 } 124 } 125 listenForCellLocation()126 private CellLocation listenForCellLocation() { 127 LinkedBlockingQueue<CellLocation> queue = new LinkedBlockingQueue<>(); 128 HandlerThread handlerThread = new HandlerThread("Telephony location CTS"); 129 handlerThread.start(); 130 Executor executor = new Handler(handlerThread.getLooper())::post; 131 PhoneStateListener listener = new PhoneStateListener(executor) { 132 @Override 133 public void onCellLocationChanged(CellLocation cellLocation) { 134 if (cellLocation == null) return; 135 queue.offer(cellLocation); 136 } 137 }; 138 139 mTelephonyManager.listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE); 140 141 try { 142 return queue.poll(LISTENER_TIMEOUT, TimeUnit.MILLISECONDS); 143 } catch (InterruptedException e) { 144 throw new RuntimeException("interrupted"); 145 } finally { 146 handlerThread.quit(); 147 } 148 } 149 listenForServiceState(TelephonyManager telephonyManager)150 public static ServiceState listenForServiceState(TelephonyManager telephonyManager) { 151 LinkedBlockingQueue<ServiceState> queue = new LinkedBlockingQueue<>(); 152 HandlerThread handlerThread = new HandlerThread("Telephony location CTS"); 153 handlerThread.start(); 154 Executor executor = new Handler(handlerThread.getLooper())::post; 155 PhoneStateListener listener = new PhoneStateListener(executor) { 156 @Override 157 public void onServiceStateChanged(ServiceState ss) { 158 if (ss == null) ss = new ServiceState(); 159 queue.offer(ss); 160 } 161 }; 162 163 telephonyManager.listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE); 164 165 try { 166 ServiceState ss = queue.poll(LISTENER_TIMEOUT, TimeUnit.MILLISECONDS); 167 if (ss == null) { 168 throw new RuntimeException("Timed out waiting for service state"); 169 } 170 return ss; 171 } catch (InterruptedException e) { 172 throw new RuntimeException("interrupted"); 173 } finally { 174 handlerThread.quit(); 175 } 176 } 177 requestCellInfoUpdate()178 private List<CellInfo> requestCellInfoUpdate() { 179 LinkedBlockingQueue<List<CellInfo>> queue = new LinkedBlockingQueue<>(); 180 HandlerThread handlerThread = new HandlerThread("Telephony location CTS"); 181 handlerThread.start(); 182 Executor executor = new Handler(handlerThread.getLooper())::post; 183 TelephonyManager.CellInfoCallback cb = new TelephonyManager.CellInfoCallback() { 184 @Override 185 public void onCellInfo(List<CellInfo> cellInfo) { 186 queue.offer(cellInfo); 187 } 188 }; 189 190 mTelephonyManager.requestCellInfoUpdate(executor, cb); 191 192 try { 193 List<CellInfo> ci = queue.poll(LISTENER_TIMEOUT, TimeUnit.MILLISECONDS); 194 if (ci == null) { 195 throw new RuntimeException("Timed out waiting for cell info"); 196 } 197 return ci; 198 } catch (InterruptedException e) { 199 throw new RuntimeException("interrupted"); 200 } finally { 201 handlerThread.quit(); 202 } 203 } 204 } 205