Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 52ba00e4 authored by Guojing Yuan's avatar Guojing Yuan Committed by Android (Google) Code Review
Browse files

Merge "[CDM] Add CompanionExemptionProcessor" into main

parents 89563e88 57a1d062
Loading
Loading
Loading
Loading
+37 −179
Original line number Diff line number Diff line
@@ -31,16 +31,13 @@ import static android.os.UserHandle.getCallingUserId;

import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.Preconditions.checkState;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOr;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;

import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MINUTES;

import android.annotation.EnforcePermission;
import android.annotation.NonNull;
@@ -69,31 +66,22 @@ import android.companion.datatransfer.PermissionSyncRequest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.net.MacAddress;
import android.net.NetworkPolicyManager;
import android.os.Binder;
import android.os.Environment;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.PowerExemptionManager;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.permission.flags.Flags;
import android.util.ArraySet;
import android.util.ExceptionUtils;
import android.util.Slog;

import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
@@ -114,35 +102,27 @@ import com.android.server.companion.devicepresence.DevicePresenceProcessor;
import com.android.server.companion.devicepresence.ObservableUuid;
import com.android.server.companion.devicepresence.ObservableUuidStore;
import com.android.server.companion.transport.CompanionTransportManager;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;

import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@SuppressLint("LongLogTag")
public class CompanionDeviceManagerService extends SystemService {
    private static final String TAG = "CDM_CompanionDeviceManagerService";

    private static final long PAIR_WITHOUT_PROMPT_WINDOW_MS = 10 * 60 * 1000; // 10 min

    private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
    private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
    private static final int MAX_CN_LENGTH = 500;

    private final ActivityTaskManagerInternal mAtmInternal;
    private final ActivityManagerInternal mAmInternal;
    private final IAppOpsService mAppOpsManager;
    private final PowerExemptionManager mPowerExemptionManager;
    private final PackageManagerInternal mPackageManagerInternal;

    private final AssociationStore mAssociationStore;
    private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
    private final ObservableUuidStore mObservableUuidStore;

    private final CompanionExemptionProcessor mCompanionExemptionProcessor;
    private final AssociationRequestsProcessor mAssociationRequestsProcessor;
    private final SystemDataTransferProcessor mSystemDataTransferProcessor;
    private final BackupRestoreProcessor mBackupRestoreProcessor;
@@ -156,12 +136,15 @@ public class CompanionDeviceManagerService extends SystemService {
        super(context);

        final ActivityManager activityManager = context.getSystemService(ActivityManager.class);
        mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class);
        mAppOpsManager = IAppOpsService.Stub.asInterface(
                ServiceManager.getService(Context.APP_OPS_SERVICE));
        mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
        final PowerExemptionManager powerExemptionManager = context.getSystemService(
                PowerExemptionManager.class);
        final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
        final ActivityTaskManagerInternal atmInternal = LocalServices.getService(
                ActivityTaskManagerInternal.class);
        final ActivityManagerInternal amInternal = LocalServices.getService(
                ActivityManagerInternal.class);
        final PackageManagerInternal packageManagerInternal = LocalServices.getService(
                PackageManagerInternal.class);
        final UserManager userManager = context.getSystemService(UserManager.class);
        final PowerManagerInternal powerManagerInternal = LocalServices.getService(
                PowerManagerInternal.class);
@@ -173,25 +156,29 @@ public class CompanionDeviceManagerService extends SystemService {

        // Init processors
        mAssociationRequestsProcessor = new AssociationRequestsProcessor(context,
                mPackageManagerInternal, mAssociationStore);
        mBackupRestoreProcessor = new BackupRestoreProcessor(context, mPackageManagerInternal,
                packageManagerInternal, mAssociationStore);
        mBackupRestoreProcessor = new BackupRestoreProcessor(context, packageManagerInternal,
                mAssociationStore, associationDiskStore, mSystemDataTransferRequestStore,
                mAssociationRequestsProcessor);

        mCompanionAppBinder = new CompanionAppBinder(context);

        mCompanionExemptionProcessor = new CompanionExemptionProcessor(context,
                powerExemptionManager, appOpsManager, packageManagerInternal, atmInternal,
                amInternal, mAssociationStore);

        mDevicePresenceProcessor = new DevicePresenceProcessor(context,
                mCompanionAppBinder, userManager, mAssociationStore, mObservableUuidStore,
                powerManagerInternal);
                powerManagerInternal, mCompanionExemptionProcessor);

        mTransportManager = new CompanionTransportManager(context, mAssociationStore);

