Merge "Revert "OMS: add transactional API""
This commit is contained in:
@@ -17,7 +17,6 @@
|
||||
package android.content.om;
|
||||
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayManagerTransaction;
|
||||
|
||||
/**
|
||||
* Api for getting information about overlay packages.
|
||||
@@ -164,18 +163,4 @@ interface IOverlayManager {
|
||||
* @param packageName The name of the overlay package whose idmap should be deleted.
|
||||
*/
|
||||
void invalidateCachesForOverlay(in String packageName, in int userIs);
|
||||
|
||||
/**
|
||||
* Perform a series of requests related to overlay packages. This is an
|
||||
* atomic operation: either all requests were performed successfully and
|
||||
* the changes were propagated to the rest of the system, or at least one
|
||||
* request could not be performed successfully and nothing is changed and
|
||||
* nothing is propagated to the rest of the system.
|
||||
*
|
||||
* @see OverlayManagerTransaction
|
||||
*
|
||||
* @param transaction the series of overlay related requests to perform
|
||||
* @throws SecurityException if the transaction failed
|
||||
*/
|
||||
void commit(in OverlayManagerTransaction transaction);
|
||||
}
|
||||
|
||||
@@ -253,29 +253,6 @@ public class OverlayManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a series of requests related to overlay packages. This is an
|
||||
* atomic operation: either all requests were performed successfully and
|
||||
* the changes were propagated to the rest of the system, or at least one
|
||||
* request could not be performed successfully and nothing is changed and
|
||||
* nothing is propagated to the rest of the system.
|
||||
*
|
||||
* @see OverlayManagerTransaction
|
||||
*
|
||||
* @param transaction the series of overlay related requests to perform
|
||||
* @throws Exception if not all the requests could be successfully and
|
||||
* atomically executed
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void commit(@NonNull final OverlayManagerTransaction transaction) {
|
||||
try {
|
||||
mService.commit(transaction);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting on R, actor enforcement and app visibility changes introduce additional failure
|
||||
* cases, but the SecurityException thrown with these checks is unexpected for existing
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
@@ -1,216 +0,0 @@
|
||||
/*
|
||||
* 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];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -24,8 +24,6 @@ import static android.content.Intent.ACTION_PACKAGE_REMOVED;
|
||||
import static android.content.Intent.ACTION_USER_ADDED;
|
||||
import static android.content.Intent.ACTION_USER_REMOVED;
|
||||
import static android.content.Intent.EXTRA_REASON;
|
||||
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED;
|
||||
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
|
||||
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
|
||||
import static android.os.Trace.TRACE_TAG_RRO;
|
||||
import static android.os.Trace.traceBegin;
|
||||
@@ -43,7 +41,6 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.om.IOverlayManager;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayManagerTransaction;
|
||||
import android.content.om.OverlayableInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
@@ -87,14 +84,11 @@ import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -244,13 +238,8 @@ public final class OverlayManagerService extends SystemService {
|
||||
|
||||
private final OverlayActorEnforcer mActorEnforcer;
|
||||
|
||||
private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> {
|
||||
persistSettings();
|
||||
FgThread.getHandler().post(() -> {
|
||||
List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId);
|
||||
updateActivityManager(affectedTargets, pair.userId);
|
||||
broadcastActionOverlayChanged(affectedTargets, pair.userId);
|
||||
});
|
||||
private final Consumer<PackageAndUser> mOnOverlaysChanged = (pair) -> {
|
||||
onOverlaysChanged(pair.packageName, pair.userId);
|
||||
};
|
||||
|
||||
public OverlayManagerService(@NonNull final Context context) {
|
||||
@@ -309,11 +298,11 @@ public final class OverlayManagerService extends SystemService {
|
||||
for (int i = 0; i < userCount; i++) {
|
||||
final UserInfo userInfo = users.get(i);
|
||||
if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
|
||||
// Initialize any users that can't be switched to, as their state would
|
||||
// Initialize any users that can't be switched to, as there state would
|
||||
// never be setup in onSwitchUser(). We will switch to the system user right
|
||||
// after this, and its state will be setup there.
|
||||
final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
|
||||
updatePackageManager(targets, users.get(i).id);
|
||||
updateOverlayPaths(users.get(i).id, targets);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,8 +316,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
// any asset changes to the rest of the system
|
||||
synchronized (mLock) {
|
||||
final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
|
||||
final List<String> affectedTargets = updatePackageManager(targets, newUserId);
|
||||
updateActivityManager(affectedTargets, newUserId);
|
||||
updateAssets(newUserId, targets);
|
||||
}
|
||||
persistSettings();
|
||||
} finally {
|
||||
@@ -418,10 +406,10 @@ public final class OverlayManagerService extends SystemService {
|
||||
try {
|
||||
if (pi.isOverlayPackage()) {
|
||||
mImpl.onOverlayPackageAdded(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
} else {
|
||||
mImpl.onTargetPackageAdded(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
}
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageAdded internal error", e);
|
||||
@@ -448,10 +436,10 @@ public final class OverlayManagerService extends SystemService {
|
||||
try {
|
||||
if (pi.isOverlayPackage()) {
|
||||
mImpl.onOverlayPackageChanged(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
} else {
|
||||
mImpl.onTargetPackageChanged(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
}
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageChanged internal error", e);
|
||||
@@ -475,7 +463,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
if (oi != null) {
|
||||
try {
|
||||
mImpl.onOverlayPackageReplacing(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageReplacing internal error", e);
|
||||
}
|
||||
@@ -500,10 +488,10 @@ public final class OverlayManagerService extends SystemService {
|
||||
try {
|
||||
if (pi.isOverlayPackage()) {
|
||||
mImpl.onOverlayPackageReplaced(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
} else {
|
||||
mImpl.onTargetPackageReplaced(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
}
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageReplaced internal error", e);
|
||||
@@ -528,10 +516,10 @@ public final class OverlayManagerService extends SystemService {
|
||||
try {
|
||||
if (oi != null) {
|
||||
mImpl.onOverlayPackageRemoved(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
} else {
|
||||
mImpl.onTargetPackageRemoved(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
}
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageRemoved internal error", e);
|
||||
@@ -557,7 +545,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
synchronized (mLock) {
|
||||
targets = mImpl.updateOverlaysForUser(userId);
|
||||
}
|
||||
updatePackageManager(targets, userId);
|
||||
updateOverlayPaths(userId, targets);
|
||||
} finally {
|
||||
traceEnd(TRACE_TAG_RRO);
|
||||
}
|
||||
@@ -654,7 +642,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mImpl.setEnabled(packageName, enable, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
@@ -686,7 +674,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
try {
|
||||
mImpl.setEnabledExclusive(packageName,
|
||||
false /* withinCategory */, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
@@ -719,7 +707,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
try {
|
||||
mImpl.setEnabledExclusive(packageName,
|
||||
true /* withinCategory */, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
@@ -751,7 +739,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mImpl.setPriority(packageName, parentPackageName, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
@@ -781,7 +769,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mImpl.setHighestPriority(packageName, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
@@ -811,7 +799,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mImpl.setLowestPriority(packageName, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(mOnOverlaysChanged);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
@@ -863,120 +851,6 @@ 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 -> list<targetPackageName>
|
||||
SparseArray<List<String>> affectedTargetsToUpdate = new SparseArray<>();
|
||||
|
||||
synchronized (mLock) {
|
||||
// map: userId -> set<targetPackageName>
|
||||
SparseArray<Set<String>> targetsToUpdate = new SparseArray<>();
|
||||
|
||||
// execute the requests (as calling user)
|
||||
for (final OverlayManagerTransaction.Request request : transaction) {
|
||||
executeRequest(request).ifPresent(target -> {
|
||||
Set<String> userTargets = targetsToUpdate.get(target.userId);
|
||||
if (userTargets == null) {
|
||||
userTargets = new ArraySet<String>();
|
||||
targetsToUpdate.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 < targetsToUpdate.size(); index++) {
|
||||
final int userId = targetsToUpdate.keyAt(index);
|
||||
final List<String> affectedTargets =
|
||||
updatePackageManager(targetsToUpdate.valueAt(index), userId);
|
||||
affectedTargetsToUpdate.put(userId, affectedTargets);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
} // synchronized (mLock)
|
||||
|
||||
FgThread.getHandler().post(() -> {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
// schedule apps to refresh + broadcast the ACTION_OVERLAY_CHANGED intents
|
||||
for (int index = 0; index < affectedTargetsToUpdate.size(); index++) {
|
||||
final int userId = affectedTargetsToUpdate.keyAt(index);
|
||||
final List<String> packageNames = affectedTargetsToUpdate.valueAt(index);
|
||||
|
||||
updateActivityManager(packageNames, userId);
|
||||
broadcastActionOverlayChanged(packageNames, userId);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShellCommand(@NonNull final FileDescriptor in,
|
||||
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
|
||||
@@ -1098,7 +972,152 @@ public final class OverlayManagerService extends SystemService {
|
||||
}
|
||||
};
|
||||
|
||||
private static final class PackageManagerHelperImpl implements PackageManagerHelper {
|
||||
private void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
|
||||
persistSettings();
|
||||
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().broadcastIntent(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 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);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PackageManagerHelperImpl implements PackageManagerHelper {
|
||||
|
||||
private final Context mContext;
|
||||
private final IPackageManager mPackageManager;
|
||||
@@ -1308,151 +1327,4 @@ 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 Collection<String> packageNames,
|
||||
final int userId) {
|
||||
for (final String packageName : packageNames) {
|
||||
broadcastActionOverlayChanged(packageName, userId);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user