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

Commit 0486d20b authored by Felipe Leme's avatar Felipe Leme
Browse files

Moved logic that gets the service name to a separate set of classes.

Such refactoring will allow a system service to connect to more than one
AbstractRemoteService.

Bug: 117779333
Test: atest CtsContentCaptureServiceTestCases CtsAutoFillServiceTestCases

Change-Id: Ib14d80fa117893126d190f07b67228d29e0dcc76
parent 37fa1033
Loading
Loading
Loading
Loading
+3 −8
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
import com.android.server.autofill.AutofillManagerService.SmartSuggestionMode;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.infra.AbstractPerUserSystemService;
import com.android.server.infra.SecureSettingsServiceNameResolver;

import java.io.PrintWriter;
import java.util.ArrayList;
@@ -154,7 +155,8 @@ final class AutofillManagerServiceImpl
    AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog requestsHistory,
            LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
            AutofillCompatState autofillCompatState, boolean disabled) {
        super(master, lock, userId);
        super(master, new SecureSettingsServiceNameResolver(master.getContext(), userId,
                Settings.Secure.AUTOFILL_SERVICE), lock, userId);

        mRequestsHistory = requestsHistory;
        mUiLatencyHistory = uiLatencyHistory;
@@ -190,11 +192,6 @@ final class AutofillManagerServiceImpl
        return mInfo.getServiceInfo();
    }

    @Override // from PerUserSystemService
    protected String getDefaultComponentName() {
        return getComponentNameFromSettings();
    }

    @Nullable
    String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
        return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
@@ -852,8 +849,6 @@ final class AutofillManagerServiceImpl
            pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabelLocked());
            pw.print(prefix); pw.print("Target SDK: "); pw.println(getTargedSdkLocked());
        }
        pw.print(prefix); pw.print("Component from settings: ");
            pw.println(getComponentNameFromSettings());
        pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
                .getString(R.string.config_defaultAutofillService));
        pw.print(prefix); pw.print("Field classification enabled: ");
