Merge "OMS: handle overlay package upgrades, uninstalls"
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 1a207ba..8464e26 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -16,10 +16,14 @@
 
 package android.content.om;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Immutable overlay information about a package. All PackageInfos that
  * represent an overlay package will have a corresponding OverlayInfo.
@@ -27,6 +31,19 @@
  * @hide
  */
 public final class OverlayInfo implements Parcelable {
+
+    @IntDef(prefix = "STATE_", value = {
+            STATE_UNKNOWN,
+            STATE_MISSING_TARGET,
+            STATE_NO_IDMAP,
+            STATE_DISABLED,
+            STATE_ENABLED,
+            STATE_TARGET_UPGRADING,
+            STATE_OVERLAY_UPGRADING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {}
+
     /**
      * An internal state used as the initial state of an overlay. OverlayInfo
      * objects exposed outside the {@link
@@ -61,6 +78,18 @@
     public static final int STATE_ENABLED = 3;
 
     /**
+     * The target package is currently being upgraded; the state will change
+     * once the package installation has finished.
+     */
+    public static final int STATE_TARGET_UPGRADING = 4;
+
+    /**
+     * The overlay package is currently being upgraded; the state will change
+     * once the package installation has finished.
+     */
+    public static final int STATE_OVERLAY_UPGRADING = 5;
+
+    /**
      * Package name of the overlay package
      */
     public final String packageName;
@@ -77,13 +106,8 @@
 
     /**
      * The state of this OverlayInfo as defined by the STATE_* constants in this class.
-     *
-     * @see #STATE_MISSING_TARGET
-     * @see #STATE_NO_IDMAP
-     * @see #STATE_DISABLED
-     * @see #STATE_ENABLED
      */
-    public final int state;
+    public final @State int state;
 
     /**
      * User handle for which this overlay applies
@@ -96,13 +120,13 @@
      * @param source the source OverlayInfo to base the new instance on
      * @param state the new state for the source OverlayInfo
      */
-    public OverlayInfo(@NonNull OverlayInfo source, int state) {
+    public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
         this(source.packageName, source.targetPackageName, source.baseCodePath, state,
                 source.userId);
     }
 
     public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
-            @NonNull String baseCodePath, int state, int userId) {
+            @NonNull String baseCodePath, @State int state, int userId) {
         this.packageName = packageName;
         this.targetPackageName = targetPackageName;
         this.baseCodePath = baseCodePath;
@@ -136,6 +160,8 @@
             case STATE_NO_IDMAP:
             case STATE_DISABLED:
             case STATE_ENABLED:
+            case STATE_TARGET_UPGRADING:
+            case STATE_OVERLAY_UPGRADING:
                 break;
             default:
                 throw new IllegalArgumentException("State " + state + " is not a valid state");
@@ -156,7 +182,8 @@
         dest.writeInt(userId);
     }
 
-    public static final Parcelable.Creator<OverlayInfo> CREATOR = new Parcelable.Creator<OverlayInfo>() {
+    public static final Parcelable.Creator<OverlayInfo> CREATOR =
+            new Parcelable.Creator<OverlayInfo>() {
         @Override
         public OverlayInfo createFromParcel(Parcel source) {
             return new OverlayInfo(source);
@@ -189,14 +216,9 @@
      * Translate a state to a human readable string. Only intended for
      * debugging purposes.
      *
-     * @see #STATE_MISSING_TARGET
-     * @see #STATE_NO_IDMAP
-     * @see #STATE_DISABLED
-     * @see #STATE_ENABLED
-     *
      * @return a human readable String representing the state.
      */
-    public static String stateToString(int state) {
+    public static String stateToString(@State int state) {
         switch (state) {
             case STATE_UNKNOWN:
                 return "STATE_UNKNOWN";
@@ -208,6 +230,10 @@
                 return "STATE_DISABLED";
             case STATE_ENABLED:
                 return "STATE_ENABLED";
+            case STATE_TARGET_UPGRADING:
+                return "STATE_TARGET_UPGRADING";
+            case STATE_OVERLAY_UPGRADING:
+                return "STATE_OVERLAY_UPGRADING";
             default:
                 return "<unknown state>";
         }
diff --git a/core/tests/overlaytests/host/Android.mk b/core/tests/overlaytests/host/Android.mk
index d8e1fc1..b48a46b 100644
--- a/core/tests/overlaytests/host/Android.mk
+++ b/core/tests/overlaytests/host/Android.mk
@@ -24,7 +24,11 @@
     OverlayHostTests_BadSignatureOverlay \
     OverlayHostTests_PlatformSignatureStaticOverlay \
     OverlayHostTests_PlatformSignatureOverlay \
-    OverlayHostTests_PlatformSignatureOverlayV2
+    OverlayHostTests_UpdateOverlay \
+    OverlayHostTests_FrameworkOverlayV1 \
+    OverlayHostTests_FrameworkOverlayV2 \
+    OverlayHostTests_AppOverlayV1 \
+    OverlayHostTests_AppOverlayV2
 include $(BUILD_HOST_JAVA_LIBRARY)
 
 # Include to build test-apps.
diff --git a/core/tests/overlaytests/host/AndroidTest.xml b/core/tests/overlaytests/host/AndroidTest.xml
index 6884623..1f750a8 100644
--- a/core/tests/overlaytests/host/AndroidTest.xml
+++ b/core/tests/overlaytests/host/AndroidTest.xml
@@ -18,6 +18,11 @@
     <option name="test-tag" value="OverlayHostTests" />
     <option name="test-suite-tag" value="apct" />
 
+    <!-- Install the device tests that will be used to verify things on device. -->
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="OverlayHostTests_UpdateOverlay.apk" />
+    </target_preparer>
+
     <test class="com.android.tradefed.testtype.HostTest">
         <option name="class" value="com.android.server.om.hosttest.InstallOverlayTests" />
     </test>
diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
index 5093710..bf91a16 100644
--- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
+++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
@@ -23,14 +23,48 @@
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class InstallOverlayTests extends BaseHostJUnit4Test {
-
-    private static final String OVERLAY_PACKAGE_NAME =
+    private static final String SIG_OVERLAY_PACKAGE_NAME =
             "com.android.server.om.hosttest.signature_overlay";
+    private static final String APP_OVERLAY_PACKAGE_NAME =
+            "com.android.server.om.hosttest.app_overlay";
+    private static final String FRAMEWORK_OVERLAY_PACKAGE_NAME =
+            "com.android.server.om.hosttest.framework_overlay";
+    private static final String[] ALL_PACKAGES = new String[] {
+            SIG_OVERLAY_PACKAGE_NAME, APP_OVERLAY_PACKAGE_NAME, FRAMEWORK_OVERLAY_PACKAGE_NAME
+    };
+
+    private static final String DEVICE_TEST_PKG =
+            "com.android.server.om.hosttest.update_overlay_test";
+    private static final String DEVICE_TEST_CLS = DEVICE_TEST_PKG + ".UpdateOverlayTest";
+
+    @Before
+    public void ensureNoOverlays() throws Exception {
+        // Make sure we're starting with a clean slate.
+        for (String pkg : ALL_PACKAGES) {
+            assertFalse(pkg + " should not be installed", isPackageInstalled(pkg));
+            assertFalse(pkg + " should not be registered with overlay manager service",
+                    overlayManagerContainsPackage(pkg));
+        }
+    }
+
+    /*
+    For some reason, SuiteApkInstaller is *not* uninstalling overlays, even though #installPackage()
+    claims it will auto-clean.
+    TODO(b/72877546): Remove when auto-clean is fixed.
+     */
+    @After
+    public void uninstallOverlays() throws Exception {
+        for (String pkg : ALL_PACKAGES) {
+            uninstallPackage(pkg);
+        }
+    }
 
     @Test
     public void failToInstallNonPlatformSignedOverlay() throws Exception {
@@ -40,7 +74,7 @@
         } catch (Exception e) {
             // Expected.
         }
-        assertFalse(overlayManagerContainsPackage());
+        assertFalse(overlayManagerContainsPackage(SIG_OVERLAY_PACKAGE_NAME));
     }
 
     @Test
@@ -51,28 +85,64 @@
         } catch (Exception e) {
             // Expected.
         }
-        assertFalse(overlayManagerContainsPackage());
+        assertFalse(overlayManagerContainsPackage(SIG_OVERLAY_PACKAGE_NAME));
     }
 
     @Test
-    public void succeedToInstallPlatformSignedOverlay() throws Exception {
+    public void installPlatformSignedOverlay() throws Exception {
         installPackage("OverlayHostTests_PlatformSignatureOverlay.apk");
-        assertTrue(overlayManagerContainsPackage());
+        assertTrue(overlayManagerContainsPackage(SIG_OVERLAY_PACKAGE_NAME));
     }
 
     @Test
-    public void succeedToInstallPlatformSignedOverlayAndUpdate() throws Exception {
-        installPackage("OverlayHostTests_PlatformSignatureOverlay.apk");
-        assertTrue(overlayManagerContainsPackage());
-        assertEquals("v1", getDevice().getAppPackageInfo(OVERLAY_PACKAGE_NAME).getVersionName());
+    public void installPlatformSignedAppOverlayAndUpdate() throws Exception {
+        assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS, "expectAppResource"));
 
-        installPackage("OverlayHostTests_PlatformSignatureOverlayV2.apk");
-        assertTrue(overlayManagerContainsPackage());
-        assertEquals("v2", getDevice().getAppPackageInfo(OVERLAY_PACKAGE_NAME).getVersionName());
+        installPackage("OverlayHostTests_AppOverlayV1.apk");
+        setOverlayEnabled(APP_OVERLAY_PACKAGE_NAME, true);
+        assertTrue(overlayManagerContainsPackage(APP_OVERLAY_PACKAGE_NAME));
+        assertEquals("v1", getDevice()
+                .getAppPackageInfo(APP_OVERLAY_PACKAGE_NAME)
+                .getVersionName());
+        assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS,
+                "expectAppOverlayV1Resource"));
+
+        installPackage("OverlayHostTests_AppOverlayV2.apk");
+        assertTrue(overlayManagerContainsPackage(APP_OVERLAY_PACKAGE_NAME));
+        assertEquals("v2", getDevice()
+                .getAppPackageInfo(APP_OVERLAY_PACKAGE_NAME)
+                .getVersionName());
+        assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS,
+                "expectAppOverlayV2Resource"));
     }
 
-    private boolean overlayManagerContainsPackage() throws Exception {
-        return getDevice().executeShellCommand("cmd overlay list")
-                .contains(OVERLAY_PACKAGE_NAME);
+    @Test
+    public void installPlatformSignedFrameworkOverlayAndUpdate() throws Exception {
+        assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS, "expectAppResource"));
+
+        installPackage("OverlayHostTests_FrameworkOverlayV1.apk");
+        setOverlayEnabled(FRAMEWORK_OVERLAY_PACKAGE_NAME, true);
+        assertTrue(overlayManagerContainsPackage(FRAMEWORK_OVERLAY_PACKAGE_NAME));
+        assertEquals("v1", getDevice()
+                .getAppPackageInfo(FRAMEWORK_OVERLAY_PACKAGE_NAME)
+                .getVersionName());
+        assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS,
+                "expectFrameworkOverlayV1Resource"));
+
+        installPackage("OverlayHostTests_FrameworkOverlayV2.apk");
+        assertTrue(overlayManagerContainsPackage(FRAMEWORK_OVERLAY_PACKAGE_NAME));
+        assertEquals("v2", getDevice()
+                .getAppPackageInfo(FRAMEWORK_OVERLAY_PACKAGE_NAME)
+                .getVersionName());
+        assertTrue(runDeviceTests(DEVICE_TEST_PKG, DEVICE_TEST_CLS,
+                "expectFrameworkOverlayV2Resource"));
+    }
+
+    private void setOverlayEnabled(String pkg, boolean enabled) throws Exception {
+        getDevice().executeShellCommand("cmd overlay " + (enabled ? "enable " : "disable ") + pkg);
+    }
+
+    private boolean overlayManagerContainsPackage(String pkg) throws Exception {
+        return getDevice().executeShellCommand("cmd overlay list").contains(pkg);
     }
 }
