| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.net.lowpan; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.net.IpPrefix; |
| import android.os.DeadObjectException; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.RemoteException; |
| |
| /** |
| * Commissioning Session. |
| * |
| * <p>This class enables a device to learn the credential needed to join a network using a technique |
| * called "in-band commissioning". |
| * |
| * @hide |
| */ |
| // @SystemApi |
| public class LowpanCommissioningSession { |
| |
| private final ILowpanInterface mBinder; |
| private final LowpanBeaconInfo mBeaconInfo; |
| private final ILowpanInterfaceListener mInternalCallback = new InternalCallback(); |
| private final Looper mLooper; |
| private Handler mHandler; |
| private Callback mCallback = null; |
| private volatile boolean mIsClosed = false; |
| |
| /** |
| * Callback base class for {@link LowpanCommissioningSession} |
| * |
| * @hide |
| */ |
| // @SystemApi |
| public abstract static class Callback { |
| public void onReceiveFromCommissioner(@NonNull byte[] packet) {}; |
| |
| public void onClosed() {}; |
| } |
| |
| private class InternalCallback extends ILowpanInterfaceListener.Stub { |
| @Override |
| public void onStateChanged(String value) { |
| if (!mIsClosed) { |
| switch (value) { |
| case ILowpanInterface.STATE_OFFLINE: |
| case ILowpanInterface.STATE_FAULT: |
| synchronized (LowpanCommissioningSession.this) { |
| lockedCleanup(); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void onReceiveFromCommissioner(byte[] packet) { |
| mHandler.post( |
| () -> { |
| synchronized (LowpanCommissioningSession.this) { |
| if (!mIsClosed && (mCallback != null)) { |
| mCallback.onReceiveFromCommissioner(packet); |
| } |
| } |
| }); |
| } |
| |
| // We ignore all other callbacks. |
| @Override |
| public void onEnabledChanged(boolean value) {} |
| |
| @Override |
| public void onConnectedChanged(boolean value) {} |
| |
| @Override |
| public void onUpChanged(boolean value) {} |
| |
| @Override |
| public void onRoleChanged(String value) {} |
| |
| @Override |
| public void onLowpanIdentityChanged(LowpanIdentity value) {} |
| |
| @Override |
| public void onLinkNetworkAdded(IpPrefix value) {} |
| |
| @Override |
| public void onLinkNetworkRemoved(IpPrefix value) {} |
| |
| @Override |
| public void onLinkAddressAdded(String value) {} |
| |
| @Override |
| public void onLinkAddressRemoved(String value) {} |
| } |
| |
| LowpanCommissioningSession( |
| ILowpanInterface binder, LowpanBeaconInfo beaconInfo, Looper looper) { |
| mBinder = binder; |
| mBeaconInfo = beaconInfo; |
| mLooper = looper; |
| |
| if (mLooper != null) { |
| mHandler = new Handler(mLooper); |
| } else { |
| mHandler = new Handler(); |
| } |
| |
| try { |
| mBinder.addListener(mInternalCallback); |
| |
| } catch (RemoteException x) { |
| throw x.rethrowAsRuntimeException(); |
| } |
| } |
| |
| private void lockedCleanup() { |
| // Note: this method is only called from synchronized contexts. |
| |
| if (!mIsClosed) { |
| try { |
| mBinder.removeListener(mInternalCallback); |
| |
| } catch (DeadObjectException x) { |
| /* We don't care if we receive a DOE at this point. |
| * DOE is as good as success as far as we are concerned. |
| */ |
| |
| } catch (RemoteException x) { |
| throw x.rethrowAsRuntimeException(); |
| } |
| |
| if (mCallback != null) { |
| mHandler.post(() -> mCallback.onClosed()); |
| } |
| } |
| |
| mCallback = null; |
| mIsClosed = true; |
| } |
| |
| /** TODO: doc */ |
| @NonNull |
| public LowpanBeaconInfo getBeaconInfo() { |
| return mBeaconInfo; |
| } |
| |
| /** TODO: doc */ |
| public void sendToCommissioner(@NonNull byte[] packet) { |
| if (!mIsClosed) { |
| try { |
| mBinder.sendToCommissioner(packet); |
| |
| } catch (DeadObjectException x) { |
| /* This method is a best-effort delivery. |
| * We don't care if we receive a DOE at this point. |
| */ |
| |
| } catch (RemoteException x) { |
| throw x.rethrowAsRuntimeException(); |
| } |
| } |
| } |
| |
| /** TODO: doc */ |
| public synchronized void setCallback(@Nullable Callback cb, @Nullable Handler handler) { |
| if (!mIsClosed) { |
| /* This class can be created with or without a default looper. |
| * Also, this method can be called with or without a specific |
| * handler. If a handler is specified, it is to always be used. |
| * Otherwise, if there was a Looper specified when this object |
| * was created, we create a new handle based on that looper. |
| * Otherwise we just create a default handler object. Since we |
| * don't really know how the previous handler was created, we |
| * end up always replacing it here. This isn't a huge problem |
| * because this method should be called infrequently. |
| */ |
| if (handler != null) { |
| mHandler = handler; |
| } else if (mLooper != null) { |
| mHandler = new Handler(mLooper); |
| } else { |
| mHandler = new Handler(); |
| } |
| mCallback = cb; |
| } |
| } |
| |
| /** TODO: doc */ |
| public synchronized void close() { |
| if (!mIsClosed) { |
| try { |
| mBinder.closeCommissioningSession(); |
| |
| lockedCleanup(); |
| |
| } catch (DeadObjectException x) { |
| /* We don't care if we receive a DOE at this point. |
| * DOE is as good as success as far as we are concerned. |
| */ |
| |
| } catch (RemoteException x) { |
| throw x.rethrowAsRuntimeException(); |
| } |
| } |
| } |
| } |