diff --git a/Android.mk b/Android.mk index 1aaa09a40ffde..283e9dc415d40 100644 --- a/Android.mk +++ b/Android.mk @@ -103,6 +103,8 @@ LOCAL_SRC_FILES += \ core/java/android/app/backup/IFullBackupRestoreObserver.aidl \ core/java/android/app/backup/IRestoreObserver.aidl \ core/java/android/app/backup/IRestoreSession.aidl \ + core/java/android/app/timezone/ICallback.aidl \ + core/java/android/app/timezone/IRulesManager.aidl \ core/java/android/app/usage/IUsageStatsManager.aidl \ core/java/android/bluetooth/IBluetooth.aidl \ core/java/android/bluetooth/IBluetoothA2dp.aidl \ diff --git a/core/java/android/app/timezone/Callback.java b/core/java/android/app/timezone/Callback.java new file mode 100644 index 0000000000000..b51e5bad0f69c --- /dev/null +++ b/core/java/android/app/timezone/Callback.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 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.timezone; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Callback interface for receiving information about an async time zone operation. + * The methods will be called on your application's main thread. + * + * @hide + */ +// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728 +public abstract class Callback { + + @Retention(RetentionPolicy.SOURCE) + @IntDef({SUCCESS, ERROR_UNKNOWN_FAILURE, ERROR_INSTALL_BAD_DISTRO_STRUCTURE, + ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION, ERROR_INSTALL_RULES_TOO_OLD, + ERROR_INSTALL_VALIDATION_ERROR}) + public @interface AsyncResultCode {} + + /** + * Indicates that an operation succeeded. + */ + public static final int SUCCESS = 0; + + /** + * Indicates an install / uninstall did not fully succeed for an unknown reason. + */ + public static final int ERROR_UNKNOWN_FAILURE = 1; + + /** + * Indicates an install failed because of a structural issue with the provided distro, + * e.g. it wasn't in the right format or the contents were structured incorrectly. + */ + public static final int ERROR_INSTALL_BAD_DISTRO_STRUCTURE = 2; + + /** + * Indicates an install failed because of a versioning issue with the provided distro, + * e.g. it was created for a different version of Android. + */ + public static final int ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION = 3; + + /** + * Indicates an install failed because the rules provided are too old for the device, + * e.g. the Android device shipped with a newer rules version. + */ + public static final int ERROR_INSTALL_RULES_TOO_OLD = 4; + + /** + * Indicates an install failed because the distro contents failed validation. + */ + public static final int ERROR_INSTALL_VALIDATION_ERROR = 5; + + /** + * Reports the result of an async time zone operation. + */ + public abstract void onFinished(@AsyncResultCode int status); +} diff --git a/core/java/android/app/timezone/DistroFormatVersion.java b/core/java/android/app/timezone/DistroFormatVersion.java new file mode 100644 index 0000000000000..e879e8f8adbc6 --- /dev/null +++ b/core/java/android/app/timezone/DistroFormatVersion.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2017 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.timezone; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Versioning information about a distro's format or a device's supported format. + * + *
The following properties are included: + *
The following properties are included: + *
This interface is only intended for system apps to call. They should use the + * {@link android.app.timezone.RulesManager} class rather than going through this + * Binder interface directly. See {@link android.app.timezone.RulesManager} for more complete + * documentation. + * + * {@hide} + */ +interface IRulesManager { + + /** + * Returns information about the current time zone rules state such as the IANA version of + * the system and any currently installed distro. This method is intended to allow clients to + * determine if the current state can be improved; for example by passing the information to a + * server that may provide a new distro for download. + */ + RulesState getRulesState(); + + /** + * Requests installation of the supplied distro. The distro must have been checked for integrity + * by the caller or have been received via a trusted mechanism. + * + * @param distroFileDescriptor the file descriptor for the distro + * @param checkToken an optional token provided if the install was triggered in response to a + * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent + * @param callback the {@link ICallback} to receive callbacks related to the + * installation + * @return zero if the installation will be attempted; nonzero on error + */ + int requestInstall(in ParcelFileDescriptor distroFileDescriptor, in byte[] checkToken, + ICallback callback); + + /** + * Requests uninstallation of the currently installed distro (leaving the device with no + * distro installed). + * + * @param checkToken an optional token provided if the uninstall was triggered in response to a + * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent + * @param callback the {@link ICallback} to receive callbacks related to the + * uninstall + * @return zero if the uninstallation will be attempted; nonzero on error + */ + int requestUninstall(in byte[] checkToken, ICallback callback); + + /** + * Requests the system does not modify the currently installed time zone distro, if any. This + * method records the fact that a time zone check operation triggered by the system is now + * complete and there was nothing to do. The token passed should be the one presented when the + * check was triggered. + * + *
Note: Passing {@code success == false} may result in more checks being triggered. Clients + * should be careful not to pass false if the failure is unlikely to resolve by itself. + * + * @param checkToken an optional token provided if the install was triggered in response to a + * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent + * @param success true if the check was successful, false if it was not successful but may + * succeed if it is retried + */ + void requestNothing(in byte[] token, boolean success); +} diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java new file mode 100644 index 0000000000000..649d894ca6857 --- /dev/null +++ b/core/java/android/app/timezone/RulesManager.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2017 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.timezone; + +import android.annotation.IntDef; +import android.content.Context; +import android.os.Handler; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; + +/** + * The interface through which a time zone update application interacts with the Android system + * to handle time zone rule updates. + * + *
This interface is intended for use with the default APK-based time zone rules update + * application but it can also be used by OEMs if that mechanism is turned off using configuration. + * All callers must possess the {@link android.Manifest.permission#UPDATE_TIME_ZONE_RULES} system + * permission. + * + *
When using the default mechanism, when properly configured the Android system will send a + * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent with a + * {@link RulesUpdaterContract#EXTRA_CHECK_TOKEN} extra to the time zone rules updater application + * when it detects that it or the OEM's APK containing time zone rules data has been modified. The + * updater application is then responsible for calling one of + * {@link #requestInstall(ParcelFileDescriptor, byte[], Callback)}, + * {@link #requestUninstall(byte[], Callback)} or + * {@link #requestNothing(byte[], boolean)}, indicating, respectively, whether a new time zone rules + * distro should be installed, the current distro should be uninstalled, or there is nothing to do + * (or that the correct operation could not be determined due to an error). In each case the updater + * must pass the {@link RulesUpdaterContract#EXTRA_CHECK_TOKEN} value it received from the intent + * back so the system in the {@code checkToken} parameter. + * + *
If OEMs want to handle their own time zone rules updates, perhaps via a server-side component + * rather than an APK, then they should disable the default triggering mechanism in config and are + * responsible for triggering their own update checks / installs / uninstalls. In this case the + * "check token" parameter can be left null and there is never any need to call + * {@link #requestNothing(byte[], boolean)}. + * + *
OEMs should not mix the default mechanism and their own as this could lead to conflicts and + * unnecessary checks being triggered. + * + *
Applications obtain this using {@link android.app.Activity#getSystemService(String)} with + * {@link Context#TIME_ZONE_RULES_MANAGER_SERVICE}. + * @hide + */ +// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728 +public final class RulesManager { + private static final String TAG = "timezone.RulesManager"; + private static final boolean DEBUG = false; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({SUCCESS, ERROR_UNKNOWN_FAILURE, ERROR_OPERATION_IN_PROGRESS}) + public @interface ResultCode {} + + /** + * Indicates that an operation succeeded. + */ + public static final int SUCCESS = 0; + + /** + * Indicates that an install/uninstall cannot be initiated because there is one already in + * progress. + */ + public static final int ERROR_OPERATION_IN_PROGRESS = 1; + + /** + * Indicates an install / uninstall did not fully succeed for an unknown reason. + */ + public static final int ERROR_UNKNOWN_FAILURE = 2; + + private final Context mContext; + private final IRulesManager mIRulesManager; + + public RulesManager(Context context) { + mContext = context; + mIRulesManager = IRulesManager.Stub.asInterface( + ServiceManager.getService(Context.TIME_ZONE_RULES_MANAGER_SERVICE)); + } + + /** + * Returns information about the current time zone rules state such as the IANA version of + * the system and any currently installed distro. This method is intended to allow clients to + * determine if the current state can be improved; for example by passing the information to a + * server that may provide a new distro for download. + */ + public RulesState getRulesState() { + try { + logDebug("sIRulesManager.getRulesState()"); + RulesState rulesState = mIRulesManager.getRulesState(); + logDebug("sIRulesManager.getRulesState() returned " + rulesState); + return rulesState; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Requests installation of the supplied distro. The distro must have been checked for integrity + * by the caller or have been received via a trusted mechanism. + * + * @param distroFileDescriptor the file descriptor for the distro + * @param checkToken an optional token provided if the install was triggered in response to a + * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent + * @param callback the {@link Callback} to receive callbacks related to the installation + * @return {@link #SUCCESS} if the installation will be attempted + */ + @ResultCode + public int requestInstall( + ParcelFileDescriptor distroFileDescriptor, byte[] checkToken, Callback callback) + throws IOException { + + ICallback iCallback = new CallbackWrapper(mContext, callback); + try { + logDebug("sIRulesManager.requestInstall()"); + return mIRulesManager.requestInstall(distroFileDescriptor, checkToken, iCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Requests uninstallation of the currently installed distro (leaving the device with no + * distro installed). + * + * @param checkToken an optional token provided if the uninstall was triggered in response to a + * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent + * @param callback the {@link Callback} to receive callbacks related to the uninstall + * @return {@link #SUCCESS} if the uninstallation will be attempted + */ + @ResultCode + public int requestUninstall(byte[] checkToken, Callback callback) { + ICallback iCallback = new CallbackWrapper(mContext, callback); + try { + logDebug("sIRulesManager.requestUninstall()"); + return mIRulesManager.requestUninstall(checkToken, iCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /* + * We wrap incoming binder calls with a private class implementation that + * redirects them into main-thread actions. This serializes the backup + * progress callbacks nicely within the usual main-thread lifecycle pattern. + */ + private class CallbackWrapper extends ICallback.Stub { + final Handler mHandler; + final Callback mCallback; + + CallbackWrapper(Context context, Callback callback) { + mCallback = callback; + mHandler = new Handler(context.getMainLooper()); + } + + // Binder calls into this object just enqueue on the main-thread handler + @Override + public void onFinished(int status) { + logDebug("mCallback.onFinished(status), status=" + status); + mHandler.post(() -> mCallback.onFinished(status)); + } + } + + /** + * Requests the system does not modify the currently installed time zone distro, if any. This + * method records the fact that a time zone check operation triggered by the system is now + * complete and there was nothing to do. The token passed should be the one presented when the + * check was triggered. + * + *
Note: Passing {@code success == false} may result in more checks being triggered. Clients + * should be careful not to pass false if the failure is unlikely to resolve by itself. + * + * @param checkToken an optional token provided if the install was triggered in response to a + * {@link RulesUpdaterContract#ACTION_TRIGGER_RULES_UPDATE_CHECK} intent + * @param succeeded true if the check was successful, false if it was not successful but may + * succeed if it is retried + */ + public void requestNothing(byte[] checkToken, boolean succeeded) { + try { + logDebug("sIRulesManager.requestNothing() with token=" + Arrays.toString(checkToken)); + mIRulesManager.requestNothing(checkToken, succeeded); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + static void logDebug(String msg) { + if (DEBUG) { + Log.v(TAG, msg); + } + } +} diff --git a/core/java/android/app/timezone/RulesState.aidl b/core/java/android/app/timezone/RulesState.aidl new file mode 100644 index 0000000000000..f789120eb7248 --- /dev/null +++ b/core/java/android/app/timezone/RulesState.aidl @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2017 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. + */ + +parcelable RulesState; \ No newline at end of file diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java new file mode 100644 index 0000000000000..33f4e8060b3e6 --- /dev/null +++ b/core/java/android/app/timezone/RulesState.java @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2017 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.timezone; + +import static android.app.timezone.Utils.validateConditionalNull; +import static android.app.timezone.Utils.validateNotNull; +import static android.app.timezone.Utils.validateRulesVersion; + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Description of the state of time zone rules on a device. + * + *
The following properties are included: + *