diff options
| author | 2022-06-24 19:31:24 +0000 | |
|---|---|---|
| committer | 2022-06-24 19:31:24 +0000 | |
| commit | cc4630cdbfdee1fca83d366c8525ddcd257f36b7 (patch) | |
| tree | 2011f12525025ed5b21e52f26366b0eab4227f0c | |
| parent | 38ff7fa0993dd2fdf4e24152e9de3fb3ddc4cdeb (diff) | |
| parent | 050cc50319f84f6381f2ee88cec549cb17c04849 (diff) | |
Merge "Remove TZUvA feature." am: 10c7acc51d am: 050cc50319
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/953617
Change-Id: I50d4b907cc0d88451b771ff414978dfeaa2e4913
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
27 files changed, 0 insertions, 5618 deletions
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7439b2f0921f..3b4b830fc0df 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -7007,10 +7007,6 @@ android:permission="android.permission.BIND_JOB_SERVICE" > </service> - <service android:name="com.android.server.timezone.TimeZoneUpdateIdler" - android:permission="android.permission.BIND_JOB_SERVICE" > - </service> - <service android:name="com.android.server.usage.UsageStatsIdleService" android:permission="android.permission.BIND_JOB_SERVICE" > </service> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 31229e97024f..7d8e06df2185 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1743,49 +1743,6 @@ suggestions to TimeDetector service. See also config_autoTimeSourcesPriority. --> <bool name="config_enableGnssTimeUpdateService">false</bool> - <!-- Enables the TimeZoneRuleManager service. This is the global switch for the updateable time - zone update mechanism. --> - <bool name="config_enableUpdateableTimeZoneRules">false</bool> - - <!-- Enables APK-based time zone update triggering. Set this to false when updates are triggered - via external events and not by APK updates. For example, if an updater checks with a server - on a regular schedule. - [This is only used if config_enableUpdateableTimeZoneRules is true.] --> - <bool name="config_timeZoneRulesUpdateTrackingEnabled">false</bool> - - <!-- The package of the time zone rules updater application. Expected to be the same - for all Android devices that support APK-based time zone rule updates. - A package-targeted com.android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK intent - will be sent to the updater app if the system server detects an update to the updater or - data app packages. - The package referenced here must have the android.permission.UPDATE_TIME_ZONE_RULES - permission. - [This is only used if config_enableUpdateableTimeZoneRules and - config_timeZoneRulesUpdateTrackingEnabled are true.] --> - <string name="config_timeZoneRulesUpdaterPackage" translatable="false">com.android.timezone.updater</string> - - <!-- The package of the time zone rules data application. Expected to be configured - by OEMs to reference their own priv-app APK package. - A package-targeted com.android.intent.action.timezone.TRIGGER_RULES_UPDATE_CHECK intent - will be sent to the updater app if the system server detects an update to the updater or - data app packages. - [This is only used if config_enableUpdateableTimeZoneRules and - config_timeZoneRulesUpdateTrackingEnabled are true.] --> - <string name="config_timeZoneRulesDataPackage" translatable="false"></string> - - <!-- The allowed time in milliseconds between an update check intent being broadcast and the - response being considered overdue. Reliability triggers will not fire in this time. - [This is only used if config_enableUpdateableTimeZoneRules and - config_timeZoneRulesUpdateTrackingEnabled are true.] --> - <!-- 5 minutes --> - <integer name="config_timeZoneRulesCheckTimeMillisAllowed">300000</integer> - - <!-- The number of times a time zone update check is allowed to fail before the system will stop - reacting to reliability triggers. - [This is only used if config_enableUpdateableTimeZoneRules and - config_timeZoneRulesUpdateTrackingEnabled are true.] --> - <integer name="config_timeZoneRulesCheckRetryCount">5</integer> - <!-- Whether the geolocation time zone detection feature is enabled. Setting this to false means the feature cannot be used. Setting this to true means system server components can be tested and location time zone detection may be used if other configuration allows (see diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index afca85d0b180..60d847e59368 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -323,12 +323,6 @@ <java-symbol type="bool" name="config_checkWallpaperAtBoot" /> <java-symbol type="string" name="config_wallpaperManagerServiceName" /> <java-symbol type="string" name="config_inputEventCompatProcessorOverrideClassName" /> - <java-symbol type="bool" name="config_enableUpdateableTimeZoneRules" /> - <java-symbol type="bool" name="config_timeZoneRulesUpdateTrackingEnabled" /> - <java-symbol type="string" name="config_timeZoneRulesUpdaterPackage" /> - <java-symbol type="string" name="config_timeZoneRulesDataPackage" /> - <java-symbol type="integer" name="config_timeZoneRulesCheckTimeMillisAllowed" /> - <java-symbol type="integer" name="config_timeZoneRulesCheckRetryCount" /> <java-symbol type="bool" name="config_sendAudioBecomingNoisy" /> <java-symbol type="bool" name="config_enableScreenshotChord" /> <java-symbol type="bool" name="config_enableWifiDisplay" /> diff --git a/services/core/Android.bp b/services/core/Android.bp index 89c8ca567dd9..b00d72b16372 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -141,8 +141,6 @@ java_library_static { ], static_libs: [ - "time_zone_distro", - "time_zone_distro_installer", "android.hardware.authsecret-V1.0-java", "android.hardware.boot-V1.0-java", "android.hardware.boot-V1.1-java", diff --git a/services/core/java/com/android/server/RuntimeService.java b/services/core/java/com/android/server/RuntimeService.java index f4249f844873..a04d5e7a525d 100644 --- a/services/core/java/com/android/server/RuntimeService.java +++ b/services/core/java/com/android/server/RuntimeService.java @@ -20,21 +20,13 @@ import android.content.Context; import android.os.Binder; import android.service.runtime.DebugEntryProto; import android.service.runtime.RuntimeServiceInfoProto; -import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.i18n.timezone.DebugInfo; import com.android.i18n.timezone.I18nModuleDebug; -import com.android.i18n.timezone.TimeZoneDataFiles; import com.android.internal.util.DumpUtils; -import com.android.timezone.distro.DistroException; -import com.android.timezone.distro.DistroVersion; -import com.android.timezone.distro.FileUtils; -import com.android.timezone.distro.TimeZoneDistro; -import java.io.File; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; /** @@ -61,7 +53,6 @@ public class RuntimeService extends Binder { ProtoOutputStream proto = null; DebugInfo i18nLibraryDebugInfo = I18nModuleDebug.getDebugInfo(); - addTimeZoneApkDebugInfo(i18nLibraryDebugInfo); if (protoFormat) { proto = new ProtoOutputStream(fd); @@ -86,20 +77,6 @@ public class RuntimeService extends Binder { } /** - * Add information to {@link DebugInfo} about the time zone data supplied by the - * "Time zone updates via APK" feature. - */ - private static void addTimeZoneApkDebugInfo(DebugInfo coreLibraryDebugInfo) { - // Add /data tz data set using the DistroVersion class (which libcore cannot use). - // This update mechanism will be removed after the time zone APEX is launched so this - // untidiness will disappear with it. - String debugKeyPrefix = "core_library.timezone.source.data_"; - String versionFileName = TimeZoneDataFiles.getDataTimeZoneFile( - TimeZoneDistro.DISTRO_VERSION_FILE_NAME); - addDistroVersionDebugInfo(versionFileName, debugKeyPrefix, coreLibraryDebugInfo); - } - - /** * Prints {@code coreLibraryDebugInfo} to {@code pw}. * * <p>If you change this method, make sure to modify @@ -131,42 +108,4 @@ public class RuntimeService extends Binder { protoStream.end(entryToken); } } - - /** - * Adds version information to {@code debugInfo} from the distro_version file that may exist - * at {@code distroVersionFileName}. If the file does not exist or cannot be read this is - * reported as debug information too. - */ - private static void addDistroVersionDebugInfo(String distroVersionFileName, - String debugKeyPrefix, DebugInfo debugInfo) { - File file = new File(distroVersionFileName); - String statusKey = debugKeyPrefix + "status"; - if (file.exists()) { - try { - byte[] versionBytes = - FileUtils.readBytes(file, DistroVersion.DISTRO_VERSION_FILE_LENGTH); - DistroVersion distroVersion = DistroVersion.fromBytes(versionBytes); - String formatVersionString = distroVersion.formatMajorVersion + "." - + distroVersion.formatMinorVersion; - debugInfo.addStringEntry(statusKey, "OK") - .addStringEntry(debugKeyPrefix + "formatVersion", formatVersionString) - .addStringEntry(debugKeyPrefix + "rulesVersion", - distroVersion.rulesVersion) - .addStringEntry(debugKeyPrefix + "revision", - distroVersion.revision); - } catch (IOException | DistroException e) { - debugInfo.addStringEntry(statusKey, "ERROR"); - debugInfo.addStringEntry(debugKeyPrefix + "exception_class", - e.getClass().getName()); - debugInfo.addStringEntry(debugKeyPrefix + "exception_msg", e.getMessage()); - logMessage("Error reading " + file, e); - } - } else { - debugInfo.addStringEntry(statusKey, "NOT_FOUND"); - } - } - - private static void logMessage(String msg, Throwable t) { - Slog.v(TAG, msg, t); - } } diff --git a/services/core/java/com/android/server/timezone/CheckToken.java b/services/core/java/com/android/server/timezone/CheckToken.java deleted file mode 100644 index 4c4a8d72f51d..000000000000 --- a/services/core/java/com/android/server/timezone/CheckToken.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.Arrays; - -/** - * A deserialized version of the byte[] sent to the time zone update application to identify a - * triggered time zone update check. It encodes the optimistic lock ID used to detect - * concurrent checks and the minimal package versions that will have been checked. - */ -final class CheckToken { - - final int mOptimisticLockId; - final PackageVersions mPackageVersions; - - CheckToken(int optimisticLockId, PackageVersions packageVersions) { - this.mOptimisticLockId = optimisticLockId; - - if (packageVersions == null) { - throw new NullPointerException("packageVersions == null"); - } - this.mPackageVersions = packageVersions; - } - - byte[] toByteArray() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(12 /* (3 * sizeof(int)) */); - try (DataOutputStream dos = new DataOutputStream(baos)) { - dos.writeInt(mOptimisticLockId); - dos.writeLong(mPackageVersions.mUpdateAppVersion); - dos.writeLong(mPackageVersions.mDataAppVersion); - } catch (IOException e) { - throw new RuntimeException("Unable to write into a ByteArrayOutputStream", e); - } - return baos.toByteArray(); - } - - static CheckToken fromByteArray(byte[] tokenBytes) throws IOException { - ByteArrayInputStream bais = new ByteArrayInputStream(tokenBytes); - try (DataInputStream dis = new DataInputStream(bais)) { - int versionId = dis.readInt(); - long updateAppVersion = dis.readLong(); - long dataAppVersion = dis.readLong(); - return new CheckToken(versionId, new PackageVersions(updateAppVersion, dataAppVersion)); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - CheckToken checkToken = (CheckToken) o; - - if (mOptimisticLockId != checkToken.mOptimisticLockId) { - return false; - } - return mPackageVersions.equals(checkToken.mPackageVersions); - } - - @Override - public int hashCode() { - int result = mOptimisticLockId; - result = 31 * result + mPackageVersions.hashCode(); - return result; - } - - @Override - public String toString() { - return "Token{" + - "mOptimisticLockId=" + mOptimisticLockId + - ", mPackageVersions=" + mPackageVersions + - '}'; - } -} diff --git a/services/core/java/com/android/server/timezone/ConfigHelper.java b/services/core/java/com/android/server/timezone/ConfigHelper.java deleted file mode 100644 index f9984fa1af5e..000000000000 --- a/services/core/java/com/android/server/timezone/ConfigHelper.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 com.android.server.timezone; - -/** - * An easy-to-mock interface around device config for use by {@link PackageTracker}; it is not - * possible to test various states with the real one because config is fixed in the system image. - */ -interface ConfigHelper { - - boolean isTrackingEnabled(); - - String getUpdateAppPackageName(); - - String getDataAppPackageName(); - - int getCheckTimeAllowedMillis(); - - int getFailedCheckRetryCount(); -} diff --git a/services/core/java/com/android/server/timezone/PackageManagerHelper.java b/services/core/java/com/android/server/timezone/PackageManagerHelper.java deleted file mode 100644 index f6e35e8fdd56..000000000000 --- a/services/core/java/com/android/server/timezone/PackageManagerHelper.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import android.content.Intent; -import android.content.pm.PackageManager; - -/** - * An easy-to-mock facade around PackageManager for use by {@link PackageTracker}; it is not - * possible to test various cases with the real one because of the need to simulate package versions - * and manifest configurations. - */ -interface PackageManagerHelper { - - long getInstalledPackageVersion(String packageName) - throws PackageManager.NameNotFoundException; - - boolean isPrivilegedApp(String packageName) throws PackageManager.NameNotFoundException; - - boolean usesPermission(String packageName, String requiredPermissionName) - throws PackageManager.NameNotFoundException; - - boolean contentProviderRegistered(String authority, String requiredPackageName); - - boolean receiverRegistered(Intent intent, String requiredPermissionName) - throws PackageManager.NameNotFoundException; -} diff --git a/services/core/java/com/android/server/timezone/PackageStatus.java b/services/core/java/com/android/server/timezone/PackageStatus.java deleted file mode 100644 index 637909615b1b..000000000000 --- a/services/core/java/com/android/server/timezone/PackageStatus.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import android.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Information about the status of the time zone update / data packages that are persisted by the - * Android system. - */ -final class PackageStatus { - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ CHECK_STARTED, CHECK_COMPLETED_SUCCESS, CHECK_COMPLETED_FAILURE }) - @interface CheckStatus {} - - /** A time zone update check has been started but not yet completed. */ - static final int CHECK_STARTED = 1; - /** A time zone update check has been completed and succeeded. */ - static final int CHECK_COMPLETED_SUCCESS = 2; - /** A time zone update check has been completed and failed. */ - static final int CHECK_COMPLETED_FAILURE = 3; - - @CheckStatus - final int mCheckStatus; - - // Non-null - final PackageVersions mVersions; - - PackageStatus(@CheckStatus int checkStatus, PackageVersions versions) { - this.mCheckStatus = checkStatus; - if (checkStatus < 1 || checkStatus > 3) { - throw new IllegalArgumentException("Unknown checkStatus " + checkStatus); - } - if (versions == null) { - throw new NullPointerException("versions == null"); - } - this.mVersions = versions; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - PackageStatus that = (PackageStatus) o; - - if (mCheckStatus != that.mCheckStatus) { - return false; - } - return mVersions.equals(that.mVersions); - } - - @Override - public int hashCode() { - int result = mCheckStatus; - result = 31 * result + mVersions.hashCode(); - return result; - } - - @Override - public String toString() { - return "PackageStatus{" + - "mCheckStatus=" + mCheckStatus + - ", mVersions=" + mVersions + - '}'; - } -} diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java deleted file mode 100644 index fd0df8d090ec..000000000000 --- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java +++ /dev/null @@ -1,389 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.FastXmlSerializer; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import android.util.AtomicFile; -import android.util.Slog; -import android.util.TypedXmlPullParser; -import android.util.TypedXmlSerializer; -import android.util.Xml; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.text.ParseException; -import java.io.PrintWriter; - -import static com.android.server.timezone.PackageStatus.CHECK_COMPLETED_FAILURE; -import static com.android.server.timezone.PackageStatus.CHECK_COMPLETED_SUCCESS; -import static com.android.server.timezone.PackageStatus.CHECK_STARTED; -import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; -import static org.xmlpull.v1.XmlPullParser.START_TAG; - -/** - * Storage logic for accessing/mutating the Android system's persistent state related to time zone - * update checking. There is expected to be a single instance. All non-private methods are thread - * safe. - */ -final class PackageStatusStorage { - - private static final String LOG_TAG = "timezone.PackageStatusStorage"; - - private static final String TAG_PACKAGE_STATUS = "PackageStatus"; - - /** - * Attribute that stores a monotonically increasing lock ID, used to detect concurrent update - * issues without on-line locks. Incremented on every write. - */ - private static final String ATTRIBUTE_OPTIMISTIC_LOCK_ID = "optimisticLockId"; - - /** - * Attribute that stores the current "check status" of the time zone update application - * packages. - */ - private static final String ATTRIBUTE_CHECK_STATUS = "checkStatus"; - - /** - * Attribute that stores the version of the time zone rules update application being checked - * / last checked. - */ - private static final String ATTRIBUTE_UPDATE_APP_VERSION = "updateAppPackageVersion"; - - /** - * Attribute that stores the version of the time zone rules data application being checked - * / last checked. - */ - private static final String ATTRIBUTE_DATA_APP_VERSION = "dataAppPackageVersion"; - - private static final long UNKNOWN_PACKAGE_VERSION = -1; - - private final AtomicFile mPackageStatusFile; - - PackageStatusStorage(File storageDir) { - mPackageStatusFile = new AtomicFile(new File(storageDir, "package-status.xml"), "timezone-status"); - } - - /** - * Initialize any storage, as needed. - * - * @throws IOException if the storage could not be initialized - */ - void initialize() throws IOException { - if (!mPackageStatusFile.getBaseFile().exists()) { - insertInitialPackageStatus(); - } - } - - void deleteFileForTests() { - synchronized(this) { - mPackageStatusFile.delete(); - } - } - - /** - * Obtain the current check status of the application packages. Returns {@code null} the first - * time it is called, or after {@link #resetCheckState()}. - */ - PackageStatus getPackageStatus() { - synchronized (this) { - try { - return getPackageStatusLocked(); - } catch (ParseException e) { - // This means that data exists in the file but it was bad. - Slog.e(LOG_TAG, "Package status invalid, resetting and retrying", e); - - // Reset the storage so it is in a good state again. - recoverFromBadData(e); - try { - return getPackageStatusLocked(); - } catch (ParseException e2) { - throw new IllegalStateException("Recovery from bad file failed", e2); - } - } - } - } - - @GuardedBy("this") - private PackageStatus getPackageStatusLocked() throws ParseException { - try (FileInputStream fis = mPackageStatusFile.openRead()) { - TypedXmlPullParser parser = parseToPackageStatusTag(fis); - Integer checkStatus = getNullableIntAttribute(parser, ATTRIBUTE_CHECK_STATUS); - if (checkStatus == null) { - return null; - } - int updateAppVersion = getIntAttribute(parser, ATTRIBUTE_UPDATE_APP_VERSION); - int dataAppVersion = getIntAttribute(parser, ATTRIBUTE_DATA_APP_VERSION); - return new PackageStatus(checkStatus, - new PackageVersions(updateAppVersion, dataAppVersion)); - } catch (IOException e) { - ParseException e2 = new ParseException("Error reading package status", 0); - e2.initCause(e); - throw e2; - } - } - - @GuardedBy("this") - private int recoverFromBadData(Exception cause) { - mPackageStatusFile.delete(); - try { - return insertInitialPackageStatus(); - } catch (IOException e) { - IllegalStateException fatal = new IllegalStateException(e); - fatal.addSuppressed(cause); - throw fatal; - } - } - - /** Insert the initial data, returning the optimistic lock ID */ - private int insertInitialPackageStatus() throws IOException { - // Doesn't matter what it is, but we avoid the obvious starting value each time the data - // is reset to ensure that old tokens are unlikely to work. - final int initialOptimisticLockId = (int) System.currentTimeMillis(); - - writePackageStatusLocked(null /* status */, initialOptimisticLockId, - null /* packageVersions */); - return initialOptimisticLockId; - } - - /** - * Generate a new {@link CheckToken} that can be passed to the time zone rules update - * application. - */ - CheckToken generateCheckToken(PackageVersions currentInstalledVersions) { - if (currentInstalledVersions == null) { - throw new NullPointerException("currentInstalledVersions == null"); - } - - synchronized (this) { - int optimisticLockId; - try { - optimisticLockId = getCurrentOptimisticLockId(); - } catch (ParseException e) { - Slog.w(LOG_TAG, "Unable to find optimistic lock ID from package status"); - - // Recover. - optimisticLockId = recoverFromBadData(e); - } - - int newOptimisticLockId = optimisticLockId + 1; - try { - boolean statusUpdated = writePackageStatusWithOptimisticLockCheck( - optimisticLockId, newOptimisticLockId, CHECK_STARTED, - currentInstalledVersions); - if (!statusUpdated) { - throw new IllegalStateException("Unable to update status to CHECK_STARTED." - + " synchronization failure?"); - } - return new CheckToken(newOptimisticLockId, currentInstalledVersions); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - } - - /** - * Reset the current device state to "unknown". - */ - void resetCheckState() { - synchronized(this) { - int optimisticLockId; - try { - optimisticLockId = getCurrentOptimisticLockId(); - } catch (ParseException e) { - Slog.w(LOG_TAG, "resetCheckState: Unable to find optimistic lock ID from package" - + " status"); - // Attempt to recover the storage state. - optimisticLockId = recoverFromBadData(e); - } - - int newOptimisticLockId = optimisticLockId + 1; - try { - if (!writePackageStatusWithOptimisticLockCheck(optimisticLockId, - newOptimisticLockId, null /* status */, null /* packageVersions */)) { - throw new IllegalStateException("resetCheckState: Unable to reset package" - + " status, newOptimisticLockId=" + newOptimisticLockId); - } - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - } - - /** - * Update the current device state if possible. Returns true if the update was successful. - * {@code false} indicates the storage has been changed since the {@link CheckToken} was - * generated and the update was discarded. - */ - boolean markChecked(CheckToken checkToken, boolean succeeded) { - synchronized (this) { - int optimisticLockId = checkToken.mOptimisticLockId; - int newOptimisticLockId = optimisticLockId + 1; - int status = succeeded ? CHECK_COMPLETED_SUCCESS : CHECK_COMPLETED_FAILURE; - try { - return writePackageStatusWithOptimisticLockCheck(optimisticLockId, - newOptimisticLockId, status, checkToken.mPackageVersions); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - } - - @GuardedBy("this") - private int getCurrentOptimisticLockId() throws ParseException { - try (FileInputStream fis = mPackageStatusFile.openRead()) { - TypedXmlPullParser parser = parseToPackageStatusTag(fis); - return getIntAttribute(parser, ATTRIBUTE_OPTIMISTIC_LOCK_ID); - } catch (IOException e) { - ParseException e2 = new ParseException("Unable to read file", 0); - e2.initCause(e); - throw e2; - } - } - - /** Returns a parser or throws ParseException, never returns null. */ - private static TypedXmlPullParser parseToPackageStatusTag(FileInputStream fis) - throws ParseException { - try { - TypedXmlPullParser parser = Xml.resolvePullParser(fis); - int type; - while ((type = parser.next()) != END_DOCUMENT) { - final String tag = parser.getName(); - if (type == START_TAG && TAG_PACKAGE_STATUS.equals(tag)) { - return parser; - } - } - throw new ParseException("Unable to find " + TAG_PACKAGE_STATUS + " tag", 0); - } catch (XmlPullParserException e) { - throw new IllegalStateException("Unable to configure parser", e); - } catch (IOException e) { - ParseException e2 = new ParseException("Error reading XML", 0); - e.initCause(e); - throw e2; - } - } - - @GuardedBy("this") - private boolean writePackageStatusWithOptimisticLockCheck(int optimisticLockId, - int newOptimisticLockId, Integer status, PackageVersions packageVersions) - throws IOException { - - int currentOptimisticLockId; - try { - currentOptimisticLockId = getCurrentOptimisticLockId(); - if (currentOptimisticLockId != optimisticLockId) { - return false; - } - } catch (ParseException e) { - recoverFromBadData(e); - return false; - } - - writePackageStatusLocked(status, newOptimisticLockId, packageVersions); - return true; - } - - @GuardedBy("this") - private void writePackageStatusLocked(Integer status, int optimisticLockId, - PackageVersions packageVersions) throws IOException { - if ((status == null) != (packageVersions == null)) { - throw new IllegalArgumentException( - "Provide both status and packageVersions, or neither."); - } - - FileOutputStream fos = null; - try { - fos = mPackageStatusFile.startWrite(); - TypedXmlSerializer serializer = Xml.resolveSerializer(fos); - serializer.startDocument(null /* encoding */, true /* standalone */); - final String namespace = null; - serializer.startTag(namespace, TAG_PACKAGE_STATUS); - String statusAttributeValue = status == null ? "" : Integer.toString(status); - serializer.attribute(namespace, ATTRIBUTE_CHECK_STATUS, statusAttributeValue); - serializer.attribute(namespace, ATTRIBUTE_OPTIMISTIC_LOCK_ID, - Integer.toString(optimisticLockId)); - long updateAppVersion = status == null - ? UNKNOWN_PACKAGE_VERSION : packageVersions.mUpdateAppVersion; - serializer.attribute(namespace, ATTRIBUTE_UPDATE_APP_VERSION, - Long.toString(updateAppVersion)); - long dataAppVersion = status == null - ? UNKNOWN_PACKAGE_VERSION : packageVersions.mDataAppVersion; - serializer.attribute(namespace, ATTRIBUTE_DATA_APP_VERSION, - Long.toString(dataAppVersion)); - serializer.endTag(namespace, TAG_PACKAGE_STATUS); - serializer.endDocument(); - serializer.flush(); - mPackageStatusFile.finishWrite(fos); - } catch (IOException e) { - if (fos != null) { - mPackageStatusFile.failWrite(fos); - } - throw e; - } - - } - - /** Only used during tests to force a known table state. */ - public void forceCheckStateForTests(int checkStatus, PackageVersions packageVersions) - throws IOException { - synchronized (this) { - try { - final int initialOptimisticLockId = (int) System.currentTimeMillis(); - writePackageStatusLocked(checkStatus, initialOptimisticLockId, packageVersions); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - } - - private static Integer getNullableIntAttribute(TypedXmlPullParser parser, String attributeName) - throws ParseException { - String attributeValue = parser.getAttributeValue(null, attributeName); - try { - if (attributeValue == null) { - throw new ParseException("Attribute " + attributeName + " missing", 0); - } else if (attributeValue.isEmpty()) { - return null; - } - return Integer.parseInt(attributeValue); - } catch (NumberFormatException e) { - throw new ParseException( - "Bad integer for attributeName=" + attributeName + ": " + attributeValue, 0); - } - } - - private static int getIntAttribute(TypedXmlPullParser parser, String attributeName) - throws ParseException { - Integer value = getNullableIntAttribute(parser, attributeName); - if (value == null) { - throw new ParseException("Missing attribute " + attributeName, 0); - } - return value; - } - - public void dump(PrintWriter printWriter) { - printWriter.println("Package status: " + getPackageStatus()); - } -} diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java deleted file mode 100644 index 8f4cada0d0ba..000000000000 --- a/services/core/java/com/android/server/timezone/PackageTracker.java +++ /dev/null @@ -1,545 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import com.android.internal.annotations.VisibleForTesting; - -import android.app.timezone.RulesUpdaterContract; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Environment; -import android.os.FileUtils; -import android.os.SystemClock; -import android.provider.TimeZoneRulesDataContract; -import android.util.Slog; - -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.time.Clock; - -/** - * Monitors the installed applications associated with time zone updates. If the app packages are - * updated it indicates there <em>might</em> be a time zone rules update to apply so a targeted - * broadcast intent is used to trigger the time zone updater app. - * - * <p>The "update triggering" behavior of this component can be disabled via device configuration. - * - * <p>The package tracker listens for package updates of the time zone "updater app" and "data app". - * It also listens for "reliability" triggers. Reliability triggers are there to ensure that the - * package tracker handles failures reliably and are "idle maintenance" events or something similar. - * Reliability triggers can cause a time zone update check to take place if the current state is - * unclear. For example, it can be unclear after boot or after a failure. If there are repeated - * failures reliability updates are halted until the next boot. - * - * <p>This component keeps persistent track of the most recent app packages checked to avoid - * unnecessary expense from broadcasting intents (which will cause other app processes to spawn). - * The current status is also stored to detect whether the most recently-generated check is - * complete successfully. For example, if the device was interrupted while doing a check and never - * acknowledged a check then a check will be retried the next time a "reliability trigger" event - * happens. - */ -// Also made non-final so it can be mocked. -@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) -public class PackageTracker { - private static final String TAG = "timezone.PackageTracker"; - - private final PackageManagerHelper mPackageManagerHelper; - private final PackageTrackerIntentHelper mIntentHelper; - private final ConfigHelper mConfigHelper; - private final PackageStatusStorage mPackageStatusStorage; - private final Clock mElapsedRealtimeClock; - - // False if tracking is disabled. - private boolean mTrackingEnabled; - - // These fields may be null if package tracking is disabled. - private String mUpdateAppPackageName; - private String mDataAppPackageName; - - // The time a triggered check is allowed to take before it is considered overdue. - private int mCheckTimeAllowedMillis; - // The number of failed checks in a row before reliability checks should stop happening. - private long mFailedCheckRetryCount; - - /* - * The minimum delay between a successive reliability triggers / other operations. Should to be - * larger than mCheckTimeAllowedMillis to avoid reliability triggers happening during package - * update checks. - */ - private int mDelayBeforeReliabilityCheckMillis; - - // Reliability check state: If a check was triggered but not acknowledged within - // mCheckTimeAllowedMillis then another one can be triggered. - private Long mLastTriggerTimestamp = null; - - // Reliability check state: Whether any checks have been triggered at all. - private boolean mCheckTriggered; - - // Reliability check state: A count of how many failures have occurred consecutively. - private int mCheckFailureCount; - - /** Creates the {@link PackageTracker} for normal use. */ - static PackageTracker create(Context context) { - Clock elapsedRealtimeClock = SystemClock.elapsedRealtimeClock(); - PackageTrackerHelperImpl helperImpl = new PackageTrackerHelperImpl(context); - File storageDir = FileUtils.createDir(Environment.getDataSystemDirectory(), "timezone"); - return new PackageTracker( - elapsedRealtimeClock /* elapsedRealtimeClock */, - helperImpl /* configHelper */, - helperImpl /* packageManagerHelper */, - new PackageStatusStorage(storageDir), - new PackageTrackerIntentHelperImpl(context)); - } - - // A constructor that can be used by tests to supply mocked / faked dependencies. - PackageTracker(Clock elapsedRealtimeClock, ConfigHelper configHelper, - PackageManagerHelper packageManagerHelper, PackageStatusStorage packageStatusStorage, - PackageTrackerIntentHelper intentHelper) { - mElapsedRealtimeClock = elapsedRealtimeClock; - mConfigHelper = configHelper; - mPackageManagerHelper = packageManagerHelper; - mPackageStatusStorage = packageStatusStorage; - mIntentHelper = intentHelper; - } - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected synchronized boolean start() { - mTrackingEnabled = mConfigHelper.isTrackingEnabled(); - if (!mTrackingEnabled) { - Slog.i(TAG, "Time zone updater / data package tracking explicitly disabled."); - return false; - } - - mUpdateAppPackageName = mConfigHelper.getUpdateAppPackageName(); - mDataAppPackageName = mConfigHelper.getDataAppPackageName(); - mCheckTimeAllowedMillis = mConfigHelper.getCheckTimeAllowedMillis(); - mFailedCheckRetryCount = mConfigHelper.getFailedCheckRetryCount(); - mDelayBeforeReliabilityCheckMillis = mCheckTimeAllowedMillis + (60 * 1000); - - // Validate the device configuration including the application packages. - // The manifest entries in the apps themselves are not validated until use as they can - // change and we don't want to prevent the system server starting due to a bad application. - throwIfDeviceSettingsOrAppsAreBad(); - - // Explicitly start in a reliability state where reliability triggering will do something. - mCheckTriggered = false; - mCheckFailureCount = 0; - - // Initialize the storage, as needed. - try { - mPackageStatusStorage.initialize(); - } catch (IOException e) { - Slog.w(TAG, "PackageTracker storage could not be initialized.", e); - return false; - } - - // Initialize the intent helper. - mIntentHelper.initialize(mUpdateAppPackageName, mDataAppPackageName, this); - - // Schedule a reliability trigger so we will have at least one after boot. This will allow - // us to catch if a package updated wasn't handled to completion. There's no hurry: it's ok - // to delay for a while before doing this even if idle. - mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); - - Slog.i(TAG, "Time zone updater / data package tracking enabled"); - return true; - } - - /** - * Performs checks that confirm the system image has correctly configured package - * tracking configuration. Only called if package tracking is enabled. Throws an exception if - * the device is configured badly which will prevent the device booting. - */ - private void throwIfDeviceSettingsOrAppsAreBad() { - // None of the checks below can be based on application manifest settings, otherwise a bad - // update could leave the device in an unbootable state. See validateDataAppManifest() and - // validateUpdaterAppManifest() for softer errors. - - throwRuntimeExceptionIfNullOrEmpty( - mUpdateAppPackageName, "Update app package name missing."); - throwRuntimeExceptionIfNullOrEmpty(mDataAppPackageName, "Data app package name missing."); - if (mFailedCheckRetryCount < 1) { - throw logAndThrowRuntimeException("mFailedRetryCount=" + mFailedCheckRetryCount, null); - } - if (mCheckTimeAllowedMillis < 1000) { - throw logAndThrowRuntimeException( - "mCheckTimeAllowedMillis=" + mCheckTimeAllowedMillis, null); - } - - // Validate the updater application package. - try { - if (!mPackageManagerHelper.isPrivilegedApp(mUpdateAppPackageName)) { - throw logAndThrowRuntimeException( - "Update app " + mUpdateAppPackageName + " must be a priv-app.", null); - } - } catch (PackageManager.NameNotFoundException e) { - throw logAndThrowRuntimeException("Could not determine update app package details for " - + mUpdateAppPackageName, e); - } - Slog.d(TAG, "Update app " + mUpdateAppPackageName + " is valid."); - - // Validate the data application package. - try { - if (!mPackageManagerHelper.isPrivilegedApp(mDataAppPackageName)) { - throw logAndThrowRuntimeException( - "Data app " + mDataAppPackageName + " must be a priv-app.", null); - } - } catch (PackageManager.NameNotFoundException e) { - throw logAndThrowRuntimeException("Could not determine data app package details for " - + mDataAppPackageName, e); - } - Slog.d(TAG, "Data app " + mDataAppPackageName + " is valid."); - } - - /** - * Inspects the current in-memory state, installed packages and storage state to determine if an - * update check is needed and then trigger if it is. - * - * @param packageChanged true if this method was called because a known packaged definitely - * changed, false if the cause is a reliability trigger - */ - public synchronized void triggerUpdateIfNeeded(boolean packageChanged) { - if (!mTrackingEnabled) { - throw new IllegalStateException("Unexpected call. Tracking is disabled."); - } - - // Validate the applications' current manifest entries: make sure they are configured as - // they should be. These are not fatal and just means that no update is triggered: we don't - // want to take down the system server if an OEM or Google have pushed a bad update to - // an application. - boolean updaterAppManifestValid = validateUpdaterAppManifest(); - boolean dataAppManifestValid = validateDataAppManifest(); - if (!updaterAppManifestValid || !dataAppManifestValid) { - Slog.e(TAG, "No update triggered due to invalid application manifest entries." - + " updaterApp=" + updaterAppManifestValid - + ", dataApp=" + dataAppManifestValid); - - // There's no point in doing any reliability triggers if the current packages are bad. - mIntentHelper.unscheduleReliabilityTrigger(); - return; - } - - if (!packageChanged) { - // This call was made because the device is doing a "reliability" check. - // 4 possible cases: - // 1) No check has previously triggered since restart. We want to trigger in this case. - // 2) A check has previously triggered and it is in progress. We want to trigger if - // the response is overdue. - // 3) A check has previously triggered and it failed. We want to trigger, but only if - // we're not in a persistent failure state. - // 4) A check has previously triggered and it succeeded. - // We don't want to trigger, and want to stop future triggers. - - if (!mCheckTriggered) { - // Case 1. - Slog.d(TAG, "triggerUpdateIfNeeded: First reliability trigger."); - } else if (isCheckInProgress()) { - // Case 2. - if (!isCheckResponseOverdue()) { - // A check is in progress but hasn't been given time to succeed. - Slog.d(TAG, - "triggerUpdateIfNeeded: checkComplete call is not yet overdue." - + " Not triggering."); - // Don't do any work now but we do schedule a future reliability trigger. - mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); - return; - } - } else if (mCheckFailureCount > mFailedCheckRetryCount) { - // Case 3. If the system is in some kind of persistent failure state we don't want - // to keep checking, so just stop. - Slog.i(TAG, "triggerUpdateIfNeeded: number of allowed consecutive check failures" - + " exceeded. Stopping reliability triggers until next reboot or package" - + " update."); - mIntentHelper.unscheduleReliabilityTrigger(); - return; - } else if (mCheckFailureCount == 0) { - // Case 4. - Slog.i(TAG, "triggerUpdateIfNeeded: No reliability check required. Last check was" - + " successful."); - mIntentHelper.unscheduleReliabilityTrigger(); - return; - } - } - - // Read the currently installed data / updater package versions. - PackageVersions currentInstalledVersions = lookupInstalledPackageVersions(); - if (currentInstalledVersions == null) { - // This should not happen if the device is configured in a valid way. - Slog.e(TAG, "triggerUpdateIfNeeded: currentInstalledVersions was null"); - mIntentHelper.unscheduleReliabilityTrigger(); - return; - } - - // Establish the current state using package manager and stored state. Determine if we have - // already successfully checked the installed versions. - PackageStatus packageStatus = mPackageStatusStorage.getPackageStatus(); - if (packageStatus == null) { - // This can imply corrupt, uninitialized storage state (e.g. first check ever on a - // device) or after some kind of reset. - Slog.i(TAG, "triggerUpdateIfNeeded: No package status data found. Data check needed."); - } else if (!packageStatus.mVersions.equals(currentInstalledVersions)) { - // The stored package version information differs from the installed version. - // Trigger the check in all cases. - Slog.i(TAG, "triggerUpdateIfNeeded: Stored package versions=" - + packageStatus.mVersions + ", do not match current package versions=" - + currentInstalledVersions + ". Triggering check."); - } else { - Slog.i(TAG, "triggerUpdateIfNeeded: Stored package versions match currently" - + " installed versions, currentInstalledVersions=" + currentInstalledVersions - + ", packageStatus.mCheckStatus=" + packageStatus.mCheckStatus); - if (packageStatus.mCheckStatus == PackageStatus.CHECK_COMPLETED_SUCCESS) { - // The last check succeeded and nothing has changed. Do nothing and disable - // reliability checks. - Slog.i(TAG, "triggerUpdateIfNeeded: Prior check succeeded. No need to trigger."); - mIntentHelper.unscheduleReliabilityTrigger(); - return; - } - } - - // Generate a token to send to the updater app. - CheckToken checkToken = - mPackageStatusStorage.generateCheckToken(currentInstalledVersions); - if (checkToken == null) { - Slog.w(TAG, "triggerUpdateIfNeeded: Unable to generate check token." - + " Not sending check request."); - // Trigger again later: perhaps we'll have better luck. - mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); - return; - } - - // Trigger the update check. - mIntentHelper.sendTriggerUpdateCheck(checkToken); - mCheckTriggered = true; - - // Update the reliability check state in case the update fails. - setCheckInProgress(); - - // Schedule a reliability trigger in case the update check doesn't succeed and there is no - // response at all. It will be cancelled if the check is successful in recordCheckResult. - mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); - } - - /** - * Used to record the result of a check. Can be called even if active package tracking is - * disabled. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected synchronized void recordCheckResult(CheckToken checkToken, boolean success) { - Slog.i(TAG, "recordOperationResult: checkToken=" + checkToken + " success=" + success); - - // If package tracking is disabled it means no record-keeping is required. However, we do - // want to clear out any stored state to make it clear that the current state is unknown and - // should tracking become enabled again (perhaps through an OTA) we'd need to perform an - // update check. - if (!mTrackingEnabled) { - // This means an updater has spontaneously modified time zone data without having been - // triggered. This can happen if the OEM is handling their own updates, but we don't - // need to do any tracking in this case. - - if (checkToken == null) { - // This is the expected case if tracking is disabled but an OEM is handling time - // zone installs using their own mechanism. - Slog.d(TAG, "recordCheckResult: Tracking is disabled and no token has been" - + " provided. Resetting tracking state."); - } else { - // This is unexpected. If tracking is disabled then no check token should have been - // generated by the package tracker. An updater should never create its own token. - // This could be a bug in the updater. - Slog.w(TAG, "recordCheckResult: Tracking is disabled and a token " + checkToken - + " has been unexpectedly provided. Resetting tracking state."); - } - mPackageStatusStorage.resetCheckState(); - return; - } - - if (checkToken == null) { - /* - * If the checkToken is null it suggests an install / uninstall / acknowledgement has - * occurred without a prior trigger (or the client didn't return the token it was given - * for some reason, perhaps a bug). - * - * This shouldn't happen under normal circumstances: - * - * If package tracking is enabled, we assume it is the package tracker responsible for - * triggering updates and a token should have been produced and returned. - * - * If the OEM is handling time zone updates case package tracking should be disabled. - * - * This could happen in tests. The device should recover back to a known state by - * itself rather than be left in an invalid state. - * - * We treat this as putting the device into an unknown state and make sure that - * reliability triggering is enabled so we should recover. - */ - Slog.i(TAG, "recordCheckResult: Unexpectedly missing checkToken, resetting" - + " storage state."); - mPackageStatusStorage.resetCheckState(); - - // Schedule a reliability trigger and reset the failure count so we know that the - // next reliability trigger will do something. - mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); - mCheckFailureCount = 0; - } else { - // This is the expected case when tracking is enabled: a check was triggered and it has - // completed. - boolean recordedCheckCompleteSuccessfully = - mPackageStatusStorage.markChecked(checkToken, success); - if (recordedCheckCompleteSuccessfully) { - // If we have recorded the result (whatever it was) we know there is no check in - // progress. - setCheckComplete(); - - if (success) { - // Since the check was successful, no reliability trigger is required until - // there is a package change. - mIntentHelper.unscheduleReliabilityTrigger(); - mCheckFailureCount = 0; - } else { - // Enable schedule a reliability trigger to check again in future. - mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); - mCheckFailureCount++; - } - } else { - // The failure to record the check means an optimistic lock failure and suggests - // that another check was triggered after the token was generated. - Slog.i(TAG, "recordCheckResult: could not update token=" + checkToken - + " with success=" + success + ". Optimistic lock failure"); - - // Schedule a reliability trigger to potentially try again in future. - mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); - mCheckFailureCount++; - } - } - } - - /** Access to consecutive failure counts for use in tests. */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - protected int getCheckFailureCountForTests() { - return mCheckFailureCount; - } - - private void setCheckInProgress() { - mLastTriggerTimestamp = mElapsedRealtimeClock.millis(); - } - - private void setCheckComplete() { - mLastTriggerTimestamp = null; - } - - private boolean isCheckInProgress() { - return mLastTriggerTimestamp != null; - } - - private boolean isCheckResponseOverdue() { - if (mLastTriggerTimestamp == null) { - return false; - } - // Risk of overflow, but highly unlikely given the implementation and not problematic. - return mElapsedRealtimeClock.millis() > mLastTriggerTimestamp + mCheckTimeAllowedMillis; - } - - private PackageVersions lookupInstalledPackageVersions() { - long updatePackageVersion; - long dataPackageVersion; - try { - updatePackageVersion = - mPackageManagerHelper.getInstalledPackageVersion(mUpdateAppPackageName); - dataPackageVersion = - mPackageManagerHelper.getInstalledPackageVersion(mDataAppPackageName); - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "lookupInstalledPackageVersions: Unable to resolve installed package" - + " versions", e); - return null; - } - return new PackageVersions(updatePackageVersion, dataPackageVersion); - } - - private boolean validateDataAppManifest() { - // We only want to talk to a provider that exposed by the known data app package - // so we look up the providers exposed by that app and check the well-known authority is - // there. This prevents the case where *even if* the data app doesn't expose the provider - // required, another app cannot expose one to replace it. - if (!mPackageManagerHelper.contentProviderRegistered( - TimeZoneRulesDataContract.AUTHORITY, mDataAppPackageName)) { - // Error! Found the package but it didn't expose the correct provider. - Slog.w(TAG, "validateDataAppManifest: Data app " + mDataAppPackageName - + " does not expose the required provider with authority=" - + TimeZoneRulesDataContract.AUTHORITY); - return false; - } - return true; - } - - private boolean validateUpdaterAppManifest() { - try { - // The updater app is expected to have the UPDATE_TIME_ZONE_RULES permission. - // The updater app is expected to have a receiver for the intent we are going to trigger - // and require the TRIGGER_TIME_ZONE_RULES_CHECK. - if (!mPackageManagerHelper.usesPermission( - mUpdateAppPackageName, - RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION)) { - Slog.w(TAG, "validateUpdaterAppManifest: Updater app " + mDataAppPackageName - + " does not use permission=" - + RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION); - return false; - } - if (!mPackageManagerHelper.receiverRegistered( - RulesUpdaterContract.createUpdaterIntent(mUpdateAppPackageName), - RulesUpdaterContract.TRIGGER_TIME_ZONE_RULES_CHECK_PERMISSION)) { - return false; - } - - return true; - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "validateUpdaterAppManifest: Updater app " + mDataAppPackageName - + " does not expose the required broadcast receiver.", e); - return false; - } - } - - private static void throwRuntimeExceptionIfNullOrEmpty(String value, String message) { - if (value == null || value.trim().isEmpty()) { - throw logAndThrowRuntimeException(message, null); - } - } - - private static RuntimeException logAndThrowRuntimeException(String message, Throwable cause) { - Slog.wtf(TAG, message, cause); - throw new RuntimeException(message, cause); - } - - public void dump(PrintWriter fout) { - fout.println("PackageTrackerState: " + toString()); - mPackageStatusStorage.dump(fout); - } - - @Override - public String toString() { - return "PackageTracker{" + - "mTrackingEnabled=" + mTrackingEnabled + - ", mUpdateAppPackageName='" + mUpdateAppPackageName + '\'' + - ", mDataAppPackageName='" + mDataAppPackageName + '\'' + - ", mCheckTimeAllowedMillis=" + mCheckTimeAllowedMillis + - ", mDelayBeforeReliabilityCheckMillis=" + mDelayBeforeReliabilityCheckMillis + - ", mFailedCheckRetryCount=" + mFailedCheckRetryCount + - ", mLastTriggerTimestamp=" + mLastTriggerTimestamp + - ", mCheckTriggered=" + mCheckTriggered + - ", mCheckFailureCount=" + mCheckFailureCount + - '}'; - } -} diff --git a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java deleted file mode 100644 index 5f90be134b8d..000000000000 --- a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import com.android.internal.R; - -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.ProviderInfo; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.os.UserHandle; -import android.util.Slog; - -import java.util.List; - -/** - * A single class that implements multiple helper interfaces for use by {@link PackageTracker}. - */ -final class PackageTrackerHelperImpl implements ConfigHelper, PackageManagerHelper { - - private static final String TAG = "PackageTrackerHelperImpl"; - - private final Context mContext; - private final PackageManager mPackageManager; - - PackageTrackerHelperImpl(Context context) { - mContext = context; - mPackageManager = context.getPackageManager(); - } - - @Override - public boolean isTrackingEnabled() { - return mContext.getResources().getBoolean(R.bool.config_timeZoneRulesUpdateTrackingEnabled); - } - - @Override - public String getUpdateAppPackageName() { - return mContext.getResources().getString(R.string.config_timeZoneRulesUpdaterPackage); - } - - @Override - public String getDataAppPackageName() { - Resources resources = mContext.getResources(); - return resources.getString(R.string.config_timeZoneRulesDataPackage); - } - - @Override - public int getCheckTimeAllowedMillis() { - return mContext.getResources().getInteger( - R.integer.config_timeZoneRulesCheckTimeMillisAllowed); - } - - @Override - public int getFailedCheckRetryCount() { - return mContext.getResources().getInteger(R.integer.config_timeZoneRulesCheckRetryCount); - } - - @Override - public long getInstalledPackageVersion(String packageName) - throws PackageManager.NameNotFoundException { - int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; - PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags); - return packageInfo.getLongVersionCode(); - } - - @Override - public boolean isPrivilegedApp(String packageName) throws PackageManager.NameNotFoundException { - int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; - PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags); - return packageInfo.applicationInfo.isPrivilegedApp(); - } - - @Override - public boolean usesPermission(String packageName, String requiredPermissionName) - throws PackageManager.NameNotFoundException { - int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS - | PackageManager.GET_PERMISSIONS; - PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags); - if (packageInfo.requestedPermissions == null) { - return false; - } - for (String requestedPermission : packageInfo.requestedPermissions) { - if (requiredPermissionName.equals(requestedPermission)) { - return true; - } - } - return false; - } - - @Override - public boolean contentProviderRegistered(String authority, String requiredPackageName) { - int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; - ProviderInfo providerInfo = mPackageManager.resolveContentProviderAsUser( - authority, flags, UserHandle.SYSTEM.getIdentifier()); - if (providerInfo == null) { - Slog.i(TAG, "contentProviderRegistered: No content provider registered with authority=" - + authority); - return false; - } - boolean packageMatches = - requiredPackageName.equals(providerInfo.applicationInfo.packageName); - if (!packageMatches) { - Slog.i(TAG, "contentProviderRegistered: App with packageName=" + requiredPackageName - + " does not expose the a content provider with authority=" + authority); - return false; - } - return true; - } - - @Override - public boolean receiverRegistered(Intent intent, String requiredPermissionName) - throws PackageManager.NameNotFoundException { - - int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; - List<ResolveInfo> resolveInfo = mPackageManager.queryBroadcastReceiversAsUser( - intent, flags, UserHandle.SYSTEM); - if (resolveInfo.size() != 1) { - Slog.i(TAG, "receiverRegistered: Zero or multiple broadcast receiver registered for" - + " intent=" + intent + ", found=" + resolveInfo); - return false; - } - - ResolveInfo matched = resolveInfo.get(0); - boolean requiresPermission = requiredPermissionName.equals(matched.activityInfo.permission); - if (!requiresPermission) { - Slog.i(TAG, "receiverRegistered: Broadcast receiver registered for intent=" - + intent + " must require permission " + requiredPermissionName); - } - return requiresPermission; - } -} diff --git a/services/core/java/com/android/server/timezone/PackageTrackerIntentHelper.java b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelper.java deleted file mode 100644 index 3753ece03bb0..000000000000 --- a/services/core/java/com/android/server/timezone/PackageTrackerIntentHelper.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 com.android.server.timezone; - -/** - * An easy-to-mock interface around intent sending / receiving for use by {@link PackageTracker}; - * it is not possible to test various cases with the real one because of the need to simulate - * receiving and broadcasting intents. - */ -interface PackageTrackerIntentHelper { - - void initialize(String updateAppPackageName, String dataAppPackageName, - PackageTracker packageTracker); - - void sendTriggerUpdateCheck(CheckToken checkToken); - - /** - * Schedule a "reliability trigger" after at least minimumDelayMillis, replacing any existing - * scheduled one. A reliability trigger ensures that the {@link PackageTracker} can pick up - * reliably if a previous update check did not complete for some reason. It can happen when - * the device is idle. The trigger is expected to call - * {@link PackageTracker#triggerUpdateIfNeeded(boolean)} with a {@code false} value. - */ - void scheduleReliabilityTrigger(long minimumDelayMillis); - - /** - * Make sure there is no reliability trigger scheduled. No-op if there wasn't one. - */ - void unscheduleReliabilityTrigger(); -} diff --git a/services/core/java/com/android/server/timezone/PackageTrackerIntentHelperImpl.java b/services/core/java/com/android/server/timezone/PackageTrackerIntentHelperImpl.java deleted file mode 100644 index 4110d881f3fb..000000000000 --- a/services/core/java/com/android/server/timezone/PackageTrackerIntentHelperImpl.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import com.android.server.EventLogTags; - -import android.app.timezone.RulesUpdaterContract; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.PatternMatcher; -import android.os.UserHandle; -import android.util.Slog; - -/** - * The bona fide implementation of {@link PackageTrackerIntentHelper}. - */ -final class PackageTrackerIntentHelperImpl implements PackageTrackerIntentHelper { - - private final static String TAG = "timezone.PackageTrackerIntentHelperImpl"; - - private final Context mContext; - private String mUpdaterAppPackageName; - - PackageTrackerIntentHelperImpl(Context context) { - mContext = context; - } - - @Override - public void initialize(String updaterAppPackageName, String dataAppPackageName, - PackageTracker packageTracker) { - mUpdaterAppPackageName = updaterAppPackageName; - - // Register for events of interest. - - // The intent filter that triggers when package update events happen that indicate there may - // be work to do. - IntentFilter packageIntentFilter = new IntentFilter(); - - packageIntentFilter.addDataScheme("package"); - packageIntentFilter.addDataSchemeSpecificPart( - updaterAppPackageName, PatternMatcher.PATTERN_LITERAL); - packageIntentFilter.addDataSchemeSpecificPart( - dataAppPackageName, PatternMatcher.PATTERN_LITERAL); - - // ACTION_PACKAGE_ADDED is fired when a package is upgraded or downgraded (in addition to - // ACTION_PACKAGE_REMOVED and ACTION_PACKAGE_REPLACED). A system/priv-app can never be - // removed entirely so we do not need to trigger on ACTION_PACKAGE_REMOVED or - // ACTION_PACKAGE_FULLY_REMOVED. - packageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - - // ACTION_PACKAGE_CHANGED is used when a package is disabled / re-enabled. It is not - // strictly necessary to trigger on this but it won't hurt anything and may catch some cases - // where a package has changed while disabled. - // Note: ACTION_PACKAGE_CHANGED is not fired when updating a suspended app, but - // ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and ACTION_PACKAGE_REPLACED are (and the app - // is left in an unsuspended state after this). - packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); - - // We do not register for ACTION_PACKAGE_RESTARTED because it doesn't imply an update. - // We do not register for ACTION_PACKAGE_DATA_CLEARED because the updater / data apps are - // not expected to need local data. - - Receiver packageUpdateReceiver = new Receiver(packageTracker); - mContext.registerReceiverAsUser( - packageUpdateReceiver, UserHandle.SYSTEM, packageIntentFilter, - null /* broadcastPermission */, null /* default handler */); - } - - /** Sends an intent to trigger an update check. */ - @Override - public void sendTriggerUpdateCheck(CheckToken checkToken) { - RulesUpdaterContract.sendBroadcast( - mContext, mUpdaterAppPackageName, checkToken.toByteArray()); - EventLogTags.writeTimezoneTriggerCheck(checkToken.toString()); - } - - @Override - public synchronized void scheduleReliabilityTrigger(long minimumDelayMillis) { - TimeZoneUpdateIdler.schedule(mContext, minimumDelayMillis); - } - - @Override - public synchronized void unscheduleReliabilityTrigger() { - TimeZoneUpdateIdler.unschedule(mContext); - } - - private static class Receiver extends BroadcastReceiver { - private final PackageTracker mPackageTracker; - - private Receiver(PackageTracker packageTracker) { - mPackageTracker = packageTracker; - } - - @Override - public void onReceive(Context context, Intent intent) { - Slog.d(TAG, "Received intent: " + intent.toString()); - mPackageTracker.triggerUpdateIfNeeded(true /* packageChanged */); - } - } -} diff --git a/services/core/java/com/android/server/timezone/PackageVersions.java b/services/core/java/com/android/server/timezone/PackageVersions.java deleted file mode 100644 index 0084c1a2e5e5..000000000000 --- a/services/core/java/com/android/server/timezone/PackageVersions.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 com.android.server.timezone; - -/** - * Package version information about the time zone updater and time zone data application packages. - */ -final class PackageVersions { - - final long mUpdateAppVersion; - final long mDataAppVersion; - - PackageVersions(long updateAppVersion, long dataAppVersion) { - this.mUpdateAppVersion = updateAppVersion; - this.mDataAppVersion = dataAppVersion; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - PackageVersions that = (PackageVersions) o; - - if (mUpdateAppVersion != that.mUpdateAppVersion) { - return false; - } - return mDataAppVersion == that.mDataAppVersion; - } - - @Override - public int hashCode() { - int result = Long.hashCode(mUpdateAppVersion); - result = 31 * result + Long.hashCode(mDataAppVersion); - return result; - } - - @Override - public String toString() { - return "PackageVersions{" + - "mUpdateAppVersion=" + mUpdateAppVersion + - ", mDataAppVersion=" + mDataAppVersion + - '}'; - } -} diff --git a/services/core/java/com/android/server/timezone/PermissionHelper.java b/services/core/java/com/android/server/timezone/PermissionHelper.java deleted file mode 100644 index 2ec31e2f5dfc..000000000000 --- a/services/core/java/com/android/server/timezone/PermissionHelper.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import java.io.PrintWriter; - -/** - * An easy-to-mock interface around permission checks for use by {@link RulesManagerService}. - */ -public interface PermissionHelper { - - void enforceCallerHasPermission(String requiredPermission) throws SecurityException; - - boolean checkDumpPermission(String tag, PrintWriter printWriter); -} diff --git a/services/core/java/com/android/server/timezone/RulesManagerIntentHelper.java b/services/core/java/com/android/server/timezone/RulesManagerIntentHelper.java deleted file mode 100644 index bb317cf2f988..000000000000 --- a/services/core/java/com/android/server/timezone/RulesManagerIntentHelper.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 com.android.server.timezone; - -/** - * An easy-to-mock interface around intent sending / receiving for use by - * {@link RulesManagerService}; it is not possible to test various cases with the real one because - * of the need to simulate broadcasting intents. - */ -interface RulesManagerIntentHelper { - - /** - * Send a broadcast informing listeners that a time zone operation is staged. - */ - void sendTimeZoneOperationStaged(); - - /** - * Send a broadcast informing listeners that a time zone operation is no longer staged. - */ - void sendTimeZoneOperationUnstaged(); -} diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java deleted file mode 100644 index fd5c6e91a885..000000000000 --- a/services/core/java/com/android/server/timezone/RulesManagerService.java +++ /dev/null @@ -1,602 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED; -import static android.app.timezone.RulesState.DISTRO_STATUS_NONE; -import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN; -import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL; -import static android.app.timezone.RulesState.STAGED_OPERATION_NONE; -import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL; -import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN; - -import android.app.timezone.Callback; -import android.app.timezone.DistroFormatVersion; -import android.app.timezone.DistroRulesVersion; -import android.app.timezone.ICallback; -import android.app.timezone.IRulesManager; -import android.app.timezone.RulesManager; -import android.app.timezone.RulesState; -import android.content.Context; -import android.icu.util.TimeZone; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.util.Slog; - -import com.android.i18n.timezone.TimeZoneDataFiles; -import com.android.i18n.timezone.TimeZoneFinder; -import com.android.i18n.timezone.TzDataSetVersion; -import com.android.i18n.timezone.ZoneInfoDb; -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.EventLogTags; -import com.android.server.SystemService; -import com.android.timezone.distro.DistroException; -import com.android.timezone.distro.DistroVersion; -import com.android.timezone.distro.StagedDistroOperation; -import com.android.timezone.distro.TimeZoneDistro; -import com.android.timezone.distro.installer.TimeZoneDistroInstaller; - - -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.util.Arrays; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; - -public final class RulesManagerService extends IRulesManager.Stub { - - private static final String TAG = "timezone.RulesManagerService"; - - /** The distro format supported by this device. */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - static final DistroFormatVersion DISTRO_FORMAT_VERSION_SUPPORTED = - new DistroFormatVersion( - TzDataSetVersion.currentFormatMajorVersion(), - TzDataSetVersion.currentFormatMinorVersion()); - - public static class Lifecycle extends SystemService { - public Lifecycle(Context context) { - super(context); - } - - @Override - public void onStart() { - RulesManagerService service = RulesManagerService.create(getContext()); - service.start(); - - // Publish the binder service so it can be accessed from other (appropriately - // permissioned) processes. - publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, service); - - // Publish the service instance locally so we can use it directly from within the system - // server from TimeZoneUpdateIdler. - publishLocalService(RulesManagerService.class, service); - } - } - - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - static final String REQUIRED_UPDATER_PERMISSION = - android.Manifest.permission.UPDATE_TIME_ZONE_RULES; - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - static final String REQUIRED_QUERY_PERMISSION = - android.Manifest.permission.QUERY_TIME_ZONE_RULES; - - private final AtomicBoolean mOperationInProgress = new AtomicBoolean(false); - private final PermissionHelper mPermissionHelper; - private final PackageTracker mPackageTracker; - private final Executor mExecutor; - private final RulesManagerIntentHelper mIntentHelper; - private final TimeZoneDistroInstaller mInstaller; - - private static RulesManagerService create(Context context) { - RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context); - File baseVersionFile = new File(TimeZoneDataFiles.getTimeZoneModuleTzVersionFile()); - File tzDataDir = new File(TimeZoneDataFiles.getDataTimeZoneRootDir()); - return new RulesManagerService( - helper /* permissionHelper */, - helper /* executor */, - helper /* intentHelper */, - PackageTracker.create(context), - new TimeZoneDistroInstaller(TAG, baseVersionFile, tzDataDir)); - } - - // A constructor that can be used by tests to supply mocked / faked dependencies. - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - RulesManagerService(PermissionHelper permissionHelper, Executor executor, - RulesManagerIntentHelper intentHelper, PackageTracker packageTracker, - TimeZoneDistroInstaller timeZoneDistroInstaller) { - mPermissionHelper = permissionHelper; - mExecutor = executor; - mIntentHelper = intentHelper; - mPackageTracker = packageTracker; - mInstaller = timeZoneDistroInstaller; - } - - public void start() { - // Return value deliberately ignored: no action required on failure to start. - mPackageTracker.start(); - } - - @Override // Binder call - public RulesState getRulesState() { - mPermissionHelper.enforceCallerHasPermission(REQUIRED_QUERY_PERMISSION); - - return getRulesStateInternal(); - } - - /** Like {@link #getRulesState()} without the permission check. */ - private RulesState getRulesStateInternal() { - synchronized(this) { - TzDataSetVersion baseVersion; - try { - baseVersion = mInstaller.readBaseVersion(); - } catch (IOException e) { - Slog.w(TAG, "Failed to read base rules version", e); - return null; - } - - // Determine the installed distro state. This should be possible regardless of whether - // there's an operation in progress. - DistroVersion installedDistroVersion; - int distroStatus = DISTRO_STATUS_UNKNOWN; - DistroRulesVersion installedDistroRulesVersion = null; - try { - installedDistroVersion = mInstaller.getInstalledDistroVersion(); - if (installedDistroVersion == null) { - distroStatus = DISTRO_STATUS_NONE; - installedDistroRulesVersion = null; - } else { - distroStatus = DISTRO_STATUS_INSTALLED; - installedDistroRulesVersion = new DistroRulesVersion( - installedDistroVersion.rulesVersion, - installedDistroVersion.revision); - } - } catch (DistroException | IOException e) { - Slog.w(TAG, "Failed to read installed distro.", e); - } - - boolean operationInProgress = this.mOperationInProgress.get(); - - // Determine the staged operation status, if possible. - DistroRulesVersion stagedDistroRulesVersion = null; - int stagedOperationStatus = STAGED_OPERATION_UNKNOWN; - if (!operationInProgress) { - StagedDistroOperation stagedDistroOperation; - try { - stagedDistroOperation = mInstaller.getStagedDistroOperation(); - if (stagedDistroOperation == null) { - stagedOperationStatus = STAGED_OPERATION_NONE; - } else if (stagedDistroOperation.isUninstall) { - stagedOperationStatus = STAGED_OPERATION_UNINSTALL; - } else { - // Must be an install. - stagedOperationStatus = STAGED_OPERATION_INSTALL; - DistroVersion stagedDistroVersion = stagedDistroOperation.distroVersion; - stagedDistroRulesVersion = new DistroRulesVersion( - stagedDistroVersion.rulesVersion, - stagedDistroVersion.revision); - } - } catch (DistroException | IOException e) { - Slog.w(TAG, "Failed to read staged distro.", e); - } - } - return new RulesState(baseVersion.getRulesVersion(), DISTRO_FORMAT_VERSION_SUPPORTED, - operationInProgress, stagedOperationStatus, stagedDistroRulesVersion, - distroStatus, installedDistroRulesVersion); - } - } - - @Override - public int requestInstall(ParcelFileDescriptor distroParcelFileDescriptor, - byte[] checkTokenBytes, ICallback callback) { - - boolean closeParcelFileDescriptorOnExit = true; - try { - mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION); - - CheckToken checkToken = null; - if (checkTokenBytes != null) { - checkToken = createCheckTokenOrThrow(checkTokenBytes); - } - EventLogTags.writeTimezoneRequestInstall(toStringOrNull(checkToken)); - - synchronized (this) { - if (distroParcelFileDescriptor == null) { - throw new NullPointerException("distroParcelFileDescriptor == null"); - } - if (callback == null) { - throw new NullPointerException("observer == null"); - } - if (mOperationInProgress.get()) { - return RulesManager.ERROR_OPERATION_IN_PROGRESS; - } - mOperationInProgress.set(true); - - // Execute the install asynchronously. - mExecutor.execute( - new InstallRunnable(distroParcelFileDescriptor, checkToken, callback)); - - // The InstallRunnable now owns the ParcelFileDescriptor, so it will close it after - // it executes (and we do not have to). - closeParcelFileDescriptorOnExit = false; - - return RulesManager.SUCCESS; - } - } finally { - // We should close() the local ParcelFileDescriptor we were passed if it hasn't been - // passed to another thread to handle. - if (distroParcelFileDescriptor != null && closeParcelFileDescriptorOnExit) { - try { - distroParcelFileDescriptor.close(); - } catch (IOException e) { - Slog.w(TAG, "Failed to close distroParcelFileDescriptor", e); - } - } - } - } - - private class InstallRunnable implements Runnable { - - private final ParcelFileDescriptor mDistroParcelFileDescriptor; - private final CheckToken mCheckToken; - private final ICallback mCallback; - - InstallRunnable(ParcelFileDescriptor distroParcelFileDescriptor, CheckToken checkToken, - ICallback callback) { - mDistroParcelFileDescriptor = distroParcelFileDescriptor; - mCheckToken = checkToken; - mCallback = callback; - } - - @Override - public void run() { - EventLogTags.writeTimezoneInstallStarted(toStringOrNull(mCheckToken)); - - boolean success = false; - // Adopt the ParcelFileDescriptor into this try-with-resources so it is closed - // when we are done. - try (ParcelFileDescriptor pfd = mDistroParcelFileDescriptor) { - // The ParcelFileDescriptor owns the underlying FileDescriptor and we'll close - // it at the end of the try-with-resources. - final boolean isFdOwner = false; - InputStream is = new FileInputStream(pfd.getFileDescriptor(), isFdOwner); - - TimeZoneDistro distro = new TimeZoneDistro(is); - int installerResult = mInstaller.stageInstallWithErrorCode(distro); - - // Notify interested parties that something is staged. - sendInstallNotificationIntentIfRequired(installerResult); - - int resultCode = mapInstallerResultToApiCode(installerResult); - EventLogTags.writeTimezoneInstallComplete(toStringOrNull(mCheckToken), resultCode); - sendFinishedStatus(mCallback, resultCode); - - // All the installer failure modes are currently non-recoverable and won't be - // improved by trying again. Therefore success = true. - success = true; - } catch (Exception e) { - Slog.w(TAG, "Failed to install distro.", e); - EventLogTags.writeTimezoneInstallComplete( - toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE); - sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE); - } finally { - // Notify the package tracker that the operation is now complete. - mPackageTracker.recordCheckResult(mCheckToken, success); - - mOperationInProgress.set(false); - } - } - - private void sendInstallNotificationIntentIfRequired(int installerResult) { - if (installerResult == TimeZoneDistroInstaller.INSTALL_SUCCESS) { - mIntentHelper.sendTimeZoneOperationStaged(); - } - } - - private int mapInstallerResultToApiCode(int installerResult) { - switch (installerResult) { - case TimeZoneDistroInstaller.INSTALL_SUCCESS: - return Callback.SUCCESS; - case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE: - return Callback.ERROR_INSTALL_BAD_DISTRO_STRUCTURE; - case TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD: - return Callback.ERROR_INSTALL_RULES_TOO_OLD; - case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION: - return Callback.ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION; - case TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR: - return Callback.ERROR_INSTALL_VALIDATION_ERROR; - default: - return Callback.ERROR_UNKNOWN_FAILURE; - } - } - } - - @Override - public int requestUninstall(byte[] checkTokenBytes, ICallback callback) { - mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION); - - CheckToken checkToken = null; - if (checkTokenBytes != null) { - checkToken = createCheckTokenOrThrow(checkTokenBytes); - } - EventLogTags.writeTimezoneRequestUninstall(toStringOrNull(checkToken)); - synchronized(this) { - if (callback == null) { - throw new NullPointerException("callback == null"); - } - - if (mOperationInProgress.get()) { - return RulesManager.ERROR_OPERATION_IN_PROGRESS; - } - mOperationInProgress.set(true); - - // Execute the uninstall asynchronously. - mExecutor.execute(new UninstallRunnable(checkToken, callback)); - - return RulesManager.SUCCESS; - } - } - - private class UninstallRunnable implements Runnable { - - private final CheckToken mCheckToken; - private final ICallback mCallback; - - UninstallRunnable(CheckToken checkToken, ICallback callback) { - mCheckToken = checkToken; - mCallback = callback; - } - - @Override - public void run() { - EventLogTags.writeTimezoneUninstallStarted(toStringOrNull(mCheckToken)); - boolean packageTrackerStatus = false; - try { - int uninstallResult = mInstaller.stageUninstall(); - - // Notify interested parties that something is staged. - sendUninstallNotificationIntentIfRequired(uninstallResult); - - packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS - || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED); - - // Right now we just have Callback.SUCCESS / Callback.ERROR_UNKNOWN_FAILURE for - // uninstall. All clients should be checking against SUCCESS. More granular failures - // may be added in future. - int callbackResultCode = - packageTrackerStatus ? Callback.SUCCESS : Callback.ERROR_UNKNOWN_FAILURE; - EventLogTags.writeTimezoneUninstallComplete( - toStringOrNull(mCheckToken), callbackResultCode); - sendFinishedStatus(mCallback, callbackResultCode); - } catch (Exception e) { - EventLogTags.writeTimezoneUninstallComplete( - toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE); - Slog.w(TAG, "Failed to uninstall distro.", e); - sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE); - } finally { - // Notify the package tracker that the operation is now complete. - mPackageTracker.recordCheckResult(mCheckToken, packageTrackerStatus); - - mOperationInProgress.set(false); - } - } - - private void sendUninstallNotificationIntentIfRequired(int uninstallResult) { - switch (uninstallResult) { - case TimeZoneDistroInstaller.UNINSTALL_SUCCESS: - mIntentHelper.sendTimeZoneOperationStaged(); - break; - case TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED: - mIntentHelper.sendTimeZoneOperationUnstaged(); - break; - case TimeZoneDistroInstaller.UNINSTALL_FAIL: - default: - // No-op - unknown or nothing to notify about. - } - } - } - - private void sendFinishedStatus(ICallback callback, int resultCode) { - try { - callback.onFinished(resultCode); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to notify observer of result", e); - } - } - - @Override - public void requestNothing(byte[] checkTokenBytes, boolean success) { - mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION); - CheckToken checkToken = null; - if (checkTokenBytes != null) { - checkToken = createCheckTokenOrThrow(checkTokenBytes); - } - EventLogTags.writeTimezoneRequestNothing(toStringOrNull(checkToken)); - mPackageTracker.recordCheckResult(checkToken, success); - EventLogTags.writeTimezoneNothingComplete(toStringOrNull(checkToken)); - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!mPermissionHelper.checkDumpPermission(TAG, pw)) { - return; - } - - RulesState rulesState = getRulesStateInternal(); - if (args != null && args.length == 2) { - // Formatting options used for automated tests. The format is less free-form than - // the -format options, which are intended to be easier to parse. - if ("-format_state".equals(args[0]) && args[1] != null) { - for (char c : args[1].toCharArray()) { - switch (c) { - case 'p': { - // Report operation in progress - String value = "Unknown"; - if (rulesState != null) { - value = Boolean.toString(rulesState.isOperationInProgress()); - } - pw.println("Operation in progress: " + value); - break; - } - case 'b': { - // Report base rules version - String value = "Unknown"; - if (rulesState != null) { - value = rulesState.getBaseRulesVersion(); - } - pw.println("Base rules version: " + value); - break; - } - case 'c': { - // Report current installation state - String value = "Unknown"; - if (rulesState != null) { - value = distroStatusToString(rulesState.getDistroStatus()); - } - pw.println("Current install state: " + value); - break; - } - case 'i': { - // Report currently installed version - String value = "Unknown"; - if (rulesState != null) { - DistroRulesVersion installedRulesVersion = - rulesState.getInstalledDistroRulesVersion(); - if (installedRulesVersion == null) { - value = "<None>"; - } else { - value = installedRulesVersion.toDumpString(); - } - } - pw.println("Installed rules version: " + value); - break; - } - case 'o': { - // Report staged operation type - String value = "Unknown"; - if (rulesState != null) { - int stagedOperationType = rulesState.getStagedOperationType(); - value = stagedOperationToString(stagedOperationType); - } - pw.println("Staged operation: " + value); - break; - } - case 't': { - // Report staged version (i.e. the one that will be installed next boot - // if the staged operation is an install). - String value = "Unknown"; - if (rulesState != null) { - DistroRulesVersion stagedDistroRulesVersion = - rulesState.getStagedDistroRulesVersion(); - if (stagedDistroRulesVersion == null) { - value = "<None>"; - } else { - value = stagedDistroRulesVersion.toDumpString(); - } - } - pw.println("Staged rules version: " + value); - break; - } - case 'a': { - // Report the active rules version (i.e. the rules in use by the current - // process). - pw.println("Active rules version (ICU, ZoneInfoDb, TimeZoneFinder): " - + TimeZone.getTZDataVersion() + "," - + ZoneInfoDb.getInstance().getVersion() + "," - + TimeZoneFinder.getInstance().getIanaVersion()); - break; - } - default: { - pw.println("Unknown option: " + c); - } - } - } - return; - } - } - - pw.println("RulesManagerService state: " + toString()); - pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): " - + TimeZone.getTZDataVersion() + "," - + ZoneInfoDb.getInstance().getVersion() + "," - + TimeZoneFinder.getInstance().getIanaVersion()); - pw.println("Distro state: " + rulesState.toString()); - mPackageTracker.dump(pw); - } - - /** - * Called when the device is considered idle. - */ - void notifyIdle() { - // No package has changed: we are just triggering because the device is idle and there - // *might* be work to do. - final boolean packageChanged = false; - mPackageTracker.triggerUpdateIfNeeded(packageChanged); - } - - @Override - public String toString() { - return "RulesManagerService{" + - "mOperationInProgress=" + mOperationInProgress + - '}'; - } - - private static CheckToken createCheckTokenOrThrow(byte[] checkTokenBytes) { - CheckToken checkToken; - try { - checkToken = CheckToken.fromByteArray(checkTokenBytes); - } catch (IOException e) { - throw new IllegalArgumentException("Unable to read token bytes " - + Arrays.toString(checkTokenBytes), e); - } - return checkToken; - } - - private static String distroStatusToString(int distroStatus) { - switch(distroStatus) { - case DISTRO_STATUS_NONE: - return "None"; - case DISTRO_STATUS_INSTALLED: - return "Installed"; - case DISTRO_STATUS_UNKNOWN: - default: - return "Unknown"; - } - } - - private static String stagedOperationToString(int stagedOperationType) { - switch(stagedOperationType) { - case STAGED_OPERATION_NONE: - return "None"; - case STAGED_OPERATION_UNINSTALL: - return "Uninstall"; - case STAGED_OPERATION_INSTALL: - return "Install"; - case STAGED_OPERATION_UNKNOWN: - default: - return "Unknown"; - } - } - - private static String toStringOrNull(Object obj) { - return obj == null ? null : obj.toString(); - } -} diff --git a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java deleted file mode 100644 index 8f5c7a783304..000000000000 --- a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import com.android.internal.util.DumpUtils; - -import android.app.timezone.RulesManager; -import android.content.Context; -import android.content.Intent; -import android.os.AsyncTask; -import android.os.UserHandle; - -import java.io.PrintWriter; -import java.util.concurrent.Executor; - -/** - * A single class that implements multiple helper interfaces for use by {@link RulesManagerService}. - */ -final class RulesManagerServiceHelperImpl - implements PermissionHelper, Executor, RulesManagerIntentHelper { - - private final Context mContext; - - RulesManagerServiceHelperImpl(Context context) { - mContext = context; - } - - @Override - public void enforceCallerHasPermission(String requiredPermission) { - mContext.enforceCallingPermission(requiredPermission, null /* message */); - } - - @Override - public boolean checkDumpPermission(String tag, PrintWriter pw) { - return DumpUtils.checkDumpPermission(mContext, tag, pw); - } - - @Override - public void execute(Runnable runnable) { - AsyncTask.execute(runnable); - } - - @Override - public void sendTimeZoneOperationStaged() { - sendOperationIntent(true /* staged */); - } - - @Override - public void sendTimeZoneOperationUnstaged() { - sendOperationIntent(false /* staged */); - } - - private void sendOperationIntent(boolean staged) { - Intent intent = new Intent(RulesManager.ACTION_RULES_UPDATE_OPERATION); - intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - intent.putExtra(RulesManager.EXTRA_OPERATION_STAGED, staged); - mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM); - } - -} diff --git a/services/core/java/com/android/server/timezone/TimeZoneUpdateIdler.java b/services/core/java/com/android/server/timezone/TimeZoneUpdateIdler.java deleted file mode 100644 index 23e3eba68c76..000000000000 --- a/services/core/java/com/android/server/timezone/TimeZoneUpdateIdler.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import android.app.job.JobInfo; -import android.app.job.JobParameters; -import android.app.job.JobScheduler; -import android.app.job.JobService; -import android.content.ComponentName; -import android.content.Context; -import android.util.Slog; - -import com.android.server.LocalServices; - -/** - * A JobService used to trigger time zone rules update work when a device falls idle. - */ -public final class TimeZoneUpdateIdler extends JobService { - - private static final String TAG = "timezone.TimeZoneUpdateIdler"; - - /** The static job ID used to handle on-idle work. */ - // Must be unique within UID (system service) - private static final int TIME_ZONE_UPDATE_IDLE_JOB_ID = 27042305; - - @Override - public boolean onStartJob(JobParameters params) { - RulesManagerService rulesManagerService = - LocalServices.getService(RulesManagerService.class); - - Slog.d(TAG, "onStartJob() called"); - - // Note: notifyIdle() explicitly handles canceling / re-scheduling so no need to reschedule - // here. - rulesManagerService.notifyIdle(); - - // Everything is handled synchronously. We are done. - return false; - } - - @Override - public boolean onStopJob(JobParameters params) { - // Reschedule if stopped unless it was cancelled due to unschedule(). - boolean reschedule = params.getStopReason() != JobParameters.STOP_REASON_CANCELLED_BY_APP; - Slog.d(TAG, "onStopJob() called: Reschedule=" + reschedule); - return reschedule; - } - - /** - * Schedules the TimeZoneUpdateIdler job service to run once. - * - * @param context Context to use to get a job scheduler. - */ - public static void schedule(Context context, long minimumDelayMillis) { - // Request that the JobScheduler tell us when the device falls idle. - JobScheduler jobScheduler = - (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); - - // The TimeZoneUpdateIdler will send an intent that will trigger the Receiver. - ComponentName idlerJobServiceName = - new ComponentName(context, TimeZoneUpdateIdler.class); - - // We require the device is idle, but also that it is charging to be as non-invasive as - // we can. - JobInfo.Builder jobInfoBuilder = - new JobInfo.Builder(TIME_ZONE_UPDATE_IDLE_JOB_ID, idlerJobServiceName) - .setRequiresDeviceIdle(true) - .setRequiresCharging(true) - .setMinimumLatency(minimumDelayMillis); - - Slog.d(TAG, "schedule() called: minimumDelayMillis=" + minimumDelayMillis); - jobScheduler.schedule(jobInfoBuilder.build()); - } - - /** - * Unschedules the TimeZoneUpdateIdler job service. - * - * @param context Context to use to get a job scheduler. - */ - public static void unschedule(Context context) { - JobScheduler jobScheduler = - (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); - Slog.d(TAG, "unschedule() called"); - jobScheduler.cancel(TIME_ZONE_UPDATE_IDLE_JOB_ID); - } -} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 66c9f55b0403..c2a694423b0d 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -344,8 +344,6 @@ public final class SystemServer implements Dumpable { "com.android.server.systemcaptions.SystemCaptionsManagerService"; private static final String TEXT_TO_SPEECH_MANAGER_SERVICE_CLASS = "com.android.server.texttospeech.TextToSpeechManagerService"; - private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS = - "com.android.server.timezone.RulesManagerService$Lifecycle"; private static final String IOT_SERVICE_CLASS = "com.android.things.server.IoTSystemService"; private static final String SLICE_MANAGER_SERVICE_CLASS = @@ -2307,19 +2305,6 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); - // timezone.RulesManagerService will prevent a device starting up if the chain of trust - // required for safe time zone updates might be broken. RuleManagerService cannot do - // this check when mOnlyCore == true, so we don't enable the service in this case. - // This service requires that JobSchedulerService is already started when it starts. - final boolean startRulesManagerService = - !mOnlyCore && context.getResources().getBoolean( - R.bool.config_enableUpdateableTimeZoneRules); - if (startRulesManagerService) { - t.traceBegin("StartTimeZoneRulesManagerService"); - mSystemServiceManager.startService(TIME_ZONE_RULES_MANAGER_SERVICE_CLASS); - t.traceEnd(); - } - if (!isWatch && !disableNetworkTime) { t.traceBegin("StartNetworkTimeUpdateService"); try { diff --git a/services/tests/servicestests/src/com/android/server/timezone/CheckTokenTest.java b/services/tests/servicestests/src/com/android/server/timezone/CheckTokenTest.java deleted file mode 100644 index f7d282b95dff..000000000000 --- a/services/tests/servicestests/src/com/android/server/timezone/CheckTokenTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.fail; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -import java.io.IOException; - -@SmallTest -public class CheckTokenTest { - - @Test - public void toByteArray() throws Exception { - PackageVersions packageVersions = - new PackageVersions(1 /* updateAppVersion */, 1 /* dataAppVersion */); - CheckToken originalToken = new CheckToken(1 /* optimisticLockId */, packageVersions); - assertEquals(originalToken, CheckToken.fromByteArray(originalToken.toByteArray())); - } - - @Test - public void fromByteArray() { - PackageVersions packageVersions = - new PackageVersions(1 /* updateAppVersion */, 1 /* dataAppVersion */); - CheckToken token = new CheckToken(1, packageVersions); - byte[] validTokenBytes = token.toByteArray(); - byte[] shortTokenBytes = new byte[validTokenBytes.length - 1]; - System.arraycopy(validTokenBytes, 0, shortTokenBytes, 0, shortTokenBytes.length); - - try { - CheckToken.fromByteArray(shortTokenBytes); - fail(); - } catch (IOException expected) {} - } - - @Test - public void equals() { - PackageVersions packageVersions1 = - new PackageVersions(1 /* updateAppVersion */, 1 /* dataAppVersion */); - PackageVersions packageVersions2 = - new PackageVersions(2 /* updateAppVersion */, 2 /* dataAppVersion */); - assertFalse(packageVersions1.equals(packageVersions2)); - - CheckToken baseline = new CheckToken(1, packageVersions1); - assertEquals(baseline, baseline); - - CheckToken deepEqual = new CheckToken(1, packageVersions1); - assertEquals(baseline, deepEqual); - - CheckToken differentOptimisticLockId = new CheckToken(2, packageVersions1); - assertFalse(differentOptimisticLockId.equals(baseline)); - - CheckToken differentPackageVersions = new CheckToken(1, packageVersions2); - assertFalse(differentPackageVersions.equals(baseline)); - } -} diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java deleted file mode 100644 index 090db111ab31..000000000000 --- a/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import static junit.framework.Assert.assertTrue; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -import android.content.Context; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; - -@SmallTest -public class PackageStatusStorageTest { - private static final PackageVersions VALID_PACKAGE_VERSIONS = new PackageVersions(1, 2); - - private PackageStatusStorage mPackageStatusStorage; - - @Before - public void setUp() throws Exception { - Context context = InstrumentationRegistry.getContext(); - File dataDir = context.getFilesDir(); - - // Using the instrumentation context means the database is created in a test app-specific - // directory. - mPackageStatusStorage = new PackageStatusStorage(dataDir); - mPackageStatusStorage.initialize(); - } - - @After - public void tearDown() throws Exception { - mPackageStatusStorage.deleteFileForTests(); - } - - @Test - public void initialize_fail() { - File readOnlyDir = new File("/system/does/not/exist"); - PackageStatusStorage packageStatusStorage = new PackageStatusStorage(readOnlyDir); - try { - packageStatusStorage.initialize(); - fail(); - } catch (IOException expected) {} - } - - @Test - public void getPackageStatus_initialState() { - assertNull(mPackageStatusStorage.getPackageStatus()); - } - - @Test - public void resetCheckState() { - // Assert initial state. - assertNull(mPackageStatusStorage.getPackageStatus()); - - CheckToken token1 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS); - - // There should now be a state. - assertNotNull(mPackageStatusStorage.getPackageStatus()); - - // Now clear the state. - mPackageStatusStorage.resetCheckState(); - - // After reset, there should be no package state again. - assertNull(mPackageStatusStorage.getPackageStatus()); - - CheckToken token2 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS); - - // Token after a reset should still be distinct. - assertFalse(token1.equals(token2)); - - // Now clear the state again. - mPackageStatusStorage.resetCheckState(); - - // After reset, there should be no package state again. - assertNull(mPackageStatusStorage.getPackageStatus()); - - CheckToken token3 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS); - - // A CheckToken generated after a reset should still be distinct. - assertFalse(token2.equals(token3)); - } - - @Test - public void generateCheckToken_missingFileBehavior() { - // Assert initial state. - assertNull(mPackageStatusStorage.getPackageStatus()); - - CheckToken token1 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS); - assertNotNull(token1); - - // There should now be state. - assertNotNull(mPackageStatusStorage.getPackageStatus()); - - // Corrupt the data by removing the file. - mPackageStatusStorage.deleteFileForTests(); - - // Check that generateCheckToken recovers. - assertNotNull(mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS)); - } - - @Test - public void getPackageStatus_missingFileBehavior() { - // Assert initial state. - assertNull(mPackageStatusStorage.getPackageStatus()); - - CheckToken token1 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS); - assertNotNull(token1); - - // There should now be a state. - assertNotNull(mPackageStatusStorage.getPackageStatus()); - - // Corrupt the data by removing the file. - mPackageStatusStorage.deleteFileForTests(); - - assertNull(mPackageStatusStorage.getPackageStatus()); - } - - @Test - public void markChecked_missingFileBehavior() { - // Assert initial state. - CheckToken token1 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS); - assertNotNull(token1); - - // There should now be a state. - assertNotNull(mPackageStatusStorage.getPackageStatus()); - - // Corrupt the data by removing the file. - mPackageStatusStorage.deleteFileForTests(); - - // The missing file should mean token1 is now considered invalid, so we should get a false. - assertFalse(mPackageStatusStorage.markChecked(token1, true /* succeeded */)); - - // The storage should have recovered and we should be able to carry on like before. - CheckToken token2 = mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS); - assertTrue(mPackageStatusStorage.markChecked(token2, true /* succeeded */)); - } - - @Test - public void checkToken_tokenIsUnique() { - PackageVersions packageVersions = VALID_PACKAGE_VERSIONS; - PackageStatus expectedPackageStatus = - new PackageStatus(PackageStatus.CHECK_STARTED, packageVersions); - - CheckToken token1 = mPackageStatusStorage.generateCheckToken(packageVersions); - assertEquals(packageVersions, token1.mPackageVersions); - - PackageStatus actualPackageStatus1 = mPackageStatusStorage.getPackageStatus(); - assertEquals(expectedPackageStatus, actualPackageStatus1); - - CheckToken token2 = mPackageStatusStorage.generateCheckToken(packageVersions); - assertEquals(packageVersions, token1.mPackageVersions); - assertFalse(token1.mOptimisticLockId == token2.mOptimisticLockId); - assertFalse(token1.equals(token2)); - } - - @Test - public void markChecked_checkSucceeded() { - PackageVersions packageVersions = VALID_PACKAGE_VERSIONS; - - CheckToken token = mPackageStatusStorage.generateCheckToken(packageVersions); - boolean writeOk = mPackageStatusStorage.markChecked(token, true /* succeeded */); - assertTrue(writeOk); - - PackageStatus expectedPackageStatus = - new PackageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions); - assertEquals(expectedPackageStatus, mPackageStatusStorage.getPackageStatus()); - } - - @Test - public void markChecked_checkFailed() { - PackageVersions packageVersions = VALID_PACKAGE_VERSIONS; - - CheckToken token = mPackageStatusStorage.generateCheckToken(packageVersions); - boolean writeOk = mPackageStatusStorage.markChecked(token, false /* succeeded */); - assertTrue(writeOk); - - PackageStatus expectedPackageStatus = - new PackageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, packageVersions); - assertEquals(expectedPackageStatus, mPackageStatusStorage.getPackageStatus()); - } - - @Test - public void markChecked_optimisticLocking_multipleToken() { - PackageVersions packageVersions = VALID_PACKAGE_VERSIONS; - CheckToken token1 = mPackageStatusStorage.generateCheckToken(packageVersions); - CheckToken token2 = mPackageStatusStorage.generateCheckToken(packageVersions); - - PackageStatus packageStatusBeforeChecked = mPackageStatusStorage.getPackageStatus(); - - boolean writeOk1 = mPackageStatusStorage.markChecked(token1, true /* succeeded */); - // Generation of token2 should mean that token1 is no longer valid. - assertFalse(writeOk1); - assertEquals(packageStatusBeforeChecked, mPackageStatusStorage.getPackageStatus()); - - boolean writeOk2 = mPackageStatusStorage.markChecked(token2, true /* succeeded */); - // token2 should still be valid, and the attempt with token1 should have had no effect. - assertTrue(writeOk2); - PackageStatus expectedPackageStatus = - new PackageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions); - assertEquals(expectedPackageStatus, mPackageStatusStorage.getPackageStatus()); - } - - @Test - public void markChecked_optimisticLocking_repeatedTokenUse() { - PackageVersions packageVersions = VALID_PACKAGE_VERSIONS; - CheckToken token = mPackageStatusStorage.generateCheckToken(packageVersions); - - boolean writeOk1 = mPackageStatusStorage.markChecked(token, true /* succeeded */); - assertTrue(writeOk1); - - PackageStatus expectedPackageStatus = - new PackageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions); - assertEquals(expectedPackageStatus, mPackageStatusStorage.getPackageStatus()); - - // token cannot be reused. - boolean writeOk2 = mPackageStatusStorage.markChecked(token, true /* succeeded */); - assertFalse(writeOk2); - assertEquals(expectedPackageStatus, mPackageStatusStorage.getPackageStatus()); - } - - @Test - public void dump() { - StringWriter stringWriter = new StringWriter(); - PrintWriter printWriter = new PrintWriter(stringWriter); - - // Dump initial state. - mPackageStatusStorage.dump(printWriter); - - // No crash and it does something. - assertFalse(stringWriter.toString().isEmpty()); - - // Reset - stringWriter.getBuffer().setLength(0); - assertTrue(stringWriter.toString().isEmpty()); - - // Store something. - mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS); - - mPackageStatusStorage.dump(printWriter); - - assertFalse(stringWriter.toString().isEmpty()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageStatusTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageStatusTest.java deleted file mode 100644 index 9b45f0542610..000000000000 --- a/services/tests/servicestests/src/com/android/server/timezone/PackageStatusTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -@SmallTest -public class PackageStatusTest { - - @Test - public void equals() { - PackageVersions packageVersions1 = - new PackageVersions(1 /* updateAppVersion */, 1 /* dataAppVersion */); - PackageVersions packageVersions2 = - new PackageVersions(2 /* updateAppVersion */, 1 /* dataAppVersion */); - assertFalse(packageVersions1.equals(packageVersions2)); - - PackageStatus baseline = - new PackageStatus(PackageStatus.CHECK_STARTED, packageVersions1); - assertEquals(baseline, baseline); - - PackageStatus deepEqual = - new PackageStatus(PackageStatus.CHECK_STARTED, packageVersions1); - assertEquals(baseline, deepEqual); - - PackageStatus differentStatus = - new PackageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions1); - assertFalse(differentStatus.equals(baseline)); - - PackageStatus differentPackageVersions = - new PackageStatus(PackageStatus.CHECK_STARTED, packageVersions2); - assertFalse(differentPackageVersions.equals(baseline)); - } -} diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java deleted file mode 100644 index 1356ea250526..000000000000 --- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java +++ /dev/null @@ -1,1527 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.mockito.hamcrest.MockitoHamcrest.argThat; - -import android.app.timezone.RulesUpdaterContract; -import android.content.Context; -import android.content.Intent; -import android.provider.TimeZoneRulesDataContract; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; - -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; - -/** - * White box interaction / unit testing of the {@link PackageTracker}. - */ -@SmallTest -public class PackageTrackerTest { - private static final String UPDATE_APP_PACKAGE_NAME = "updateAppPackageName"; - private static final String DATA_APP_PACKAGE_NAME = "dataAppPackageName"; - private static final PackageVersions INITIAL_APP_PACKAGE_VERSIONS = - new PackageVersions(2 /* updateAppVersion */, 2 /* dataAppVersion */); - - private ConfigHelper mMockConfigHelper; - private PackageManagerHelper mMockPackageManagerHelper; - - private FakeClock mFakeClock; - private FakeIntentHelper mFakeIntentHelper; - private PackageStatusStorage mPackageStatusStorage; - private PackageTracker mPackageTracker; - - @Before - public void setUp() throws Exception { - Context context = InstrumentationRegistry.getContext(); - - mFakeClock = new FakeClock(); - - // Read-only interfaces so are easy to mock. - mMockConfigHelper = mock(ConfigHelper.class); - mMockPackageManagerHelper = mock(PackageManagerHelper.class); - - // Using the instrumentation context means the database is created in a test app-specific - // directory. We can use the real thing for this test. - mPackageStatusStorage = new PackageStatusStorage(context.getFilesDir()); - - // For other interactions with the Android framework we create a fake object. - mFakeIntentHelper = new FakeIntentHelper(); - - // Create the PackageTracker to use in tests. - mPackageTracker = new PackageTracker( - mFakeClock, - mMockConfigHelper, - mMockPackageManagerHelper, - mPackageStatusStorage, - mFakeIntentHelper); - } - - @After - public void tearDown() throws Exception { - if (mPackageStatusStorage != null) { - mPackageStatusStorage.deleteFileForTests(); - } - } - - @Test - public void trackingDisabled_intentHelperNotUsed() { - // Set up device configuration. - configureTrackingDisabled(); - - // Initialize the tracker. - assertFalse(mPackageTracker.start()); - - // Check the IntentHelper was not initialized. - mFakeIntentHelper.assertNotInitialized(); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - } - - @Test - public void trackingDisabled_triggerUpdateIfNeededNotAllowed() { - // Set up device configuration. - configureTrackingDisabled(); - - // Initialize the tracker. - assertFalse(mPackageTracker.start()); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - - try { - // This call should also not be allowed and will throw an exception if tracking is - // disabled. - mPackageTracker.triggerUpdateIfNeeded(true); - fail(); - } catch (IllegalStateException expected) {} - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - } - - @Test - public void trackingDisabled_unsolicitedResultsIgnored_withoutToken() { - // Set up device configuration. - configureTrackingDisabled(); - - // Initialize the tracker. - assertFalse(mPackageTracker.start()); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - - // Receiving a check result when tracking is disabled should cause the storage to be - // reset. - mPackageTracker.recordCheckResult(null /* checkToken */, true /* success */); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - - // Assert the storage was reset. - checkPackageStorageStatusIsInitialOrReset(); - } - - @Test - public void trackingDisabled_unsolicitedResultsIgnored_withToken() { - // Set up device configuration. - configureTrackingDisabled(); - - // Set the storage into an arbitrary state so we can detect a reset. - mPackageStatusStorage.generateCheckToken(INITIAL_APP_PACKAGE_VERSIONS); - - // Initialize the tracker. - assertFalse(mPackageTracker.start()); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - - // Receiving a check result when tracking is disabled should cause the storage to be reset. - mPackageTracker.recordCheckResult(createArbitraryCheckToken(), true /* success */); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - - // Assert the storage was reset. - checkPackageStorageStatusIsInitialOrReset(); - } - - @Test - public void trackingEnabled_updateAppConfigMissing() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureUpdateAppPackageNameMissing(); - configureDataAppPackageOk(DATA_APP_PACKAGE_NAME); - - try { - // Initialize the tracker. - mPackageTracker.start(); - fail(); - } catch (RuntimeException expected) {} - - mFakeIntentHelper.assertNotInitialized(); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - } - - @Test - public void trackingEnabled_updateAppNotPrivileged() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureUpdateAppPackageNotPrivileged(UPDATE_APP_PACKAGE_NAME); - configureDataAppPackageOk(DATA_APP_PACKAGE_NAME); - - try { - // Initialize the tracker. - mPackageTracker.start(); - fail(); - } catch (RuntimeException expected) {} - - mFakeIntentHelper.assertNotInitialized(); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - } - - @Test - public void trackingEnabled_dataAppConfigMissing() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureUpdateAppPackageOk(UPDATE_APP_PACKAGE_NAME); - configureDataAppPackageNameMissing(); - - try { - // Initialize the tracker. - mPackageTracker.start(); - fail(); - } catch (RuntimeException expected) {} - - mFakeIntentHelper.assertNotInitialized(); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - } - - @Test - public void trackingEnabled_dataAppNotPrivileged() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureUpdateAppPackageOk(UPDATE_APP_PACKAGE_NAME); - configureDataAppPackageNotPrivileged(DATA_APP_PACKAGE_NAME); - - try { - // Initialize the tracker. - mPackageTracker.start(); - fail(); - } catch (RuntimeException expected) {} - - mFakeIntentHelper.assertNotInitialized(); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - } - - @Test - public void trackingEnabled_storageInitializationFails() throws Exception { - // Create a PackageStateStorage that will fail to initialize. - PackageStatusStorage packageStatusStorage = - new PackageStatusStorage(new File("/system/does/not/exist")); - - // Create a new PackageTracker to use the bad storage. - mPackageTracker = new PackageTracker( - mFakeClock, - mMockConfigHelper, - mMockPackageManagerHelper, - packageStatusStorage, - mFakeIntentHelper); - - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureValidApplications(); - - // Initialize the tracker. - assertFalse(mPackageTracker.start()); - - // Check the IntentHelper was not initialized. - mFakeIntentHelper.assertNotInitialized(); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - } - - @Test - public void trackingEnabled_packageUpdate_badUpdateAppManifestEntry() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureValidApplications(); - - // Initialize the tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatusIsInitialOrReset(); - - // Configure a bad manifest for the update app. Should effectively turn off tracking. - PackageVersions packageVersions = - new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */); - configureUpdateAppManifestBad(UPDATE_APP_PACKAGE_NAME); - configureDataAppManifestOk(DATA_APP_PACKAGE_NAME); - configureUpdateAppPackageVersion( - UPDATE_APP_PACKAGE_NAME, packageVersions.mUpdateAppVersion); - configureDataAppPackageVersion(DATA_APP_PACKAGE_NAME, packageVersions.mDataAppVersion); - // Simulate a tracked package being updated. - mFakeIntentHelper.simulatePackageUpdatedEvent(); - - // Assert the PackageTracker did not attempt to trigger an update. - mFakeIntentHelper.assertUpdateNotTriggered(); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - - // Assert the storage was not touched. - checkPackageStorageStatusIsInitialOrReset(); - } - - @Test - public void trackingEnabled_packageUpdate_badDataAppManifestEntry() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureValidApplications(); - - // Initialize the tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatusIsInitialOrReset(); - - // Configure a bad manifest for the data app. Should effectively turn off tracking. - PackageVersions packageVersions = - new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */); - configureUpdateAppManifestOk(UPDATE_APP_PACKAGE_NAME); - configureDataAppManifestBad(DATA_APP_PACKAGE_NAME); - configureUpdateAppPackageVersion( - UPDATE_APP_PACKAGE_NAME, packageVersions.mUpdateAppVersion); - configureDataAppPackageVersion(DATA_APP_PACKAGE_NAME, packageVersions.mDataAppVersion); - mFakeIntentHelper.simulatePackageUpdatedEvent(); - - // Assert the PackageTracker did not attempt to trigger an update. - mFakeIntentHelper.assertUpdateNotTriggered(); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - - // Assert the storage was not touched. - checkPackageStorageStatusIsInitialOrReset(); - } - - @Test - public void trackingEnabled_packageUpdate_responseWithToken_success() throws Exception { - trackingEnabled_packageUpdate_responseWithToken(true); - } - - @Test - public void trackingEnabled_packageUpdate_responseWithToken_failed() throws Exception { - trackingEnabled_packageUpdate_responseWithToken(false); - } - - private void trackingEnabled_packageUpdate_responseWithToken(boolean success) throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureValidApplications(); - - // Initialize the tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatusIsInitialOrReset(); - - // Simulate a tracked package being updated. - PackageVersions packageVersions = - new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */); - simulatePackageInstallation(packageVersions); - - // Confirm an update was triggered. - checkUpdateCheckTriggered(packageVersions); - - // Get the token that was passed to the intent helper, and pass it back. - CheckToken token = mFakeIntentHelper.captureAndResetLastToken(); - mPackageTracker.recordCheckResult(token, success); - - // Check storage and reliability triggering state. - if (success) { - checkUpdateCheckSuccessful(packageVersions); - } else { - checkUpdateCheckFailed(packageVersions); - } - } - - @Test - public void trackingEnabled_packageUpdate_responseWithoutTokenCausesStorageReset_success() - throws Exception { - trackingEnabled_packageUpdate_responseWithoutTokenCausesStorageReset(true); - } - - @Test - public void trackingEnabled_packageUpdate_responseWithoutTokenCausesStorageReset_failed() - throws Exception { - trackingEnabled_packageUpdate_responseWithoutTokenCausesStorageReset(false); - } - - private void trackingEnabled_packageUpdate_responseWithoutTokenCausesStorageReset( - boolean success) throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureValidApplications(); - - // Initialize the tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatusIsInitialOrReset(); - - // Set up installed app versions / manifests. - PackageVersions packageVersions = - new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */); - simulatePackageInstallation(packageVersions); - - // Confirm an update was triggered. - checkUpdateCheckTriggered(packageVersions); - - // Ignore the token that was given to the intent helper, just pass null. - mPackageTracker.recordCheckResult(null /* checkToken */, success); - - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerScheduled(); - - // Assert the storage was reset. - checkPackageStorageStatusIsInitialOrReset(); - } - - /** - * Two package updates triggered for the same package versions. The second is triggered while - * the first is still happening. - */ - @Test - public void trackingEnabled_packageUpdate_twoChecksNoPackageChange_secondWhileFirstInProgress() - throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureValidApplications(); - - // Initialize the tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatusIsInitialOrReset(); - - // Simulate package installation. - PackageVersions packageVersions = - new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */); - simulatePackageInstallation(packageVersions); - - // Confirm an update was triggered. - checkUpdateCheckTriggered(packageVersions); - - // Get the first token. - CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken(); - assertEquals(packageVersions, token1.mPackageVersions); - - // Now attempt to generate another check while the first is in progress and without having - // updated the package versions. The PackageTracker should trigger again for safety. - simulatePackageInstallation(packageVersions); - - // Confirm an update was triggered. - checkUpdateCheckTriggered(packageVersions); - - CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken(); - assertEquals(packageVersions, token2.mPackageVersions); - assertEquals(token1.mPackageVersions, token2.mPackageVersions); - assertTrue(token1.mOptimisticLockId != token2.mOptimisticLockId); - } - - /** - * Two package updates triggered for the same package versions. The second happens after - * the first has succeeded. - */ - @Test - public void trackingEnabled_packageUpdate_twoChecksNoPackageChange_sequential() - throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureValidApplications(); - - // Initialize the tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatusIsInitialOrReset(); - - // Simulate package installation. - PackageVersions packageVersions = - new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */); - simulatePackageInstallation(packageVersions); - - // Confirm an update was triggered. - checkUpdateCheckTriggered(packageVersions); - - // Get the token. - CheckToken token = mFakeIntentHelper.captureAndResetLastToken(); - assertEquals(packageVersions, token.mPackageVersions); - - // Simulate a successful check. - mPackageTracker.recordCheckResult(token, true /* success */); - - // Check storage and reliability triggering state. - checkUpdateCheckSuccessful(packageVersions); - - // Now attempt to generate another check, but without having updated the package. The - // PackageTracker should be smart enough to recognize there's nothing to do here. - simulatePackageInstallation(packageVersions); - - // Assert the PackageTracker did not attempt to trigger an update. - mFakeIntentHelper.assertUpdateNotTriggered(); - - // Check storage and reliability triggering state. - checkUpdateCheckSuccessful(packageVersions); - } - - /** - * Two package updates triggered for the same package versions. The second is triggered after - * the first has failed. - */ - @Test - public void trackingEnabled_packageUpdate_afterFailure() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureValidApplications(); - - // Initialize the tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatusIsInitialOrReset(); - - // Simulate package installation. - PackageVersions packageVersions = - new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */); - simulatePackageInstallation(packageVersions); - - // Confirm an update was triggered. - checkUpdateCheckTriggered(packageVersions); - - // Get the first token. - CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken(); - assertEquals(packageVersions, token1.mPackageVersions); - - // Simulate an *unsuccessful* check. - mPackageTracker.recordCheckResult(token1, false /* success */); - - // Check storage and reliability triggering state. - checkUpdateCheckFailed(packageVersions); - - // Now generate another check, but without having updated the package. The - // PackageTracker should recognize the last check failed and trigger again. - simulatePackageInstallation(packageVersions); - - // Confirm an update was triggered. - checkUpdateCheckTriggered(packageVersions); - - // Get the second token. - CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken(); - - // Assert some things about the tokens. - assertEquals(packageVersions, token2.mPackageVersions); - assertTrue(token1.mOptimisticLockId != token2.mOptimisticLockId); - - // For completeness, now simulate this check was successful. - mPackageTracker.recordCheckResult(token2, true /* success */); - - // Check storage and reliability triggering state. - checkUpdateCheckSuccessful(packageVersions); - } - - /** - * Two package updates triggered for different package versions. The second is triggered while - * the first is still happening. - */ - @Test - public void trackingEnabled_packageUpdate_twoChecksWithPackageChange_firstCheckInProcess() - throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureValidApplications(); - - // Initialize the package tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatusIsInitialOrReset(); - - // Simulate package installation. - PackageVersions packageVersions1 = - new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */); - simulatePackageInstallation(packageVersions1); - - // Confirm an update was triggered. - checkUpdateCheckTriggered(packageVersions1); - - // Get the first token. - CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken(); - assertEquals(packageVersions1, token1.mPackageVersions); - - // Simulate a tracked package being updated a second time (before the response for the - // first has been received). - PackageVersions packageVersions2 = - new PackageVersions(3 /* updateAppPackageVersion */, 4 /* dataAppPackageVersion */); - simulatePackageInstallation(packageVersions2); - - // Confirm an update was triggered. - checkUpdateCheckTriggered(packageVersions2); - - // Get the second token. - CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken(); - assertEquals(packageVersions2, token2.mPackageVersions); - - // token1 should be invalid because the token2 was generated. - mPackageTracker.recordCheckResult(token1, true /* success */); - - // Reliability triggering should still be enabled. - mFakeIntentHelper.assertReliabilityTriggerScheduled(); - - // Check the expected storage state. - checkPackageStorageStatus(PackageStatus.CHECK_STARTED, packageVersions2); - - // token2 should still be accepted. - mPackageTracker.recordCheckResult(token2, true /* success */); - - // Check storage and reliability triggering state. - checkUpdateCheckSuccessful(packageVersions2); - } - - /** - * Two package updates triggered for different package versions. The second is triggered after - * the first has completed successfully. - */ - @Test - public void trackingEnabled_packageUpdate_twoChecksWithPackageChange_sequential() - throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureValidApplications(); - - // Initialize the package tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatusIsInitialOrReset(); - - // Simulate package installation. - PackageVersions packageVersions1 = - new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */); - simulatePackageInstallation(packageVersions1); - - // Confirm an update was triggered. - checkUpdateCheckTriggered(packageVersions1); - - // Get the first token. - CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken(); - assertEquals(packageVersions1, token1.mPackageVersions); - - // token1 should be accepted. - mPackageTracker.recordCheckResult(token1, true /* success */); - - // Check storage and reliability triggering state. - checkUpdateCheckSuccessful(packageVersions1); - - // Simulate a tracked package being updated a second time. - PackageVersions packageVersions2 = - new PackageVersions(3 /* updateAppPackageVersion */, 4 /* dataAppPackageVersion */); - simulatePackageInstallation(packageVersions2); - - // Confirm an update was triggered. - checkUpdateCheckTriggered(packageVersions2); - - // Get the second token. - CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken(); - assertEquals(packageVersions2, token2.mPackageVersions); - - // token2 should still be accepted. - mPackageTracker.recordCheckResult(token2, true /* success */); - - // Check storage and reliability triggering state. - checkUpdateCheckSuccessful(packageVersions2); - } - - /** - * Replaying the same token twice. - */ - @Test - public void trackingEnabled_packageUpdate_sameTokenReplayFails() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - configureValidApplications(); - - // Initialize the package tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatusIsInitialOrReset(); - - // Simulate package installation. - PackageVersions packageVersions1 = - new PackageVersions(2 /* updateAppPackageVersion */, 3 /* dataAppPackageVersion */); - simulatePackageInstallation(packageVersions1); - - // Confirm an update was triggered. - checkUpdateCheckTriggered(packageVersions1); - - // Get the first token. - CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken(); - assertEquals(packageVersions1, token1.mPackageVersions); - - // token1 should be accepted. - mPackageTracker.recordCheckResult(token1, true /* success */); - - // Check storage and reliability triggering state. - checkUpdateCheckSuccessful(packageVersions1); - - // Apply token1 again. - mPackageTracker.recordCheckResult(token1, true /* success */); - - // Check the expected storage state. No real way to tell if it has been updated, but - // we can check the final state is still what it should be. - checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions1); - - // Under the covers we expect it to fail to update because the storage should recognize that - // the token is no longer valid. - mFakeIntentHelper.assertReliabilityTriggerScheduled(); - - // Peek inside the package tracker to make sure it is tracking failure counts properly. - assertEquals(1, mPackageTracker.getCheckFailureCountForTests()); - } - - @Test - public void trackingEnabled_reliabilityTrigger_firstTime_initialStorage() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - PackageVersions packageVersions = configureValidApplications(); - - // Initialize the package tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatusIsInitialOrReset(); - - // Simulate a reliability trigger. - mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */); - - // Assert the PackageTracker did trigger an update. - checkUpdateCheckTriggered(packageVersions); - - // Confirm the token was correct. - CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken(); - assertEquals(packageVersions, token1.mPackageVersions); - - // token1 should be accepted. - mPackageTracker.recordCheckResult(token1, true /* success */); - - // Check storage and reliability triggering state. - checkUpdateCheckSuccessful(packageVersions); - } - - @Test - public void trackingEnabled_reliabilityTrigger_afterRebootNoTriggerNeeded() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - PackageVersions packageVersions = configureValidApplications(); - - // Force the storage into a state we want. - mPackageStatusStorage.forceCheckStateForTests( - PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions); - - // Initialize the package tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions); - - // Simulate a reliability trigger. - mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */); - - // Assert the PackageTracker did not attempt to trigger an update. - mFakeIntentHelper.assertUpdateNotTriggered(); - - // Check storage and reliability triggering state. - checkUpdateCheckSuccessful(packageVersions); - } - - /** - * Simulates the device starting where the storage records do not match the installed app - * versions. The reliability trigger should cause the package tracker to perform a check. - */ - @Test - public void trackingEnabled_reliabilityTrigger_afterRebootTriggerNeededBecausePreviousFailed() - throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - configureReliabilityConfigSettingsOk(); - - PackageVersions oldPackageVersions = new PackageVersions(1, 1); - PackageVersions currentPackageVersions = new PackageVersions(2, 2); - - // Simulate there being a newer version installed than the one recorded in storage. - configureValidApplications(currentPackageVersions); - - // Force the storage into a state we want. - mPackageStatusStorage.forceCheckStateForTests( - PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions); - - // Initialize the package tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions); - - // Simulate a reliability trigger. - mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */); - - // Assert the PackageTracker did trigger an update. - checkUpdateCheckTriggered(currentPackageVersions); - - // Simulate the update check completing successfully. - CheckToken checkToken = mFakeIntentHelper.captureAndResetLastToken(); - mPackageTracker.recordCheckResult(checkToken, true /* success */); - - // Check storage and reliability triggering state. - checkUpdateCheckSuccessful(currentPackageVersions); - } - - /** - * Simulates persistent failures of the reliability check. It should stop after the configured - * number of checks. - */ - @Test - public void trackingEnabled_reliabilityTrigger_repeatedFailures() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - - int retriesAllowed = 3; - int checkDelayMillis = 5 * 60 * 1000; - configureReliabilityConfigSettings(retriesAllowed, checkDelayMillis); - - PackageVersions oldPackageVersions = new PackageVersions(1, 1); - PackageVersions currentPackageVersions = new PackageVersions(2, 2); - - // Simulate there being a newer version installed than the one recorded in storage. - configureValidApplications(currentPackageVersions); - - // Force the storage into a state we want. - mPackageStatusStorage.forceCheckStateForTests( - PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions); - - // Initialize the package tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions); - - for (int i = 0; i < retriesAllowed + 1; i++) { - // Simulate a reliability trigger. - mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */); - - // Assert the PackageTracker did trigger an update. - checkUpdateCheckTriggered(currentPackageVersions); - - // Check the PackageTracker failure count before calling recordCheckResult. - assertEquals(i, mPackageTracker.getCheckFailureCountForTests()); - - // Simulate a check failure. - CheckToken checkToken = mFakeIntentHelper.captureAndResetLastToken(); - mPackageTracker.recordCheckResult(checkToken, false /* success */); - - // Peek inside the package tracker to make sure it is tracking failure counts properly. - assertEquals(i + 1, mPackageTracker.getCheckFailureCountForTests()); - - // Confirm nothing has changed. - mFakeIntentHelper.assertUpdateNotTriggered(); - checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, - currentPackageVersions); - - // Check reliability triggering is in the correct state. - if (i <= retriesAllowed) { - mFakeIntentHelper.assertReliabilityTriggerScheduled(); - } else { - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - } - } - } - - @Test - public void trackingEnabled_reliabilityTrigger_failureCountIsReset() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - - int retriesAllowed = 3; - int checkDelayMillis = 5 * 60 * 1000; - configureReliabilityConfigSettings(retriesAllowed, checkDelayMillis); - - PackageVersions oldPackageVersions = new PackageVersions(1, 1); - PackageVersions currentPackageVersions = new PackageVersions(2, 2); - - // Simulate there being a newer version installed than the one recorded in storage. - configureValidApplications(currentPackageVersions); - - // Force the storage into a state we want. - mPackageStatusStorage.forceCheckStateForTests( - PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions); - - // Initialize the package tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions); - - // Fail (retries - 1) times. - for (int i = 0; i < retriesAllowed - 1; i++) { - // Simulate a reliability trigger. - mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */); - - // Assert the PackageTracker did trigger an update. - checkUpdateCheckTriggered(currentPackageVersions); - - // Check the PackageTracker failure count before calling recordCheckResult. - assertEquals(i, mPackageTracker.getCheckFailureCountForTests()); - - // Simulate a check failure. - CheckToken checkToken = mFakeIntentHelper.captureAndResetLastToken(); - mPackageTracker.recordCheckResult(checkToken, false /* success */); - - // Peek inside the package tracker to make sure it is tracking failure counts properly. - assertEquals(i + 1, mPackageTracker.getCheckFailureCountForTests()); - - // Confirm nothing has changed. - mFakeIntentHelper.assertUpdateNotTriggered(); - checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, - currentPackageVersions); - - // Check reliability triggering is still enabled. - mFakeIntentHelper.assertReliabilityTriggerScheduled(); - } - - // Simulate a reliability trigger. - mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */); - - // Assert the PackageTracker did trigger an update. - checkUpdateCheckTriggered(currentPackageVersions); - - // Check the PackageTracker failure count before calling recordCheckResult. - assertEquals(retriesAllowed - 1, mPackageTracker.getCheckFailureCountForTests()); - - // On the last possible try, succeed. - CheckToken checkToken = mFakeIntentHelper.captureAndResetLastToken(); - mPackageTracker.recordCheckResult(checkToken, true /* success */); - - checkUpdateCheckSuccessful(currentPackageVersions); - } - - /** - * Simulates reliability triggers happening too close together. Package tracker should ignore - * the ones it doesn't need. - */ - @Test - public void trackingEnabled_reliabilityTrigger_tooSoon() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - - int retriesAllowed = 5; - int checkDelayMillis = 5 * 60 * 1000; - configureReliabilityConfigSettings(retriesAllowed, checkDelayMillis); - - PackageVersions oldPackageVersions = new PackageVersions(1, 1); - PackageVersions currentPackageVersions = new PackageVersions(2, 2); - - // Simulate there being a newer version installed than the one recorded in storage. - configureValidApplications(currentPackageVersions); - - // Force the storage into a state we want. - mPackageStatusStorage.forceCheckStateForTests( - PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions); - - // Initialize the package tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions); - - // Simulate a reliability trigger. - mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */); - - // Assert the PackageTracker did trigger an update. - checkUpdateCheckTriggered(currentPackageVersions); - CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken(); - - // Increment the clock, but not enough. - mFakeClock.incrementClock(checkDelayMillis - 1); - - // Simulate a reliability trigger. - mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */); - - // Assert the PackageTracker did not trigger an update. - mFakeIntentHelper.assertUpdateNotTriggered(); - checkPackageStorageStatus(PackageStatus.CHECK_STARTED, currentPackageVersions); - mFakeIntentHelper.assertReliabilityTriggerScheduled(); - - // Increment the clock slightly more. Should now consider the response overdue. - mFakeClock.incrementClock(2); - - // Simulate a reliability trigger. - mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */); - - // Triggering should have happened. - checkUpdateCheckTriggered(currentPackageVersions); - CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken(); - - // Check a new token was generated. - assertFalse(token1.equals(token2)); - } - - /** - * Tests what happens when a package update doesn't complete and a reliability trigger cleans - * up for it. - */ - @Test - public void trackingEnabled_reliabilityTrigger_afterPackageUpdateDidNotComplete() - throws Exception { - - // Set up device configuration. - configureTrackingEnabled(); - - int retriesAllowed = 5; - int checkDelayMillis = 5 * 60 * 1000; - configureReliabilityConfigSettings(retriesAllowed, checkDelayMillis); - - PackageVersions currentPackageVersions = new PackageVersions(1, 1); - PackageVersions newPackageVersions = new PackageVersions(2, 2); - - // Simulate there being a newer version installed than the one recorded in storage. - configureValidApplications(currentPackageVersions); - - // Force the storage into a state we want. - mPackageStatusStorage.forceCheckStateForTests( - PackageStatus.CHECK_COMPLETED_SUCCESS, currentPackageVersions); - - // Initialize the package tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Simulate a reliability trigger. - simulatePackageInstallation(newPackageVersions); - - // Assert the PackageTracker did trigger an update. - checkUpdateCheckTriggered(newPackageVersions); - CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken(); - - // Increment the clock, but not enough. - mFakeClock.incrementClock(checkDelayMillis + 1); - - // Simulate a reliability trigger. - mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */); - - // Assert the PackageTracker triggered an update. - checkUpdateCheckTriggered(newPackageVersions); - CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken(); - - // Check a new token was generated. - assertFalse(token1.equals(token2)); - - // Simulate the reliability check completing. - mPackageTracker.recordCheckResult(token2, true /* success */); - - // Check everything is now as it should be. - checkUpdateCheckSuccessful(newPackageVersions); - } - - /** - * Simulates a reliability trigger happening too soon after a package update trigger occurred. - */ - @Test - public void trackingEnabled_reliabilityTriggerAfterUpdate_tooSoon() throws Exception { - // Set up device configuration. - configureTrackingEnabled(); - - int retriesAllowed = 5; - int checkDelayMillis = 5 * 60 * 1000; - configureReliabilityConfigSettings(retriesAllowed, checkDelayMillis); - - PackageVersions currentPackageVersions = new PackageVersions(1, 1); - PackageVersions newPackageVersions = new PackageVersions(2, 2); - - // Simulate there being a newer version installed than the one recorded in storage. - configureValidApplications(currentPackageVersions); - - // Force the storage into a state we want. - mPackageStatusStorage.forceCheckStateForTests( - PackageStatus.CHECK_COMPLETED_SUCCESS, currentPackageVersions); - - // Initialize the package tracker. - assertTrue(mPackageTracker.start()); - - // Check the intent helper is properly configured. - checkIntentHelperInitializedAndReliabilityTrackingEnabled(); - - // Check the initial storage state. - checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, currentPackageVersions); - - // Simulate a package update trigger. - simulatePackageInstallation(newPackageVersions); - - // Assert the PackageTracker did trigger an update. - checkUpdateCheckTriggered(newPackageVersions); - CheckToken token1 = mFakeIntentHelper.captureAndResetLastToken(); - - // Increment the clock, but not enough. - mFakeClock.incrementClock(checkDelayMillis - 1); - - // Simulate a reliability trigger. - mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */); - - // Assert the PackageTracker did not trigger an update. - mFakeIntentHelper.assertUpdateNotTriggered(); - checkPackageStorageStatus(PackageStatus.CHECK_STARTED, newPackageVersions); - mFakeIntentHelper.assertReliabilityTriggerScheduled(); - - // Increment the clock slightly more. Should now consider the response overdue. - mFakeClock.incrementClock(2); - - // Simulate a reliability trigger. - mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */); - - // Triggering should have happened. - checkUpdateCheckTriggered(newPackageVersions); - CheckToken token2 = mFakeIntentHelper.captureAndResetLastToken(); - - // Check a new token was generated. - assertFalse(token1.equals(token2)); - } - - @Test - public void dump() { - StringWriter stringWriter = new StringWriter(); - PrintWriter printWriter = new PrintWriter(stringWriter); - - mPackageTracker.dump(printWriter); - - assertFalse(stringWriter.toString().isEmpty()); - } - - private void simulatePackageInstallation(PackageVersions packageVersions) throws Exception { - configureApplicationsValidManifests(packageVersions); - - // Simulate a tracked package being updated. - mFakeIntentHelper.simulatePackageUpdatedEvent(); - } - - /** - * Checks an update check was triggered, reliability triggering is therefore enabled and the - * storage state reflects that there is a check in progress. - */ - private void checkUpdateCheckTriggered(PackageVersions packageVersions) { - // Assert the PackageTracker attempted to trigger an update. - mFakeIntentHelper.assertUpdateTriggered(); - - // If an update check was triggered reliability triggering should always be enabled to - // ensure that it can be completed if it fails. - mFakeIntentHelper.assertReliabilityTriggerScheduled(); - - // Check the expected storage state. - checkPackageStorageStatus(PackageStatus.CHECK_STARTED, packageVersions); - } - - private void checkUpdateCheckFailed(PackageVersions packageVersions) { - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerScheduled(); - - // Assert the storage was updated. - checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, packageVersions); - } - - private void checkUpdateCheckSuccessful(PackageVersions packageVersions) { - // Check reliability triggering state. - mFakeIntentHelper.assertReliabilityTriggerNotScheduled(); - - // Assert the storage was updated. - checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions); - - // Peek inside the package tracker to make sure it is tracking failure counts properly. - assertEquals(0, mPackageTracker.getCheckFailureCountForTests()); - } - - private PackageVersions configureValidApplications() throws Exception { - configureValidApplications(INITIAL_APP_PACKAGE_VERSIONS); - return INITIAL_APP_PACKAGE_VERSIONS; - } - - private void configureValidApplications(PackageVersions versions) throws Exception { - configureUpdateAppPackageOk(UPDATE_APP_PACKAGE_NAME); - configureDataAppPackageOk(DATA_APP_PACKAGE_NAME); - configureApplicationsValidManifests(versions); - } - - private void configureApplicationsValidManifests(PackageVersions versions) throws Exception { - configureUpdateAppManifestOk(UPDATE_APP_PACKAGE_NAME); - configureDataAppManifestOk(DATA_APP_PACKAGE_NAME); - configureUpdateAppPackageVersion(UPDATE_APP_PACKAGE_NAME, versions.mUpdateAppVersion); - configureDataAppPackageVersion(DATA_APP_PACKAGE_NAME, versions.mDataAppVersion); - } - - private void configureUpdateAppPackageVersion(String updateAppPackageName, - long updataAppPackageVersion) throws Exception { - when(mMockPackageManagerHelper.getInstalledPackageVersion(updateAppPackageName)) - .thenReturn(updataAppPackageVersion); - } - - private void configureDataAppPackageVersion(String dataAppPackageName, - long dataAppPackageVersion) throws Exception { - when(mMockPackageManagerHelper.getInstalledPackageVersion(dataAppPackageName)) - .thenReturn(dataAppPackageVersion); - } - - private void configureUpdateAppManifestOk(String updateAppPackageName) throws Exception { - Intent expectedIntent = RulesUpdaterContract.createUpdaterIntent(updateAppPackageName); - when(mMockPackageManagerHelper.receiverRegistered( - filterEquals(expectedIntent), - eq(RulesUpdaterContract.TRIGGER_TIME_ZONE_RULES_CHECK_PERMISSION))) - .thenReturn(true); - when(mMockPackageManagerHelper.usesPermission( - updateAppPackageName, RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION)) - .thenReturn(true); - } - - private void configureUpdateAppManifestBad(String updateAppPackageName) throws Exception { - Intent expectedIntent = RulesUpdaterContract.createUpdaterIntent(updateAppPackageName); - when(mMockPackageManagerHelper.receiverRegistered( - filterEquals(expectedIntent), - eq(RulesUpdaterContract.TRIGGER_TIME_ZONE_RULES_CHECK_PERMISSION))) - .thenReturn(false); - // Has permission, but that shouldn't matter if the check above is false. - when(mMockPackageManagerHelper.usesPermission( - updateAppPackageName, RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION)) - .thenReturn(true); - } - - private void configureDataAppManifestOk(String dataAppPackageName) throws Exception { - when(mMockPackageManagerHelper.contentProviderRegistered( - TimeZoneRulesDataContract.AUTHORITY, dataAppPackageName)) - .thenReturn(true); - } - - private void configureDataAppManifestBad(String dataAppPackageName) throws Exception { - // Simulate the data app not exposing the content provider we require. - when(mMockPackageManagerHelper.contentProviderRegistered( - TimeZoneRulesDataContract.AUTHORITY, dataAppPackageName)) - .thenReturn(false); - } - - private void configureTrackingEnabled() { - when(mMockConfigHelper.isTrackingEnabled()).thenReturn(true); - } - - private void configureTrackingDisabled() { - when(mMockConfigHelper.isTrackingEnabled()).thenReturn(false); - } - - private void configureReliabilityConfigSettings(int retriesAllowed, int checkDelayMillis) { - when(mMockConfigHelper.getFailedCheckRetryCount()).thenReturn(retriesAllowed); - when(mMockConfigHelper.getCheckTimeAllowedMillis()).thenReturn(checkDelayMillis); - } - - private void configureReliabilityConfigSettingsOk() { - configureReliabilityConfigSettings(5, 5 * 60 * 1000); - } - - private void configureUpdateAppPackageOk(String updateAppPackageName) throws Exception { - when(mMockConfigHelper.getUpdateAppPackageName()).thenReturn(updateAppPackageName); - when(mMockPackageManagerHelper.isPrivilegedApp(updateAppPackageName)).thenReturn(true); - } - - private void configureUpdateAppPackageNotPrivileged(String updateAppPackageName) - throws Exception { - when(mMockConfigHelper.getUpdateAppPackageName()).thenReturn(updateAppPackageName); - when(mMockPackageManagerHelper.isPrivilegedApp(updateAppPackageName)).thenReturn(false); - } - - private void configureUpdateAppPackageNameMissing() { - when(mMockConfigHelper.getUpdateAppPackageName()).thenReturn(null); - } - - private void configureDataAppPackageOk(String dataAppPackageName) throws Exception { - when(mMockConfigHelper.getDataAppPackageName()).thenReturn(dataAppPackageName); - when(mMockPackageManagerHelper.isPrivilegedApp(dataAppPackageName)).thenReturn(true); - } - - private void configureDataAppPackageNotPrivileged(String dataAppPackageName) - throws Exception { - when(mMockConfigHelper.getUpdateAppPackageName()).thenReturn(dataAppPackageName); - when(mMockPackageManagerHelper.isPrivilegedApp(dataAppPackageName)).thenReturn(false); - } - - private void configureDataAppPackageNameMissing() { - when(mMockConfigHelper.getDataAppPackageName()).thenThrow(new RuntimeException()); - } - - private void checkIntentHelperInitializedAndReliabilityTrackingEnabled() { - // Verify that calling start initialized the IntentHelper as well. - mFakeIntentHelper.assertInitialized(UPDATE_APP_PACKAGE_NAME, DATA_APP_PACKAGE_NAME); - - // Assert that reliability tracking is always enabled after initialization. - mFakeIntentHelper.assertReliabilityTriggerScheduled(); - } - - private void checkPackageStorageStatus( - int expectedCheckStatus, PackageVersions expectedPackageVersions) { - PackageStatus packageStatus = mPackageStatusStorage.getPackageStatus(); - assertEquals(expectedCheckStatus, packageStatus.mCheckStatus); - assertEquals(expectedPackageVersions, packageStatus.mVersions); - } - - private void checkPackageStorageStatusIsInitialOrReset() { - assertNull(mPackageStatusStorage.getPackageStatus()); - } - - private static CheckToken createArbitraryCheckToken() { - return new CheckToken(1, INITIAL_APP_PACKAGE_VERSIONS); - } - - /** - * A fake IntentHelper implementation for use in tests. - */ - private static class FakeIntentHelper implements PackageTrackerIntentHelper { - - private PackageTracker mPackageTracker; - private String mUpdateAppPackageName; - private String mDataAppPackageName; - - private CheckToken mLastToken; - - private boolean mReliabilityTriggerScheduled; - - @Override - public void initialize(String updateAppPackageName, String dataAppPackageName, - PackageTracker packageTracker) { - assertNotNull(updateAppPackageName); - assertNotNull(dataAppPackageName); - assertNotNull(packageTracker); - mPackageTracker = packageTracker; - mUpdateAppPackageName = updateAppPackageName; - mDataAppPackageName = dataAppPackageName; - } - - public void assertInitialized( - String expectedUpdateAppPackageName, String expectedDataAppPackageName) { - assertNotNull(mPackageTracker); - assertEquals(expectedUpdateAppPackageName, mUpdateAppPackageName); - assertEquals(expectedDataAppPackageName, mDataAppPackageName); - } - - public void assertNotInitialized() { - assertNull(mPackageTracker); - } - - @Override - public void sendTriggerUpdateCheck(CheckToken checkToken) { - if (mLastToken != null) { - fail("lastToken already set"); - } - mLastToken = checkToken; - } - - @Override - public void scheduleReliabilityTrigger(long minimumDelayMillis) { - mReliabilityTriggerScheduled = true; - } - - @Override - public void unscheduleReliabilityTrigger() { - mReliabilityTriggerScheduled = false; - } - - public void assertReliabilityTriggerScheduled() { - assertTrue(mReliabilityTriggerScheduled); - } - - public void assertReliabilityTriggerNotScheduled() { - assertFalse(mReliabilityTriggerScheduled); - } - - public void assertUpdateTriggered() { - assertNotNull(mLastToken); - } - - public void assertUpdateNotTriggered() { - assertNull(mLastToken); - } - - public CheckToken captureAndResetLastToken() { - CheckToken toReturn = mLastToken; - assertNotNull("No update triggered", toReturn); - mLastToken = null; - return toReturn; - } - - public void simulatePackageUpdatedEvent() { - mPackageTracker.triggerUpdateIfNeeded(true /* packageChanged */); - } - } - - private static class FakeClock extends Clock { - - private long currentTime = 1000; - - @Override - public long millis() { - return currentTime; - } - - public void incrementClock(long millis) { - currentTime += millis; - } - - @Override - public ZoneId getZone() { - throw new UnsupportedOperationException(); - } - - @Override - public Clock withZone(ZoneId zone) { - throw new UnsupportedOperationException(); - } - - @Override - public Instant instant() { - throw new UnsupportedOperationException(); - } - } - - /** - * Registers a mockito parameter matcher that uses {@link Intent#filterEquals(Intent)}. to - * check the parameter against the intent supplied. - */ - private static Intent filterEquals(final Intent expected) { - final Matcher<Intent> m = new BaseMatcher<Intent>() { - @Override - public boolean matches(Object actual) { - return actual != null && expected.filterEquals((Intent) actual); - } - @Override - public void describeTo(Description description) { - description.appendText(expected.toString()); - } - }; - return argThat(m); - } -} diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageVersionsTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageVersionsTest.java deleted file mode 100644 index 9ffc4e97e019..000000000000 --- a/services/tests/servicestests/src/com/android/server/timezone/PackageVersionsTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -@SmallTest -public class PackageVersionsTest { - - @Test - public void equals() { - PackageVersions baseline = - new PackageVersions(1 /* updateAppVersion */, 1 /* dataAppVersion */); - assertEquals(baseline, baseline); - - PackageVersions deepEqual = - new PackageVersions(1 /* updateAppVersion */, 1 /* dataAppVersion */); - assertEquals(baseline, deepEqual); - - PackageVersions differentUpdateAppVersion = - new PackageVersions(2 /* updateAppVersion */, 1 /* dataAppVersion */); - assertFalse(baseline.equals(differentUpdateAppVersion)); - - PackageVersions differentDataAppVersion = - new PackageVersions(1 /* updateAppVersion */, 2 /* dataAppVersion */); - assertFalse(baseline.equals(differentDataAppVersion)); - } -} diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java deleted file mode 100644 index 2c4c4d0ee91f..000000000000 --- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java +++ /dev/null @@ -1,1107 +0,0 @@ -/* - * 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 com.android.server.timezone; - -import static com.android.server.timezone.RulesManagerService.REQUIRED_QUERY_PERMISSION; -import static com.android.server.timezone.RulesManagerService.REQUIRED_UPDATER_PERMISSION; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - -import android.app.timezone.Callback; -import android.app.timezone.DistroRulesVersion; -import android.app.timezone.ICallback; -import android.app.timezone.RulesManager; -import android.app.timezone.RulesState; -import android.os.ParcelFileDescriptor; - -import com.android.i18n.timezone.TzDataSetVersion; -import com.android.timezone.distro.DistroVersion; -import com.android.timezone.distro.StagedDistroOperation; -import com.android.timezone.distro.TimeZoneDistro; -import com.android.timezone.distro.installer.TimeZoneDistroInstaller; - -import libcore.io.IoUtils; - -import org.junit.Before; -import org.junit.Test; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.concurrent.Executor; - -import javax.annotation.Nullable; - -/** - * White box interaction / unit testing of the {@link RulesManagerService}. - */ -public class RulesManagerServiceTest { - - private static final int CURRENT_FORMAT_MAJOR_VERSION = - TzDataSetVersion.currentFormatMajorVersion(); - private static final int CURRENT_FORMAT_MINOR_VERSION = - TzDataSetVersion.currentFormatMinorVersion(); - - private RulesManagerService mRulesManagerService; - - private FakeExecutor mFakeExecutor; - private PermissionHelper mMockPermissionHelper; - private RulesManagerIntentHelper mMockIntentHelper; - private PackageTracker mMockPackageTracker; - private TimeZoneDistroInstaller mMockTimeZoneDistroInstaller; - - @Before - public void setUp() { - mFakeExecutor = new FakeExecutor(); - - mMockPackageTracker = mock(PackageTracker.class); - mMockPermissionHelper = mock(PermissionHelper.class); - mMockIntentHelper = mock(RulesManagerIntentHelper.class); - mMockTimeZoneDistroInstaller = mock(TimeZoneDistroInstaller.class); - - mRulesManagerService = new RulesManagerService( - mMockPermissionHelper, - mFakeExecutor, - mMockIntentHelper, - mMockPackageTracker, - mMockTimeZoneDistroInstaller); - } - - @Test(expected = SecurityException.class) - public void getRulesState_noCallerPermission() throws Exception { - configureCallerDoesNotHaveQueryPermission(); - mRulesManagerService.getRulesState(); - } - - @Test(expected = SecurityException.class) - public void requestInstall_noCallerPermission() throws Exception { - configureCallerDoesNotHaveUpdatePermission(); - mRulesManagerService.requestInstall(null, null, null); - } - - @Test(expected = SecurityException.class) - public void requestUninstall_noCallerPermission() throws Exception { - configureCallerDoesNotHaveUpdatePermission(); - mRulesManagerService.requestUninstall(null, null); - } - - @Test(expected = SecurityException.class) - public void requestNothing_noCallerPermission() throws Exception { - configureCallerDoesNotHaveUpdatePermission(); - mRulesManagerService.requestNothing(null, true); - } - - @Test - public void getRulesState_baseVersionError() throws Exception { - configureDeviceCannotReadBaseVersion(); - - assertNull(mRulesManagerService.getRulesState()); - } - - @Test - public void getRulesState_stagedInstall() throws Exception { - configureCallerHasPermission(); - - configureDeviceBaseVersion("2016a"); - - DistroVersion stagedDistroVersion = new DistroVersion( - CURRENT_FORMAT_MAJOR_VERSION, - CURRENT_FORMAT_MINOR_VERSION - 1, - "2016c", - 3 /* revision */); - configureStagedInstall(stagedDistroVersion); - - DistroVersion installedDistroVersion = new DistroVersion( - CURRENT_FORMAT_MAJOR_VERSION, - CURRENT_FORMAT_MINOR_VERSION - 1, - "2016b", - 4); - configureInstalledDistroVersion(installedDistroVersion); - - DistroRulesVersion stagedDistroRulesVersion = new DistroRulesVersion( - stagedDistroVersion.rulesVersion, stagedDistroVersion.revision); - DistroRulesVersion installedDistroRulesVersion = new DistroRulesVersion( - installedDistroVersion.rulesVersion, installedDistroVersion.revision); - RulesState expectedRuleState = new RulesState( - "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED, - false /* operationInProgress */, - RulesState.STAGED_OPERATION_INSTALL, stagedDistroRulesVersion, - RulesState.DISTRO_STATUS_INSTALLED, installedDistroRulesVersion); - assertEquals(expectedRuleState, mRulesManagerService.getRulesState()); - } - - @Test - public void getRulesState_nothingStaged() throws Exception { - configureCallerHasPermission(); - - configureDeviceBaseVersion("2016a"); - - configureNoStagedOperation(); - - DistroVersion installedDistroVersion = new DistroVersion( - CURRENT_FORMAT_MAJOR_VERSION, - CURRENT_FORMAT_MINOR_VERSION - 1, - "2016b", - 4); - configureInstalledDistroVersion(installedDistroVersion); - - DistroRulesVersion installedDistroRulesVersion = new DistroRulesVersion( - installedDistroVersion.rulesVersion, installedDistroVersion.revision); - RulesState expectedRuleState = new RulesState( - "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED, - false /* operationInProgress */, - RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */, - RulesState.DISTRO_STATUS_INSTALLED, installedDistroRulesVersion); - assertEquals(expectedRuleState, mRulesManagerService.getRulesState()); - } - - @Test - public void getRulesState_uninstallStaged() throws Exception { - configureCallerHasPermission(); - - configureDeviceBaseVersion("2016a"); - - configureStagedUninstall(); - - DistroVersion installedDistroVersion = new DistroVersion( - CURRENT_FORMAT_MAJOR_VERSION, - CURRENT_FORMAT_MINOR_VERSION - 1, - "2016b", - 4); - configureInstalledDistroVersion(installedDistroVersion); - - DistroRulesVersion installedDistroRulesVersion = new DistroRulesVersion( - installedDistroVersion.rulesVersion, installedDistroVersion.revision); - RulesState expectedRuleState = new RulesState( - "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED, - false /* operationInProgress */, - RulesState.STAGED_OPERATION_UNINSTALL, null /* stagedDistroRulesVersion */, - RulesState.DISTRO_STATUS_INSTALLED, installedDistroRulesVersion); - assertEquals(expectedRuleState, mRulesManagerService.getRulesState()); - } - - @Test - public void getRulesState_installedRulesError() throws Exception { - configureCallerHasPermission(); - - String baseRulesVersion = "2016a"; - configureDeviceBaseVersion(baseRulesVersion); - - configureStagedUninstall(); - configureDeviceCannotReadInstalledDistroVersion(); - - RulesState expectedRuleState = new RulesState( - "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED, - false /* operationInProgress */, - RulesState.STAGED_OPERATION_UNINSTALL, null /* stagedDistroRulesVersion */, - RulesState.DISTRO_STATUS_UNKNOWN, null /* installedDistroRulesVersion */); - assertEquals(expectedRuleState, mRulesManagerService.getRulesState()); - } - - @Test - public void getRulesState_stagedRulesError() throws Exception { - configureCallerHasPermission(); - - String baseRulesVersion = "2016a"; - configureDeviceBaseVersion(baseRulesVersion); - - configureDeviceCannotReadStagedDistroOperation(); - - DistroVersion installedDistroVersion = new DistroVersion( - CURRENT_FORMAT_MAJOR_VERSION, - CURRENT_FORMAT_MINOR_VERSION - 1, - "2016b", - 4); - configureInstalledDistroVersion(installedDistroVersion); - - DistroRulesVersion installedDistroRulesVersion = new DistroRulesVersion( - installedDistroVersion.rulesVersion, installedDistroVersion.revision); - RulesState expectedRuleState = new RulesState( - "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED, - false /* operationInProgress */, - RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */, - RulesState.DISTRO_STATUS_INSTALLED, installedDistroRulesVersion); - assertEquals(expectedRuleState, mRulesManagerService.getRulesState()); - } - - @Test - public void getRulesState_noInstalledRules() throws Exception { - configureCallerHasPermission(); - - String baseRulesVersion = "2016a"; - configureDeviceBaseVersion(baseRulesVersion); - configureNoStagedOperation(); - configureInstalledDistroVersion(null); - - RulesState expectedRuleState = new RulesState( - baseRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED, - false /* operationInProgress */, - RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */, - RulesState.DISTRO_STATUS_NONE, null /* installedDistroRulesVersion */); - assertEquals(expectedRuleState, mRulesManagerService.getRulesState()); - } - - @Test - public void getRulesState_operationInProgress() throws Exception { - configureCallerHasPermission(); - - String baseRulesVersion = "2016a"; - String installedRulesVersion = "2016b"; - int revision = 3; - - configureDeviceBaseVersion(baseRulesVersion); - - DistroVersion installedDistroVersion = new DistroVersion( - CURRENT_FORMAT_MAJOR_VERSION, - CURRENT_FORMAT_MINOR_VERSION - 1, - installedRulesVersion, - revision); - configureInstalledDistroVersion(installedDistroVersion); - - ParcelFileDescriptor parcelFileDescriptor = - createParcelFileDescriptor(createArbitraryBytes(1000)); - - // Start an async operation so there is one in progress. The mFakeExecutor won't actually - // execute it. - byte[] tokenBytes = createArbitraryTokenBytes(); - ICallback callback = new StubbedCallback(); - - mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback); - - // Request the rules state while the async operation is "happening". - RulesState actualRulesState = mRulesManagerService.getRulesState(); - DistroRulesVersion expectedInstalledDistroRulesVersion = - new DistroRulesVersion(installedRulesVersion, revision); - RulesState expectedRuleState = new RulesState( - baseRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED, - true /* operationInProgress */, - RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */, - RulesState.DISTRO_STATUS_INSTALLED, expectedInstalledDistroRulesVersion); - assertEquals(expectedRuleState, actualRulesState); - } - - @Test - public void requestInstall_operationInProgress() throws Exception { - configureCallerHasPermission(); - - ParcelFileDescriptor parcelFileDescriptor1 = - createParcelFileDescriptor(createArbitraryBytes(1000)); - - byte[] tokenBytes = createArbitraryTokenBytes(); - ICallback callback = new StubbedCallback(); - - // First request should succeed. - assertEquals(RulesManager.SUCCESS, - mRulesManagerService.requestInstall(parcelFileDescriptor1, tokenBytes, callback)); - - // Something async should be enqueued. Clear it but do not execute it so we can detect the - // second request does nothing. - mFakeExecutor.getAndResetLastCommand(); - - // Second request should fail. - ParcelFileDescriptor parcelFileDescriptor2 = - createParcelFileDescriptor(createArbitraryBytes(1000)); - assertEquals(RulesManager.ERROR_OPERATION_IN_PROGRESS, - mRulesManagerService.requestInstall(parcelFileDescriptor2, tokenBytes, callback)); - - assertClosed(parcelFileDescriptor2); - - // Assert nothing async was enqueued. - mFakeExecutor.assertNothingQueued(); - verifyNoInstallerCallsMade(); - verifyNoPackageTrackerCallsMade(); - verifyNoIntentsSent(); - } - - @Test - public void requestInstall_badToken() throws Exception { - configureCallerHasPermission(); - - ParcelFileDescriptor parcelFileDescriptor = - createParcelFileDescriptor(createArbitraryBytes(1000)); - - byte[] badTokenBytes = new byte[2]; - ICallback callback = new StubbedCallback(); - - try { - mRulesManagerService.requestInstall(parcelFileDescriptor, badTokenBytes, callback); - fail(); - } catch (IllegalArgumentException expected) { - } - - assertClosed(parcelFileDescriptor); - - // Assert nothing async was enqueued. - mFakeExecutor.assertNothingQueued(); - verifyNoInstallerCallsMade(); - verifyNoPackageTrackerCallsMade(); - verifyNoIntentsSent(); - } - - @Test - public void requestInstall_nullParcelFileDescriptor() throws Exception { - configureCallerHasPermission(); - - ParcelFileDescriptor parcelFileDescriptor = null; - byte[] tokenBytes = createArbitraryTokenBytes(); - ICallback callback = new StubbedCallback(); - - try { - mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback); - fail(); - } catch (NullPointerException expected) {} - - // Assert nothing async was enqueued. - mFakeExecutor.assertNothingQueued(); - verifyNoInstallerCallsMade(); - verifyNoPackageTrackerCallsMade(); - verifyNoIntentsSent(); - } - - @Test - public void requestInstall_nullCallback() throws Exception { - configureCallerHasPermission(); - - ParcelFileDescriptor parcelFileDescriptor = - createParcelFileDescriptor(createArbitraryBytes(1000)); - byte[] tokenBytes = createArbitraryTokenBytes(); - ICallback callback = null; - - try { - mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback); - fail(); - } catch (NullPointerException expected) {} - - assertClosed(parcelFileDescriptor); - - // Assert nothing async was enqueued. - mFakeExecutor.assertNothingQueued(); - verifyNoInstallerCallsMade(); - verifyNoPackageTrackerCallsMade(); - verifyNoIntentsSent(); - } - - @Test - public void requestInstall_asyncSuccess() throws Exception { - configureCallerHasPermission(); - - ParcelFileDescriptor parcelFileDescriptor = - createParcelFileDescriptor(createArbitraryBytes(1000)); - - CheckToken token = createArbitraryToken(); - byte[] tokenBytes = token.toByteArray(); - - TestCallback callback = new TestCallback(); - - // Request the install. - assertEquals(RulesManager.SUCCESS, - mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback)); - - // Assert nothing has happened yet. - callback.assertNoResultReceived(); - verifyNoInstallerCallsMade(); - verifyNoPackageTrackerCallsMade(); - verifyNoIntentsSent(); - - // Set up the installer. - configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_SUCCESS); - - // Simulate the async execution. - mFakeExecutor.simulateAsyncExecutionOfLastCommand(); - - assertClosed(parcelFileDescriptor); - - // Verify the expected calls were made to other components. - verifyStageInstallCalled(); - verifyPackageTrackerCalled(token, true /* success */); - verifyStagedOperationIntentSent(); - - // Check the callback was called. - callback.assertResultReceived(Callback.SUCCESS); - } - - @Test - public void requestInstall_nullTokenBytes() throws Exception { - configureCallerHasPermission(); - - ParcelFileDescriptor parcelFileDescriptor = - createParcelFileDescriptor(createArbitraryBytes(1000)); - - TestCallback callback = new TestCallback(); - - // Request the install. - assertEquals(RulesManager.SUCCESS, - mRulesManagerService.requestInstall( - parcelFileDescriptor, null /* tokenBytes */, callback)); - - // Assert nothing has happened yet. - verifyNoInstallerCallsMade(); - callback.assertNoResultReceived(); - verifyNoIntentsSent(); - - // Set up the installer. - configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_SUCCESS); - - // Simulate the async execution. - mFakeExecutor.simulateAsyncExecutionOfLastCommand(); - - assertClosed(parcelFileDescriptor); - - // Verify the expected calls were made to other components. - verifyStageInstallCalled(); - verifyPackageTrackerCalled(null /* expectedToken */, true /* success */); - verifyStagedOperationIntentSent(); - - // Check the callback was received. - callback.assertResultReceived(Callback.SUCCESS); - } - - @Test - public void requestInstall_asyncInstallFail() throws Exception { - configureCallerHasPermission(); - - ParcelFileDescriptor parcelFileDescriptor = - createParcelFileDescriptor(createArbitraryBytes(1000)); - - CheckToken token = createArbitraryToken(); - byte[] tokenBytes = token.toByteArray(); - - TestCallback callback = new TestCallback(); - - // Request the install. - assertEquals(RulesManager.SUCCESS, - mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback)); - - // Assert nothing has happened yet. - verifyNoInstallerCallsMade(); - callback.assertNoResultReceived(); - verifyNoIntentsSent(); - - // Set up the installer. - configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR); - - // Simulate the async execution. - mFakeExecutor.simulateAsyncExecutionOfLastCommand(); - - assertClosed(parcelFileDescriptor); - - // Verify the expected calls were made to other components. - verifyStageInstallCalled(); - - // Validation failure is treated like a successful check: repeating it won't improve things. - boolean expectedSuccess = true; - verifyPackageTrackerCalled(token, expectedSuccess); - - // Nothing should be staged, so no intents sent. - verifyNoIntentsSent(); - - // Check the callback was received. - callback.assertResultReceived(Callback.ERROR_INSTALL_VALIDATION_ERROR); - } - - @Test - public void requestUninstall_operationInProgress() throws Exception { - configureCallerHasPermission(); - - byte[] tokenBytes = createArbitraryTokenBytes(); - ICallback callback = new StubbedCallback(); - - // First request should succeed. - assertEquals(RulesManager.SUCCESS, - mRulesManagerService.requestUninstall(tokenBytes, callback)); - - // Something async should be enqueued. Clear it but do not execute it so we can detect the - // second request does nothing. - mFakeExecutor.getAndResetLastCommand(); - - // Second request should fail. - assertEquals(RulesManager.ERROR_OPERATION_IN_PROGRESS, - mRulesManagerService.requestUninstall(tokenBytes, callback)); - - // Assert nothing async was enqueued. - mFakeExecutor.assertNothingQueued(); - verifyNoInstallerCallsMade(); - verifyNoPackageTrackerCallsMade(); - verifyNoIntentsSent(); - } - - @Test - public void requestUninstall_badToken() throws Exception { - configureCallerHasPermission(); - - byte[] badTokenBytes = new byte[2]; - ICallback callback = new StubbedCallback(); - - try { - mRulesManagerService.requestUninstall(badTokenBytes, callback); - fail(); - } catch (IllegalArgumentException expected) { - } - - // Assert nothing async was enqueued. - mFakeExecutor.assertNothingQueued(); - verifyNoInstallerCallsMade(); - verifyNoPackageTrackerCallsMade(); - verifyNoIntentsSent(); - } - - @Test - public void requestUninstall_nullCallback() throws Exception { - configureCallerHasPermission(); - - byte[] tokenBytes = createArbitraryTokenBytes(); - ICallback callback = null; - - try { - mRulesManagerService.requestUninstall(tokenBytes, callback); - fail(); - } catch (NullPointerException expected) {} - - // Assert nothing async was enqueued. - mFakeExecutor.assertNothingQueued(); - verifyNoInstallerCallsMade(); - verifyNoPackageTrackerCallsMade(); - verifyNoIntentsSent(); - } - - @Test - public void requestUninstall_asyncSuccess() throws Exception { - configureCallerHasPermission(); - - CheckToken token = createArbitraryToken(); - byte[] tokenBytes = token.toByteArray(); - - TestCallback callback = new TestCallback(); - - // Request the uninstall. - assertEquals(RulesManager.SUCCESS, - mRulesManagerService.requestUninstall(tokenBytes, callback)); - - // Assert nothing has happened yet. - callback.assertNoResultReceived(); - verifyNoInstallerCallsMade(); - verifyNoPackageTrackerCallsMade(); - verifyNoIntentsSent(); - - // Set up the installer. - configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_SUCCESS); - - // Simulate the async execution. - mFakeExecutor.simulateAsyncExecutionOfLastCommand(); - - // Verify the expected calls were made to other components. - verifyStageUninstallCalled(); - verifyPackageTrackerCalled(token, true /* success */); - verifyStagedOperationIntentSent(); - - // Check the callback was called. - callback.assertResultReceived(Callback.SUCCESS); - } - - @Test - public void requestUninstall_asyncNothingInstalled() throws Exception { - configureCallerHasPermission(); - - CheckToken token = createArbitraryToken(); - byte[] tokenBytes = token.toByteArray(); - - TestCallback callback = new TestCallback(); - - // Request the uninstall. - assertEquals(RulesManager.SUCCESS, - mRulesManagerService.requestUninstall(tokenBytes, callback)); - - // Assert nothing has happened yet. - callback.assertNoResultReceived(); - verifyNoInstallerCallsMade(); - verifyNoPackageTrackerCallsMade(); - verifyNoIntentsSent(); - - // Set up the installer. - configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED); - - // Simulate the async execution. - mFakeExecutor.simulateAsyncExecutionOfLastCommand(); - - // Verify the expected calls were made to other components. - verifyStageUninstallCalled(); - verifyPackageTrackerCalled(token, true /* success */); - verifyUnstagedOperationIntentSent(); - - // Check the callback was called. - callback.assertResultReceived(Callback.SUCCESS); - } - - @Test - public void requestUninstall_nullTokenBytes() throws Exception { - configureCallerHasPermission(); - - TestCallback callback = new TestCallback(); - - // Request the uninstall. - assertEquals(RulesManager.SUCCESS, - mRulesManagerService.requestUninstall(null /* tokenBytes */, callback)); - - // Assert nothing has happened yet. - verifyNoInstallerCallsMade(); - callback.assertNoResultReceived(); - verifyNoIntentsSent(); - - // Set up the installer. - configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_SUCCESS); - - // Simulate the async execution. - mFakeExecutor.simulateAsyncExecutionOfLastCommand(); - - // Verify the expected calls were made to other components. - verifyStageUninstallCalled(); - verifyPackageTrackerCalled(null /* expectedToken */, true /* success */); - verifyStagedOperationIntentSent(); - - // Check the callback was received. - callback.assertResultReceived(Callback.SUCCESS); - } - - @Test - public void requestUninstall_asyncUninstallFail() throws Exception { - configureCallerHasPermission(); - - CheckToken token = createArbitraryToken(); - byte[] tokenBytes = token.toByteArray(); - - TestCallback callback = new TestCallback(); - - // Request the uninstall. - assertEquals(RulesManager.SUCCESS, - mRulesManagerService.requestUninstall(tokenBytes, callback)); - - // Assert nothing has happened yet. - verifyNoInstallerCallsMade(); - callback.assertNoResultReceived(); - verifyNoIntentsSent(); - - // Set up the installer. - configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_FAIL); - - // Simulate the async execution. - mFakeExecutor.simulateAsyncExecutionOfLastCommand(); - - // Verify the expected calls were made to other components. - verifyStageUninstallCalled(); - verifyPackageTrackerCalled(token, false /* success */); - verifyNoIntentsSent(); - - // Check the callback was received. - callback.assertResultReceived(Callback.ERROR_UNKNOWN_FAILURE); - } - - @Test - public void requestNothing_operationInProgressOk() throws Exception { - configureCallerHasPermission(); - - // Set up a parallel operation. - assertEquals(RulesManager.SUCCESS, - mRulesManagerService.requestUninstall(null, new StubbedCallback())); - // Something async should be enqueued. Clear it but do not execute it to simulate it still - // being in progress. - mFakeExecutor.getAndResetLastCommand(); - - CheckToken token = createArbitraryToken(); - byte[] tokenBytes = token.toByteArray(); - - // Make the call. - mRulesManagerService.requestNothing(tokenBytes, true /* success */); - - // Assert nothing async was enqueued. - mFakeExecutor.assertNothingQueued(); - - // Verify the expected calls were made to other components. - verifyPackageTrackerCalled(token, true /* success */); - verifyNoInstallerCallsMade(); - verifyNoIntentsSent(); - } - - @Test - public void requestNothing_badToken() throws Exception { - configureCallerHasPermission(); - - byte[] badTokenBytes = new byte[2]; - - try { - mRulesManagerService.requestNothing(badTokenBytes, true /* success */); - fail(); - } catch (IllegalArgumentException expected) { - } - - // Assert nothing async was enqueued. - mFakeExecutor.assertNothingQueued(); - - // Assert no other calls were made. - verifyNoInstallerCallsMade(); - verifyNoPackageTrackerCallsMade(); - verifyNoIntentsSent(); - } - - @Test - public void requestNothing() throws Exception { - configureCallerHasPermission(); - - CheckToken token = createArbitraryToken(); - byte[] tokenBytes = token.toByteArray(); - - // Make the call. - mRulesManagerService.requestNothing(tokenBytes, false /* success */); - - // Assert everything required was done. - verifyNoInstallerCallsMade(); - verifyPackageTrackerCalled(token, false /* success */); - verifyNoIntentsSent(); - } - - @Test - public void requestNothing_nullTokenBytes() throws Exception { - configureCallerHasPermission(); - - // Make the call. - mRulesManagerService.requestNothing(null /* tokenBytes */, true /* success */); - - // Assert everything required was done. - verifyNoInstallerCallsMade(); - verifyPackageTrackerCalled(null /* token */, true /* success */); - verifyNoIntentsSent(); - } - - @Test - public void dump_noPermission() throws Exception { - when(mMockPermissionHelper.checkDumpPermission(any(String.class), any(PrintWriter.class))) - .thenReturn(false); - - doDumpCallAndCapture(mRulesManagerService, null); - verifyZeroInteractions(mMockPackageTracker, mMockTimeZoneDistroInstaller); - } - - @Test - public void dump_emptyArgs() throws Exception { - doSuccessfulDumpCall(mRulesManagerService, new String[0]); - - // Verify the package tracker was consulted. - verify(mMockPackageTracker).dump(any(PrintWriter.class)); - } - - @Test - public void dump_nullArgs() throws Exception { - doSuccessfulDumpCall(mRulesManagerService, null); - // Verify the package tracker was consulted. - verify(mMockPackageTracker).dump(any(PrintWriter.class)); - } - - @Test - public void dump_unknownArgs() throws Exception { - String dumpedTextUnknownArgs = doSuccessfulDumpCall( - mRulesManagerService, new String[] { "foo", "bar"}); - - // Verify the package tracker was consulted. - verify(mMockPackageTracker).dump(any(PrintWriter.class)); - - String dumpedTextZeroArgs = doSuccessfulDumpCall(mRulesManagerService, null); - assertEquals(dumpedTextZeroArgs, dumpedTextUnknownArgs); - } - - @Test - public void dump_formatState() throws Exception { - // Just expect these to not throw exceptions, not return nothing, and not interact with the - // package tracker. - doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("p")); - doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("s")); - doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("c")); - doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("i")); - doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("o")); - doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("t")); - doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("a")); - doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("z" /* Unknown */)); - doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("piscotz")); - - verifyZeroInteractions(mMockPackageTracker); - } - - private static String[] dumpFormatArgs(String argsString) { - return new String[] { "-format_state", argsString}; - } - - private String doSuccessfulDumpCall(RulesManagerService rulesManagerService, String[] args) - throws Exception { - when(mMockPermissionHelper.checkDumpPermission(any(String.class), any(PrintWriter.class))) - .thenReturn(true); - - // Set up the mocks to return (arbitrary) information about the current device state. - TzDataSetVersion baseVersion = new TzDataSetVersion( - CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, "2017a", - 1 /* revision */); - when(mMockTimeZoneDistroInstaller.readBaseVersion()).thenReturn(baseVersion); - DistroVersion installedDistroVersion = new DistroVersion( - CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, "2017b", - 4 /* revision */); - when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion()) - .thenReturn(installedDistroVersion); - DistroVersion stagedDistroVersion = new DistroVersion( - CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, "2017c", - 7 /* revision */); - when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()).thenReturn( - StagedDistroOperation.install(stagedDistroVersion)); - - // Do the dump call. - String dumpedOutput = doDumpCallAndCapture(rulesManagerService, args); - - assertFalse(dumpedOutput.isEmpty()); - - return dumpedOutput; - } - - private static String doDumpCallAndCapture( - RulesManagerService rulesManagerService, String[] args) throws IOException { - File file = File.createTempFile("dump", null); - try { - try (FileOutputStream fos = new FileOutputStream(file)) { - FileDescriptor fd = fos.getFD(); - rulesManagerService.dump(fd, args); - } - return IoUtils.readFileAsString(file.getAbsolutePath()); - } finally { - file.delete(); - } - } - - private void verifyNoPackageTrackerCallsMade() { - verifyNoMoreInteractions(mMockPackageTracker); - reset(mMockPackageTracker); - } - - private void verifyPackageTrackerCalled( - CheckToken expectedCheckToken, boolean expectedSuccess) { - verify(mMockPackageTracker).recordCheckResult(expectedCheckToken, expectedSuccess); - reset(mMockPackageTracker); - } - - private void verifyNoIntentsSent() { - verifyNoMoreInteractions(mMockIntentHelper); - reset(mMockIntentHelper); - } - - private void verifyStagedOperationIntentSent() { - verify(mMockIntentHelper).sendTimeZoneOperationStaged(); - reset(mMockIntentHelper); - } - - private void verifyUnstagedOperationIntentSent() { - verify(mMockIntentHelper).sendTimeZoneOperationUnstaged(); - reset(mMockIntentHelper); - } - - private void configureCallerHasPermission() throws Exception { - doNothing() - .when(mMockPermissionHelper) - .enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION); - } - - private void configureCallerDoesNotHaveUpdatePermission() { - doThrow(new SecurityException("Simulated permission failure")) - .when(mMockPermissionHelper) - .enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION); - } - - private void configureCallerDoesNotHaveQueryPermission() { - doThrow(new SecurityException("Simulated permission failure")) - .when(mMockPermissionHelper) - .enforceCallerHasPermission(REQUIRED_QUERY_PERMISSION); - } - - private void configureStageInstallExpectation(int resultCode) - throws Exception { - when(mMockTimeZoneDistroInstaller.stageInstallWithErrorCode(any(TimeZoneDistro.class))) - .thenReturn(resultCode); - } - - private void configureStageUninstallExpectation(int resultCode) throws Exception { - doReturn(resultCode).when(mMockTimeZoneDistroInstaller).stageUninstall(); - } - - private void verifyStageInstallCalled() throws Exception { - verify(mMockTimeZoneDistroInstaller).stageInstallWithErrorCode(any(TimeZoneDistro.class)); - verifyNoMoreInteractions(mMockTimeZoneDistroInstaller); - reset(mMockTimeZoneDistroInstaller); - } - - private void verifyStageUninstallCalled() throws Exception { - verify(mMockTimeZoneDistroInstaller).stageUninstall(); - verifyNoMoreInteractions(mMockTimeZoneDistroInstaller); - reset(mMockTimeZoneDistroInstaller); - } - - private void verifyNoInstallerCallsMade() { - verifyNoMoreInteractions(mMockTimeZoneDistroInstaller); - reset(mMockTimeZoneDistroInstaller); - } - - private static byte[] createArbitraryBytes(int length) { - byte[] bytes = new byte[length]; - for (int i = 0; i < length; i++) { - bytes[i] = (byte) i; - } - return bytes; - } - - private byte[] createArbitraryTokenBytes() { - return createArbitraryToken().toByteArray(); - } - - private CheckToken createArbitraryToken() { - return new CheckToken(1, new PackageVersions(1, 1)); - } - - private void configureDeviceBaseVersion(String baseRulesVersion) throws Exception { - TzDataSetVersion tzDataSetVersion = new TzDataSetVersion( - CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, baseRulesVersion, - 1 /* revision */); - when(mMockTimeZoneDistroInstaller.readBaseVersion()).thenReturn(tzDataSetVersion); - } - - private void configureInstalledDistroVersion(@Nullable DistroVersion installedDistroVersion) - throws Exception { - when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion()) - .thenReturn(installedDistroVersion); - } - - private void configureStagedInstall(DistroVersion stagedDistroVersion) throws Exception { - when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()) - .thenReturn(StagedDistroOperation.install(stagedDistroVersion)); - } - - private void configureStagedUninstall() throws Exception { - when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()) - .thenReturn(StagedDistroOperation.uninstall()); - } - - private void configureNoStagedOperation() throws Exception { - when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()).thenReturn(null); - } - - private void configureDeviceCannotReadStagedDistroOperation() throws Exception { - when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()) - .thenThrow(new IOException("Simulated failure")); - } - - private void configureDeviceCannotReadBaseVersion() throws Exception { - when(mMockTimeZoneDistroInstaller.readBaseVersion()) - .thenThrow(new IOException("Simulated failure")); - } - - private void configureDeviceCannotReadInstalledDistroVersion() throws Exception { - when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion()) - .thenThrow(new IOException("Simulated failure")); - } - - private static void assertClosed(ParcelFileDescriptor parcelFileDescriptor) { - assertFalse(parcelFileDescriptor.getFileDescriptor().valid()); - } - - private static class FakeExecutor implements Executor { - - private Runnable mLastCommand; - - @Override - public void execute(Runnable command) { - assertNull(mLastCommand); - assertNotNull(command); - mLastCommand = command; - } - - public Runnable getAndResetLastCommand() { - assertNotNull(mLastCommand); - Runnable toReturn = mLastCommand; - mLastCommand = null; - return toReturn; - } - - public void simulateAsyncExecutionOfLastCommand() { - Runnable toRun = getAndResetLastCommand(); - toRun.run(); - } - - public void assertNothingQueued() { - assertNull(mLastCommand); - } - } - - private static class TestCallback extends ICallback.Stub { - - private boolean mOnFinishedCalled; - private int mLastError; - - @Override - public void onFinished(int error) { - assertFalse(mOnFinishedCalled); - mOnFinishedCalled = true; - mLastError = error; - } - - public void assertResultReceived(int expectedResult) { - assertTrue(mOnFinishedCalled); - assertEquals(expectedResult, mLastError); - } - - public void assertNoResultReceived() { - assertFalse(mOnFinishedCalled); - } - } - - private static class StubbedCallback extends ICallback.Stub { - @Override - public void onFinished(int error) { - fail("Unexpected call"); - } - } - - private static ParcelFileDescriptor createParcelFileDescriptor(byte[] bytes) - throws IOException { - File file = File.createTempFile("pfd", null); - try (FileOutputStream fos = new FileOutputStream(file)) { - fos.write(bytes); - } - ParcelFileDescriptor pfd = - ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); - // This should now be safe to delete. The ParcelFileDescriptor has an open fd. - file.delete(); - return pfd; - } -} |