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;
|
package android.content.om;
|
||||||
|
|
||||||
import android.content.om.OverlayInfo;
|
import android.content.om.OverlayInfo;
|
||||||
|
import android.content.om.OverlayManagerTransaction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Api for getting information about overlay packages.
|
* 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.
|
* @param packageName The name of the overlay package whose idmap should be deleted.
|
||||||
*/
|
*/
|
||||||
void invalidateCachesForOverlay(in String packageName, in int userIs);
|
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
|
* Starting on R, actor enforcement and app visibility changes introduce additional failure
|
||||||
* cases, but the SecurityException thrown with these checks is unexpected for existing
|
* 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",
|
name: "OverlayDeviceTests",
|
||||||
srcs: ["src/**/*.java"],
|
srcs: ["src/**/*.java"],
|
||||||
platform_apis: true,
|
platform_apis: true,
|
||||||
static_libs: ["androidx.test.rules"],
|
certificate: "platform",
|
||||||
|
static_libs: [
|
||||||
|
"androidx.test.rules",
|
||||||
|
"testng",
|
||||||
|
],
|
||||||
test_suites: ["device-tests"],
|
test_suites: ["device-tests"],
|
||||||
data: [
|
data: [
|
||||||
":OverlayDeviceTests_AppOverlayOne",
|
":OverlayDeviceTests_AppOverlayOne",
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
<uses-sdk android:minSdkVersion="21" />
|
<uses-sdk android:minSdkVersion="21" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
|
||||||
|
|
||||||
<application>
|
<application>
|
||||||
<uses-library android:name="android.test.runner"/>
|
<uses-library android:name="android.test.runner"/>
|
||||||
</application>
|
</application>
|
||||||
|
|||||||
@@ -19,9 +19,20 @@
|
|||||||
<option name="test-suite-tag" value="apct" />
|
<option name="test-suite-tag" value="apct" />
|
||||||
<option name="test-suite-tag" value="apct-instrumentation" />
|
<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">
|
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
|
||||||
<option name="cleanup-apks" value="true" />
|
<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_AppOverlayOne.apk" />
|
||||||
<option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" />
|
<option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" />
|
||||||
<option name="test-file-name" value="OverlayDeviceTests_FrameworkOverlay.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 static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
|
||||||
import android.app.UiAutomation;
|
import android.annotation.NonNull;
|
||||||
import android.content.res.Resources;
|
import android.content.Context;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.content.om.OverlayManager;
|
||||||
|
import android.content.om.OverlayManagerTransaction;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
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.Executor;
|
||||||
import java.util.concurrent.FutureTask;
|
import java.util.concurrent.FutureTask;
|
||||||
|
|
||||||
class LocalOverlayManager {
|
class LocalOverlayManager {
|
||||||
private static final long TIMEOUT = 30;
|
private static final long TIMEOUT = 30;
|
||||||
|
|
||||||
public static void setEnabledAndWait(Executor executor, final String packageName,
|
public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable,
|
||||||
boolean enable) throws Exception {
|
@NonNull final String[] overlaysToDisable) throws Exception {
|
||||||
final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName;
|
final int userId = UserHandle.myUserId();
|
||||||
if (executeShellCommand("cmd overlay list").contains(pattern)) {
|
OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder();
|
||||||
// nothing to do, overlay already in the requested state
|
for (String pkg : overlaysToEnable) {
|
||||||
return;
|
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 Context ctx = InstrumentationRegistry.getTargetContext();
|
||||||
final String[] oldApkPaths = res.getAssets().getApkPaths();
|
|
||||||
FutureTask<Boolean> task = new FutureTask<>(() -> {
|
FutureTask<Boolean> task = new FutureTask<>(() -> {
|
||||||
while (true) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
Thread.sleep(10);
|
Thread.sleep(10);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
OverlayManager om = ctx.getSystemService(OverlayManager.class);
|
||||||
|
om.commit(transaction);
|
||||||
|
|
||||||
|
Executor executor = (cmd) -> new Thread(cmd).start();
|
||||||
executor.execute(task);
|
executor.execute(task);
|
||||||
executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName);
|
|
||||||
task.get(TIMEOUT, SECONDS);
|
task.get(TIMEOUT, SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String executeShellCommand(final String command)
|
private static boolean arrayTailContains(@NonNull final String[] array,
|
||||||
throws Exception {
|
@NonNull final String[] substrings) {
|
||||||
final UiAutomation uiAutomation =
|
if (array.length < substrings.length) {
|
||||||
InstrumentationRegistry.getInstrumentation().getUiAutomation();
|
return false;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
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.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public class WithMultipleOverlaysTest extends OverlayBaseTest {
|
public class WithMultipleOverlaysTest extends OverlayBaseTest {
|
||||||
@@ -33,9 +31,8 @@ public class WithMultipleOverlaysTest extends OverlayBaseTest {
|
|||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void enableOverlay() throws Exception {
|
public static void enableOverlay() throws Exception {
|
||||||
Executor executor = (cmd) -> new Thread(cmd).start();
|
LocalOverlayManager.toggleOverlaysAndWait(
|
||||||
LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
|
new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG},
|
||||||
LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true);
|
new String[]{});
|
||||||
LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import org.junit.BeforeClass;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public class WithOverlayTest extends OverlayBaseTest {
|
public class WithOverlayTest extends OverlayBaseTest {
|
||||||
@@ -32,10 +30,9 @@ public class WithOverlayTest extends OverlayBaseTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void enableOverlay() throws Exception {
|
public static void enableOverlays() throws Exception {
|
||||||
Executor executor = (cmd) -> new Thread(cmd).start();
|
LocalOverlayManager.toggleOverlaysAndWait(
|
||||||
LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
|
new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG},
|
||||||
LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
|
new String[]{APP_OVERLAY_TWO_PKG});
|
||||||
LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import org.junit.BeforeClass;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public class WithoutOverlayTest extends OverlayBaseTest {
|
public class WithoutOverlayTest extends OverlayBaseTest {
|
||||||
@@ -33,9 +31,8 @@ public class WithoutOverlayTest extends OverlayBaseTest {
|
|||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void disableOverlays() throws Exception {
|
public static void disableOverlays() throws Exception {
|
||||||
Executor executor = (cmd) -> new Thread(cmd).start();
|
LocalOverlayManager.toggleOverlaysAndWait(
|
||||||
LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false);
|
new String[]{},
|
||||||
LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
|
new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
|
||||||
LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,6 @@
|
|||||||
android_test {
|
android_test {
|
||||||
name: "OverlayDeviceTests_AppOverlayOne",
|
name: "OverlayDeviceTests_AppOverlayOne",
|
||||||
sdk_version: "current",
|
sdk_version: "current",
|
||||||
|
certificate: "platform",
|
||||||
aaptflags: ["--no-resource-removal"],
|
aaptflags: ["--no-resource-removal"],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,6 @@
|
|||||||
android_test {
|
android_test {
|
||||||
name: "OverlayDeviceTests_AppOverlayTwo",
|
name: "OverlayDeviceTests_AppOverlayTwo",
|
||||||
sdk_version: "current",
|
sdk_version: "current",
|
||||||
|
certificate: "platform",
|
||||||
aaptflags: ["--no-resource-removal"],
|
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_ADDED;
|
||||||
import static android.content.Intent.ACTION_USER_REMOVED;
|
import static android.content.Intent.ACTION_USER_REMOVED;
|
||||||
import static android.content.Intent.EXTRA_REASON;
|
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.content.pm.PackageManager.SIGNATURE_MATCH;
|
||||||
import static android.os.Trace.TRACE_TAG_RRO;
|
import static android.os.Trace.TRACE_TAG_RRO;
|
||||||
import static android.os.Trace.traceBegin;
|
import static android.os.Trace.traceBegin;
|
||||||
import static android.os.Trace.traceEnd;
|
import static android.os.Trace.traceEnd;
|
||||||
|
|
||||||
|
import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
@@ -39,6 +43,7 @@ import android.content.Intent;
|
|||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.om.IOverlayManager;
|
import android.content.om.IOverlayManager;
|
||||||
import android.content.om.OverlayInfo;
|
import android.content.om.OverlayInfo;
|
||||||
|
import android.content.om.OverlayManagerTransaction;
|
||||||
import android.content.om.OverlayableInfo;
|
import android.content.om.OverlayableInfo;
|
||||||
import android.content.pm.IPackageManager;
|
import android.content.pm.IPackageManager;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
@@ -48,6 +53,7 @@ import android.content.res.ApkAssets;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
import android.os.HandlerThread;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
@@ -64,7 +70,6 @@ import android.util.SparseArray;
|
|||||||
|
|
||||||
import com.android.internal.content.om.OverlayConfig;
|
import com.android.internal.content.om.OverlayConfig;
|
||||||
import com.android.server.FgThread;
|
import com.android.server.FgThread;
|
||||||
import com.android.server.IoThread;
|
|
||||||
import com.android.server.LocalServices;
|
import com.android.server.LocalServices;
|
||||||
import com.android.server.SystemConfig;
|
import com.android.server.SystemConfig;
|
||||||
import com.android.server.SystemService;
|
import com.android.server.SystemService;
|
||||||
@@ -82,12 +87,15 @@ import java.io.IOException;
|
|||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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.
|
* Service to manage asset overlays.
|
||||||
@@ -236,7 +244,14 @@ public final class OverlayManagerService extends SystemService {
|
|||||||
|
|
||||||
private final OverlayActorEnforcer mActorEnforcer;
|
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) {
|
public OverlayManagerService(@NonNull final Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
@@ -249,17 +264,19 @@ public final class OverlayManagerService extends SystemService {
|
|||||||
IdmapManager im = new IdmapManager(IdmapDaemon.getInstance(), mPackageManager);
|
IdmapManager im = new IdmapManager(IdmapDaemon.getInstance(), mPackageManager);
|
||||||
mSettings = new OverlayManagerSettings();
|
mSettings = new OverlayManagerSettings();
|
||||||
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
|
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
|
||||||
OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(),
|
OverlayConfig.getSystemInstance(), getDefaultOverlayPackages());
|
||||||
new OverlayChangeListener());
|
|
||||||
mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
|
mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
|
||||||
|
|
||||||
|
HandlerThread packageReceiverThread = new HandlerThread(TAG);
|
||||||
|
packageReceiverThread.start();
|
||||||
|
|
||||||
final IntentFilter packageFilter = new IntentFilter();
|
final IntentFilter packageFilter = new IntentFilter();
|
||||||
packageFilter.addAction(ACTION_PACKAGE_ADDED);
|
packageFilter.addAction(ACTION_PACKAGE_ADDED);
|
||||||
packageFilter.addAction(ACTION_PACKAGE_CHANGED);
|
packageFilter.addAction(ACTION_PACKAGE_CHANGED);
|
||||||
packageFilter.addAction(ACTION_PACKAGE_REMOVED);
|
packageFilter.addAction(ACTION_PACKAGE_REMOVED);
|
||||||
packageFilter.addDataScheme("package");
|
packageFilter.addDataScheme("package");
|
||||||
getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
|
getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
|
||||||
packageFilter, null, null);
|
packageFilter, null, packageReceiverThread.getThreadHandler());
|
||||||
|
|
||||||
final IntentFilter userFilter = new IntentFilter();
|
final IntentFilter userFilter = new IntentFilter();
|
||||||
userFilter.addAction(ACTION_USER_ADDED);
|
userFilter.addAction(ACTION_USER_ADDED);
|
||||||
@@ -292,11 +309,11 @@ public final class OverlayManagerService extends SystemService {
|
|||||||
for (int i = 0; i < userCount; i++) {
|
for (int i = 0; i < userCount; i++) {
|
||||||
final UserInfo userInfo = users.get(i);
|
final UserInfo userInfo = users.get(i);
|
||||||
if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
|
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
|
// never be setup in onSwitchUser(). We will switch to the system user right
|
||||||
// after this, and its state will be setup there.
|
// after this, and its state will be setup there.
|
||||||
final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
|
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
|
// any asset changes to the rest of the system
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
|
final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
|
||||||
updateAssets(newUserId, targets);
|
final List<String> affectedTargets = updatePackageManager(targets, newUserId);
|
||||||
|
updateActivityManager(affectedTargets, newUserId);
|
||||||
}
|
}
|
||||||
schedulePersistSettings();
|
persistSettings();
|
||||||
} finally {
|
} finally {
|
||||||
traceEnd(TRACE_TAG_RRO);
|
traceEnd(TRACE_TAG_RRO);
|
||||||
}
|
}
|
||||||
@@ -396,10 +414,17 @@ public final class OverlayManagerService extends SystemService {
|
|||||||
false);
|
false);
|
||||||
if (pi != null && !pi.applicationInfo.isInstantApp()) {
|
if (pi != null && !pi.applicationInfo.isInstantApp()) {
|
||||||
mPackageManager.cachePackageInfo(packageName, userId, pi);
|
mPackageManager.cachePackageInfo(packageName, userId, pi);
|
||||||
if (pi.isOverlayPackage()) {
|
|
||||||
mImpl.onOverlayPackageAdded(packageName, userId);
|
try {
|
||||||
} else {
|
if (pi.isOverlayPackage()) {
|
||||||
mImpl.onTargetPackageAdded(packageName, userId);
|
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);
|
false);
|
||||||
if (pi != null && pi.applicationInfo.isInstantApp()) {
|
if (pi != null && pi.applicationInfo.isInstantApp()) {
|
||||||
mPackageManager.cachePackageInfo(packageName, userId, pi);
|
mPackageManager.cachePackageInfo(packageName, userId, pi);
|
||||||
if (pi.isOverlayPackage()) {
|
|
||||||
mImpl.onOverlayPackageChanged(packageName, userId);
|
try {
|
||||||
} else {
|
if (pi.isOverlayPackage()) {
|
||||||
mImpl.onTargetPackageChanged(packageName, userId);
|
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);
|
mPackageManager.forgetPackageInfo(packageName, userId);
|
||||||
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
|
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
|
||||||
if (oi != null) {
|
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);
|
false);
|
||||||
if (pi != null && !pi.applicationInfo.isInstantApp()) {
|
if (pi != null && !pi.applicationInfo.isInstantApp()) {
|
||||||
mPackageManager.cachePackageInfo(packageName, userId, pi);
|
mPackageManager.cachePackageInfo(packageName, userId, pi);
|
||||||
if (pi.isOverlayPackage()) {
|
try {
|
||||||
mImpl.onOverlayPackageReplaced(packageName, userId);
|
if (pi.isOverlayPackage()) {
|
||||||
} else {
|
mImpl.onOverlayPackageReplaced(packageName, userId)
|
||||||
mImpl.onTargetPackageReplaced(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) {
|
synchronized (mLock) {
|
||||||
mPackageManager.forgetPackageInfo(packageName, userId);
|
mPackageManager.forgetPackageInfo(packageName, userId);
|
||||||
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
|
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
|
||||||
if (oi != null) {
|
|
||||||
mImpl.onOverlayPackageRemoved(packageName, userId);
|
try {
|
||||||
} else {
|
if (oi != null) {
|
||||||
mImpl.onTargetPackageRemoved(packageName, userId);
|
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) {
|
synchronized (mLock) {
|
||||||
targets = mImpl.updateOverlaysForUser(userId);
|
targets = mImpl.updateOverlaysForUser(userId);
|
||||||
}
|
}
|
||||||
updateOverlayPaths(userId, targets);
|
updatePackageManager(targets, userId);
|
||||||
} finally {
|
} finally {
|
||||||
traceEnd(TRACE_TAG_RRO);
|
traceEnd(TRACE_TAG_RRO);
|
||||||
}
|
}
|
||||||
@@ -602,7 +652,13 @@ public final class OverlayManagerService extends SystemService {
|
|||||||
final long ident = Binder.clearCallingIdentity();
|
final long ident = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mImpl.setEnabled(packageName, enable, realUserId);
|
try {
|
||||||
|
mImpl.setEnabled(packageName, enable, realUserId)
|
||||||
|
.ifPresent(mPropagateOverlayChange);
|
||||||
|
return true;
|
||||||
|
} catch (OperationFailedException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(ident);
|
Binder.restoreCallingIdentity(ident);
|
||||||
@@ -627,8 +683,14 @@ public final class OverlayManagerService extends SystemService {
|
|||||||
final long ident = Binder.clearCallingIdentity();
|
final long ident = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mImpl.setEnabledExclusive(packageName, false /* withinCategory */,
|
try {
|
||||||
realUserId);
|
mImpl.setEnabledExclusive(packageName,
|
||||||
|
false /* withinCategory */, realUserId)
|
||||||
|
.ifPresent(mPropagateOverlayChange);
|
||||||
|
return true;
|
||||||
|
} catch (OperationFailedException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(ident);
|
Binder.restoreCallingIdentity(ident);
|
||||||
@@ -654,8 +716,14 @@ public final class OverlayManagerService extends SystemService {
|
|||||||
final long ident = Binder.clearCallingIdentity();
|
final long ident = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mImpl.setEnabledExclusive(packageName, true /* withinCategory */,
|
try {
|
||||||
realUserId);
|
mImpl.setEnabledExclusive(packageName,
|
||||||
|
true /* withinCategory */, realUserId)
|
||||||
|
.ifPresent(mPropagateOverlayChange);
|
||||||
|
return true;
|
||||||
|
} catch (OperationFailedException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(ident);
|
Binder.restoreCallingIdentity(ident);
|
||||||
@@ -681,7 +749,13 @@ public final class OverlayManagerService extends SystemService {
|
|||||||
final long ident = Binder.clearCallingIdentity();
|
final long ident = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mImpl.setPriority(packageName, parentPackageName, realUserId);
|
try {
|
||||||
|
mImpl.setPriority(packageName, parentPackageName, realUserId)
|
||||||
|
.ifPresent(mPropagateOverlayChange);
|
||||||
|
return true;
|
||||||
|
} catch (OperationFailedException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(ident);
|
Binder.restoreCallingIdentity(ident);
|
||||||
@@ -705,7 +779,13 @@ public final class OverlayManagerService extends SystemService {
|
|||||||
final long ident = Binder.clearCallingIdentity();
|
final long ident = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mImpl.setHighestPriority(packageName, realUserId);
|
try {
|
||||||
|
mImpl.setHighestPriority(packageName, realUserId)
|
||||||
|
.ifPresent(mPropagateOverlayChange);
|
||||||
|
return true;
|
||||||
|
} catch (OperationFailedException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(ident);
|
Binder.restoreCallingIdentity(ident);
|
||||||
@@ -729,7 +809,13 @@ public final class OverlayManagerService extends SystemService {
|
|||||||
final long ident = Binder.clearCallingIdentity();
|
final long ident = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mImpl.setLowestPriority(packageName, realUserId);
|
try {
|
||||||
|
mImpl.setLowestPriority(packageName, realUserId)
|
||||||
|
.ifPresent(mPropagateOverlayChange);
|
||||||
|
return true;
|
||||||
|
} catch (OperationFailedException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(ident);
|
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
|
@Override
|
||||||
public void onShellCommand(@NonNull final FileDescriptor in,
|
public void onShellCommand(@NonNull final FileDescriptor in,
|
||||||
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
|
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
|
||||||
@@ -898,162 +1107,7 @@ public final class OverlayManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final class OverlayChangeListener
|
private static final class PackageManagerHelperImpl implements PackageManagerHelper {
|
||||||
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 final Context mContext;
|
private final Context mContext;
|
||||||
private final IPackageManager mPackageManager;
|
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.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +72,6 @@ final class OverlayManagerServiceImpl {
|
|||||||
private final OverlayManagerSettings mSettings;
|
private final OverlayManagerSettings mSettings;
|
||||||
private final OverlayConfig mOverlayConfig;
|
private final OverlayConfig mOverlayConfig;
|
||||||
private final String[] mDefaultOverlays;
|
private final String[] mDefaultOverlays;
|
||||||
private final OverlayChangeListener mListener;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to merge the overlay manager's (as read from overlays.xml)
|
* 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 IdmapManager idmapManager,
|
||||||
@NonNull final OverlayManagerSettings settings,
|
@NonNull final OverlayManagerSettings settings,
|
||||||
@NonNull final OverlayConfig overlayConfig,
|
@NonNull final OverlayConfig overlayConfig,
|
||||||
@NonNull final String[] defaultOverlays,
|
@NonNull final String[] defaultOverlays) {
|
||||||
@NonNull final OverlayChangeListener listener) {
|
|
||||||
mPackageManager = packageManager;
|
mPackageManager = packageManager;
|
||||||
mIdmapManager = idmapManager;
|
mIdmapManager = idmapManager;
|
||||||
mSettings = settings;
|
mSettings = settings;
|
||||||
mOverlayConfig = overlayConfig;
|
mOverlayConfig = overlayConfig;
|
||||||
mDefaultOverlays = defaultOverlays;
|
mDefaultOverlays = defaultOverlays;
|
||||||
mListener = listener;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -259,52 +257,58 @@ final class OverlayManagerServiceImpl {
|
|||||||
mSettings.removeUser(userId);
|
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) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
|
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) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
|
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) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId="
|
Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId="
|
||||||
+ 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) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "onTargetPackageReplaced packageName=" + packageName + " userId=" + userId);
|
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) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
|
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.
|
* Update the state of any overlays for this target.
|
||||||
*/
|
*/
|
||||||
private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName,
|
private Optional<PackageAndUser> updateAndRefreshOverlaysForTarget(
|
||||||
final int userId, final int flags) {
|
@NonNull final String targetPackageName, final int userId, final int flags)
|
||||||
|
throws OperationFailedException {
|
||||||
final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName,
|
final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName,
|
||||||
userId);
|
userId);
|
||||||
|
|
||||||
@@ -364,11 +368,13 @@ final class OverlayManagerServiceImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (modified) {
|
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) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
|
Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
|
||||||
}
|
}
|
||||||
@@ -376,8 +382,7 @@ final class OverlayManagerServiceImpl {
|
|||||||
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
||||||
if (overlayPackage == null) {
|
if (overlayPackage == null) {
|
||||||
Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
|
Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
|
||||||
onOverlayPackageRemoved(packageName, userId);
|
return onOverlayPackageRemoved(packageName, userId);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mSettings.init(packageName, userId, overlayPackage.overlayTarget,
|
mSettings.init(packageName, userId, overlayPackage.overlayTarget,
|
||||||
@@ -389,15 +394,17 @@ final class OverlayManagerServiceImpl {
|
|||||||
overlayPackage.overlayCategory);
|
overlayPackage.overlayCategory);
|
||||||
try {
|
try {
|
||||||
if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
|
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) {
|
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||||
Slog.e(TAG, "failed to update settings", e);
|
|
||||||
mSettings.remove(packageName, userId);
|
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) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId);
|
Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId);
|
||||||
}
|
}
|
||||||
@@ -405,14 +412,16 @@ final class OverlayManagerServiceImpl {
|
|||||||
try {
|
try {
|
||||||
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
|
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
|
||||||
if (updateState(oi.targetPackageName, packageName, userId, 0)) {
|
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) {
|
} 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) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId="
|
Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId="
|
||||||
+ userId);
|
+ userId);
|
||||||
@@ -423,14 +432,16 @@ final class OverlayManagerServiceImpl {
|
|||||||
if (updateState(oi.targetPackageName, packageName, userId,
|
if (updateState(oi.targetPackageName, packageName, userId,
|
||||||
FLAG_OVERLAY_IS_BEING_REPLACED)) {
|
FLAG_OVERLAY_IS_BEING_REPLACED)) {
|
||||||
removeIdmapIfPossible(oi);
|
removeIdmapIfPossible(oi);
|
||||||
mListener.onOverlaysChanged(oi.targetPackageName, userId);
|
return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
|
||||||
}
|
}
|
||||||
|
return Optional.empty();
|
||||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
} 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) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId="
|
Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId="
|
||||||
+ userId);
|
+ userId);
|
||||||
@@ -439,16 +450,12 @@ final class OverlayManagerServiceImpl {
|
|||||||
final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId);
|
final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId);
|
||||||
if (pkg == null) {
|
if (pkg == null) {
|
||||||
Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found");
|
Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found");
|
||||||
onOverlayPackageRemoved(packageName, userId);
|
return onOverlayPackageRemoved(packageName, userId);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId);
|
final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId);
|
||||||
if (mustReinitializeOverlay(pkg, oldOi)) {
|
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,
|
mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName,
|
||||||
pkg.applicationInfo.getBaseCodePath(),
|
pkg.applicationInfo.getBaseCodePath(),
|
||||||
isPackageConfiguredMutable(pkg.packageName),
|
isPackageConfiguredMutable(pkg.packageName),
|
||||||
@@ -457,22 +464,25 @@ final class OverlayManagerServiceImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
|
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) {
|
} 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 {
|
try {
|
||||||
final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId);
|
final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId);
|
||||||
if (mSettings.remove(packageName, userId)) {
|
if (mSettings.remove(packageName, userId)) {
|
||||||
removeIdmapIfPossible(overlayInfo);
|
removeIdmapIfPossible(overlayInfo);
|
||||||
mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId);
|
return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
|
||||||
}
|
}
|
||||||
|
return Optional.empty();
|
||||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
} 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);
|
return mSettings.getOverlaysForUser(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean setEnabled(@NonNull final String packageName, final boolean enable,
|
Optional<PackageAndUser> setEnabled(@NonNull final String packageName, final boolean enable,
|
||||||
final int userId) {
|
final int userId) throws OperationFailedException {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
|
Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
|
||||||
packageName, enable, userId));
|
packageName, enable, userId));
|
||||||
@@ -502,30 +512,33 @@ final class OverlayManagerServiceImpl {
|
|||||||
|
|
||||||
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
||||||
if (overlayPackage == null) {
|
if (overlayPackage == null) {
|
||||||
return false;
|
throw new OperationFailedException(
|
||||||
|
String.format("failed to find overlay package %s for user %d",
|
||||||
|
packageName, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
|
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
|
||||||
if (!oi.isMutable) {
|
if (!oi.isMutable) {
|
||||||
// Ignore immutable overlays.
|
// Ignore immutable overlays.
|
||||||
return false;
|
throw new OperationFailedException(
|
||||||
|
"cannot enable immutable overlay packages in runtime");
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean modified = mSettings.setEnabled(packageName, userId, enable);
|
boolean modified = mSettings.setEnabled(packageName, userId, enable);
|
||||||
modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
|
modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
|
||||||
|
|
||||||
if (modified) {
|
if (modified) {
|
||||||
mListener.onOverlaysChanged(oi.targetPackageName, userId);
|
return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
|
||||||
}
|
}
|
||||||
return true;
|
return Optional.empty();
|
||||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||||
return false;
|
throw new OperationFailedException("failed to update settings", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean setEnabledExclusive(@NonNull final String packageName, boolean withinCategory,
|
Optional<PackageAndUser> setEnabledExclusive(@NonNull final String packageName,
|
||||||
final int userId) {
|
boolean withinCategory, final int userId) throws OperationFailedException {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, String.format("setEnabledExclusive packageName=%s"
|
Slog.d(TAG, String.format("setEnabledExclusive packageName=%s"
|
||||||
+ " withinCategory=%s userId=%d", packageName, withinCategory, userId));
|
+ " withinCategory=%s userId=%d", packageName, withinCategory, userId));
|
||||||
@@ -533,7 +546,8 @@ final class OverlayManagerServiceImpl {
|
|||||||
|
|
||||||
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
||||||
if (overlayPackage == null) {
|
if (overlayPackage == null) {
|
||||||
return false;
|
throw new OperationFailedException(String.format(
|
||||||
|
"failed to find overlay package %s for user %d", packageName, userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -576,11 +590,11 @@ final class OverlayManagerServiceImpl {
|
|||||||
modified |= updateState(targetPackageName, packageName, userId, 0);
|
modified |= updateState(targetPackageName, packageName, userId, 0);
|
||||||
|
|
||||||
if (modified) {
|
if (modified) {
|
||||||
mListener.onOverlaysChanged(targetPackageName, userId);
|
return Optional.of(new PackageAndUser(targetPackageName, userId));
|
||||||
}
|
}
|
||||||
return true;
|
return Optional.empty();
|
||||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
} 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);
|
return mOverlayConfig.isEnabled(packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean setPriority(@NonNull final String packageName,
|
Optional<PackageAndUser> setPriority(@NonNull final String packageName,
|
||||||
@NonNull final String newParentPackageName, final int userId) {
|
@NonNull final String newParentPackageName, final int userId)
|
||||||
|
throws OperationFailedException {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName="
|
Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName="
|
||||||
+ newParentPackageName + " userId=" + userId);
|
+ newParentPackageName + " userId=" + userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPackageConfiguredMutable(packageName)) {
|
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);
|
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
||||||
if (overlayPackage == null) {
|
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)) {
|
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) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
|
Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPackageConfiguredMutable(packageName)) {
|
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);
|
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
||||||
if (overlayPackage == null) {
|
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)) {
|
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) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
|
Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPackageConfiguredMutable(packageName)) {
|
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);
|
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
|
||||||
if (overlayPackage == null) {
|
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)) {
|
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) {
|
void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
|
||||||
@@ -797,12 +820,13 @@ final class OverlayManagerServiceImpl {
|
|||||||
mIdmapManager.removeIdmap(oi, oi.userId);
|
mIdmapManager.removeIdmap(oi, oi.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OverlayChangeListener {
|
static final class OperationFailedException extends Exception {
|
||||||
|
OperationFailedException(@NonNull final String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
OperationFailedException(@NonNull final String message, @NonNull Throwable cause) {
|
||||||
* An event triggered by changes made to overlay state or settings as well as changes that
|
super(message, cause);
|
||||||
* add or remove target packages of overlays.
|
}
|
||||||
**/
|
|
||||||
void onOverlaysChanged(@NonNull String targetPackage, int userId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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
|
@Test
|
||||||
public void testImmutableEnabledChange() {
|
public void testImmutableEnabledChange() throws Exception {
|
||||||
final OverlayManagerServiceImpl impl = getImpl();
|
final OverlayManagerServiceImpl impl = getImpl();
|
||||||
installNewPackage(target(TARGET), USER);
|
installNewPackage(target(TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||||
@@ -106,7 +106,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMutableEnabledChangeHasNoEffect() {
|
public void testMutableEnabledChangeHasNoEffect() throws Exception {
|
||||||
final OverlayManagerServiceImpl impl = getImpl();
|
final OverlayManagerServiceImpl impl = getImpl();
|
||||||
installNewPackage(target(TARGET), USER);
|
installNewPackage(target(TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||||
@@ -134,7 +134,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMutableEnabledToImmutableEnabled() {
|
public void testMutableEnabledToImmutableEnabled() throws Exception {
|
||||||
final OverlayManagerServiceImpl impl = getImpl();
|
final OverlayManagerServiceImpl impl = getImpl();
|
||||||
installNewPackage(target(TARGET), USER);
|
installNewPackage(target(TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||||
@@ -178,7 +178,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMutablePriorityChange() {
|
public void testMutablePriorityChange() throws Exception {
|
||||||
final OverlayManagerServiceImpl impl = getImpl();
|
final OverlayManagerServiceImpl impl = getImpl();
|
||||||
installNewPackage(target(TARGET), USER);
|
installNewPackage(target(TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||||
@@ -218,7 +218,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testImmutablePriorityChange() {
|
public void testImmutablePriorityChange() throws Exception {
|
||||||
final OverlayManagerServiceImpl impl = getImpl();
|
final OverlayManagerServiceImpl impl = getImpl();
|
||||||
installNewPackage(target(TARGET), USER);
|
installNewPackage(target(TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY, 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 android.os.OverlayablePolicy.CONFIG_SIGNATURE;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.testng.Assert.assertThrows;
|
||||||
|
|
||||||
import android.content.om.OverlayInfo;
|
import android.content.om.OverlayInfo;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
@@ -35,6 +38,7 @@ import org.junit.runner.RunWith;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
|
public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
|
||||||
@@ -55,7 +59,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
|||||||
private static final String CERT_CONFIG_NOK = "config_certificate_nok";
|
private static final String CERT_CONFIG_NOK = "config_certificate_nok";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetOverlayInfo() {
|
public void testGetOverlayInfo() throws Exception {
|
||||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||||
|
|
||||||
final OverlayManagerServiceImpl impl = getImpl();
|
final OverlayManagerServiceImpl impl = getImpl();
|
||||||
@@ -67,7 +71,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetOverlayInfosForTarget() {
|
public void testGetOverlayInfosForTarget() throws Exception {
|
||||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY3, TARGET), USER2);
|
installNewPackage(overlay(OVERLAY3, TARGET), USER2);
|
||||||
@@ -92,7 +96,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetOverlayInfosForUser() {
|
public void testGetOverlayInfosForUser() throws Exception {
|
||||||
installNewPackage(target(TARGET), USER);
|
installNewPackage(target(TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
||||||
@@ -119,7 +123,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPriority() {
|
public void testPriority() throws Exception {
|
||||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY3, TARGET), USER);
|
installNewPackage(overlay(OVERLAY3, TARGET), USER);
|
||||||
@@ -131,18 +135,21 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
|||||||
|
|
||||||
assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
|
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);
|
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);
|
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);
|
assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOverlayInfoStateTransitions() {
|
public void testOverlayInfoStateTransitions() throws Exception {
|
||||||
final OverlayManagerServiceImpl impl = getImpl();
|
final OverlayManagerServiceImpl impl = getImpl();
|
||||||
assertNull(impl.getOverlayInfo(OVERLAY, USER));
|
assertNull(impl.getOverlayInfo(OVERLAY, USER));
|
||||||
|
|
||||||
@@ -153,7 +160,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
|||||||
installNewPackage(target, USER);
|
installNewPackage(target, USER);
|
||||||
assertState(STATE_DISABLED, OVERLAY, 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);
|
assertState(STATE_ENABLED, OVERLAY, USER);
|
||||||
|
|
||||||
// target upgrades do not change the state of the overlay
|
// target upgrades do not change the state of the overlay
|
||||||
@@ -168,50 +176,40 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnOverlayPackageUpgraded() {
|
public void testOnOverlayPackageUpgraded() throws Exception {
|
||||||
final FakeListener listener = getListener();
|
|
||||||
final FakeDeviceState.PackageBuilder target = target(TARGET);
|
final FakeDeviceState.PackageBuilder target = target(TARGET);
|
||||||
final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
|
final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
|
||||||
installNewPackage(target, USER);
|
installNewPackage(target, USER);
|
||||||
installNewPackage(overlay, USER);
|
installNewPackage(overlay, USER);
|
||||||
listener.count = 0;
|
|
||||||
upgradePackage(overlay, USER);
|
upgradePackage(overlay, USER);
|
||||||
assertEquals(2, listener.count);
|
|
||||||
|
|
||||||
// upgrade to a version where the overlay has changed its target
|
// 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");
|
final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
|
||||||
upgradePackage(overlay2, USER);
|
final Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> pair =
|
||||||
assertEquals(3, listener.count);
|
upgradePackage(overlay2, USER);
|
||||||
|
assertEquals(pair.first, Optional.of(new PackageAndUser(TARGET, USER)));
|
||||||
listener.count = 0;
|
assertEquals(pair.second, Optional.of(new PackageAndUser("some.other.target", USER)));
|
||||||
upgradePackage(overlay2, USER);
|
|
||||||
assertEquals(2, listener.count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testListener() {
|
public void testSetEnabledAtVariousConditions() throws Exception {
|
||||||
final OverlayManagerServiceImpl impl = getImpl();
|
final OverlayManagerServiceImpl impl = getImpl();
|
||||||
final FakeListener listener = getListener();
|
assertThrows(OverlayManagerServiceImpl.OperationFailedException.class,
|
||||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
() -> impl.setEnabled(OVERLAY, true, USER));
|
||||||
assertEquals(1, listener.count);
|
|
||||||
listener.count = 0;
|
|
||||||
|
|
||||||
|
// request succeeded, and there was a change that needs to be
|
||||||
|
// propagated to the rest of the system
|
||||||
installNewPackage(target(TARGET), USER);
|
installNewPackage(target(TARGET), USER);
|
||||||
assertEquals(1, listener.count);
|
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||||
listener.count = 0;
|
assertEquals(impl.setEnabled(OVERLAY, true, USER),
|
||||||
|
Optional.of(new PackageAndUser(TARGET, USER)));
|
||||||
|
|
||||||
impl.setEnabled(OVERLAY, true, USER);
|
// request succeeded, but nothing changed
|
||||||
assertEquals(1, listener.count);
|
assertFalse(impl.setEnabled(OVERLAY, true, USER).isPresent());
|
||||||
listener.count = 0;
|
|
||||||
|
|
||||||
impl.setEnabled(OVERLAY, true, USER);
|
|
||||||
assertEquals(0, listener.count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigSignaturePolicyOk() {
|
public void testConfigSignaturePolicyOk() throws Exception {
|
||||||
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
|
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
|
||||||
reinitializeImpl();
|
reinitializeImpl();
|
||||||
|
|
||||||
@@ -229,7 +227,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigSignaturePolicyCertNok() {
|
public void testConfigSignaturePolicyCertNok() throws Exception {
|
||||||
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
|
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
|
||||||
reinitializeImpl();
|
reinitializeImpl();
|
||||||
|
|
||||||
@@ -247,7 +245,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigSignaturePolicyNoConfig() {
|
public void testConfigSignaturePolicyNoConfig() throws Exception {
|
||||||
addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
|
addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
|
||||||
installNewPackage(target(TARGET), USER);
|
installNewPackage(target(TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
||||||
@@ -262,7 +260,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigSignaturePolicyNoRefPkg() {
|
public void testConfigSignaturePolicyNoRefPkg() throws Exception {
|
||||||
installNewPackage(target(TARGET), USER);
|
installNewPackage(target(TARGET), USER);
|
||||||
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
||||||
|
|
||||||
@@ -276,7 +274,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConfigSignaturePolicyRefPkgNotSystem() {
|
public void testConfigSignaturePolicyRefPkgNotSystem() throws Exception {
|
||||||
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
|
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
|
||||||
reinitializeImpl();
|
reinitializeImpl();
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.server.om;
|
package com.android.server.om;
|
||||||
|
|
||||||
|
import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
@@ -30,6 +32,7 @@ import android.content.pm.PackageInfo;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
@@ -43,13 +46,13 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
|
/** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
|
||||||
class OverlayManagerServiceImplTestsBase {
|
class OverlayManagerServiceImplTestsBase {
|
||||||
private OverlayManagerServiceImpl mImpl;
|
private OverlayManagerServiceImpl mImpl;
|
||||||
private FakeDeviceState mState;
|
private FakeDeviceState mState;
|
||||||
private FakeListener mListener;
|
|
||||||
private FakePackageManagerHelper mPackageManager;
|
private FakePackageManagerHelper mPackageManager;
|
||||||
private FakeIdmapDaemon mIdmapDaemon;
|
private FakeIdmapDaemon mIdmapDaemon;
|
||||||
private OverlayConfig mOverlayConfig;
|
private OverlayConfig mOverlayConfig;
|
||||||
@@ -58,7 +61,6 @@ class OverlayManagerServiceImplTestsBase {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
mState = new FakeDeviceState();
|
mState = new FakeDeviceState();
|
||||||
mListener = new FakeListener();
|
|
||||||
mPackageManager = new FakePackageManagerHelper(mState);
|
mPackageManager = new FakePackageManagerHelper(mState);
|
||||||
mIdmapDaemon = new FakeIdmapDaemon(mState);
|
mIdmapDaemon = new FakeIdmapDaemon(mState);
|
||||||
mOverlayConfig = mock(OverlayConfig.class);
|
mOverlayConfig = mock(OverlayConfig.class);
|
||||||
@@ -73,18 +75,13 @@ class OverlayManagerServiceImplTestsBase {
|
|||||||
new IdmapManager(mIdmapDaemon, mPackageManager),
|
new IdmapManager(mIdmapDaemon, mPackageManager),
|
||||||
new OverlayManagerSettings(),
|
new OverlayManagerSettings(),
|
||||||
mOverlayConfig,
|
mOverlayConfig,
|
||||||
new String[0],
|
new String[0]);
|
||||||
mListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayManagerServiceImpl getImpl() {
|
OverlayManagerServiceImpl getImpl() {
|
||||||
return mImpl;
|
return mImpl;
|
||||||
}
|
}
|
||||||
|
|
||||||
FakeListener getListener() {
|
|
||||||
return mListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
FakeIdmapDaemon getIdmapd() {
|
FakeIdmapDaemon getIdmapd() {
|
||||||
return mIdmapDaemon;
|
return mIdmapDaemon;
|
||||||
}
|
}
|
||||||
@@ -155,7 +152,8 @@ class OverlayManagerServiceImplTestsBase {
|
|||||||
*
|
*
|
||||||
* @throws IllegalStateException if the package is currently installed
|
* @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) {
|
if (mState.select(pkg.packageName, userId) != null) {
|
||||||
throw new IllegalStateException("package " + pkg.packageName + " already installed");
|
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#ACTION_PACKAGE_ADDED} broadcast with the
|
||||||
* {@link android.content.Intent#EXTRA_REPLACING} extra.
|
* {@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
|
* @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);
|
final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
|
||||||
if (replacedPackage == null) {
|
if (replacedPackage == null) {
|
||||||
throw new IllegalStateException("package " + pkg.packageName + " not installed");
|
throw new IllegalStateException("package " + pkg.packageName + " not installed");
|
||||||
}
|
}
|
||||||
|
Optional<PackageAndUser> opt1 = Optional.empty();
|
||||||
if (replacedPackage.targetPackageName != null) {
|
if (replacedPackage.targetPackageName != null) {
|
||||||
mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
|
opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
mState.add(pkg, userId);
|
mState.add(pkg, userId);
|
||||||
|
Optional<PackageAndUser> opt2;
|
||||||
if (pkg.targetPackage == null) {
|
if (pkg.targetPackage == null) {
|
||||||
mImpl.onTargetPackageReplaced(pkg.packageName, userId);
|
opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId);
|
||||||
} else {
|
} 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
|
* @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);
|
final FakeDeviceState.Package pkg = mState.select(packageName, userId);
|
||||||
if (pkg == null) {
|
if (pkg == null) {
|
||||||
throw new IllegalStateException("package " + packageName+ " not installed");
|
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