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

Commit fe1991ca authored by Evan Chen's avatar Evan Chen Committed by Android (Google) Code Review
Browse files

Merge "Create a job scheduler for CDM association cleanup"

parents 073db951 4107e3da
Loading
Loading
Loading
Loading
+36 −3
Original line number Diff line number Diff line
@@ -35,6 +35,10 @@ import java.util.Objects;
 * an app should have access to a companion device.
 */
public final class AssociationInfo implements Parcelable {
    /**
     * A String indicates the selfManaged device is not connected.
     */
    private static final String LAST_TIME_CONNECTED_NONE = "None";
    /**
     * A unique ID of this Association record.
     * Disclosed to the clients (ie. companion applications) for referring to this record (eg. in
@@ -52,6 +56,11 @@ public final class AssociationInfo implements Parcelable {
    private final boolean mSelfManaged;
    private boolean mNotifyOnDeviceNearby;
    private final long mTimeApprovedMs;
    /**
     * A long value indicates the last time connected reported by selfManaged devices
     * Default value is Long.MAX_VALUE.
     */
    private long mLastTimeConnectedMs;

    /**
     * Creates a new Association.
@@ -62,7 +71,7 @@ public final class AssociationInfo implements Parcelable {
    public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName,
            @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
            @Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby,
            long timeApprovedMs) {
            long timeApprovedMs, long lastTimeConnectedMs) {
        if (id <= 0) {
            throw new IllegalArgumentException("Association ID should be greater than 0");
        }
@@ -83,6 +92,7 @@ public final class AssociationInfo implements Parcelable {
        mSelfManaged = selfManaged;
        mNotifyOnDeviceNearby = notifyOnDeviceNearby;
        mTimeApprovedMs = timeApprovedMs;
        mLastTimeConnectedMs = lastTimeConnectedMs;
    }

    /**
@@ -151,13 +161,21 @@ public final class AssociationInfo implements Parcelable {
    }

    /**
     * Should only be used by the CdmService.
     * Should only be used by the CompanionDeviceManagerService.
     * @hide
     */
    public void setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) {
        mNotifyOnDeviceNearby = notifyOnDeviceNearby;
    }

    /**
     * Should only be used by the CompanionDeviceManagerService.
     * @hide
     */
    public void setLastTimeConnected(long lastTimeConnectedMs) {
        mLastTimeConnectedMs = lastTimeConnectedMs;
    }

    /** @hide */
    public boolean isNotifyOnDeviceNearby() {
        return mNotifyOnDeviceNearby;
@@ -173,6 +191,14 @@ public final class AssociationInfo implements Parcelable {
        return mUserId == userId && Objects.equals(mPackageName, packageName);
    }

    /**
     * @return the last time self reported disconnected for selfManaged only.
     * @hide
     */
    public Long getLastTimeConnectedMs() {
        return mLastTimeConnectedMs;
    }

    /**
     * Utility method for checking if the association represents a device with the given MAC
     * address.
@@ -223,6 +249,9 @@ public final class AssociationInfo implements Parcelable {
                + ", mSelfManaged=" + mSelfManaged
                + ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby
                + ", mTimeApprovedMs=" + new Date(mTimeApprovedMs)
                + ", mLastTimeConnectedMs=" + (
                    mLastTimeConnectedMs == Long.MAX_VALUE
                        ? LAST_TIME_CONNECTED_NONE : new Date(mLastTimeConnectedMs))
                + '}';
    }

@@ -236,6 +265,7 @@ public final class AssociationInfo implements Parcelable {
                && mSelfManaged == that.mSelfManaged
                && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
                && mTimeApprovedMs == that.mTimeApprovedMs
                && mLastTimeConnectedMs == that.mLastTimeConnectedMs
                && Objects.equals(mPackageName, that.mPackageName)
                && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
                && Objects.equals(mDisplayName, that.mDisplayName)
@@ -245,7 +275,8 @@ public final class AssociationInfo implements Parcelable {
    @Override
    public int hashCode() {
        return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
                mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mTimeApprovedMs);
                mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mTimeApprovedMs,
                mLastTimeConnectedMs);
    }

    @Override
@@ -267,6 +298,7 @@ public final class AssociationInfo implements Parcelable {
        dest.writeBoolean(mSelfManaged);
        dest.writeBoolean(mNotifyOnDeviceNearby);
        dest.writeLong(mTimeApprovedMs);
        dest.writeLong(mLastTimeConnectedMs);
    }

    private AssociationInfo(@NonNull Parcel in) {
@@ -282,6 +314,7 @@ public final class AssociationInfo implements Parcelable {
        mSelfManaged = in.readBoolean();
        mNotifyOnDeviceNearby = in.readBoolean();
        mTimeApprovedMs = in.readLong();
        mLastTimeConnectedMs = in.readLong();
    }

    @NonNull
+4 −0
Original line number Diff line number Diff line
@@ -6603,6 +6603,10 @@
                 android:permission="android.permission.BIND_JOB_SERVICE">
        </service>

        <service android:name="com.android.server.companion.AssociationCleanUpService"
                 android:permission="android.permission.BIND_JOB_SERVICE">
        </service>

        <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"
            android:exported="false">
            <intent-filter>
+77 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.companion;

import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;

import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.companion.AssociationRequest;
import android.content.ComponentName;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Slog;

import com.android.server.LocalServices;

/**
 * A Job Service responsible for clean up the Association.
 * The job will be executed only if the device is charging and in idle mode due to the application
 * will be killed if association/role are revoked.
 */
public class AssociationCleanUpService extends JobService {
    private static final String TAG = LOG_TAG + ".AssociationCleanUpService";
    private static final int JOB_ID = AssociationCleanUpService.class.hashCode();
    private static final long ONE_DAY_INTERVAL = 3 * 24 * 60 * 60 * 1000; // 1 Day
    private CompanionDeviceManagerServiceInternal mCdmServiceInternal = LocalServices.getService(
            CompanionDeviceManagerServiceInternal.class);

    @Override
    public boolean onStartJob(final JobParameters params) {
        Slog.i(LOG_TAG, "Execute the Association CleanUp job");
        // Special policy for APP_STREAMING role that need to revoke associations if the device
        // does not connect for 3 months.
        AsyncTask.execute(() -> {
            mCdmServiceInternal.associationCleanUp(AssociationRequest.DEVICE_PROFILE_APP_STREAMING);
            jobFinished(params, false);
        });
        return true;
    }

    @Override
    public boolean onStopJob(final JobParameters params) {
        Slog.d(TAG, "Association cleanup job stopped; id=" + params.getJobId()
                + ", reason="
                + JobParameters.getInternalReasonCodeDescription(
                params.getInternalStopReasonCode()));
        return false;
    }

    static void schedule(Context context) {
        Slog.i(LOG_TAG, "Scheduling the Association Cleanup job");
        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
        final JobInfo job = new JobInfo.Builder(JOB_ID,
                new ComponentName(context, AssociationCleanUpService.class))
                .setRequiresCharging(true)
                .setRequiresDeviceIdle(true)
                .setPeriodic(ONE_DAY_INTERVAL)
                .build();
        jobScheduler.schedule(job);
    }
}
+45 −1
Original line number Diff line number Diff line
@@ -149,6 +149,9 @@ public class CompanionDeviceManagerService extends SystemService
    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 long ASSOCIATION_CLEAN_UP_TIME_WINDOW =
            90L * 24 * 60 * 60 * 1000; // 3 months

    private static DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    static {
        sDateFormat.setTimeZone(TimeZone.getDefault());
@@ -179,6 +182,7 @@ public class CompanionDeviceManagerService extends SystemService
            new ArrayMap<>();
    private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners =
            new RemoteCallbackList<>();
    private final CompanionDeviceManagerServiceInternal mLocalService = new LocalService(this);

    final Handler mMainHandler = Handler.getMain();
    private CompanionDevicePresenceController mCompanionDevicePresenceController;
@@ -208,6 +212,8 @@ public class CompanionDeviceManagerService extends SystemService
        mPermissionControllerManager = requireNonNull(
                context.getSystemService(PermissionControllerManager.class));
        mUserManager = context.getSystemService(UserManager.class);

        LocalServices.addService(CompanionDeviceManagerServiceInternal.class, mLocalService);
    }

    @Override
@@ -250,6 +256,9 @@ public class CompanionDeviceManagerService extends SystemService
            } else {
                Slog.w(LOG_TAG, "No BluetoothAdapter available");
            }
        } else if (phase == PHASE_BOOT_COMPLETED) {
            // Run the Association CleanUp job service daily.
            AssociationCleanUpService.schedule(getContext());
        }
    }

