diff options
| author | Neil Fuller <nfuller@google.com> | 2020-09-30 14:46:11 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-09-30 14:46:11 +0000 |
| commit | e23fca65cd63c96fe31bd84a06d3095fcef5b412 (patch) | |
| tree | 9ab463517a6a2fb0f1969dd4b0380042aec715e8 /core/java/android | |
| parent | b3e2b63d91c20345c279f9cbd257167efba23383 (diff) | |
| parent | 02d943f44d4ed11a7cde1fa33a4c0c07b9cb7e3c (diff) | |
Merge "Get ready to add System APIs for use by SUW"
Diffstat (limited to 'core/java/android')
14 files changed, 550 insertions, 318 deletions
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index d50cdeed6d73..9100d577fd68 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -33,6 +33,7 @@ import android.app.prediction.AppPredictionManager; import android.app.role.RoleControllerManager; import android.app.role.RoleManager; import android.app.slice.SliceManager; +import android.app.time.TimeManager; import android.app.timedetector.TimeDetector; import android.app.timedetector.TimeDetectorImpl; import android.app.timezone.RulesManager; @@ -1218,6 +1219,14 @@ public final class SystemServiceRegistry { return new TimeZoneDetectorImpl(); }}); + registerService(Context.TIME_MANAGER, TimeManager.class, + new CachedServiceFetcher<TimeManager>() { + @Override + public TimeManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new TimeManager(); + }}); + registerService(Context.PERMISSION_SERVICE, PermissionManager.class, new CachedServiceFetcher<PermissionManager>() { @Override diff --git a/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl b/core/java/android/app/time/ITimeZoneDetectorListener.aidl index 6d0fe72b9de1..723ad5969afc 100644 --- a/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl +++ b/core/java/android/app/time/ITimeZoneDetectorListener.aidl @@ -14,11 +14,9 @@ * limitations under the License. */ -package android.app.timezonedetector; - -import android.app.timezonedetector.TimeZoneConfiguration; +package android.app.time; /** {@hide} */ -oneway interface ITimeZoneConfigurationListener { +oneway interface ITimeZoneDetectorListener { void onChange(); }
\ No newline at end of file diff --git a/core/java/android/app/time/TEST_MAPPING b/core/java/android/app/time/TEST_MAPPING new file mode 100644 index 000000000000..951905bcbac5 --- /dev/null +++ b/core/java/android/app/time/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.app.time." + } + ] + } + ] +} diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java new file mode 100644 index 000000000000..9864afba534a --- /dev/null +++ b/core/java/android/app/time/TimeManager.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemService; +import android.app.timezonedetector.ITimeZoneDetectorService; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; + +import java.util.concurrent.Executor; + +/** + * The interface through which system components can interact with time and time zone services. + * + * @hide + */ +// @SystemApi +@SystemService(Context.TIME_MANAGER) +public final class TimeManager { + private static final String TAG = "time.TimeManager"; + private static final boolean DEBUG = false; + + private final Object mLock = new Object(); + private final ITimeZoneDetectorService mITimeZoneDetectorService; + + @GuardedBy("mLock") + private ITimeZoneDetectorListener mTimeZoneDetectorReceiver; + + /** + * The registered listeners. The key is the actual listener that was registered, the value is a + * wrapper that ensures the listener is executed on the correct Executor. + */ + @GuardedBy("mLock") + private ArrayMap<TimeZoneDetectorListener, TimeZoneDetectorListener> mTimeZoneDetectorListeners; + + /** @hide */ + public TimeManager() throws ServiceNotFoundException { + // TimeManager is an API over one or possibly more services. At least until there's an + // internal refactoring. + mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE)); + } + + /** + * Returns the calling user's time zone capabilities and configuration. + */ + @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) + @NonNull + public TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig() { + if (DEBUG) { + Log.d(TAG, "getTimeZoneCapabilities called"); + } + try { + return mITimeZoneDetectorService.getCapabilitiesAndConfig(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Modifies the time zone detection configuration. + * + * <p>Configuration settings vary in scope: some may be global (affect all users), others may be + * specific to the current user. + * + * <p>The ability to modify configuration settings can be subject to restrictions. For + * example, they may be determined by device hardware, general policy (i.e. only the primary + * user can set them), or by a managed device policy. Use {@link + * #getTimeZoneCapabilitiesAndConfig()} to obtain information at runtime about the user's + * capabilities. + * + * <p>Attempts to modify configuration settings with capabilities that are {@link + * TimeZoneCapabilities#CAPABILITY_NOT_SUPPORTED} or {@link + * TimeZoneCapabilities#CAPABILITY_NOT_ALLOWED} will have no effect and a {@code false} + * will be returned. Modifying configuration settings with capabilities that are {@link + * TimeZoneCapabilities#CAPABILITY_NOT_APPLICABLE} or {@link + * TimeZoneCapabilities#CAPABILITY_POSSESSED} will succeed. See {@link + * TimeZoneCapabilities} for further details. + * + * <p>If the supplied configuration only has some values set, then only the specified settings + * will be updated (where the user's capabilities allow) and other settings will be left + * unchanged. + * + * @return {@code true} if all the configuration settings specified have been set to the + * new values, {@code false} if none have + */ + @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) + public boolean updateTimeZoneConfiguration(@NonNull TimeZoneConfiguration configuration) { + if (DEBUG) { + Log.d(TAG, "updateConfiguration called: " + configuration); + } + try { + return mITimeZoneDetectorService.updateConfiguration(configuration); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * An interface that can be used to listen for changes to the time zone detector behavior. + */ + @FunctionalInterface + public interface TimeZoneDetectorListener { + /** + * Called when something about the time zone detector behavior on the device has changed. + * For example, this could be because the current user has switched, one of the global or + * user's settings been changed, or something that could affect a user's capabilities with + * respect to the time zone detector has changed. Because different users can have different + * configuration and capabilities, this method may be called when nothing has changed for + * the receiving user. + */ + void onChange(); + } + + /** + * Registers a listener that will be informed when something about the time zone detector + * behavior changes. + */ + @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) + public void addTimeZoneDetectorListener(@NonNull Executor executor, + @NonNull TimeZoneDetectorListener listener) { + + if (DEBUG) { + Log.d(TAG, "addTimeZoneDetectorListener called: " + listener); + } + synchronized (mLock) { + if (mTimeZoneDetectorListeners == null) { + mTimeZoneDetectorListeners = new ArrayMap<>(); + } else if (mTimeZoneDetectorListeners.containsKey(listener)) { + return; + } + + if (mTimeZoneDetectorReceiver == null) { + ITimeZoneDetectorListener iListener = new ITimeZoneDetectorListener.Stub() { + @Override + public void onChange() { + notifyTimeZoneDetectorListeners(); + } + }; + mTimeZoneDetectorReceiver = iListener; + try { + mITimeZoneDetectorService.addListener(mTimeZoneDetectorReceiver); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + mTimeZoneDetectorListeners.put(listener, () -> executor.execute(listener::onChange)); + } + } + + private void notifyTimeZoneDetectorListeners() { + ArrayMap<TimeZoneDetectorListener, TimeZoneDetectorListener> timeZoneDetectorListeners; + synchronized (mLock) { + if (mTimeZoneDetectorListeners == null || mTimeZoneDetectorListeners.isEmpty()) { + return; + } + timeZoneDetectorListeners = new ArrayMap<>(mTimeZoneDetectorListeners); + } + int size = timeZoneDetectorListeners.size(); + for (int i = 0; i < size; i++) { + timeZoneDetectorListeners.valueAt(i).onChange(); + } + } + + /** + * Removes a listener previously passed to + * {@link #addTimeZoneDetectorListener(Executor, TimeZoneDetectorListener)} + */ + @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) + public void removeTimeZoneDetectorListener(@NonNull TimeZoneDetectorListener listener) { + if (DEBUG) { + Log.d(TAG, "removeConfigurationListener called: " + listener); + } + + synchronized (mLock) { + if (mTimeZoneDetectorListeners == null || mTimeZoneDetectorListeners.isEmpty()) { + return; + } + mTimeZoneDetectorListeners.remove(listener); + + // If the last local listener has been removed, remove and discard the + // mTimeZoneDetectorReceiver. + if (mTimeZoneDetectorListeners.isEmpty()) { + try { + mITimeZoneDetectorService.removeListener(mTimeZoneDetectorReceiver); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } finally { + mTimeZoneDetectorReceiver = null; + } + } + } + } +} diff --git a/core/java/android/app/timezonedetector/TimeZoneCapabilities.aidl b/core/java/android/app/time/TimeZoneCapabilities.aidl index fede6458318a..f744bf162c67 100644 --- a/core/java/android/app/timezonedetector/TimeZoneCapabilities.aidl +++ b/core/java/android/app/time/TimeZoneCapabilities.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.app.timezonedetector; +package android.app.time; parcelable TimeZoneCapabilities; diff --git a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java index 09fffe9f4f25..c62c2b34f35d 100644 --- a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java +++ b/core/java/android/app/time/TimeZoneCapabilities.java @@ -14,16 +14,16 @@ * limitations under the License. */ -package android.app.timezonedetector; - -import static android.app.timezonedetector.TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED; -import static android.app.timezonedetector.TimeZoneConfiguration.SETTING_GEO_DETECTION_ENABLED; +package android.app.time; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.timezonedetector.ManualTimeZoneSuggestion; +import android.app.timezonedetector.TimeZoneDetector; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -42,15 +42,18 @@ import java.util.Objects; * <p>Actions have associated methods, see the documentation for each action for details. * * <p>For configuration settings capabilities, the associated settings value can be found via - * {@link #getConfiguration()} and may be changed using {@link - * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} (if the user's capabilities allow). + * {@link TimeManager#getTimeZoneCapabilitiesAndConfig()} and may be changed using {@link + * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)} (if the user's capabilities + * allow). * * <p>Note: Capabilities are independent of app permissions required to call the associated APIs. * * @hide */ +// @SystemApi public final class TimeZoneCapabilities implements Parcelable { + /** @hide */ @IntDef({ CAPABILITY_NOT_SUPPORTED, CAPABILITY_NOT_ALLOWED, CAPABILITY_NOT_APPLICABLE, CAPABILITY_POSSESSED }) @Retention(RetentionPolicy.SOURCE) @@ -94,64 +97,60 @@ public final class TimeZoneCapabilities implements Parcelable { } }; - - @NonNull private final TimeZoneConfiguration mConfiguration; - private final @CapabilityState int mConfigureAutoDetectionEnabled; - private final @CapabilityState int mConfigureGeoDetectionEnabled; - private final @CapabilityState int mSuggestManualTimeZone; + /** + * The user the capabilities are for. This is used for object equality and debugging but there + * is no accessor. + */ + @NonNull private final UserHandle mUserHandle; + private final @CapabilityState int mConfigureAutoDetectionEnabledCapability; + private final @CapabilityState int mConfigureGeoDetectionEnabledCapability; + private final @CapabilityState int mSuggestManualTimeZoneCapability; private TimeZoneCapabilities(@NonNull Builder builder) { - this.mConfiguration = Objects.requireNonNull(builder.mConfiguration); - this.mConfigureAutoDetectionEnabled = builder.mConfigureAutoDetectionEnabled; - this.mConfigureGeoDetectionEnabled = builder.mConfigureGeoDetectionEnabled; - this.mSuggestManualTimeZone = builder.mSuggestManualTimeZone; + this.mUserHandle = Objects.requireNonNull(builder.mUserHandle); + this.mConfigureAutoDetectionEnabledCapability = + builder.mConfigureAutoDetectionEnabledCapability; + this.mConfigureGeoDetectionEnabledCapability = + builder.mConfigureGeoDetectionEnabledCapability; + this.mSuggestManualTimeZoneCapability = builder.mSuggestManualTimeZoneCapability; } @NonNull private static TimeZoneCapabilities createFromParcel(Parcel in) { - return new TimeZoneCapabilities.Builder() - .setConfiguration(in.readParcelable(null)) - .setConfigureAutoDetectionEnabled(in.readInt()) - .setConfigureGeoDetectionEnabled(in.readInt()) - .setSuggestManualTimeZone(in.readInt()) + UserHandle userHandle = UserHandle.readFromParcel(in); + return new TimeZoneCapabilities.Builder(userHandle) + .setConfigureAutoDetectionEnabledCapability(in.readInt()) + .setConfigureGeoDetectionEnabledCapability(in.readInt()) + .setSuggestManualTimeZoneCapability(in.readInt()) .build(); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mConfiguration, flags); - dest.writeInt(mConfigureAutoDetectionEnabled); - dest.writeInt(mConfigureGeoDetectionEnabled); - dest.writeInt(mSuggestManualTimeZone); - } - - /** - * Returns the user's time zone behavior configuration. - */ - public @NonNull TimeZoneConfiguration getConfiguration() { - return mConfiguration; + UserHandle.writeToParcel(mUserHandle, dest); + dest.writeInt(mConfigureAutoDetectionEnabledCapability); + dest.writeInt(mConfigureGeoDetectionEnabledCapability); + dest.writeInt(mSuggestManualTimeZoneCapability); } /** * Returns the capability state associated with the user's ability to modify the automatic time * zone detection setting. The setting can be updated via {@link - * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and accessed via {@link - * #getConfiguration()}. + * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)}. */ @CapabilityState - public int getConfigureAutoDetectionEnabled() { - return mConfigureAutoDetectionEnabled; + public int getConfigureAutoDetectionEnabledCapability() { + return mConfigureAutoDetectionEnabledCapability; } /** * Returns the capability state associated with the user's ability to modify the geolocation * detection setting. The setting can be updated via {@link - * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and accessed via {@link - * #getConfiguration()}. + * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)}. */ @CapabilityState - public int getConfigureGeoDetectionEnabled() { - return mConfigureGeoDetectionEnabled; + public int getConfigureGeoDetectionEnabledCapability() { + return mConfigureGeoDetectionEnabledCapability; } /** @@ -160,36 +159,37 @@ public final class TimeZoneCapabilities implements Parcelable { * * <p>The suggestion will be ignored in all cases unless the value is {@link * #CAPABILITY_POSSESSED}. See also {@link TimeZoneConfiguration#isAutoDetectionEnabled()}. + * + * @hide */ @CapabilityState - public int getSuggestManualTimeZone() { - return mSuggestManualTimeZone; + public int getSuggestManualTimeZoneCapability() { + return mSuggestManualTimeZoneCapability; } /** - * Constructs a new {@link TimeZoneConfiguration} from an {@code oldConfiguration} and a set of - * {@code requestedChanges}, if the current capabilities allow. The new configuration is - * returned and the capabilities are left unchanged. If the capabilities do not permit one or - * more of the changes then {@code null} is returned. + * Tries to create a new {@link TimeZoneConfiguration} from the {@code config} and the set of + * {@code requestedChanges}, if {@code this} capabilities allow. The new configuration is + * returned. If the capabilities do not permit one or more of the requested changes then {@code + * null} is returned. + * + * @hide */ @Nullable - public TimeZoneConfiguration applyUpdate(TimeZoneConfiguration requestedChanges) { - if (requestedChanges.getUserId() != mConfiguration.getUserId()) { - throw new IllegalArgumentException("User does not match:" - + " this=" + mConfiguration + ", other=" + requestedChanges); - } - + public TimeZoneConfiguration tryApplyConfigChanges( + @NonNull TimeZoneConfiguration config, + @NonNull TimeZoneConfiguration requestedChanges) { TimeZoneConfiguration.Builder newConfigBuilder = - new TimeZoneConfiguration.Builder(mConfiguration); - if (requestedChanges.hasSetting(SETTING_AUTO_DETECTION_ENABLED)) { - if (getConfigureAutoDetectionEnabled() < CAPABILITY_NOT_APPLICABLE) { + new TimeZoneConfiguration.Builder(config); + if (requestedChanges.hasIsAutoDetectionEnabled()) { + if (this.getConfigureAutoDetectionEnabledCapability() < CAPABILITY_NOT_APPLICABLE) { return null; } newConfigBuilder.setAutoDetectionEnabled(requestedChanges.isAutoDetectionEnabled()); } - if (requestedChanges.hasSetting(SETTING_GEO_DETECTION_ENABLED)) { - if (getConfigureGeoDetectionEnabled() < CAPABILITY_NOT_APPLICABLE) { + if (requestedChanges.hasIsGeoDetectionEnabled()) { + if (this.getConfigureGeoDetectionEnabledCapability() < CAPABILITY_NOT_APPLICABLE) { return null; } newConfigBuilder.setGeoDetectionEnabled(requestedChanges.isGeoDetectionEnabled()); @@ -212,71 +212,71 @@ public final class TimeZoneCapabilities implements Parcelable { return false; } TimeZoneCapabilities that = (TimeZoneCapabilities) o; - return Objects.equals(mConfiguration, that.mConfiguration) - && mConfigureAutoDetectionEnabled == that.mConfigureAutoDetectionEnabled - && mConfigureGeoDetectionEnabled == that.mConfigureGeoDetectionEnabled - && mSuggestManualTimeZone == that.mSuggestManualTimeZone; + return mUserHandle.equals(that.mUserHandle) + && mConfigureAutoDetectionEnabledCapability + == that.mConfigureAutoDetectionEnabledCapability + && mConfigureGeoDetectionEnabledCapability + == that.mConfigureGeoDetectionEnabledCapability + && mSuggestManualTimeZoneCapability == that.mSuggestManualTimeZoneCapability; } @Override public int hashCode() { - return Objects.hash(mConfiguration, - mConfigureAutoDetectionEnabled, - mConfigureGeoDetectionEnabled, - mSuggestManualTimeZone); + return Objects.hash(mUserHandle, mConfigureAutoDetectionEnabledCapability, + mConfigureGeoDetectionEnabledCapability, mSuggestManualTimeZoneCapability); } @Override public String toString() { return "TimeZoneDetectorCapabilities{" - + "mConfiguration=" + mConfiguration - + ", mConfigureAutomaticDetectionEnabled=" + mConfigureAutoDetectionEnabled - + ", mConfigureGeoDetectionEnabled=" + mConfigureGeoDetectionEnabled - + ", mSuggestManualTimeZone=" + mSuggestManualTimeZone + + "mUserHandle=" + mUserHandle + + ", mConfigureAutoDetectionEnabledCapability=" + + mConfigureAutoDetectionEnabledCapability + + ", mConfigureGeoDetectionEnabledCapability=" + + mConfigureGeoDetectionEnabledCapability + + ", mSuggestManualTimeZoneCapability=" + mSuggestManualTimeZoneCapability + '}'; } /** @hide */ public static class Builder { - private TimeZoneConfiguration mConfiguration; - private @CapabilityState int mConfigureAutoDetectionEnabled; - private @CapabilityState int mConfigureGeoDetectionEnabled; - private @CapabilityState int mSuggestManualTimeZone; + @NonNull private UserHandle mUserHandle; + private @CapabilityState int mConfigureAutoDetectionEnabledCapability; + private @CapabilityState int mConfigureGeoDetectionEnabledCapability; + private @CapabilityState int mSuggestManualTimeZoneCapability; - /** Sets the user-visible configuration settings. */ - public Builder setConfiguration(@NonNull TimeZoneConfiguration configuration) { - if (!configuration.isComplete()) { - throw new IllegalArgumentException(configuration + " is not complete"); - } - this.mConfiguration = configuration; - return this; + public Builder(@NonNull UserHandle userHandle) { + mUserHandle = Objects.requireNonNull(userHandle); } /** Sets the state for the automatic time zone detection enabled config. */ - public Builder setConfigureAutoDetectionEnabled(@CapabilityState int value) { - this.mConfigureAutoDetectionEnabled = value; + public Builder setConfigureAutoDetectionEnabledCapability(@CapabilityState int value) { + this.mConfigureAutoDetectionEnabledCapability = value; return this; } /** Sets the state for the geolocation time zone detection enabled config. */ - public Builder setConfigureGeoDetectionEnabled(@CapabilityState int value) { - this.mConfigureGeoDetectionEnabled = value; + public Builder setConfigureGeoDetectionEnabledCapability(@CapabilityState int value) { + this.mConfigureGeoDetectionEnabledCapability = value; return this; } /** Sets the state for the suggestManualTimeZone action. */ - public Builder setSuggestManualTimeZone(@CapabilityState int value) { - this.mSuggestManualTimeZone = value; + public Builder setSuggestManualTimeZoneCapability(@CapabilityState int value) { + this.mSuggestManualTimeZoneCapability = value; return this; } /** Returns the {@link TimeZoneCapabilities}. */ @NonNull public TimeZoneCapabilities build() { - verifyCapabilitySet(mConfigureAutoDetectionEnabled, "configureAutoDetectionEnabled"); - verifyCapabilitySet(mConfigureGeoDetectionEnabled, "configureGeoDetectionEnabled"); - verifyCapabilitySet(mSuggestManualTimeZone, "suggestManualTimeZone"); + verifyCapabilitySet(mConfigureAutoDetectionEnabledCapability, + "configureAutoDetectionEnabledCapability"); + verifyCapabilitySet(mConfigureGeoDetectionEnabledCapability, + "configureGeoDetectionEnabledCapability"); + verifyCapabilitySet(mSuggestManualTimeZoneCapability, + "suggestManualTimeZoneCapability"); return new TimeZoneCapabilities(this); } diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.aidl b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.aidl new file mode 100644 index 000000000000..d7b6b58bf85a --- /dev/null +++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +parcelable TimeZoneCapabilitiesAndConfig; diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java new file mode 100644 index 000000000000..6a04f3f277ed --- /dev/null +++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * A pair containing a user's {@link TimeZoneCapabilities} and {@link TimeZoneConfiguration}. + * + * @hide + */ +// @SystemApi +public final class TimeZoneCapabilitiesAndConfig implements Parcelable { + + public static final @NonNull Creator<TimeZoneCapabilitiesAndConfig> CREATOR = + new Creator<TimeZoneCapabilitiesAndConfig>() { + public TimeZoneCapabilitiesAndConfig createFromParcel(Parcel in) { + return TimeZoneCapabilitiesAndConfig.createFromParcel(in); + } + + public TimeZoneCapabilitiesAndConfig[] newArray(int size) { + return new TimeZoneCapabilitiesAndConfig[size]; + } + }; + + + @NonNull private final TimeZoneCapabilities mCapabilities; + @NonNull private final TimeZoneConfiguration mConfiguration; + + /** + * Creates a new instance. + * + * @hide + */ + public TimeZoneCapabilitiesAndConfig( + @NonNull TimeZoneCapabilities capabilities, + @NonNull TimeZoneConfiguration configuration) { + this.mCapabilities = Objects.requireNonNull(capabilities); + this.mConfiguration = Objects.requireNonNull(configuration); + } + + @NonNull + private static TimeZoneCapabilitiesAndConfig createFromParcel(Parcel in) { + TimeZoneCapabilities capabilities = in.readParcelable(null); + TimeZoneConfiguration configuration = in.readParcelable(null); + return new TimeZoneCapabilitiesAndConfig(capabilities, configuration); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(mCapabilities, flags); + dest.writeParcelable(mConfiguration, flags); + } + + /** + * Returns the user's time zone behavior capabilities. + */ + @NonNull + public TimeZoneCapabilities getCapabilities() { + return mCapabilities; + } + + /** + * Returns the user's time zone behavior configuration. + */ + @NonNull + public TimeZoneConfiguration getConfiguration() { + return mConfiguration; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TimeZoneCapabilitiesAndConfig that = (TimeZoneCapabilitiesAndConfig) o; + return mCapabilities.equals(that.mCapabilities) + && mConfiguration.equals(that.mConfiguration); + } + + @Override + public int hashCode() { + return Objects.hash(mCapabilities, mConfiguration); + } + + @Override + public String toString() { + return "TimeZoneDetectorCapabilitiesAndConfig{" + + "mCapabilities=" + mCapabilities + + ", mConfiguration=" + mConfiguration + + '}'; + } +} diff --git a/core/java/android/app/timezonedetector/TimeZoneConfiguration.aidl b/core/java/android/app/time/TimeZoneConfiguration.aidl index 62240ba5946b..8e859299d073 100644 --- a/core/java/android/app/timezonedetector/TimeZoneConfiguration.aidl +++ b/core/java/android/app/time/TimeZoneConfiguration.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.app.timezonedetector; +package android.app.time; parcelable TimeZoneConfiguration; diff --git a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java b/core/java/android/app/time/TimeZoneConfiguration.java index e879091cd68e..488818a528ef 100644 --- a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java +++ b/core/java/android/app/time/TimeZoneConfiguration.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package android.app.timezonedetector; +package android.app.time; import android.annotation.NonNull; import android.annotation.StringDef; -import android.annotation.UserIdInt; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -36,15 +35,13 @@ import java.util.Objects; * several settings, the device behavior may not be directly affected by the setting value. * * <p>Settings can be left absent when updating configuration via {@link - * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and those settings will not be + * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)} and those settings will not be * changed. Not all configuration settings can be modified by all users: see {@link - * TimeZoneDetector#getCapabilities()} and {@link TimeZoneCapabilities} for details. - * - * <p>See {@link #hasSetting(String)} with {@code PROPERTY_} constants for testing for the presence - * of individual settings. + * TimeManager#getTimeZoneCapabilitiesAndConfig()} and {@link TimeZoneCapabilities} for details. * * @hide */ +// @SystemApi public final class TimeZoneConfiguration implements Parcelable { public static final @NonNull Creator<TimeZoneConfiguration> CREATOR = @@ -58,53 +55,48 @@ public final class TimeZoneConfiguration implements Parcelable { } }; - /** All configuration properties */ + /** + * All configuration properties + * + * @hide + */ @StringDef({ SETTING_AUTO_DETECTION_ENABLED, SETTING_GEO_DETECTION_ENABLED }) @Retention(RetentionPolicy.SOURCE) @interface Setting {} /** See {@link TimeZoneConfiguration#isAutoDetectionEnabled()} for details. */ @Setting - public static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled"; + private static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled"; /** See {@link TimeZoneConfiguration#isGeoDetectionEnabled()} for details. */ @Setting - public static final String SETTING_GEO_DETECTION_ENABLED = "geoDetectionEnabled"; + private static final String SETTING_GEO_DETECTION_ENABLED = "geoDetectionEnabled"; - private final @UserIdInt int mUserId; @NonNull private final Bundle mBundle; private TimeZoneConfiguration(Builder builder) { - this.mUserId = builder.mUserId; this.mBundle = Objects.requireNonNull(builder.mBundle); } private static TimeZoneConfiguration createFromParcel(Parcel in) { - return new TimeZoneConfiguration.Builder(in.readInt()) + return new TimeZoneConfiguration.Builder() .setPropertyBundleInternal(in.readBundle()) .build(); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mUserId); dest.writeBundle(mBundle); } - /** Returns the ID of the user this configuration is associated with. */ - public @UserIdInt int getUserId() { - return mUserId; - } - - /** Returns {@code true} if all known settings are present. */ + /** + * Returns {@code true} if all known settings are present. + * + * @hide + */ public boolean isComplete() { - return hasSetting(SETTING_AUTO_DETECTION_ENABLED) - && hasSetting(SETTING_GEO_DETECTION_ENABLED); - } - - /** Returns true if the specified setting is set. */ - public boolean hasSetting(@Setting String setting) { - return mBundle.containsKey(setting); + return hasIsAutoDetectionEnabled() + && hasIsGeoDetectionEnabled(); } /** @@ -112,9 +104,10 @@ public final class TimeZoneConfiguration implements Parcelable { * controls whether a device will attempt to determine the time zone automatically using * contextual information if the device supports auto detection. * - * <p>This setting is global and can be updated by some users. + * <p>See {@link TimeZoneCapabilities#getConfigureAutoDetectionEnabledCapability()} for how to + * tell if the setting is meaningful for the current user at this time. * - * @throws IllegalStateException if the setting has not been set + * @throws IllegalStateException if the setting is not present */ public boolean isAutoDetectionEnabled() { enforceSettingPresent(SETTING_AUTO_DETECTION_ENABLED); @@ -122,21 +115,39 @@ public final class TimeZoneConfiguration implements Parcelable { } /** + * Returns {@code true} if the {@link #isAutoDetectionEnabled()} setting is present. + * + * @hide + */ + public boolean hasIsAutoDetectionEnabled() { + return mBundle.containsKey(SETTING_AUTO_DETECTION_ENABLED); + } + + /** * Returns the value of the {@link #SETTING_GEO_DETECTION_ENABLED} setting. This - * controls whether a device can use geolocation to determine time zone. Only used when - * {@link #isAutoDetectionEnabled()} is {@code true} and when the user has allowed their - * location to be used. + * controls whether the device can use geolocation to determine time zone. This value may only + * be used by Android under some circumstances. For example, it is not used when + * {@link #isGeoDetectionEnabled()} is {@code false}. * - * <p>This setting is user-scoped and can be updated by some users. - * See {@link TimeZoneCapabilities#getConfigureGeoDetectionEnabled()}. + * <p>See {@link TimeZoneCapabilities#getConfigureGeoDetectionEnabledCapability()} for how to + * tell if the setting is meaningful for the current user at this time. * - * @throws IllegalStateException if the setting has not been set + * @throws IllegalStateException if the setting is not present */ public boolean isGeoDetectionEnabled() { enforceSettingPresent(SETTING_GEO_DETECTION_ENABLED); return mBundle.getBoolean(SETTING_GEO_DETECTION_ENABLED); } + /** + * Returns {@code true} if the {@link #isGeoDetectionEnabled()} setting is present. + * + * @hide + */ + public boolean hasIsGeoDetectionEnabled() { + return mBundle.containsKey(SETTING_GEO_DETECTION_ENABLED); + } + @Override public int describeContents() { return 0; @@ -151,20 +162,18 @@ public final class TimeZoneConfiguration implements Parcelable { return false; } TimeZoneConfiguration that = (TimeZoneConfiguration) o; - return mUserId == that.mUserId - && mBundle.kindofEquals(that.mBundle); + return mBundle.kindofEquals(that.mBundle); } @Override public int hashCode() { - return Objects.hash(mUserId, mBundle); + return Objects.hash(mBundle); } @Override public String toString() { return "TimeZoneConfiguration{" - + "mUserId=" + mUserId - + ", mBundle=" + mBundle + + "mBundle=" + mBundle + '}'; } @@ -174,43 +183,43 @@ public final class TimeZoneConfiguration implements Parcelable { } } - /** @hide */ - public static class Builder { + /** + * A builder for {@link TimeZoneConfiguration} objects. + * + * @hide + */ + // @SystemApi + public static final class Builder { - private final @UserIdInt int mUserId; private final Bundle mBundle = new Bundle(); /** - * Creates a new Builder for a userId with no settings held. + * Creates a new Builder with no settings held. */ - public Builder(@UserIdInt int userId) { - mUserId = userId; + public Builder() { } /** - * Creates a new Builder by copying the user ID and settings from an existing instance. + * Creates a new Builder by copying the settings from an existing instance. */ - public Builder(TimeZoneConfiguration toCopy) { - this.mUserId = toCopy.mUserId; + public Builder(@NonNull TimeZoneConfiguration toCopy) { mergeProperties(toCopy); } /** * Merges {@code other} settings into this instances, replacing existing values in this * where the settings appear in both. + * + * @hide */ - public Builder mergeProperties(TimeZoneConfiguration other) { - if (mUserId != other.mUserId) { - throw new IllegalArgumentException( - "Cannot merge configurations for different user IDs." - + " this.mUserId=" + this.mUserId - + ", other.mUserId=" + other.mUserId); - } + @NonNull + public Builder mergeProperties(@NonNull TimeZoneConfiguration other) { this.mBundle.putAll(other.mBundle); return this; } - Builder setPropertyBundleInternal(Bundle bundle) { + @NonNull + Builder setPropertyBundleInternal(@NonNull Bundle bundle) { this.mBundle.putAll(bundle); return this; } @@ -218,6 +227,7 @@ public final class TimeZoneConfiguration implements Parcelable { /** * Sets the state of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting. */ + @NonNull public Builder setAutoDetectionEnabled(boolean enabled) { this.mBundle.putBoolean(SETTING_AUTO_DETECTION_ENABLED, enabled); return this; @@ -226,6 +236,7 @@ public final class TimeZoneConfiguration implements Parcelable { /** * Sets the state of the {@link #SETTING_GEO_DETECTION_ENABLED} setting. */ + @NonNull public Builder setGeoDetectionEnabled(boolean enabled) { this.mBundle.putBoolean(SETTING_GEO_DETECTION_ENABLED, enabled); return this; diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl index 4f7e1f62928a..af0389a14c4b 100644 --- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl +++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl @@ -16,11 +16,11 @@ package android.app.timezonedetector; -import android.app.timezonedetector.ITimeZoneConfigurationListener; +import android.app.time.ITimeZoneDetectorListener; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; -import android.app.timezonedetector.TimeZoneCapabilities; -import android.app.timezonedetector.TimeZoneConfiguration; /** * System private API to communicate with time zone detector service. @@ -35,9 +35,9 @@ import android.app.timezonedetector.TimeZoneConfiguration; * {@hide} */ interface ITimeZoneDetectorService { - TimeZoneCapabilities getCapabilities(); - void addConfigurationListener(ITimeZoneConfigurationListener listener); - void removeConfigurationListener(ITimeZoneConfigurationListener listener); + TimeZoneCapabilitiesAndConfig getCapabilitiesAndConfig(); + void addListener(ITimeZoneDetectorListener listener); + void removeListener(ITimeZoneDetectorListener listener); boolean updateConfiguration(in TimeZoneConfiguration configuration); diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java index 2b1cbf259c55..486232d0f6ed 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetector.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java @@ -30,70 +30,6 @@ import android.content.Context; public interface TimeZoneDetector { /** - * Returns the current user's time zone capabilities. See {@link TimeZoneCapabilities}. - */ - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @NonNull - TimeZoneCapabilities getCapabilities(); - - /** - * Modifies the time zone detection configuration. - * - * <p>Configuration settings vary in scope: some may be global (affect all users), others may be - * specific to the current user. - * - * <p>The ability to modify configuration settings can be subject to restrictions. For - * example, they may be determined by device hardware, general policy (i.e. only the primary - * user can set them), or by a managed device policy. Use {@link #getCapabilities()} to obtain - * information at runtime about the user's capabilities. - * - * <p>Attempts to modify configuration settings with capabilities that are {@link - * TimeZoneCapabilities#CAPABILITY_NOT_SUPPORTED} or {@link - * TimeZoneCapabilities#CAPABILITY_NOT_ALLOWED} will have no effect and a {@code false} - * will be returned. Modifying configuration settings with capabilities that are {@link - * TimeZoneCapabilities#CAPABILITY_NOT_APPLICABLE} or {@link - * TimeZoneCapabilities#CAPABILITY_POSSESSED} will succeed. See {@link - * TimeZoneCapabilities} for further details. - * - * <p>If the supplied configuration only has some values set, then only the specified settings - * will be updated (where the user's capabilities allow) and other settings will be left - * unchanged. - * - * @return {@code true} if all the configuration settings specified have been set to the - * new values, {@code false} if none have - */ - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration); - - /** - * An interface that can be used to listen for changes to the time zone detector configuration. - */ - @FunctionalInterface - interface TimeZoneConfigurationListener { - /** - * Called when something about the time zone configuration on the device has changed. - * This could be because the current user has changed, one of the device's relevant settings - * has changed, or something that could affect a user's capabilities has changed. - * There are no guarantees about the thread used. - */ - void onChange(); - } - - /** - * Registers a listener that will be informed when something about the time zone configuration - * changes. - */ - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - void addConfigurationListener(@NonNull TimeZoneConfigurationListener listener); - - /** - * Removes a listener previously passed to - * {@link #addConfigurationListener(ITimeZoneConfigurationListener)} - */ - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - void removeConfigurationListener(@NonNull TimeZoneConfigurationListener listener); - - /** * A shared utility method to create a {@link ManualTimeZoneSuggestion}. * * @hide diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java index 4c69732abec9..3bd6b4bd692a 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java @@ -21,7 +21,6 @@ import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; -import android.util.ArraySet; import android.util.Log; /** @@ -35,108 +34,12 @@ public final class TimeZoneDetectorImpl implements TimeZoneDetector { private final ITimeZoneDetectorService mITimeZoneDetectorService; - private ITimeZoneConfigurationListener mConfigurationReceiver; - private ArraySet<TimeZoneConfigurationListener> mConfigurationListeners; - public TimeZoneDetectorImpl() throws ServiceNotFoundException { mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE)); } @Override - @NonNull - public TimeZoneCapabilities getCapabilities() { - if (DEBUG) { - Log.d(TAG, "getCapabilities called"); - } - try { - return mITimeZoneDetectorService.getCapabilities(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - @Override - public boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration) { - if (DEBUG) { - Log.d(TAG, "updateConfiguration called: " + configuration); - } - try { - return mITimeZoneDetectorService.updateConfiguration(configuration); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - @Override - public void addConfigurationListener(@NonNull TimeZoneConfigurationListener listener) { - if (DEBUG) { - Log.d(TAG, "addConfigurationListener called: " + listener); - } - synchronized (this) { - if (mConfigurationListeners.contains(listener)) { - return; - } - if (mConfigurationReceiver == null) { - ITimeZoneConfigurationListener iListener = - new ITimeZoneConfigurationListener.Stub() { - @Override - public void onChange() { - notifyConfigurationListeners(); - } - }; - mConfigurationReceiver = iListener; - } - if (mConfigurationListeners == null) { - mConfigurationListeners = new ArraySet<>(); - } - - boolean wasEmpty = mConfigurationListeners.isEmpty(); - mConfigurationListeners.add(listener); - if (wasEmpty) { - try { - mITimeZoneDetectorService.addConfigurationListener(mConfigurationReceiver); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - } - } - - private void notifyConfigurationListeners() { - final ArraySet<TimeZoneConfigurationListener> configurationListeners; - synchronized (this) { - configurationListeners = new ArraySet<>(mConfigurationListeners); - } - int size = configurationListeners.size(); - for (int i = 0; i < size; i++) { - configurationListeners.valueAt(i).onChange(); - } - } - - @Override - public void removeConfigurationListener(@NonNull TimeZoneConfigurationListener listener) { - if (DEBUG) { - Log.d(TAG, "removeConfigurationListener called: " + listener); - } - - synchronized (this) { - if (mConfigurationListeners == null) { - return; - } - boolean wasEmpty = mConfigurationListeners.isEmpty(); - mConfigurationListeners.remove(listener); - if (mConfigurationListeners.isEmpty() && !wasEmpty) { - try { - mITimeZoneDetectorService.removeConfigurationListener(mConfigurationReceiver); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - } - } - - @Override public boolean suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) { if (DEBUG) { Log.d(TAG, "suggestManualTimeZone called: " + timeZoneSuggestion); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 005648ffec36..666ba32d0e4f 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -40,6 +40,7 @@ import android.app.ActivityManager; import android.app.IApplicationThread; import android.app.IServiceConnection; import android.app.VrManager; +import android.app.time.TimeManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -5090,6 +5091,14 @@ public abstract class Context { public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector"; /** + * Use with {@link #getSystemService(String)} to retrieve an {@link TimeManager}. + * @hide + * + * @see #getSystemService(String) + */ + public static final String TIME_MANAGER = "time_manager"; + + /** * Binder service name for {@link AppBindingService}. * @hide */ |
