120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti/*
220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti * Copyright (C) 2014 The Android Open Source Project
320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti *
420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti * Licensed under the Apache License, Version 2.0 (the "License");
520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti * you may not use this file except in compliance with the License.
620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti * You may obtain a copy of the License at
720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti *
820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti *      http://www.apache.org/licenses/LICENSE-2.0
920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti *
1020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti * Unless required by applicable law or agreed to in writing, software
1120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti * distributed under the License is distributed on an "AS IS" BASIS,
1220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti * See the License for the specific language governing permissions and
1420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti * limitations under the License.
1520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti */
1620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
1720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittipackage com.android.server.ethernet;
1820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
1920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport android.content.Context;
2020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport android.content.pm.PackageManager;
2120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport android.net.IEthernetManager;
22a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kimimport android.net.IEthernetServiceListener;
2320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport android.net.IpConfiguration;
2420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport android.net.IpConfiguration.IpAssignment;
2520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport android.net.IpConfiguration.ProxySettings;
2620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport android.os.Binder;
2720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport android.os.Handler;
2820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport android.os.HandlerThread;
29a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kimimport android.os.RemoteCallbackList;
3020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport android.os.RemoteException;
3176502788598a85d24b9ae3e253016a9370c49a42Billy Lauimport android.provider.Settings;
3220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport android.util.Log;
3320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport android.util.PrintWriterPrinter;
3420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
35a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kimimport com.android.internal.util.IndentingPrintWriter;
36a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim
3720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport java.io.FileDescriptor;
3820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport java.io.PrintWriter;
3920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittiimport java.util.concurrent.atomic.AtomicBoolean;
4020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
4120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti/**
4220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti * EthernetServiceImpl handles remote Ethernet operation requests by implementing
4320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti * the IEthernetManager interface.
4420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti *
4520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti * @hide
4620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti */
4720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colittipublic class EthernetServiceImpl extends IEthernetManager.Stub {
4820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    private static final String TAG = "EthernetServiceImpl";
4920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
5020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    private final Context mContext;
5120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    private final EthernetConfigStore mEthernetConfigStore;
5220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    private final AtomicBoolean mStarted = new AtomicBoolean(false);
5320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    private IpConfiguration mIpConfiguration;
5420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
5520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    private Handler mHandler;
56aea43f5901e6591c390b83b10d3dd9b515a6442aLorenzo Colitti    private final EthernetNetworkFactory mTracker;
57a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    private final RemoteCallbackList<IEthernetServiceListener> mListeners =
58a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim            new RemoteCallbackList<IEthernetServiceListener>();
5920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
6020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    public EthernetServiceImpl(Context context) {
6120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        mContext = context;
6220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        Log.i(TAG, "Creating EthernetConfigStore");
6320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        mEthernetConfigStore = new EthernetConfigStore();
6420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        mIpConfiguration = mEthernetConfigStore.readIpAndProxyConfigurations();
6520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
6620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        Log.i(TAG, "Read stored IP configuration: " + mIpConfiguration);
6720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
68a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim        mTracker = new EthernetNetworkFactory(mListeners);
6920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    }
7020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
7120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    private void enforceAccessPermission() {
7220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        mContext.enforceCallingOrSelfPermission(
7320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti                android.Manifest.permission.ACCESS_NETWORK_STATE,
7420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti                "EthernetService");
7520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    }
7620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
7720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    private void enforceConnectivityInternalPermission() {
7820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        mContext.enforceCallingOrSelfPermission(
7920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti                android.Manifest.permission.CONNECTIVITY_INTERNAL,
8020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti                "ConnectivityService");
8120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    }
8220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
8320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    public void start() {
8420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        Log.i(TAG, "Starting Ethernet service");
8520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
8620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        HandlerThread handlerThread = new HandlerThread("EthernetServiceThread");
8720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        handlerThread.start();
8820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        mHandler = new Handler(handlerThread.getLooper());
8920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
9020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        mTracker.start(mContext, mHandler);
9120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
9220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        mStarted.set(true);
9320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    }
9420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
9520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    /**
9620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti     * Get Ethernet configuration
9720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti     * @return the Ethernet Configuration, contained in {@link IpConfiguration}.
9820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti     */
99a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    @Override
10020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    public IpConfiguration getConfiguration() {
10120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        enforceAccessPermission();
10220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
10320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        synchronized (mIpConfiguration) {
10420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti            return new IpConfiguration(mIpConfiguration);
10520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        }
10620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    }
10720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
10820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    /**
10920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti     * Set Ethernet configuration
11020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti     */
111a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    @Override
11220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    public void setConfiguration(IpConfiguration config) {
11320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        if (!mStarted.get()) {
11420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti            Log.w(TAG, "System isn't ready enough to change ethernet configuration");
11520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        }
11620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
11720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        enforceConnectivityInternalPermission();
11820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
11920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        synchronized (mIpConfiguration) {
12020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti            mEthernetConfigStore.writeIpAndProxyConfigurations(config);
12120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
12220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti            // TODO: this does not check proxy settings, gateways, etc.
12320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti            // Fix this by making IpConfiguration a complete representation of static configuration.
12420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti            if (!config.equals(mIpConfiguration)) {
12541a372f13a7d19807d91e09f2e955b8a61f02d5cLorenzo Colitti                mIpConfiguration = new IpConfiguration(config);
12620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti                mTracker.stop();
12720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti                mTracker.start(mContext, mHandler);
12820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti            }
12920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        }
13020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    }
131a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim
132a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    /**
133a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim     * Indicates whether the system currently has one or more
134a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim     * Ethernet interfaces.
135a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim     */
136a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    @Override
137a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    public boolean isAvailable() {
138a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim        enforceAccessPermission();
139a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim        return mTracker.isTrackingInterface();
140a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    }
141a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim
142a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    /**
143a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim     * Addes a listener.
144a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim     * @param listener A {@link IEthernetServiceListener} to add.
145a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim     */
146a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    public void addListener(IEthernetServiceListener listener) {
147a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim        if (listener == null) {
148a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim            throw new IllegalArgumentException("listener must not be null");
149a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim        }
150a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim        enforceAccessPermission();
151a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim        mListeners.register(listener);
152a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    }
153a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim
154a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    /**
155a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim     * Removes a listener.
156a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim     * @param listener A {@link IEthernetServiceListener} to remove.
157a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim     */
158a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    public void removeListener(IEthernetServiceListener listener) {
159a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim        if (listener == null) {
160a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim            throw new IllegalArgumentException("listener must not be null");
161a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim        }
162a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim        enforceAccessPermission();
163a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim        mListeners.unregister(listener);
164a3d7e61812f8d68ca109280c4e7589e4f968723aJaewan Kim    }
16520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
16620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    @Override
16720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
16820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
16920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
17020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti                != PackageManager.PERMISSION_GRANTED) {
17120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti            pw.println("Permission Denial: can't dump EthernetService from pid="
17220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti                    + Binder.getCallingPid()
17320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti                    + ", uid=" + Binder.getCallingUid());
17420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti            return;
17520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        }
17620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
177aea43f5901e6591c390b83b10d3dd9b515a6442aLorenzo Colitti        pw.println("Current Ethernet state: ");
178aea43f5901e6591c390b83b10d3dd9b515a6442aLorenzo Colitti        pw.increaseIndent();
179aea43f5901e6591c390b83b10d3dd9b515a6442aLorenzo Colitti        mTracker.dump(fd, pw, args);
180aea43f5901e6591c390b83b10d3dd9b515a6442aLorenzo Colitti        pw.decreaseIndent();
181aea43f5901e6591c390b83b10d3dd9b515a6442aLorenzo Colitti
182aea43f5901e6591c390b83b10d3dd9b515a6442aLorenzo Colitti        pw.println();
183aea43f5901e6591c390b83b10d3dd9b515a6442aLorenzo Colitti        pw.println("Stored Ethernet configuration: ");
18420c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        pw.increaseIndent();
18520c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        pw.println(mIpConfiguration);
18620c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        pw.decreaseIndent();
18720c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti
18820c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        pw.println("Handler:");
18920c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        pw.increaseIndent();
19020c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        mHandler.dump(new PrintWriterPrinter(pw), "EthernetServiceImpl");
19120c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti        pw.decreaseIndent();
19220c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti    }
19320c1c99c4351abd8bb3d6e8f966fcf3b6de0e5b0Lorenzo Colitti}
194