Home | History | Annotate | Download | only in lowpan
      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