// Demonstrate calling external programs, by Warren E. Downs (2015) // Based on examples by Steve Garman // This program also demonstrates using Asynchronous Callbacks // to continue processing once the result of the external program // is received. //Called when application is started. function OnStart() { // Show roots >100 Mb in size. showRoots(100,function() { // Called when showRoots is complete alert('3. The program continues here'); runCmd( "ExpectAnErrorHere", function(lines) { alert("Expected empty array: "+JSON.stringify(lines)); }, function(errmsg) { alert('Expected Error: '+errmsg); }); }); alert('1. Immediate continuation.'); } // Shows a list of roots greater than specified minMb in size // Calls next function (if any) once complete. function showRoots(minMb, next) { listRoots(minMb*1000, function(roots) { var result="2. Roots:\n"; for(var xa=0; xa<roots.length; xa++) { var freeGb=(roots[xa].available/1024/1024).toFixed(2); result += " " + freeGb + " Gb free on "+roots[xa].mountedOn+"\n"; } alert(result); if(next) next(); }); } ////////////////////////////////////////////////////////////// /**************** Run External Commands *********************/ function runCmd(cmd, gotLines, gotError) { var tmp="/sdcard/."+app.GetAppName()+"_runcmd.txt"; var tmpErr=tmp+".err"; if(typeof globs === "undefined") { globs={}; } if(cmd != null) { // Pull result from previous run if cmd == null var outTmp=tmp+".tmp"; var errTmp=tmpErr+".tmp"; var redOut=" >" + outTmp; var redErr=" 2>" + errTmp; cmd += redOut + redErr; cmd += "; mv " + outTmp + " " + tmp; // Rename out & err once done. cmd += "; mv " + errTmp + " " + tmpErr; cmd += "\n"; // On-demand create globs.sys if not already created if(globs.sys == null) { globs.sys = app.CreateSysProc("sh"); } globs.sys.Out(cmd); globs.runCmdGotLines=gotLines; globs.runCmdGotError=gotError; } // Retry until both stdout and stderr exist (though they may be empty) if(!app.FileExists(tmp) || !app.FileExists(tmpErr)) { setTimeout('runCmd(null, globs.runCmdGotLines, globs.runCmdGotError)', 1); return; } // Read stdout and stderr var lines=app.ReadFile(tmp); // Retrieve command stdout if(lines == null) { alert("Unable to read stdout("+cmd+"): "+tmp); return; } var errmsg=app.ReadFile(tmpErr); // Retrieve command stderr if(errmsg == null) { alert("Unable to read stderr("+cmd+"): "+tmpErr); return; } // Process stdout lines = lines.split("\n"); app.DeleteFile(tmp); gotLines(lines); if(errmsg.length > 0) { gotError(errmsg); } } /****************** List Storage Roots **********************/ function listRoots(minSize, gotRoots) { runCmd( "busybox df -k", function(lines) { // Filesystem 1K-blocks Used Available Use% Mounted on // L R R R R L // ^^^^^^^^^^^^^^^^^^ HEAD ALIGNMENT ^^^^^^^^^^^^^^^^^ var head=lines[0]; // Parse the variable length/fixed-width column headers // Columns are fixed width but width depends on content. // If content exceeds a column (e.g. Filesystem path), // the next line will contain the remaining columns. lines=lines.slice(1); var headOfs={filesystem:0,blocks:head.indexOf('1K-blocks'), used:head.indexOf('Used'),available:head.indexOf('Available'), usepct:head.indexOf('Use%'),mountedOn:head.indexOf('Mounted on')}; headOfs.available = headOfs.used + 4; headOfs.used = headOfs.blocks + 9; for(var xa=0; xa<lines.length; xa++) { var item={}; var line=lines[xa]; var nextLine = (xa < lines.length-1) ? lines[xa+1] : ""; var ret=parseDfLine(headOfs, line, item, nextLine); lines[xa]=item; if(ret == null) { // Skip next line (already processed) lines[++xa] = {}; } } var roots=[]; for(var xa=0; xa<lines.length; xa++) { var ll=lines[xa]; if(ll.blocks > minSize) { var mo=ll.mountedOn; if(mo.indexOf('/dev') == 0) { continue; } if(mo.indexOf('/mnt/') == 0) { continue; } if(mo.indexOf('/cache') == 0) { continue; } if(mo.indexOf('/system') == 0) { continue; } if(mo.indexOf('/Android/data/') > -1) { continue; } roots.push(ll); } } roots.sort(function(a, b) { if(a.available > b.available) { return -1; } if(a.available < b.available) { return 1; } return 0; }); gotRoots(roots); }, function(errmsg) { // alert('Error: '+errmsg); // Uncomment if alert desired }); } function parseDfLine(headOfs, line, item, nextLine) { if(line.indexOf(' ') > -1) { if(item.filesystem == null) { item.filesystem=line.substring(0,headOfs.blocks); } item.blocks=parseInt(line.substring(headOfs.blocks, headOfs.used)); item.used=parseInt(line.substring(headOfs.used, headOfs.available)); item.available=parseInt(line.substring(headOfs.available, headOfs.usepct)); item.usepct=parseInt(line.substring(headOfs.usepct, headOfs.mountedOn)); item.mountedOn=line.substring(headOfs.mountedOn); } else { item.filesystem=line; parseDfLine(headOfs, nextLine, item, null); return null; } return item; } //////////////////////////////////////////////////////////