diff --git a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
index b051a82..4249549 100644
--- a/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
+++ b/core/tests/overlaytests/host/test-apps/SignatureOverlay/Android.mk
@@ -40,13 +40,4 @@
 LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
 include $(BUILD_PACKAGE)
 
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := OverlayHostTests_PlatformSignatureOverlayV2
-LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_CERTIFICATE := platform
-LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
-LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
-include $(BUILD_PACKAGE)
-
 my_package_prefix :=
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
new file mode 100644
index 0000000..bd6d73d
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/Android.mk
@@ -0,0 +1,73 @@
+# Copyright (C) 2018 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_PACKAGE_NAME := OverlayHostTests_UpdateOverlay
+LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+include $(BUILD_PACKAGE)
+
+my_package_prefix := com.android.server.om.hosttest.framework_overlay
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV1
+LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_CERTIFICATE := platform
+LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
+LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/framework/v1/res
+LOCAL_MANIFEST_FILE := framework/AndroidManifest.xml
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayHostTests_FrameworkOverlayV2
+LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_CERTIFICATE := platform
+LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
+LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/framework/v2/res
+LOCAL_MANIFEST_FILE := framework/AndroidManifest.xml
+include $(BUILD_PACKAGE)
+
+my_package_prefix := com.android.server.om.hosttest.app_overlay
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV1
+LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_CERTIFICATE := platform
+LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v1
+LOCAL_AAPT_FLAGS += --version-code 1 --version-name v1
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/v1/res
+LOCAL_MANIFEST_FILE := app/AndroidManifest.xml
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := OverlayHostTests_AppOverlayV2
+LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_CERTIFICATE := platform
+LOCAL_AAPT_FLAGS := --custom-package $(my_package_prefix)_v2
+LOCAL_AAPT_FLAGS += --version-code 2 --version-name v2
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/app/v2/res
+LOCAL_MANIFEST_FILE := app/AndroidManifest.xml
+include $(BUILD_PACKAGE)
+
+my_package_prefix :=
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..06077a7
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.server.om.hosttest.update_overlay_test">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.server.om.hosttest.update_overlay_test"
+        android:label="Update Overlay Test"/>
+</manifest>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/AndroidManifest.xml
new file mode 100644
index 0000000..73804eb
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.server.om.hosttest.app_overlay">
+    <overlay android:targetPackage="com.android.server.om.hosttest.update_overlay_test" />
+</manifest>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v1/res/values/values.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v1/res/values/values.xml
new file mode 100644
index 0000000..63f85c2
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v1/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="app_resource">App Resource Overlay V1</string>
+</resources>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v2/res/values/values.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v2/res/values/values.xml
new file mode 100644
index 0000000..fa4a697
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/app/v2/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="app_resource">App Resource Overlay V2</string>
+</resources>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/AndroidManifest.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/AndroidManifest.xml
new file mode 100644
index 0000000..8c8fe94
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.server.om.hosttest.framework_overlay">
+    <overlay android:targetPackage="android" />
+</manifest>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/v1/res/values/values.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/v1/res/values/values.xml
new file mode 100644
index 0000000..fedb2c6
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/v1/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="ok">Framework Overlay V1</string>
+</resources>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/v2/res/values/values.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/v2/res/values/values.xml
new file mode 100644
index 0000000..8aebf483
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/framework/v2/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="ok">Framework Overlay V2</string>
+</resources>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/res/values/values.xml b/core/tests/overlaytests/host/test-apps/UpdateOverlay/res/values/values.xml
new file mode 100644
index 0000000..7393166
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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="app_resource">App Resource</string>
+</resources>
diff --git a/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
new file mode 100644
index 0000000..d46bb37
--- /dev/null
+++ b/core/tests/overlaytests/host/test-apps/UpdateOverlay/src/com/android/server/om/hosttest/update_overlay_test/UpdateOverlayTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 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.om.hosttest.update_overlay_test;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UpdateOverlayTest {
+    private Resources mResources;
+
+    @Before
+    public void setUp() throws Exception {
+        final Configuration defaultLocaleConfiguration = new Configuration();
+        defaultLocaleConfiguration.setLocale(Locale.US);
+        mResources = InstrumentationRegistry
+                .getInstrumentation()
+                .getContext()
+                .createConfigurationContext(defaultLocaleConfiguration)
+                .getResources();
+    }
+
+    @Test
+    public void expectAppResource() throws Exception {
+        assertEquals("App Resource", mResources.getString(R.string.app_resource));
+    }
+
+    @Test
+    public void expectAppOverlayV1Resource() throws Exception {
+        assertEquals("App Resource Overlay V1", mResources.getString(R.string.app_resource));
+    }
+
+    @Test
+    public void expectAppOverlayV2Resource() throws Exception {
+        assertEquals("App Resource Overlay V2", mResources.getString(R.string.app_resource));
+    }
+
+    @Test
+    public void expectFrameworkOverlayResource() throws Exception {
+        assertEquals("OK", mResources.getString(android.R.string.ok));
+    }
+
+    @Test
+    public void expectFrameworkOverlayV1Resource() throws Exception {
+        assertEquals("Framework Overlay V1", mResources.getString(android.R.string.ok));
+    }
+
+    @Test
+    public void expectFrameworkOverlayV2Resource() throws Exception {
+        assertEquals("Framework Overlay V2", mResources.getString(android.R.string.ok));
+    }
+}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 7600e81..6e02db7 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -20,6 +20,8 @@
 import static android.content.om.OverlayInfo.STATE_ENABLED;
 import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
 import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
