Merge changes I33358ce4,I5746ffae,Ie307ce30,I8cd5eb46,Ie02ddd37, ... am: 0b5e65b81d am: 77ad0e767c
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1550792 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Idbf5c68c939202662e62e4031f128f40c805af52
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package android.content.om;
|
||||
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayManagerTransaction;
|
||||
|
||||
/**
|
||||
* Api for getting information about overlay packages.
|
||||
@@ -163,4 +164,18 @@ interface IOverlayManager {
|
||||
* @param packageName The name of the overlay package whose idmap should be deleted.
|
||||
*/
|
||||
void invalidateCachesForOverlay(in String packageName, in int userIs);
|
||||
|
||||
/**
|
||||
* Perform a series of requests related to overlay packages. This is an
|
||||
* atomic operation: either all requests were performed successfully and
|
||||
* the changes were propagated to the rest of the system, or at least one
|
||||
* request could not be performed successfully and nothing is changed and
|
||||
* nothing is propagated to the rest of the system.
|
||||
*
|
||||
* @see OverlayManagerTransaction
|
||||
*
|
||||
* @param transaction the series of overlay related requests to perform
|
||||
* @throws SecurityException if the transaction failed
|
||||
*/
|
||||
void commit(in OverlayManagerTransaction transaction);
|
||||
}
|
||||
|
||||
@@ -253,6 +253,29 @@ public class OverlayManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a series of requests related to overlay packages. This is an
|
||||
* atomic operation: either all requests were performed successfully and
|
||||
* the changes were propagated to the rest of the system, or at least one
|
||||
* request could not be performed successfully and nothing is changed and
|
||||
* nothing is propagated to the rest of the system.
|
||||
*
|
||||
* @see OverlayManagerTransaction
|
||||
*
|
||||
* @param transaction the series of overlay related requests to perform
|
||||
* @throws Exception if not all the requests could be successfully and
|
||||
* atomically executed
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void commit(@NonNull final OverlayManagerTransaction transaction) {
|
||||
try {
|
||||
mService.commit(transaction);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting on R, actor enforcement and app visibility changes introduce additional failure
|
||||
* cases, but the SecurityException thrown with these checks is unexpected for existing
|
||||
|
||||
19
core/java/android/content/om/OverlayManagerTransaction.aidl
Normal file
19
core/java/android/content/om/OverlayManagerTransaction.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.content.om;
|
||||
|
||||
parcelable OverlayManagerTransaction;
|
||||
216
core/java/android/content/om/OverlayManagerTransaction.java
Normal file
216
core/java/android/content/om/OverlayManagerTransaction.java
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.content.om;
|
||||
|
||||
import static com.android.internal.util.Preconditions.checkNotNull;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Container for a batch of requests to the OverlayManagerService.
|
||||
*
|
||||
* Transactions are created using a builder interface. Example usage:
|
||||
*
|
||||
* final OverlayManager om = ctx.getSystemService(OverlayManager.class);
|
||||
* final OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
|
||||
* .setEnabled(...)
|
||||
* .setEnabled(...)
|
||||
* .build();
|
||||
* om.commit(t);
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class OverlayManagerTransaction
|
||||
implements Iterable<OverlayManagerTransaction.Request>, Parcelable {
|
||||
// TODO: remove @hide from this class when OverlayManager is added to the
|
||||
// SDK, but keep OverlayManagerTransaction.Request @hidden
|
||||
private final List<Request> mRequests;
|
||||
|
||||
OverlayManagerTransaction(@NonNull final List<Request> requests) {
|
||||
checkNotNull(requests);
|
||||
if (requests.contains(null)) {
|
||||
throw new IllegalArgumentException("null request");
|
||||
}
|
||||
mRequests = requests;
|
||||
}
|
||||
|
||||
private OverlayManagerTransaction(@NonNull final Parcel source) {
|
||||
final int size = source.readInt();
|
||||
mRequests = new ArrayList<Request>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
final int request = source.readInt();
|
||||
final String packageName = source.readString();
|
||||
final int userId = source.readInt();
|
||||
mRequests.add(new Request(request, packageName, userId));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Request> iterator() {
|
||||
return mRequests.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("OverlayManagerTransaction { mRequests = %s }", mRequests);
|
||||
}
|
||||
|
||||
/**
|
||||
* A single unit of the transaction, such as a request to enable an
|
||||
* overlay, or to disable an overlay.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static class Request {
|
||||
@IntDef(prefix = "TYPE_", value = {
|
||||
TYPE_SET_ENABLED,
|
||||
TYPE_SET_DISABLED,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface RequestType {}
|
||||
|
||||
public static final int TYPE_SET_ENABLED = 0;
|
||||
public static final int TYPE_SET_DISABLED = 1;
|
||||
|
||||
@RequestType public final int type;
|
||||
public final String packageName;
|
||||
public final int userId;
|
||||
|
||||
public Request(@RequestType final int type, @NonNull final String packageName,
|
||||
final int userId) {
|
||||
this.type = type;
|
||||
this.packageName = packageName;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Request{type=0x%02x (%s), packageName=%s, userId=%d}",
|
||||
type, typeToString(), packageName, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate the request type into a human readable string. Only
|
||||
* intended for debugging.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public String typeToString() {
|
||||
switch (type) {
|
||||
case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED";
|
||||
case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED";
|
||||
default: return String.format("TYPE_UNKNOWN (0x%02x)", type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for OverlayManagerTransaction objects.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static class Builder {
|
||||
private final List<Request> mRequests = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Request that an overlay package be enabled and change its loading
|
||||
* order to the last package to be loaded, or disabled
|
||||
*
|
||||
* If the caller has the correct permissions, it is always possible to
|
||||
* disable an overlay. Due to technical and security reasons it may not
|
||||
* always be possible to enable an overlay, for instance if the overlay
|
||||
* does not successfully overlay any target resources due to
|
||||
* overlayable policy restrictions.
|
||||
*
|
||||
* An enabled overlay is a part of target package's resources, i.e. it will
|
||||
* be part of any lookups performed via {@link android.content.res.Resources}
|
||||
* and {@link android.content.res.AssetManager}. A disabled overlay will no
|
||||
* longer affect the resources of the target package. If the target is
|
||||
* currently running, its outdated resources will be replaced by new ones.
|
||||
*
|
||||
* @param packageName The name of the overlay package.
|
||||
* @param enable true to enable the overlay, false to disable it.
|
||||
* @return this Builder object, so you can chain additional requests
|
||||
*/
|
||||
public Builder setEnabled(@NonNull String packageName, boolean enable) {
|
||||
return setEnabled(packageName, enable, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public Builder setEnabled(@NonNull String packageName, boolean enable, int userId) {
|
||||
checkNotNull(packageName);
|
||||
@Request.RequestType final int type =
|
||||
enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED;
|
||||
mRequests.add(new Request(type, packageName, userId));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new transaction out of the requests added so far. Execute
|
||||
* the transaction by calling OverlayManager#commit.
|
||||
*
|
||||
* @see OverlayManager#commit
|
||||
* @return a new transaction
|
||||
*/
|
||||
public OverlayManagerTransaction build() {
|
||||
return new OverlayManagerTransaction(mRequests);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
final int size = mRequests.size();
|
||||
dest.writeInt(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
final Request req = mRequests.get(i);
|
||||
dest.writeInt(req.type);
|
||||
dest.writeString(req.packageName);
|
||||
dest.writeInt(req.userId);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<OverlayManagerTransaction> CREATOR =
|
||||
new Parcelable.Creator<OverlayManagerTransaction>() {
|
||||
|
||||
@Override
|
||||
public OverlayManagerTransaction createFromParcel(Parcel source) {
|
||||
return new OverlayManagerTransaction(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OverlayManagerTransaction[] newArray(int size) {
|
||||
return new OverlayManagerTransaction[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -16,7 +16,11 @@ android_test {
|
||||
name: "OverlayDeviceTests",
|
||||
srcs: ["src/**/*.java"],
|
||||
platform_apis: true,
|
||||
static_libs: ["androidx.test.rules"],
|
||||
certificate: "platform",
|
||||
static_libs: [
|
||||
"androidx.test.rules",
|
||||
"testng",
|
||||
],
|
||||
test_suites: ["device-tests"],
|
||||
data: [
|
||||
":OverlayDeviceTests_AppOverlayOne",
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" />
|
||||
|
||||
<uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner"/>
|
||||
</application>
|
||||
|
||||
@@ -19,9 +19,20 @@
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<option name="test-suite-tag" value="apct-instrumentation" />
|
||||
|
||||
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="remount-system" value="true" />
|
||||
<option name="push" value="OverlayDeviceTests.apk->/system/app/OverlayDeviceTests.apk" />
|
||||
</target_preparer>
|
||||
|
||||
<!-- Reboot to have the test APK scanned by PM and reboot after to remove the test APK. -->
|
||||
<target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer">
|
||||
<option name="pre-reboot" value="true" />
|
||||
<option name="post-reboot" value="true" />
|
||||
</target_preparer>
|
||||
|
||||
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
|
||||
<option name="cleanup-apks" value="true" />
|
||||
<option name="test-file-name" value="OverlayDeviceTests.apk" />
|
||||
<option name="test-file-name" value="OverlayDeviceTests_AppOverlayOne.apk" />
|
||||
<option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" />
|
||||
<option name="test-file-name" value="OverlayDeviceTests_FrameworkOverlay.apk" />
|
||||
|
||||
@@ -18,60 +18,76 @@ package com.android.overlaytest;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import android.app.UiAutomation;
|
||||
import android.content.res.Resources;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.om.OverlayManager;
|
||||
import android.content.om.OverlayManagerTransaction;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
class LocalOverlayManager {
|
||||
private static final long TIMEOUT = 30;
|
||||
|
||||
public static void setEnabledAndWait(Executor executor, final String packageName,
|
||||
boolean enable) throws Exception {
|
||||
final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName;
|
||||
if (executeShellCommand("cmd overlay list").contains(pattern)) {
|
||||
// nothing to do, overlay already in the requested state
|
||||
return;
|
||||
public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable,
|
||||
@NonNull final String[] overlaysToDisable) throws Exception {
|
||||
final int userId = UserHandle.myUserId();
|
||||
OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder();
|
||||
for (String pkg : overlaysToEnable) {
|
||||
builder.setEnabled(pkg, true, userId);
|
||||
}
|
||||
for (String pkg : overlaysToDisable) {
|
||||
builder.setEnabled(pkg, false, userId);
|
||||
}
|
||||
OverlayManagerTransaction transaction = builder.build();
|
||||
|
||||
final Resources res = InstrumentationRegistry.getContext().getResources();
|
||||
final String[] oldApkPaths = res.getAssets().getApkPaths();
|
||||
final Context ctx = InstrumentationRegistry.getTargetContext();
|
||||
FutureTask<Boolean> task = new FutureTask<>(() -> {
|
||||
while (true) {
|
||||
if (!Arrays.equals(oldApkPaths, res.getAssets().getApkPaths())) {
|
||||
final String[] paths = ctx.getResources().getAssets().getApkPaths();
|
||||
if (arrayTailContains(paths, overlaysToEnable)
|
||||
&& arrayDoesNotContain(paths, overlaysToDisable)) {
|
||||
return true;
|
||||
}
|
||||
Thread.sleep(10);
|
||||
}
|
||||
});
|
||||
|
||||
OverlayManager om = ctx.getSystemService(OverlayManager.class);
|
||||
om.commit(transaction);
|
||||
|
||||
Executor executor = (cmd) -> new Thread(cmd).start();
|
||||
executor.execute(task);
|
||||
executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName);
|
||||
task.get(TIMEOUT, SECONDS);
|
||||
}
|
||||
|
||||
private static String executeShellCommand(final String command)
|
||||
throws Exception {
|
||||
final UiAutomation uiAutomation =
|
||||
InstrumentationRegistry.getInstrumentation().getUiAutomation();
|
||||
final ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command);
|
||||
try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
|
||||
final BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||
StringBuilder str = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
str.append(line);
|
||||
}
|
||||
return str.toString();
|
||||
private static boolean arrayTailContains(@NonNull final String[] array,
|
||||
@NonNull final String[] substrings) {
|
||||
if (array.length < substrings.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < substrings.length; i++) {
|
||||
String a = array[array.length - substrings.length + i];
|
||||
String s = substrings[i];
|
||||
if (!a.contains(s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean arrayDoesNotContain(@NonNull final String[] array,
|
||||
@NonNull final String[] substrings) {
|
||||
for (String s : substrings) {
|
||||
for (String a : array) {
|
||||
if (a.contains(s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.overlaytest;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayManager;
|
||||
import android.content.om.OverlayManagerTransaction;
|
||||
import android.content.res.Resources;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.MediumTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
@MediumTest
|
||||
public class TransactionTest {
|
||||
static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
|
||||
static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
|
||||
|
||||
private Context mContext;
|
||||
private Resources mResources;
|
||||
private OverlayManager mOverlayManager;
|
||||
private int mUserId;
|
||||
private UserHandle mUserHandle;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mContext = InstrumentationRegistry.getContext();
|
||||
mResources = mContext.getResources();
|
||||
mOverlayManager = mContext.getSystemService(OverlayManager.class);
|
||||
mUserId = UserHandle.myUserId();
|
||||
mUserHandle = UserHandle.of(mUserId);
|
||||
|
||||
LocalOverlayManager.toggleOverlaysAndWait(
|
||||
new String[]{},
|
||||
new String[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidTransaction() throws Exception {
|
||||
assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
|
||||
assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
|
||||
|
||||
OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
|
||||
.setEnabled(APP_OVERLAY_ONE_PKG, true)
|
||||
.setEnabled(APP_OVERLAY_TWO_PKG, true)
|
||||
.build();
|
||||
mOverlayManager.commit(t);
|
||||
|
||||
assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
|
||||
assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
|
||||
List<OverlayInfo> ois =
|
||||
mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
|
||||
assertEquals(ois.size(), 2);
|
||||
assertEquals(ois.get(0).packageName, APP_OVERLAY_ONE_PKG);
|
||||
assertEquals(ois.get(1).packageName, APP_OVERLAY_TWO_PKG);
|
||||
|
||||
OverlayManagerTransaction t2 = new OverlayManagerTransaction.Builder()
|
||||
.setEnabled(APP_OVERLAY_TWO_PKG, true)
|
||||
.setEnabled(APP_OVERLAY_ONE_PKG, true)
|
||||
.build();
|
||||
mOverlayManager.commit(t2);
|
||||
|
||||
assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
|
||||
assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
|
||||
List<OverlayInfo> ois2 =
|
||||
mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
|
||||
assertEquals(ois2.size(), 2);
|
||||
assertEquals(ois2.get(0).packageName, APP_OVERLAY_TWO_PKG);
|
||||
assertEquals(ois2.get(1).packageName, APP_OVERLAY_ONE_PKG);
|
||||
|
||||
OverlayManagerTransaction t3 = new OverlayManagerTransaction.Builder()
|
||||
.setEnabled(APP_OVERLAY_TWO_PKG, false)
|
||||
.build();
|
||||
mOverlayManager.commit(t3);
|
||||
|
||||
assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
|
||||
assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
|
||||
List<OverlayInfo> ois3 =
|
||||
mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
|
||||
assertEquals(ois3.size(), 2);
|
||||
assertEquals(ois3.get(0).packageName, APP_OVERLAY_TWO_PKG);
|
||||
assertEquals(ois3.get(1).packageName, APP_OVERLAY_ONE_PKG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidRequestHasNoEffect() {
|
||||
assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
|
||||
assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
|
||||
|
||||
OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
|
||||
.setEnabled(APP_OVERLAY_ONE_PKG, true)
|
||||
.setEnabled("does-not-exist", true)
|
||||
.setEnabled(APP_OVERLAY_TWO_PKG, true)
|
||||
.build();
|
||||
assertThrows(SecurityException.class, () -> mOverlayManager.commit(t));
|
||||
|
||||
assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
|
||||
assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
|
||||
}
|
||||
|
||||
private void assertOverlayIsEnabled(final String packageName, boolean enabled, int userId) {
|
||||
final OverlayInfo oi = mOverlayManager.getOverlayInfo(packageName, UserHandle.of(userId));
|
||||
assertNotNull(oi);
|
||||
assertEquals(oi.isEnabled(), enabled);
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,6 @@ import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
@MediumTest
|
||||
public class WithMultipleOverlaysTest extends OverlayBaseTest {
|
||||
@@ -33,9 +31,8 @@ public class WithMultipleOverlaysTest extends OverlayBaseTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void enableOverlay() throws Exception {
|
||||
Executor executor = (cmd) -> new Thread(cmd).start();
|
||||
LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
|
||||
LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true);
|
||||
LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
|
||||
LocalOverlayManager.toggleOverlaysAndWait(
|
||||
new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG},
|
||||
new String[]{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@ import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
@MediumTest
|
||||
public class WithOverlayTest extends OverlayBaseTest {
|
||||
@@ -32,10 +30,9 @@ public class WithOverlayTest extends OverlayBaseTest {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void enableOverlay() throws Exception {
|
||||
Executor executor = (cmd) -> new Thread(cmd).start();
|
||||
LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
|
||||
LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
|
||||
LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
|
||||
public static void enableOverlays() throws Exception {
|
||||
LocalOverlayManager.toggleOverlaysAndWait(
|
||||
new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG},
|
||||
new String[]{APP_OVERLAY_TWO_PKG});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,6 @@ import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
@MediumTest
|
||||
public class WithoutOverlayTest extends OverlayBaseTest {
|
||||
@@ -33,9 +31,8 @@ public class WithoutOverlayTest extends OverlayBaseTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void disableOverlays() throws Exception {
|
||||
Executor executor = (cmd) -> new Thread(cmd).start();
|
||||
LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false);
|
||||
LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
|
||||
LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false);
|
||||
LocalOverlayManager.toggleOverlaysAndWait(
|
||||
new String[]{},
|
||||
new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
android_test {
|
||||
name: "OverlayDeviceTests_AppOverlayOne",
|
||||
sdk_version: "current",
|
||||
|
||||
certificate: "platform",
|
||||
aaptflags: ["--no-resource-removal"],
|
||||
}
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
android_test {
|
||||
name: "OverlayDeviceTests_AppOverlayTwo",
|
||||
sdk_version: "current",
|
||||
|
||||
certificate: "platform",
|
||||
aaptflags: ["--no-resource-removal"],
|
||||
}
|
||||
|
||||
@@ -24,11 +24,15 @@ import static android.content.Intent.ACTION_PACKAGE_REMOVED;
|
||||
import static android.content.Intent.ACTION_USER_ADDED;
|
||||
import static android.content.Intent.ACTION_USER_REMOVED;
|
||||
import static android.content.Intent.EXTRA_REASON;
|
||||
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED;
|
||||
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
|
||||
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
|
||||
import static android.os.Trace.TRACE_TAG_RRO;
|
||||
import static android.os.Trace.traceBegin;
|
||||
import static android.os.Trace.traceEnd;
|
||||
|
||||
import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
@@ -39,6 +43,7 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.om.IOverlayManager;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayManagerTransaction;
|
||||
import android.content.om.OverlayableInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
@@ -48,6 +53,7 @@ import android.content.res.ApkAssets;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Environment;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
@@ -64,7 +70,6 @@ import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.content.om.OverlayConfig;
|
||||
import com.android.server.FgThread;
|
||||
import com.android.server.IoThread;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemConfig;
|
||||
import com.android.server.SystemService;
|
||||
@@ -82,12 +87,15 @@ import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Service to manage asset overlays.
|
||||
@@ -236,7 +244,14 @@ public final class OverlayManagerService extends SystemService {
|
||||
|
||||
private final OverlayActorEnforcer mActorEnforcer;
|
||||
|
||||
private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
|
||||
private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> {
|
||||
persistSettings();
|
||||
FgThread.getHandler().post(() -> {
|
||||
List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId);
|
||||
updateActivityManager(affectedTargets, pair.userId);
|
||||
broadcastActionOverlayChanged(pair.packageName, pair.userId);
|
||||
});
|
||||
};
|
||||
|
||||
public OverlayManagerService(@NonNull final Context context) {
|
||||
super(context);
|
||||
@@ -249,17 +264,19 @@ public final class OverlayManagerService extends SystemService {
|
||||
IdmapManager im = new IdmapManager(IdmapDaemon.getInstance(), mPackageManager);
|
||||
mSettings = new OverlayManagerSettings();
|
||||
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
|
||||
OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(),
|
||||
new OverlayChangeListener());
|
||||
OverlayConfig.getSystemInstance(), getDefaultOverlayPackages());
|
||||
mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
|
||||
|
||||
HandlerThread packageReceiverThread = new HandlerThread(TAG);
|
||||
packageReceiverThread.start();
|
||||
|
||||
final IntentFilter packageFilter = new IntentFilter();
|
||||
packageFilter.addAction(ACTION_PACKAGE_ADDED);
|
||||
packageFilter.addAction(ACTION_PACKAGE_CHANGED);
|
||||
packageFilter.addAction(ACTION_PACKAGE_REMOVED);
|
||||
packageFilter.addDataScheme("package");
|
||||
getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
|
||||
packageFilter, null, null);
|
||||
packageFilter, null, packageReceiverThread.getThreadHandler());
|
||||
|
||||
final IntentFilter userFilter = new IntentFilter();
|
||||
userFilter.addAction(ACTION_USER_ADDED);
|
||||
@@ -292,11 +309,11 @@ public final class OverlayManagerService extends SystemService {
|
||||
for (int i = 0; i < userCount; i++) {
|
||||
final UserInfo userInfo = users.get(i);
|
||||
if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
|
||||
// Initialize any users that can't be switched to, as there state would
|
||||
// Initialize any users that can't be switched to, as their state would
|
||||
// never be setup in onSwitchUser(). We will switch to the system user right
|
||||
// after this, and its state will be setup there.
|
||||
final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
|
||||
updateOverlayPaths(users.get(i).id, targets);
|
||||
updatePackageManager(targets, users.get(i).id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,9 +327,10 @@ public final class OverlayManagerService extends SystemService {
|
||||
// any asset changes to the rest of the system
|
||||
synchronized (mLock) {
|
||||
final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
|
||||
updateAssets(newUserId, targets);
|
||||
final List<String> affectedTargets = updatePackageManager(targets, newUserId);
|
||||
updateActivityManager(affectedTargets, newUserId);
|
||||
}
|
||||
schedulePersistSettings();
|
||||
persistSettings();
|
||||
} finally {
|
||||
traceEnd(TRACE_TAG_RRO);
|
||||
}
|
||||
@@ -396,10 +414,17 @@ public final class OverlayManagerService extends SystemService {
|
||||
false);
|
||||
if (pi != null && !pi.applicationInfo.isInstantApp()) {
|
||||
mPackageManager.cachePackageInfo(packageName, userId, pi);
|
||||
if (pi.isOverlayPackage()) {
|
||||
mImpl.onOverlayPackageAdded(packageName, userId);
|
||||
} else {
|
||||
mImpl.onTargetPackageAdded(packageName, userId);
|
||||
|
||||
try {
|
||||
if (pi.isOverlayPackage()) {
|
||||
mImpl.onOverlayPackageAdded(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
} else {
|
||||
mImpl.onTargetPackageAdded(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
}
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageAdded internal error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,10 +444,17 @@ public final class OverlayManagerService extends SystemService {
|
||||
false);
|
||||
if (pi != null && pi.applicationInfo.isInstantApp()) {
|
||||
mPackageManager.cachePackageInfo(packageName, userId, pi);
|
||||
if (pi.isOverlayPackage()) {
|
||||
mImpl.onOverlayPackageChanged(packageName, userId);
|
||||
} else {
|
||||
mImpl.onTargetPackageChanged(packageName, userId);
|
||||
|
||||
try {
|
||||
if (pi.isOverlayPackage()) {
|
||||
mImpl.onOverlayPackageChanged(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
} else {
|
||||
mImpl.onTargetPackageChanged(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
}
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageChanged internal error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -441,7 +473,12 @@ public final class OverlayManagerService extends SystemService {
|
||||
mPackageManager.forgetPackageInfo(packageName, userId);
|
||||
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
|
||||
if (oi != null) {
|
||||
mImpl.onOverlayPackageReplacing(packageName, userId);
|
||||
try {
|
||||
mImpl.onOverlayPackageReplacing(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageReplacing internal error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -460,10 +497,16 @@ public final class OverlayManagerService extends SystemService {
|
||||
false);
|
||||
if (pi != null && !pi.applicationInfo.isInstantApp()) {
|
||||
mPackageManager.cachePackageInfo(packageName, userId, pi);
|
||||
if (pi.isOverlayPackage()) {
|
||||
mImpl.onOverlayPackageReplaced(packageName, userId);
|
||||
} else {
|
||||
mImpl.onTargetPackageReplaced(packageName, userId);
|
||||
try {
|
||||
if (pi.isOverlayPackage()) {
|
||||
mImpl.onOverlayPackageReplaced(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
} else {
|
||||
mImpl.onTargetPackageReplaced(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
}
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageReplaced internal error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -481,10 +524,17 @@ public final class OverlayManagerService extends SystemService {
|
||||
synchronized (mLock) {
|
||||
mPackageManager.forgetPackageInfo(packageName, userId);
|
||||
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
|
||||
if (oi != null) {
|
||||
mImpl.onOverlayPackageRemoved(packageName, userId);
|
||||
} else {
|
||||
mImpl.onTargetPackageRemoved(packageName, userId);
|
||||
|
||||
try {
|
||||
if (oi != null) {
|
||||
mImpl.onOverlayPackageRemoved(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
} else {
|
||||
mImpl.onTargetPackageRemoved(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
}
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageRemoved internal error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -507,7 +557,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
synchronized (mLock) {
|
||||
targets = mImpl.updateOverlaysForUser(userId);
|
||||
}
|
||||
updateOverlayPaths(userId, targets);
|
||||
updatePackageManager(targets, userId);
|
||||
} finally {
|
||||
traceEnd(TRACE_TAG_RRO);
|
||||
}
|
||||
@@ -602,7 +652,13 @@ public final class OverlayManagerService extends SystemService {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
return mImpl.setEnabled(packageName, enable, realUserId);
|
||||
try {
|
||||
mImpl.setEnabled(packageName, enable, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
@@ -627,8 +683,14 @@ public final class OverlayManagerService extends SystemService {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
return mImpl.setEnabledExclusive(packageName, false /* withinCategory */,
|
||||
realUserId);
|
||||
try {
|
||||
mImpl.setEnabledExclusive(packageName,
|
||||
false /* withinCategory */, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
@@ -654,8 +716,14 @@ public final class OverlayManagerService extends SystemService {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
return mImpl.setEnabledExclusive(packageName, true /* withinCategory */,
|
||||
realUserId);
|
||||
try {
|
||||
mImpl.setEnabledExclusive(packageName,
|
||||
true /* withinCategory */, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
@@ -681,7 +749,13 @@ public final class OverlayManagerService extends SystemService {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
return mImpl.setPriority(packageName, parentPackageName, realUserId);
|
||||
try {
|
||||
mImpl.setPriority(packageName, parentPackageName, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
@@ -705,7 +779,13 @@ public final class OverlayManagerService extends SystemService {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
return mImpl.setHighestPriority(packageName, realUserId);
|
||||
try {
|
||||
mImpl.setHighestPriority(packageName, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
@@ -729,7 +809,13 @@ public final class OverlayManagerService extends SystemService {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
return mImpl.setLowestPriority(packageName, realUserId);
|
||||
try {
|
||||
mImpl.setLowestPriority(packageName, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
@@ -777,6 +863,129 @@ public final class OverlayManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit(@NonNull final OverlayManagerTransaction transaction)
|
||||
throws RemoteException {
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#commit " + transaction);
|
||||
try {
|
||||
executeAllRequests(transaction);
|
||||
} catch (Exception e) {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
restoreSettings();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
Slog.d(TAG, "commit failed: " + e.getMessage(), e);
|
||||
throw new SecurityException("commit failed"
|
||||
+ (DEBUG ? ": " + e.getMessage() : ""));
|
||||
}
|
||||
} finally {
|
||||
traceEnd(TRACE_TAG_RRO);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<PackageAndUser> executeRequest(
|
||||
@NonNull final OverlayManagerTransaction.Request request) throws Exception {
|
||||
final int realUserId = handleIncomingUser(request.userId, request.typeToString());
|
||||
enforceActor(request.packageName, request.typeToString(), realUserId);
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
switch (request.type) {
|
||||
case TYPE_SET_ENABLED:
|
||||
Optional<PackageAndUser> opt1 =
|
||||
mImpl.setEnabled(request.packageName, true, request.userId);
|
||||
Optional<PackageAndUser> opt2 =
|
||||
mImpl.setHighestPriority(request.packageName, request.userId);
|
||||
// Both setEnabled and setHighestPriority affected the same
|
||||
// target package and user: if both return non-empty
|
||||
// Optionals, they are identical
|
||||
return opt1.isPresent() ? opt1 : opt2;
|
||||
case TYPE_SET_DISABLED:
|
||||
return mImpl.setEnabled(request.packageName, false, request.userId);
|
||||
default:
|
||||
throw new IllegalArgumentException("unsupported request: " + request);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeAllRequests(@NonNull final OverlayManagerTransaction transaction)
|
||||
throws Exception {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "commit " + transaction);
|
||||
}
|
||||
if (transaction == null) {
|
||||
throw new IllegalArgumentException("null transaction");
|
||||
}
|
||||
|
||||
// map: userId -> set<package-name>: target packages of overlays in
|
||||
// this transaction
|
||||
SparseArray<Set<String>> transactionTargets = new SparseArray<>();
|
||||
|
||||
// map: userId -> set<package-name>: packages that need to reload
|
||||
// their resources due to changes to the overlays in this
|
||||
// transaction
|
||||
SparseArray<List<String>> affectedPackagesToUpdate = new SparseArray<>();
|
||||
|
||||
synchronized (mLock) {
|
||||
|
||||
// execute the requests (as calling user)
|
||||
for (final OverlayManagerTransaction.Request request : transaction) {
|
||||
executeRequest(request).ifPresent(target -> {
|
||||
Set<String> userTargets = transactionTargets.get(target.userId);
|
||||
if (userTargets == null) {
|
||||
userTargets = new ArraySet<String>();
|
||||
transactionTargets.put(target.userId, userTargets);
|
||||
}
|
||||
userTargets.add(target.packageName);
|
||||
});
|
||||
}
|
||||
|
||||
// past the point of no return: the entire transaction has been
|
||||
// processed successfully, we can no longer fail: continue as
|
||||
// system_server
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
persistSettings();
|
||||
|
||||
// inform the package manager about the new paths
|
||||
for (int index = 0; index < transactionTargets.size(); index++) {
|
||||
final int userId = transactionTargets.keyAt(index);
|
||||
final List<String> affectedTargets =
|
||||
updatePackageManager(transactionTargets.valueAt(index), userId);
|
||||
affectedPackagesToUpdate.put(userId, affectedTargets);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
} // synchronized (mLock)
|
||||
|
||||
FgThread.getHandler().post(() -> {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
// schedule apps to refresh
|
||||
for (int index = 0; index < affectedPackagesToUpdate.size(); index++) {
|
||||
final int userId = affectedPackagesToUpdate.keyAt(index);
|
||||
updateActivityManager(affectedPackagesToUpdate.valueAt(index), userId);
|
||||
}
|
||||
|
||||
// broadcast the ACTION_OVERLAY_CHANGED intents
|
||||
for (int index = 0; index < transactionTargets.size(); index++) {
|
||||
final int userId = transactionTargets.keyAt(index);
|
||||
for (String pkg: transactionTargets.valueAt(index)) {
|
||||
broadcastActionOverlayChanged(pkg, userId);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShellCommand(@NonNull final FileDescriptor in,
|
||||
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
|
||||
@@ -898,162 +1107,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
}
|
||||
};
|
||||
|
||||
private final class OverlayChangeListener
|
||||
implements OverlayManagerServiceImpl.OverlayChangeListener {
|
||||
@Override
|
||||
public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
|
||||
schedulePersistSettings();
|
||||
FgThread.getHandler().post(() -> {
|
||||
updateAssets(userId, targetPackageName);
|
||||
|
||||
final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
|
||||
Uri.fromParts("package", targetPackageName, null));
|
||||
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "send broadcast " + intent);
|
||||
}
|
||||
|
||||
try {
|
||||
ActivityManager.getService().broadcastIntentWithFeature(null, null, intent,
|
||||
null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE,
|
||||
null, false, false, userId);
|
||||
} catch (RemoteException e) {
|
||||
// Intentionally left empty.
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the target packages' set of enabled overlays in PackageManager.
|
||||
*/
|
||||
private ArrayList<String> updateOverlayPaths(int userId, List<String> targetPackageNames) {
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Updating overlay assets");
|
||||
}
|
||||
final PackageManagerInternal pm =
|
||||
LocalServices.getService(PackageManagerInternal.class);
|
||||
final boolean updateFrameworkRes = targetPackageNames.contains("android");
|
||||
if (updateFrameworkRes) {
|
||||
targetPackageNames = pm.getTargetPackageNames(userId);
|
||||
}
|
||||
|
||||
final Map<String, List<String>> pendingChanges =
|
||||
new ArrayMap<>(targetPackageNames.size());
|
||||
synchronized (mLock) {
|
||||
final List<String> frameworkOverlays =
|
||||
mImpl.getEnabledOverlayPackageNames("android", userId);
|
||||
final int n = targetPackageNames.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final String targetPackageName = targetPackageNames.get(i);
|
||||
List<String> list = new ArrayList<>();
|
||||
if (!"android".equals(targetPackageName)) {
|
||||
list.addAll(frameworkOverlays);
|
||||
}
|
||||
list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
|
||||
pendingChanges.put(targetPackageName, list);
|
||||
}
|
||||
}
|
||||
|
||||
final HashSet<String> updatedPackages = new HashSet<>();
|
||||
final int n = targetPackageNames.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final String targetPackageName = targetPackageNames.get(i);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
|
||||
+ TextUtils.join(",", pendingChanges.get(targetPackageName))
|
||||
+ "] userId=" + userId);
|
||||
}
|
||||
|
||||
if (!pm.setEnabledOverlayPackages(
|
||||
userId, targetPackageName, pendingChanges.get(targetPackageName),
|
||||
updatedPackages)) {
|
||||
Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
|
||||
targetPackageName, userId));
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(updatedPackages);
|
||||
} finally {
|
||||
traceEnd(TRACE_TAG_RRO);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAssets(final int userId, final String targetPackageName) {
|
||||
updateAssets(userId, Collections.singletonList(targetPackageName));
|
||||
}
|
||||
|
||||
private void updateAssets(final int userId, List<String> targetPackageNames) {
|
||||
final IActivityManager am = ActivityManager.getService();
|
||||
try {
|
||||
final ArrayList<String> updatedPaths = updateOverlayPaths(userId, targetPackageNames);
|
||||
am.scheduleApplicationInfoChanged(updatedPaths, userId);
|
||||
} catch (RemoteException e) {
|
||||
// Intentionally left empty.
|
||||
}
|
||||
}
|
||||
|
||||
private void schedulePersistSettings() {
|
||||
if (mPersistSettingsScheduled.getAndSet(true)) {
|
||||
return;
|
||||
}
|
||||
IoThread.getHandler().post(() -> {
|
||||
mPersistSettingsScheduled.set(false);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Writing overlay settings");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
FileOutputStream stream = null;
|
||||
try {
|
||||
stream = mSettingsFile.startWrite();
|
||||
mSettings.persist(stream);
|
||||
mSettingsFile.finishWrite(stream);
|
||||
} catch (IOException | XmlPullParserException e) {
|
||||
mSettingsFile.failWrite(stream);
|
||||
Slog.e(TAG, "failed to persist overlay state", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void restoreSettings() {
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
|
||||
synchronized (mLock) {
|
||||
if (!mSettingsFile.getBaseFile().exists()) {
|
||||
return;
|
||||
}
|
||||
try (FileInputStream stream = mSettingsFile.openRead()) {
|
||||
mSettings.restore(stream);
|
||||
|
||||
// We might have data for dying users if the device was
|
||||
// restarted before we received USER_REMOVED. Remove data for
|
||||
// users that will not exist after the system is ready.
|
||||
|
||||
final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
|
||||
final int[] liveUserIds = new int[liveUsers.size()];
|
||||
for (int i = 0; i < liveUsers.size(); i++) {
|
||||
liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
|
||||
}
|
||||
Arrays.sort(liveUserIds);
|
||||
|
||||
for (int userId : mSettings.getUsers()) {
|
||||
if (Arrays.binarySearch(liveUserIds, userId) < 0) {
|
||||
mSettings.removeUser(userId);
|
||||
}
|
||||
}
|
||||
} catch (IOException | XmlPullParserException e) {
|
||||
Slog.e(TAG, "failed to restore overlay state", e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
traceEnd(TRACE_TAG_RRO);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PackageManagerHelperImpl implements PackageManagerHelper {
|
||||
private static final class PackageManagerHelperImpl implements PackageManagerHelper {
|
||||
|
||||
private final Context mContext;
|
||||
private final IPackageManager mPackageManager;
|
||||
@@ -1263,4 +1317,144 @@ public final class OverlayManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper methods to update other parts of the system or read/write
|
||||
// settings: these methods should never call into each other!
|
||||
|
||||
private void broadcastActionOverlayChanged(@NonNull final String targetPackageName,
|
||||
final int userId) {
|
||||
final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
|
||||
Uri.fromParts("package", targetPackageName, null));
|
||||
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
|
||||
try {
|
||||
ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null,
|
||||
null, android.app.AppOpsManager.OP_NONE, null, false, false, userId);
|
||||
} catch (RemoteException e) {
|
||||
// Intentionally left empty.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the activity manager to tell a set of packages to reload their
|
||||
* resources.
|
||||
*/
|
||||
private void updateActivityManager(List<String> targetPackageNames, final int userId) {
|
||||
final IActivityManager am = ActivityManager.getService();
|
||||
try {
|
||||
am.scheduleApplicationInfoChanged(targetPackageNames, userId);
|
||||
} catch (RemoteException e) {
|
||||
// Intentionally left empty.
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<String> updatePackageManager(String targetPackageNames, final int userId) {
|
||||
return updatePackageManager(Collections.singletonList(targetPackageNames), userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the target packages' set of enabled overlays in PackageManager.
|
||||
* @return the package names of affected targets (a superset of
|
||||
* targetPackageNames: the target themserlves and shared libraries)
|
||||
*/
|
||||
private ArrayList<String> updatePackageManager(@NonNull Collection<String> targetPackageNames,
|
||||
final int userId) {
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#updatePackageManager " + targetPackageNames);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Update package manager about changed overlays");
|
||||
}
|
||||
final PackageManagerInternal pm =
|
||||
LocalServices.getService(PackageManagerInternal.class);
|
||||
final boolean updateFrameworkRes = targetPackageNames.contains("android");
|
||||
if (updateFrameworkRes) {
|
||||
targetPackageNames = pm.getTargetPackageNames(userId);
|
||||
}
|
||||
|
||||
final Map<String, List<String>> pendingChanges =
|
||||
new ArrayMap<>(targetPackageNames.size());
|
||||
synchronized (mLock) {
|
||||
final List<String> frameworkOverlays =
|
||||
mImpl.getEnabledOverlayPackageNames("android", userId);
|
||||
for (final String targetPackageName : targetPackageNames) {
|
||||
List<String> list = new ArrayList<>();
|
||||
if (!"android".equals(targetPackageName)) {
|
||||
list.addAll(frameworkOverlays);
|
||||
}
|
||||
list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
|
||||
pendingChanges.put(targetPackageName, list);
|
||||
}
|
||||
}
|
||||
|
||||
final HashSet<String> updatedPackages = new HashSet<>();
|
||||
for (final String targetPackageName : targetPackageNames) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
|
||||
+ TextUtils.join(",", pendingChanges.get(targetPackageName))
|
||||
+ "] userId=" + userId);
|
||||
}
|
||||
|
||||
if (!pm.setEnabledOverlayPackages(
|
||||
userId, targetPackageName, pendingChanges.get(targetPackageName),
|
||||
updatedPackages)) {
|
||||
Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
|
||||
targetPackageName, userId));
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(updatedPackages);
|
||||
} finally {
|
||||
traceEnd(TRACE_TAG_RRO);
|
||||
}
|
||||
}
|
||||
|
||||
private void persistSettings() {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Writing overlay settings");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
FileOutputStream stream = null;
|
||||
try {
|
||||
stream = mSettingsFile.startWrite();
|
||||
mSettings.persist(stream);
|
||||
mSettingsFile.finishWrite(stream);
|
||||
} catch (IOException | XmlPullParserException e) {
|
||||
mSettingsFile.failWrite(stream);
|
||||
Slog.e(TAG, "failed to persist overlay state", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreSettings() {
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
|
||||
synchronized (mLock) {
|
||||
if (!mSettingsFile.getBaseFile().exists()) {
|
||||
return;
|
||||
}
|
||||
try (FileInputStream stream = mSettingsFile.openRead()) {
|
||||
mSettings.restore(stream);
|
||||
|
||||
// We might have data for dying users if the device was
|
||||
// restarted before we received USER_REMOVED. Remove data for
|
||||
// users that will not exist after the system is ready.
|
||||
|
||||
final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
|
||||
final int[] liveUserIds = new int[liveUsers.size()];
|
||||
for (int i = 0; i < liveUsers.size(); i++) {
|
||||
liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
|
||||
}
|
||||
Arrays.sort(liveUserIds);
|
||||
|
||||
for (int userId : mSettings.getUsers()) {
|
||||
if (Arrays.binarySearch(liveUserIds, userId) < 0) {
|
||||
mSettings.removeUser(userId);
|
||||
}
|
||||
}
|
||||
} catch (IOException | XmlPullParserException e) {
|
||||
Slog.e(TAG, "failed to restore overlay state", e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
traceEnd(TRACE_TAG_RRO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -71,7 +72,6 @@ final class OverlayManagerServiceImpl {
|
||||
private final OverlayManagerSettings mSettings;
|
||||
private final OverlayConfig mOverlayConfig;
|
||||
private final String[] mDefaultOverlays;
|
||||
private final OverlayChangeListener mListener;
|
||||
|
||||
/**
|
||||
* Helper method to merge the overlay manager's (as read from overlays.xml)
|
||||
@@ -114,14 +114,12 @@ final class OverlayManagerServiceImpl {
|
||||
@NonNull final IdmapManager idmapManager,
|
||||
@NonNull final OverlayManagerSettings settings,
|
||||
@NonNull final OverlayConfig overlayConfig,
|
||||
@NonNull final String[] defaultOverlays,
|
||||
@NonNull final OverlayChangeListener listener) {
|
||||
@NonNull final String[] defaultOverlays) {
|
||||
mPackageManager = packageManager;
|
||||
mIdmapManager = idmapManager;
|
||||
mSettings = settings;
|
||||
mOverlayConfig = overlayConfig;
|
||||
mDefaultOverlays = defaultOverlays;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,52 +257,58 @@ final class OverlayManagerServiceImpl {
|
||||
mSettings.removeUser(userId);
|
||||
}
|
||||
|
||||
void onTargetPackageAdded(@NonNull final String packageName, final int userId) {
|
||||
Optional<PackageAndUser> onTargetPackageAdded(@NonNull final String packageName,
|
||||
final int userId) throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
|
||||
}
|
||||
|
||||
updateAndRefreshOverlaysForTarget(packageName, userId, 0);
|
||||
return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
|
||||
}
|
||||
|
||||
void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
|
||||
Optional<PackageAndUser> onTargetPackageChanged(@NonNull final String packageName,
|
||||
final int userId) throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
|
||||
}
|
||||
|
||||
updateAndRefreshOverlaysForTarget(packageName, userId, 0);
|
||||
return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
|
||||
}
|
||||
|
||||
void onTargetPackageReplacing(@NonNull final String packageName, final int userId) {
|
||||
Optional<PackageAndUser> onTargetPackageReplacing(@NonNull final String packageName,
|
||||
final int userId) throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId="
|
||||
+ userId);
|
||||
}
|
||||
|
||||
updateAndRefreshOverlaysForTarget(packageName, userId, 0);
|
||||
return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
|
||||
}
|
||||
|
||||
void onTargetPackageReplaced(@NonNull final String packageName, final int userId) {
|
||||
Optional<PackageAndUser> onTargetPackageReplaced(@NonNull final String packageName,
|
||||
final int userId) throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onTargetPackageReplaced packageName=" + packageName + " userId=" + userId);
|
||||
}
|
||||
|
||||
updateAndRefreshOverlaysForTarget(packageName, userId, 0);
|
||||
return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
|
||||
}
|
||||
|
||||
void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
|
||||
Optional<PackageAndUser> onTargetPackageRemoved(@NonNull final String packageName,
|
||||
final int userId) throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
|
||||
}
|
||||
|
||||
updateAndRefreshOverlaysForTarget(packageName, userId, 0);
|
||||
return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the state of any overlays for this target.
|
||||
*/
|
||||
private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName,
|
||||
final int userId, final int flags) {
|
||||
private Optional<PackageAndUser> updateAndRefreshOverlaysForTarget(
|
||||
@NonNull final String targetPackageName, final int userId, final int flags)
|
||||
throws OperationFailedException {
|
||||
final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName,
|
||||
userId);
|
||||
|
||||
@@ -364,11 +368,13 @@ final class OverlayManagerServiceImpl {
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
mListener.onOverlaysChanged(targetPackageName, userId);
|
||||
return Optional.of(new PackageAndUser(targetPackageName, userId));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
|
||||
Optional<PackageAndUser> onOverlayPackageAdded(@NonNull final String packageName,
|
||||
final int userId) throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
|
||||
}
|
||||
@@ -376,8 +382,7 @@ final class OverlayManagerServiceImpl {
|
||||
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
||||
if (overlayPackage == null) {
|
||||
Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
|
||||
onOverlayPackageRemoved(packageName, userId);
|
||||
return;
|
||||
return onOverlayPackageRemoved(packageName, userId);
|
||||
}
|
||||
|
||||
mSettings.init(packageName, userId, overlayPackage.overlayTarget,
|
||||
@@ -389,15 +394,17 @@ final class OverlayManagerServiceImpl {
|
||||
overlayPackage.overlayCategory);
|
||||
try {
|
||||
if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
|
||||
mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
|
||||
return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
|
||||
}
|
||||
return Optional.empty();
|
||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||
Slog.e(TAG, "failed to update settings", e);
|
||||
mSettings.remove(packageName, userId);
|
||||
throw new OperationFailedException("failed to update settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
void onOverlayPackageChanged(@NonNull final String packageName, final int userId) {
|
||||
Optional<PackageAndUser> onOverlayPackageChanged(@NonNull final String packageName,
|
||||
final int userId) throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId);
|
||||
}
|
||||
@@ -405,14 +412,16 @@ final class OverlayManagerServiceImpl {
|
||||
try {
|
||||
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
|
||||
if (updateState(oi.targetPackageName, packageName, userId, 0)) {
|
||||
mListener.onOverlaysChanged(oi.targetPackageName, userId);
|
||||
return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
|
||||
}
|
||||
return Optional.empty();
|
||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||
Slog.e(TAG, "failed to update settings", e);
|
||||
throw new OperationFailedException("failed to update settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
void onOverlayPackageReplacing(@NonNull final String packageName, final int userId) {
|
||||
Optional<PackageAndUser> onOverlayPackageReplacing(@NonNull final String packageName,
|
||||
final int userId) throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId="
|
||||
+ userId);
|
||||
@@ -423,14 +432,16 @@ final class OverlayManagerServiceImpl {
|
||||
if (updateState(oi.targetPackageName, packageName, userId,
|
||||
FLAG_OVERLAY_IS_BEING_REPLACED)) {
|
||||
removeIdmapIfPossible(oi);
|
||||
mListener.onOverlaysChanged(oi.targetPackageName, userId);
|
||||
return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
|
||||
}
|
||||
return Optional.empty();
|
||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||
Slog.e(TAG, "failed to update settings", e);
|
||||
throw new OperationFailedException("failed to update settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
void onOverlayPackageReplaced(@NonNull final String packageName, final int userId) {
|
||||
Optional<PackageAndUser> onOverlayPackageReplaced(@NonNull final String packageName,
|
||||
final int userId) throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId="
|
||||
+ userId);
|
||||
@@ -439,16 +450,12 @@ final class OverlayManagerServiceImpl {
|
||||
final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId);
|
||||
if (pkg == null) {
|
||||
Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found");
|
||||
onOverlayPackageRemoved(packageName, userId);
|
||||
return;
|
||||
return onOverlayPackageRemoved(packageName, userId);
|
||||
}
|
||||
|
||||
try {
|
||||
final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId);
|
||||
if (mustReinitializeOverlay(pkg, oldOi)) {
|
||||
if (oldOi != null && !oldOi.targetPackageName.equals(pkg.overlayTarget)) {
|
||||
mListener.onOverlaysChanged(pkg.overlayTarget, userId);
|
||||
}
|
||||
mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName,
|
||||
pkg.applicationInfo.getBaseCodePath(),
|
||||
isPackageConfiguredMutable(pkg.packageName),
|
||||
@@ -457,22 +464,25 @@ final class OverlayManagerServiceImpl {
|
||||
}
|
||||
|
||||
if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
|
||||
mListener.onOverlaysChanged(pkg.overlayTarget, userId);
|
||||
return Optional.of(new PackageAndUser(pkg.overlayTarget, userId));
|
||||
}
|
||||
return Optional.empty();
|
||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||
Slog.e(TAG, "failed to update settings", e);
|
||||
throw new OperationFailedException("failed to update settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) {
|
||||
Optional<PackageAndUser> onOverlayPackageRemoved(@NonNull final String packageName,
|
||||
final int userId) throws OperationFailedException {
|
||||
try {
|
||||
final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId);
|
||||
if (mSettings.remove(packageName, userId)) {
|
||||
removeIdmapIfPossible(overlayInfo);
|
||||
mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId);
|
||||
return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
|
||||
}
|
||||
return Optional.empty();
|
||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||
Slog.e(TAG, "failed to remove overlay", e);
|
||||
throw new OperationFailedException("failed to remove overlay", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,8 +503,8 @@ final class OverlayManagerServiceImpl {
|
||||
return mSettings.getOverlaysForUser(userId);
|
||||
}
|
||||
|
||||
boolean setEnabled(@NonNull final String packageName, final boolean enable,
|
||||
final int userId) {
|
||||
Optional<PackageAndUser> setEnabled(@NonNull final String packageName, final boolean enable,
|
||||
final int userId) throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
|
||||
packageName, enable, userId));
|
||||
@@ -502,30 +512,33 @@ final class OverlayManagerServiceImpl {
|
||||
|
||||
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
||||
if (overlayPackage == null) {
|
||||
return false;
|
||||
throw new OperationFailedException(
|
||||
String.format("failed to find overlay package %s for user %d",
|
||||
packageName, userId));
|
||||
}
|
||||
|
||||
try {
|
||||
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
|
||||
if (!oi.isMutable) {
|
||||
// Ignore immutable overlays.
|
||||
return false;
|
||||
throw new OperationFailedException(
|
||||
"cannot enable immutable overlay packages in runtime");
|
||||
}
|
||||
|
||||
boolean modified = mSettings.setEnabled(packageName, userId, enable);
|
||||
modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
|
||||
|
||||
if (modified) {
|
||||
mListener.onOverlaysChanged(oi.targetPackageName, userId);
|
||||
return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
|
||||
}
|
||||
return true;
|
||||
return Optional.empty();
|
||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||
return false;
|
||||
throw new OperationFailedException("failed to update settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
boolean setEnabledExclusive(@NonNull final String packageName, boolean withinCategory,
|
||||
final int userId) {
|
||||
Optional<PackageAndUser> setEnabledExclusive(@NonNull final String packageName,
|
||||
boolean withinCategory, final int userId) throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, String.format("setEnabledExclusive packageName=%s"
|
||||
+ " withinCategory=%s userId=%d", packageName, withinCategory, userId));
|
||||
@@ -533,7 +546,8 @@ final class OverlayManagerServiceImpl {
|
||||
|
||||
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
||||
if (overlayPackage == null) {
|
||||
return false;
|
||||
throw new OperationFailedException(String.format(
|
||||
"failed to find overlay package %s for user %d", packageName, userId));
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -576,11 +590,11 @@ final class OverlayManagerServiceImpl {
|
||||
modified |= updateState(targetPackageName, packageName, userId, 0);
|
||||
|
||||
if (modified) {
|
||||
mListener.onOverlaysChanged(targetPackageName, userId);
|
||||
return Optional.of(new PackageAndUser(targetPackageName, userId));
|
||||
}
|
||||
return true;
|
||||
return Optional.empty();
|
||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||
return false;
|
||||
throw new OperationFailedException("failed to update settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,66 +610,75 @@ final class OverlayManagerServiceImpl {
|
||||
return mOverlayConfig.isEnabled(packageName);
|
||||
}
|
||||
|
||||
boolean setPriority(@NonNull final String packageName,
|
||||
@NonNull final String newParentPackageName, final int userId) {
|
||||
Optional<PackageAndUser> setPriority(@NonNull final String packageName,
|
||||
@NonNull final String newParentPackageName, final int userId)
|
||||
throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName="
|
||||
+ newParentPackageName + " userId=" + userId);
|
||||
}
|
||||
|
||||
if (!isPackageConfiguredMutable(packageName)) {
|
||||
return false;
|
||||
throw new OperationFailedException(String.format(
|
||||
"overlay package %s user %d is not updatable", packageName, userId));
|
||||
}
|
||||
|
||||
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
||||
if (overlayPackage == null) {
|
||||
return false;
|
||||
throw new OperationFailedException(String.format(
|
||||
"failed to find overlay package %s for user %d", packageName, userId));
|
||||
}
|
||||
|
||||
if (mSettings.setPriority(packageName, newParentPackageName, userId)) {
|
||||
mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
|
||||
return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
|
||||
}
|
||||
return true;
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
boolean setHighestPriority(@NonNull final String packageName, final int userId) {
|
||||
Optional<PackageAndUser> setHighestPriority(@NonNull final String packageName,
|
||||
final int userId) throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
|
||||
}
|
||||
|
||||
if (!isPackageConfiguredMutable(packageName)) {
|
||||
return false;
|
||||
throw new OperationFailedException(String.format(
|
||||
"overlay package %s user %d is not updatable", packageName, userId));
|
||||
}
|
||||
|
||||
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
||||
if (overlayPackage == null) {
|
||||
return false;
|
||||
throw new OperationFailedException(String.format(
|
||||
"failed to find overlay package %s for user %d", packageName, userId));
|
||||
}
|
||||
|
||||
if (mSettings.setHighestPriority(packageName, userId)) {
|
||||
mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
|
||||
return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
|
||||
}
|
||||
return true;
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
boolean setLowestPriority(@NonNull final String packageName, final int userId) {
|
||||
Optional<PackageAndUser> setLowestPriority(@NonNull final String packageName, final int userId)
|
||||
throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
|
||||
}
|
||||
|
||||
if (!isPackageConfiguredMutable(packageName)) {
|
||||
return false;
|
||||
throw new OperationFailedException(String.format(
|
||||
"overlay package %s user %d is not updatable", packageName, userId));
|
||||
}
|
||||
|
||||
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
||||
if (overlayPackage == null) {
|
||||
return false;
|
||||
throw new OperationFailedException(String.format(
|
||||
"failed to find overlay package %s for user %d", packageName, userId));
|
||||
}
|
||||
|
||||
if (mSettings.setLowestPriority(packageName, userId)) {
|
||||
mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
|
||||
return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
|
||||
}
|
||||
return true;
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
|
||||
@@ -797,12 +820,13 @@ final class OverlayManagerServiceImpl {
|
||||
mIdmapManager.removeIdmap(oi, oi.userId);
|
||||
}
|
||||
|
||||
interface OverlayChangeListener {
|
||||
static final class OperationFailedException extends Exception {
|
||||
OperationFailedException(@NonNull final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* An event triggered by changes made to overlay state or settings as well as changes that
|
||||
* add or remove target packages of overlays.
|
||||
**/
|
||||
void onOverlaysChanged(@NonNull String targetPackage, int userId);
|
||||
OperationFailedException(@NonNull final String message, @NonNull Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
57
services/core/java/com/android/server/om/PackageAndUser.java
Normal file
57
services/core/java/com/android/server/om/PackageAndUser.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.om;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
|
||||
final class PackageAndUser {
|
||||
public final @NonNull String packageName;
|
||||
public final @UserIdInt int userId;
|
||||
|
||||
PackageAndUser(@NonNull String packageName, @UserIdInt int userId) {
|
||||
this.packageName = packageName;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof PackageAndUser)) {
|
||||
return false;
|
||||
}
|
||||
PackageAndUser other = (PackageAndUser) obj;
|
||||
return packageName.equals(other.packageName) && userId == other.userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + packageName.hashCode();
|
||||
result = prime * result + userId;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PackageAndUser{packageName=%s, userId=%d}", packageName, userId);
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImmutableEnabledChange() {
|
||||
public void testImmutableEnabledChange() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
@@ -106,7 +106,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMutableEnabledChangeHasNoEffect() {
|
||||
public void testMutableEnabledChangeHasNoEffect() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
@@ -134,7 +134,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMutableEnabledToImmutableEnabled() {
|
||||
public void testMutableEnabledToImmutableEnabled() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
@@ -178,7 +178,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMutablePriorityChange() {
|
||||
public void testMutablePriorityChange() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
@@ -218,7 +218,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImmutablePriorityChange() {
|
||||
public void testImmutablePriorityChange() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
|
||||
@@ -22,11 +22,14 @@ import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
|
||||
import static android.os.OverlayablePolicy.CONFIG_SIGNATURE;
|
||||
|
||||
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.testng.Assert.assertThrows;
|
||||
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
@@ -35,6 +38,7 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
|
||||
@@ -55,7 +59,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
private static final String CERT_CONFIG_NOK = "config_certificate_nok";
|
||||
|
||||
@Test
|
||||
public void testGetOverlayInfo() {
|
||||
public void testGetOverlayInfo() throws Exception {
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
@@ -67,7 +71,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOverlayInfosForTarget() {
|
||||
public void testGetOverlayInfosForTarget() throws Exception {
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY3, TARGET), USER2);
|
||||
@@ -92,7 +96,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOverlayInfosForUser() {
|
||||
public void testGetOverlayInfosForUser() throws Exception {
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
@@ -119,7 +123,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPriority() {
|
||||
public void testPriority() throws Exception {
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY3, TARGET), USER);
|
||||
@@ -131,18 +135,21 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
|
||||
assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
|
||||
|
||||
assertTrue(impl.setLowestPriority(OVERLAY3, USER));
|
||||
assertEquals(impl.setLowestPriority(OVERLAY3, USER),
|
||||
Optional.of(new PackageAndUser(TARGET, USER)));
|
||||
assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2);
|
||||
|
||||
assertTrue(impl.setHighestPriority(OVERLAY3, USER));
|
||||
assertEquals(impl.setHighestPriority(OVERLAY3, USER),
|
||||
Optional.of(new PackageAndUser(TARGET, USER)));
|
||||
assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
|
||||
|
||||
assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER));
|
||||
assertEquals(impl.setPriority(OVERLAY, OVERLAY2, USER),
|
||||
Optional.of(new PackageAndUser(TARGET, USER)));
|
||||
assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlayInfoStateTransitions() {
|
||||
public void testOverlayInfoStateTransitions() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
assertNull(impl.getOverlayInfo(OVERLAY, USER));
|
||||
|
||||
@@ -153,7 +160,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
installNewPackage(target, USER);
|
||||
assertState(STATE_DISABLED, OVERLAY, USER);
|
||||
|
||||
impl.setEnabled(OVERLAY, true, USER);
|
||||
assertEquals(impl.setEnabled(OVERLAY, true, USER),
|
||||
Optional.of(new PackageAndUser(TARGET, USER)));
|
||||
assertState(STATE_ENABLED, OVERLAY, USER);
|
||||
|
||||
// target upgrades do not change the state of the overlay
|
||||
@@ -168,50 +176,40 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnOverlayPackageUpgraded() {
|
||||
final FakeListener listener = getListener();
|
||||
public void testOnOverlayPackageUpgraded() throws Exception {
|
||||
final FakeDeviceState.PackageBuilder target = target(TARGET);
|
||||
final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
|
||||
installNewPackage(target, USER);
|
||||
installNewPackage(overlay, USER);
|
||||
listener.count = 0;
|
||||
upgradePackage(overlay, USER);
|
||||
assertEquals(2, listener.count);
|
||||
|
||||
// upgrade to a version where the overlay has changed its target
|
||||
// expect once for the old target package, once for the new target package
|
||||
listener.count = 0;
|
||||
final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
|
||||
upgradePackage(overlay2, USER);
|
||||
assertEquals(3, listener.count);
|
||||
|
||||
listener.count = 0;
|
||||
upgradePackage(overlay2, USER);
|
||||
assertEquals(2, listener.count);
|
||||
final Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> pair =
|
||||
upgradePackage(overlay2, USER);
|
||||
assertEquals(pair.first, Optional.of(new PackageAndUser(TARGET, USER)));
|
||||
assertEquals(pair.second, Optional.of(new PackageAndUser("some.other.target", USER)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListener() {
|
||||
public void testSetEnabledAtVariousConditions() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
final FakeListener listener = getListener();
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
assertEquals(1, listener.count);
|
||||
listener.count = 0;
|
||||
assertThrows(OverlayManagerServiceImpl.OperationFailedException.class,
|
||||
() -> impl.setEnabled(OVERLAY, true, USER));
|
||||
|
||||
// request succeeded, and there was a change that needs to be
|
||||
// propagated to the rest of the system
|
||||
installNewPackage(target(TARGET), USER);
|
||||
assertEquals(1, listener.count);
|
||||
listener.count = 0;
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
assertEquals(impl.setEnabled(OVERLAY, true, USER),
|
||||
Optional.of(new PackageAndUser(TARGET, USER)));
|
||||
|
||||
impl.setEnabled(OVERLAY, true, USER);
|
||||
assertEquals(1, listener.count);
|
||||
listener.count = 0;
|
||||
|
||||
impl.setEnabled(OVERLAY, true, USER);
|
||||
assertEquals(0, listener.count);
|
||||
// request succeeded, but nothing changed
|
||||
assertFalse(impl.setEnabled(OVERLAY, true, USER).isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigSignaturePolicyOk() {
|
||||
public void testConfigSignaturePolicyOk() throws Exception {
|
||||
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
|
||||
reinitializeImpl();
|
||||
|
||||
@@ -229,7 +227,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigSignaturePolicyCertNok() {
|
||||
public void testConfigSignaturePolicyCertNok() throws Exception {
|
||||
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
|
||||
reinitializeImpl();
|
||||
|
||||
@@ -247,7 +245,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigSignaturePolicyNoConfig() {
|
||||
public void testConfigSignaturePolicyNoConfig() throws Exception {
|
||||
addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
||||
@@ -262,7 +260,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigSignaturePolicyNoRefPkg() {
|
||||
public void testConfigSignaturePolicyNoRefPkg() throws Exception {
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
||||
|
||||
@@ -276,7 +274,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigSignaturePolicyRefPkgNotSystem() {
|
||||
public void testConfigSignaturePolicyRefPkgNotSystem() throws Exception {
|
||||
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
|
||||
reinitializeImpl();
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.om;
|
||||
|
||||
import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -30,6 +32,7 @@ import android.content.pm.PackageInfo;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -43,13 +46,13 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
|
||||
class OverlayManagerServiceImplTestsBase {
|
||||
private OverlayManagerServiceImpl mImpl;
|
||||
private FakeDeviceState mState;
|
||||
private FakeListener mListener;
|
||||
private FakePackageManagerHelper mPackageManager;
|
||||
private FakeIdmapDaemon mIdmapDaemon;
|
||||
private OverlayConfig mOverlayConfig;
|
||||
@@ -58,7 +61,6 @@ class OverlayManagerServiceImplTestsBase {
|
||||
@Before
|
||||
public void setUp() {
|
||||
mState = new FakeDeviceState();
|
||||
mListener = new FakeListener();
|
||||
mPackageManager = new FakePackageManagerHelper(mState);
|
||||
mIdmapDaemon = new FakeIdmapDaemon(mState);
|
||||
mOverlayConfig = mock(OverlayConfig.class);
|
||||
@@ -73,18 +75,13 @@ class OverlayManagerServiceImplTestsBase {
|
||||
new IdmapManager(mIdmapDaemon, mPackageManager),
|
||||
new OverlayManagerSettings(),
|
||||
mOverlayConfig,
|
||||
new String[0],
|
||||
mListener);
|
||||
new String[0]);
|
||||
}
|
||||
|
||||
OverlayManagerServiceImpl getImpl() {
|
||||
return mImpl;
|
||||
}
|
||||
|
||||
FakeListener getListener() {
|
||||
return mListener;
|
||||
}
|
||||
|
||||
FakeIdmapDaemon getIdmapd() {
|
||||
return mIdmapDaemon;
|
||||
}
|
||||
@@ -155,7 +152,8 @@ class OverlayManagerServiceImplTestsBase {
|
||||
*
|
||||
* @throws IllegalStateException if the package is currently installed
|
||||
*/
|
||||
void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) {
|
||||
void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId)
|
||||
throws OperationFailedException {
|
||||
if (mState.select(pkg.packageName, userId) != null) {
|
||||
throw new IllegalStateException("package " + pkg.packageName + " already installed");
|
||||
}
|
||||
@@ -176,23 +174,30 @@ class OverlayManagerServiceImplTestsBase {
|
||||
* {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the
|
||||
* {@link android.content.Intent#EXTRA_REPLACING} extra.
|
||||
*
|
||||
* @return the two Optional<PackageAndUser> objects from starting and finishing the upgrade
|
||||
*
|
||||
* @throws IllegalStateException if the package is not currently installed
|
||||
*/
|
||||
void upgradePackage(FakeDeviceState.PackageBuilder pkg, int userId) {
|
||||
Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> upgradePackage(
|
||||
FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException {
|
||||
final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
|
||||
if (replacedPackage == null) {
|
||||
throw new IllegalStateException("package " + pkg.packageName + " not installed");
|
||||
}
|
||||
Optional<PackageAndUser> opt1 = Optional.empty();
|
||||
if (replacedPackage.targetPackageName != null) {
|
||||
mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
|
||||
opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
|
||||
}
|
||||
|
||||
mState.add(pkg, userId);
|
||||
Optional<PackageAndUser> opt2;
|
||||
if (pkg.targetPackage == null) {
|
||||
mImpl.onTargetPackageReplaced(pkg.packageName, userId);
|
||||
opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId);
|
||||
} else {
|
||||
mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
|
||||
opt2 = mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
|
||||
}
|
||||
|
||||
return Pair.create(opt1, opt2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,7 +208,7 @@ class OverlayManagerServiceImplTestsBase {
|
||||
*
|
||||
* @throws IllegalStateException if the package is not currently installed
|
||||
*/
|
||||
void uninstallPackage(String packageName, int userId) {
|
||||
void uninstallPackage(String packageName, int userId) throws OperationFailedException {
|
||||
final FakeDeviceState.Package pkg = mState.select(packageName, userId);
|
||||
if (pkg == null) {
|
||||
throw new IllegalStateException("package " + packageName+ " not installed");
|
||||
@@ -485,12 +490,4 @@ class OverlayManagerServiceImplTestsBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class FakeListener implements OverlayManagerServiceImpl.OverlayChangeListener {
|
||||
public int count;
|
||||
|
||||
public void onOverlaysChanged(@NonNull String targetPackage, int userId) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user