blob: 43d4f1f91350d6dfd635ab286a2f99bdf908303d [file] [log] [blame]
/*
* Copyright (C) 2006 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.internal.telephony.gsm;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import android.database.Cursor;
import android.os.Message;
import android.os.SystemProperties;
import android.os.SystemService;
import com.android.internal.telephony.gsm.DataConnectionTracker.State;
import com.android.internal.util.ArrayUtils;
import android.util.Log;
/**
* Represents a PPP link.
*
* Ideally this would be managed by the RIL implementation, but
* we currently have implementations where this is not the case.
*
* {@hide}
*/
final class PppLink extends DataLink implements DataLinkInterface {
private static final String LOG_TAG = "GSM";
static final String PATH_PPP_OPERSTATE = "/sys/class/net/ppp0/operstate";
static final String SERVICE_PPPD_GPRS = "pppd_gprs";
static final String PROPERTY_PPPD_EXIT_CODE = "net.gprs.ppp-exit";
static final int POLL_SYSFS_MILLIS = 5 * 1000;
static final int EVENT_POLL_DATA_CONNECTION = 2;
static final int EVENT_PPP_OPERSTATE_CHANGED = 8;
static final int EVENT_PPP_PIDFILE_CHANGED = 9;
static final byte[] UP_ASCII_STRING = new byte[] {
'u' & 0xff,
'p' & 0xff,
};
static final byte[] DOWN_ASCII_STRING = new byte[] {
'd' & 0xff,
'o' & 0xff,
'w' & 0xff,
'n' & 0xff,
};
static final byte[] UNKNOWN_ASCII_STRING = new byte[] {
'u' & 0xff,
'n' & 0xff,
'k' & 0xff,
'n' & 0xff,
'o' & 0xff,
'w' & 0xff,
'n' & 0xff,
};
private final byte[] mCheckPPPBuffer = new byte[32];
int lastPppdExitCode = EXIT_OK;
PppLink(DataConnectionTracker dc) {
super(dc);
}
public void connect() {
// Clear any previous exit code
SystemProperties.set(PROPERTY_PPPD_EXIT_CODE, "");
SystemService.start(SERVICE_PPPD_GPRS);
removeMessages(EVENT_POLL_DATA_CONNECTION);
Message poll = obtainMessage();
poll.what = EVENT_POLL_DATA_CONNECTION;
sendMessageDelayed(poll, POLL_SYSFS_MILLIS);
}
public void disconnect() {
SystemService.stop(SERVICE_PPPD_GPRS);
}
public int getLastLinkExitCode() {
return lastPppdExitCode;
}
public void setPasswordInfo(Cursor cursor) {
StringBuilder builder = new StringBuilder();
FileOutputStream output = null;
try {
output = new FileOutputStream("/etc/ppp/pap-secrets");
if (cursor.moveToFirst()) {
do {
builder.append(cursor.getString(cursor.getColumnIndex("user")));
builder.append(" ");
builder.append(cursor.getString(cursor.getColumnIndex("server")));
builder.append(" ");
builder.append(cursor.getString(cursor.getColumnIndex("password")));
builder.append("\n");
} while (cursor.moveToNext());
}
output.write(builder.toString().getBytes());
} catch (java.io.IOException e) {
Log.e(LOG_TAG, "Could not create '/etc/ppp/pap-secrets'", e);
} finally {
try {
if (output != null) output.close();
} catch (java.io.IOException e) {
Log.e(LOG_TAG, "Error closing '/etc/ppp/pap-secrets'", e);
}
}
}
public void handleMessage (Message msg) {
switch (msg.what) {
case EVENT_POLL_DATA_CONNECTION:
checkPPP();
// keep polling in case interface goes down
if (dataConnection.state != State.IDLE) {
Message poll = obtainMessage();
poll.what = EVENT_POLL_DATA_CONNECTION;
sendMessageDelayed(poll, POLL_SYSFS_MILLIS);
}
break;
}
}
private void checkPPP() {
boolean connecting = (dataConnection.state == State.CONNECTING);
try {
RandomAccessFile file = new RandomAccessFile(PATH_PPP_OPERSTATE, "r");
file.read(mCheckPPPBuffer);
file.close();
// Unfortunately, we're currently seeing operstate
// "unknown" where one might otherwise expect "up"
if (ArrayUtils.equals(mCheckPPPBuffer, UP_ASCII_STRING, UP_ASCII_STRING.length)
|| ArrayUtils.equals(mCheckPPPBuffer, UNKNOWN_ASCII_STRING,
UNKNOWN_ASCII_STRING.length)
&& dataConnection.state == State.CONNECTING) {
Log.i(LOG_TAG,
"found ppp interface. Notifying GPRS connected");
if (mLinkChangeRegistrant != null) {
mLinkChangeRegistrant.notifyResult(LinkState.LINK_UP);
}
connecting = false;
} else if (dataConnection.state == State.CONNECTED
&& ArrayUtils.equals(mCheckPPPBuffer, DOWN_ASCII_STRING,
DOWN_ASCII_STRING.length)) {
Log.i(LOG_TAG,
"ppp interface went down. Reconnecting...");
if (mLinkChangeRegistrant != null) {
mLinkChangeRegistrant.notifyResult(LinkState.LINK_DOWN);
}
}
} catch (IOException ex) {
if (! (ex instanceof FileNotFoundException)) {
Log.i(LOG_TAG, "Poll ppp0 ex " + ex.toString());
}
if (dataConnection.state == State.CONNECTED &&
mLinkChangeRegistrant != null) {
mLinkChangeRegistrant.notifyResult(LinkState.LINK_DOWN);
}
}
// CONNECTING means pppd has started but negotiation is not complete
// If we're still CONNECTING here, check to see if pppd has
// already exited
if (connecting) {
String exitCode;
exitCode = SystemProperties.get(PROPERTY_PPPD_EXIT_CODE, "");
if (!exitCode.equals("")) {
// pppd has exited. Let's figure out why
lastPppdExitCode = Integer.parseInt(exitCode);
Log.d(LOG_TAG,"pppd exited with " + exitCode);
if (mLinkChangeRegistrant != null) {
mLinkChangeRegistrant.notifyResult(LinkState.LINK_EXITED);
}
}
}
}
}