Merge "Delay role revoke when the app is not visible to the user" into tm-qpr-dev
This commit is contained in:
@@ -55,6 +55,14 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
|
|
||||||
private final boolean mSelfManaged;
|
private final boolean mSelfManaged;
|
||||||
private final boolean mNotifyOnDeviceNearby;
|
private final boolean mNotifyOnDeviceNearby;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the association has been revoked (removed), but we keep the association
|
||||||
|
* record for final clean up (e.g. removing the app from the list of the role holders).
|
||||||
|
*
|
||||||
|
* @see CompanionDeviceManager#disassociate(int)
|
||||||
|
*/
|
||||||
|
private final boolean mRevoked;
|
||||||
private final long mTimeApprovedMs;
|
private final long mTimeApprovedMs;
|
||||||
/**
|
/**
|
||||||
* A long value indicates the last time connected reported by selfManaged devices
|
* A long value indicates the last time connected reported by selfManaged devices
|
||||||
@@ -71,7 +79,7 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName,
|
public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName,
|
||||||
@Nullable MacAddress macAddress, @Nullable CharSequence displayName,
|
@Nullable MacAddress macAddress, @Nullable CharSequence displayName,
|
||||||
@Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby,
|
@Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby,
|
||||||
long timeApprovedMs, long lastTimeConnectedMs) {
|
boolean revoked, long timeApprovedMs, long lastTimeConnectedMs) {
|
||||||
if (id <= 0) {
|
if (id <= 0) {
|
||||||
throw new IllegalArgumentException("Association ID should be greater than 0");
|
throw new IllegalArgumentException("Association ID should be greater than 0");
|
||||||
}
|
}
|
||||||
@@ -91,6 +99,7 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
|
|
||||||
mSelfManaged = selfManaged;
|
mSelfManaged = selfManaged;
|
||||||
mNotifyOnDeviceNearby = notifyOnDeviceNearby;
|
mNotifyOnDeviceNearby = notifyOnDeviceNearby;
|
||||||
|
mRevoked = revoked;
|
||||||
mTimeApprovedMs = timeApprovedMs;
|
mTimeApprovedMs = timeApprovedMs;
|
||||||
mLastTimeConnectedMs = lastTimeConnectedMs;
|
mLastTimeConnectedMs = lastTimeConnectedMs;
|
||||||
}
|
}
|
||||||
@@ -175,6 +184,14 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
return mUserId == userId && Objects.equals(mPackageName, packageName);
|
return mUserId == userId && Objects.equals(mPackageName, packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return if the association has been revoked (removed).
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public boolean isRevoked() {
|
||||||
|
return mRevoked;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the last time self reported disconnected for selfManaged only.
|
* @return the last time self reported disconnected for selfManaged only.
|
||||||
* @hide
|
* @hide
|
||||||
@@ -244,6 +261,7 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
+ ", mDeviceProfile='" + mDeviceProfile + '\''
|
+ ", mDeviceProfile='" + mDeviceProfile + '\''
|
||||||
+ ", mSelfManaged=" + mSelfManaged
|
+ ", mSelfManaged=" + mSelfManaged
|
||||||
+ ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby
|
+ ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby
|
||||||
|
+ ", mRevoked=" + mRevoked
|
||||||
+ ", mTimeApprovedMs=" + new Date(mTimeApprovedMs)
|
+ ", mTimeApprovedMs=" + new Date(mTimeApprovedMs)
|
||||||
+ ", mLastTimeConnectedMs=" + (
|
+ ", mLastTimeConnectedMs=" + (
|
||||||
mLastTimeConnectedMs == Long.MAX_VALUE
|
mLastTimeConnectedMs == Long.MAX_VALUE
|
||||||
@@ -260,6 +278,7 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
&& mUserId == that.mUserId
|
&& mUserId == that.mUserId
|
||||||
&& mSelfManaged == that.mSelfManaged
|
&& mSelfManaged == that.mSelfManaged
|
||||||
&& mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
|
&& mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
|
||||||
|
&& mRevoked == that.mRevoked
|
||||||
&& mTimeApprovedMs == that.mTimeApprovedMs
|
&& mTimeApprovedMs == that.mTimeApprovedMs
|
||||||
&& mLastTimeConnectedMs == that.mLastTimeConnectedMs
|
&& mLastTimeConnectedMs == that.mLastTimeConnectedMs
|
||||||
&& Objects.equals(mPackageName, that.mPackageName)
|
&& Objects.equals(mPackageName, that.mPackageName)
|
||||||
@@ -271,7 +290,7 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
|
return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
|
||||||
mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mTimeApprovedMs,
|
mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mRevoked, mTimeApprovedMs,
|
||||||
mLastTimeConnectedMs);
|
mLastTimeConnectedMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,6 +312,7 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
|
|
||||||
dest.writeBoolean(mSelfManaged);
|
dest.writeBoolean(mSelfManaged);
|
||||||
dest.writeBoolean(mNotifyOnDeviceNearby);
|
dest.writeBoolean(mNotifyOnDeviceNearby);
|
||||||
|
dest.writeBoolean(mRevoked);
|
||||||
dest.writeLong(mTimeApprovedMs);
|
dest.writeLong(mTimeApprovedMs);
|
||||||
dest.writeLong(mLastTimeConnectedMs);
|
dest.writeLong(mLastTimeConnectedMs);
|
||||||
}
|
}
|
||||||
@@ -309,6 +329,7 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
|
|
||||||
mSelfManaged = in.readBoolean();
|
mSelfManaged = in.readBoolean();
|
||||||
mNotifyOnDeviceNearby = in.readBoolean();
|
mNotifyOnDeviceNearby = in.readBoolean();
|
||||||
|
mRevoked = in.readBoolean();
|
||||||
mTimeApprovedMs = in.readLong();
|
mTimeApprovedMs = in.readLong();
|
||||||
mLastTimeConnectedMs = in.readLong();
|
mLastTimeConnectedMs = in.readLong();
|
||||||
}
|
}
|
||||||
@@ -352,11 +373,13 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
@NonNull
|
@NonNull
|
||||||
private final AssociationInfo mOriginalInfo;
|
private final AssociationInfo mOriginalInfo;
|
||||||
private boolean mNotifyOnDeviceNearby;
|
private boolean mNotifyOnDeviceNearby;
|
||||||
|
private boolean mRevoked;
|
||||||
private long mLastTimeConnectedMs;
|
private long mLastTimeConnectedMs;
|
||||||
|
|
||||||
private Builder(@NonNull AssociationInfo info) {
|
private Builder(@NonNull AssociationInfo info) {
|
||||||
mOriginalInfo = info;
|
mOriginalInfo = info;
|
||||||
mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby;
|
mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby;
|
||||||
|
mRevoked = info.mRevoked;
|
||||||
mLastTimeConnectedMs = info.mLastTimeConnectedMs;
|
mLastTimeConnectedMs = info.mLastTimeConnectedMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,6 +410,17 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should only be used by the CompanionDeviceManagerService.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public Builder setRevoked(boolean revoked) {
|
||||||
|
mRevoked = revoked;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
@@ -401,6 +435,7 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
mOriginalInfo.mDeviceProfile,
|
mOriginalInfo.mDeviceProfile,
|
||||||
mOriginalInfo.mSelfManaged,
|
mOriginalInfo.mSelfManaged,
|
||||||
mNotifyOnDeviceNearby,
|
mNotifyOnDeviceNearby,
|
||||||
|
mRevoked,
|
||||||
mOriginalInfo.mTimeApprovedMs,
|
mOriginalInfo.mTimeApprovedMs,
|
||||||
mLastTimeConnectedMs
|
mLastTimeConnectedMs
|
||||||
);
|
);
|
||||||
@@ -433,5 +468,12 @@ public final class AssociationInfo implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
Builder setLastTimeConnected(long lastTimeConnectedMs);
|
Builder setLastTimeConnected(long lastTimeConnectedMs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should only be used by the CompanionDeviceManagerService.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Builder setRevoked(boolean revoked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ class AssociationStoreImpl implements AssociationStore {
|
|||||||
private final Set<OnChangeListener> mListeners = new LinkedHashSet<>();
|
private final Set<OnChangeListener> mListeners = new LinkedHashSet<>();
|
||||||
|
|
||||||
void addAssociation(@NonNull AssociationInfo association) {
|
void addAssociation(@NonNull AssociationInfo association) {
|
||||||
|
// Validity check first.
|
||||||
|
checkNotRevoked(association);
|
||||||
|
|
||||||
final int id = association.getId();
|
final int id = association.getId();
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -99,6 +102,9 @@ class AssociationStoreImpl implements AssociationStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateAssociation(@NonNull AssociationInfo updated) {
|
void updateAssociation(@NonNull AssociationInfo updated) {
|
||||||
|
// Validity check first.
|
||||||
|
checkNotRevoked(updated);
|
||||||
|
|
||||||
final int id = updated.getId();
|
final int id = updated.getId();
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -292,6 +298,9 @@ class AssociationStoreImpl implements AssociationStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setAssociations(Collection<AssociationInfo> allAssociations) {
|
void setAssociations(Collection<AssociationInfo> allAssociations) {
|
||||||
|
// Validity check first.
|
||||||
|
allAssociations.forEach(AssociationStoreImpl::checkNotRevoked);
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.i(TAG, "setAssociations() n=" + allAssociations.size());
|
Log.i(TAG, "setAssociations() n=" + allAssociations.size());
|
||||||
final StringJoiner stringJoiner = new StringJoiner(", ");
|
final StringJoiner stringJoiner = new StringJoiner(", ");
|
||||||
@@ -324,4 +333,11 @@ class AssociationStoreImpl implements AssociationStore {
|
|||||||
mAddressMap.clear();
|
mAddressMap.clear();
|
||||||
mCachedPerUser.clear();
|
mCachedPerUser.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void checkNotRevoked(@NonNull AssociationInfo association) {
|
||||||
|
if (association.isRevoked()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Revoked (removed) associations MUST NOT appear in the AssociationStore");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
package com.android.server.companion;
|
package com.android.server.companion;
|
||||||
|
|
||||||
import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
|
import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
|
||||||
|
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
|
||||||
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
|
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
|
||||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
import static android.os.Process.SYSTEM_UID;
|
import static android.os.Process.SYSTEM_UID;
|
||||||
@@ -48,6 +49,8 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.app.ActivityManager.RunningAppProcessInfo;
|
||||||
import android.app.ActivityManagerInternal;
|
import android.app.ActivityManagerInternal;
|
||||||
import android.app.AppOpsManager;
|
import android.app.AppOpsManager;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
@@ -91,6 +94,7 @@ import android.util.SparseBooleanArray;
|
|||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.app.IAppOpsService;
|
import com.android.internal.app.IAppOpsService;
|
||||||
import com.android.internal.content.PackageMonitor;
|
import com.android.internal.content.PackageMonitor;
|
||||||
|
import com.android.internal.infra.PerUser;
|
||||||
import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
|
import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
|
||||||
import com.android.internal.os.BackgroundThread;
|
import com.android.internal.os.BackgroundThread;
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
@@ -104,6 +108,7 @@ import com.android.server.pm.UserManagerInternal;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -127,6 +132,9 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
|
|
||||||
private static final long ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT = DAYS.toMillis(90);
|
private static final long ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT = DAYS.toMillis(90);
|
||||||
|
|
||||||
|
private final ActivityManager mActivityManager;
|
||||||
|
private final OnPackageVisibilityChangeListener mOnPackageVisibilityChangeListener;
|
||||||
|
|
||||||
private PersistentDataStore mPersistentStore;
|
private PersistentDataStore mPersistentStore;
|
||||||
private final PersistUserStateHandler mUserPersistenceHandler;
|
private final PersistUserStateHandler mUserPersistenceHandler;
|
||||||
|
|
||||||
@@ -150,12 +158,40 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
@GuardedBy("mPreviouslyUsedIds")
|
@GuardedBy("mPreviouslyUsedIds")
|
||||||
private final SparseArray<Map<String, Set<Integer>>> mPreviouslyUsedIds = new SparseArray<>();
|
private final SparseArray<Map<String, Set<Integer>>> mPreviouslyUsedIds = new SparseArray<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A structure that consists of a set of revoked associations that pending for role holder
|
||||||
|
* removal per each user.
|
||||||
|
*
|
||||||
|
* @see #maybeRemoveRoleHolderForAssociation(AssociationInfo)
|
||||||
|
* @see #addToPendingRoleHolderRemoval(AssociationInfo)
|
||||||
|
* @see #removeFromPendingRoleHolderRemoval(AssociationInfo)
|
||||||
|
* @see #getPendingRoleHolderRemovalAssociationsForUser(int)
|
||||||
|
*/
|
||||||
|
@GuardedBy("mRevokedAssociationsPendingRoleHolderRemoval")
|
||||||
|
private final PerUserAssociationSet mRevokedAssociationsPendingRoleHolderRemoval =
|
||||||
|
new PerUserAssociationSet();
|
||||||
|
/**
|
||||||
|
* Contains uid-s of packages pending to be removed from the role holder list (after
|
||||||
|
* revocation of an association), which will happen one the package is no longer visible to the
|
||||||
|
* user.
|
||||||
|
* For quicker uid -> (userId, packageName) look-up this is not a {@code Set<Integer>} but
|
||||||
|
* a {@code Map<Integer, String>} which maps uid-s to packageName-s (userId-s can be derived
|
||||||
|
* from uid-s using {@link UserHandle#getUserId(int)}).
|
||||||
|
*
|
||||||
|
* @see #maybeRemoveRoleHolderForAssociation(AssociationInfo)
|
||||||
|
* @see #addToPendingRoleHolderRemoval(AssociationInfo)
|
||||||
|
* @see #removeFromPendingRoleHolderRemoval(AssociationInfo)
|
||||||
|
*/
|
||||||
|
@GuardedBy("mRevokedAssociationsPendingRoleHolderRemoval")
|
||||||
|
private final Map<Integer, String> mUidsPendingRoleHolderRemoval = new HashMap<>();
|
||||||
|
|
||||||
private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners =
|
private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners =
|
||||||
new RemoteCallbackList<>();
|
new RemoteCallbackList<>();
|
||||||
|
|
||||||
public CompanionDeviceManagerService(Context context) {
|
public CompanionDeviceManagerService(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
|
mActivityManager = context.getSystemService(ActivityManager.class);
|
||||||
mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
|
mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
|
||||||
mAppOpsManager = IAppOpsService.Stub.asInterface(
|
mAppOpsManager = IAppOpsService.Stub.asInterface(
|
||||||
ServiceManager.getService(Context.APP_OPS_SERVICE));
|
ServiceManager.getService(Context.APP_OPS_SERVICE));
|
||||||
@@ -165,6 +201,9 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
|
|
||||||
mUserPersistenceHandler = new PersistUserStateHandler();
|
mUserPersistenceHandler = new PersistUserStateHandler();
|
||||||
mAssociationStore = new AssociationStoreImpl();
|
mAssociationStore = new AssociationStoreImpl();
|
||||||
|
|
||||||
|
mOnPackageVisibilityChangeListener =
|
||||||
|
new OnPackageVisibilityChangeListener(mActivityManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -201,7 +240,33 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
mUserManager.getAliveUsers(), allAssociations, mPreviouslyUsedIds);
|
mUserManager.getAliveUsers(), allAssociations, mPreviouslyUsedIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
mAssociationStore.setAssociations(allAssociations);
|
final Set<AssociationInfo> activeAssociations =
|
||||||
|
new ArraySet<>(/* capacity */ allAssociations.size());
|
||||||
|
// A set contains the userIds that need to persist state after remove the app
|
||||||
|
// from the list of role holders.
|
||||||
|
final Set<Integer> usersToPersistStateFor = new ArraySet<>();
|
||||||
|
|
||||||
|
for (AssociationInfo association : allAssociations) {
|
||||||
|
if (!association.isRevoked()) {
|
||||||
|
activeAssociations.add(association);
|
||||||
|
} else if (maybeRemoveRoleHolderForAssociation(association)) {
|
||||||
|
// Nothing more to do here, but we'll need to persist all the associations to the
|
||||||
|
// disk afterwards.
|
||||||
|
usersToPersistStateFor.add(association.getUserId());
|
||||||
|
} else {
|
||||||
|
addToPendingRoleHolderRemoval(association);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mAssociationStore.setAssociations(activeAssociations);
|
||||||
|
|
||||||
|
// IMPORTANT: only do this AFTER mAssociationStore.setAssociations(), because
|
||||||
|
// persistStateForUser() queries AssociationStore.
|
||||||
|
// (If persistStateForUser() is invoked before mAssociationStore.setAssociations() it
|
||||||
|
// would effectively just clear-out all the persisted associations).
|
||||||
|
for (int userId : usersToPersistStateFor) {
|
||||||
|
persistStateForUser(userId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -351,10 +416,18 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void persistStateForUser(@UserIdInt int userId) {
|
private void persistStateForUser(@UserIdInt int userId) {
|
||||||
final List<AssociationInfo> updatedAssociations =
|
// We want to store both active associations and the revoked (removed) association that we
|
||||||
mAssociationStore.getAssociationsForUser(userId);
|
// are keeping around for the final clean-up (delayed role holder removal).
|
||||||
|
final List<AssociationInfo> allAssociations;
|
||||||
|
// Start with the active associations - these we can get from the AssociationStore.
|
||||||
|
allAssociations = new ArrayList<>(
|
||||||
|
mAssociationStore.getAssociationsForUser(userId));
|
||||||
|
// ... and add the revoked (removed) association, that are yet to be permanently removed.
|
||||||
|
allAssociations.addAll(getPendingRoleHolderRemovalAssociationsForUser(userId));
|
||||||
|
|
||||||
final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId);
|
final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId);
|
||||||
mPersistentStore.persistStateForUser(userId, updatedAssociations, usedIdsForUser);
|
|
||||||
|
mPersistentStore.persistStateForUser(userId, allAssociations, usedIdsForUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyListeners(
|
private void notifyListeners(
|
||||||
@@ -422,13 +495,17 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
removalWindow = ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT;
|
removalWindow = ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (AssociationInfo ai : mAssociationStore.getAssociations()) {
|
for (AssociationInfo association : mAssociationStore.getAssociations()) {
|
||||||
if (!ai.isSelfManaged()) continue;
|
if (!association.isSelfManaged()) continue;
|
||||||
final boolean isInactive = currentTime - ai.getLastTimeConnectedMs() >= removalWindow;
|
|
||||||
if (isInactive) {
|
final boolean isInactive =
|
||||||
Slog.i(TAG, "Removing inactive self-managed association: " + ai.getId());
|
currentTime - association.getLastTimeConnectedMs() >= removalWindow;
|
||||||
disassociateInternal(ai.getId());
|
if (!isInactive) continue;
|
||||||
}
|
|
||||||
|
final int id = association.getId();
|
||||||
|
|
||||||
|
Slog.i(TAG, "Removing inactive self-managed association id=" + id);
|
||||||
|
disassociateInternal(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,7 +745,7 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
enforceCallerIsSystemOr(userId, packageName);
|
enforceCallerIsSystemOr(userId, packageName);
|
||||||
|
|
||||||
AssociationInfo association = mAssociationStore.getAssociationsForPackageWithAddress(
|
AssociationInfo association = mAssociationStore.getAssociationsForPackageWithAddress(
|
||||||
userId, packageName, deviceAddress);
|
userId, packageName, deviceAddress);
|
||||||
|
|
||||||
if (association == null) {
|
if (association == null) {
|
||||||
throw new RemoteException(new DeviceNotAssociatedException("App " + packageName
|
throw new RemoteException(new DeviceNotAssociatedException("App " + packageName
|
||||||
@@ -728,7 +805,7 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
|
|
||||||
enforceUsesCompanionDeviceFeature(getContext(), userId, callingPackage);
|
enforceUsesCompanionDeviceFeature(getContext(), userId, callingPackage);
|
||||||
checkState(!ArrayUtils.isEmpty(
|
checkState(!ArrayUtils.isEmpty(
|
||||||
mAssociationStore.getAssociationsForPackage(userId, callingPackage)),
|
mAssociationStore.getAssociationsForPackage(userId, callingPackage)),
|
||||||
"App must have an association before calling this API");
|
"App must have an association before calling this API");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -788,8 +865,8 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
final long timestamp = System.currentTimeMillis();
|
final long timestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
final AssociationInfo association = new AssociationInfo(id, userId, packageName,
|
final AssociationInfo association = new AssociationInfo(id, userId, packageName,
|
||||||
macAddress, displayName, deviceProfile, selfManaged, false, timestamp,
|
macAddress, displayName, deviceProfile, selfManaged,
|
||||||
Long.MAX_VALUE);
|
/* notifyOnDeviceNearby */ false, /* revoked */ false, timestamp, Long.MAX_VALUE);
|
||||||
Slog.i(TAG, "New CDM association created=" + association);
|
Slog.i(TAG, "New CDM association created=" + association);
|
||||||
mAssociationStore.addAssociation(association);
|
mAssociationStore.addAssociation(association);
|
||||||
|
|
||||||
@@ -801,6 +878,11 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
|
|
||||||
updateSpecialAccessPermissionForAssociatedPackage(association);
|
updateSpecialAccessPermissionForAssociatedPackage(association);
|
||||||
logCreateAssociation(deviceProfile);
|
logCreateAssociation(deviceProfile);
|
||||||
|
|
||||||
|
// Don't need to update the mRevokedAssociationsPendingRoleHolderRemoval since
|
||||||
|
// maybeRemoveRoleHolderForAssociation in PackageInactivityListener will handle the case
|
||||||
|
// that there are other devices with the same profile, so the role holder won't be removed.
|
||||||
|
|
||||||
return association;
|
return association;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -881,36 +963,184 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
final String packageName = association.getPackageName();
|
final String packageName = association.getPackageName();
|
||||||
final String deviceProfile = association.getDeviceProfile();
|
final String deviceProfile = association.getDeviceProfile();
|
||||||
|
|
||||||
|
if (!maybeRemoveRoleHolderForAssociation(association)) {
|
||||||
|
// Need to remove the app from list of the role holders, but will have to do it later
|
||||||
|
// (the app is in foreground at the moment).
|
||||||
|
addToPendingRoleHolderRemoval(association);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to check if device still present now because CompanionDevicePresenceMonitor will
|
||||||
|
// remove current connected device after mAssociationStore.removeAssociation
|
||||||
final boolean wasPresent = mDevicePresenceMonitor.isDevicePresent(associationId);
|
final boolean wasPresent = mDevicePresenceMonitor.isDevicePresent(associationId);
|
||||||
|
|
||||||
// Removing the association.
|
// Removing the association.
|
||||||
mAssociationStore.removeAssociation(associationId);
|
mAssociationStore.removeAssociation(associationId);
|
||||||
|
// Do not need to persistUserState since CompanionDeviceManagerService will get callback
|
||||||
|
// from #onAssociationChanged, and it will handle the persistUserState which including
|
||||||
|
// active and revoked association.
|
||||||
logRemoveAssociation(deviceProfile);
|
logRemoveAssociation(deviceProfile);
|
||||||
|
|
||||||
final List<AssociationInfo> otherAssociations =
|
|
||||||
mAssociationStore.getAssociationsForPackage(userId, packageName);
|
|
||||||
|
|
||||||
// Check if the package is associated with other devices with the same profile.
|
|
||||||
// If not: take away the role.
|
|
||||||
if (deviceProfile != null) {
|
|
||||||
final boolean shouldKeepTheRole = any(otherAssociations,
|
|
||||||
it -> deviceProfile.equals(it.getDeviceProfile()));
|
|
||||||
if (!shouldKeepTheRole) {
|
|
||||||
Binder.withCleanCallingIdentity(() ->
|
|
||||||
removeRoleHolderForAssociation(getContext(), association));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wasPresent || !association.isNotifyOnDeviceNearby()) return;
|
if (!wasPresent || !association.isNotifyOnDeviceNearby()) return;
|
||||||
// The device was connected and the app was notified: check if we need to unbind the app
|
// The device was connected and the app was notified: check if we need to unbind the app
|
||||||
// now.
|
// now.
|
||||||
final boolean shouldStayBound = any(otherAssociations,
|
final boolean shouldStayBound = any(
|
||||||
|
mAssociationStore.getAssociationsForPackage(userId, packageName),
|
||||||
it -> it.isNotifyOnDeviceNearby()
|
it -> it.isNotifyOnDeviceNearby()
|
||||||
&& mDevicePresenceMonitor.isDevicePresent(it.getId()));
|
&& mDevicePresenceMonitor.isDevicePresent(it.getId()));
|
||||||
if (shouldStayBound) return;
|
if (shouldStayBound) return;
|
||||||
mCompanionAppController.unbindCompanionApplication(userId, packageName);
|
mCompanionAppController.unbindCompanionApplication(userId, packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First, checks if the companion application should be removed from the list role holders when
|
||||||
|
* upon association's removal, i.e.: association's profile (matches the role) is not null,
|
||||||
|
* the application does not have other associations with the same profile, etc.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Then, if establishes that the application indeed has to be removed from the list of the role
|
||||||
|
* holders, checks if it could be done right now -
|
||||||
|
* {@link android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, UserHandle, java.util.concurrent.Executor, java.util.function.Consumer) RoleManager#removeRoleHolderAsUser()}
|
||||||
|
* will kill the application's process, which leads poor user experience if the application was
|
||||||
|
* in foreground when this happened, to avoid this CDMS delays invoking
|
||||||
|
* {@code RoleManager.removeRoleHolderAsUser()} until the app is no longer in foreground.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the application does NOT need be removed from the list of the role
|
||||||
|
* holders OR if the application was successfully removed from the list of role holders.
|
||||||
|
* I.e.: from the role-management perspective the association is done with.
|
||||||
|
* {@code false} if the application needs to be removed from the list of role the role
|
||||||
|
* holders, BUT it CDMS would prefer to do it later.
|
||||||
|
* I.e.: application is in the foreground at the moment, but invoking
|
||||||
|
* {@code RoleManager.removeRoleHolderAsUser()} will kill the application's process,
|
||||||
|
* which would lead to the poor UX, hence need to try later.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private boolean maybeRemoveRoleHolderForAssociation(@NonNull AssociationInfo association) {
|
||||||
|
if (DEBUG) Log.d(TAG, "maybeRemoveRoleHolderForAssociation() association=" + association);
|
||||||
|
|
||||||
|
final String deviceProfile = association.getDeviceProfile();
|
||||||
|
if (deviceProfile == null) {
|
||||||
|
// No role was granted to for this association, there is nothing else we need to here.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the applications is associated with another devices with the profile. If so,
|
||||||
|
// it should remain the role holder.
|
||||||
|
final int id = association.getId();
|
||||||
|
final int userId = association.getUserId();
|
||||||
|
final String packageName = association.getPackageName();
|
||||||
|
final boolean roleStillInUse = any(
|
||||||
|
mAssociationStore.getAssociationsForPackage(userId, packageName),
|
||||||
|
it -> deviceProfile.equals(it.getDeviceProfile()) && id != it.getId());
|
||||||
|
if (roleStillInUse) {
|
||||||
|
// Application should remain a role holder, there is nothing else we need to here.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int packageProcessImportance = getPackageProcessImportance(userId, packageName);
|
||||||
|
if (packageProcessImportance <= IMPORTANCE_VISIBLE) {
|
||||||
|
// Need to remove the app from the list of role holders, but the process is visible to
|
||||||
|
// the user at the moment, so we'll need to it later: log and return false.
|
||||||
|
Slog.i(TAG, "Cannot remove role holder for the removed association id=" + id
|
||||||
|
+ " now - process is visible.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeRoleHolderForAssociation(getContext(), association);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getPackageProcessImportance(@UserIdInt int userId, @NonNull String packageName) {
|
||||||
|
return Binder.withCleanCallingIdentity(() -> {
|
||||||
|
final int uid =
|
||||||
|
mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId);
|
||||||
|
return mActivityManager.getUidImportance(uid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set revoked flag for active association and add the revoked association and the uid into
|
||||||
|
* the caches.
|
||||||
|
*
|
||||||
|
* @see #mRevokedAssociationsPendingRoleHolderRemoval
|
||||||
|
* @see #mUidsPendingRoleHolderRemoval
|
||||||
|
* @see OnPackageVisibilityChangeListener
|
||||||
|
*/
|
||||||
|
private void addToPendingRoleHolderRemoval(@NonNull AssociationInfo association) {
|
||||||
|
// First: set revoked flag.
|
||||||
|
association = AssociationInfo.builder(association)
|
||||||
|
.setRevoked(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final String packageName = association.getPackageName();
|
||||||
|
final int userId = association.getUserId();
|
||||||
|
final int uid = mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId);
|
||||||
|
|
||||||
|
// Second: add to the set.
|
||||||
|
synchronized (mRevokedAssociationsPendingRoleHolderRemoval) {
|
||||||
|
mRevokedAssociationsPendingRoleHolderRemoval.forUser(association.getUserId())
|
||||||
|
.add(association);
|
||||||
|
if (!mUidsPendingRoleHolderRemoval.containsKey(uid)) {
|
||||||
|
mUidsPendingRoleHolderRemoval.put(uid, packageName);
|
||||||
|
|
||||||
|
if (mUidsPendingRoleHolderRemoval.size() == 1) {
|
||||||
|
// Just added first uid: start the listener
|
||||||
|
mOnPackageVisibilityChangeListener.startListening();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the revoked association form the cache and also remove the uid form the map if
|
||||||
|
* there are other associations with the same package still pending for role holder removal.
|
||||||
|
*
|
||||||
|
* @see #mRevokedAssociationsPendingRoleHolderRemoval
|
||||||
|
* @see #mUidsPendingRoleHolderRemoval
|
||||||
|
* @see OnPackageVisibilityChangeListener
|
||||||
|
*/
|
||||||
|
private void removeFromPendingRoleHolderRemoval(@NonNull AssociationInfo association) {
|
||||||
|
final String packageName = association.getPackageName();
|
||||||
|
final int userId = association.getUserId();
|
||||||
|
final int uid = mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId);
|
||||||
|
|
||||||
|
synchronized (mRevokedAssociationsPendingRoleHolderRemoval) {
|
||||||
|
mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId)
|
||||||
|
.remove(association);
|
||||||
|
|
||||||
|
final boolean shouldKeepUidForRemoval = any(
|
||||||
|
getPendingRoleHolderRemovalAssociationsForUser(userId),
|
||||||
|
ai -> packageName.equals(ai.getPackageName()));
|
||||||
|
// Do not remove the uid form the map since other associations with
|
||||||
|
// the same packageName still pending for role holder removal.
|
||||||
|
if (!shouldKeepUidForRemoval) {
|
||||||
|
mUidsPendingRoleHolderRemoval.remove(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mUidsPendingRoleHolderRemoval.isEmpty()) {
|
||||||
|
// The set is empty now - can "turn off" the listener.
|
||||||
|
mOnPackageVisibilityChangeListener.stopListening();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a copy of the revoked associations set (safeguarding against
|
||||||
|
* {@code ConcurrentModificationException}-s).
|
||||||
|
*/
|
||||||
|
private @NonNull Set<AssociationInfo> getPendingRoleHolderRemovalAssociationsForUser(
|
||||||
|
@UserIdInt int userId) {
|
||||||
|
synchronized (mRevokedAssociationsPendingRoleHolderRemoval) {
|
||||||
|
// Return a copy.
|
||||||
|
return new ArraySet<>(mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPackageNameByUid(int uid) {
|
||||||
|
synchronized (mRevokedAssociationsPendingRoleHolderRemoval) {
|
||||||
|
return mUidsPendingRoleHolderRemoval.get(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateSpecialAccessPermissionForAssociatedPackage(AssociationInfo association) {
|
private void updateSpecialAccessPermissionForAssociatedPackage(AssociationInfo association) {
|
||||||
final PackageInfo packageInfo =
|
final PackageInfo packageInfo =
|
||||||
getPackageInfo(getContext(), association.getUserId(), association.getPackageName());
|
getPackageInfo(getContext(), association.getUserId(), association.getPackageName());
|
||||||
@@ -1128,4 +1358,80 @@ public class CompanionDeviceManagerService extends SystemService {
|
|||||||
persistStateForUser(userId);
|
persistStateForUser(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An OnUidImportanceListener class which watches the importance of the packages.
|
||||||
|
* In this class, we ONLY interested in the importance of the running process is greater than
|
||||||
|
* {@link RunningAppProcessInfo.IMPORTANCE_VISIBLE} for the uids have been added into the
|
||||||
|
* {@link mUidsPendingRoleHolderRemoval}. Lastly remove the role holder for the revoked
|
||||||
|
* associations for the same packages.
|
||||||
|
*
|
||||||
|
* @see #maybeRemoveRoleHolderForAssociation(AssociationInfo)
|
||||||
|
* @see #removeFromPendingRoleHolderRemoval(AssociationInfo)
|
||||||
|
* @see #getPendingRoleHolderRemovalAssociationsForUser(int)
|
||||||
|
*/
|
||||||
|
private class OnPackageVisibilityChangeListener implements
|
||||||
|
ActivityManager.OnUidImportanceListener {
|
||||||
|
final @NonNull ActivityManager mAm;
|
||||||
|
|
||||||
|
OnPackageVisibilityChangeListener(@NonNull ActivityManager am) {
|
||||||
|
this.mAm = am;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startListening() {
|
||||||
|
Binder.withCleanCallingIdentity(
|
||||||
|
() -> mAm.addOnUidImportanceListener(
|
||||||
|
/* listener */ OnPackageVisibilityChangeListener.this,
|
||||||
|
RunningAppProcessInfo.IMPORTANCE_VISIBLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopListening() {
|
||||||
|
Binder.withCleanCallingIdentity(
|
||||||
|
() -> mAm.removeOnUidImportanceListener(
|
||||||
|
/* listener */ OnPackageVisibilityChangeListener.this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUidImportance(int uid, int importance) {
|
||||||
|
if (importance <= RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
|
||||||
|
// The lower the importance value the more "important" the process is.
|
||||||
|
// We are only interested when the process ceases to be visible.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String packageName = getPackageNameByUid(uid);
|
||||||
|
if (packageName == null) {
|
||||||
|
// Not interested in this uid.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int userId = UserHandle.getUserId(uid);
|
||||||
|
|
||||||
|
boolean needToPersistStateForUser = false;
|
||||||
|
|
||||||
|
for (AssociationInfo association :
|
||||||
|
getPendingRoleHolderRemovalAssociationsForUser(userId)) {
|
||||||
|
if (!packageName.equals(association.getPackageName())) continue;
|
||||||
|
|
||||||
|
if (!maybeRemoveRoleHolderForAssociation(association)) {
|
||||||
|
// Did not remove the role holder, will have to try again later.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeFromPendingRoleHolderRemoval(association);
|
||||||
|
needToPersistStateForUser = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needToPersistStateForUser) {
|
||||||
|
mUserPersistenceHandler.postPersistUserState(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PerUserAssociationSet extends PerUser<Set<AssociationInfo>> {
|
||||||
|
@Override
|
||||||
|
protected @NonNull Set<AssociationInfo> create(int userId) {
|
||||||
|
return new ArraySet<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
* Since Android T the data is stored to "companion_device_manager.xml" file in
|
* Since Android T the data is stored to "companion_device_manager.xml" file in
|
||||||
* {@link Environment#getDataSystemDeDirectory(int) /data/system_de/}.
|
* {@link Environment#getDataSystemDeDirectory(int) /data/system_de/}.
|
||||||
*
|
*
|
||||||
* See {@link #getBaseStorageFileForUser(int) getBaseStorageFileForUser()}
|
* See {@link DataStoreUtils#getBaseStorageFileForUser(int, String)}
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Since Android T the data is stored using the v1 schema.
|
* Since Android T the data is stored using the v1 schema.
|
||||||
@@ -120,7 +120,7 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
* <li> {@link #readPreviouslyUsedIdsV1(TypedXmlPullParser, Map) readPreviouslyUsedIdsV1()}
|
* <li> {@link #readPreviouslyUsedIdsV1(TypedXmlPullParser, Map) readPreviouslyUsedIdsV1()}
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* The following snippet is a sample of a file that is using v0 schema.
|
* The following snippet is a sample of a file that is using v1 schema.
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* <state persistence-version="1">
|
* <state persistence-version="1">
|
||||||
* <associations>
|
* <associations>
|
||||||
@@ -130,6 +130,8 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
* mac_address="AA:BB:CC:DD:EE:00"
|
* mac_address="AA:BB:CC:DD:EE:00"
|
||||||
* self_managed="false"
|
* self_managed="false"
|
||||||
* notify_device_nearby="false"
|
* notify_device_nearby="false"
|
||||||
|
* revoked="false"
|
||||||
|
* last_time_connected="1634641160229"
|
||||||
* time_approved="1634389553216"/>
|
* time_approved="1634389553216"/>
|
||||||
*
|
*
|
||||||
* <association
|
* <association
|
||||||
@@ -139,6 +141,8 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
* display_name="Jhon's Chromebook"
|
* display_name="Jhon's Chromebook"
|
||||||
* self_managed="true"
|
* self_managed="true"
|
||||||
* notify_device_nearby="false"
|
* notify_device_nearby="false"
|
||||||
|
* revoked="false"
|
||||||
|
* last_time_connected="1634641160229"
|
||||||
* time_approved="1634641160229"/>
|
* time_approved="1634641160229"/>
|
||||||
* </associations>
|
* </associations>
|
||||||
*
|
*
|
||||||
@@ -178,6 +182,7 @@ final class PersistentDataStore {
|
|||||||
private static final String XML_ATTR_PROFILE = "profile";
|
private static final String XML_ATTR_PROFILE = "profile";
|
||||||
private static final String XML_ATTR_SELF_MANAGED = "self_managed";
|
private static final String XML_ATTR_SELF_MANAGED = "self_managed";
|
||||||
private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
|
private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
|
||||||
|
private static final String XML_ATTR_REVOKED = "revoked";
|
||||||
private static final String XML_ATTR_TIME_APPROVED = "time_approved";
|
private static final String XML_ATTR_TIME_APPROVED = "time_approved";
|
||||||
private static final String XML_ATTR_LAST_TIME_CONNECTED = "last_time_connected";
|
private static final String XML_ATTR_LAST_TIME_CONNECTED = "last_time_connected";
|
||||||
|
|
||||||
@@ -415,7 +420,8 @@ final class PersistentDataStore {
|
|||||||
|
|
||||||
out.add(new AssociationInfo(associationId, userId, appPackage,
|
out.add(new AssociationInfo(associationId, userId, appPackage,
|
||||||
MacAddress.fromString(deviceAddress), null, profile,
|
MacAddress.fromString(deviceAddress), null, profile,
|
||||||
/* managedByCompanionApp */false, notify, timeApproved, Long.MAX_VALUE));
|
/* managedByCompanionApp */ false, notify, /* revoked */ false, timeApproved,
|
||||||
|
Long.MAX_VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void readAssociationsV1(@NonNull TypedXmlPullParser parser,
|
private static void readAssociationsV1(@NonNull TypedXmlPullParser parser,
|
||||||
@@ -444,13 +450,14 @@ final class PersistentDataStore {
|
|||||||
final String displayName = readStringAttribute(parser, XML_ATTR_DISPLAY_NAME);
|
final String displayName = readStringAttribute(parser, XML_ATTR_DISPLAY_NAME);
|
||||||
final boolean selfManaged = readBooleanAttribute(parser, XML_ATTR_SELF_MANAGED);
|
final boolean selfManaged = readBooleanAttribute(parser, XML_ATTR_SELF_MANAGED);
|
||||||
final boolean notify = readBooleanAttribute(parser, XML_ATTR_NOTIFY_DEVICE_NEARBY);
|
final boolean notify = readBooleanAttribute(parser, XML_ATTR_NOTIFY_DEVICE_NEARBY);
|
||||||
|
final boolean revoked = readBooleanAttribute(parser, XML_ATTR_REVOKED, false);
|
||||||
final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
|
final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
|
||||||
final long lastTimeConnected = readLongAttribute(
|
final long lastTimeConnected = readLongAttribute(
|
||||||
parser, XML_ATTR_LAST_TIME_CONNECTED, Long.MAX_VALUE);
|
parser, XML_ATTR_LAST_TIME_CONNECTED, Long.MAX_VALUE);
|
||||||
|
|
||||||
final AssociationInfo associationInfo = createAssociationInfoNoThrow(associationId, userId,
|
final AssociationInfo associationInfo = createAssociationInfoNoThrow(associationId, userId,
|
||||||
appPackage, macAddress, displayName, profile, selfManaged, notify, timeApproved,
|
appPackage, macAddress, displayName, profile, selfManaged, notify, revoked,
|
||||||
lastTimeConnected);
|
timeApproved, lastTimeConnected);
|
||||||
if (associationInfo != null) {
|
if (associationInfo != null) {
|
||||||
out.add(associationInfo);
|
out.add(associationInfo);
|
||||||
}
|
}
|
||||||
@@ -503,6 +510,8 @@ final class PersistentDataStore {
|
|||||||
writeBooleanAttribute(serializer, XML_ATTR_SELF_MANAGED, a.isSelfManaged());
|
writeBooleanAttribute(serializer, XML_ATTR_SELF_MANAGED, a.isSelfManaged());
|
||||||
writeBooleanAttribute(
|
writeBooleanAttribute(
|
||||||
serializer, XML_ATTR_NOTIFY_DEVICE_NEARBY, a.isNotifyOnDeviceNearby());
|
serializer, XML_ATTR_NOTIFY_DEVICE_NEARBY, a.isNotifyOnDeviceNearby());
|
||||||
|
writeBooleanAttribute(
|
||||||
|
serializer, XML_ATTR_REVOKED, a.isRevoked());
|
||||||
writeLongAttribute(serializer, XML_ATTR_TIME_APPROVED, a.getTimeApprovedMs());
|
writeLongAttribute(serializer, XML_ATTR_TIME_APPROVED, a.getTimeApprovedMs());
|
||||||
writeLongAttribute(
|
writeLongAttribute(
|
||||||
serializer, XML_ATTR_LAST_TIME_CONNECTED, a.getLastTimeConnectedMs());
|
serializer, XML_ATTR_LAST_TIME_CONNECTED, a.getLastTimeConnectedMs());
|
||||||
@@ -544,11 +553,12 @@ final class PersistentDataStore {
|
|||||||
private static AssociationInfo createAssociationInfoNoThrow(int associationId,
|
private static AssociationInfo createAssociationInfoNoThrow(int associationId,
|
||||||
@UserIdInt int userId, @NonNull String appPackage, @Nullable MacAddress macAddress,
|
@UserIdInt int userId, @NonNull String appPackage, @Nullable MacAddress macAddress,
|
||||||
@Nullable CharSequence displayName, @Nullable String profile, boolean selfManaged,
|
@Nullable CharSequence displayName, @Nullable String profile, boolean selfManaged,
|
||||||
boolean notify, long timeApproved, long lastTimeConnected) {
|
boolean notify, boolean revoked, long timeApproved, long lastTimeConnected) {
|
||||||
AssociationInfo associationInfo = null;
|
AssociationInfo associationInfo = null;
|
||||||
try {
|
try {
|
||||||
associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress,
|
associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress,
|
||||||
displayName, profile, selfManaged, notify, timeApproved, lastTimeConnected);
|
displayName, profile, selfManaged, notify, revoked, timeApproved,
|
||||||
|
lastTimeConnected);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e);
|
if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ final class RolesUtils {
|
|||||||
final int userId = associationInfo.getUserId();
|
final int userId = associationInfo.getUserId();
|
||||||
final UserHandle userHandle = UserHandle.of(userId);
|
final UserHandle userHandle = UserHandle.of(userId);
|
||||||
|
|
||||||
|
Slog.i(TAG, "Removing CDM role holder, role=" + deviceProfile
|
||||||
|
+ ", package=u" + userId + "\\" + packageName);
|
||||||
roleManager.removeRoleHolderAsUser(deviceProfile, packageName,
|
roleManager.removeRoleHolderAsUser(deviceProfile, packageName,
|
||||||
MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
|
MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
|
||||||
success -> {
|
success -> {
|
||||||
|
|||||||
Reference in New Issue
Block a user