867 lines · 20.2 KB · C
RawDownload
/* TODO/BUGS in no order:   should send acknowledgement when an sms is read from /phone/sms, not just on every read   consider notifying RIL of screen state using RIL_REQUEST_SCREEN_STATE to conserve power   too many assumptions made in loop_for_data() about read() giving exactly the right amount of data   parcel code will go crazy if ints overflow   need to support DTMF*/ #include "dat.h"#include "fns.h"#include "error.h"#include "version.h"#include "mp.h"#include "libsec.h"#include "keyboard.h"#include <stdio.h>#include <cutils/sockets.h>#include <stdlib.h>#include <telephony/ril.h>#include <cutils/jstring.h>#include <arpa/inet.h>#include <libgen.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <semaphore.h>#include <dlfcn.h>#include "smsutils.h"#include "parcel.h" // RIL-related definitions #define RESPONSE_SOLICITED 0#define RESPONSE_UNSOLICITED 1#define POWER_ON 2000#define POWER_OFF 2001 // Inferno-related globals enum{	Qdir,	Qctl,	Qsms,	Qphone,	Qsignal,	Qstatus,	Qcalls}; Dirtab phonetab[] ={	".",	  {Qdir, 0, QTDIR}, 0, DMDIR|0555,	"ctl",	  {Qctl},	    0, 0666,	"sms",	  {Qsms},	    0, 0666,	"phone",  {Qphone},	    0, 0666,	"signal", {Qsignal},	    0, 0666,	"status", {Qstatus},	    0, 0666,	"calls",  {Qcalls},	    0, 0666,}; static int signal_strength;static int fd;static int power_state = 0; static struct {	QLock l;	Rendez r;	int ready;	int used;	char *msg;} status_msg; static struct {	QLock l;	Rendez r;	int ready;	int used; // cat will read until it gets 0 bytes from read()	          // need to be able to give cat 0 bytes after it's gotten the	          // data	RIL_Call *cs;	int num; // number of calls in cs} calls; static struct listener_node *phone_listeners = NULL;static struct listener_node *sms_listeners = NULL; static void loop_for_data(void *v);static void send_sms(char *smsc_pdu, char *pdu);static void dial(char *number);static void activate_net(void);static void deactivate_net(void);static void radio_power(int power);static void answer(void);static void acknowledge_sms(void);static void get_reg_state(void);static void set_mute(int);static void get_current_calls(void);static void hangup(int);static void hangup_current(void); // AudioFlinger layer stuffstatic void *af_handle; void (*af_setMode)(int);void (*af_setVoiceVolume)(float);void (*af_setParameters)(char *);void (*af_test)(void); enum audio_mode {	MODE_INVALID = -2,	MODE_CURRENT = -1,	MODE_NORMAL = 0,	MODE_RINGTONE,	MODE_IN_CALL,	MODE_IN_COMMUNICATION,	NUM_MODES  // not a valid entry, denotes end-of-list}; #define DPRINT if(1) print // Function for Sleep() to check readiness withstatic intcalls_ready(void *unused){	return calls.ready;} // Function for Sleep() to check readiness withstatic intstatus_ready(void *unused){	return status_msg.ready;} voidphoneinit(void){	af_handle = dlopen("libinfernoaudio.so", RTLD_NOW);	if(af_handle == NULL) {		fprintf(stderr, "opening libinfernoaudio.so failed: %s\n",			dlerror());	}	af_setMode = dlsym(af_handle, "af_setMode");	af_setVoiceVolume = dlsym(af_handle, "af_setVoiceVolume");	af_setParameters = dlsym(af_handle, "af_setParameters"); 	if(!(af_setMode && af_setVoiceVolume && af_setParameters)) {		fprintf(stderr, "could not load AudioFlinger functions");		dlclose(af_handle);	} 	af_setMode(MODE_NORMAL); 	calls.cs = NULL;	calls.num = 0;	calls.ready = 0;	calls.used = 0;	memset(&calls.l, 0, sizeof(QLock)); 	status_msg.ready = 0;	status_msg.used = 0;	memset(&status_msg.l, 0, sizeof(QLock)); 	// set up proper permissions (need to be user radio, group radio to	// access socket)	if(setegid(1001) == -1) {		perror("setegid(1001) failed");	}	if(seteuid(1001) == -1) {		perror("seteuid(1001) failed");	} 	fd = socket_local_client("rild",				 ANDROID_SOCKET_NAMESPACE_RESERVED,				 SOCK_STREAM);	if(fd == -1) {		perror("socket_local_client failed");	} 	// set privileges back to root/root	if(seteuid(0) == -1) {		perror("seteuid(0) failed");	}	if(setegid(0) == -1) {		perror("setegid(0) failed");	} 	kproc("phone", loop_for_data, 0, 0);} static Chan*phoneattach(char *spec){	// setup kprocs if necessary	return devattach('f', spec);} static Walkqid*phonewalk(Chan *c, Chan *nc, char **name, int nname){	return devwalk(c, nc, name, nname, phonetab, nelem(phonetab), devgen);} static intphonestat(Chan *c, uchar *db, int n){	return devstat(c, db, n, phonetab, nelem(phonetab), devgen);} static Chan*phoneopen(Chan *c, int omode){	c = devopen(c, omode, phonetab, nelem(phonetab), devgen); 	switch((ulong) c->qid.path) {	case Qstatus:		qlock(&status_msg.l);		status_msg.used = 0;		status_msg.ready = 0;		get_reg_state();		break;	case Qcalls:		qlock(&calls.l);		calls.used = 0;		calls.ready = 0;		get_current_calls();		break;	case Qphone:		c->aux = qopen(512, 0, nil, nil);		add_listener(&phone_listeners, c->aux);		break;	case Qsms:		c->aux = qopen(512, 0, nil, nil);		add_listener(&sms_listeners, c->aux);		break;	} 	return c;} static voidphoneclose(Chan *c){	if((c->flag & COPEN) == 0)		return; 	switch((ulong) c->qid.path) {	case Qphone:		del_listener(&phone_listeners, c->aux);		qfree(c->aux);		c->aux = NULL;		break;	case Qsms:		del_listener(&sms_listeners, c->aux);		qfree(c->aux);		c->aux = NULL;		break;	}} static longphoneread(Chan *c, void *va, long n, vlong offset){	char buf[255];	int i, str_offset;	if(c->qid.type & QTDIR) {		return devdirread(c, va, n, phonetab, nelem(phonetab), devgen);	} 	switch((ulong) c->qid.path) {	case Qctl:		return readstr(offset, va, n, power_state ? "on\n" : "off\n");	case Qphone:		return qread(c->aux, va, n);	case Qsms:		// FIXME only acknowledge when an SMS is being read		acknowledge_sms();		return qread(c->aux, va, n);	case Qsignal:		snprintf(buf, sizeof(buf), "%d\n", signal_strength);		return readstr(offset, va, n, buf);	case Qstatus:		Sleep(&status_msg.r, status_ready, NULL);		if(status_msg.ready == -1) {			// an error occurred			status_msg.used = 1;			qunlock(&status_msg.l);			return readstr(offset, va, n, "error\n");		}		if(status_msg.used) {			status_msg.ready = 0;			status_msg.used = 0;			return 0; // already sent the data		}		status_msg.used = 1;		return readstr(offset, va, n, status_msg.msg);	case Qcalls:		Sleep(&calls.r, calls_ready, NULL);		if(calls.ready == -1) {			// an error occurred			calls.used = 1;			qunlock(&calls.l);			return readstr(offset, va, n, "");		}		if(calls.used || calls.num == 0) {			calls.ready = 0;			calls.used = 0;			qunlock(&calls.l);			return 0; // already sent the data		}		calls.used = 1; 		str_offset = 0;		for(i = 0; i < calls.num; i++) {			RIL_Call c = calls.cs[i];			str_offset += snprintf(va + str_offset, n - str_offset,					       "call %d:\n "					       " state %d\n index %d\n toa %d\n"					       " isMpty %d\n isMT %d\n als %d\n isVoice %d\n"					       " isVoicePrivacy %d\n number %s\n"					       " numberPresentation %d\n name %s\n"					       " namePresentation %d\n",					       i, c.state, c.index, c.toa, c.isMpty, c.isMT,					       c.als, c.isVoice, c.isVoicePrivacy, c.number,					       c.numberPresentation, c.name,					       c.namePresentation);		}		qunlock(&calls.l);		return str_offset;	}	return 0;} static longphonewrite(Chan *c, void *va, long n, vlong offset){	char *pdu;	Rune *runestr;	int i, j;	Cmdbuf *cmd; 	if(c->qid.type & QTDIR)		error(Eperm); 	cmd = parsecmd(va, n);	if(waserror()) {		free(cmd);		nexterror();		return 0;	} 	switch((ulong) c->qid.path) {	case Qctl:		if(strcmp(cmd->f[0], "on") == 0) {			radio_power(1);		} else if(strcmp(cmd->f[0], "off") == 0) {			radio_power(0);		} else if(strcmp(cmd->f[0], "net") == 0) {			if(cmd->nf >= 2) {				if(strcmp(cmd->f[1], "on") == 0) {					activate_net();				} else if(strcmp(cmd->f[1], "off") == 0) {					deactivate_net();				}			}		} else if(strcmp(cmd->f[0], "mute") == 0) {			set_mute(1);		} else if(strcmp(cmd->f[0], "unmute") == 0) {			set_mute(0);		} else if(strcmp(cmd->f[0], "route") == 0) {			if(cmd->nf >= 2) {				if(strcmp(cmd->f[1], "earpiece") == 0) {					af_setParameters("routing=1");				} else if(strcmp(cmd->f[1], "speaker") == 0) {					af_setParameters("routing=2");				} else if(strcmp(cmd->f[1], "headphone") == 0) {					af_setParameters("routing=3");				}			}		} else if(strcmp(cmd->f[0], "volume") == 0) {			if(cmd->nf >= 2) {				switch(strtol(cmd->f[1], NULL, 10)) {				case 0: af_setVoiceVolume(0.0); break;				case 1: af_setVoiceVolume(0.2); break;				case 2: af_setVoiceVolume(0.4); break;				case 3: af_setVoiceVolume(0.6); break;				case 4: af_setVoiceVolume(0.8); break;				case 5: af_setVoiceVolume(1.0); break;				default: af_setVoiceVolume(1.0); break;				}			}		}		break;	case Qsms:		if(strcmp(cmd->f[0], "send") == 0) {			if(cmd->nf == 3) {				runestr = malloc((utflen(cmd->f[2]) + 1) * sizeof(Rune));				for(i = 0, j = 0; j < strlen(cmd->f[2]); i++) {					j += chartorune(runestr + i, cmd->f[2] + j);				}				runestr[i] = 0;				pdu = encode_sms(cmd->f[1], runestr);				send_sms(NULL, pdu);								free(runestr);				free(pdu);			}		}		break;	case Qphone:		if(strcmp(cmd->f[0], "answer") == 0) {			answer();		} else if(strcmp(cmd->f[0], "dial") == 0) {			if(cmd->nf != 2) {				fprintf(stderr, "malformed dial request\n");				break;			}			dial(cmd->f[1]);		} else if(strcmp(cmd->f[0], "hangup") == 0) {			if(cmd->nf == 2) {				hangup(strtol(cmd->f[1], NULL, 10));			} else {				hangup_current();			}		}		break;	}	poperror();	free(cmd);	return n;} Dev phonedevtab = {	'f',	"phone", 	phoneinit,	phoneattach,	phonewalk,	phonestat,	phoneopen,	devcreate,	phoneclose,	phoneread,	devbread,	phonewrite,	devbwrite,	devremove,	devwstat}; /* RIL functions */ static voidhandle_error(int seq, int error){	// RIL error messages	char *errmsgs[] = { "error: no error\n", "error: radio not available\n", "error: generic failure\n", "error: password incorrect\n", "error: need SIM PIN2\n", "error: need SIM PUK2\n", "error: request not supported\n", "error: cancelled\n", "error: cannot access network during voice calls\n", "error: cannot access network before registering to network\n", "error: retry sending sms\n", "error: no SIM\n", "error: no subscription\n", "error: mode not supported\n", "error: FDN list check failed\n", "error: illegal SIM or ME\n" };	char *errmsg = errmsgs[error];	switch(seq) {	case RIL_REQUEST_SEND_SMS:		listeners_produce(&sms_listeners, errmsg, strlen(errmsg));		break;	case RIL_REQUEST_DIAL:		listeners_produce(&phone_listeners, errmsg, strlen(errmsg));		break;	case RIL_REQUEST_REGISTRATION_STATE:		status_msg.ready = -1;		Wakeup(&status_msg.r);		qunlock(&status_msg.l);		break;	case RIL_REQUEST_GET_CURRENT_CALLS:		calls.ready = -1;		Wakeup(&calls.r);		qunlock(&calls.l);		break;	}} /* handle_sol_response is called when we recieve a solicited response from RIL,   i.e. the response to a request we sent. The response can contain data we   asked for, indicate an error, or simply indicate success. */static voidhandle_sol_response(struct parcel *p){	int seq, error, i, num, offset = 0;	char buf[200];	seq = parcel_r_int32(p);	error = parcel_r_int32(p);	DPRINT("received sol response: seq: %d err: %d\n", seq, error);	if(error != 0) {		handle_error(seq, error);		return;	}	switch(seq) {	case RIL_REQUEST_SETUP_DATA_CALL:		if(!parcel_data_avail(p)) return;		num = parcel_r_int32(p);		for(i = 0; i < num; i++) {

Lines 1–500 of 867.