When you want to change the mobile system date or time in your application, how do you go about doing it?
You cannot on a normal off the shelf handset, because it's not possible to gain the SET_TIME permission. This permission has the protectionLevel of signatureOrSystem, so there's no way for a market app to change global system time (but perhaps with black vodoo magic I do not know yet).
You cannot use other approaches because this is prevented on a Linux level, (see the long answer below) - this is why all trials using terminals and SysExecs gonna fail.
If you CAN gain the permission either because you rooted your phone or built and signed your own platform image, read on.
Short Answer
It's possible and has been done. You need android.permission.SET_TIME. Afterward use the AlarmManager via Context.getSystemService(Context.ALARM_SERVICE) and its method setTime().
Snippet for setting the time to 2010/1/1 12:00:00 from an Activity or Service:
Calendar c = Calendar.getInstance();
c.set(2010, 1, 1, 12, 00, 00);
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.setTime(c.getTimeInMillis());
If you which to change the timezone, the approach should be very similar (see android.permission.SET_TIME_ZONE and setTimeZone)
Long Answer
As it has been pointed out in several threads, only the system user can change the system time. This is only half of the story. SystemClock.setCurrentTimeMillis() directly writes to /dev/alarm which is a device file owned by system lacking world writeable rights. So in other words only processes running as system may use the SystemClock approach. For this way android permissions do not matter, there's no entity involved which checks proper permissions.
This is the way the internal preinstalled Settings App works. It just runs under the system user account.
For all the other kids in town there's the alarm manager. It's a system service running in the system_server process under the - guess what - system user account. It exposes the mentioned setTime method but enforces the SET_TIME permission and in in turn just calls SystemClock.setCurrentTimeMillis internally (which succeeds because of the user the alarm manager is running as).
Cheers
According to this thread, user apps cannot set the time, regardless of the permissions we give it. Instead, the best approach is to make the user set the time manually. We will use:
startActivity(new Intent(android.provider.Settings.ACTION_DATE_SETTINGS));
Unfortunately, there is no way to link them directly to the time setting (which would save them one more click). By making use of ellapsedRealtime, we can ensure that the user sets the time correctly.
A solution for rooted devices could be execute the commands
su
date -s YYYYMMDD.HHMMSS
You can do this by code with the following method:
private void changeSystemTime(String year,String month,String day,String hour,String minute,String second){
try {
Process process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
String command = "date -s "+year+month+day+"."+hour+minute+second+"\n";
Log.e("command",command);
os.writeBytes(command);
os.flush();
os.writeBytes("exit\n");
os.flush();
process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Just call the previous method like this:
changeSystemTime("2015","04","06","13","09","30");
I didn't see this one on the list anywhere but it works for me. My device is rooted and I have superuser installed, but if superuser works on non-rooted devices, this might work. I used an AsyncTask and called the following:
protected String doInBackground(String... params){
Runtime.getRuntime().exec("su && date -s " + params[0]);}
In our application case, the dirty workaround was:
When the user is connected to Internet, we get the Internet Time (NTP server) and compare the difference (-) of the internal device time (registederOffsetFromInternetTime). We save it on the config record file of the user.
We use the time of the devide + registederOffsetFromInternetTime to consider the correct updated time for OUR application.
All GETHOUR processes check the difference between the actual time with the time of the last comparission (with the Internet time). If the time over 10 minutes, do a new comparission to update registederOffsetFromInternetTime and mantain accuracy.
If the user uses the App without Internet, we can only use the registederOffsetFromInternetTime stored as reference, and use it. Just if the user changes the hour in local device when offline and use the app, the app will consider incorrect times. But when the user comes back to internet access we warn he about the clock changed , asking to resynchronize all or desconsider updates did offline with the incorrect hour.
thanks penquin. In quickshortcutmaker I catch name of date/time seting activity exactly. so to start system time setting:
Intent intent=new Intent();
intent.setComponent(new ComponentName("com.android.settings",
"com.android.settings.DateTimeSettingsSetupWizard"));
startActivity(intent);
`
Related
I want Android apps to believe that Automatic Time Zone is set to ON even though it actually is not. I read that Automatic Time Zone is detected using Settings.Global.AUTO_TIME variable, which returns value 1 if Automatic Time Zone is ON and value 0 if Automatic Time Zone is OFF.
This variable is read using the below line
Settings.Global.getInt(getContentResolver(), Settings.Global.AUTO_TIME)
I read that this variable can only be set by a System App. Is there a way to change this variable somehow? Can I write an application that allows me to change this variable? Can this be done by rooting the android device.
The most commonly suggested solution is to use Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME, 1). You will end up seeing this message in logcat, though:
Setting auto_time has moved from android.provider.Settings.System to android.provider.Settings.Global, value is unchanged.
According to the Android documentation, apps cannot modify these settings.
https://developer.android.com/reference/android/provider/Settings.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.
These settings are read-only unless your app is installed as a system app or is signed by the manufacturer of the device that it is running on.
Most people end up rooting their device but there is an alternative. Device Owner apps can modify global system settings through a different API.
https://developer.android.com/reference/android/app/admin/DevicePolicyManager#setGlobalSetting(android.content.ComponentName,%20java.lang.String,%20java.lang.String)
You would do it like so:
DevicePolicyManager dpm = (DevicePolicyManager) getApplicationContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName component = new ComponentName(getApplicationContext(), DeviceAdminReceiver.class);
dpm.setGlobalSetting(component, Settings.Global.AUTO_TIME, enable ? "1" : "0");
This alternative is mostly suitable for homebrew apps and proprietary Android-based products because the device cannot have user accounts. Play Store will not install apps as Device Owner, it must be done manually.
Here is how to do it with ADB:
https://developer.android.com/work/dpc/dedicated-devices/cookbook#dev-setup
You can use Settings.Global.putInt(getContentResolver(), Settings.Global.AUTO_TIME, 1) to set the value from your app.
You need to have the WRITE_SETTINGS permission though.
Let's say the user open "Settings" application, is there a way to "intercept" this intent, from my app's service, in order to detect that "Settings" app is going to be openned?
For instance, in SOTI MobiControl app you can manage (from a web dashboard) the permissions of the user with the app installed (and enrolled to your server). If you don't allow one user to open Settings app, when he tries to open it, a toast appears saying "Unauthorized". How do they that?
Doing so is against Google Play Developer Program Policy, as it states in its System Interference section:
An app downloaded from Google Play (or its components or derivative
elements) must not make changes to the user’s device outside of the
app without the user’s knowledge and consent.
This includes behavior such as replacing or reordering the default
presentation of apps, widgets, or the settings on the device. If an
app makes such changes with the user’s knowledge and consent, it must
be clear to the user which app has made the change and the user must
be able to reverse the change easily, or by uninstalling the app
altogether.
Apps and their ads must not modify or add browser settings or
bookmarks, add homescreen shortcuts, or icons on the user’s device as
a service to third parties or for advertising purposes.
Apps and their ads must not display advertisements through system
level notifications on the user’s device, unless the notifications
derive from an integral feature provided by the installed app (e.g.,
an airline app that notifies users of special deals, or a game that
notifies users of in-game promotions).
Apps must not encourage, incentivize, or mislead users into removing
or disabling third-party apps except as part of a security service
provided by the app.
https://play.google.com/intl/ALL_us/about/developer-content-policy.html
Is there a way to "intercept" this intent, from my app's service, in
order to detect that "Settings" app is going to be opened?
As other mentioned before it's not possible to intercept a launch intent.
For instance, in SOTI MobiControl app you can manage (from a web
dashboard) the permissions of the user with the app installed (and
enrolled to your server). If you don't allow one user to open Settings
app, when he tries to open it, a toast appears saying "Unauthorized".
How do they that?
It's however possible to determine if an app is opened and "intercept" that call. By intercept I mean draw over the starting app's screen and present a login screen or a not authorized screen.
I haven't worked out a full sample that would work an any Android version but from my research with AppLocks, I'd say it works more or less like this:
On pre-Lollipop Android you'd use this to retrieve the running processes:
ActivityManager manager = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo info : manager.getRunningAppProcesses()) {
Log.e("TAG", "Running process: " + info.processName);
if ("com.mycompany.mycoolapp".equals(info.processName)) {
// do stuff...
}
}
requires:
<uses-permission android:name="android.permission.GET_TASKS"/>
or alternatively:
for (ActivityManager.RunningTaskInfo recentTaskInfo : manager.getRunningTasks(100)) {
Log.e("TAG", "Recent tasks: " + recentTaskInfo.baseActivity.getPackageName());
}
On Lollipop and higher you'd use UsageStats to determine if an app is running:
UsageStatsManager usageStatsManager = (UsageStatsManager)getSystemService(USAGE_STATS_SERVICE);
Calendar cal = Calendar.getInstance();
cal.add(Calendar.YEAR, -1);
long start = cal.getTimeInMillis();
long end = System.currentTimeMillis();
List<UsageStats> queryUsageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, start, end);
for (UsageStats stats : queryUsageStats) {
Log.e("TAG", "Usage stats for: " + stats.getPackageName());
}
requires:
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
I would probably run both using the AlarmManager to perform that recurring task.
I'm fairly certain these are the two ways to get the list of running apps. If the permission for usage stats is denied to AppLock it's not working any more an Android 6.0 devices. On pre-M devices it however still works which is an indicator that the app has an alternative way to get the list of running apps (the first option described above).
Once it's determined an app has been started (it's running and hasn't been running the last time we checked), we can "take over" the screen. And that's how I'd do it: http://www.piwai.info/chatheads-basics/
Of course that's just the basic idea and I'm sure there are a couple of pitfalls when implementing a reliable solution but this should give you something to start with.
Let's say the user open "Settings" application, is there a way to "intercept" this intent, from my app's service, in order to detect that "Settings" app is going to be openned?
No, unless you are the one that is calling startActivity() to launch the application in the first place.
I am writing an Andoid app so that when battery life gets below a certain level, a dialog with options of how to save the battery appears. One of those options is to close all background apps/services (processes) using ActivityManager.killBackgroundProcesses(). The code is shown here:
public void TaskKiller( View view){
List<ApplicationInfo> packages;
PackageManager pm;
pm = getPackageManager();
packages = pm.getInstalledApplications(0);
ActivityManager mActivityManager = (ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);
for (ApplicationInfo packageInfo : packages) {
mActivityManager.killBackgroundProcesses(packageInfo.packageName);
}
}
However, when I click the button that calls TaskKiller() and closes the background processes, some of the apps (Email, Google Maps) instantly begin he process of restarting. How can I alter my code so these apps stay closed until they are reopened? Also, is this approach sensible in regard to saving power or am I attacking this the wrong way?
I don't think that's the right way of handeling the problem.
These apps have broadcast receivers, which mean they'll restart the service whenever something happens (i.e. AC plugged in/WiFi turned on), and I don't think there's a way to stop that without root, and actually disabling the broadcast receiver.
You could make something that kills it every 5 minutes, but that wouldn't be very battery-friendly.
I don't think it's a good idea to force close the Maps app everytime, it's a bug in Android i think..
One of the answers is as following:
"
Actually, Maps always runs when you have "Backround Data" checkmarked in your General Sync Settings under Account Settings in your phone's Gmail app. Syncing backround data is necessary, unfortunately, in order for your phone service provider to provide calling and texting (although internet access will still work without this item checkmarked). Unchecking this box will remove Maps from Running applications (& any other app that needs it), improving battery time and speeding up your phone. But, if you want to make calls, text or use apps that require Backround sync, you have to have this ckeckmarked. If all you want to do is browse the net...uncheckmark it. There are currently no other legitimate solutions to the issue. Hope this is helpful...
"
See this issue (https://code.google.com/p/android/issues/detail?id=10251)
I trying some stuffs with android as i am learning android development, now i have a scenario here.
I want to change the android phone's system date through my application( first i would like to know is this doable? )
Layout is as below
Now what i want is when the user clicks the button the date should increase by say 20 days
how can i do this.
i am unable to start...please help
As I already said that's impossible. You need the SET_TIME permission and that permission is granted by system only to applications that are in the Android system image. If You are able to gain that privilege you can easily change with the AlarmManager. SystemClock.setCurrentTimeMillis write the /dev/allarm file
adb shell ls -l /dev/alarm
crw-rw-r-- system radio 10, 46 2013-06-12 10:46 alarm
c stays for Character special file (stored in /dev).
system is the owner of the file
radio is the group
system and radio have read and write permissions (rw-, tree bits, int value 6), the others have only the read permission (r, int value 4). So the file permission is 664. If you can get root user (running su), you can change the permission of this file and wrote in it a new value. A bug report has been filled in order to ask google to allow apps to modify programmatically the mobile date but it has been declied. Here the reference
On Android, the only way for an application do have write access to the time&date is to get the SET_TIME permission, which is only possible for "applications that are in the Android system image or that are signed with the same certificate as the application that declared the permission." (see signatureOrSystem protection level).
The only way for your application to reach this protection level is to run on a rooted device, or build and sign your own android rom.
If this is your case, you can easily use the AlarmManager or simply the Calendar instance.
Good luck!
Normal user applications does not have permission to change the device time. Read the answer by cashbash in the following post for the alternate option.
Unfortunately, blackbelt is right; android lets us do a lot of cool things, but changing system time programmatically is not one of them.
Since I see that you are looking for more credible/official sources, I suggest you check out this open ticket with Google, which suggests this is an open problem--it ought to work, but doesn't, and it doesn't seem Google is going to fix it anytime soon. The gist of it is that the SET_TIME protection level is set higher than it ought to be. (for more information on permissions, see here)
Although this is not quite the same as changing the time programmatically, you can still make the user change the system time for you if for some reason you do need system time to be changed. This thread will explain how to go about implementing that if you want to go that route.
Hope this was helpful information!
My application should have some changes after some time (in hours or days) and I would like to test that.
I try to use SystemClock.setCurrentTimeMillis() in my unit test to simulate the change of time/date, but without any luck.
I declared the <uses-permission android:name="android.permission.WRITE_SETTINGS" /> in the manifest of both the application and the test application, that did not change anything.
At this time, I am running those tests on the emulator if that makes any difference...
Edit : With Nick's help, also requested SET_TIME permission:
<uses-permission android:name="android.permission.SET_TIME" />
But now, the logcat shows :
WARN/PackageManager(59): Not granting permission android.permission.SET_TIME to package com.matthieu.tests (protectionLevel=3 flags=0xbe46)
Another Edit : With dtmilano's answer...
Added this code (with the right try/catch) in my test:
Runtime.getRuntime().exec("date \"$((10800 + $(date +%s)))\"").waitFor();
When I go on the shell, I can run that command without any problem and I see the time changing in the emulator (I am trying to add 3 hours). When I run my tests with my code, the time does not change at all... ?
What I do on the command line is :
date "$((10800 + $(date +%s)))"
I doubt I need to add the "adb shell" part in the Runtime.exec...
Seems like this is on the right path, but still not able to get it running. And it might also go along what Nick pointed to that it needs to be a system process to be able to change the time... ?
Add android.permission.SET_TIME_ZONE to your manifest if you need to change the timezone,
then from your activity (or wherever you need it.. but you'll need to grab an appropriate context..)
Context context = getContext(); // or (Context) this, or whatever depending on where you are
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mgr.setTimeZone(timezoneid); // replace timezoneid with the time zone you need e.g. "pacific/Auckland"
mgr.setTime(timeinmillis); // replace timeinmillis with the time you need in millis, probably from a Date or Calendar object... but watch the timezone in the calendar ;-)
The whole point being that the AlarmManager system service contains members to set the system time and timezone..
http://developer.android.com/reference/android/Manifest.permission.html#SET_TIME
I searched "android permissions" then on the permissions page, did a find on the word "time". if you are past api level 8, i would recommend you request that permission.
Update: based on this link, I do not think it is possible to set the system time from a user space app. For your testing purposes, you may need to manually change the time on the emulator. The only other option I have found requires building and signing your own Android build.
From your host (assuming you are using linux) you may run:
$ adb shell date $(date --date='2011-06-11 12:10:10' +%s.0)
mainly if is the emulator. Replace the date and time with the desired values.
You should find a way of synchronizing this change with your tests or you can even run it from your tests using Runtime.exec().