+6 −4
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ import java.util.List;
 * <ul>
 *   <li>Disabling the service when {@link UserManager} restrictions change.
 *   <li>Refreshing the service when its underlying
 *   {@link #getServiceSettingsProperty() Settings property} changed.
 *   {@link #getSettingsProperty() Settings property} changed.
 *   <li>Calling the service when other Settings properties changed.
 * </ul>
 *
@@ -214,7 +214,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
    }

    /**
     * Temporary sets the service implementation.
     * Temporarily sets the service implementation.
     *
     * <p>Typically used by Shell command and/or CTS tests.
     *
@@ -473,8 +473,10 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem
            pw.print(" Verbose: "); pw.println(realVerbose);
            pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
            pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
            pw.print(prefix); pw.print("Settings property: "); pw.println(
                    getServiceSettingsProperty());
            final String settingsProperty = getServiceSettingsProperty();
            if (settingsProperty != null) {
                pw.print(prefix); pw.print("Settings property: "); pw.println(settingsProperty);
            }
            pw.print(prefix); pw.print("Cached services: ");
            if (size == 0) {
                pw.println("none");
+14 −103
Original line number Diff line number Diff line
@@ -26,17 +26,12 @@ import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
import android.util.TimeUtils;

import com.android.internal.annotations.GuardedBy;

@@ -54,15 +49,14 @@ import java.io.PrintWriter;
public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S, M>,
        M extends AbstractMasterSystemService<M, S>> {

    /** Handler message to {@link #resetTemporaryServiceLocked()} */
    private static final int MSG_RESET_TEMPORARY_SERVICE = 0;

    protected final @UserIdInt int mUserId;
    protected final Object mLock;
    protected final String mTag = getClass().getSimpleName();

    protected final M mMaster;

    private final ServiceNameResolver mServiceNameResolver;

    /**
     * Whether service was disabled for user due to {@link UserManager} restrictions.
     */
@@ -78,29 +72,13 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
    @GuardedBy("mLock")
    private ServiceInfo mServiceInfo;

    /**
     * Temporary service name set by {@link #setTemporaryServiceLocked(String, int)}.
     *
     * <p>Typically used by Shell command and/or CTS tests.
     */
    @GuardedBy("mLock")
    private String mTemporaryServiceName;

    /**
     * When the temporary service will expire (and reset back to the default).
     */
    @GuardedBy("mLock")
    private long mTemporaryServiceExpiration;

    /**
     * Handler used to reset the temporary service name.
     */
    @GuardedBy("mLock")
    private Handler mTemporaryHandler;

    protected AbstractPerUserSystemService(@NonNull M master, @NonNull Object lock,
    protected AbstractPerUserSystemService(@NonNull M master,
            @NonNull ServiceNameResolver serviceNamer, @NonNull Object lock,
            @UserIdInt int userId) {
        mMaster = master;
        mServiceNameResolver = serviceNamer;
        mServiceNameResolver
                .setOnTemporaryServiceNameChangedCallback(() -> updateLocked(mDisabled));
        mLock = lock;
        mUserId = userId;
    }
@@ -219,43 +197,18 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
    }

    /**
     * Gets the current name of the service, which is either the
     *  {@link #getDefaultComponentName() default service} or the
     * Gets the current name of the service, which is either the default service or the
     *  {@link #setTemporaryServiceLocked(String, int) temporary one}.
     */
    protected final String getComponentNameLocked() {
        if (mTemporaryServiceName != null) {
            // Always log it, as it should only be used on CTS or during development
            Slog.w(mTag, "getComponentName(): using temporary name " + mTemporaryServiceName);
            return mTemporaryServiceName;
        }
        return getDefaultComponentName();
    }

    /**
     * Gets the name of the default component for the service.
     *
     * <p>Typically implemented by returning {@link #getComponentNameFromSettings()} or by using
     * a string from the system resources.
     */
    @Nullable
    protected abstract String getDefaultComponentName();

    /**
     * Gets this name of the remote service this service binds to as defined by {@link Settings}.
     */
    @Nullable
    protected final String getComponentNameFromSettings() {
        final String property = mMaster.getServiceSettingsProperty();
        return property == null ? null : Settings.Secure
                .getStringForUser(getContext().getContentResolver(), property, mUserId);
        return mServiceNameResolver.getServiceNameLocked();
    }

    /**
     * Checks whether the current service for the user was temporarily set.
     */
    public final boolean isTemporaryServiceSetLocked() {
        return mTemporaryServiceName != null;
        return mServiceNameResolver.isTemporaryLocked();
    }

    /**
@@ -266,49 +219,14 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
     * to the default component after this timeout expires).
     */
    protected final void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
        mTemporaryServiceName = componentName;

        if (mTemporaryHandler == null) {
            mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
                        synchronized (mLock) {
                            resetTemporaryServiceLocked();
                        }
                    } else {
                        Slog.wtf(mTag, "invalid handler msg: " + msg);
                    }
                }
            };
        } else {
            removeResetTemporaryServiceMessageLocked();
        }
        mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
        mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);

        updateLocked(mDisabled);
    }

    private void removeResetTemporaryServiceMessageLocked() {
        if (mMaster.verbose) {
            Slog.v(mTag, "setTemporaryServiceLocked(): removing old message");
        }
        // NOTE: caller should already have checked it
        mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
        mServiceNameResolver.setTemporaryServiceLocked(componentName, durationMs);
    }

    /**
     * Resets the temporary service implementation to the default component.
     */
    protected final void resetTemporaryServiceLocked() {
        Slog.i(mTag, "resetting temporary service from " + mTemporaryServiceName);
        mTemporaryServiceName = null;
        if (mTemporaryHandler != null) {
            removeResetTemporaryServiceMessageLocked();
            mTemporaryHandler = null;
        }
        updateLocked(mDisabled);
        mServiceNameResolver.resetTemporaryServiceLocked();
    }

    /**
@@ -401,14 +319,7 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
            pw.print(prefix); pw.print("Service UID: ");
            pw.println(mServiceInfo.applicationInfo.uid);
        }
        if (mTemporaryServiceName != null) {
            pw.print(prefix); pw.print("Temporary service name: "); pw.print(mTemporaryServiceName);
            final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
            pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.println(")");
            pw.print(prefix); pw.print(prefix);
            pw.print("Default service name: "); pw.println(getDefaultComponentName());
        } else {
            pw.print(prefix); pw.print("Service name: "); pw.println(getDefaultComponentName());
        }
        pw.print(prefix); pw.print("Name resolver: "); mServiceNameResolver.dumpShortLocked(pw);
        pw.println();
    }
}
+170 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.infra;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.UserIdInt;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Slog;
import android.util.TimeUtils;

import com.android.internal.annotations.GuardedBy;

import java.io.PrintWriter;

/**
 * Gets the service name using a framework resources, temporarily changing the service if necessary
 * (typically during CTS tests or service development).
 *
 * @hide
 */
public final class FrameworkResourcesServiceNameResolver implements ServiceNameResolver {

    private static final String TAG = FrameworkResourcesServiceNameResolver.class.getSimpleName();

