Secure Setting Logger

Share and discuss your flows and ideas with other users.

Moderator: Martin

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Secure Setting Logger

Post by Desmanto » 05 Mar 2019 15:18

Prologue
How many times you found some setting you want to Automate using Automagic and can't find the corresponding action to change it? Or maybe you are sick with Google that keep limiting android generic function and making things hard, such as changing network mode/location now require root in newer android version. Or you are using custom ROM that has some special setting that doesn't have the usual toggling method. Even if you have root, you probably can't change the value automatically. Those above can be done using Control UI, which I have explained here : viewtopic.php?f=6&t=7320 Unfortunately, Control UI has UI distraction and doesn't work when the screen is off/locked. So in here, we are going to play with one of the powerful, yet dangerous part of the Android System, Write System setting. (also called write secure setting)

System setting database contains most of the setting from generic to secure ones. Network mode, display brightness, volume and many various settings are stored in here. There are 3 categories until Android Pie 9.0, which are (from android documentation)
System : System settings, containing miscellaneous system preferences. This table holds simple name/value pairs. There are convenience functions for accessing individual settings entries.
Secure : Secure system settings, containing system preferences that applications can read but are not allowed to write. These are for preferences that the user must explicitly modify through the system UI or specialized APIs for those values, not modified directly by applications.
Global : Global system settings, containing preferences that always apply identically to all defined users. Applications can read these but are not allowed to write; like the "Secure" settings, these are for preferences that the user must explicitly modify through the system UI or specialized APIs for those values

Prior to Marshmallow 6.0, these system setting is stored in SQLite db, /data/data/com.android.providers.settings/databases/settings.db
After Marshmallow 6.0 above, the format has changed to xml and store in /data/system/users/0/settings_global.xml, /data/system/users/0/settings_secure.xml and /data/system/users/0/settings_system.xml
(My flow here will use the Marshmallow 6.0 above format, since I have Pie 9.0 now.)

Purpose of the flow
Since these setting database contains one of the setting that you can change, it is curious to us that which setting name (key) correspond to the setting we want to change. Example, location feature is usually stored at Secure Category with key : location_providers_allowed and value : network,gps. How we supposed to know that? Majority of the key value will be similar across multiple android devices. But with so many various OEM/brand implementation, some of these value can have different names or located in different database. Not to mention any special feature for certain vendors, they can have some unique naming.

Forget about the detail of the key value, just remember any setting you want to find/use, will most likely fall into one of these 3 categories. So the purpose of this flow, is to help you find the corresponding key value that you can use to change the setting later. Example, to find that location_providers_allowed is the one responding to set the location feature.

WARNING
Warning : Enabling adb, and using Action Set System Setting improperly can brick your phone. You may lose your data and may need reset your phone. There are cases of bootloop caused by improper modification to the system setting. Make sure you understand the risk before continuing. When in doubt, just stop. Do With Your Own Risk.



Prerequisite
In order to use this flow, you must have granted Automagic the write secure setting permission. If you have grant it (or you have root), you can skip to Flow Import directly.

PC setup
You need adb for this, of course means a PC. Can be windows (preferred), linux or mac. No root is required, that's why we need the help of adb. Xda has a quite neat tutorial for this : How to Install ADB on Windows, macOS, and Linux. You should check it out.
For windows, the fastest way to install the adb is to use adb installer. Just need to run it as admin, yes yes yes, and it is done.

You might to need to install your phone specific driver to make sure the adb can recognize your phone. My previous phones are in this category, need to install additional phone driver provided in the internal emulated CD built-in. Do whatever necessary until adb can detect your devices. You might need to disable firewall/antivirus for a while during the adb command, if the firewall/antivirus policy is too tight.