+import static android.content.om.OverlayInfo.STATE_OVERLAY_UPGRADING;
+import static android.content.om.OverlayInfo.STATE_TARGET_UPGRADING;
 
 import static com.android.server.om.OverlayManagerService.DEBUG;
 import static com.android.server.om.OverlayManagerService.TAG;
@@ -50,6 +52,10 @@
  * @see OverlayManagerService
  */
 final class OverlayManagerServiceImpl {
+    // Flags to use in conjunction with updateState.
+    private static final int FLAG_TARGET_IS_UPGRADING = 1<<0;
+    private static final int FLAG_OVERLAY_IS_UPGRADING = 1<<1;
+
     private final PackageManagerHelper mPackageManager;
     private final IdmapManager mIdmapManager;
     private final OverlayManagerSettings mSettings;
@@ -123,9 +129,7 @@
             }
 
             try {
-                final PackageInfo targetPackage =
-                        mPackageManager.getPackageInfo(overlayPackage.overlayTarget, newUserId);
-                updateState(targetPackage, overlayPackage, newUserId);
+                updateState(overlayPackage.overlayTarget, overlayPackage.packageName, newUserId, 0);
             } catch (OverlayManagerSettings.BadKeyException e) {
                 Slog.e(TAG, "failed to update settings", e);
                 mSettings.remove(overlayPackage.packageName, newUserId);
@@ -168,8 +172,7 @@
             Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
         }
 
