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 17package android.net.lowpan; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.os.Handler; 22import android.os.RemoteException; 23import android.os.ServiceSpecificException; 24import java.util.ArrayList; 25import java.util.Collection; 26import java.util.HashMap; 27import java.util.Map; 28 29/** 30 * LoWPAN Scanner 31 * 32 * <p>This class allows performing network (active) scans and energy (passive) scans. 33 * 34 * @see LowpanInterface 35 * @hide 36 */ 37// @SystemApi 38public class LowpanScanner { 39 private static final String TAG = LowpanScanner.class.getSimpleName(); 40 41 // Public Classes 42 43 /** 44 * Callback base class for LowpanScanner 45 * 46 * @hide 47 */ 48 // @SystemApi 49 public abstract static class Callback { 50 public void onNetScanBeacon(LowpanBeaconInfo beacon) {} 51 52 public void onEnergyScanResult(LowpanEnergyScanResult result) {} 53 54 public void onScanFinished() {} 55 } 56 57 // Instance Variables 58 59 private ILowpanInterface mBinder; 60 private Callback mCallback = null; 61 private Handler mHandler = null; 62 private ArrayList<Integer> mChannelMask = null; 63 private int mTxPower = Integer.MAX_VALUE; 64 65 // Constructors/Accessors and Exception Glue 66 67 LowpanScanner(@NonNull ILowpanInterface binder) { 68 mBinder = binder; 69 } 70 71 /** Sets an instance of {@link LowpanScanner.Callback} to receive events. */ 72 public synchronized void setCallback(@Nullable Callback cb, @Nullable Handler handler) { 73 mCallback = cb; 74 mHandler = handler; 75 } 76 77 /** Sets an instance of {@link LowpanScanner.Callback} to receive events. */ 78 public void setCallback(@Nullable Callback cb) { 79 setCallback(cb, null); 80 } 81 82 /** 83 * Sets the channel mask to use when scanning. 84 * 85 * @param mask The channel mask to use when scanning. If <code>null</code>, any previously set 86 * channel mask will be cleared and all channels not masked by the current regulatory zone 87 * will be scanned. 88 */ 89 public void setChannelMask(@Nullable Collection<Integer> mask) { 90 if (mask == null) { 91 mChannelMask = null; 92 } else { 93 if (mChannelMask == null) { 94 mChannelMask = new ArrayList<>(); 95 } else { 96 mChannelMask.clear(); 97 } 98 mChannelMask.addAll(mask); 99 } 100 } 101 102 /** 103 * Gets the current channel mask. 104 * 105 * @return the current channel mask, or <code>null</code> if no channel mask is currently set. 106 */ 107 public @Nullable Collection<Integer> getChannelMask() { 108 return (Collection<Integer>) mChannelMask.clone(); 109 } 110 111 /** 112 * Adds a channel to the channel mask used for scanning. 113 * 114 * <p>If a channel mask was previously <code>null</code>, a new one is created containing only 115 * this channel. May be called multiple times to add additional channels ot the channel mask. 116 * 117 * @see #setChannelMask 118 * @see #getChannelMask 119 * @see #getTxPower 120 */ 121 public void addChannel(int channel) { 122 if (mChannelMask == null) { 123 mChannelMask = new ArrayList<>(); 124 } 125 mChannelMask.add(Integer.valueOf(channel)); 126 } 127 128 /** 129 * Sets the maximum transmit power to be used for active scanning. 130 * 131 * <p>The actual transmit power used is the lesser of this value and the currently configured 132 * maximum transmit power for the interface. 133 * 134 * @see #getTxPower 135 */ 136 public void setTxPower(int txPower) { 137 mTxPower = txPower; 138 } 139 140 /** 141 * Gets the maximum transmit power used for active scanning. 142 * 143 * @see #setTxPower 144 */ 145 public int getTxPower() { 146 return mTxPower; 147 } 148 149 private Map<String, Object> createScanOptionMap() { 150 Map<String, Object> map = new HashMap(); 151 152 if (mChannelMask != null) { 153 LowpanProperties.KEY_CHANNEL_MASK.putInMap( 154 map, mChannelMask.stream().mapToInt(i -> i).toArray()); 155 } 156 157 if (mTxPower != Integer.MAX_VALUE) { 158 LowpanProperties.KEY_MAX_TX_POWER.putInMap(map, Integer.valueOf(mTxPower)); 159 } 160 161 return map; 162 } 163 164 /** 165 * Start a network scan. 166 * 167 * <p>This method will return once the scan has started. 168 * 169 * @see #stopNetScan 170 */ 171 public void startNetScan() throws LowpanException { 172 Map<String, Object> map = createScanOptionMap(); 173 174 ILowpanNetScanCallback binderListener = 175 new ILowpanNetScanCallback.Stub() { 176 public void onNetScanBeacon(LowpanBeaconInfo beaconInfo) { 177 Callback callback; 178 Handler handler; 179 180 synchronized (LowpanScanner.this) { 181 callback = mCallback; 182 handler = mHandler; 183 } 184 185 if (callback == null) { 186 return; 187 } 188 189 Runnable runnable = () -> callback.onNetScanBeacon(beaconInfo); 190 191 if (handler != null) { 192 handler.post(runnable); 193 } else { 194 runnable.run(); 195 } 196 } 197 198 public void onNetScanFinished() { 199 Callback callback; 200 Handler handler; 201 202 synchronized (LowpanScanner.this) { 203 callback = mCallback; 204 handler = mHandler; 205 } 206 207 if (callback == null) { 208 return; 209 } 210 211 Runnable runnable = () -> callback.onScanFinished(); 212 213 if (handler != null) { 214 handler.post(runnable); 215 } else { 216 runnable.run(); 217 } 218 } 219 }; 220 221 try { 222 mBinder.startNetScan(map, binderListener); 223 224 } catch (RemoteException x) { 225 throw x.rethrowAsRuntimeException(); 226 227 } catch (ServiceSpecificException x) { 228 throw LowpanException.rethrowFromServiceSpecificException(x); 229 } 230 } 231 232 /** 233 * Stop a network scan currently in progress. 234 * 235 * @see #startNetScan 236 */ 237 public void stopNetScan() { 238 try { 239 mBinder.stopNetScan(); 240 241 } catch (RemoteException x) { 242 throw x.rethrowAsRuntimeException(); 243 } 244 } 245 246 /** 247 * Start an energy scan. 248 * 249 * <p>This method will return once the scan has started. 250 * 251 * @see #stopEnergyScan 252 */ 253 public void startEnergyScan() throws LowpanException { 254 Map<String, Object> map = createScanOptionMap(); 255 256 ILowpanEnergyScanCallback binderListener = 257 new ILowpanEnergyScanCallback.Stub() { 258 public void onEnergyScanResult(int channel, int rssi) { 259 Callback callback = mCallback; 260 Handler handler = mHandler; 261 262 if (callback == null) { 263 return; 264 } 265 266 Runnable runnable = 267 () -> { 268 if (callback != null) { 269 LowpanEnergyScanResult result = 270 new LowpanEnergyScanResult(); 271 result.setChannel(channel); 272 result.setMaxRssi(rssi); 273 callback.onEnergyScanResult(result); 274 } 275 }; 276 277 if (handler != null) { 278 handler.post(runnable); 279 } else { 280 runnable.run(); 281 } 282 } 283 284 public void onEnergyScanFinished() { 285 Callback callback = mCallback; 286 Handler handler = mHandler; 287 288 if (callback == null) { 289 return; 290 } 291 292 Runnable runnable = () -> callback.onScanFinished(); 293 294 if (handler != null) { 295 handler.post(runnable); 296 } else { 297 runnable.run(); 298 } 299 } 300 }; 301 302 try { 303 mBinder.startEnergyScan(map, binderListener); 304 305 } catch (RemoteException x) { 306 throw x.rethrowAsRuntimeException(); 307 308 } catch (ServiceSpecificException x) { 309 throw LowpanException.rethrowFromServiceSpecificException(x); 310 } 311 } 312 313 /** 314 * Stop an energy scan currently in progress. 315 * 316 * @see #startEnergyScan 317 */ 318 public void stopEnergyScan() { 319 try { 320 mBinder.stopEnergyScan(); 321 322 } catch (RemoteException x) { 323 throw x.rethrowAsRuntimeException(); 324 } 325 } 326} 327