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.net.lowpan; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.net.IpPrefix; 22 import android.os.DeadObjectException; 23 import android.os.Handler; 24 import android.os.Looper; 25 import android.os.RemoteException; 26 27 /** 28 * Commissioning Session. 29 * 30 * <p>This class enables a device to learn the credential needed to join a network using a technique 31 * called "in-band commissioning". 32 * 33 * @hide 34 */ 35 // @SystemApi 36 public class LowpanCommissioningSession { 37 38 private final ILowpanInterface mBinder; 39 private final LowpanBeaconInfo mBeaconInfo; 40 private final ILowpanInterfaceListener mInternalCallback = new InternalCallback(); 41 private final Looper mLooper; 42 private Handler mHandler; 43 private Callback mCallback = null; 44 private volatile boolean mIsClosed = false; 45 46 /** 47 * Callback base class for {@link LowpanCommissioningSession} 48 * 49 * @hide 50 */ 51 // @SystemApi 52 public abstract static class Callback { 53 public void onReceiveFromCommissioner(@NonNull byte[] packet) {}; 54 55 public void onClosed() {}; 56 } 57 58 private class InternalCallback extends ILowpanInterfaceListener.Stub { 59 @Override 60 public void onStateChanged(String value) { 61 if (!mIsClosed) { 62 switch (value) { 63 case ILowpanInterface.STATE_OFFLINE: 64 case ILowpanInterface.STATE_FAULT: 65 synchronized (LowpanCommissioningSession.this) { 66 lockedCleanup(); 67 } 68 } 69 } 70 } 71 72 @Override 73 public void onReceiveFromCommissioner(byte[] packet) { 74 mHandler.post( 75 () -> { 76 synchronized (LowpanCommissioningSession.this) { 77 if (!mIsClosed && (mCallback != null)) { 78 mCallback.onReceiveFromCommissioner(packet); 79 } 80 } 81 }); 82 } 83 84 // We ignore all other callbacks. 85 @Override 86 public void onEnabledChanged(boolean value) {} 87 88 @Override 89 public void onConnectedChanged(boolean value) {} 90 91 @Override 92 public void onUpChanged(boolean value) {} 93 94 @Override 95 public void onRoleChanged(String value) {} 96 97 @Override 98 public void onLowpanIdentityChanged(LowpanIdentity value) {} 99 100 @Override 101 public void onLinkNetworkAdded(IpPrefix value) {} 102 103 @Override 104 public void onLinkNetworkRemoved(IpPrefix value) {} 105 106 @Override 107 public void onLinkAddressAdded(String value) {} 108 109 @Override 110 public void onLinkAddressRemoved(String value) {} 111 } 112 113 LowpanCommissioningSession( 114 ILowpanInterface binder, LowpanBeaconInfo beaconInfo, Looper looper) { 115 mBinder = binder; 116 mBeaconInfo = beaconInfo; 117 mLooper = looper; 118 119 if (mLooper != null) { 120 mHandler = new Handler(mLooper); 121 } else { 122 mHandler = new Handler(); 123 } 124 125 try { 126 mBinder.addListener(mInternalCallback); 127 128 } catch (RemoteException x) { 129 throw x.rethrowAsRuntimeException(); 130 } 131 } 132 133 private void lockedCleanup() { 134 // Note: this method is only called from synchronized contexts. 135 136 if (!mIsClosed) { 137 try { 138 mBinder.removeListener(mInternalCallback); 139 140 } catch (DeadObjectException x) { 141 /* We don't care if we receive a DOE at this point. 142 * DOE is as good as success as far as we are concerned. 143 */ 144 145 } catch (RemoteException x) { 146 throw x.rethrowAsRuntimeException(); 147 } 148 149 if (mCallback != null) { 150 mHandler.post(() -> mCallback.onClosed()); 151 } 152 } 153 154 mCallback = null; 155 mIsClosed = true; 156 } 157 158 /** TODO: doc */ 159 @NonNull 160 public LowpanBeaconInfo getBeaconInfo() { 161 return mBeaconInfo; 162 } 163 164 /** TODO: doc */ 165 public void sendToCommissioner(@NonNull byte[] packet) { 166 if (!mIsClosed) { 167 try { 168 mBinder.sendToCommissioner(packet); 169 170 } catch (DeadObjectException x) { 171 /* This method is a best-effort delivery. 172 * We don't care if we receive a DOE at this point. 173 */ 174 175 } catch (RemoteException x) { 176 throw x.rethrowAsRuntimeException(); 177 } 178 } 179 } 180 181 /** TODO: doc */ 182 public synchronized void setCallback(@Nullable Callback cb, @Nullable Handler handler) { 183 if (!mIsClosed) { 184 /* This class can be created with or without a default looper. 185 * Also, this method can be called with or without a specific 186 * handler. If a handler is specified, it is to always be used. 187 * Otherwise, if there was a Looper specified when this object 188 * was created, we create a new handle based on that looper. 189 * Otherwise we just create a default handler object. Since we 190 * don't really know how the previous handler was created, we 191 * end up always replacing it here. This isn't a huge problem 192 * because this method should be called infrequently. 193 */ 194 if (handler != null) { 195 mHandler = handler; 196 } else if (mLooper != null) { 197 mHandler = new Handler(mLooper); 198 } else { 199 mHandler = new Handler(); 200 } 201 mCallback = cb; 202 } 203 } 204 205 /** TODO: doc */ 206 public synchronized void close() { 207 if (!mIsClosed) { 208 try { 209 mBinder.closeCommissioningSession(); 210 211 lockedCleanup(); 212 213 } catch (DeadObjectException x) { 214 /* We don't care if we receive a DOE at this point. 215 * DOE is as good as success as far as we are concerned. 216 */ 217 218 } catch (RemoteException x) { 219 throw x.rethrowAsRuntimeException(); 220 } 221 } 222 } 223 } 224