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