Project: android-vpn-settings
/*
 * Copyright (C) 2009 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 com.android.settings.vpn; 
 
import com.android.settings.R; 
 
import android.app.Dialog; 
import android.content.ComponentName; 
import android.content.Context; 
import android.content.ServiceConnection; 
import android.net.vpn.IVpnService; 
import android.net.vpn.VpnManager; 
import android.net.vpn.VpnProfile; 
import android.net.vpn.VpnState; 
import android.os.ConditionVariable; 
import android.os.IBinder; 
import android.os.RemoteException; 
import android.text.TextUtils; 
import android.util.Log; 
import android.view.View; 
import android.widget.CheckBox; 
import android.widget.TextView; 
 
import java.io.IOException; 
 
/**
 * A {@link VpnProfileActor} that provides an authentication view for users to 
 * input username and password before connecting to the VPN server. 
 */
 
public class AuthenticationActor implements VpnProfileActor { 
    private static final String TAG = AuthenticationActor.class.getName(); 
 
    private Context mContext; 
    private VpnProfile mProfile; 
    private VpnManager mVpnManager; 
 
    public AuthenticationActor(Context context, VpnProfile p) { 
        mContext = context; 
        mProfile = p; 
        mVpnManager = new VpnManager(context); 
    } 
 
    //@Override 
    public VpnProfile getProfile() { 
        return mProfile; 
    } 
 
    //@Override 
    public boolean isConnectDialogNeeded() { 
        return true
    } 
 
    //@Override 
    public String validateInputs(Dialog d) { 
        TextView usernameView = (TextView) d.findViewById(R.id.username_value); 
        TextView passwordView = (TextView) d.findViewById(R.id.password_value); 
        Context c = mContext; 
        if (TextUtils.isEmpty(usernameView.getText().toString())) { 
            return c.getString(R.string.vpn_a_username); 
        } else if (TextUtils.isEmpty(passwordView.getText().toString())) { 
            return c.getString(R.string.vpn_a_password); 
        } else { 
            return null
        } 
    } 
 
    //@Override 
    public void connect(Dialog d) { 
        TextView usernameView = (TextView) d.findViewById(R.id.username_value); 
        TextView passwordView = (TextView) d.findViewById(R.id.password_value); 
        CheckBox saveUsername = (CheckBox) d.findViewById(R.id.save_username); 
 
        try { 
            setSavedUsername(saveUsername.isChecked() 
                    ? usernameView.getText().toString() 
                    : ""); 
        } catch (IOException e) { 
            Log.e(TAG, "setSavedUsername()", e); 
        } 
 
        connect(usernameView.getText().toString(), 
                passwordView.getText().toString()); 
        passwordView.setText(""); 
    } 
 
    //@Override 
    public View createConnectView() { 
        View v = View.inflate(mContext, R.layout.vpn_connect_dialog_view, null); 
        TextView usernameView = (TextView) v.findViewById(R.id.username_value); 
        TextView passwordView = (TextView) v.findViewById(R.id.password_value); 
        CheckBox saveUsername = (CheckBox) v.findViewById(R.id.save_username); 
 
        String username = mProfile.getSavedUsername(); 
        if (!TextUtils.isEmpty(username)) { 
            usernameView.setText(username); 
            saveUsername.setChecked(true); 
            passwordView.requestFocus(); 
        } 
        return v; 
    } 
 
    protected Context getContext() { 
        return mContext; 
    } 
 
    private void connect(final String username, final String password) { 
        mVpnManager.startVpnService(); 
        ServiceConnection c = new ServiceConnection() { 
            public void onServiceConnected(ComponentName className, 
                    IBinder service) { 
                try { 
                    boolean success = IVpnService.Stub.asInterface(service) 
                            .connect(mProfile, username, password); 
                    if (!success) { 
                        Log.d(TAG, "~~~~~~ connect() failed!"); 
                    } else { 
                        Log.d(TAG, "~~~~~~ connect() succeeded!"); 
                    } 
                } catch (Throwable e) { 
                    Log.e(TAG, "connect()", e); 
                    broadcastConnectivity(VpnState.IDLE, 
                            VpnManager.VPN_ERROR_CONNECTION_FAILED); 
                } finally { 
                    mContext.unbindService(this); 
                } 
            } 
 
            public void onServiceDisconnected(ComponentName className) { 
                checkStatus(); 
            } 
        }; 
        if (!bindService(c)) { 
            broadcastConnectivity(VpnState.IDLE, 
                    VpnManager.VPN_ERROR_CONNECTION_FAILED); 
        } 
    } 
 
    //@Override 
    public void disconnect() { 
        ServiceConnection c = new ServiceConnection() { 
            public void onServiceConnected(ComponentName className, 
                    IBinder service) { 
                try { 
                    IVpnService.Stub.asInterface(service).disconnect(); 
                } catch (RemoteException e) { 
                    Log.e(TAG, "disconnect()", e); 
                    checkStatus(); 
                } finally { 
                    mContext.unbindService(this); 
                } 
            } 
 
            public void onServiceDisconnected(ComponentName className) { 
                checkStatus(); 
            } 
        }; 
        if (!bindService(c)) { 
            checkStatus(); 
        } 
    } 
 
    //@Override 
    public void checkStatus() { 
        final ConditionVariable cv = new ConditionVariable(); 
        cv.close(); 
        ServiceConnection c = new ServiceConnection() { 
            public synchronized void onServiceConnected(ComponentName className, 
                    IBinder service) { 
                cv.open(); 
                try { 
                    IVpnService.Stub.asInterface(service).checkStatus(mProfile); 
                } catch (RemoteException e) { 
                    Log.e(TAG, "checkStatus()", e); 
                    broadcastConnectivity(VpnState.IDLE); 
                } finally { 
                    mContext.unbindService(this); 
                } 
            } 
 
            public void onServiceDisconnected(ComponentName className) { 
                cv.open(); 
                broadcastConnectivity(VpnState.IDLE); 
                mContext.unbindService(this); 
            } 
        }; 
        if (bindService(c)) { 
            // wait for a second, let status propagate 
            if (!cv.block(1000)) broadcastConnectivity(VpnState.IDLE); 
        } 
    } 
 
    private boolean bindService(ServiceConnection c) { 
        return mVpnManager.bindVpnService(c); 
    } 
 
    private void broadcastConnectivity(VpnState s) { 
        mVpnManager.broadcastConnectivity(mProfile.getName(), s); 
    } 
 
    private void broadcastConnectivity(VpnState s, int errorCode) { 
        mVpnManager.broadcastConnectivity(mProfile.getName(), s, errorCode); 
    } 
 
    private void setSavedUsername(String name) throws IOException { 
        if (!name.equals(mProfile.getSavedUsername())) { 
            mProfile.setSavedUsername(name); 
            VpnSettings.saveProfileToStorage(mProfile); 
        } 
    } 
}