# The MIT License (MIT) # Copyright (c) 2012 Jochen Streicher # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import time import android # # config # INTERVAL_HIFRQ = 1000 # (approximate) interval for frequently checked # state [ms] INTERVAL_LOFRQ = 60000 # (approximate) interval for less frequently # checked state [ms] INTERVAL_LOCATION = 10000 # interval for location updates [ms], 0 means off INTERVAL_SENSORS = 5000 # interval for sensor data (acceleration, # magnetometer, light, ...) [ms], 0 means off FIELD_SEP = "; " # field separator RECORD_SEP = "\n" # record separator ATTR_ON = "1" # how additions and removals of list elements are ATTR_OFF = "0" # marked # # LOG FORMAT (assuming unchanged configuration): # # timestamp; "attribute"; "value" # # # data collected with high frequency # # see http://code.google.com/p/android-scripting/wiki/ApiReference # for an explanation of the individual data sources # def log_hifreq(): # radio state log("network.type" ,droid.getNetworkType().result) log("network.roaming" ,droid.checkNetworkRoaming().result) log("network.cells.own", droid.getCellLocation().result) log("network.cells.neighbors", droid.getNeighboringCellInfo().result) log("bluetooth" ,droid.checkBluetoothState().result) log("wifi" ,droid.checkWifiState().result) log("wifi.connection",droid.wifiGetConnectionInfo().result) log("airplanemode" ,droid.checkAirplaneMode().result) logLD("wifi.scan" , "bssid", droid.wifiGetScanResults().result) # application context log("packages.running" ,droid.getRunningPackages().result) # media context log("media.volume" ,droid.getMediaVolume().result) log("media.maxvolume" ,droid.getMaxMediaVolume().result) log("media.playlist" ,droid.mediaPlayList().result) # notifications log("ringer.volume" ,droid.getRingerVolume().result) log("ringer.vibrate" ,droid.getVibrateMode(True).result) log("ringer.silent" ,droid.checkRingerSilentMode().result) log("ringer.maxvolume" ,droid.getMaxRingerVolume().result) log("notifications.vibrate" ,droid.getVibrateMode(False).result) # screen state log("screen" , droid.checkScreenOn().result) log("screen.brightness" ,droid.getScreenBrightness().result) log("screen.timeout" ,droid.getScreenTimeout().result) # battery state log("battery" , droid.readBatteryData().result) # contacts & sms # (uncomment if you really want to write this into a log file) # # logLD("sms", "_id", droid.smsGetMessages(False).result) # logLD("contacts", "_id", droid.contactsGet().result) # # less frequently checked data sources # def log_lofreq(): droid.wifiStartScan() # mobile network data log("sim.serial" ,droid.getSimSerialNumber().result) log("sim.state" ,droid.getSimState().result) log("sim.country" ,droid.getSimCountryIso().result) log("sim.subscriberid" ,droid.getSubscriberId().result) log("sim.operator.id" ,droid.getSimOperator().result) log("sim.operator.name" ,droid.getSimOperatorName().result) log("network.operator.id" ,droid.getNetworkOperator().result) log("network.operator.name" ,droid.getNetworkOperatorName().result) # # event-driven log function # def log_event(data): # # events from SL4A maycontain information about # * current sensor values # * location updates (network + GPS) # * battery state # * phone events (incoming calls etc.) # log(data["name"], data["data"]) # # log function called only at the beginning # def log_once(): l1n=str() log("device.type", droid.getPhoneType().result) log("device.swversion", droid.getDeviceSoftwareVersion().result) log("device.id", droid.getDeviceId().result) log("phone.number", droid.getLine1Number().result) # # register for external sensors and events # def register_sensors(): if INTERVAL_LOCATION > 0: droid.startLocating(INTERVAL_LOCATION, 0) if INTERVAL_SENSORS > 0: droid.startSensingTimed(1, INTERVAL_SENSORS) droid.batteryStartMonitoring() droid.startTrackingSignalStrengths() droid.startTrackingPhoneState() # # Convenient log() Functor # class LogFunctor: def __init__(self): self.logproc = LogProcessor() file_path = "data_" + str(gettime()) + ".log" self.f = f = open(file_path,'w') self.logproc.set_output(self.output) def __call__(self, attrprefix, data): line = self.logproc.process(attrprefix, data) def flush(self): self.f.flush() def output(self, attr, value): line = (str(gettime()) + FIELD_SEP + '"' + unicode(attr).encode("utf_8") + '"' + FIELD_SEP + '"' + unicode(value).encode("utf_8") + '"') print line self.f.write(line + RECORD_SEP) # # log function for lists of dictionaries, with # a unique ID per dictionary # (e.g., list of wifis, bssid is the unique ID) # def logLD(attrprefix, uidname, data): uidlist = [] if data: for d in data: uid = d[uidname] del d[uidname] uidlist.append(uid) log(attrprefix + "." + uid, d) log(attrprefix, uidlist) def gettime(): return int(time.time() * 1000) # # LogProcessor # - extracts attributes and values from dictionaries # - transforms lists into attributes and values # ( attribute: list item, value: added/removed) # - only outputs attributes whose values have changed # class LogProcessor: attrqual = '.' def __init__(self): self.reset() def set_output(self, output_method): self.out = output_method def process(self, attrprefix, data): self.log_rec(attrprefix, data) def log_rec(self, prefix, data): dt = type(data) if dt is dict: self.log_dict(prefix, data) elif dt is list: self.log_list(prefix, data) else: self.log_val(prefix, data) def log_dict(self, prefix, data): for attr in data: qualattr = prefix + LogProcessor.attrqual + attr self.log_rec(qualattr, data[attr]) def log_list(self, prefix, data): for item in data: qualattr = prefix + LogProcessor.attrqual + str(item) self.log_val(qualattr, ATTR_ON) if prefix in self.buf: for olditem in self.buf[prefix]: if not(olditem in data): qualattr = prefix + LogProcessor.attrqual + str(olditem) self.log_val(qualattr, ATTR_OFF) self.buf[prefix] = data def log_val(self, attr, value): if attr in self.buf: if value == self.buf[attr]: return self.buf[attr] = value self.out(attr, value) def reset(self): self.buf = {} # # MAIN FUNCTION # print("start logging") log = LogFunctor() # instantiate Android API droid = android.Android() # data sources queried exactly once per session log_once() register_sensors() cnt=0 while True: # Wait for and process SL4A events until the next HIFRQ interval nextlogt = gettime() + INTERVAL_HIFRQ while gettime() < nextlogt: event = droid.eventWait(nextlogt - gettime()) if not(event): continue if not(event.result): continue # event-driven data sources log_event(event.result) # highly-frequently checked data sources log_hifreq() # less-frequently checked data sources if cnt <= 0: log_lofreq() cnt = int(INTERVAL_LOFRQ / INTERVAL_HIFRQ) # write buffered changes to the logfile log.flush() cnt -= 1