Merge "Parcel: better debug recycle"
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java
index a27e16a..279681b 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java
@@ -51,7 +51,7 @@
@Parameterized.Parameter(0)
public Algorithm mAlgorithm;
- public String mProvider = "AndroidSSL";
+ public String mProvider = "AndroidOpenSSL";
private static final int DATA_SIZE = 8192;
private static final byte[] DATA = new byte[DATA_SIZE];
diff --git a/api/Android.bp b/api/Android.bp
index 69d602a..3fc3991 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -188,6 +188,37 @@
"$(location :frameworks-base-api-module-lib-current.txt)",
}
+// This produces the same annotations.zip as framework-doc-stubs, but by using
+// outputs from individual modules instead of all the source code.
+genrule_defaults {
+ name: "sdk-annotations-defaults",
+ out: ["annotations.zip"],
+ tools: [
+ "merge_annotation_zips",
+ "soong_zip",
+ ],
+ cmd: "$(location merge_annotation_zips) $(genDir)/out $(in) && " +
+ "$(location soong_zip) -o $(out) -C $(genDir)/out -D $(genDir)/out",
+}
+
+genrule {
+ name: "sdk-annotations.zip",
+ defaults: ["sdk-annotations-defaults"],
+ srcs: [
+ ":android-non-updatable-doc-stubs{.annotations.zip}",
+ ":all-modules-public-annotations",
+ ],
+}
+
+genrule {
+ name: "sdk-annotations-system.zip",
+ defaults: ["sdk-annotations-defaults"],
+ srcs: [
+ ":android-non-updatable-doc-stubs-system{.annotations.zip}",
+ ":all-modules-system-annotations",
+ ],
+}
+
genrule {
name: "combined-removed-dex",
visibility: [
diff --git a/api/api.go b/api/api.go
index 93f1354..bf6b085 100644
--- a/api/api.go
+++ b/api/api.go
@@ -148,17 +148,18 @@
ctx.CreateModule(genrule.GenRuleFactory, &props)
}
-// This produces the same annotations.zip as framework-doc-stubs, but by using
-// outputs from individual modules instead of all the source code.
-func createMergedAnnotations(ctx android.LoadHookContext, modules []string) {
- props := genruleProps{}
- props.Name = proptools.StringPtr("sdk-annotations.zip")
- props.Tools = []string{"merge_annotation_zips", "soong_zip"}
- props.Out = []string{"annotations.zip"}
- props.Cmd = proptools.StringPtr("$(location merge_annotation_zips) $(genDir)/out $(in) && " +
- "$(location soong_zip) -o $(out) -C $(genDir)/out -D $(genDir)/out")
- props.Srcs = append([]string{":android-non-updatable-doc-stubs{.annotations.zip}"}, createSrcs(modules, "{.public.annotations.zip}")...)
- ctx.CreateModule(genrule.GenRuleFactory, &props)
+func createMergedPublicAnnotationsFilegroup(ctx android.LoadHookContext, modules []string) {
+ props := fgProps{}
+ props.Name = proptools.StringPtr("all-modules-public-annotations")
+ props.Srcs = createSrcs(modules, "{.public.annotations.zip}")
+ ctx.CreateModule(android.FileGroupFactory, &props)
+}
+
+func createMergedSystemAnnotationsFilegroup(ctx android.LoadHookContext, modules []string) {
+ props := fgProps{}
+ props.Name = proptools.StringPtr("all-modules-system-annotations")
+ props.Srcs = createSrcs(modules, "{.system.annotations.zip}")
+ ctx.CreateModule(android.FileGroupFactory, &props)
}
func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) {
@@ -299,7 +300,8 @@
createMergedFrameworkModuleLibStubs(ctx, bootclasspath)
createMergedFrameworkImpl(ctx, bootclasspath)
- createMergedAnnotations(ctx, bootclasspath)
+ createMergedPublicAnnotationsFilegroup(ctx, bootclasspath)
+ createMergedSystemAnnotationsFilegroup(ctx, bootclasspath)
createFilteredApiVersions(ctx, bootclasspath)
diff --git a/core/java/android/debug/AdbManagerInternal.java b/core/java/android/debug/AdbManagerInternal.java
index d730129..e448706 100644
--- a/core/java/android/debug/AdbManagerInternal.java
+++ b/core/java/android/debug/AdbManagerInternal.java
@@ -55,6 +55,12 @@
public abstract File getAdbTempKeysFile();
/**
+ * Notify the AdbManager that the key files have changed and any in-memory state should be
+ * reloaded.
+ */
+ public abstract void notifyKeyFilesUpdated();
+
+ /**
* Starts adbd for a transport.
*/
public abstract void startAdbdForTransport(byte transportType);
diff --git a/core/java/android/hardware/radio/OWNERS b/core/java/android/hardware/radio/OWNERS
index ea4421e..d2bdd64 100644
--- a/core/java/android/hardware/radio/OWNERS
+++ b/core/java/android/hardware/radio/OWNERS
@@ -1,2 +1,3 @@
-twasilczyk@google.com
-randolphs@google.com
+xuweilin@google.com
+oscarazu@google.com
+keunyoung@google.com
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 43ea2e7..a52c989 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -133,6 +133,9 @@
/**
* Flag requesting vibration effect to be played even under limited interruptions.
+ *
+ * <p>Only privileged apps can ignore user settings that limit interruptions, and this
+ * flag will be ignored otherwise.
*/
public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 0x1;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 134cca1..595fd4a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16400,7 +16400,7 @@
public @interface SyncDisabledMode {}
/**
- * Sync is not not disabled.
+ * Sync is not disabled.
*
* @hide
*/
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ce212e2..34330b2 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -160,7 +160,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 200;
+ static final int VERSION = 201;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -929,6 +929,28 @@
screenBrightnessTimers[i].reset(false, elapsedRealtimeUs);
}
}
+
+ /**
+ * Write data to summary parcel
+ */
+ public void writeSummaryToParcel(Parcel out, long elapsedRealtimeUs) {
+ screenOnTimer.writeSummaryFromParcelLocked(out, elapsedRealtimeUs);
+ screenDozeTimer.writeSummaryFromParcelLocked(out, elapsedRealtimeUs);
+ for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; i++) {
+ screenBrightnessTimers[i].writeSummaryFromParcelLocked(out, elapsedRealtimeUs);
+ }
+ }
+
+ /**
+ * Read data from summary parcel
+ */
+ public void readSummaryFromParcel(Parcel in) {
+ screenOnTimer.readSummaryFromParcelLocked(in);
+ screenDozeTimer.readSummaryFromParcelLocked(in);
+ for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; i++) {
+ screenBrightnessTimers[i].readSummaryFromParcelLocked(in);
+ }
+ }
}
DisplayBatteryStats[] mPerDisplayBatteryStats;
@@ -15636,6 +15658,10 @@
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].readSummaryFromParcelLocked(in);
}
+ final int numDisplays = in.readInt();
+ for (int i = 0; i < numDisplays; i++) {
+ mPerDisplayBatteryStats[i].readSummaryFromParcel(in);
+ }
mInteractive = false;
mInteractiveTimer.readSummaryFromParcelLocked(in);
mPhoneOn = false;
@@ -16135,6 +16161,11 @@
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
}
+ final int numDisplays = mPerDisplayBatteryStats.length;
+ out.writeInt(numDisplays);
+ for (int i = 0; i < numDisplays; i++) {
+ mPerDisplayBatteryStats[i].writeSummaryToParcel(out, NOWREAL_SYS);
+ }
mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mPowerSaveModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
out.writeLong(mLongestLightIdleTimeMs);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 51939b2..682dbc31 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -381,6 +381,7 @@
// (e.g. gDefaultServiceManager)
"libbinder",
"libhidlbase", // libhwbinder is in here
+ "libvintf",
],
},
},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d2c4b64..5a5d7e8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6325,10 +6325,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 160376c..cd7f99f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1644,49 +1644,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 5894bdc..5c95721 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/core/tests/BroadcastRadioTests/OWNERS b/core/tests/BroadcastRadioTests/OWNERS
index 3e360e7..d2bdd64 100644
--- a/core/tests/BroadcastRadioTests/OWNERS
+++ b/core/tests/BroadcastRadioTests/OWNERS
@@ -1,3 +1,3 @@
-keunyoung@google.com
+xuweilin@google.com
oscarazu@google.com
-twasilczyk@google.com
+keunyoung@google.com
diff --git a/data/keyboards/Vendor_054c_Product_0ce6.kl b/data/keyboards/Vendor_054c_Product_0ce6.kl
index 4d51a9e..411dd95 100644
--- a/data/keyboards/Vendor_054c_Product_0ce6.kl
+++ b/data/keyboards/Vendor_054c_Product_0ce6.kl
@@ -16,6 +16,8 @@
# Sony Playstation(R) DualSense Controller
#
+# Only use this key layout if we have HID_PLAYSTATION!
+requires_kernel_config CONFIG_HID_PLAYSTATION
# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
diff --git a/data/keyboards/Vendor_054c_Product_0ce6_fallback.kl b/data/keyboards/Vendor_054c_Product_0ce6_fallback.kl
new file mode 100644
index 0000000..d1a364c
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ce6_fallback.kl
@@ -0,0 +1,75 @@
+# Copyright (C) 2022 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.
+
+#
+# Sony Playstation(R) DualSense Controller
+#
+
+# Use this if HID_PLAYSTATION is not available
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+# Square
+key 304 BUTTON_X
+# Cross
+key 305 BUTTON_A
+# Circle
+key 306 BUTTON_B
+# Triangle
+key 307 BUTTON_Y
+
+key 308 BUTTON_L1
+key 309 BUTTON_R1
+key 310 BUTTON_L2
+key 311 BUTTON_R2
+
+# L2 axis
+axis 0x03 LTRIGGER
+# R2 axis
+axis 0x04 RTRIGGER
+
+# Left Analog Stick
+axis 0x00 X
+axis 0x01 Y
+# Right Analog Stick
+axis 0x02 Z
+axis 0x05 RZ
+
+# Left stick click
+key 314 BUTTON_THUMBL
+# Right stick click
+key 315 BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Share / "half-sun"
+key 312 BUTTON_SELECT
+# Options / three horizontal lines
+key 313 BUTTON_START
+# PS key
+key 316 BUTTON_MODE
+
+# Touchpad press
+key 317 BUTTON_1
+
+# SENSORs
+sensor 0x00 ACCELEROMETER X
+sensor 0x01 ACCELEROMETER Y
+sensor 0x02 ACCELEROMETER Z
+sensor 0x03 GYROSCOPE X
+sensor 0x04 GYROSCOPE Y
+sensor 0x05 GYROSCOPE Z
diff --git a/packages/StatementService/OWNERS b/packages/StatementService/OWNERS
new file mode 100644
index 0000000..f0b4ce7
--- /dev/null
+++ b/packages/StatementService/OWNERS
@@ -0,0 +1,2 @@
+include /PACKAGE_MANAGER_OWNERS
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index 9ae7ea2..4578a61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -110,7 +110,7 @@
@Override
public void onMobileStatusChanged(boolean updateTelephony,
MobileStatus mobileStatus) {
- if (Log.isLoggable(mTag, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(mTag, "onMobileStatusChanged="
+ " updateTelephony=" + updateTelephony
+ " mobileStatus=" + mobileStatus.toString());
@@ -719,7 +719,7 @@
* This will call listeners if necessary.
*/
private void updateTelephony() {
- if (Log.isLoggable(mTag, Log.DEBUG)) {
+ if (DEBUG) {
Log.d(mTag, "updateTelephonySignalStrength: hasService="
+ mCurrentState.isInService()
+ " ss=" + mCurrentState.signalStrength
diff --git a/services/core/Android.bp b/services/core/Android.bp
index e4a0097..797df5a4 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -132,8 +132,6 @@
],
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 f4249f8..a04d5e7 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.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 @@
ProtoOutputStream proto = null;
DebugInfo i18nLibraryDebugInfo = I18nModuleDebug.getDebugInfo();
- addTimeZoneApkDebugInfo(i18nLibraryDebugInfo);
if (protoFormat) {
proto = new ProtoOutputStream(fd);
@@ -86,20 +77,6 @@
}
/**
- * 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 @@
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/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 297d28d..56990ed 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -19,7 +19,7 @@
import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
import android.annotation.NonNull;
-import android.annotation.TestApi;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -102,11 +102,26 @@
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * Provides communication to the Android Debug Bridge daemon to allow, deny, or clear public keysi
+ * Provides communication to the Android Debug Bridge daemon to allow, deny, or clear public keys
* that are authorized to connect to the ADB service itself.
+ *
+ * <p>The AdbDebuggingManager controls two files:
+ * <ol>
+ * <li>adb_keys
+ * <li>adb_temp_keys.xml
+ * </ol>
+ *
+ * <p>The ADB Daemon (adbd) reads <em>only</em> the adb_keys file for authorization. Public keys
+ * from registered hosts are stored in adb_keys, one entry per line.
+ *
+ * <p>AdbDebuggingManager also keeps adb_temp_keys.xml, which is used for two things
+ * <ol>
+ * <li>Removing unused keys from the adb_keys file
+ * <li>Managing authorized WiFi access points for ADB over WiFi
+ * </ol>
*/
public class AdbDebuggingManager {
- private static final String TAG = "AdbDebuggingManager";
+ private static final String TAG = AdbDebuggingManager.class.getSimpleName();
private static final boolean DEBUG = false;
private static final boolean MDNS_DEBUG = false;
@@ -118,18 +133,20 @@
// as a subsequent connection occurs within the allowed duration.
private static final String ADB_TEMP_KEYS_FILE = "adb_temp_keys.xml";
private static final int BUFFER_SIZE = 65536;
+ private static final Ticker SYSTEM_TICKER = () -> System.currentTimeMillis();
private final Context mContext;
private final ContentResolver mContentResolver;
- private final Handler mHandler;
- private AdbDebuggingThread mThread;
+ @VisibleForTesting final AdbDebuggingHandler mHandler;
+ @Nullable private AdbDebuggingThread mThread;
private boolean mAdbUsbEnabled = false;
private boolean mAdbWifiEnabled = false;
private String mFingerprints;
// A key can be used more than once (e.g. USB, wifi), so need to keep a refcount
- private final Map<String, Integer> mConnectedKeys;
- private String mConfirmComponent;
- private final File mTestUserKeyFile;
+ private final Map<String, Integer> mConnectedKeys = new HashMap<>();
+ private final String mConfirmComponent;
+ @Nullable private final File mUserKeyFile;
+ @Nullable private final File mTempKeysFile;
private static final String WIFI_PERSISTENT_CONFIG_PROPERTY =
"persist.adb.tls_server.enable";
@@ -138,37 +155,44 @@
private static final int PAIRING_CODE_LENGTH = 6;
private PairingThread mPairingThread = null;
// A list of keys connected via wifi
- private final Set<String> mWifiConnectedKeys;
+ private final Set<String> mWifiConnectedKeys = new HashSet<>();
// The current info of the adbwifi connection.
- private AdbConnectionInfo mAdbConnectionInfo;
+ private AdbConnectionInfo mAdbConnectionInfo = new AdbConnectionInfo();
// Polls for a tls port property when adb wifi is enabled
private AdbConnectionPortPoller mConnectionPortPoller;
private final PortListenerImpl mPortListener = new PortListenerImpl();
+ private final Ticker mTicker;
public AdbDebuggingManager(Context context) {
- mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
- mContext = context;
- mContentResolver = mContext.getContentResolver();
- mTestUserKeyFile = null;
- mConnectedKeys = new HashMap<String, Integer>();
- mWifiConnectedKeys = new HashSet<String>();
- mAdbConnectionInfo = new AdbConnectionInfo();
+ this(
+ context,
+ /* confirmComponent= */ null,
+ getAdbFile(ADB_KEYS_FILE),
+ getAdbFile(ADB_TEMP_KEYS_FILE),
+ /* adbDebuggingThread= */ null,
+ SYSTEM_TICKER);
}
/**
* Constructor that accepts the component to be invoked to confirm if the user wants to allow
* an adb connection from the key.
*/
- @TestApi
- protected AdbDebuggingManager(Context context, String confirmComponent, File testUserKeyFile) {
- mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
+ @VisibleForTesting
+ AdbDebuggingManager(
+ Context context,
+ String confirmComponent,
+ File testUserKeyFile,
+ File tempKeysFile,
+ AdbDebuggingThread adbDebuggingThread,
+ Ticker ticker) {
mContext = context;
mContentResolver = mContext.getContentResolver();
mConfirmComponent = confirmComponent;
- mTestUserKeyFile = testUserKeyFile;
- mConnectedKeys = new HashMap<String, Integer>();
- mWifiConnectedKeys = new HashSet<String>();
- mAdbConnectionInfo = new AdbConnectionInfo();
+ mUserKeyFile = testUserKeyFile;
+ mTempKeysFile = tempKeysFile;
+ mThread = adbDebuggingThread;
+ mTicker = ticker;
+ mHandler = new AdbDebuggingHandler(FgThread.get().getLooper(), mThread);
}
static void sendBroadcastWithDebugPermission(@NonNull Context context, @NonNull Intent intent,
@@ -189,8 +213,7 @@
// consisting of only letters, digits, and hyphens, must begin and end
// with a letter or digit, must not contain consecutive hyphens, and
// must contain at least one letter.
- @VisibleForTesting
- static final String SERVICE_PROTOCOL = "adb-tls-pairing";
+ @VisibleForTesting static final String SERVICE_PROTOCOL = "adb-tls-pairing";
private final String mServiceType = String.format("_%s._tcp.", SERVICE_PROTOCOL);
private int mPort;
@@ -352,16 +375,24 @@
}
}
- class AdbDebuggingThread extends Thread {
+ @VisibleForTesting
+ static class AdbDebuggingThread extends Thread {
private boolean mStopped;
private LocalSocket mSocket;
private OutputStream mOutputStream;
private InputStream mInputStream;
+ private Handler mHandler;
+ @VisibleForTesting
AdbDebuggingThread() {
super(TAG);
}
+ @VisibleForTesting
+ void setHandler(Handler handler) {
+ mHandler = handler;
+ }
+
@Override
public void run() {
if (DEBUG) Slog.d(TAG, "Entering thread");
@@ -536,7 +567,7 @@
}
}
- class AdbConnectionInfo {
+ private static class AdbConnectionInfo {
private String mBssid;
private String mSsid;
private int mPort;
@@ -743,11 +774,14 @@
// Notification when adbd socket is disconnected.
static final int MSG_ADBD_SOCKET_DISCONNECTED = 27;
+ // === Messages from other parts of the system
+ private static final int MESSAGE_KEY_FILES_UPDATED = 28;
+
// === Messages we can send to adbd ===========
static final String MSG_DISCONNECT_DEVICE = "DD";
static final String MSG_DISABLE_ADBDWIFI = "DA";
- private AdbKeyStore mAdbKeyStore;
+ @Nullable @VisibleForTesting AdbKeyStore mAdbKeyStore;
// Usb, Wi-Fi transports can be enabled together or separately, so don't break the framework
// connection unless all transport types are disconnected.
@@ -762,19 +796,19 @@
}
};
- AdbDebuggingHandler(Looper looper) {
- super(looper);
- }
-
- /**
- * Constructor that accepts the AdbDebuggingThread to which responses should be sent
- * and the AdbKeyStore to be used to store the temporary grants.
- */
- @TestApi
- AdbDebuggingHandler(Looper looper, AdbDebuggingThread thread, AdbKeyStore adbKeyStore) {
+ /** Constructor that accepts the AdbDebuggingThread to which responses should be sent. */
+ @VisibleForTesting
+ AdbDebuggingHandler(Looper looper, AdbDebuggingThread thread) {
super(looper);
mThread = thread;
- mAdbKeyStore = adbKeyStore;
+ }
+
+ /** Initialize the AdbKeyStore so tests can grab mAdbKeyStore immediately. */
+ @VisibleForTesting
+ void initKeyStore() {
+ if (mAdbKeyStore == null) {
+ mAdbKeyStore = new AdbKeyStore();
+ }
}
// Show when at least one device is connected.
@@ -805,6 +839,7 @@
registerForAuthTimeChanges();
mThread = new AdbDebuggingThread();
+ mThread.setHandler(mHandler);
mThread.start();
mAdbKeyStore.updateKeyStore();
@@ -825,8 +860,7 @@
if (!mConnectedKeys.isEmpty()) {
for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
- mAdbKeyStore.setLastConnectionTime(entry.getKey(),
- System.currentTimeMillis());
+ mAdbKeyStore.setLastConnectionTime(entry.getKey(), mTicker.currentTimeMillis());
}
sendPersistKeyStoreMessage();
mConnectedKeys.clear();
@@ -836,9 +870,7 @@
}
public void handleMessage(Message msg) {
- if (mAdbKeyStore == null) {
- mAdbKeyStore = new AdbKeyStore();
- }
+ initKeyStore();
switch (msg.what) {
case MESSAGE_ADB_ENABLED:
@@ -873,7 +905,7 @@
if (!mConnectedKeys.containsKey(key)) {
mConnectedKeys.put(key, 1);
}
- mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
+ mAdbKeyStore.setLastConnectionTime(key, mTicker.currentTimeMillis());
sendPersistKeyStoreMessage();
scheduleJobToUpdateAdbKeyStore();
}
@@ -920,9 +952,7 @@
mConnectedKeys.clear();
// If the key store has not yet been instantiated then do so now; this avoids
// the unnecessary creation of the key store when adb is not enabled.
- if (mAdbKeyStore == null) {
- mAdbKeyStore = new AdbKeyStore();
- }
+ initKeyStore();
mWifiConnectedKeys.clear();
mAdbKeyStore.deleteKeyStore();
cancelJobToUpdateAdbKeyStore();
@@ -937,7 +967,8 @@
alwaysAllow = true;
int refcount = mConnectedKeys.get(key) - 1;
if (refcount == 0) {
- mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
+ mAdbKeyStore.setLastConnectionTime(
+ key, mTicker.currentTimeMillis());
sendPersistKeyStoreMessage();
scheduleJobToUpdateAdbKeyStore();
mConnectedKeys.remove(key);
@@ -963,7 +994,7 @@
if (!mConnectedKeys.isEmpty()) {
for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
mAdbKeyStore.setLastConnectionTime(entry.getKey(),
- System.currentTimeMillis());
+ mTicker.currentTimeMillis());
}
sendPersistKeyStoreMessage();
scheduleJobToUpdateAdbKeyStore();
@@ -984,7 +1015,7 @@
} else {
mConnectedKeys.put(key, mConnectedKeys.get(key) + 1);
}
- mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
+ mAdbKeyStore.setLastConnectionTime(key, mTicker.currentTimeMillis());
sendPersistKeyStoreMessage();
scheduleJobToUpdateAdbKeyStore();
logAdbConnectionChanged(key, AdbProtoEnums.AUTOMATICALLY_ALLOWED, true);
@@ -1206,6 +1237,10 @@
}
break;
}
+ case MESSAGE_KEY_FILES_UPDATED: {
+ mAdbKeyStore.reloadKeyMap();
+ break;
+ }
}
}
@@ -1377,8 +1412,7 @@
AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent,
UserHandle.ALL);
// Add the key into the keystore
- mAdbKeyStore.setLastConnectionTime(publicKey,
- System.currentTimeMillis());
+ mAdbKeyStore.setLastConnectionTime(publicKey, mTicker.currentTimeMillis());
sendPersistKeyStoreMessage();
scheduleJobToUpdateAdbKeyStore();
}
@@ -1449,19 +1483,13 @@
extras.add(new AbstractMap.SimpleEntry<String, String>("ssid", ssid));
extras.add(new AbstractMap.SimpleEntry<String, String>("bssid", bssid));
int currentUserId = ActivityManager.getCurrentUser();
- UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
- String componentString;
- if (userInfo.isAdmin()) {
- componentString = Resources.getSystem().getString(
- com.android.internal.R.string.config_customAdbWifiNetworkConfirmationComponent);
- } else {
- componentString = Resources.getSystem().getString(
- com.android.internal.R.string.config_customAdbWifiNetworkConfirmationComponent);
- }
+ String componentString =
+ Resources.getSystem().getString(
+ R.string.config_customAdbWifiNetworkConfirmationComponent);
ComponentName componentName = ComponentName.unflattenFromString(componentString);
+ UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras)
- || startConfirmationService(componentName, userInfo.getUserHandle(),
- extras)) {
+ || startConfirmationService(componentName, userInfo.getUserHandle(), extras)) {
return;
}
Slog.e(TAG, "Unable to start customAdbWifiNetworkConfirmation[SecondaryUser]Component "
@@ -1543,7 +1571,7 @@
/**
* Returns a new File with the specified name in the adb directory.
*/
- private File getAdbFile(String fileName) {
+ private static File getAdbFile(String fileName) {
File dataDir = Environment.getDataDirectory();
File adbDir = new File(dataDir, ADB_DIRECTORY);
@@ -1556,66 +1584,38 @@
}
File getAdbTempKeysFile() {
- return getAdbFile(ADB_TEMP_KEYS_FILE);
+ return mTempKeysFile;
}
File getUserKeyFile() {
- return mTestUserKeyFile == null ? getAdbFile(ADB_KEYS_FILE) : mTestUserKeyFile;
- }
-
- private void writeKey(String key) {
- try {
- File keyFile = getUserKeyFile();
-
- if (keyFile == null) {
- return;
- }
-
- FileOutputStream fo = new FileOutputStream(keyFile, true);
- fo.write(key.getBytes());
- fo.write('\n');
- fo.close();
-
- FileUtils.setPermissions(keyFile.toString(),
- FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1);
- } catch (IOException ex) {
- Slog.e(TAG, "Error writing key:" + ex);
- }
+ return mUserKeyFile;
}
private void writeKeys(Iterable<String> keys) {
- AtomicFile atomicKeyFile = null;
+ if (mUserKeyFile == null) {
+ return;
+ }
+
+ AtomicFile atomicKeyFile = new AtomicFile(mUserKeyFile);
+ // Note: Do not use a try-with-resources with the FileOutputStream, because AtomicFile
+ // requires that it's cleaned up with AtomicFile.failWrite();
FileOutputStream fo = null;
try {
- File keyFile = getUserKeyFile();
-
- if (keyFile == null) {
- return;
- }
-
- atomicKeyFile = new AtomicFile(keyFile);
fo = atomicKeyFile.startWrite();
for (String key : keys) {
fo.write(key.getBytes());
fo.write('\n');
}
atomicKeyFile.finishWrite(fo);
-
- FileUtils.setPermissions(keyFile.toString(),
- FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1);
} catch (IOException ex) {
Slog.e(TAG, "Error writing keys: " + ex);
- if (atomicKeyFile != null) {
- atomicKeyFile.failWrite(fo);
- }
+ atomicKeyFile.failWrite(fo);
+ return;
}
- }
- private void deleteKeyFile() {
- File keyFile = getUserKeyFile();
- if (keyFile != null) {
- keyFile.delete();
- }
+ FileUtils.setPermissions(
+ mUserKeyFile.toString(),
+ FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1);
}
/**
@@ -1745,6 +1745,13 @@
}
/**
+ * Notify that they key files were updated so the AdbKeyManager reloads the keys.
+ */
+ public void notifyKeyFilesUpdated() {
+ mHandler.sendEmptyMessage(AdbDebuggingHandler.MESSAGE_KEY_FILES_UPDATED);
+ }
+
+ /**
* Sends a message to the handler to persist the keystore.
*/
private void sendPersistKeyStoreMessage() {
@@ -1778,7 +1785,7 @@
try {
dump.write("keystore", AdbDebuggingManagerProto.KEYSTORE,
- FileUtils.readTextFile(getAdbTempKeysFile(), 0, null));
+ FileUtils.readTextFile(mTempKeysFile, 0, null));
} catch (IOException e) {
Slog.i(TAG, "Cannot read keystore: ", e);
}
@@ -1792,12 +1799,12 @@
* ADB_ALLOWED_CONNECTION_TIME setting.
*/
class AdbKeyStore {
- private Map<String, Long> mKeyMap;
- private Set<String> mSystemKeys;
- private File mKeyFile;
private AtomicFile mAtomicKeyFile;
- private List<String> mTrustedNetworks;
+ private final Set<String> mSystemKeys;
+ private final Map<String, Long> mKeyMap = new HashMap<>();
+ private final List<String> mTrustedNetworks = new ArrayList<>();
+
private static final int KEYSTORE_VERSION = 1;
private static final int MAX_SUPPORTED_KEYSTORE_VERSION = 1;
private static final String XML_KEYSTORE_START_TAG = "keyStore";
@@ -1819,26 +1826,22 @@
public static final long NO_PREVIOUS_CONNECTION = 0;
/**
- * Constructor that uses the default location for the persistent adb keystore.
+ * Create an AdbKeyStore instance.
+ *
+ * <p>Upon creation, we parse {@link #mTempKeysFile} to determine authorized WiFi APs and
+ * retrieve the map of stored ADB keys and their last connected times. After that, we read
+ * the {@link #mUserKeyFile}, and any keys that exist in that file that do not exist in the
+ * map are added to the map (for backwards compatibility).
*/
AdbKeyStore() {
- init();
- }
-
- /**
- * Constructor that uses the specified file as the location for the persistent adb keystore.
- */
- AdbKeyStore(File keyFile) {
- mKeyFile = keyFile;
- init();
- }
-
- private void init() {
initKeyFile();
- mKeyMap = getKeyMap();
- mTrustedNetworks = getTrustedNetworks();
+ readTempKeysFile();
mSystemKeys = getSystemKeysFromFile(SYSTEM_KEY_FILE);
- addUserKeysToKeyStore();
+ addExistingUserKeysToKeyStore();
+ }
+
+ public void reloadKeyMap() {
+ readTempKeysFile();
}
public void addTrustedNetwork(String bssid) {
@@ -1877,7 +1880,6 @@
public void removeKey(String key) {
if (mKeyMap.containsKey(key)) {
mKeyMap.remove(key);
- writeKeys(mKeyMap.keySet());
sendPersistKeyStoreMessage();
}
}
@@ -1886,12 +1888,9 @@
* Initializes the key file that will be used to persist the adb grants.
*/
private void initKeyFile() {
- if (mKeyFile == null) {
- mKeyFile = getAdbTempKeysFile();
- }
- // getAdbTempKeysFile can return null if the adb file cannot be obtained
- if (mKeyFile != null) {
- mAtomicKeyFile = new AtomicFile(mKeyFile);
+ // mTempKeysFile can be null if the adb file cannot be obtained
+ if (mTempKeysFile != null) {
+ mAtomicKeyFile = new AtomicFile(mTempKeysFile);
}
}
@@ -1932,201 +1931,108 @@
}
/**
- * Returns the key map with the keys and last connection times from the key file.
+ * Update the key map and the trusted networks list with values parsed from the temp keys
+ * file.
*/
- private Map<String, Long> getKeyMap() {
- Map<String, Long> keyMap = new HashMap<String, Long>();
- // if the AtomicFile could not be instantiated before attempt again; if it still fails
- // return an empty key map.
+ private void readTempKeysFile() {
+ mKeyMap.clear();
+ mTrustedNetworks.clear();
if (mAtomicKeyFile == null) {
initKeyFile();
if (mAtomicKeyFile == null) {
- Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
- return keyMap;
+ Slog.e(
+ TAG,
+ "Unable to obtain the key file, " + mTempKeysFile + ", for reading");
+ return;
}
}
if (!mAtomicKeyFile.exists()) {
- return keyMap;
+ return;
}
try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
- TypedXmlPullParser parser = Xml.resolvePullParser(keyStream);
- // Check for supported keystore version.
- XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
- if (parser.next() != XmlPullParser.END_DOCUMENT) {
- String tagName = parser.getName();
- if (tagName == null || !XML_KEYSTORE_START_TAG.equals(tagName)) {
- Slog.e(TAG, "Expected " + XML_KEYSTORE_START_TAG + ", but got tag="
- + tagName);
- return keyMap;
- }
+ TypedXmlPullParser parser;
+ try {
+ parser = Xml.resolvePullParser(keyStream);
+ XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
+
int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION);
if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
Slog.e(TAG, "Keystore version=" + keystoreVersion
+ " not supported (max_supported="
+ MAX_SUPPORTED_KEYSTORE_VERSION + ")");
- return keyMap;
+ return;
}
+ } catch (XmlPullParserException e) {
+ // This could be because the XML document doesn't start with
+ // XML_KEYSTORE_START_TAG. Try again, instead just starting the document with
+ // the adbKey tag (the old format).
+ parser = Xml.resolvePullParser(keyStream);
}
- while (parser.next() != XmlPullParser.END_DOCUMENT) {
- String tagName = parser.getName();
- if (tagName == null) {
- break;
- } else if (!tagName.equals(XML_TAG_ADB_KEY)) {
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
- long connectionTime;
- try {
- connectionTime = parser.getAttributeLong(null,
- XML_ATTRIBUTE_LAST_CONNECTION);
- } catch (XmlPullParserException e) {
- Slog.e(TAG,
- "Caught a NumberFormatException parsing the last connection time: "
- + e);
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- keyMap.put(key, connectionTime);
- }
+ readKeyStoreContents(parser);
} catch (IOException e) {
Slog.e(TAG, "Caught an IOException parsing the XML key file: ", e);
} catch (XmlPullParserException e) {
- Slog.w(TAG, "Caught XmlPullParserException parsing the XML key file: ", e);
- // The file could be written in a format prior to introducing keystore tag.
- return getKeyMapBeforeKeystoreVersion();
+ Slog.e(TAG, "Caught XmlPullParserException parsing the XML key file: ", e);
}
- return keyMap;
}
-
- /**
- * Returns the key map with the keys and last connection times from the key file.
- * This implementation was prior to adding the XML_KEYSTORE_START_TAG.
- */
- private Map<String, Long> getKeyMapBeforeKeystoreVersion() {
- Map<String, Long> keyMap = new HashMap<String, Long>();
- // if the AtomicFile could not be instantiated before attempt again; if it still fails
- // return an empty key map.
- if (mAtomicKeyFile == null) {
- initKeyFile();
- if (mAtomicKeyFile == null) {
- Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
- return keyMap;
+ private void readKeyStoreContents(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ // This parser is very forgiving. For backwards-compatibility, we simply iterate through
+ // all the tags in the file, skipping over anything that's not an <adbKey> tag or a
+ // <wifiAP> tag. Invalid tags (such as ones that don't have a valid "lastConnection"
+ // attribute) are simply ignored.
+ while ((parser.next()) != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if (XML_TAG_ADB_KEY.equals(tagName)) {
+ addAdbKeyToKeyMap(parser);
+ } else if (XML_TAG_WIFI_ACCESS_POINT.equals(tagName)) {
+ addTrustedNetworkToTrustedNetworks(parser);
+ } else {
+ Slog.w(TAG, "Ignoring tag '" + tagName + "'. Not recognized.");
}
+ XmlUtils.skipCurrentTag(parser);
}
- if (!mAtomicKeyFile.exists()) {
- return keyMap;
- }
- try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
- TypedXmlPullParser parser = Xml.resolvePullParser(keyStream);
- XmlUtils.beginDocument(parser, XML_TAG_ADB_KEY);
- while (parser.next() != XmlPullParser.END_DOCUMENT) {
- String tagName = parser.getName();
- if (tagName == null) {
- break;
- } else if (!tagName.equals(XML_TAG_ADB_KEY)) {
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
- long connectionTime;
- try {
- connectionTime = parser.getAttributeLong(null,
- XML_ATTRIBUTE_LAST_CONNECTION);
- } catch (XmlPullParserException e) {
- Slog.e(TAG,
- "Caught a NumberFormatException parsing the last connection time: "
- + e);
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- keyMap.put(key, connectionTime);
- }
- } catch (IOException | XmlPullParserException e) {
- Slog.e(TAG, "Caught an exception parsing the XML key file: ", e);
- }
- return keyMap;
}
- /**
- * Returns the map of trusted networks from the keystore file.
- *
- * This was implemented in keystore version 1.
- */
- private List<String> getTrustedNetworks() {
- List<String> trustedNetworks = new ArrayList<String>();
- // if the AtomicFile could not be instantiated before attempt again; if it still fails
- // return an empty key map.
- if (mAtomicKeyFile == null) {
- initKeyFile();
- if (mAtomicKeyFile == null) {
- Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
- return trustedNetworks;
- }
+ private void addAdbKeyToKeyMap(TypedXmlPullParser parser) {
+ String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
+ try {
+ long connectionTime =
+ parser.getAttributeLong(null, XML_ATTRIBUTE_LAST_CONNECTION);
+ mKeyMap.put(key, connectionTime);
+ } catch (XmlPullParserException e) {
+ Slog.e(TAG, "Error reading adbKey attributes", e);
}
- if (!mAtomicKeyFile.exists()) {
- return trustedNetworks;
- }
- try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
- TypedXmlPullParser parser = Xml.resolvePullParser(keyStream);
- // Check for supported keystore version.
- XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
- if (parser.next() != XmlPullParser.END_DOCUMENT) {
- String tagName = parser.getName();
- if (tagName == null || !XML_KEYSTORE_START_TAG.equals(tagName)) {
- Slog.e(TAG, "Expected " + XML_KEYSTORE_START_TAG + ", but got tag="
- + tagName);
- return trustedNetworks;
- }
- int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION);
- if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
- Slog.e(TAG, "Keystore version=" + keystoreVersion
- + " not supported (max_supported="
- + MAX_SUPPORTED_KEYSTORE_VERSION);
- return trustedNetworks;
- }
- }
- while (parser.next() != XmlPullParser.END_DOCUMENT) {
- String tagName = parser.getName();
- if (tagName == null) {
- break;
- } else if (!tagName.equals(XML_TAG_WIFI_ACCESS_POINT)) {
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- String bssid = parser.getAttributeValue(null, XML_ATTRIBUTE_WIFI_BSSID);
- trustedNetworks.add(bssid);
- }
- } catch (IOException | XmlPullParserException | NumberFormatException e) {
- Slog.e(TAG, "Caught an exception parsing the XML key file: ", e);
- }
- return trustedNetworks;
+ }
+
+ private void addTrustedNetworkToTrustedNetworks(TypedXmlPullParser parser) {
+ String bssid = parser.getAttributeValue(null, XML_ATTRIBUTE_WIFI_BSSID);
+ mTrustedNetworks.add(bssid);
}
/**
* Updates the keystore with keys that were previously set to be always allowed before the
* connection time of keys was tracked.
*/
- private void addUserKeysToKeyStore() {
- File userKeyFile = getUserKeyFile();
+ private void addExistingUserKeysToKeyStore() {
+ if (mUserKeyFile == null || !mUserKeyFile.exists()) {
+ return;
+ }
boolean mapUpdated = false;
- if (userKeyFile != null && userKeyFile.exists()) {
- try (BufferedReader in = new BufferedReader(new FileReader(userKeyFile))) {
- long time = System.currentTimeMillis();
- String key;
- while ((key = in.readLine()) != null) {
- // if the keystore does not contain the key from the user key file then add
- // it to the Map with the current system time to prevent it from expiring
- // immediately if the user is actively using this key.
- if (!mKeyMap.containsKey(key)) {
- mKeyMap.put(key, time);
- mapUpdated = true;
- }
+ try (BufferedReader in = new BufferedReader(new FileReader(mUserKeyFile))) {
+ String key;
+ while ((key = in.readLine()) != null) {
+ // if the keystore does not contain the key from the user key file then add
+ // it to the Map with the current system time to prevent it from expiring
+ // immediately if the user is actively using this key.
+ if (!mKeyMap.containsKey(key)) {
+ mKeyMap.put(key, mTicker.currentTimeMillis());
+ mapUpdated = true;
}
- } catch (IOException e) {
- Slog.e(TAG, "Caught an exception reading " + userKeyFile + ": " + e);
}
+ } catch (IOException e) {
+ Slog.e(TAG, "Caught an exception reading " + mUserKeyFile + ": " + e);
}
if (mapUpdated) {
sendPersistKeyStoreMessage();
@@ -2147,7 +2053,9 @@
if (mAtomicKeyFile == null) {
initKeyFile();
if (mAtomicKeyFile == null) {
- Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for writing");
+ Slog.e(
+ TAG,
+ "Unable to obtain the key file, " + mTempKeysFile + ", for writing");
return;
}
}
@@ -2178,17 +2086,21 @@
Slog.e(TAG, "Caught an exception writing the key map: ", e);
mAtomicKeyFile.failWrite(keyStream);
}
+ writeKeys(mKeyMap.keySet());
}
private boolean filterOutOldKeys() {
- boolean keysDeleted = false;
long allowedTime = getAllowedConnectionTime();
- long systemTime = System.currentTimeMillis();
+ if (allowedTime == 0) {
+ return false;
+ }
+ boolean keysDeleted = false;
+ long systemTime = mTicker.currentTimeMillis();
Iterator<Map.Entry<String, Long>> keyMapIterator = mKeyMap.entrySet().iterator();
while (keyMapIterator.hasNext()) {
Map.Entry<String, Long> keyEntry = keyMapIterator.next();
long connectionTime = keyEntry.getValue();
- if (allowedTime != 0 && systemTime > (connectionTime + allowedTime)) {
+ if (systemTime > (connectionTime + allowedTime)) {
keyMapIterator.remove();
keysDeleted = true;
}
@@ -2212,7 +2124,7 @@
if (allowedTime == 0) {
return minExpiration;
}
- long systemTime = System.currentTimeMillis();
+ long systemTime = mTicker.currentTimeMillis();
Iterator<Map.Entry<String, Long>> keyMapIterator = mKeyMap.entrySet().iterator();
while (keyMapIterator.hasNext()) {
Map.Entry<String, Long> keyEntry = keyMapIterator.next();
@@ -2233,7 +2145,9 @@
public void deleteKeyStore() {
mKeyMap.clear();
mTrustedNetworks.clear();
- deleteKeyFile();
+ if (mUserKeyFile != null) {
+ mUserKeyFile.delete();
+ }
if (mAtomicKeyFile == null) {
return;
}
@@ -2260,7 +2174,8 @@
* is set to true the time will be set even if it is older than the previously written
* connection time.
*/
- public void setLastConnectionTime(String key, long connectionTime, boolean force) {
+ @VisibleForTesting
+ void setLastConnectionTime(String key, long connectionTime, boolean force) {
// Do not set the connection time to a value that is earlier than what was previously
// stored as the last connection time unless force is set.
if (mKeyMap.containsKey(key) && mKeyMap.get(key) >= connectionTime && !force) {
@@ -2271,11 +2186,6 @@
if (mSystemKeys.contains(key)) {
return;
}
- // if this is the first time the key is being added then write it to the key file as
- // well.
- if (!mKeyMap.containsKey(key)) {
- writeKey(key);
- }
mKeyMap.put(key, connectionTime);
}
@@ -2307,12 +2217,8 @@
long allowedConnectionTime = getAllowedConnectionTime();
// if the allowed connection time is 0 then revert to the previous behavior of always
// allowing previously granted adb grants.
- if (allowedConnectionTime == 0 || (System.currentTimeMillis() < (lastConnectionTime
- + allowedConnectionTime))) {
- return true;
- } else {
- return false;
- }
+ return allowedConnectionTime == 0
+ || (mTicker.currentTimeMillis() < (lastConnectionTime + allowedConnectionTime));
}
/**
@@ -2324,4 +2230,15 @@
return mTrustedNetworks.contains(bssid);
}
}
+
+ /**
+ * A Guava-like interface for getting the current system time.
+ *
+ * This allows us to swap a fake ticker in for testing to reduce "Thread.sleep()" calls and test
+ * for exact expected times instead of random ones.
+ */
+ @VisibleForTesting
+ interface Ticker {
+ long currentTimeMillis();
+ }
}
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 69f6d3a..991edf5 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -152,6 +152,14 @@
}
@Override
+ public void notifyKeyFilesUpdated() {
+ if (mDebuggingManager == null) {
+ return;
+ }
+ mDebuggingManager.notifyKeyFilesUpdated();
+ }
+
+ @Override
public void startAdbdForTransport(byte transportType) {
FgThread.getHandler().sendMessage(obtainMessage(
AdbService::setAdbdEnabledForTransport, AdbService.this, true, transportType));
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index c4efbd7..15887f0 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -1,4 +1,3 @@
-
# Applications & Processes
yamasani@google.com
jsharkey@google.com
@@ -18,8 +17,6 @@
ogunwale@google.com
# Permissions & Packages
-svetoslavganov@google.com
-toddke@google.com
patb@google.com
# Battery Stats
@@ -35,8 +32,8 @@
per-file *Assist* = file:/core/java/android/service/voice/OWNERS
per-file *Voice* = file:/core/java/android/service/voice/OWNERS
-per-file SettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com
+per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com
-per-file CarUserSwitchingDialog.java = keunyoung@google.com, felipeal@google.com, gurunagarajan@google.com
+per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNERS
per-file ContentProviderHelper.java = varunshah@google.com, omakoto@google.com, jsharkey@google.com, yamasani@google.com
diff --git a/services/core/java/com/android/server/broadcastradio/OWNERS b/services/core/java/com/android/server/broadcastradio/OWNERS
index 3e360e7..d2bdd64 100644
--- a/services/core/java/com/android/server/broadcastradio/OWNERS
+++ b/services/core/java/com/android/server/broadcastradio/OWNERS
@@ -1,3 +1,3 @@
-keunyoung@google.com
+xuweilin@google.com
oscarazu@google.com
-twasilczyk@google.com
+keunyoung@google.com
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a545ed6..5b282ce 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2712,8 +2712,8 @@
*/
@NonNull private final ScheduledThreadPoolExecutor mExecutor;
- @Nullable private ScheduledFuture<?> mScheduledHandleNetworkLostTimeout;
- @Nullable private ScheduledFuture<?> mScheduledHandleRetryIkeSessionTimeout;
+ @Nullable private ScheduledFuture<?> mScheduledHandleNetworkLostFuture;
+ @Nullable private ScheduledFuture<?> mScheduledHandleRetryIkeSessionFuture;
/** Signal to ensure shutdown is honored even if a new Network is connected. */
private boolean mIsRunning = true;
@@ -2955,6 +2955,8 @@
}
try {
+ mTunnelIface.setUnderlyingNetwork(mIkeConnectionInfo.getNetwork());
+
// Transforms do not need to be persisted; the IkeSession will keep
// them alive for us
mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform);
@@ -3136,13 +3138,13 @@
// If the default network is lost during the retry delay, the mActiveNetwork will be
// null, and the new IKE session won't be established until there is a new default
// network bringing up.
- mScheduledHandleRetryIkeSessionTimeout =
+ mScheduledHandleRetryIkeSessionFuture =
mExecutor.schedule(() -> {
startOrMigrateIkeSession(mActiveNetwork);
- // Reset mScheduledHandleRetryIkeSessionTimeout since it's already run on
+ // Reset mScheduledHandleRetryIkeSessionFuture since it's already run on
// executor thread.
- mScheduledHandleRetryIkeSessionTimeout = null;
+ mScheduledHandleRetryIkeSessionFuture = null;
}, retryDelay, TimeUnit.SECONDS);
}
@@ -3185,12 +3187,10 @@
mActiveNetwork = null;
}
- if (mScheduledHandleNetworkLostTimeout != null
- && !mScheduledHandleNetworkLostTimeout.isCancelled()
- && !mScheduledHandleNetworkLostTimeout.isDone()) {
+ if (mScheduledHandleNetworkLostFuture != null) {
final IllegalStateException exception =
new IllegalStateException(
- "Found a pending mScheduledHandleNetworkLostTimeout");
+ "Found a pending mScheduledHandleNetworkLostFuture");
Log.i(
TAG,
"Unexpected error in onDefaultNetworkLost. Tear down session",
@@ -3207,13 +3207,26 @@
+ " on session with token "
+ mCurrentToken);
+ final int token = mCurrentToken;
// Delay the teardown in case a new network will be available soon. For example,
// during handover between two WiFi networks, Android will disconnect from the
// first WiFi and then connects to the second WiFi.
- mScheduledHandleNetworkLostTimeout =
+ mScheduledHandleNetworkLostFuture =
mExecutor.schedule(
() -> {
- handleSessionLost(null, network);
+ if (isActiveToken(token)) {
+ handleSessionLost(null, network);
+ } else {
+ Log.d(
+ TAG,
+ "Scheduled handleSessionLost fired for "
+ + "obsolete token "
+ + token);
+ }
+
+ // Reset mScheduledHandleNetworkLostFuture since it's
+ // already run on executor thread.
+ mScheduledHandleNetworkLostFuture = null;
},
NETWORK_LOST_TIMEOUT_MS,
TimeUnit.MILLISECONDS);
@@ -3224,28 +3237,26 @@
}
private void cancelHandleNetworkLostTimeout() {
- if (mScheduledHandleNetworkLostTimeout != null
- && !mScheduledHandleNetworkLostTimeout.isDone()) {
+ if (mScheduledHandleNetworkLostFuture != null) {
// It does not matter what to put in #cancel(boolean), because it is impossible
- // that the task tracked by mScheduledHandleNetworkLostTimeout is
+ // that the task tracked by mScheduledHandleNetworkLostFuture is
// in-progress since both that task and onDefaultNetworkChanged are submitted to
// mExecutor who has only one thread.
Log.d(TAG, "Cancel the task for handling network lost timeout");
- mScheduledHandleNetworkLostTimeout.cancel(false /* mayInterruptIfRunning */);
- mScheduledHandleNetworkLostTimeout = null;
+ mScheduledHandleNetworkLostFuture.cancel(false /* mayInterruptIfRunning */);
+ mScheduledHandleNetworkLostFuture = null;
}
}
private void cancelRetryNewIkeSessionFuture() {
- if (mScheduledHandleRetryIkeSessionTimeout != null
- && !mScheduledHandleRetryIkeSessionTimeout.isDone()) {
+ if (mScheduledHandleRetryIkeSessionFuture != null) {
// It does not matter what to put in #cancel(boolean), because it is impossible
- // that the task tracked by mScheduledHandleRetryIkeSessionTimeout is
+ // that the task tracked by mScheduledHandleRetryIkeSessionFuture is
// in-progress since both that task and onDefaultNetworkChanged are submitted to
// mExecutor who has only one thread.
Log.d(TAG, "Cancel the task for handling new ike session timeout");
- mScheduledHandleRetryIkeSessionTimeout.cancel(false /* mayInterruptIfRunning */);
- mScheduledHandleRetryIkeSessionTimeout = null;
+ mScheduledHandleRetryIkeSessionFuture.cancel(false /* mayInterruptIfRunning */);
+ mScheduledHandleRetryIkeSessionFuture = null;
}
}
@@ -3285,13 +3296,18 @@
}
private void handleSessionLost(@Nullable Exception exception, @Nullable Network network) {
- // Cancel mScheduledHandleNetworkLostTimeout if the session it is going to terminate is
+ // Cancel mScheduledHandleNetworkLostFuture if the session it is going to terminate is
// already terminated due to other failures.
cancelHandleNetworkLostTimeout();
synchronized (Vpn.this) {
+ String category = null;
+ int errorClass = -1;
+ int errorCode = -1;
if (exception instanceof IkeProtocolException) {
final IkeProtocolException ikeException = (IkeProtocolException) exception;
+ category = VpnManager.CATEGORY_EVENT_IKE_ERROR;
+ errorCode = ikeException.getErrorType();
switch (ikeException.getErrorType()) {
case IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN: // Fallthrough
@@ -3301,105 +3317,53 @@
case IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED: // Fallthrough
case IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE:
// All the above failures are configuration errors, and are terminal
- // TODO(b/230548427): Remove SDK check once VPN related stuff are
- // decoupled from ConnectivityServiceTest.
- if (SdkLevel.isAtLeastT()) {
- sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_IKE_ERROR,
- VpnManager.ERROR_CLASS_NOT_RECOVERABLE,
- ikeException.getErrorType(),
- getPackage(), mSessionKey, makeVpnProfileStateLocked(),
- network,
- getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
- mUnderlyingNetworkCapabilities),
- getRedactedLinkPropertiesOfUnderlyingNetwork(
- mUnderlyingLinkProperties));
- }
- markFailedAndDisconnect(exception);
- return;
+ errorClass = VpnManager.ERROR_CLASS_NOT_RECOVERABLE;
+ break;
// All other cases possibly recoverable.
default:
// All the above failures are configuration errors, and are terminal
- // TODO(b/230548427): Remove SDK check once VPN related stuff are
- // decoupled from ConnectivityServiceTest.
- if (SdkLevel.isAtLeastT()) {
- sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_IKE_ERROR,
- VpnManager.ERROR_CLASS_RECOVERABLE,
- ikeException.getErrorType(),
- getPackage(), mSessionKey, makeVpnProfileStateLocked(),
- network,
- getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
- mUnderlyingNetworkCapabilities),
- getRedactedLinkPropertiesOfUnderlyingNetwork(
- mUnderlyingLinkProperties));
- }
+ errorClass = VpnManager.ERROR_CLASS_RECOVERABLE;
}
} else if (exception instanceof IllegalArgumentException) {
// Failed to build IKE/ChildSessionParams; fatal profile configuration error
markFailedAndDisconnect(exception);
return;
} else if (exception instanceof IkeNetworkLostException) {
- // TODO(b/230548427): Remove SDK check once VPN related stuff are
- // decoupled from ConnectivityServiceTest.
- if (SdkLevel.isAtLeastT()) {
- sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR,
- VpnManager.ERROR_CLASS_RECOVERABLE,
- VpnManager.ERROR_CODE_NETWORK_LOST,
- getPackage(), mSessionKey, makeVpnProfileStateLocked(),
- network,
- getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
- mUnderlyingNetworkCapabilities),
- getRedactedLinkPropertiesOfUnderlyingNetwork(
- mUnderlyingLinkProperties));
- }
+ category = VpnManager.CATEGORY_EVENT_NETWORK_ERROR;
+ errorClass = VpnManager.ERROR_CLASS_RECOVERABLE;
+ errorCode = VpnManager.ERROR_CODE_NETWORK_LOST;
} else if (exception instanceof IkeNonProtocolException) {
+ category = VpnManager.CATEGORY_EVENT_NETWORK_ERROR;
+ errorClass = VpnManager.ERROR_CLASS_RECOVERABLE;
if (exception.getCause() instanceof UnknownHostException) {
- // TODO(b/230548427): Remove SDK check once VPN related stuff are
- // decoupled from ConnectivityServiceTest.
- if (SdkLevel.isAtLeastT()) {
- sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR,
- VpnManager.ERROR_CLASS_RECOVERABLE,
- VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST,
- getPackage(), mSessionKey, makeVpnProfileStateLocked(),
- network,
- getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
- mUnderlyingNetworkCapabilities),
- getRedactedLinkPropertiesOfUnderlyingNetwork(
- mUnderlyingLinkProperties));
- }
+ errorCode = VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST;
} else if (exception.getCause() instanceof IkeTimeoutException) {
- // TODO(b/230548427): Remove SDK check once VPN related stuff are
- // decoupled from ConnectivityServiceTest.
- if (SdkLevel.isAtLeastT()) {
- sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR,
- VpnManager.ERROR_CLASS_RECOVERABLE,
- VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT,
- getPackage(), mSessionKey, makeVpnProfileStateLocked(),
- network,
- getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
- mUnderlyingNetworkCapabilities),
- getRedactedLinkPropertiesOfUnderlyingNetwork(
- mUnderlyingLinkProperties));
- }
+ errorCode = VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT;
} else if (exception.getCause() instanceof IOException) {
- // TODO(b/230548427): Remove SDK check once VPN related stuff are
- // decoupled from ConnectivityServiceTest.
- if (SdkLevel.isAtLeastT()) {
- sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR,
- VpnManager.ERROR_CLASS_RECOVERABLE,
- VpnManager.ERROR_CODE_NETWORK_IO,
- getPackage(), mSessionKey, makeVpnProfileStateLocked(),
- network,
- getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
- mUnderlyingNetworkCapabilities),
- getRedactedLinkPropertiesOfUnderlyingNetwork(
- mUnderlyingLinkProperties));
- }
+ errorCode = VpnManager.ERROR_CODE_NETWORK_IO;
}
} else if (exception != null) {
Log.wtf(TAG, "onSessionLost: exception = " + exception);
}
- scheduleRetryNewIkeSession();
+ // TODO(b/230548427): Remove SDK check once VPN related stuff are
+ // decoupled from ConnectivityServiceTest.
+ if (SdkLevel.isAtLeastT() && category != null) {
+ sendEventToVpnManagerApp(category, errorClass, errorCode,
+ getPackage(), mSessionKey, makeVpnProfileStateLocked(),
+ mActiveNetwork,
+ getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
+ mUnderlyingNetworkCapabilities),
+ getRedactedLinkPropertiesOfUnderlyingNetwork(
+ mUnderlyingLinkProperties));
+ }
+
+ if (errorClass == VpnManager.ERROR_CLASS_NOT_RECOVERABLE) {
+ markFailedAndDisconnect(exception);
+ return;
+ } else {
+ scheduleRetryNewIkeSession();
+ }
}
mUnderlyingNetworkCapabilities = null;
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
index b6a4135..452bdf4 100644
--- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -189,6 +189,7 @@
if (adbManager.getAdbTempKeysFile() != null) {
writeBytesToFile(persistentData.mAdbTempKeys, adbManager.getAdbTempKeysFile().toPath());
}
+ adbManager.notifyKeyFilesUpdated();
}
private void configureUser() {
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 4c4a8d7..0000000
--- 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 f9984fa..0000000
--- 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 f6e35e8..0000000
--- 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 63790961..0000000
--- 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 fd0df8d..0000000
--- 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 8f4cada..0000000
--- 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 5f90be1..0000000
--- 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 3753ece..0000000
--- 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 4110d88..0000000
--- 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 0084c1a..0000000
--- 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 2ec31e2..0000000
--- 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 bb317cf..0000000
--- 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 fd5c6e9..0000000
--- 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 8f5c7a7..0000000
--- 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 23e3eba..0000000
--- 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 dc729f2..d3b928f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -326,8 +326,6 @@
"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 =
@@ -2183,19 +2181,6 @@
}
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/net/Android.bp b/services/net/Android.bp
index 886a397..804ccc5 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -28,30 +28,6 @@
],
}
-// Version of services.net for usage by the wifi mainline module.
-// Note: This is compiled against module_current.
-// TODO(b/172457099): This should be moved to networkstack-client,
-// with dependencies moved to frameworks/libs/net right.
-java_library {
- name: "services.net-module-wifi",
- sdk_version: "module_current",
- min_sdk_version: "30",
- static_libs: [
- // All the classes in netd_aidl_interface must be jarjar so they do not conflict with the
- // classes generated by netd_aidl_interfaces-platform-java above.
- "netd_aidl_interface-V3-java",
- "networkstack-client",
- "net-utils-services-common",
- ],
- apex_available: [
- "com.android.wifi",
- ],
- visibility: [
- "//packages/modules/Wifi/service",
- "//packages/modules/Wifi/service/tests/wifitests",
- ],
-}
-
filegroup {
name: "services-tethering-shared-srcs",
srcs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
index 5db8867..de27d77 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java
@@ -204,7 +204,7 @@
@Test
public void testUpdatePropertiesFromSettings_PropertyAndSettingNotPresent() {
- // Test that empty property will not not be set if setting is not set
+ // Test that empty property will not be set if setting is not set
mTestMapper.updatePropertiesFromSettings();
String propValue = mSystemSettingsMap.get("TestProperty");
Assert.assertNull("Property should not be set if setting is null", propValue);
diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
index 02cf971..336fd5c 100644
--- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
@@ -36,8 +36,6 @@
import androidx.test.InstrumentationRegistry;
-import com.android.server.FgThread;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -48,6 +46,11 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
@@ -88,6 +91,7 @@
private long mOriginalAllowedConnectionTime;
private File mAdbKeyXmlFile;
private File mAdbKeyFile;
+ private FakeTicker mFakeTicker;
@Before
public void setUp() throws Exception {
@@ -96,14 +100,25 @@
if (mAdbKeyFile.exists()) {
mAdbKeyFile.delete();
}
- mManager = new AdbDebuggingManager(mContext, ADB_CONFIRM_COMPONENT, mAdbKeyFile);
mAdbKeyXmlFile = new File(mContext.getFilesDir(), "test_adb_keys.xml");
if (mAdbKeyXmlFile.exists()) {
mAdbKeyXmlFile.delete();
}
+
+ mFakeTicker = new FakeTicker();
+ // Set the ticker time to October 22, 2008 (the day the T-Mobile G1 was released)
+ mFakeTicker.advance(1224658800L);
+
mThread = new AdbDebuggingThreadTest();
- mKeyStore = mManager.new AdbKeyStore(mAdbKeyXmlFile);
- mHandler = mManager.new AdbDebuggingHandler(FgThread.get().getLooper(), mThread, mKeyStore);
+ mManager = new AdbDebuggingManager(
+ mContext, ADB_CONFIRM_COMPONENT, mAdbKeyFile, mAdbKeyXmlFile, mThread, mFakeTicker);
+
+ mHandler = mManager.mHandler;
+ mThread.setHandler(mHandler);
+
+ mHandler.initKeyStore();
+ mKeyStore = mHandler.mAdbKeyStore;
+
mOriginalAllowedConnectionTime = mKeyStore.getAllowedConnectionTime();
mBlockingQueue = new ArrayBlockingQueue<>(1);
}
@@ -122,7 +137,7 @@
private void setAllowedConnectionTime(long connectionTime) {
Settings.Global.putLong(mContext.getContentResolver(),
Settings.Global.ADB_ALLOWED_CONNECTION_TIME, connectionTime);
- };
+ }
@Test
public void testAllowNewKeyOnce() throws Exception {
@@ -158,20 +173,15 @@
// Allow a connection from a new key with the 'Always allow' option selected.
runAdbTest(TEST_KEY_1, true, true, false);
- // Get the last connection time for the currently connected key to verify that it is updated
- // after the disconnect.
- long lastConnectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
-
- // Sleep for a small amount of time to ensure a difference can be observed in the last
- // connection time after a disconnect.
- Thread.sleep(10);
+ // Advance the clock by 10ms to ensure there's a difference
+ mFakeTicker.advance(10 * 1_000_000);
// Send the disconnect message for the currently connected key to trigger an update of the
// last connection time.
disconnectKey(TEST_KEY_1);
- assertNotEquals(
+ assertEquals(
"The last connection time was not updated after the disconnect",
- lastConnectionTime,
+ mFakeTicker.currentTimeMillis(),
mKeyStore.getLastConnectionTime(TEST_KEY_1));
}
@@ -244,8 +254,8 @@
// Get the current last connection time for comparison after the scheduled job is run
long lastConnectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
- // Sleep a small amount of time to ensure that the updated connection time changes
- Thread.sleep(10);
+ // Advance a small amount of time to ensure that the updated connection time changes
+ mFakeTicker.advance(10);
// Send a message to the handler to update the last connection time for the active key
updateKeyStore();
@@ -269,13 +279,13 @@
persistKeyStore();
assertTrue(
"The key with the 'Always allow' option selected was not persisted in the keystore",
- mManager.new AdbKeyStore(mAdbKeyXmlFile).isKeyAuthorized(TEST_KEY_1));
+ mManager.new AdbKeyStore().isKeyAuthorized(TEST_KEY_1));
// Get the current last connection time to ensure it is updated in the persisted keystore.
long lastConnectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
- // Sleep a small amount of time to ensure the last connection time is updated.
- Thread.sleep(10);
+ // Advance a small amount of time to ensure the last connection time is updated.
+ mFakeTicker.advance(10);
// Send a message to the handler to update the last connection time for the active key.
updateKeyStore();
@@ -286,7 +296,7 @@
assertNotEquals(
"The last connection time in the key file was not updated after the update "
+ "connection time message", lastConnectionTime,
- mManager.new AdbKeyStore(mAdbKeyXmlFile).getLastConnectionTime(TEST_KEY_1));
+ mManager.new AdbKeyStore().getLastConnectionTime(TEST_KEY_1));
// Verify that the key is in the adb_keys file
assertTrue("The key was not in the adb_keys file after persisting the keystore",
isKeyInFile(TEST_KEY_1, mAdbKeyFile));
@@ -327,8 +337,8 @@
// Set the allowed window to a small value to ensure the time is beyond the allowed window.
setAllowedConnectionTime(1);
- // Sleep for a small amount of time to exceed the allowed window.
- Thread.sleep(10);
+ // Advance a small amount of time to exceed the allowed window.
+ mFakeTicker.advance(10);
// The AdbKeyStore has a method to get the time of the next key expiration to ensure the
// scheduled job runs at the time of the next expiration or after 24 hours, whichever occurs
@@ -478,9 +488,12 @@
// Set the current expiration time to a minute from expiration and verify this new value is
// returned.
final long newExpirationTime = 60000;
- mKeyStore.setLastConnectionTime(TEST_KEY_1,
- System.currentTimeMillis() - Settings.Global.DEFAULT_ADB_ALLOWED_CONNECTION_TIME
- + newExpirationTime, true);
+ mKeyStore.setLastConnectionTime(
+ TEST_KEY_1,
+ mFakeTicker.currentTimeMillis()
+ - Settings.Global.DEFAULT_ADB_ALLOWED_CONNECTION_TIME
+ + newExpirationTime,
+ true);
expirationTime = mKeyStore.getNextExpirationTime();
if (Math.abs(expirationTime - newExpirationTime) > epsilon) {
fail("The expiration time for a key about to expire, " + expirationTime
@@ -525,7 +538,7 @@
// Get the last connection time for the key to verify that it is updated when the connected
// key message is sent.
long connectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
- Thread.sleep(10);
+ mFakeTicker.advance(10);
mHandler.obtainMessage(AdbDebuggingManager.AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY,
TEST_KEY_1).sendToTarget();
flushHandlerQueue();
@@ -536,7 +549,7 @@
// Verify that the scheduled job updates the connection time of the key.
connectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
- Thread.sleep(10);
+ mFakeTicker.advance(10);
updateKeyStore();
assertNotEquals(
"The connection time for the key must be updated when the update keystore message"
@@ -545,7 +558,7 @@
// Verify that the connection time is updated when the key is disconnected.
connectionTime = mKeyStore.getLastConnectionTime(TEST_KEY_1);
- Thread.sleep(10);
+ mFakeTicker.advance(10);
disconnectKey(TEST_KEY_1);
assertNotEquals(
"The connection time for the key must be updated when the disconnected message is"
@@ -628,11 +641,11 @@
setAllowedConnectionTime(Settings.Global.DEFAULT_ADB_ALLOWED_CONNECTION_TIME);
// The untracked keys should be added to the keystore as part of the constructor.
- AdbDebuggingManager.AdbKeyStore adbKeyStore = mManager.new AdbKeyStore(mAdbKeyXmlFile);
+ AdbDebuggingManager.AdbKeyStore adbKeyStore = mManager.new AdbKeyStore();
// Verify that the connection time for each test key is within a small value of the current
// time.
- long time = System.currentTimeMillis();
+ long time = mFakeTicker.currentTimeMillis();
for (String key : testKeys) {
long connectionTime = adbKeyStore.getLastConnectionTime(key);
if (Math.abs(connectionTime - connectionTime) > epsilon) {
@@ -651,11 +664,11 @@
runAdbTest(TEST_KEY_1, true, true, false);
runAdbTest(TEST_KEY_2, true, true, false);
- // Sleep a small amount of time to ensure the connection time is updated by the scheduled
+ // Advance a small amount of time to ensure the connection time is updated by the scheduled
// job.
long connectionTime1 = mKeyStore.getLastConnectionTime(TEST_KEY_1);
long connectionTime2 = mKeyStore.getLastConnectionTime(TEST_KEY_2);
- Thread.sleep(10);
+ mFakeTicker.advance(10);
updateKeyStore();
assertNotEquals(
"The connection time for test key 1 must be updated after the scheduled job runs",
@@ -669,7 +682,7 @@
disconnectKey(TEST_KEY_2);
connectionTime1 = mKeyStore.getLastConnectionTime(TEST_KEY_1);
connectionTime2 = mKeyStore.getLastConnectionTime(TEST_KEY_2);
- Thread.sleep(10);
+ mFakeTicker.advance(10);
updateKeyStore();
assertNotEquals(
"The connection time for test key 1 must be updated after another key is "
@@ -686,8 +699,6 @@
// to clear the adb authorizations when adb is disabled after a boot a NullPointerException
// was thrown as deleteKeyStore is invoked against the key store. This test ensures the
// key store can be successfully cleared when adb is disabled.
- mHandler = mManager.new AdbDebuggingHandler(FgThread.get().getLooper());
-
clearKeyStore();
}
@@ -723,6 +734,9 @@
// Now remove one of the keys and make sure the other key is still there
mKeyStore.removeKey(TEST_KEY_1);
+ // Wait for the handler queue to receive the MESSAGE_ADB_PERSIST_KEYSTORE
+ flushHandlerQueue();
+
assertFalse("The key was still in the adb_keys file after removing the key",
isKeyInFile(TEST_KEY_1, mAdbKeyFile));
assertTrue("The key was not in the adb_keys file after removing a different key",
@@ -730,6 +744,95 @@
}
@Test
+ public void testAdbKeyStore_addDuplicateKey_doesNotAddDuplicateToAdbKeyFile() throws Exception {
+ setAllowedConnectionTime(0);
+
+ runAdbTest(TEST_KEY_1, true, true, false);
+ persistKeyStore();
+ runAdbTest(TEST_KEY_1, true, true, false);
+ persistKeyStore();
+
+ assertEquals("adb_keys contains duplicate keys", 1, adbKeyFileKeys(mAdbKeyFile).size());
+ }
+
+ @Test
+ public void testAdbKeyStore_adbTempKeysFile_readsLastConnectionTimeFromXml() throws Exception {
+ long insertTime = mFakeTicker.currentTimeMillis();
+ runAdbTest(TEST_KEY_1, true, true, false);
+ persistKeyStore();
+
+ mFakeTicker.advance(10);
+ AdbDebuggingManager.AdbKeyStore newKeyStore = mManager.new AdbKeyStore();
+
+ assertEquals(
+ "KeyStore not populated from the XML file.",
+ insertTime,
+ newKeyStore.getLastConnectionTime(TEST_KEY_1));
+ }
+
+ @Test
+ public void test_notifyKeyFilesUpdated_filesDeletedRemovesPreviouslyAddedKey()
+ throws Exception {
+ runAdbTest(TEST_KEY_1, true, true, false);
+ persistKeyStore();
+
+ Files.delete(mAdbKeyXmlFile.toPath());
+ Files.delete(mAdbKeyFile.toPath());
+
+ mManager.notifyKeyFilesUpdated();
+ flushHandlerQueue();
+
+ assertFalse(
+ "Key is authorized after reloading deleted key files. Was state preserved?",
+ mKeyStore.isKeyAuthorized(TEST_KEY_1));
+ }
+
+ @Test
+ public void test_notifyKeyFilesUpdated_newKeyIsAuthorized() throws Exception {
+ runAdbTest(TEST_KEY_1, true, true, false);
+ persistKeyStore();
+
+ // Back up the existing key files
+ Path tempXmlFile = Files.createTempFile("adbKeyXmlFile", ".tmp");
+ Path tempAdbKeysFile = Files.createTempFile("adb_keys", ".tmp");
+ Files.copy(mAdbKeyXmlFile.toPath(), tempXmlFile, StandardCopyOption.REPLACE_EXISTING);
+ Files.copy(mAdbKeyFile.toPath(), tempAdbKeysFile, StandardCopyOption.REPLACE_EXISTING);
+
+ // Delete the existing key files
+ Files.delete(mAdbKeyXmlFile.toPath());
+ Files.delete(mAdbKeyFile.toPath());
+
+ // Notify the manager that adb key files have changed.
+ mManager.notifyKeyFilesUpdated();
+ flushHandlerQueue();
+
+ // Copy the files back
+ Files.copy(tempXmlFile, mAdbKeyXmlFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ Files.copy(tempAdbKeysFile, mAdbKeyFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
+
+ // Tell the manager that the key files have changed.
+ mManager.notifyKeyFilesUpdated();
+ flushHandlerQueue();
+
+ assertTrue(
+ "Key is not authorized after reloading key files.",
+ mKeyStore.isKeyAuthorized(TEST_KEY_1));
+ }
+
+ @Test
+ public void testAdbKeyStore_adbWifiConnect_storesBssidWhenAlwaysAllow() throws Exception {
+ String trustedNetwork = "My Network";
+ mKeyStore.addTrustedNetwork(trustedNetwork);
+ persistKeyStore();
+
+ AdbDebuggingManager.AdbKeyStore newKeyStore = mManager.new AdbKeyStore();
+
+ assertTrue(
+ "Persisted trusted network not found in new keystore instance.",
+ newKeyStore.isTrustedNetwork(trustedNetwork));
+ }
+
+ @Test
public void testIsValidMdnsServiceName() {
// Longer than 15 characters
assertFalse(isValidMdnsServiceName("abcd1234abcd1234"));
@@ -1030,28 +1133,27 @@
if (key == null) {
return false;
}
+ return adbKeyFileKeys(keyFile).contains(key);
+ }
+
+ private static List<String> adbKeyFileKeys(File keyFile) throws Exception {
+ List<String> keys = new ArrayList<>();
if (keyFile.exists()) {
try (BufferedReader in = new BufferedReader(new FileReader(keyFile))) {
String currKey;
while ((currKey = in.readLine()) != null) {
- if (key.equals(currKey)) {
- return true;
- }
+ keys.add(currKey);
}
}
}
- return false;
+ return keys;
}
/**
* Helper class that extends AdbDebuggingThread to receive the response from AdbDebuggingManager
* indicating whether the key should be allowed to connect.
*/
- class AdbDebuggingThreadTest extends AdbDebuggingManager.AdbDebuggingThread {
- AdbDebuggingThreadTest() {
- mManager.super();
- }
-
+ private class AdbDebuggingThreadTest extends AdbDebuggingManager.AdbDebuggingThread {
@Override
public void sendResponse(String msg) {
TestResult result = new TestResult(TestResult.RESULT_RESPONSE_RECEIVED, msg);
@@ -1091,4 +1193,17 @@
return "{mReturnCode = " + mReturnCode + ", mMessage = " + mMessage + "}";
}
}
+
+ private static class FakeTicker implements AdbDebuggingManager.Ticker {
+ private long mCurrentTime;
+
+ private void advance(long milliseconds) {
+ mCurrentTime += milliseconds;
+ }
+
+ @Override
+ public long currentTimeMillis() {
+ return mCurrentTime;
+ }
+ }
}
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 f7d282b..0000000
--- 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 090db11..0000000
--- 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 9b45f05..0000000
--- 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 1356ea2..0000000
--- 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 9ffc4e9..0000000
--- 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 2c4c4d0..0000000
--- 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;
- }
-}
diff --git a/tests/RollbackTest/SampleRollbackApp/Android.bp b/tests/RollbackTest/SampleRollbackApp/Android.bp
new file mode 100644
index 0000000..a18488d
--- /dev/null
+++ b/tests/RollbackTest/SampleRollbackApp/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2022 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+ name: "SampleRollbackApp",
+ srcs: [
+ "src/**/*.java",
+ ],
+ resource_dirs: ["res"],
+ certificate: "platform",
+ sdk_version: "system_current",
+}
diff --git a/tests/RollbackTest/SampleRollbackApp/AndroidManifest.xml b/tests/RollbackTest/SampleRollbackApp/AndroidManifest.xml
new file mode 100644
index 0000000..5a135c9
--- /dev/null
+++ b/tests/RollbackTest/SampleRollbackApp/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sample.rollbackapp" >
+ <uses-permission android:name="android.permission.TEST_MANAGE_ROLLBACKS" />
+ <application
+ android:label="@string/title_activity_main">
+ <activity
+ android:name="com.android.sample.rollbackapp.MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/RollbackTest/SampleRollbackApp/res/layout/activity_main.xml b/tests/RollbackTest/SampleRollbackApp/res/layout/activity_main.xml
new file mode 100644
index 0000000..3fb987b
--- /dev/null
+++ b/tests/RollbackTest/SampleRollbackApp/res/layout/activity_main.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:id="@+id/trigger_rollback_button"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ style="?android:attr/buttonBarButtonStyle"
+ android:text="Rollback Selected" />
+
+ <ListView
+ android:id="@+id/listView"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:divider="?android:attr/dividerHorizontal"
+ android:dividerHeight="1dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/RollbackTest/SampleRollbackApp/res/layout/listitem_rollbackinfo.xml b/tests/RollbackTest/SampleRollbackApp/res/layout/listitem_rollbackinfo.xml
new file mode 100644
index 0000000..f650dd5
--- /dev/null
+++ b/tests/RollbackTest/SampleRollbackApp/res/layout/listitem_rollbackinfo.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="10dp"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp">
+ <TextView android:id="@+id/rollback_id"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="20dp"/>
+ <TextView android:id="@+id/rollback_packages"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="16dp"/>
+ <CheckBox android:id="@+id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Roll Back"/>
+ </LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/RollbackTest/SampleRollbackApp/res/values/strings.xml b/tests/RollbackTest/SampleRollbackApp/res/values/strings.xml
new file mode 100644
index 0000000..a85b680
--- /dev/null
+++ b/tests/RollbackTest/SampleRollbackApp/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<resources>
+ <string name="title_activity_main" description="Launcher title">Rollback Sample App</string>
+</resources>
\ No newline at end of file
diff --git a/tests/RollbackTest/SampleRollbackApp/src/com/android/sample/rollbackapp/MainActivity.java b/tests/RollbackTest/SampleRollbackApp/src/com/android/sample/rollbackapp/MainActivity.java
new file mode 100644
index 0000000..916551a
--- /dev/null
+++ b/tests/RollbackTest/SampleRollbackApp/src/com/android/sample/rollbackapp/MainActivity.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 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.sample.rollbackapp;
+
+import static android.app.PendingIntent.FLAG_MUTABLE;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.rollback.PackageRollbackInfo;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class MainActivity extends Activity {
+
+ List<Integer> mIdsToRollback = new ArrayList<>();
+ Button mTriggerRollbackButton;
+ RollbackManager mRollbackManager;
+ static final String ROLLBACK_ID_EXTRA = "rollbackId";
+ static final String ACTION_NAME = MainActivity.class.getName();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ ListView rollbackListView = findViewById(R.id.listView);
+ mRollbackManager = getApplicationContext().getSystemService(RollbackManager.class);
+ initTriggerRollbackButton();
+
+ // Populate list of available rollbacks.
+ List<RollbackInfo> availableRollbacks = mRollbackManager.getAvailableRollbacks();
+ CustomAdapter adapter = new CustomAdapter(availableRollbacks);
+ rollbackListView.setAdapter(adapter);
+
+ // Register receiver for rollback status events.
+ getApplicationContext().registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context,
+ Intent intent) {
+ int rollbackId = intent.getIntExtra(ROLLBACK_ID_EXTRA, -1);
+ int rollbackStatusCode = intent.getIntExtra(RollbackManager.EXTRA_STATUS,
+ RollbackManager.STATUS_FAILURE);
+ String rollbackStatus = "FAILED";
+ if (rollbackStatusCode == RollbackManager.STATUS_SUCCESS) {
+ rollbackStatus = "SUCCESS";
+ }
+ makeToast("Status for rollback ID " + rollbackId + " is " + rollbackStatus);
+ }}, new IntentFilter(ACTION_NAME), Context.RECEIVER_NOT_EXPORTED);
+ }
+
+ private void initTriggerRollbackButton() {
+ mTriggerRollbackButton = findViewById(R.id.trigger_rollback_button);
+ mTriggerRollbackButton.setClickable(false);
+ mTriggerRollbackButton.setOnClickListener(v -> {
+ // Commits all selected rollbacks. Rollback status events will be sent to our receiver.
+ for (int i = 0; i < mIdsToRollback.size(); i++) {
+ Intent intent = new Intent(ACTION_NAME);
+ intent.putExtra(ROLLBACK_ID_EXTRA, mIdsToRollback.get(i));
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ getApplicationContext(), 0, intent, FLAG_MUTABLE);
+ mRollbackManager.commitRollback(mIdsToRollback.get(i),
+ Collections.emptyList(),
+ pendingIntent.getIntentSender());
+ }
+ });
+ }
+
+
+
+ private void makeToast(String message) {
+ runOnUiThread(() -> Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show());
+ }
+
+ public class CustomAdapter extends BaseAdapter {
+ List<RollbackInfo> mRollbackInfos;
+ LayoutInflater mInflater = LayoutInflater.from(getApplicationContext());
+
+ CustomAdapter(List<RollbackInfo> rollbackInfos) {
+ mRollbackInfos = rollbackInfos;
+ }
+
+ @Override
+ public int getCount() {
+ return mRollbackInfos.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mRollbackInfos.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mRollbackInfos.get(position).getRollbackId();
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ if (view == null) {
+ view = mInflater.inflate(R.layout.listitem_rollbackinfo, null);
+ }
+ RollbackInfo rollbackInfo = mRollbackInfos.get(position);
+ TextView rollbackIdView = view.findViewById(R.id.rollback_id);
+ rollbackIdView.setText("Rollback ID " + rollbackInfo.getRollbackId());
+ TextView rollbackPackagesTextView = view.findViewById(R.id.rollback_packages);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < rollbackInfo.getPackages().size(); i++) {
+ PackageRollbackInfo pkgInfo = rollbackInfo.getPackages().get(i);
+ sb.append(pkgInfo.getPackageName() + ": "
+ + pkgInfo.getVersionRolledBackFrom().getLongVersionCode() + " -> "
+ + pkgInfo.getVersionRolledBackTo().getLongVersionCode() + ",");
+ }
+ sb.deleteCharAt(sb.length() - 1);
+ rollbackPackagesTextView.setText(sb.toString());
+ CheckBox checkbox = view.findViewById(R.id.checkbox);
+ checkbox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (isChecked) {
+ mIdsToRollback.add(rollbackInfo.getRollbackId());
+ } else {
+ mIdsToRollback.remove(Integer.valueOf(rollbackInfo.getRollbackId()));
+ }
+ mTriggerRollbackButton.setClickable(mIdsToRollback.size() > 0);
+ });
+ return view;
+ }
+ }
+}
diff --git a/tools/localedata/OWNERS b/tools/localedata/OWNERS
new file mode 100644
index 0000000..2501679
--- /dev/null
+++ b/tools/localedata/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 24949
+include platform/external/icu:/OWNERS
diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp
index ff24d16..25373f9 100644
--- a/tools/validatekeymaps/Android.bp
+++ b/tools/validatekeymaps/Android.bp
@@ -21,6 +21,7 @@
cflags: [
"-Wall",
"-Werror",
+ "-Wextra",
],
static_libs: [
@@ -31,6 +32,9 @@
"liblog",
"libui-types",
],
+ shared_libs: [
+ "libvintf",
+ ],
target: {
host_linux: {
static_libs: [
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 991b280..cbfdfe4 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -97,6 +97,11 @@
case FileType::KEY_LAYOUT: {
base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(filename);
if (!ret.ok()) {
+ if (ret.error().message() == "Missing kernel config") {
+ // It means the layout is valid, but won't be loaded on this device because
+ // this layout requires a certain kernel config.
+ return true;
+ }
error("Error %s parsing key layout file.\n\n", ret.error().message().c_str());
return false;
}