-        final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
-        if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+        if (updateAllOverlaysForTarget(packageName, userId, 0)) {
             mListener.onOverlaysChanged(packageName, userId);
         }
     }
@@ -179,18 +182,18 @@
             Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
         }
 
-        final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
-        if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+        if (updateAllOverlaysForTarget(packageName, userId, 0)) {
             mListener.onOverlaysChanged(packageName, userId);
         }
     }
 
     void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
-            Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId=" + userId);
+            Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId="
+                    + userId);
         }
 
-        if (updateAllOverlaysForTarget(packageName, userId, null)) {
+        if (updateAllOverlaysForTarget(packageName, userId, FLAG_TARGET_IS_UPGRADING)) {
             mListener.onOverlaysChanged(packageName, userId);
         }
     }
@@ -200,8 +203,7 @@
             Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
         }
 
-        final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
-        if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+        if (updateAllOverlaysForTarget(packageName, userId, 0)) {
             mListener.onOverlaysChanged(packageName, userId);
         }
     }
@@ -211,7 +213,7 @@
             Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
         }
 
-        if (updateAllOverlaysForTarget(packageName, userId, null)) {
+        if (updateAllOverlaysForTarget(packageName, userId, 0)) {
             mListener.onOverlaysChanged(packageName, userId);
         }
     }