        mDisassociationProcessor = new DisassociationProcessor(context, activityManager,
                mAssociationStore, mPackageManagerInternal, mDevicePresenceProcessor,
                mAssociationStore, packageManagerInternal, mDevicePresenceProcessor,
                mCompanionAppBinder, mSystemDataTransferRequestStore, mTransportManager);

        mSystemDataTransferProcessor = new SystemDataTransferProcessor(this,
                mPackageManagerInternal, mAssociationStore,
                packageManagerInternal, mAssociationStore,
                mSystemDataTransferRequestStore, mTransportManager);

        // TODO(b/279663946): move context sync to a dedicated system service
@@ -202,7 +189,6 @@ public class CompanionDeviceManagerService extends SystemService {
    public void onStart() {
        // Init association stores
        mAssociationStore.refreshCache();
        mAssociationStore.registerLocalListener(mAssociationStoreChangeListener);

        // Init UUID store
        mObservableUuidStore.getObservableUuidsForUser(getContext().getUserId());
@@ -240,11 +226,11 @@ public class CompanionDeviceManagerService extends SystemService {

        if (associations.isEmpty()) return;

        updateAtm(userId, associations);
        mCompanionExemptionProcessor.updateAtm(userId, associations);

        BackgroundThread.getHandler().sendMessageDelayed(
                obtainMessage(CompanionDeviceManagerService::maybeGrantAutoRevokeExemptions, this),
                MINUTES.toMillis(10));
        try (ExecutorService executor = Executors.newSingleThreadExecutor()) {
            executor.execute(mCompanionExemptionProcessor::updateAutoRevokeExemptions);
        }
    }

    @Override
@@ -262,30 +248,30 @@ public class CompanionDeviceManagerService extends SystemService {
        if (!associationsForPackage.isEmpty()) {
            Slog.i(TAG, "Package removed or data cleared for user=[" + userId + "], package=["
                    + packageName + "]. Cleaning up CDM data...");
        }

            for (AssociationInfo association : associationsForPackage) {
                mDisassociationProcessor.disassociate(association.getId());
            }

            mCompanionAppBinder.onPackagesChanged(userId, packageName);
        }

        // Clear observable UUIDs for the package.
        final List<ObservableUuid> uuidsTobeObserved =
                mObservableUuidStore.getObservableUuidsForPackage(userId, packageName);
        for (ObservableUuid uuid : uuidsTobeObserved) {
            mObservableUuidStore.removeObservableUuid(userId, uuid.getUuid(), packageName);
        }

        mCompanionAppBinder.onPackagesChanged(userId);
    }

    private void onPackageModifiedInternal(@UserIdInt int userId, @NonNull String packageName) {
        final List<AssociationInfo> associationsForPackage =
        final List<AssociationInfo> associations =
                mAssociationStore.getAssociationsByPackage(userId, packageName);
        for (AssociationInfo association : associationsForPackage) {
            updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(),
                    association.getPackageName());
        }
        if (!associations.isEmpty()) {
            mCompanionExemptionProcessor.exemptPackage(userId, packageName, false);

        mCompanionAppBinder.onPackagesChanged(userId);
            mCompanionAppBinder.onPackagesChanged(userId, packageName);
        }
    }

    private void onPackageAddedInternal(@UserIdInt int userId, @NonNull String packageName) {
@@ -765,130 +751,6 @@ public class CompanionDeviceManagerService extends SystemService {
        }
    }

    /**
     * Update special access for the association's package
     */
    public void updateSpecialAccessPermissionForAssociatedPackage(int userId, String packageName) {
        final PackageInfo packageInfo =
                getPackageInfo(getContext(), userId, packageName);

        Binder.withCleanCallingIdentity(() -> updateSpecialAccessPermissionAsSystem(packageInfo));
    }

