864 lines · 20.2 KB · Text
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,}; Queue *phoneq;Queue *smsq; 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;	RIL_Call *cs;	int num;} calls; 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};  static intcalls_ready(void *unused){	return calls.ready;} static 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));	phoneq = qopen(512, 0, nil, nil);	if(phoneq == 0)		panic("no memory");	smsq = qopen(512, 0, nil, nil);	if(smsq == 0)		panic("no memory"); 	// 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;	} 	return c;} static voidphoneclose(Chan *c){	if((c->flag & COPEN) == 0)		return;} 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(phoneq, va, n);	case Qsms:		// FIXME only acknowledge when an SMS is being read		acknowledge_sms();		return qread(smsq, 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;	char *args[5];	char *str;	Rune *runestr;	int nargs = 0, i, j; 	if(c->qid.type & QTDIR)		error(Eperm);	switch((ulong) c->qid.path) {	case Qctl:		str = malloc((n + 1) * sizeof(char));		strncpy(str, va, n);		str[n] = '\0';		printf("Qctl: va = %s\n", str);		nargs = getfields(str, args, sizeof(args), 1, " \n");		if(strcmp(args[0], "on") == 0) {			radio_power(1);		} else if(strcmp(args[0], "off") == 0) {			radio_power(0);		} else if(strcmp(args[0], "net") == 0) {			if(nargs >= 2) {				if(strcmp(args[1], "on") == 0) {					activate_net();				} else if(strcmp(args[1], "off") == 0) {					deactivate_net();				}			}		} else if(strcmp(args[0], "mute") == 0) {			set_mute(1);		} else if(strcmp(args[0], "unmute") == 0) {			set_mute(0);		} else if(strcmp(args[0], "route") == 0) {			if(nargs >= 2) {				if(strcmp(args[1], "earpiece") == 0) {					af_setParameters("routing=1");				} else if(strcmp(args[1], "speaker") == 0) {					af_setParameters("routing=2");				} else if(strcmp(args[1], "headphone") == 0) {					af_setParameters("routing=3");				}			}		} else if(strcmp(args[0], "volume") == 0) {			if(nargs >= 2) {				switch(strtol(args[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;				}			}		}		free(str);		break;	case Qsms:		str = malloc((n + 1) * sizeof(char));		strncpy(str, va, n);		str[n] = '\0';		printf("Qsms: va = %s\n", str);		nargs = getfields(str, args, 3, 1, " ");		if(strcmp(args[0], "send") == 0) {			if(nargs == 3) {				runestr = malloc((utflen(args[2]) + 1) * sizeof(Rune));				for(i = 0, j = 0; j < strlen(args[2]); i++) {					j += chartorune(runestr + i, args[2] + j);				}				runestr[i] = 0;				pdu = encode_sms(args[1], runestr);				send_sms(NULL, pdu);								free(runestr);				free(pdu);			}		}		free(str);		break;	case Qphone:		str = malloc((n + 1) * sizeof(char));		strncpy(str, va, n);		str[n] = '\0';		printf("Qphone: va = %s\n", str);		nargs = getfields(str, args, sizeof(args), 1, " \n");		if(strcmp(args[0], "answer") == 0) {			answer();		} else if(strcmp(args[0], "dial") == 0) {			if(nargs != 2) {				fprintf(stderr, "malformed dial request\n");				free(str);				break;			}			dial(args[1]);		} else if(strcmp(args[0], "hangup") == 0) {			if(nargs == 2) {				hangup(strtol(args[1], NULL, 10));			} else {				hangup_current();			}		}		free(str);		break;	}	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", "error: radio not available", "error: generic failure", "error: password incorrect", "error: need SIM PIN2", "error: need SIM PUK2", "error: request not supported", "error: cancelled", "error: cannot access network during voice calls", "error: cannot access network before registering to network", "error: retry sending sms", "error: no SIM", "error: no subscription", "error: mode not supported", "error: FDN list check failed", "error: illegal SIM or ME" };	char *errmsg = errmsgs[error];	switch(seq) {	case RIL_REQUEST_SEND_SMS:		qproduce(smsq, errmsg, strlen(errmsg));		break;	case RIL_REQUEST_DIAL:		qproduce(phoneq, 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);	printf("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++) {			printf("%s\n", parcel_r_string(p));		}		break;	case POWER_ON:		power_state = 1;		break;	case POWER_OFF:		power_state = 0;		af_setMode(MODE_NORMAL);		break;

Lines 1–500 of 864.