2

I'm currently developing a home automation user interface, running on a wall-mounted android (version 4.2.2) tablet, which, after a couple of minutes of inactivity, displays a "screensaver" html page.

enter image description here

As you can see in the screenshot above, this "screensaver" basically consists of the following features :

  1. a clock displaying the current time and date, triggered by the script date_time.js and refreshed at one second interval;
  2. a picture on the top right-hand side which shows the current status of the alarm;
  3. a picture "Touchez l'écran pour quitter le mode veille" which is randomly repositioned every 3500 miliseconds.

Both points 2) and 3) above are run by another script stored in a script called "screensaver_run.js". The method getAlarmDataFromDatabase is retrieving data from my mysql database.

Now, for the problem statement: after approximately two hours, the whole screen freezes (both the clock and the repositioning script) in such a way that even the tablet is no longer pingable. I suspect that a memory leak is occurring but after a couple of sleepless nights ... and a lot of coffee ... I am not able to find out the root cause of my issue.

Reading some documentation on the internet, notably https://www.lambdatest.com/blog/eradicating-memory-leaks-in-javascript, I have already implemented some changes such as changing the declaration of the variables from "var" to "let".

In Chrome, I run the script during 2 1/2 minutes and profiled the memory usage (I have the heaptimeline file available, if it can be of any help, but here I'm not quite sure how to analyse it?):

enter image description here

Besides, I queried the console for :

  • performance.memory.usedJSHeapSize and got some changing values : 3430886, 3195206, 4743246, 3402767, etc

  • performance.memory.jsHeapSizeLimit and got : 4294705152

Does anyone have any hint where to start investigating, knowing that debugging possibilities of this tablet are not as advanced as on a modern browser? My tablet is "already" 5 years old... Upgrading this tablet is not an option.

Many thanks for your time reading me and I do hope that my post is understandable, well documented and in the future, could help other to have restful nights :)