    private void updateSpecialAccessPermissionAsSystem(PackageInfo packageInfo) {
        if (packageInfo == null) {
            return;
        }

        if (containsEither(packageInfo.requestedPermissions,
                android.Manifest.permission.RUN_IN_BACKGROUND,
                android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
            mPowerExemptionManager.addToPermanentAllowList(packageInfo.packageName);
        } else {
            try {
                mPowerExemptionManager.removeFromPermanentAllowList(packageInfo.packageName);
            } catch (UnsupportedOperationException e) {
                Slog.w(TAG, packageInfo.packageName + " can't be removed from power save"
                        + " whitelist. It might due to the package is whitelisted by the system.");
            }
        }

        NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext());
        try {
            if (containsEither(packageInfo.requestedPermissions,
                    android.Manifest.permission.USE_DATA_IN_BACKGROUND,
                    android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)) {
                networkPolicyManager.addUidPolicy(
                        packageInfo.applicationInfo.uid,
                        NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
            } else {
                networkPolicyManager.removeUidPolicy(
                        packageInfo.applicationInfo.uid,
                        NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
            }
        } catch (IllegalArgumentException e) {
            Slog.e(TAG, e.getMessage());
        }

        exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
    }

    private void exemptFromAutoRevoke(String packageName, int uid) {
        try {
            mAppOpsManager.setMode(
                    AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
                    uid,
                    packageName,
                    AppOpsManager.MODE_IGNORED);
        } catch (RemoteException e) {
            Slog.w(TAG, "Error while granting auto revoke exemption for " + packageName, e);
        }
    }

    private void updateAtm(int userId, List<AssociationInfo> associations) {
        final Set<Integer> companionAppUids = new ArraySet<>();
        for (AssociationInfo association : associations) {
            final int uid = mPackageManagerInternal.getPackageUid(association.getPackageName(),
                    0, userId);
            if (uid >= 0) {
                companionAppUids.add(uid);
            }
        }
        if (mAtmInternal != null) {
            mAtmInternal.setCompanionAppUids(userId, companionAppUids);
        }
        if (mAmInternal != null) {
            // Make a copy of the set and send it to ActivityManager.
            mAmInternal.setCompanionAppUids(userId, new ArraySet<>(companionAppUids));
        }
    }

    private void maybeGrantAutoRevokeExemptions() {
        Slog.d(TAG, "maybeGrantAutoRevokeExemptions()");

        PackageManager pm = getContext().getPackageManager();
        for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) {
            SharedPreferences pref = getContext().getSharedPreferences(
                    new File(Environment.getUserSystemDirectory(userId), PREF_FILE_NAME),
                    Context.MODE_PRIVATE);
            if (pref.getBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, false)) {
                continue;
            }

            try {
                final List<AssociationInfo> associations =
                        mAssociationStore.getActiveAssociationsByUser(userId);
                for (AssociationInfo a : associations) {
                    try {
                        int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
                        exemptFromAutoRevoke(a.getPackageName(), uid);
                    } catch (PackageManager.NameNotFoundException e) {
                        Slog.w(TAG, "Unknown companion package: " + a.getPackageName(), e);
                    }
                }
            } finally {
                pref.edit().putBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, true).apply();
            }
        }
    }

    private final AssociationStore.OnChangeListener mAssociationStoreChangeListener =
            new AssociationStore.OnChangeListener() {
                @Override
                public void onAssociationChanged(int changeType, AssociationInfo association) {
                    Slog.d(TAG, "onAssociationChanged changeType=[" + changeType
                            + "], association=[" + association);

                    final int userId = association.getUserId();
                    final List<AssociationInfo> updatedAssociations =
                            mAssociationStore.getActiveAssociationsByUser(userId);

                    updateAtm(userId, updatedAssociations);
                    updateSpecialAccessPermissionForAssociatedPackage(association.getUserId(),
                            association.getPackageName());
                }
            };

    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
        @Override
        public void onPackageRemoved(String packageName, int uid) {
@@ -911,10 +773,6 @@ public class CompanionDeviceManagerService extends SystemService {
        }
    };

    private static <T> boolean containsEither(T[] array, T a, T b) {
        return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b);
    }

    private class LocalService implements CompanionDeviceManagerServiceInternal {

        @Override
+219 −0

File added.

Preview size limit exceeded, changes collapsed.

+4 −1
Original line number Diff line number Diff line
@@ -95,7 +95,10 @@ public class CompanionAppBinder {
    /**
     * On package changed.
     */
    public void onPackagesChanged(@UserIdInt int userId) {
    public void onPackagesChanged(@UserIdInt int userId, String packageName) {
        // TODO: We shouldn't need to clean up the whole user registry. We only need to remove the
        //       package. I will do it in a separate change since it's not appropriate to use
        //       PerUser anymore.
        mCompanionServicesRegister.invalidate(userId);
    }

+17 −10
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ import android.util.SparseBooleanArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.CollectionUtils;
import com.android.server.companion.CompanionExemptionProcessor;
import com.android.server.companion.association.AssociationStore;

import java.io.PrintWriter;
@@ -101,6 +102,8 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
    private final PowerManagerInternal mPowerManagerInternal;
    @NonNull
    private final UserManager mUserManager;
    @NonNull
    private final CompanionExemptionProcessor mCompanionExemptionProcessor;

    // NOTE: Same association may appear in more than one of the following sets at the same time.
    // (E.g. self-managed devices that have MAC addresses, could be reported as present by their
@@ -111,7 +114,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
    @NonNull
    private final Set<Integer> mNearbyBleDevices = new HashSet<>();
    @NonNull
    private final Set<Integer> mReportedSelfManagedDevices = new HashSet<>();
    private final Set<Integer> mConnectedSelfManagedDevices = new HashSet<>();
    @NonNull
    private final Set<ParcelUuid> mConnectedUuidDevices = new HashSet<>();
    @NonNull
@@ -146,7 +149,8 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
            @NonNull UserManager userManager,
            @NonNull AssociationStore associationStore,
            @NonNull ObservableUuidStore observableUuidStore,
            @NonNull PowerManagerInternal powerManagerInternal) {
            @NonNull PowerManagerInternal powerManagerInternal,
            @NonNull CompanionExemptionProcessor companionExemptionProcessor) {
        mContext = context;
        mCompanionAppBinder = companionAppBinder;
        mAssociationStore = associationStore;
@@ -156,6 +160,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
                mObservableUuidStore, this);
        mBleDeviceProcessor = new BleDeviceProcessor(associationStore, this);
        mPowerManagerInternal = powerManagerInternal;
        mCompanionExemptionProcessor = companionExemptionProcessor;
    }

    /** Initialize {@link DevicePresenceProcessor} */
@@ -404,7 +409,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
     * nearby (for "self-managed" associations).
     */
    public boolean isDevicePresent(int associationId) {
        return mReportedSelfManagedDevices.contains(associationId)
        return mConnectedSelfManagedDevices.contains(associationId)
                || mConnectedBtDevices.contains(associationId)
                || mNearbyBleDevices.contains(associationId)
                || mSimulated.contains(associationId);
@@ -451,7 +456,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
     * notifyDeviceAppeared()}
     */
    public void onSelfManagedDeviceConnected(int associationId) {
        onDevicePresenceEvent(mReportedSelfManagedDevices,
        onDevicePresenceEvent(mConnectedSelfManagedDevices,
                associationId, EVENT_SELF_MANAGED_APPEARED);
    }

@@ -467,7 +472,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
     * notifyDeviceDisappeared()}
     */
    public void onSelfManagedDeviceDisconnected(int associationId) {
        onDevicePresenceEvent(mReportedSelfManagedDevices,
        onDevicePresenceEvent(mConnectedSelfManagedDevices,
                associationId, EVENT_SELF_MANAGED_DISAPPEARED);
    }

@@ -475,7 +480,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
     * Marks a "self-managed" device as disconnected when binderDied.
     */
    public void onSelfManagedDeviceReporterBinderDied(int associationId) {
        onDevicePresenceEvent(mReportedSelfManagedDevices,
        onDevicePresenceEvent(mConnectedSelfManagedDevices,
                associationId, EVENT_SELF_MANAGED_DISAPPEARED);
    }

@@ -683,6 +688,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene

                if (association.shouldBindWhenPresent()) {
                    bindApplicationIfNeeded(userId, packageName, association.isSelfManaged());
                    mCompanionExemptionProcessor.exemptPackage(userId, packageName, true);
                } else {
                    return;
                }
@@ -715,6 +721,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
                // Check if there are other devices associated to the app that are present.
                if (!shouldBindPackage(userId, packageName)) {
                    mCompanionAppBinder.unbindCompanionApp(userId, packageName);
                    mCompanionExemptionProcessor.exemptPackage(userId, packageName, false);
                }
                break;
            default:
@@ -940,7 +947,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene

        mConnectedBtDevices.remove(id);
        mNearbyBleDevices.remove(id);
        mReportedSelfManagedDevices.remove(id);
        mConnectedSelfManagedDevices.remove(id);
        mSimulated.remove(id);
        synchronized (mBtDisconnectedDevices) {
            mBtDisconnectedDevices.remove(id);
@@ -1100,7 +1107,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
        out.append("Companion Device Present: ");
        if (mConnectedBtDevices.isEmpty()
                && mNearbyBleDevices.isEmpty()
                && mReportedSelfManagedDevices.isEmpty()) {
                && mConnectedSelfManagedDevices.isEmpty()) {
            out.append("<empty>\n");
            return;
        } else {
@@ -1130,11 +1137,11 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene
        }

        out.append("  Self-Reported Devices: ");
        if (mReportedSelfManagedDevices.isEmpty()) {
        if (mConnectedSelfManagedDevices.isEmpty()) {
            out.append("<empty>\n");
        } else {
            out.append("\n");
            for (int associationId : mReportedSelfManagedDevices) {
            for (int associationId : mConnectedSelfManagedDevices) {
                AssociationInfo a = mAssociationStore.getAssociationById(associationId);
                out.append("    ").append(a.toShortString()).append('\n');
            }