SupplicantStaIfaceHal.java revision 240671db659a4f7ca7e217d41c7aee9d85e22c33
1240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne/* 2240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * Copyright (C) 2016 The Android Open Source Project 3240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * 4240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * Licensed under the Apache License, Version 2.0 (the "License"); 5240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * you may not use this file except in compliance with the License. 6240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * You may obtain a copy of the License at 7240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * 8240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * http://www.apache.org/licenses/LICENSE-2.0 9240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * 10240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * Unless required by applicable law or agreed to in writing, software 11240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * distributed under the License is distributed on an "AS IS" BASIS, 12240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * See the License for the specific language governing permissions and 14240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * limitations under the License. 15240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne */ 16240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhnepackage com.android.server.wifi; 17240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne 18240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhneimport android.hardware.wifi.supplicant.V1_0.ISupplicant; 19240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhneimport android.hardware.wifi.supplicant.V1_0.ISupplicantIface; 20240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhneimport android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIface; 21240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhneimport android.hardware.wifi.supplicant.V1_0.ISupplicantStaIface; 22240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhneimport android.hardware.wifi.supplicant.V1_0.IfaceType; 23240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhneimport android.hardware.wifi.supplicant.V1_0.SupplicantStatus; 24240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhneimport android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode; 25240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhneimport android.hidl.manager.V1_0.IServiceManager; 26240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhneimport android.hidl.manager.V1_0.IServiceNotification; 27240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhneimport android.os.RemoteException; 28240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhneimport android.util.Log; 29240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne 30240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhneimport java.util.ArrayList; 31240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne/** 32240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * Hal calls for bring up/shut down of the supplicant daemon and for 33240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * sending requests to the supplicant daemon 34240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * 35240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * {@hide} 36240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne */ 37240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhnepublic class WifiSupplicantHal { 38240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne private static final boolean DBG = false; 39240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne private static final String TAG = "WifiSupplicantHal"; 40240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne // Supplicant HAL HIDL interface objects 41240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne private ISupplicant mHidlSupplicant; 42240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne private ISupplicantStaIface mHidlSupplicantStaIface; 43240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne private ISupplicantP2pIface mHidlSupplicantP2pIface; 44240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne private final Object mLock = new Object(); 45240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne 46240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne /** 47240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * Registers a service notification for the ISupplicant service, which gets the service, 48240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * ISupplicantStaIface and ISupplicantP2pIface. 49240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * @return true if the service notification was successfully registered 50240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne */ 51240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne public boolean initialize() { 52240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.i(TAG, "Registering SupplicantHidl service ready callback."); 53240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne synchronized (mLock) { 54240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne mHidlSupplicant = null; 55240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne mHidlSupplicantStaIface = null; 56240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne mHidlSupplicantP2pIface = null; 57240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne try { 58240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne final IServiceManager serviceManager = IServiceManager.getService("manager"); 59240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne if (serviceManager == null) { 60240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "Failed to get HIDL Service Manager"); 61240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return false; 62240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 63240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne if (!serviceManager.linkToDeath(cookie -> { 64240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.wtf(TAG, "IServiceManager died: cookie=" + cookie); 65240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne synchronized (mLock) { 66240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne supplicantServiceDiedHandler(); 67240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 68240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne }, 0)) { 69240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.wtf(TAG, "Error on linkToDeath on IServiceManager"); 70240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne supplicantServiceDiedHandler(); 71240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return false; 72240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 73240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne IServiceNotification serviceNotificationCb = new IServiceNotification.Stub() { 74240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne public void onRegistration(String fqName, String name, boolean preexisting) { 75240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName + ", " 76240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne + name + " preexisting=" + preexisting); 77240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne if (!getSupplicantService() || !getSupplicantStaIface() 78240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne || !getSupplicantP2pIface()) { 79240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "initalizing ISupplicantIfaces failed."); 80240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne supplicantServiceDiedHandler(); 81240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 82240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.i(TAG, "Completed initialization of ISupplicant service and Ifaces!"); 83240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 84240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne }; 85240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne /* TODO(b/33639391) : Use the new ISupplicant.registerForNotifications() once it 86240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne exists */ 87240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne if (!serviceManager.registerForNotifications(ISupplicant.kInterfaceName, 88240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne "", serviceNotificationCb)) { 89240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "Failed to register for notifications to " 90240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne + ISupplicant.kInterfaceName); 91240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return false; 92240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 93240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } catch (RemoteException e) { 94240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: " 95240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne + e); 96240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 97240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return true; 98240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 99240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 100240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne 101240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne private boolean getSupplicantService() { 102240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne synchronized (mLock) { 103240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne try { 104240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne mHidlSupplicant = ISupplicant.getService(); 105240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } catch (RemoteException e) { 106240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "ISupplicant.getService exception: " + e); 107240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return false; 108240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 109240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne if (mHidlSupplicant == null) { 110240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup"); 111240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return false; 112240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 113240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 114240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return true; 115240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 116240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne 117240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne /** 118240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne * @return ISupplicantIface of the requested type 119240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne */ 120240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne private ISupplicantIface getSupplicantIface(int ifaceType) { 121240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne synchronized (mLock) { 122240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne /** List all supplicant Ifaces */ 123240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList<>(); 124240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne try { 125240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne mHidlSupplicant.listInterfaces((SupplicantStatus status, 126240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne ArrayList<ISupplicant.IfaceInfo> ifaces) -> { 127240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne if (status.code != SupplicantStatusCode.SUCCESS) { 128240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "Getting Supplicant Interfaces failed: " + status.code); 129240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return; 130240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 131240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne supplicantIfaces.addAll(ifaces); 132240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne }); 133240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } catch (RemoteException e) { 134240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "ISupplicant.listInterfaces exception: " + e); 135240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return null; 136240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 137240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne if (supplicantIfaces.size() == 0) { 138240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup."); 139240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return null; 140240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 141240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne /** Get the STA iface */ 142240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne boolean hasStaIface = false; 143240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Mutable<ISupplicantIface> supplicantIface = new Mutable<>(); 144240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) { 145240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne if (ifaceInfo.type == ifaceType) { 146240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne hasStaIface = true; 147240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne try { 148240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne mHidlSupplicant.getInterface(ifaceInfo, 149240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne (SupplicantStatus status, ISupplicantIface iface) -> { 150240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne if (status.code != SupplicantStatusCode.SUCCESS) { 151240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "Failed to get ISupplicantIface " + status.code); 152240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return; 153240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 154240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne supplicantIface.value = iface; 155240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne }); 156240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } catch (RemoteException e) { 157240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "ISupplicant.getInterface exception: " + e); 158240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return null; 159240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 160240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne break; 161240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 162240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 163240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne if (!hasStaIface) { 164240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "No ISupplicantIface matching requested type: " + ifaceType + ", got " 165240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne + supplicantIfaces.size() + " ifaces."); 166240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 167240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return supplicantIface.value; 168240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 169240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 170240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne 171240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne private boolean getSupplicantStaIface() { 172240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne synchronized (mLock) { 173240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne /** Cast ISupplicantIface into ISupplicantStaIface*/ 174240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne ISupplicantIface supplicantIface = getSupplicantIface(IfaceType.STA); 175240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne if (supplicantIface == null) { 176240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "getSupplicantStaIface failed"); 177240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return false; 178240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 179240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne mHidlSupplicantStaIface = 180240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne ISupplicantStaIface.asInterface(supplicantIface.asBinder()); 181240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return true; 182240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 183240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 184240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne 185240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne private boolean getSupplicantP2pIface() { 186240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne synchronized (mLock) { 187240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne /** Cast ISupplicantIface into ISupplicantStaIface*/ 188240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne ISupplicantIface supplicantIface = getSupplicantIface(IfaceType.P2P); 189240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne if (supplicantIface == null) { 190240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Log.e(TAG, "getSupplicantP2pIface failed"); 191240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return false; 192240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 193240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne mHidlSupplicantP2pIface = 194240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne ISupplicantP2pIface.asInterface(supplicantIface.asBinder()); 195240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne return true; 196240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 197240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 198240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne 199240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne private void supplicantServiceDiedHandler() { 200240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne synchronized (mLock) { 201240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne mHidlSupplicant = null; 202240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne mHidlSupplicantStaIface = null; 203240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne mHidlSupplicantP2pIface = null; 204240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 205240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 206240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne 207240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne private static class Mutable<E> { 208240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne public E value; 209240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne 210240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Mutable() { 211240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne value = null; 212240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 213240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne 214240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne Mutable(E value) { 215240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne this.value = value; 216240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 217240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne } 218240671db659a4f7ca7e217d41c7aee9d85e22c33Glen Kuhne} 219