@@ -294,6 +303,24 @@ public class CompanionDeviceManagerService extends SystemService
        return association;
    }

    // Revoke associations if the selfManaged companion device does not connect for 3
    // months for specific profile.
    private void associationCleanUp(String profile) {
        for (AssociationInfo ai : mAssociationStore.getAssociations()) {
            if (ai.isSelfManaged()
                    && profile.equals(ai.getDeviceProfile())
                    && System.currentTimeMillis() - ai.getLastTimeConnectedMs()
                    >= ASSOCIATION_CLEAN_UP_TIME_WINDOW) {
                Slog.d(LOG_TAG, "Removing the association for associationId: "
                        + ai.getId()
                        + " due to the device does not connect for 3 months."
                        + " Current time: "
                        + new Date(System.currentTimeMillis()));
                disassociateInternal(ai.getId());
            }
        }
    }

    void maybeGrantAutoRevokeExemptions() {
        Slog.d(LOG_TAG, "maybeGrantAutoRevokeExemptions()");
        PackageManager pm = getContext().getPackageManager();
@@ -573,6 +600,9 @@ public class CompanionDeviceManagerService extends SystemService
                return;
            }

            association.setLastTimeConnected(System.currentTimeMillis());
            mAssociationStore.updateAssociation(association);

            mCompanionDevicePresenceController.onDeviceNotifyAppeared(
                    association, getContext(), mMainHandler);
        }
@@ -735,7 +765,8 @@ public class CompanionDeviceManagerService extends SystemService
        final long timestamp = System.currentTimeMillis();

        final AssociationInfo association = new AssociationInfo(id, userId, packageName,
                macAddress, displayName, deviceProfile, selfManaged, false, timestamp);
                macAddress, displayName, deviceProfile, selfManaged, false, timestamp,
                Long.MAX_VALUE);
        Slog.i(LOG_TAG, "New CDM association created=" + association);
        mAssociationStore.addAssociation(association);

@@ -1297,4 +1328,17 @@ public class CompanionDeviceManagerService extends SystemService

        return Collections.unmodifiableMap(copy);
    }

    private final class LocalService extends CompanionDeviceManagerServiceInternal {
        private final CompanionDeviceManagerService mService;

        LocalService(CompanionDeviceManagerService service) {
            mService = service;
        }

        @Override
        public void associationCleanUp(String profile) {
            mService.associationCleanUp(profile);
        }
    }
}
+29 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.companion;

/**
 * Companion Device Manager Local System Service Interface.
 *
 * @hide Only for use within the system server.
 */
public abstract class CompanionDeviceManagerServiceInternal {
    /**
     * @see CompanionDeviceManagerService#associationCleanUp
     */
    public abstract void associationCleanUp(String profile);
}
Loading