Add setTime and setTimeZone API.
Fix: 67497358
Test: wait for cts test in the following cl. For unitest:
runtest -x services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
Add setTime and setTimeZone API in devicepolicymanager.
Add unitest for each API.
Following design doc: https://docs.google.com/document/d/1NV93mr2CT157S_haru1QbKo9HLeP9iPM9eMiGfVmfCM/edit
Change-Id: I188c27b0b99137b6f01e42ae1ad49356ce0a81b2
diff --git a/api/current.txt b/api/current.txt
index 93cc843..0530744 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6445,6 +6445,8 @@
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
+ method public boolean setTime(android.content.ComponentName, long);
+ method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
diff --git a/api/system-current.txt b/api/system-current.txt
index 9767e44..cfb7f9d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6680,6 +6680,8 @@
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
+ method public boolean setTime(android.content.ComponentName, long);
+ method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
diff --git a/api/test-current.txt b/api/test-current.txt
index 702ec3b..2546bbeb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6511,6 +6511,8 @@
method public boolean setStatusBarDisabled(android.content.ComponentName, boolean);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void setSystemUpdatePolicy(android.content.ComponentName, android.app.admin.SystemUpdatePolicy);
+ method public boolean setTime(android.content.ComponentName, long);
+ method public boolean setTimeZone(android.content.ComponentName, java.lang.String);
method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3c53063..ab8edee 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6533,6 +6533,52 @@
}
/**
+ * Called by device owner to set the system wall clock time. This only takes effect if called
+ * when {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false} will be
+ * returned.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+ * @param millis time in milliseconds since the Epoch
+ * @return {@code true} if set time succeeded, {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public boolean setTime(@NonNull ComponentName admin, long millis) {
+ throwIfParentInstance("setTime");
+ if (mService != null) {
+ try {
+ return mService.setTime(admin, millis);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Called by device owner to set the system's persistent default time zone. This only takes
+ * effect if called when {@link android.provider.Settings.Global#AUTO_TIME_ZONE} is 0, otherwise
+ * {@code false} will be returned.
+ *
+ * @see android.app.AlarmManager#setTimeZone(String)
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+ * @param timeZone one of the Olson ids from the list returned by
+ * {@link java.util.TimeZone#getAvailableIDs}
+ * @return {@code true} if set timezone succeeded, {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public boolean setTimeZone(@NonNull ComponentName admin, String timeZone) {
+ throwIfParentInstance("setTimeZone");
+ if (mService != null) {
+ try {
+ return mService.setTimeZone(admin, timeZone);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Called by profile or device owners to update {@link android.provider.Settings.Secure}
* settings. Validation that the value of the setting is in the correct form for the setting
* type should be performed by the caller.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 8865a05..e77c186 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -229,6 +229,9 @@
void setGlobalSetting(in ComponentName who, in String setting, in String value);
void setSecureSetting(in ComponentName who, in String setting, in String value);
+ boolean setTime(in ComponentName who, long millis);
+ boolean setTimeZone(in ComponentName who, String timeZone);
+
void setMasterVolumeMuted(in ComponentName admin, boolean on);
boolean isMasterVolumeMuted(in ComponentName admin);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c59f44e..80f6a4b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -175,6 +175,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
@@ -1682,6 +1683,10 @@
return getCallingUid() == Process.myUid();
}
+ void binderWithCleanCallingIdentity(@NonNull ThrowingRunnable action) {
+ Binder.withCleanCallingIdentity(action);
+ }
+
final int userHandleGetCallingUserId() {
return UserHandle.getUserId(binderGetCallingUid());
}
@@ -9023,6 +9028,31 @@
}
@Override
+ public boolean setTime(ComponentName who, long millis) {
+ Preconditions.checkNotNull(who, "ComponentName is null in setTime");
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ // Don't allow set time when auto time is on.
+ if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
+ return false;
+ }
+ mInjector.binderWithCleanCallingIdentity(() -> mInjector.getAlarmManager().setTime(millis));
+ return true;
+ }
+
+ @Override
+ public boolean setTimeZone(ComponentName who, String timeZone) {
+ Preconditions.checkNotNull(who, "ComponentName is null in setTimeZone");
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ // Don't allow set timezone when auto timezone is on.
+ if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
+ return false;
+ }
+ mInjector.binderWithCleanCallingIdentity(() ->
+ mInjector.getAlarmManager().setTimeZone(timeZone));
+ return true;
+ }
+
+ @Override
public void setSecureSetting(ComponentName who, String setting, String value) {
Preconditions.checkNotNull(who, "ComponentName is null");
int callingUserId = mInjector.userHandleGetCallingUserId();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 5471715..ae4b569 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -15,6 +15,7 @@
*/
package com.android.server.devicepolicy;
+import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -34,11 +35,13 @@
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.security.KeyChain;
+import android.support.annotation.NonNull;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Pair;
import android.view.IWindowManager;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.widget.LockPatternUtils;
import java.io.File;
@@ -194,6 +197,9 @@
}
@Override
+ AlarmManager getAlarmManager() {return services.alarmManager;}
+
+ @Override
LockPatternUtils newLockPatternUtils() {
return services.lockPatternUtils;
}
@@ -234,6 +240,11 @@
}
@Override
+ void binderWithCleanCallingIdentity(@NonNull ThrowingRunnable action) {
+ context.binder.withCleanCallingIdentity(action);
+ }
+
+ @Override
int binderGetCallingUid() {
return context.binder.getCallingUid();
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index a8bf8f1..e1e9cf5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3090,6 +3090,47 @@
assertEquals(-1, dpm.getLastSecurityLogRetrievalTime());
}
+ public void testSetTime() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ dpm.setTime(admin1, 0);
+ verify(getServices().alarmManager).setTime(0);
+ }
+
+ public void testSetTimeFailWithPO() throws Exception {
+ setupProfileOwner();
+ assertExpectException(SecurityException.class, null, () -> dpm.setTime(admin1, 0));
+ }
+
+ public void testSetTimeWithAutoTimeOn() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ when(getServices().settings.settingsGlobalGetInt(Settings.Global.AUTO_TIME, 0))
+ .thenReturn(1);
+ assertFalse(dpm.setTime(admin1, 0));
+ }
+
+ public void testSetTimeZone() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ dpm.setTimeZone(admin1, "Asia/Shanghai");
+ verify(getServices().alarmManager).setTimeZone("Asia/Shanghai");
+ }
+
+ public void testSetTimeZoneFailWithPO() throws Exception {
+ setupProfileOwner();
+ assertExpectException(SecurityException.class, null,
+ () -> dpm.setTimeZone(admin1, "Asia/Shanghai"));
+ }
+
+ public void testSetTimeZoneWithAutoTimeZoneOn() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ when(getServices().settings.settingsGlobalGetInt(Settings.Global.AUTO_TIME_ZONE, 0))
+ .thenReturn(1);
+ assertFalse(dpm.setTimeZone(admin1, "Asia/Shanghai"));
+ }
+
public void testGetLastBugReportRequestTime() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 9702118..7e11e87 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -30,8 +30,12 @@
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManagerInternal;
+import android.support.annotation.NonNull;
import android.test.mock.MockContext;
import android.util.ArrayMap;
+import android.util.ExceptionUtils;
+
+import com.android.internal.util.FunctionalUtils;
import org.junit.Assert;
@@ -95,6 +99,21 @@
callingPid = (int) token;
}
+ public void withCleanCallingIdentity(@NonNull FunctionalUtils.ThrowingRunnable action) {
+ long callingIdentity = clearCallingIdentity();
+ Throwable throwableToPropagate = null;
+ try {
+ action.run();
+ } catch (Throwable throwable) {
+ throwableToPropagate = throwable;
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ if (throwableToPropagate != null) {
+ throw ExceptionUtils.propagate(throwableToPropagate);
+ }
+ }
+ }
+
public int getCallingUid() {
return callingUid;
}