Phone setup
At your phone, make sure the USB debugging is on, which is hidden in developer options. To enable developer options (if you haven't), tap 7 times at the build number/kernel version at about phone. After that, go to settings > Developer option > enable the USB Debugging, tap OK for the prompt. Developer option can be located on different place, depends on the phone/ROM.

After adb and drivers has been installed properly, and USB debugging has been turned on, connect your phone with the PC using the original cable (or any cable that support data transfer). For some phones, you might need to switch to MTP to enable the USB Debugging detection. You might see some drivers installing if it is the first time you connect the adb.

Command Prompt
At PC, open command prompt/terminal at the adb folder (or can be anywhere if you install system-wide). Type this command to check if your device has been recognized

Code: Select all

adb devices
You should see the some 8 hexadecimal that is your device adb serial number. There will be a prompt to allow the USB debugging connection for this PC, tap OK at the phone.

Then type this (or just copy from here, right click at command prompt > paste) to grant automagic write secure setting permission

Code: Select all

adb shell pm grant ch.gridvision.ppam.androidautomagic android.permission.WRITE_SECURE_SETTINGS
After you enter it, there will be nothing display to indicate it is success. You can only know if it errors, there will be error code displayed. So if nothing appear, you are good to go. But if you just want to make sure, you can check it using dumpsys, make sure granted=true.

Code: Select all

adb shell dumpsys package ch.gridvision.ppam.androidautomagic | grep WRITE_SECURE
You should see something like this.
android.permission.WRITE_SECURE_SETTINGS
lineageos.permission.WRITE_SECURE_SETTINGS
lineageos.permission.WRITE_SECURE_SETTINGS: granted=true
android.permission.WRITE_SECURE_SETTINGS: granted=true


The permission has been granted, now we can continue on the phone. Eject/unmount any mounted drive from you phone and unplug the cable. PC is not needed anymore.
You only need grant the permission once. The permission sustained until you uninstall automagic, factory reset your or change ROM.

Grant Write PC without PC
For Root users, you have the privellege. You can simply run the command above directly from terminal emulator with root privellege. (without adb shell)

Code: Select all

pm grant ch.gridvision.ppam.androidautomagic android.permission.WRITE_SECURE_SETTINGS
Or you can also use Automagic action : Execute Root Command, copy-paste the command above, execute it manually once and done.



Flow import
Download this flow and import it : Secure Setting Logger
[UPDATE : Modified version from vertigo are below]

You will have 2 flows, Secure Setting and Secure Setting Logger. Just enable Secure Setting flow only, add the shortcut to the home screen for easy access. Secure Setting Logger flow will be enabled by using Secure Setting flow choices later.

In order to start logging, you have to create the glovar first. I use the glovar name global_secure_setting, create it manually with type list, leave it blank, OK. Or you can create it using script, execute below script manually.

Code: Select all

global_secure_setting = newList();
This is to store the logged glovar later. For root user, you should create another folder under the /storage/emulated/0/Automagic/Resources/, because this is where I store the sec.txt, the result of the query. You can of course change the save location, change the script also.


Modified version
Modified version from vertigo, with some extra features
vertigo wrote:
03 May 2020 19:04
As mentioned in another thread, I made some tweaks to this, to hopefully make it a bit easier to use. Up to you if you want to include it in your main post, but I thought it should at least be posted here for others to see and use.

Here are the changes:

1. Blank line added between log entries to make them more readable.
2. Checks if AutoMagic has root and only shows menu items that require root if it does.
3. Checks if global_secure_setting var is defined and creates it as newList() if not, therefore eliminating the need for the user to do this manually before use.
4. Added a "Start Logging (Reset Log)" option which clears the log before running, to make it easier to determine what results are from the latest run.
5. Checks if auto/adaptive-brightness is enabled when logging is started and, if so, disables it. I realize some people may not like that, so I considered just doing a message box warning them, but ultimately decided this way is probably better.

Download the flow : http://automagic4android.com/flow.php?i ... 61e6d6a5da

Flow branches
Secure Setting Logger flow is a single branch, only for logging purpose. So it is easy to understand. But the main flow Secure Setting, has 5 main branches (and 2 sub branches). For non root user, you can only use the 3 branches on the right, which is Start Logging, Stop Logging, and Show Last Log. Root user can use all of them.
1. Secure Setting Flow.png
1. Secure Setting Flow.png (355.95 KiB) Viewed 39587 times
Unfortunately, until Automagic action Init Variable System Setting can support global pattern or all settings, there is only root method to query all available settings. I use the cat /data/system/users/0/settings_global.xml (secure and system) method, instead of the built-in settings list global. Because xml can provide more detail data, there is default value and the last app which modify the value. But currently I only use the key and value, so default value and last app are useless for now. Despite that, I still keep this method in case I am going to expand the flow later.

How to use
For non root user, simply tap the shortcut you have created. Choose Start Logging for the first time. (make sure you have create the glovar before). This will enable Secure Setting Logger flow, which will start logging all system setting changes. Go to the setting of your phone and change something, example change the network mode for SIM 1. You should see a toast message containing the [category, key, old value and the new value]. Toast message only indicate the changes, you can check the changes later. Change as many thing as you want/need and see the toast message.
2. Change Network Mode.png
2. Change Network Mode.png (206.96 KiB) Viewed 39586 times
Once you have finished, tap the shortcut again and choose Stop Logging. This will disable the Secure Setting Logger flow and show you all changes that happens since you started to enable the flow. You can choose any of these and you will be presented option to copy category, key, value from the choosen. Tap any of these to copy it to clipboard. Usually you will need the key only, and sometimes value. You can remember the category easily. You can use this value later in action Set System Setting. In case you mis-closes the input dialog, you can still reopen last log by running the flow again and choose Show Last Log.

The logger flow has been set to stop after 20 executions. This is to prevent the logger flow running forever. You can increase this by changing the execution count. You can also change the maximum last log stored (default 20).

Root branch
For root user, besides above feature, you can do more. You can choose Save All Settings, this will query all available setting using root command and save it to /storage/emulated/0/Automagic/Resources/sec.txt. The sec.txt is json, in form of nested map-list, with the key is the setting name and the list are [id, value, packagename, default value, and category].

Once you save the setting, you can use it for future comparison. Use your phone as usual, change some setting and run the flow again. Now choose What Changed? This will query current system setting and compare it to the sec.txt we have saved before, find out the changes and show it. You can choose any of those value to copy the category, key, and value. This is very useful for some setting that you don't know when it will change and you don't want to run the logger flow all the time.

Another cool feature you can use is to Show All. This will show all key value including the category it belongs to. You can also choose to Show Global, to show only global category; the same for secure and system. Last, you can even use Search keyword feature. Put in the keyword you want to find, and it will query upon all key or value and show you the matched ones. This is extremely useful if you already know part of the keyword. Searching in each one of the category require 3 searches, but using this only requires single search. However, please remember that all option from Show All, Search keyword, show global/secure/system; use the sec.txt you have save before. So you have to run the Save All Settings at least once. And remember that these choices doesn't show current setting value. To show current one, you have to choose Save All Settings once again before using any of these 5 choices. I purposely make it this way, so you don't query root command too much to the system. Because usually we only need to save once, but can search for several times.

Some catch for Oreo and Pie
For Android Oreo and Pie users, before you start logging, maybe you should turn off Auto brightness (Oreo) or Adaptive Brightness (Pie). Both android version will change the value too frequently if you are in the dynamic lighting condition or you cover your light sensor when using your phone. You will understand it if you don't turn it off and see so many toast message indicating the changes. If you look at my log, you can see half of the log is "contaminated" by
[System] screen_brightness changes.


Using value found
After you got the value, you can copy from the logging result, you can then add the Action Set System Setting, choose the category it has shown to you and paste/search the key/value you've got before. Since it shows you the possibility of the value too, you can try to mimic the same value. Example, [secure] location_providers_allowed, can have value : network when only network location provider battery saving mode enabled. When the GPS is on, the value changed to network,gps You can then use +gps in here to add the gps value to this key. Or -gps to remove the gps. This is the same as turning on/off the GPS state. Remember to do it this way, as you can only change one value at one time. Some phone/ROM allow the changes more than one at once, but if your phone/ROM doesn't support it, change only one value at one time. Below is example from the network that we have got from previous test.
3. Set System Setting.png
3. Set System Setting.png (455.92 KiB) Viewed 39586 times
For other value, sometimes it is single digit number. Some are toggling value, such as 0 and 1, maybe 0 and 2. Some can be choices of number, can be 0, 1, 2, or 3. Some have packagename inside. Just mimic whatever state you got from the changes. You can also use this in Trigger System Setting Changed, to monitor the value of this setting. Example, you can monitor if gaming mode has been enabled, then you start the timer to count how long you enable the gaming mode.

However, remember that, not all device/ROM honor/reflect the changes you made directly to the system setting database. Sometimes you still have to open the setting page which contain the setting you are trying to change (for network mode, have to use launch app to go directly to that setting page). As explained above, some setting are protected by special API or require some UI interaction to get the changes. For those kind of setting, we are out of luck. We can't change the value directly, only can act on the changes of the value (trigger System Setting Changed still works). Control UI still works though, but yeah, hindered by UI distraction. Example, global - sysui_tuner_demo_on, which correspond to the system UI demo mode. When we enable Demo mode, this will be changed to 1. However, changing this value from 0 to 1, won't enable Demo mode. Because demo mode must be enabled by using special broadcast com.android.systemui.demo, with android.permission.DUMP or root privellege. sysui_tuner_demo_on only store the state of the demo mode, but doesn't reflect the changes upon it.


Adb shell List settings
For root user, some of you might be asking why I don't use built-in settings root command to list out all possible system settings

Code: Select all

settings list global
settings list secure
settings list system
It is because I don't know this when I create the flow! After I learnt this command, found out it only provide key=value only. But parsing from xml, can give the last packagename modifier and default value. So I just use my own previous finished method, rather than recreate the script again to parse the result of the command.

LineageOS System Setting
Beside android built-in System Setting, custom ROM LineageOS (LOS) also has its own separated System setting. As per LOS 16, the setting is stored as SQLite db at /data/user_de/0/org.lineageos.lineagesettings/databases/lineagesettings.db. Any LOS based ROM also will have this. Example Resurrection Remix (RR) which is based on LOS, also has this database. You can check that file, if it exists, then your ROM support it.

Automagic support to Init and Set the LOS system setting too. You need to grant additional permission from adb or terminal root.

Code: Select all

adb shell pm grant ch.gridvision.ppam.androidautomagic lineageos.permission.WRITE_SECURE_SETTINGS
As per LOS 15.1, the actions are still working. However since LOS 16, and until Automagic 1.37.0, Automagic can't recognize it anymore. I am using RR 7.0.0 Pie 9.0 based on LOS 16, Set LineageOS System Setting now give me error : "Changing LineageOS system settings is not supported on this ROM". So we should wait until maybe LOS fix it or maybe Martin add support for LOS 16.

We hope to get the similar action set as in default android System Setting too. I can't built the flow for it, since there is no trigger to detect the changes. But since LOS based user are minority, even less than the percentage of root users, so this maybe not such a pressing need to support it. I just document this here for completeness.

Epilogue
Hopefully, this flow will help you to find the correct system setting you can modify to help in creating another flow. However, remember to be careful when changing these value. Make sure you have made proper backup first, either your other data, the setting or Automagic's flow backup (you can backup your flow using my Automagic Backup Flow : viewtopic.php?f=3&t=7858). Changing/deleting wrong value can result in catastrophe, as I have experienced here : viewtopic.php?f=4&t=7272

At last, Happy Logging! :D
Last edited by Desmanto on 05 May 2020 18:34, edited 1 time in total.
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.

tphg
Posts: 57
Joined: 17 Apr 2017 05:31

Re: Secure Setting Logger

Post by tphg » 06 Mar 2019 00:58

Always see academic post from you. Thanks @desmanto for the useful tricks.

moonspeak
Posts: 25
Joined: 15 Jul 2015 19:42

Re: Secure Setting Logger

Post by moonspeak » 28 Mar 2019 20:52

This is fantastic! I have wanted this exact functionality for a long time. Needed it so many times. But I somehow never thought about copying the settings files to compare changes (guess I didn't realize they simply were files like that).

Thank you so much for sharing your work. I can't wait to use this! I agree with tphg; you're always posting very in-depth and informative stuff. And it is appreciated! :)

anuraag
Posts: 371
Joined: 24 Jan 2015 02:06

Re: Secure Setting Logger

Post by anuraag » 08 Nov 2019 23:38

@desmanto
I request you to create similar for logcat when 1.38 out of EAP. You might have seen tasker's latest beta feature.

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Re: Secure Setting Logger

Post by Desmanto » 10 Nov 2019 18:25

anuraag wrote:
08 Nov 2019 23:38
@desmanto
I request you to create similar for logcat when 1.38 out of EAP. You might have seen tasker's latest beta feature.
I am actually about to request the feature. Currently I use a simple root command logcat with 3 seconds before to log certain action. Then parse it to get the event and filter it catch that. So far, I only use it for my fingerprint sensor (trigger fingerprint gesture doesn't work at my RN5).

I can extend the result to get a similar UI with what tasker has done there (including finding keyword). The only problem is I am afraid that if the Command Output trigger is enable all the time, it will consume much more battery, as it keep looking at the logacat to filter out some words. I don't know how tasker deal with it under the hood. Maybe Martin can give us some clue. When I have some time, I will test the command result and check if it drains battery significantly.

And even if 1.38 is out, I don't know if the execute command alone (without root) has enough permission to get the read log permission (in order to logcat). Maybe we need a new trigger specifically for this, similar to tasker. And in this case, I don't need to create the flow anymore :)
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.

vertigo
Posts: 147
Joined: 28 Oct 2018 00:28

Re: Secure Setting Logger

Post by vertigo » 03 May 2020 19:04

As mentioned in another thread, I made some tweaks to this, to hopefully make it a bit easier to use. Up to you if you want to include it in your main post, but I thought it should at least be posted here for others to see and use.

Here are the changes:

1. Blank line added between log entries to make them more readable.
2. Checks if AutoMagic has root and only shows menu items that require root if it does.
3. Checks if global_secure_setting var is defined and creates it as newList() if not, therefore eliminating the need for the user to do this manually before use.
4. Added a "Start Logging (Reset Log)" option which clears the log before running, to make it easier to determine what results are from the latest run.
5. Checks if auto/adaptive-brightness is enabled when logging is started and, if so, disables it. I realize some people may not like that, so I considered just doing a message box warning them, but ultimately decided this way is probably better.

Since I get the error "The upload was rejected because the uploaded file was identified as a possible attack vector" when trying to upload the updated flows here, I'll just paste the xml code here:

Code: Select all

<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<data version="1.37.0">
  <trigger type="shortcut">
    <useDefaultName>true</useDefaultName>
    <name>Shortcut: Secure Set Tools</name>
    <enabled>true</enabled>
    <title>Secure Set Tools</title>
  </trigger>
  <trigger type="system_setting_changed">
    <useDefaultName>true</useDefaultName>
    <name>System Setting Changed: Global *</name>
    <enabled>false</enabled>
    <settingCategory>GLOBAL</settingCategory>
    <settingName>*</settingName>
    <variable>setting</variable>
  </trigger>
  <trigger type="system_setting_changed">
    <useDefaultName>true</useDefaultName>
    <name>System Setting Changed: Secure *</name>
    <enabled>false</enabled>
    <settingCategory>SECURE</settingCategory>
    <settingName>*</settingName>
    <variable>setting</variable>
  </trigger>
  <trigger type="system_setting_changed">
    <useDefaultName>true</useDefaultName>
    <name>System Setting Changed: System *</name>
    <enabled>false</enabled>
    <settingCategory>SYSTEM</settingCategory>
    <settingName>*</settingName>
    <variable>setting</variable>
  </trigger>
  <condition type="execution_count">
    <useDefaultName>true</useDefaultName>
    <name>Execution Count: 20 times</name>
    <count>20</count>
    <timed>false</timed>
    <duration>60000</duration>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: containsElement(newList( "Save all settings", "What changed?" ), value)</name>
    <expression>containsElement(newList(
"Save all settings",
"What changed?"
), value)</expression>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: containsElement(newList( "Show All", "Search keyword", "Show Global", "Show Secure", "Show System" ), value)</name>
    <expression>containsElement(newList(
"Show All",
"Search keyword",
"Show Global",
"Show Secure",
"Show System"
), value)</expression>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: if(operation == "ok") { find = findAll(value, '(\\[.*?\\]) (.*?) : (?:(.*?) &gt;&gt;&gt; (.*)|(.*))', true); //find = [category, key, (oldv &gt;&gt;&gt; newv | newv) ] if(isEmpty(find)) return false; cate = find[0][1]; key = find[0][2]; oldv = find[0][3]; newv = if(find[0][5] == null) find[0][4] else find[0][5]; copycho = newList( "{cate} {key} : {newv}", key, newv); //add oldv format if from changed if(oldv != null) { addElement(copycho, 0, "{cate} {key} : {oldv} &gt;&gt;&gt; {newv}"); addElement(copycho, 3, oldv); } return true; } return false;</name>
    <expression>if(operation == "ok")
{
  find = findAll(value, '(\\[.*?\\]) (.*?) : (?:(.*?) &gt;&gt;&gt; (.*)|(.*))', true);
  //find = [category, key, (oldv &gt;&gt;&gt; newv | newv) ]

  if(isEmpty(find))
    return false;
  cate = find[0][1];
  key = find[0][2];
  oldv = find[0][3];
  newv = if(find[0][5] == null) find[0][4] else find[0][5];

  copycho = newList(
  "{cate} {key} : {newv}",
  key,
  newv);
  
  //add oldv format if from changed
  if(oldv != null)
  {
    addElement(copycho, 0, "{cate} {key} : {oldv} &gt;&gt;&gt; {newv}");
    addElement(copycho, 3, oldv);
  }
  return true;
}
return false;</expression>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: if(operation == "ok") { find = findAll(value, '\\d+\\. (\\[.*?\\]) (.*?) : (.*)', true); copycho = newList( "{find[0][1]} {find[0][2]} : {find[0][3]}", find[0][2], find[0][3] ); return true; } return false;</name>
    <expression>if(operation == "ok")
{
  find = findAll(value, '\\d+\\. (\\[.*?\\]) (.*?) : (.*)', true);
  copycho = newList(
  "{find[0][1]} {find[0][2]} : {find[0][3]}",
  find[0][2],
  find[0][3] );
  return true;
}
return false;</expression>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: if(operation == "ok") { sec = global_secure_setting[index]; copycho = newList( "[{sec[1]}] {sec[2]} : {sec[3]} &gt;&gt;&gt; {sec[4]}", "[{sec[1]}] {sec[2]} : {sec[4]}", sec[2], sec[3], sec[4]) ; return true; } return false;</name>
    <expression>if(operation == "ok")
{
  sec = global_secure_setting[index];
  copycho = newList(
  "[{sec[1]}] {sec[2]} : {sec[3]} &gt;&gt;&gt; {sec[4]}",
  "[{sec[1]}] {sec[2]} : {sec[4]}",
  sec[2],
  sec[3],
  sec[4]) ;
  return true;
}
return false; </expression>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: if(operation == "ok") { value = toLowerCase(value); for(i in getMapKeys(secmap)) { if(contains(toLowerCase(i), value) OR contains(toLowerCase(secmap[i][1]), value)) { addElement(choice, "{no}. [{secmap[i][4]}] {i} : {secmap[i][1]}"); no = no + 1; } } return true; } return false;</name>
    <expression>if(operation == "ok")
{
  value = toLowerCase(value);
  for(i in getMapKeys(secmap))
  {
    if(contains(toLowerCase(i), value) OR contains(toLowerCase(secmap[i][1]), value))
    {
      addElement(choice, "{no}. [{secmap[i][4]}] {i} : {secmap[i][1]}");
      no = no + 1;
    }
  }
  return true;
}
return false;</expression>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: operation == "ok"</name>
    <expression>operation == "ok"</expression>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: value == "Save all settings"</name>
    <expression>value == "Save all settings"</expression>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: value == "Search keyword"</name>
    <expression>value == "Search keyword"</expression>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: value == "Show Last Log"</name>
    <expression>value == "Show Last Log"</expression>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: value == "Start Logging (Reset Log)"</name>
    <expression>value == "Start Logging (Reset Log)"</expression>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: value == "Start Logging"</name>
    <expression>value == "Start Logging"</expression>
  </condition>
  <condition type="expression">
    <useDefaultName>true</useDefaultName>
    <name>Expression: value == "Stop Logging"</name>
    <expression>value == "Stop Logging"</expression>
  </condition>
  <condition type="root_setting_enabled">
    <useDefaultName>true</useDefaultName>
    <name>Root Setting Enabled</name>
  </condition>
  <condition type="screen_auto_brightness_enabled">
    <useDefaultName>true</useDefaultName>
    <name>Screen Automatic Brightness Enabled</name>
  </condition>
  <action type="copy_text_to_clipboard">
    <useDefaultName>true</useDefaultName>
    <name>Copy Text to Clipboard: {value}</name>
    <text>{value}</text>
  </action>
  <action type="execute_root_command">
    <useDefaultName>true</useDefaultName>
    <name>Execute Root Command: {cmd} in </name>
    <command>{cmd}</command>
    <user></user>
    <seContext></seContext>
    <workingDirectory></workingDirectory>
    <timeout>5000</timeout>
    <variableStdout>stdout</variableStdout>
    <variableStderr>stderr</variableStderr>
    <variableExitCode>exit_code</variableExitCode>
  </action>
  <action type="init_variable_text_file">
    <useDefaultName>true</useDefaultName>
    <name>Init Variable Text File: {secpath} to file_text</name>
    <path>{secpath}</path>
    <encoding>UTF-8</encoding>
    <variable>file_text</variable>
    <stripBOM>true</stripBOM>
  </action>
  <action type="input_dialog">
    <useDefaultName>true</useDefaultName>
    <name>Input Dialog: "{value}", Tap the key value to copy it Single Choice Menu {choice,listformat,comma}</name>
    <title>"{value}", Tap the key value to copy it</title>
    <inputDialogType>SINGLE_CHOICE_MENU</inputDialogType>
    <prompt></prompt>
    <inputValues>{choice,listformat,comma}</inputValues>
    <defaultValue></defaultValue>
    <titleFontSize>DEFAULT</titleFontSize>
    <promptFontSize>DEFAULT</promptFontSize>
    <fontSize>DEFAULT</fontSize>
    <timeoutEnabled>false</timeoutEnabled>
    <timeout>60000</timeout>
    <defaultValueOnTimeout></defaultValueOnTimeout>
    <showWhenLocked>false</showWhenLocked>
    <turnScreenOn>false</turnScreenOn>
    <keepScreenOn>false</keepScreenOn>
    <enlargeDialogWidth>false</enlargeDialogWidth>
    <keyboardCapitalizeSentences>false</keyboardCapitalizeSentences>
  </action>
  <action type="input_dialog">
    <useDefaultName>true</useDefaultName>
    <name>Input Dialog: Search keyword Single-line text Please type the search keyword</name>
    <title>Search keyword</title>
    <inputDialogType>SINGLE_LINE_TEXT</inputDialogType>
    <prompt>Please type the search keyword</prompt>
    <inputValues></inputValues>
    <defaultValue></defaultValue>
    <titleFontSize>DEFAULT</titleFontSize>
    <promptFontSize>DEFAULT</promptFontSize>
    <fontSize>DEFAULT</fontSize>
    <timeoutEnabled>false</timeoutEnabled>
    <timeout>60000</timeout>
    <defaultValueOnTimeout></defaultValueOnTimeout>
    <showWhenLocked>false</showWhenLocked>
    <turnScreenOn>false</turnScreenOn>
    <keepScreenOn>false</keepScreenOn>
    <enlargeDialogWidth>false</enlargeDialogWidth>
    <keyboardCapitalizeSentences>false</keyboardCapitalizeSentences>
  </action>
  <action type="input_dialog">
    <useDefaultName>true</useDefaultName>
    <name>Input Dialog: Secure settings tools Single Choice Menu {choice,listformat,comma}</name>
    <title>Secure settings tools</title>
    <inputDialogType>SINGLE_CHOICE_MENU</inputDialogType>
    <prompt></prompt>
    <inputValues>{choice,listformat,comma}</inputValues>
    <defaultValue></defaultValue>
    <titleFontSize>DEFAULT</titleFontSize>
    <promptFontSize>DEFAULT</promptFontSize>
    <fontSize>DEFAULT</fontSize>
    <timeoutEnabled>false</timeoutEnabled>
    <timeout>60000</timeout>
    <defaultValueOnTimeout></defaultValueOnTimeout>
    <showWhenLocked>false</showWhenLocked>
    <turnScreenOn>false</turnScreenOn>
    <keepScreenOn>false</keepScreenOn>
    <enlargeDialogWidth>false</enlargeDialogWidth>
    <keyboardCapitalizeSentences>false</keyboardCapitalizeSentences>
  </action>
  <action type="input_dialog">
    <useDefaultName>true</useDefaultName>
    <name>Input Dialog: Tap the key value to copy it Single Choice Menu {choice,listformat,comma}</name>
    <title>Tap the key value to copy it</title>
    <inputDialogType>SINGLE_CHOICE_MENU</inputDialogType>
    <prompt></prompt>
    <inputValues>{choice,listformat,comma}</inputValues>
    <defaultValue></defaultValue>
    <titleFontSize>DEFAULT</titleFontSize>
    <promptFontSize>DEFAULT</promptFontSize>
    <fontSize>DEFAULT</fontSize>
    <timeoutEnabled>false</timeoutEnabled>
    <timeout>60000</timeout>
    <defaultValueOnTimeout></defaultValueOnTimeout>
    <showWhenLocked>false</showWhenLocked>
    <turnScreenOn>false</turnScreenOn>
    <keepScreenOn>false</keepScreenOn>
    <enlargeDialogWidth>false</enlargeDialogWidth>
    <keyboardCapitalizeSentences>false</keyboardCapitalizeSentences>
  </action>
  <action type="input_dialog">
    <useDefaultName>true</useDefaultName>
    <name>Input Dialog: Which to Copy? Single Choice Menu {copycho,listformat,comma}</name>
    <title>Which to Copy?</title>
    <inputDialogType>SINGLE_CHOICE_MENU</inputDialogType>
    <prompt></prompt>
    <inputValues>{copycho,listformat,comma}</inputValues>
    <defaultValue></defaultValue>
    <titleFontSize>DEFAULT</titleFontSize>
    <promptFontSize>DEFAULT</promptFontSize>
    <fontSize>DEFAULT</fontSize>
    <timeoutEnabled>false</timeoutEnabled>
    <timeout>60000</timeout>
    <defaultValueOnTimeout></defaultValueOnTimeout>
    <showWhenLocked>false</showWhenLocked>
    <turnScreenOn>false</turnScreenOn>
    <keepScreenOn>false</keepScreenOn>
    <enlargeDialogWidth>false</enlargeDialogWidth>
    <keyboardCapitalizeSentences>false</keyboardCapitalizeSentences>
  </action>
  <action type="notification_screen">
    <useDefaultName>true</useDefaultName>
    <name>Notification on Screen: [{category}] {setting_name} : {old_setting} &gt;&gt;&gt; {setting}</name>
    <showText>true</showText>
    <text>[{category}] {setting_name} : {old_setting} &gt;&gt;&gt; {setting}</text>
    <widgetName></widgetName>
    <width>200</width>
    <height>250</height>
    <stretchToFillArea>false</stretchToFillArea>
    <opacity>1.00</opacity>
    <durationLong>false</durationLong>
    <customPosition>false</customPosition>
    <gravityType>TOP_LEFT</gravityType>
    <xOffset>0</xOffset>
    <yOffset>0</yOffset>
  </action>
  <action type="notification_screen">
    <useDefaultName>true</useDefaultName>
    <name>Notification on Screen: All secure setting has been saved.</name>
    <showText>true</showText>
    <text>All secure setting has been saved.</text>
    <widgetName></widgetName>
    <width>200</width>
    <height>250</height>
    <stretchToFillArea>false</stretchToFillArea>
    <opacity>1.00</opacity>
    <durationLong>false</durationLong>
    <customPosition>false</customPosition>
    <gravityType>TOP_LEFT</gravityType>
    <xOffset>0</xOffset>
    <yOffset>0</yOffset>
  </action>
  <action type="notification_screen">
    <useDefaultName>true</useDefaultName>
    <name>Notification on Screen: Secure Setting Logger Started</name>
    <showText>true</showText>
    <text>Secure Setting Logger Started</text>
    <widgetName></widgetName>
    <width>200</width>
    <height>250</height>
    <stretchToFillArea>false</stretchToFillArea>
    <opacity>1.00</opacity>
    <durationLong>false</durationLong>
    <customPosition>false</customPosition>
    <gravityType>TOP_LEFT</gravityType>
    <xOffset>0</xOffset>
    <yOffset>0</yOffset>
  </action>
  <action type="notification_screen">
    <useDefaultName>true</useDefaultName>
    <name>Notification on Screen: Secure Setting Logger Stopped</name>
    <showText>true</showText>
    <text>Secure Setting Logger Stopped</text>
    <widgetName></widgetName>
    <width>200</width>
    <height>250</height>
    <stretchToFillArea>false</stretchToFillArea>
    <opacity>1.00</opacity>
    <durationLong>false</durationLong>
    <customPosition>false</customPosition>
    <gravityType>TOP_LEFT</gravityType>
    <xOffset>0</xOffset>
    <yOffset>0</yOffset>
  </action>
  <action type="notification_screen">
    <useDefaultName>true</useDefaultName>
    <name>Notification on Screen: {value}

copied (long)</name>
    <showText>true</showText>
    <text>{value}

copied</text>
    <widgetName></widgetName>
    <width>200</width>
    <height>250</height>
    <stretchToFillArea>false</stretchToFillArea>
    <opacity>1.00</opacity>
    <durationLong>true</durationLong>
    <customPosition>false</customPosition>
    <gravityType>TOP_LEFT</gravityType>
    <xOffset>0</xOffset>
    <yOffset>0</yOffset>
  </action>
  <action type="script">
    <useDefaultName>true</useDefaultName>
    <name>Script: //define map and choose select secmap = fromJSON(file_text); select = toLowerCase(replace(value, "Show ", "")); choice = newList(); no = 1; //show all if(select == "all") { for(i in getMapKeys(secmap)) { addElement(choice, "{no}. [{secmap[i][4]}] {i} : {secmap[i][1]}"); no = no + 1; } } //separate show global, secure, system if(containsElement(sectri, select)) { for(i in getMapKeys(secmap)) { if(secmap[i][4] == select) { addElement(choice, "{no}. [{secmap[i][4]}] {i} : {secmap[i][1]}"); no = no + 1; } } }</name>
    <script>//define map and choose select
secmap = fromJSON(file_text);
select = toLowerCase(replace(value, "Show ", ""));

choice = newList(); no = 1;

//show all
if(select == "all")
{
  for(i in getMapKeys(secmap))
  {
    addElement(choice, "{no}. [{secmap[i][4]}] {i} : {secmap[i][1]}");
    no = no + 1;
  }
}

//separate show global, secure, system
if(containsElement(sectri, select))
{
  for(i in getMapKeys(secmap))
  {
    if(secmap[i][4] == select)
    {
      addElement(choice, "{no}. [{secmap[i][4]}] {i} : {secmap[i][1]}");
      no = no + 1;
    }
  }
}</script>
  </action>
  <action type="script">
    <useDefaultName>true</useDefaultName>
    <name>Script: //split stdout and remove the first blank element sec = split(stdout, "&lt;\\?xml version='1.0' encoding='UTF-8' standalone='yes' \\?&gt;"); removeElement(sec, 0); map = newMap(); for(i in [0 to 2]) { find = findAll(sec[i], ' &lt;setting id="(\\d+)" name="(.*)" value="(.*)" package="(.*?)"(?: defaultValue="(.*?)")?(?: defaultSysSet="(true|false)?")? /&gt;', true); for(j in find) addMapEntry(map, j[2], newList(j[1], j[3], j[4], j[5], sectri[i])); //setting name : [id, value, packagename, default value, category] } //sort the map secmap = newMap(); sort = sort(getMapKeys(map), true, true); for(i in sort) secmap[i] = map[i];</name>
    <script>//split stdout and remove the first blank element
sec = split(stdout, "&lt;\\?xml version='1.0' encoding='UTF-8' standalone='yes' \\?&gt;");
removeElement(sec, 0);

map = newMap();

for(i in [0 to 2])
{
  find = findAll(sec[i], '  &lt;setting id="(\\d+)" name="(.*)" value="(.*)" package="(.*?)"(?: defaultValue="(.*?)")?(?: defaultSysSet="(true|false)?")? /&gt;', true);

  for(j in find)
    addMapEntry(map, j[2], newList(j[1], j[3], j[4], j[5], sectri[i]));
  //setting name : [id, value, packagename, default value, category]
}

//sort the map
secmap = newMap();
sort = sort(getMapKeys(map), true, true);
for(i in sort)
  secmap[i] = map[i];</script>
  </action>
  <action type="script">
    <useDefaultName>true</useDefaultName>
    <name>Script: choice = newList( "Save all settings", "What changed?", "Show All", "Search keyword", "Show Global", "Show Secure", "Show System", "Start Logging (Reset Log)", "Start Logging", "Stop Logging", "Show Last Log"); cmd = "cat /data/system/users/0/settings_global.xml /data/system/users/0/settings_secure.xml /data/system/users/0/settings_system.xml"; secpath = "/storage/emulated/0/Automagic/Resources/sec.txt"; sectri = newList("global", "secure", "system"); if (global_secure_setting == null) { global_secure_setting = newList() }</name>
    <script>choice = newList(
"Save all settings",
"What changed?",
"Show All",
"Search keyword",
"Show Global",
"Show Secure",
"Show System",
"Start Logging (Reset Log)",
"Start Logging",
"Stop Logging",
"Show Last Log");

cmd = "cat /data/system/users/0/settings_global.xml /data/system/users/0/settings_secure.xml /data/system/users/0/settings_system.xml";

secpath = "/storage/emulated/0/Automagic/Resources/sec.txt";
sectri = newList("global", "secure", "system");

if (global_secure_setting  == null)
{
     global_secure_setting = newList()
}</script>
  </action>
  <action type="script">
    <useDefaultName>true</useDefaultName>
    <name>Script: choice = newList( "Start Logging (Reset Log)", "Start Logging", "Stop Logging", "Show Last Log"); cmd = "cat /data/system/users/0/settings_global.xml /data/system/users/0/settings_secure.xml /data/system/users/0/settings_system.xml"; secpath = "/storage/emulated/0/Automagic/Resources/sec.txt"; sectri = newList("global", "secure", "system"); if (global_secure_setting == null) { global_secure_setting = newList() }</name>
    <script>choice = newList(
"Start Logging (Reset Log)",
"Start Logging",
"Stop Logging",
"Show Last Log");

cmd = "cat /data/system/users/0/settings_global.xml /data/system/users/0/settings_secure.xml /data/system/users/0/settings_system.xml";

secpath = "/storage/emulated/0/Automagic/Resources/sec.txt";
sectri = newList("global", "secure", "system");

if (global_secure_setting  == null)
{
     global_secure_setting = newList()
}</script>
  </action>
  <action type="script">
    <useDefaultName>true</useDefaultName>
    <name>Script: choice = newList(); for(i in global_secure_setting) { date = "{i[0],dateformat,dd MMM yyyy, HH:mm:ss}"; ago = getDurationString((getDate() - i[0]) /60000 *60000); cate = i[1]; key = i[2]; oldv = i[3]; newv = i[4]; //date ago \n [category] key : oldvalue &gt;&gt;&gt; value addElement(choice, date + " ( " + ago + " ago )\n" + "[{cate}] {key} : {oldv} &gt;&gt;&gt; {newv}\n"); }</name>
    <script>choice = newList();
for(i in global_secure_setting)
{
  date = "{i[0],dateformat,dd MMM yyyy,  HH:mm:ss}";
  ago = getDurationString((getDate() - i[0]) /60000 *60000);
  cate = i[1];
  key = i[2];
  oldv = i[3];
  newv = i[4];
  
  //date ago \n [category] key : oldvalue &gt;&gt;&gt; value
  addElement(choice, date + " ( " + ago + " ago )\n" + "[{cate}] {key} : {oldv}  &gt;&gt;&gt;  {newv}\n");
}</script>
  </action>
  <action type="script">
    <useDefaultName>true</useDefaultName>
    <name>Script: global_secure_setting = newList()</name>
    <script>global_secure_setting = newList()</script>
  </action>
  <action type="script">
    <useDefaultName>true</useDefaultName>
    <name>Script: oldmap = fromJSON(file_text); //splitting to old new both oldhas = removeAllElementValues(getMapKeys(oldmap), getMapKeys(secmap)); newhas = removeAllElementValues(getMapKeys(secmap), getMapKeys(oldmap)); bothhave = removeAllElementValues(getMapKeys(oldmap), oldhas); //finding change if both have change = newList(); for(i in bothhave) { if(oldmap[i][1] != secmap[i][1]) addElement(change, i); } //Changes if both have choice = newList("Changes Happened : "); for(i in change) addElement(choice, "[{secmap[i][4]}] {i} : {oldmap[i][1]} &gt;&gt;&gt; {secmap[i][1]}"); //Old has, new doesn't if(!isEmpty(oldhas)) { addElement(choice, "\n\nOld setting has : "); for(i in oldhas) addElement(choice, "[{oldmap[i][4]}] {i} : {oldmap[i][1]}"); } //New has, old doesn't if(!isEmpty(newhas)) { addElement(choice, "\n\nNew setting has : "); for(i in newhas) addElement(choice, "[{secmap[i][4]}] {i} : {secmap[i][1]}"); }</name>
    <script>oldmap = fromJSON(file_text);

//splitting to old new both
oldhas = removeAllElementValues(getMapKeys(oldmap), getMapKeys(secmap));
newhas = removeAllElementValues(getMapKeys(secmap), getMapKeys(oldmap));
bothhave = removeAllElementValues(getMapKeys(oldmap), oldhas);

//finding change if both have
change = newList();
for(i in bothhave)
{
  if(oldmap[i][1] != secmap[i][1])
    addElement(change, i);
}

//Changes if both have
choice = newList("Changes Happened : ");
for(i in change)
  addElement(choice, "[{secmap[i][4]}] {i} : {oldmap[i][1]} &gt;&gt;&gt; {secmap[i][1]}");

//Old has, new doesn't
if(!isEmpty(oldhas))
{
  addElement(choice, "\n\nOld setting has : ");
  for(i in oldhas)
    addElement(choice, "[{oldmap[i][4]}] {i} : {oldmap[i][1]}");
}

//New has, old doesn't
if(!isEmpty(newhas))
{
  addElement(choice, "\n\nNew setting has : ");
  for(i in newhas)
    addElement(choice, "[{secmap[i][4]}] {i} : {secmap[i][1]}");
}</script>
  </action>
  <action type="script">
    <useDefaultName>true</useDefaultName>
    <name>Script: var = newList( "triggertime", "category", "setting_name", "old_setting", "setting"); category = findAll(trigger, "System Setting Changed: (\\w+) \\*", true)[0][1]; lis = newList(); for(i in var) addElement(lis, eval(i)); addElement(global_secure_setting, 0, lis); //log only last 20 if(length(global_secure_setting) &gt; 20) removeElement(global_secure_setting, 20);</name>
    <script>var = newList(
"triggertime",
"category",
"setting_name",
"old_setting",
"setting");

category = findAll(trigger, "System Setting Changed: (\\w+) \\*", true)[0][1];

lis = newList();
for(i in var)
  addElement(lis, eval(i));
addElement(global_secure_setting, 0, lis);

//log only last 20
if(length(global_secure_setting) &gt; 20)
  removeElement(global_secure_setting, 20);</script>
  </action>
  <action type="set_flow_state">
    <useDefaultName>true</useDefaultName>
    <name>Set Flow State: Disable Secure Setting Logger</name>
    <enable>false</enable>
    <includeFlowNamePatternList>Secure Setting Logger</includeFlowNamePatternList>
    <excludeFlowNamePatternList></excludeFlowNamePatternList>
  </action>
  <action type="set_flow_state">
    <useDefaultName>true</useDefaultName>
    <name>Set Flow State: Enable Secure Setting Logger</name>
    <enable>true</enable>
    <includeFlowNamePatternList>Secure Setting Logger</includeFlowNamePatternList>
    <excludeFlowNamePatternList></excludeFlowNamePatternList>
  </action>
  <action type="set_screen_brightness">
    <useDefaultName>true</useDefaultName>
    <name>Set Screen Brightness: 65</name>
    <automatic>false</automatic>
    <brightness>65</brightness>
    <changeAdaptive>false</changeAdaptive>
    <adaptiveValue>50</adaptiveValue>
    <forceUpdate>true</forceUpdate>
  </action>
  <action type="write_to_file">
    <useDefaultName>true</useDefaultName>
    <name>Write to File: {toJSON(secmap)} to {secpath} (overwrite)</name>
    <path>{secpath}</path>
    <text>{toJSON(secmap)}</text>
    <append>false</append>
  </action>
  <flow type="flow">
    <name>Secure Setting</name>
    <group>Logging</group>
    <enabled>true</enabled>
    <executionPolicy>PARALLEL</executionPolicy>
    <actioncontainer id="t1" x="280.0" y="577.5">Script: //define map and choose select secmap = fromJSON(file_text); select = toLowerCase(replace(value, "Show ", "")); choice = newList(); no = 1; //show all if(select == "all") { for(i in getMapKeys(secmap)) { addElement(choice, "{no}. [{secmap[i][4]}] {i} : {secmap[i][1]}"); no = no + 1; } } //separate show global, secure, system if(containsElement(sectri, select)) { for(i in getMapKeys(secmap)) { if(secmap[i][4] == select) { addElement(choice, "{no}. [{secmap[i][4]}] {i} : {secmap[i][1]}"); no = no + 1; } } }</actioncontainer>
    <actioncontainer id="t2" x="70.0" y="437.5">Execute Root Command: {cmd} in </actioncontainer>
    <actioncontainer id="t3" x="70.0" y="577.5">Script: //split stdout and remove the first blank element sec = split(stdout, "&lt;\\?xml version='1.0' encoding='UTF-8' standalone='yes' \\?&gt;"); removeElement(sec, 0); map = newMap(); for(i in [0 to 2]) { find = findAll(sec[i], ' &lt;setting id="(\\d+)" name="(.*)" value="(.*)" package="(.*?)"(?: defaultValue="(.*?)")?(?: defaultSysSet="(true|false)?")? /&gt;', true); for(j in find) addMapEntry(map, j[2], newList(j[1], j[3], j[4], j[5], sectri[i])); //setting name : [id, value, packagename, default value, category] } //sort the map secmap = newMap(); sort = sort(getMapKeys(map), true, true); for(i in sort) secmap[i] = map[i];</actioncontainer>
    <actioncontainer id="t4" x="280.0" y="437.5">Init Variable Text File: {secpath} to file_text</actioncontainer>
    <conditioncontainer id="t5" x="910.0" y="297.50003">Expression: value == "Stop Logging"</conditioncontainer>
    <conditioncontainer id="t6" x="1120.0002" y="297.5">Expression: value == "Show Last Log"</conditioncontainer>
    <actioncontainer id="t7" x="910.0" y="437.5">Set Flow State: Disable Secure Setting Logger</actioncontainer>
    <actioncontainer id="t8" x="910.0" y="577.5">Notification on Screen: Secure Setting Logger Stopped</actioncontainer>
    <conditioncontainer id="t9" x="70.0" y="297.5">Expression: containsElement(newList( "Save all settings", "What changed?" ), value)</conditioncontainer>
    <conditioncontainer id="t10" x="280.0" y="297.5">Expression: containsElement(newList( "Show All", "Search keyword", "Show Global", "Show Secure", "Show System" ), value)</conditioncontainer>
    <conditioncontainer id="t11" x="280.00006" y="-157.5">Root Setting Enabled</conditioncontainer>
    <triggercontainer id="t12" x="279.99994" y="-297.49994">
      <trigger>Shortcut: Secure Set Tools</trigger>
    </triggercontainer>
    <actioncontainer id="t13" x="175.00009" y="-17.499973">Script: choice = newList( "Save all settings", "What changed?", "Show All", "Search keyword", "Show Global", "Show Secure", "Show System", "Start Logging (Reset Log)", "Start Logging", "Stop Logging", "Show Last Log"); cmd = "cat /data/system/users/0/settings_global.xml /data/system/users/0/settings_secure.xml /data/system/users/0/settings_system.xml"; secpath = "/storage/emulated/0/Automagic/Resources/sec.txt"; sectri = newList("global", "secure", "system"); if (global_secure_setting == null) { global_secure_setting = newList() }</actioncontainer>
    <actioncontainer id="t14" x="385.0001" y="-17.499985">Script: choice = newList( "Start Logging (Reset Log)", "Start Logging", "Stop Logging", "Show Last Log"); cmd = "cat /data/system/users/0/settings_global.xml /data/system/users/0/settings_secure.xml /data/system/users/0/settings_system.xml"; secpath = "/storage/emulated/0/Automagic/Resources/sec.txt"; sectri = newList("global", "secure", "system"); if (global_secure_setting == null) { global_secure_setting = newList() }</actioncontainer>
    <conditioncontainer id="t15" x="490.00012" y="297.49994">Expression: value == "Start Logging (Reset Log)"</conditioncontainer>
    <actioncontainer id="t16" x="490.00034" y="437.5">Script: global_secure_setting = newList()</actioncontainer>
    <actioncontainer id="t17" x="700.00024" y="577.49976">Set Flow State: Enable Secure Setting Logger</actioncontainer>
    <conditioncontainer id="t18" x="700.0" y="437.50006">Screen Automatic Brightness Enabled</conditioncontainer>
    <actioncontainer id="t19" x="910.0" y="717.5">Script: choice = newList(); for(i in global_secure_setting) { date = "{i[0],dateformat,dd MMM yyyy, HH:mm:ss}"; ago = getDurationString((getDate() - i[0]) /60000 *60000); cate = i[1]; key = i[2]; oldv = i[3]; newv = i[4]; //date ago \n [category] key : oldvalue &gt;&gt;&gt; value addElement(choice, date + " ( " + ago + " ago )\n" + "[{cate}] {key} : {oldv} &gt;&gt;&gt; {newv}\n"); }</actioncontainer>
    <actioncontainer id="t20" x="910.0" y="857.5">Input Dialog: Tap the key value to copy it Single Choice Menu {choice,listformat,comma}</actioncontainer>
    <conditioncontainer id="t21" x="910.0" y="997.50024">Expression: if(operation == "ok") { sec = global_secure_setting[index]; copycho = newList( "[{sec[1]}] {sec[2]} : {sec[3]} &gt;&gt;&gt; {sec[4]}", "[{sec[1]}] {sec[2]} : {sec[4]}", sec[2], sec[3], sec[4]) ; return true; } return false;</conditioncontainer>
    <actioncontainer id="t22" x="700.0" y="717.5">Notification on Screen: Secure Setting Logger Started</actioncontainer>
    <actioncontainer id="t23" x="490.0" y="717.5">Input Dialog: Search keyword Single-line text Please type the search keyword</actioncontainer>
    <conditioncontainer id="t24" x="490.0" y="857.5">Expression: if(operation == "ok") { value = toLowerCase(value); for(i in getMapKeys(secmap)) { if(contains(toLowerCase(i), value) OR contains(toLowerCase(secmap[i][1]), value)) { addElement(choice, "{no}. [{secmap[i][4]}] {i} : {secmap[i][1]}"); no = no + 1; } } return true; } return false;</conditioncontainer>
    <conditioncontainer id="t25" x="280.0" y="717.5">Expression: value == "Search keyword"</conditioncontainer>
    <actioncontainer id="t26" x="280.0" y="857.5">Input Dialog: "{value}", Tap the key value to copy it Single Choice Menu {choice,listformat,comma}</actioncontainer>
    <conditioncontainer id="t27" x="69.99999" y="717.5">Expression: value == "Save all settings"</conditioncontainer>
    <actioncontainer id="t28" x="70.0" y="857.5">Write to File: {toJSON(secmap)} to {secpath} (overwrite)</actioncontainer>
    <actioncontainer id="t29" x="-140.0" y="717.5">Init Variable Text File: {secpath} to file_text</actioncontainer>
    <actioncontainer id="t30" x="-140.0" y="857.5">Script: oldmap = fromJSON(file_text); //splitting to old new both oldhas = removeAllElementValues(getMapKeys(oldmap), getMapKeys(secmap)); newhas = removeAllElementValues(getMapKeys(secmap), getMapKeys(oldmap)); bothhave = removeAllElementValues(getMapKeys(oldmap), oldhas); //finding change if both have change = newList(); for(i in bothhave) { if(oldmap[i][1] != secmap[i][1]) addElement(change, i); } //Changes if both have choice = newList("Changes Happened : "); for(i in change) addElement(choice, "[{secmap[i][4]}] {i} : {oldmap[i][1]} &gt;&gt;&gt; {secmap[i][1]}"); //Old has, new doesn't if(!isEmpty(oldhas)) { addElement(choice, "\n\nOld setting has : "); for(i in oldhas) addElement(choice, "[{oldmap[i][4]}] {i} : {oldmap[i][1]}"); } //New has, old doesn't if(!isEmpty(newhas)) { addElement(choice, "\n\nNew setting has : "); for(i in newhas) addElement(choice, "[{secmap[i][4]}] {i} : {secmap[i][1]}"); }</actioncontainer>
    <actioncontainer id="t31" x="-140.0" y="997.5">Input Dialog: Tap the key value to copy it Single Choice Menu {choice,listformat,comma}</actioncontainer>
    <actioncontainer id="t32" x="70.0" y="997.5">Notification on Screen: All secure setting has been saved.</actioncontainer>
    <conditioncontainer id="t33" x="280.0" y="997.5">Expression: if(operation == "ok") { find = findAll(value, '\\d+\\. (\\[.*?\\]) (.*?) : (.*)', true); copycho = newList( "{find[0][1]} {find[0][2]} : {find[0][3]}", find[0][2], find[0][3] ); return true; } return false;</conditioncontainer>
    <conditioncontainer id="t34" x="-140.0" y="1137.5">Expression: if(operation == "ok") { find = findAll(value, '(\\[.*?\\]) (.*?) : (?:(.*?) &gt;&gt;&gt; (.*)|(.*))', true); //find = [category, key, (oldv &gt;&gt;&gt; newv | newv) ] if(isEmpty(find)) return false; cate = find[0][1]; key = find[0][2]; oldv = find[0][3]; newv = if(find[0][5] == null) find[0][4] else find[0][5]; copycho = newList( "{cate} {key} : {newv}", key, newv); //add oldv format if from changed if(oldv != null) { addElement(copycho, 0, "{cate} {key} : {oldv} &gt;&gt;&gt; {newv}"); addElement(copycho, 3, oldv); } return true; } return false;</conditioncontainer>
    <actioncontainer id="t35" x="280.0" y="1137.5">Input Dialog: Which to Copy? Single Choice Menu {copycho,listformat,comma}</actioncontainer>
    <conditioncontainer id="t36" x="280.0" y="1277.5">Expression: operation == "ok"</conditioncontainer>
    <actioncontainer id="t37" x="280.0" y="1417.5">Copy Text to Clipboard: {value}</actioncontainer>
    <actioncontainer id="t38" x="280.0" y="1557.5">Notification on Screen: {value}

copied (long)</actioncontainer>
    <actioncontainer id="t39" x="280.0" y="122.500015">Input Dialog: Secure settings tools Single Choice Menu {choice,listformat,comma}</actioncontainer>
    <conditioncontainer id="t40" x="700.0001" y="297.5">Expression: value == "Start Logging"</conditioncontainer>
    <actioncontainer id="t41" x="490.00018" y="577.49976">Set Screen Brightness: 65</actioncontainer>
    <connection from="t1" to="t25" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t2" to="t3" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t3" to="t27" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t4" to="t1" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t5" to="t7" type="TRUE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t6" to="t19" type="TRUE" sourcePosition="SOUTH" targetPosition="EAST" />
    <connection from="t7" to="t8" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t8" to="t19" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t9" to="t2" type="TRUE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t10" to="t4" type="TRUE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t11" to="t13" type="TRUE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t11" to="t14" type="FALSE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t12" to="t11" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t13" to="t39" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t14" to="t39" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t15" to="t16" type="TRUE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t16" to="t18" type="NORMAL" sourcePosition="EAST" targetPosition="WEST" />
    <connection from="t17" to="t22" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t18" to="t41" type="TRUE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t18" to="t17" type="FALSE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t19" to="t20" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t20" to="t21" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t21" to="t35" type="TRUE" sourcePosition="SOUTH" targetPosition="EAST" />
    <connection from="t23" to="t24" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t24" to="t26" type="TRUE" sourcePosition="WEST" targetPosition="EAST" />
    <connection from="t25" to="t26" type="FALSE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t25" to="t23" type="TRUE" sourcePosition="EAST" targetPosition="WEST" />
    <connection from="t26" to="t33" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t27" to="t28" type="TRUE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t27" to="t29" type="FALSE" sourcePosition="WEST" targetPosition="EAST" />
    <connection from="t28" to="t32" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t29" to="t30" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t30" to="t31" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t31" to="t34" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t33" to="t35" type="TRUE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t34" to="t35" type="TRUE" sourcePosition="EAST" targetPosition="WEST" />
    <connection from="t35" to="t36" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t36" to="t37" type="TRUE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t37" to="t38" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t39" to="t9" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t39" to="t10" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t39" to="t15" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t39" to="t5" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t39" to="t6" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t39" to="t40" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t40" to="t18" type="TRUE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t41" to="t17" type="NORMAL" sourcePosition="EAST" targetPosition="WEST" />
  </flow>
  <flow type="flow">
    <name>Secure Setting Logger</name>
    <group>Logging</group>
    <enabled>false</enabled>
    <executionPolicy>QUEUE</executionPolicy>
    <triggercontainer id="t1" x="70.0" y="52.5">
      <trigger>System Setting Changed: System *</trigger>
      <trigger>System Setting Changed: Global *</trigger>
      <trigger>System Setting Changed: Secure *</trigger>
    </triggercontainer>
    <conditioncontainer id="t2" x="70.0" y="472.5">Execution Count: 20 times</conditioncontainer>
    <actioncontainer id="t3" x="70.0" y="612.5">Set Flow State: Disable Secure Setting Logger</actioncontainer>
    <actioncontainer id="t4" x="70.0" y="332.5">Notification on Screen: [{category}] {setting_name} : {old_setting} &gt;&gt;&gt; {setting}</actioncontainer>
    <actioncontainer id="t5" x="70.0" y="752.5">Notification on Screen: Secure Setting Logger Stopped</actioncontainer>
    <actioncontainer id="t6" x="70.0" y="192.5">Script: var = newList( "triggertime", "category", "setting_name", "old_setting", "setting"); category = findAll(trigger, "System Setting Changed: (\\w+) \\*", true)[0][1]; lis = newList(); for(i in var) addElement(lis, eval(i)); addElement(global_secure_setting, 0, lis); //log only last 20 if(length(global_secure_setting) &gt; 20) removeElement(global_secure_setting, 20);</actioncontainer>
    <connection from="t1" to="t6" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t2" to="t3" type="TRUE" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t3" to="t5" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t4" to="t2" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
    <connection from="t6" to="t4" type="NORMAL" sourcePosition="SOUTH" targetPosition="NORTH" />
  </flow>
</data>

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Re: Secure Setting Logger

Post by Desmanto » 05 May 2020 11:42

@vertigo : Thanks for the improvement. I never able to upload xml file directly as attachment, always blocked. So I have to use "Forum" button in AM and Publish the flows. Then copy the link, paste it here. This is what I've been doing since the first time I share flow. Try to upload it again. I will copy your link later to my first post.

I will wait until AM 1.38 later before making new improvement on the flow, which doesn't require root. As we can use dumpsys settings to get all the system setting.
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.


anuraag
Posts: 371
Joined: 24 Jan 2015 02:06

Re: Secure Setting Logger

Post by anuraag » 08 May 2020 00:28

This is interesting. So this flow can work without root now.
https://www.reddit.com/r/tasker/comment ... ?context=3

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Re: Secure Setting Logger

Post by Desmanto » 08 May 2020 10:10

@anuraag : That's awesome. Why we never know something like that before? I am gonna add the query, integrated vertigo's features and then add 3 additionals control UI to help creating trigger/action. It seems I am gonna release it as separate thread.
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.

Post Reply