    /** Handler message to {@link #resetTemporaryServiceLocked()} */
    private static final int MSG_RESET_TEMPORARY_SERVICE = 0;

    private final @NonNull Context mContext;
    private final @NonNull @UserIdInt int mUserId;
    private final @NonNull Object mLock;
    private final @StringRes int mResourceId;
    private @Nullable Runnable mOnSetCallback;

    /**
     * Temporary service name set by {@link #setTemporaryServiceLocked(String, int)}.
     *
     * <p>Typically used by Shell command and/or CTS tests.
     */
    @GuardedBy("mLock")
    @Nullable
    private String mTemporaryServiceName;

    /**
     * When the temporary service will expire (and reset back to the default).
     */
    @GuardedBy("mLock")
    private long mTemporaryServiceExpiration;

    /**
     * Handler used to reset the temporary service name.
     */
    @GuardedBy("mLock")
    private Handler mTemporaryHandler;

    public FrameworkResourcesServiceNameResolver(@NonNull Context context, @UserIdInt int userId,
            @NonNull Object lock, @StringRes int resourceId) {
        mLock = lock;
        mContext = context;
        mUserId = userId;
        mResourceId = resourceId;
    }

    @Override
    public void setOnTemporaryServiceNameChangedCallback(@NonNull Runnable callback) {
        this.mOnSetCallback = callback;
    }

    @Override
    public String getDefaultServiceName() {
        final String name = mContext.getString(mResourceId);
        return TextUtils.isEmpty(name) ? null : name;
    }

    @Override
    public String getServiceNameLocked() {
        if (mTemporaryServiceName != null) {
            // Always log it, as it should only be used on CTS or during development
            Slog.w(TAG, "getComponentName(): using temporary name " + mTemporaryServiceName);
            return mTemporaryServiceName;
        } else {
            return getDefaultServiceName();
        }
    }

    @Override
    public boolean isTemporaryLocked() {
        return mTemporaryServiceName != null;
    }

    @Override
    public void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
        mTemporaryServiceName = componentName;

        if (mTemporaryHandler == null) {
            mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
                        synchronized (mLock) {
                            resetTemporaryServiceLocked();
                        }
                    } else {
                        Slog.wtf(TAG, "invalid handler msg: " + msg);
                    }
                }
            };
        } else {
            removeResetTemporaryServiceMessageLocked();
        }
        mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
        mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
        onServiceNameChanged();
    }

    @Override
    public void resetTemporaryServiceLocked() {
        Slog.i(TAG, "resetting temporary service from " + mTemporaryServiceName);
        mTemporaryServiceName = null;
        if (mTemporaryHandler != null) {
            removeResetTemporaryServiceMessageLocked();
            mTemporaryHandler = null;
        }
        onServiceNameChanged();
    }

    // TODO(b/117779333): support proto
    @Override
    public void dumpShortLocked(@NonNull PrintWriter pw) {
        pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId);
        if (mTemporaryServiceName != null) {
            pw.print(", tmpName="); pw.print(mTemporaryServiceName);
            final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
            pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.print(")");
            pw.print(", defaultName="); pw.println(getDefaultServiceName());
        } else {
            pw.print(", serviceName="); pw.println(getDefaultServiceName());
        }
    }

    private void onServiceNameChanged() {
        if (mOnSetCallback != null) {
            mOnSetCallback.run();
        }
    }

    private void removeResetTemporaryServiceMessageLocked() {
        // NOTE: caller should already have checked it
        mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
    }
}
+57 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.infra;

import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
import android.provider.Settings;

import java.io.PrintWriter;

/**
 * Gets the service name using a property from the {@link android.provider.Settings.Secure}
 * provider.
 *
 * @hide
 */
public final class SecureSettingsServiceNameResolver implements ServiceNameResolver {

    private final @NonNull Context mContext;
    private final @NonNull @UserIdInt int mUserId;

    @NonNull
    private final String mProperty;

    public SecureSettingsServiceNameResolver(@NonNull Context context, @UserIdInt int userId,
            @NonNull String property) {
        mContext = context;
        mUserId = userId;
        mProperty = property;
    }

    @Override
    public String getDefaultServiceName() {
        return Settings.Secure.getStringForUser(mContext.getContentResolver(), mProperty, mUserId);
    }

    // TODO(b/117779333): support proto
    @Override
    public void dumpShortLocked(@NonNull PrintWriter pw) {
        pw.print("SecureSettingsServiceNamer: prop="); pw.print(mProperty);
        pw.print(", value="); pw.println(getDefaultServiceName());
    }
}
Loading