The code of my html page (named after 'screen_saver.html) is the following :

<!DOCTYPE html>
<html>
  <head>
    <title>Domoos | Screen saver screen</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
    <meta http-equiv="pragma" content="no-cache">
    <link rel="shortcut icon" href="favicon.ico">
    <link rel="stylesheet" type="text/css" href="css/mystyle_saver.css?rnd=999" />
    <script type="text/javascript" src="scripts/date_time.js"></script>
    <script type="text/javascript" src="scripts/screensaver_run.js"></script>
  </head>
  <body onload="runScreenSaver(); setup();">
    <div style="position:absolute" id="randomPlacement">
      <p><img src="assets/pictures/texte_sortie_veille.png" alt ="" style="width:90px;height:90px;" border="0"></p>
    </div>
    <div id="svg">
      <svg height="210" width="1020">
        <line x1="11" y1="100" x2="1015" y2="100" style="stroke:rgb(69,69,66);stroke-width:3" />
      </svg>
    </div>
    <div id="date"></div>
    <div id="time"></div>
    <div id="icon_alarm">
      <img id="img_alarm" src="assets/icons/alarme_eteinte.png" alt ="" style="width:27px;height:35px;">
    </div>
    <div id="tag_temperature">
      <p>21&deg;C</p>
    </div>
    <div id="tag_free_text">
      <p>151<sup>&egrave;me</sup> jour de l'ann&eacute;e 2020.<br>Bonsoir</p>
    </div>
    <div id="meteo_icon">
      <img src="assets/meteo_icons/eclaircies-big.png" alt="" style="width:40px;height:40px;">
    </div>
    <div id="tag_weather_condition">
      <p>Eclaircies</p>
    </div>
  </body>
</html>

Code of my javasript file screensaver_run.js:

function runScreenSaver()
{
  let xmin = 0;
  let xmax = 890;
  let ymin = 0;
  let ymax = 430;
  let sDate;
  let sTime;
  let bOverlapAuthorised;
  let bDisplayPos;
  let zRandomImage;
  let xCoord;
  let yCoord;
  let xCoordStr;
  let yCoordStr;

  bOverlapAuthorised = true;
  bDisplayPos = false;

  // If overlap is forbidden, the x min and y min parameters will be redefined to be slightly below the line
  if (!bOverlapAuthorised)
  {
    xmin = 15;
    ymin = 130;
  }

  // Computes a random x and y, based on the min and ma
  xCoord = Math.floor((Math.random()*xmax)+xmin);
  yCoord = Math.floor((Math.random()*ymax)+ymin);

  xCoordStr = xCoord.toString() + "px";
  yCoordStr = yCoord.toString() + "px";

  zRandomImage = document.getElementById("randomPlacement");
  zRandomImage.style.left = xCoordStr;
  zRandomImage.style.top = yCoordStr;

  // Instead of displaying a message in the 'tag_free_text',
  // shows the randomly defined coordinates of the 'randomPlacement' object
  if (bDisplayPos)
  {
    document.getElementById("tag_free_text").innerHTML = 'X:' + xCoordStr + '<br>Y:' + yCoordStr;
  }

  document.getElementById("date").innerhtml=getTimeDate('date');

  getAlarmDataFromDatabase();

  zRandomImage = null;
  xCoord = null;
  yCoord = null;
  xCoordStr = null;
  yCoordStr = null;
  setTimeout('runScreenSaver()','3500');
}

function setup()
{
  this.addEventListener("mousemove", exitScreenSaver, false);
  this.addEventListener("mousedown", exitScreenSaver, false);
  this.addEventListener("keypress", exitScreenSaver, false);
  this.addEventListener("DOMMouseScroll", exitScreenSaver, false);
  this.addEventListener("mousewheel", exitScreenSaver, false);
  this.addEventListener("touchstart", exitScreenSaver, false);
  this.addEventListener("MSPointerMove", exitScreenSaver, false);
}

function getAlarmDataFromDatabase()
{
  let ajax = new XMLHttpRequest();
  let id_component;
  let technical_name_html;
  let comp_value;
  let data;

  ajax.open("GET", "php/data4screensaver1.php", true);
  ajax.send();
  ajax.onreadystatechange = function() 
  {
    if (this.readyState == 4 && this.status == 200) 
    {
      data = JSON.parse(this.responseText);

      for(var a = 0; a < data.length; a++) 
      {
        id_component = data[a]['id_component'];
        technical_name_html = data[a]['technical_name_html'];
        comp_value = parseInt(data[a]['value']);
      }

      data = null;

      console.log("ID Component: " + id_component);
      //console.log("Valeur de l'alarme : " + comp_value);

      switch (comp_value) 
      {
        case 0:
        case 50:
        case 100:
          displayPictureAlarm(comp_value);
          break;

        default:
          displayPictureAlarm(-1);
          break;
      }
    }
  };

  ajax = null;
  id_component = null;
  technical_name_html = null;
  comp_value = null;
}

function exitScreenSaver(e)
{
  goActive(e);
}

function goActive(event)
{
  // do something
  console.log(".. active ..");
  //event.preventDefault();

  this.removeEventListener("mousemove", exitScreenSaver);
  this.removeEventListener("mousedown", exitScreenSaver);
  this.removeEventListener("keypress", exitScreenSaver);
  this.removeEventListener("DOMMouseScroll", exitScreenSaver);
  this.removeEventListener("mousewheel", exitScreenSaver);
  this.removeEventListener("touchstart", exitScreenSaver);
  this.removeEventListener("MSPointerMove", exitScreenSaver);

  window.open("index.html","_self");
}


function displayPictureAlarm(pValue)
{
  let z;

  z = document.getElementById("img_alarm");

  if (pValue == 0) // désarmée
  {
    z.src = "assets/icons/alarme_desarmee.png";
  }

  if (pValue == 50) // partielle
  {
    z.src = "assets/icons/alarme_partielle.png";
  }

  if (pValue == 100) // totale
  {
    z.src = "assets/icons/alarme_totale.png";
  }

  if (pValue == -1) // éteinte
  {
    z.src = "assets/icons/alarme_eteinte.png";
  }

  z = null;
}

Code of my javascript file datetime.js (as called in the the method runScreenSaver above):

function getDate(id)
{
  date = new Date;
  year = date.getFullYear();
  month = date.getMonth();
  month += 1;        
  d = date.getDate();
  day = date.getDay();
  days = new Array('Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi');

  if (d<10)
  {
    d = "0"+d;
  }
  if(month<10)
  {
    month = "0"+month;
  }

  result = ''+days[day]+' '+d+'.'+month+'.'+year;
  result = days[day]+' '+d+'.'+month+'.'+year;
  document.getElementById(id).innerHTML = result;
  setTimeout('getDate("'+id+'");','1000');

  return true;
}

function getTimeDateMainScreen()
{
  var za;
  var zb;
  var zc;
  var mydate;
  var result1;
  var result2;
  var result3;

  mydate = new Date;
  year = mydate.getFullYear();
  month = mydate.getMonth();
  day = mydate.getDate();
  weekday = mydate.getDay();
  hrs = mydate.getHours();
  mns = mydate.getMinutes();
  secs = mydate.getSeconds();

  days = new Array('Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi');

  month += 1;

  if (day < 10)
  {
    day = "0" + day;
  }

  if(month < 10)
  {
    month = "0" + month;
  }

  if(hrs < 10)
  {
    hrs = "0" + hrs;
  }
  if(mns < 10)
  {
    mns = "0" + mns;
  }
  if(secs < 10)
  {
    secs = "0" + secs;
  }

  //result = ''+days[weekday]+' '+d+'.'+month+'.'+year;
  //result = days[weekday]+' '+d+'.'+month+'.'+year;
  result1 = day + "." + month + "." + year;
  result2 = days[weekday];
  result3 = hrs + ":" + mns + ":" + secs;

  za = document.getElementById("curr_date");
  zb = document.getElementById("curr_weekday");
  zc = document.getElementById("curr_time");

  za.innerHTML = result1;
  zb.innerHTML = result2;
  zc.innerHTML = result3;

  za = null;
  zb = null;
  zc = null;
  mydate = null;
  result1 = null;
  result2 = null;
  result3 = null;

  setTimeout('getTimeDateMainScreen();','500');
}

function getTime(id)
{
  date = new Date;

  h = date.getHours();

  if(h<10)
  {
          h = "0"+h;
  }
  m = date.getMinutes();
  if(m<10)
  {
          m = "0"+m;
  }
  s = date.getSeconds();
  if(s<10)
  {
    s = "0"+s;
  }
  result = ''+h+':'+m+':'+s;
  document.getElementById(id).innerHTML = result;
  setTimeout('getTime("'+id+'");','1000');

  return true;
}

function getTime2()
{
  date = new Date;

  h = date.getHours();

  if(h<10)
  {
          h = "0"+h;
  }
  m = date.getMinutes();
  if(m<10)
  {
          m = "0"+m;
  }
  s = date.getSeconds();
  if(s<10)
  {
    s = "0"+s;
  }
  result = ''+h+':'+m+':'+s;
  document.getElementById("time").innerHTML = result;
  setTimeout('getTime2();','1000');
}

function getTimeDate(id)
{
  let date;
  let year;
  let month;
  let d;
  let day;
  let days;
  let h;
  let m;
  let s;
  let result;

  date = new Date;
  console.log("J'affiche la date3");
  year = date.getFullYear();
  month = date.getMonth();
  month += 1;
  d = date.getDate();
  day = date.getDay();
  days = new Array('Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi');
  h = date.getHours();
  m = date.getMinutes();
  s = date.getSeconds();


  if (d<10)
  {
    d = "0"+d;
  }

  if(month<10)
  {
    month = "0"+month;
  }

  if(h<10)
  {
    h = "0"+h;
  }

  if(m<10)
  {
    m = "0"+m;
  }

  if(s<10)
  {
    s = "0"+s;
  }

  result = ''+days[day]+' '+d+'.'+month+'.'+year +' ' + h+':'+m+':'+s;
  document.getElementById(id).innerHTML = result;

  date = null;
  year = null;
  month = null;
  d = null;
  day = null;
  days = null;
  h = null;
  m = null;
  s = null;
  result = null;

  setTimeout('getTimeDate("'+id+'");','1000');
  return true;   

 }

And, finally, here is my php (data4screensaver1.php) to retrieve the data from my mysql database :

<?php

$host = "ip_Address_db";
$db_user_encoded = "user_encoded";
$db_password_encoded = "pw_encoded";
$db_name_encoded = "db_name_encoded";

$conn = mysqli_connect($host, (encrypt_decrypt('decrypt', $db_user_encoded)), (encrypt_decrypt('decrypt', $db_password_encoded)), (encrypt_decrypt('decrypt', $db_name_encoded )));
$result = mysqli_query($conn, "CALL sp_tbl_domotique_components_get_lab61()");

$data = array();
while ($row = mysqli_fetch_object($result))
{
    array_push($data, $row);
}

echo json_encode($data);
exit();


function encrypt_decrypt($action, $string) 
{
    $output = false;
    $encrypt_method = "AES-256-CBC";
    $secret_key = '$SecretKey$';
    $secret_iv = '$SecretIV$';
    // hash
    $key = hash('sha256', $secret_key);

    // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
    $iv = substr(hash('sha256', $secret_iv), 0, 16);
    if ( $action == 'encrypt' ) {
        $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
        $output = base64_encode($output);
    } else if( $action == 'decrypt' ) {
        $output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
    }
    return $output;
}

?>
Laurent
  • 711
  • 2
  • 12
  • 30
  • Not sure if these questions will help, What OS is the tablet running? What is RAM available on it? In the Settings is there anything that kills long running activities? – dev-aentgs Jun 19 '20 at 16:24
  • @Felipe Zavan, thank you for the correction, it was indeed a typo – Laurent Jun 19 '20 at 16:37
  • @dev-aentgs: Thank you, the android version is 4.2.2. – Laurent Jun 19 '20 at 16:37
  • @dev-aentgs: The RAM on the tablet is 1GB DDR3. And no, there is nothing that kills long running activities on it. – Laurent Jun 19 '20 at 16:47
  • Thank you for the details not sure what could be the issue. On forums [Cnet](https://www.cnet.com/forums/discussions/tablet-keeps-freezing/), [HP](https://support.hp.com/in-en/document/c03939193) , factory reset is suggested in case of freezing tablets but it may not be the solution. – dev-aentgs Jun 19 '20 at 17:00
  • First, you should find out which devices and browsers are affected. Is that android native browser or embedded in app (it can be app bug aswell)? .If it does not occur on pc, I would analyse memory around 2h of runtime. Then you should have enough info to decide what the root cause is (javascript, app, os, browser) – bigless Jun 19 '20 at 19:56
  • @bigless I can say for sure that it is not linked with the browser embedded in my android app. The only issue that I have is that I don't have many debugging options on the tablet, like I would have with Chrome on my PC. I will gladly take your advice of letting the app run for 2 hours. Thanks for your help. Another question: do you believe that replacing var by let could have make a difference? – Laurent Jun 19 '20 at 20:24
  • 1
    In some cases it can bring some perf optimizations for js garbage collector, otherwise I dont think so. Looking at your js, this row get my attention: `setTimeout('runScreenSaver()','3500');` Is that copypaste mistake? Btw. there could be potentially very deep recursion. Did you try setInterval instead? – bigless Jun 19 '20 at 21:42
  • 1
    [https://stackoverflow.com/questions/6081560/is-there-ever-a-good-reason-to-pass-a-string-to-settimeout](https://stackoverflow.com/questions/6081560/is-there-ever-a-good-reason-to-pass-a-string-to-settimeout) – bigless Jun 19 '20 at 21:55
  • @bigless, thank you, once again :). If I read you correctly, you were intrigued by the quotes around the time (i.e. 3500 milliseconds), right?. Indeed, this was an error on my end, so I have now corrected the line to read : setTimeout('runScreenSaver()',3500); If I am not mistaken, I couldn't get rid of the quote around the call to the runScreenSaver method. Also, thank you for redirecting me to the above-mentioned stackoverflow question, this was very helpful. – Laurent Jun 20 '20 at 10:32

0 Answers0