@@ -219,20 +221,21 @@
     /**
      * Returns true if the settings were modified for this target.
      */
-    private boolean updateAllOverlaysForTarget(@NonNull final String packageName, final int userId,
-            @Nullable final PackageInfo targetPackage) {
+    private boolean updateAllOverlaysForTarget(@NonNull final String targetPackageName,
+            final int userId, final int flags) {
         boolean modified = false;
-        final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(packageName, userId);
+        final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId);
         final int N = ois.size();
         for (int i = 0; i < N; i++) {
             final OverlayInfo oi = ois.get(i);
-            final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, userId);
+            final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
+                    userId);
             if (overlayPackage == null) {
                 modified |= mSettings.remove(oi.packageName, oi.userId);
                 removeIdmapIfPossible(oi);
             } else {
                 try {
-                    modified |= updateState(targetPackage, overlayPackage, userId);
+                    modified |= updateState(targetPackageName, oi.packageName, userId, flags);
                 } catch (OverlayManagerSettings.BadKeyException e) {
                     Slog.e(TAG, "failed to update settings", e);
                     modified |= mSettings.remove(oi.packageName, userId);
@@ -254,14 +257,11 @@
             return;
         }
 
-        final PackageInfo targetPackage =
-                mPackageManager.getPackageInfo(overlayPackage.overlayTarget, userId);
-
         mSettings.init(packageName, userId, overlayPackage.overlayTarget,
                 overlayPackage.applicationInfo.getBaseCodePath(),
                 overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority);
         try {
-            if (updateState(targetPackage, overlayPackage, userId)) {
+            if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
                 mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
             }
         } catch (OverlayManagerSettings.BadKeyException e) {
@@ -271,15 +271,64 @@
     }
 
     void onOverlayPackageChanged(@NonNull final String packageName, final int userId) {
-        Slog.wtf(TAG, "onOverlayPackageChanged called, but only pre-installed overlays supported");
+        if (DEBUG) {
+            Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId);
+        }
+
+        try {
+            final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
+            if (updateState(oi.targetPackageName, packageName, userId, 0)) {
+                mListener.onOverlaysChanged(oi.targetPackageName, userId);
+            }
+        } catch (OverlayManagerSettings.BadKeyException e) {
+            Slog.e(TAG, "failed to update settings", e);
+        }
     }
 
     void onOverlayPackageUpgrading(@NonNull final String packageName, final int userId) {
-        Slog.wtf(TAG, "onOverlayPackageUpgrading called, but only pre-installed overlays supported");
+        if (DEBUG) {
+            Slog.d(TAG, "onOverlayPackageUpgrading packageName=" + packageName + " userId="
+                    + userId);
+        }
+
+        try {
+            final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
+            if (updateState(oi.targetPackageName, packageName, userId, FLAG_OVERLAY_IS_UPGRADING)) {
+                removeIdmapIfPossible(oi);
+                mListener.onOverlaysChanged(oi.targetPackageName, userId);
+            }
+        } catch (OverlayManagerSettings.BadKeyException e) {
+            Slog.e(TAG, "failed to update settings", e);
+        }
     }
 
     void onOverlayPackageUpgraded(@NonNull final String packageName, final int userId) {
-        Slog.wtf(TAG, "onOverlayPackageUpgraded called, but only pre-installed overlays supported");
+        if (DEBUG) {
+            Slog.d(TAG, "onOverlayPackageUpgraded packageName=" + packageName + " userId="
+                    + userId);
+        }
+
+        final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId);
+        if (pkg == null) {
+            Slog.w(TAG, "overlay package " + packageName + " was upgraded, but couldn't be found");
+            onOverlayPackageRemoved(packageName, userId);
+            return;
+        }
+
+        try {
+            final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId);
+            if (!oldOi.targetPackageName.equals(pkg.overlayTarget)) {
+                mSettings.init(packageName, userId, pkg.overlayTarget,
+                        pkg.applicationInfo.getBaseCodePath(), pkg.isStaticOverlayPackage(),
+                        pkg.overlayPriority);
+            }
+
+            if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
+                mListener.onOverlaysChanged(pkg.overlayTarget, userId);
+            }
+        } catch (OverlayManagerSettings.BadKeyException e) {
+            Slog.e(TAG, "failed to update settings", e);
+        }
     }
 
     void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) {
@@ -333,10 +382,8 @@
 
         try {
             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
-            final PackageInfo targetPackage =
-                    mPackageManager.getPackageInfo(oi.targetPackageName, userId);
             boolean modified = mSettings.setEnabled(packageName, userId, enable);
-            modified |= updateState(targetPackage, overlayPackage, userId);
+            modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
 
             if (modified) {
                 mListener.onOverlaysChanged(oi.targetPackageName, userId);
@@ -349,7 +396,8 @@
 
     boolean setEnabledExclusive(@NonNull final String packageName, final int userId) {
         if (DEBUG) {
-            Slog.d(TAG, String.format("setEnabledExclusive packageName=%s userId=%d", packageName, userId));
+            Slog.d(TAG, String.format("setEnabledExclusive packageName=%s userId=%d", packageName,
+                    userId));
         }
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
@@ -359,10 +407,9 @@
 
         try {
             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
-            final PackageInfo targetPackage =
-                    mPackageManager.getPackageInfo(oi.targetPackageName, userId);
+            final String targetPackageName = oi.targetPackageName;
 
-            List<OverlayInfo> allOverlays = getOverlayInfosForTarget(oi.targetPackageName, userId);
+            List<OverlayInfo> allOverlays = getOverlayInfosForTarget(targetPackageName, userId);
 
             boolean modified = false;
 
@@ -384,15 +431,15 @@
 
                 // Disable the overlay.
                 modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false);
-                modified |= updateState(targetPackage, disabledOverlayPackageInfo, userId);
+                modified |= updateState(targetPackageName, disabledOverlayPackageName, userId, 0);
             }
 
             // Enable the selected overlay.
             modified |= mSettings.setEnabled(packageName, userId, true);
-            modified |= updateState(targetPackage, overlayPackage, userId);
+            modified |= updateState(targetPackageName, packageName, userId, 0);
 
             if (modified) {
-                mListener.onOverlaysChanged(oi.targetPackageName, userId);
+                mListener.onOverlaysChanged(targetPackageName, userId);
             }
             return true;
         } catch (OverlayManagerSettings.BadKeyException e) {
@@ -477,7 +524,8 @@
 
     List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
             final int userId) {
-        final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, userId);
+        final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
+                userId);
         final List<String> paths = new ArrayList<>(overlays.size());
         final int N = overlays.size();
         for (int i = 0; i < N; i++) {
@@ -492,36 +540,59 @@
     /**
      * Returns true if the settings/state was modified, false otherwise.
      */
-    private boolean updateState(@Nullable final PackageInfo targetPackage,
-            @NonNull final PackageInfo overlayPackage, final int userId)
+    private boolean updateState(@NonNull final String targetPackageName,
+            @NonNull final String overlayPackageName, final int userId, final int flags)
             throws OverlayManagerSettings.BadKeyException {
+
+        final PackageInfo targetPackage = mPackageManager.getPackageInfo(targetPackageName, userId);
+        final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName,
+                userId);
+
         // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
-        if (targetPackage != null &&
-                !("android".equals(targetPackage.packageName)
+        if (targetPackage != null && overlayPackage != null &&
+                !("android".equals(targetPackageName)
                         && overlayPackage.isStaticOverlayPackage())) {
             mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
         }
 
-        boolean modified = mSettings.setBaseCodePath(overlayPackage.packageName, userId,
-                overlayPackage.applicationInfo.getBaseCodePath());
+        boolean modified = false;
+        if (overlayPackage != null) {
+            modified |= mSettings.setBaseCodePath(overlayPackageName, userId,
+                    overlayPackage.applicationInfo.getBaseCodePath());
+        }
 
-        final int currentState = mSettings.getState(overlayPackage.packageName, userId);
-        final int newState = calculateNewState(targetPackage, overlayPackage, userId);
+        final @OverlayInfo.State int currentState = mSettings.getState(overlayPackageName, userId);
+        final @OverlayInfo.State int newState = calculateNewState(targetPackage, overlayPackage,
+                userId, flags);
         if (currentState != newState) {
             if (DEBUG) {
                 Slog.d(TAG, String.format("%s:%d: %s -> %s",
-                            overlayPackage.packageName, userId,
+                            overlayPackageName, userId,
                             OverlayInfo.stateToString(currentState),
                             OverlayInfo.stateToString(newState)));
             }
-            modified |= mSettings.setState(overlayPackage.packageName, userId, newState);
+            modified |= mSettings.setState(overlayPackageName, userId, newState);
         }
         return modified;
     }
 
-    private int calculateNewState(@Nullable final PackageInfo targetPackage,
-            @NonNull final PackageInfo overlayPackage, final int userId)
+    private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage,
+            @Nullable final PackageInfo overlayPackage, final int userId, final int flags)
         throws OverlayManagerSettings.BadKeyException {
+
+        if ((flags & FLAG_TARGET_IS_UPGRADING) != 0) {
+            return STATE_TARGET_UPGRADING;
+        }
+
+        if ((flags & FLAG_OVERLAY_IS_UPGRADING) != 0) {
+            return STATE_OVERLAY_UPGRADING;
+        }
+
+        // assert expectation on overlay package: can only be null if the flags are used
+        if (DEBUG && overlayPackage == null) {
+            throw new IllegalArgumentException("null overlay package not compatible with no flags");
+        }
+
         if (targetPackage == null) {
             return STATE_MISSING_TARGET;
         }
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 17b38de..a80cae4 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -145,7 +145,8 @@
         return mItems.get(idx).setEnabled(enable);
     }
 
-    int getState(@NonNull final String packageName, final int userId) throws BadKeyException {
+    @OverlayInfo.State int getState(@NonNull final String packageName, final int userId)
+            throws BadKeyException {
         final int idx = select(packageName, userId);
         if (idx < 0) {
             throw new BadKeyException(packageName, userId);
@@ -156,8 +157,8 @@
     /**
      * Returns true if the settings were modified, false if they remain the same.
      */
-    boolean setState(@NonNull final String packageName, final int userId, final int state)
-            throws BadKeyException {
+    boolean setState(@NonNull final String packageName, final int userId,
+            final @OverlayInfo.State int state) throws BadKeyException {
         final int idx = select(packageName, userId);
         if (idx < 0) {
             throw new BadKeyException(packageName, userId);
@@ -413,7 +414,7 @@
         private final String mPackageName;
         private final String mTargetPackageName;
         private String mBaseCodePath;
-        private int mState;
+        private @OverlayInfo.State int mState;
         private boolean mIsEnabled;
         private OverlayInfo mCache;
         private boolean mIsStatic;
@@ -421,7 +422,7 @@
 
         SettingsItem(@NonNull final String packageName, final int userId,
                 @NonNull final String targetPackageName, @NonNull final String baseCodePath,
-                final int state, final boolean isEnabled, final boolean isStatic,
+                final @OverlayInfo.State int state, final boolean isEnabled, final boolean isStatic,
                 final int priority) {
             mPackageName = packageName;
             mUserId = userId;
@@ -462,11 +463,11 @@
             return false;
         }
 
-        private int getState() {
+        private @OverlayInfo.State int getState() {
             return mState;
         }
 
-        private boolean setState(final int state) {
+        private boolean setState(final @OverlayInfo.State int state) {
             if (mState != state) {
                 mState = state;
                 invalidateCache();