1ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff/*
2ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff * Copyright (C) 2010 The Android Open Source Project
3ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff *
4ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff * Licensed under the Apache License, Version 2.0 (the "License");
5ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff * you may not use this file except in compliance with the License.
6ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff * You may obtain a copy of the License at
7ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff *
8ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff *      http://www.apache.org/licenses/LICENSE-2.0
9ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff *
10ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff * Unless required by applicable law or agreed to in writing, software
11ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff * distributed under the License is distributed on an "AS IS" BASIS,
12ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff * See the License for the specific language governing permissions and
14ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff * limitations under the License.
15ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff */
16ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
17ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffpackage android.net.wifi;
18ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
19ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport android.content.Context;
20ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport android.net.wifi.WifiConfiguration.KeyMgmt;
21ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport android.os.Environment;
22ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport android.os.Handler;
239575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriffimport android.os.Message;
249575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriffimport android.os.Messenger;
25ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport android.util.Log;
26ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
279575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriffimport com.android.internal.util.AsyncChannel;
289575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriffimport com.android.internal.R;
299575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriffimport com.android.internal.util.State;
309575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriffimport com.android.internal.util.StateMachine;
319575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff
32ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport java.io.BufferedInputStream;
33ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport java.io.BufferedOutputStream;
34ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport java.io.DataInputStream;
35ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport java.io.DataOutputStream;
36ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport java.io.FileInputStream;
37ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport java.io.FileOutputStream;
38ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport java.io.IOException;
39ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport java.net.InetAddress;
40ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriffimport java.util.UUID;
41ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
42ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff/**
43ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff * Provides API to the WifiStateMachine for doing read/write access
44ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff * to soft access point configuration
45ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff */
469575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriffclass WifiApConfigStore extends StateMachine {
47ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
489575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    private Context mContext;
49ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff    private static final String TAG = "WifiApConfigStore";
50ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
51ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff    private static final String AP_CONFIG_FILE = Environment.getDataDirectory() +
52ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff        "/misc/wifi/softap.conf";
53ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
54ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff    private static final int AP_CONFIG_FILE_VERSION = 1;
55ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
569575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    private State mDefaultState = new DefaultState();
579575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    private State mInactiveState = new InactiveState();
589575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    private State mActiveState = new ActiveState();
599575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff
609575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    private WifiConfiguration mWifiApConfig = null;
619575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    private AsyncChannel mReplyChannel = new AsyncChannel();
62ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
639575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    WifiApConfigStore(Context context, Handler target) {
649575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        super(TAG, target.getLooper());
65ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
669575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        mContext = context;
679575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        addState(mDefaultState);
689575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            addState(mInactiveState, mDefaultState);
699575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            addState(mActiveState, mDefaultState);
70ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
719575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        setInitialState(mInactiveState);
72ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff    }
73ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
749575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    public static WifiApConfigStore makeWifiApConfigStore(Context context, Handler target) {
759575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        WifiApConfigStore s = new WifiApConfigStore(context, target);
769575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        s.start();
779575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        return s;
789575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    }
79ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
809575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    class DefaultState extends State {
819575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        public boolean processMessage(Message message) {
829575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            switch (message.what) {
839575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                case WifiStateMachine.CMD_SET_AP_CONFIG:
849575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED:
859575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    Log.e(TAG, "Unexpected message: " + message);
869575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    break;
879575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                case WifiStateMachine.CMD_REQUEST_AP_CONFIG:
889575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    mReplyChannel.replyToMessage(message,
899575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                            WifiStateMachine.CMD_RESPONSE_AP_CONFIG, mWifiApConfig);
909575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    break;
919575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                default:
929575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    Log.e(TAG, "Failed to handle " + message);
939575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    break;
949575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            }
959575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            return HANDLED;
96ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff        }
97ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff    }
98ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
999575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    class InactiveState extends State {
1009575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        public boolean processMessage(Message message) {
1019575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            switch (message.what) {
1029575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                case WifiStateMachine.CMD_SET_AP_CONFIG:
1039575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    mWifiApConfig = (WifiConfiguration) message.obj;
1049575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    transitionTo(mActiveState);
1059575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    break;
1069575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                default:
1079575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    return NOT_HANDLED;
1089575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            }
1099575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            return HANDLED;
110ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff        }
111ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff    }
112ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
1139575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    class ActiveState extends State {
1149575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        public void enter() {
1159575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            new Thread(new Runnable() {
1169575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                public void run() {
1179575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    writeApConfiguration(mWifiApConfig);
1189575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED);
1199575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                }
1209575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            }).start();
121ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff        }
122ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
1239575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        public boolean processMessage(Message message) {
1249575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            switch (message.what) {
1259575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                //TODO: have feedback to the user when we do this
1269575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                //to indicate the write is currently in progress
1279575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                case WifiStateMachine.CMD_SET_AP_CONFIG:
1289575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    deferMessage(message);
129ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff                    break;
1309575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED:
1319575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    transitionTo(mInactiveState);
132ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff                    break;
133ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff                default:
1349575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    return NOT_HANDLED;
135ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff            }
1369575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            return HANDLED;
137ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff        }
1389575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    }
139ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
1409575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    void loadApConfiguration() {
1419575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        DataInputStream in = null;
1429575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        try {
1439575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            WifiConfiguration config = new WifiConfiguration();
1449575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
1459575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                            AP_CONFIG_FILE)));
146ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
1479575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            int version = in.readInt();
1489575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            if (version != 1) {
1499575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                Log.e(TAG, "Bad version on hotspot configuration file, set defaults");
150ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff                setDefaultApConfiguration();
1519575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                return;
1529575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            }
1539575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            config.SSID = in.readUTF();
1549575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            int authType = in.readInt();
1559575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            config.allowedKeyManagement.set(authType);
1569575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            if (authType != KeyMgmt.NONE) {
1579575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                config.preSharedKey = in.readUTF();
1589575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            }
1599575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            mWifiApConfig = config;
1609575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        } catch (IOException ignore) {
1619575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            setDefaultApConfiguration();
1629575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        } finally {
1639575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            if (in != null) {
1649575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                try {
1659575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    in.close();
1669575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                } catch (IOException e) {}
167ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff            }
168ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff        }
1699575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    }
170ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff
1719575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    Messenger getMessenger() {
1729575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        return new Messenger(getHandler());
1739575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    }
1749575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff
1759575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    private void writeApConfiguration(final WifiConfiguration config) {
1769575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        DataOutputStream out = null;
1779575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        try {
1789575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            out = new DataOutputStream(new BufferedOutputStream(
1799575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                        new FileOutputStream(AP_CONFIG_FILE)));
1809575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff
1819575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            out.writeInt(AP_CONFIG_FILE_VERSION);
1829575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            out.writeUTF(config.SSID);
1839575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            int authType = config.getAuthType();
1849575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            out.writeInt(authType);
1859575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            if(authType != KeyMgmt.NONE) {
1869575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                out.writeUTF(config.preSharedKey);
1879575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            }
1889575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        } catch (IOException e) {
1899575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            Log.e(TAG, "Error writing hotspot configuration" + e);
1909575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        } finally {
1919575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            if (out != null) {
1929575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                try {
1939575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                    out.close();
1949575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff                } catch (IOException e) {}
1959575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff            }
196ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff        }
197ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff    }
1989575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff
1999575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    /* Generate a default WPA2 based configuration with a random password.
2009575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff       We are changing the Wifi Ap configuration storage from secure settings to a
2019575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff       flat file accessible only by the system. A WPA2 based default configuration
2029575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff       will keep the device secure after the update */
2039575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    private void setDefaultApConfiguration() {
2049575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        WifiConfiguration config = new WifiConfiguration();
2059575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        config.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
2069575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
2079575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        String randomUUID = UUID.randomUUID().toString();
2089575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
2099575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9,13);
2109575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff        sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG, config);
2119575a1bea1787efe1686bd8562bcc70c72d01721Irfan Sheriff    }
212ffcea7ae7316ab748a49f5e8f6c6798356f35719Irfan Sheriff}
213