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

Commit 8c7c5cc9 authored by Makoto Onuki's avatar Makoto Onuki
Browse files

Persistent connection to SMS app 2/2 (main)

Bug: 109809543
Test: atest CtsAppBindingHostTestCases
Test: atest ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
Change-Id: If927050fba5edea63137e10af5570c3450165237
parent 23961c8a
Loading
Loading
Loading
Loading
+25 −4
Original line number Original line Diff line number Diff line
@@ -24,21 +24,42 @@ import android.os.IBinder;
 * it so that the process is always running, which allows the app to have a persistent connection
 * it so that the process is always running, which allows the app to have a persistent connection
 * to the server.
 * to the server.
 *
 *
 * <p>The service must have {@link android.telephony.TelephonyManager#ACTION_SMS_APP_SERVICE}
 * <p>The service must have an {@link android.telephony.TelephonyManager#ACTION_SMS_APP_SERVICE}
 * action in the intent handler, and be protected with
 * action in the intent handler, and be protected with
 * {@link android.Manifest.permission#BIND_SMS_APP_SERVICE}. However the service does not have to
 * {@link android.Manifest.permission#BIND_SMS_APP_SERVICE}. However the service does not have to
 * be exported.
 * be exported.
 *
 *
 * <p>Apps can use
 * <p>The service must be associated with a non-main process, meaning it must have an
 * {@code android:process} tag in its manifest entry.
 *
 * <p>An app can use
 * {@link android.content.pm.PackageManager#setComponentEnabledSetting(ComponentName, int, int)}
 * {@link android.content.pm.PackageManager#setComponentEnabledSetting(ComponentName, int, int)}
 * to disable/enable the service. Apps should use it to disable the service when it no longer needs
 * to disable or enable the service. An app should use it to disable the service when it no longer
 * to be running.
 * needs to be running.
 *
 *
 * <p>When the owner process crashes, the service will be re-bound automatically after a
 * <p>When the owner process crashes, the service will be re-bound automatically after a
 * back-off.
 * back-off.
 *
 *
 * <p>Note the process may still be killed if the system is under heavy memory pressure, in which
 * <p>Note the process may still be killed if the system is under heavy memory pressure, in which
 * case the process will be re-started later.
 * case the process will be re-started later.
 *
 * <p>Example: First, define a subclass in the application:
 * <pre>
 * public class MySmsAppService extends SmsAppService {
 * }
 * </pre>
 * Then, declare it in its {@code AndroidManifest.xml}:
 * <pre>
 * &lt;service
 *    android:name=".MySmsAppService"
 *    android:exported="false"
 *    android:process=":persistent"
 *    android:permission="android.permission.BIND_SMS_APP_SERVICE"&gt;
 *    &lt;intent-filter&gt;
 *        &lt;action android:name="android.telephony.action.SMS_APP_SERVICE" /&gt;
 *    &lt;/intent-filter&gt;
 * &lt;/service&gt;
 * </pre>
 */
 */
public class SmsAppService extends Service {
public class SmsAppService extends Service {
    private final ISmsAppService mImpl;
    private final ISmsAppService mImpl;
+93 −3
Original line number Original line Diff line number Diff line
@@ -59,6 +59,8 @@ import java.io.PrintWriter;
 * know what to do when the service component has gone missing, for example.  If the user of this
 * know what to do when the service component has gone missing, for example.  If the user of this
 * class wants to restore the connection, then it should call {@link #unbind()} and {@link #bind}
 * class wants to restore the connection, then it should call {@link #unbind()} and {@link #bind}
 * explicitly.
 * explicitly.
 *
 * atest ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
 */
 */
public abstract class PersistentConnection<T> {
public abstract class PersistentConnection<T> {
    private final Object mLock = new Object();
    private final Object mLock = new Object();
@@ -76,6 +78,7 @@ public abstract class PersistentConnection<T> {
    private final long mRebindBackoffMs;
    private final long mRebindBackoffMs;
    private final double mRebindBackoffIncrease;
    private final double mRebindBackoffIncrease;
    private final long mRebindMaxBackoffMs;
    private final long mRebindMaxBackoffMs;
    private final long mResetBackoffDelay;


    private long mReconnectTime;
    private long mReconnectTime;


@@ -109,6 +112,9 @@ public abstract class PersistentConnection<T> {
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private int mNumBindingDied;
    private int mNumBindingDied;


    @GuardedBy("mLock")
    private long mLastConnectedTime;

    private final ServiceConnection mServiceConnection = new ServiceConnection() {
    private final ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
        public void onServiceConnected(ComponentName name, IBinder service) {
@@ -127,7 +133,10 @@ public abstract class PersistentConnection<T> {
                mNumConnected++;
                mNumConnected++;


                mIsConnected = true;
                mIsConnected = true;
                mLastConnectedTime = injectUptimeMillis();
                mService = asInterface(service);
                mService = asInterface(service);

                scheduleStableCheckLocked();
            }
            }
        }
        }


@@ -140,6 +149,9 @@ public abstract class PersistentConnection<T> {
                mNumDisconnected++;
                mNumDisconnected++;


                cleanUpConnectionLocked();
                cleanUpConnectionLocked();

                // Note we won't increase the rebind timeout here, because we don't explicitly
                // rebind in this case.
            }
            }
        }
        }


@@ -168,7 +180,8 @@ public abstract class PersistentConnection<T> {


    public PersistentConnection(@NonNull String tag, @NonNull Context context,
    public PersistentConnection(@NonNull String tag, @NonNull Context context,
            @NonNull Handler handler, int userId, @NonNull ComponentName componentName,
            @NonNull Handler handler, int userId, @NonNull ComponentName componentName,
            long rebindBackoffSeconds, double rebindBackoffIncrease, long rebindMaxBackoffSeconds) {
            long rebindBackoffSeconds, double rebindBackoffIncrease, long rebindMaxBackoffSeconds,
            long resetBackoffDelay) {
        mTag = tag;
        mTag = tag;
        mContext = context;
        mContext = context;
        mHandler = handler;
        mHandler = handler;
@@ -178,6 +191,7 @@ public abstract class PersistentConnection<T> {
        mRebindBackoffMs = rebindBackoffSeconds * 1000;
        mRebindBackoffMs = rebindBackoffSeconds * 1000;
        mRebindBackoffIncrease = rebindBackoffIncrease;
        mRebindBackoffIncrease = rebindBackoffIncrease;
        mRebindMaxBackoffMs = rebindMaxBackoffSeconds * 1000;
        mRebindMaxBackoffMs = rebindMaxBackoffSeconds * 1000;
        mResetBackoffDelay = resetBackoffDelay * 1000;


        mNextBackoffMs = mRebindBackoffMs;
        mNextBackoffMs = mRebindBackoffMs;
    }
    }
@@ -242,6 +256,42 @@ public abstract class PersistentConnection<T> {
        }
        }
    }
    }


    /** Return the next back-off time */
    public long getNextBackoffMs() {
        synchronized (mLock) {
            return mNextBackoffMs;
        }
    }

    /** Return the number of times the connected callback called. */
    public int getNumConnected() {
        synchronized (mLock) {
            return mNumConnected;
        }
    }

    /** Return the number of times the disconnected callback called. */
    public int getNumDisconnected() {
        synchronized (mLock) {
            return mNumDisconnected;
        }
    }

    /** Return the number of times the binding died callback called. */
    public int getNumBindingDied() {
        synchronized (mLock) {
            return mNumBindingDied;
        }
    }

    @GuardedBy("mLock")
    private void resetBackoffLocked() {
        if (mNextBackoffMs != mRebindBackoffMs) {
            mNextBackoffMs = mRebindBackoffMs;
            Log.i(mTag, "Backoff reset to " + mNextBackoffMs);
        }
    }

    @GuardedBy("mLock")
    @GuardedBy("mLock")
    public final void bindInnerLocked(boolean resetBackoff) {
    public final void bindInnerLocked(boolean resetBackoff) {
        unscheduleRebindLocked();
        unscheduleRebindLocked();
@@ -251,9 +301,10 @@ public abstract class PersistentConnection<T> {
        }
        }
        mBound = true;
        mBound = true;


        unscheduleStableCheckLocked();

        if (resetBackoff) {
        if (resetBackoff) {
            // Note this is the only place we reset the backoff time.
            resetBackoffLocked();
            mNextBackoffMs = mRebindBackoffMs;
        }
        }


        final Intent service = new Intent().setComponent(mComponentName);
        final Intent service = new Intent().setComponent(mComponentName);
@@ -287,6 +338,8 @@ public abstract class PersistentConnection<T> {
    private void cleanUpConnectionLocked() {
    private void cleanUpConnectionLocked() {
        mIsConnected = false;
        mIsConnected = false;
        mService = null;
        mService = null;

        unscheduleStableCheckLocked();
    }
    }


    /**
    /**
@@ -297,6 +350,7 @@ public abstract class PersistentConnection<T> {
            mShouldBeBound = false;
            mShouldBeBound = false;


            unbindLocked();
            unbindLocked();
            unscheduleStableCheckLocked();
        }
        }
    }
    }


@@ -338,6 +392,33 @@ public abstract class PersistentConnection<T> {
        }
        }
    }
    }


    private final Runnable mStableCheck = this::stableConnectionCheck;

    private void stableConnectionCheck() {
        synchronized (mLock) {
            final long now = injectUptimeMillis();
            final long timeRemaining = (mLastConnectedTime + mResetBackoffDelay) - now;
            if (DEBUG) {
                Log.d(mTag, "stableConnectionCheck: bound=" + mBound + " connected=" + mIsConnected
                        + " remaining=" + timeRemaining);
            }
            if (mBound && mIsConnected && timeRemaining <= 0) {
                resetBackoffLocked();
            }
        }
    }

    @GuardedBy("mLock")
    private void unscheduleStableCheckLocked() {
        injectRemoveCallbacks(mStableCheck);
    }

    @GuardedBy("mLock")
    private void scheduleStableCheckLocked() {
        unscheduleStableCheckLocked();
        injectPostAtTime(mStableCheck, injectUptimeMillis() + mResetBackoffDelay);
    }

    /** Must be implemented by a subclass to convert an {@link IBinder} to a stub. */
    /** Must be implemented by a subclass to convert an {@link IBinder} to a stub. */
    protected abstract T asInterface(IBinder binder);
    protected abstract T asInterface(IBinder binder);


@@ -367,6 +448,10 @@ public abstract class PersistentConnection<T> {
            pw.print(mNumDisconnected);
            pw.print(mNumDisconnected);
            pw.print("  Died: ");
            pw.print("  Died: ");
            pw.print(mNumBindingDied);
            pw.print(mNumBindingDied);
            if (mIsConnected) {
                pw.print("  Duration: ");
                TimeUtils.formatDuration((injectUptimeMillis() - mLastConnectedTime), pw);
            }
            pw.println();
            pw.println();
        }
        }
    }
    }
@@ -406,6 +491,11 @@ public abstract class PersistentConnection<T> {
        return mBindForBackoffRunnable;
        return mBindForBackoffRunnable;
    }
    }


    @VisibleForTesting
    Runnable getStableCheckRunnableForTest() {
        return mStableCheck;
    }

    @VisibleForTesting
    @VisibleForTesting
    boolean shouldBeBoundForTest() {
    boolean shouldBeBoundForTest() {
        return mShouldBeBound;
        return mShouldBeBound;
+37 −3
Original line number Original line Diff line number Diff line
@@ -15,6 +15,7 @@
 */
 */
package com.android.server.appbinding;
package com.android.server.appbinding;


import android.content.Context;
import android.util.KeyValueListParser;
import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.Slog;


@@ -24,7 +25,7 @@ import java.util.concurrent.TimeUnit;
/**
/**
 * Constants that are configurable via the global settings for {@link AppBindingService}.
 * Constants that are configurable via the global settings for {@link AppBindingService}.
 */
 */
class AppBindingConstants {
public class AppBindingConstants {
    private static final String TAG = AppBindingService.TAG;
    private static final String TAG = AppBindingService.TAG;


    private static final String SERVICE_RECONNECT_BACKOFF_SEC_KEY =
    private static final String SERVICE_RECONNECT_BACKOFF_SEC_KEY =
@@ -36,6 +37,12 @@ class AppBindingConstants {
    private static final String SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY =
    private static final String SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY =
            "service_reconnect_max_backoff_sec";
            "service_reconnect_max_backoff_sec";


    private static final String SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY =
            "service_stable_connection_threshold_sec";

    private static final String SMS_APP_BIND_FLAGS_KEY =
            "sms_app_bind_flags";

    public final String sourceSettings;
    public final String sourceSettings;


    /**
    /**
@@ -54,6 +61,16 @@ class AppBindingConstants {
     */
     */
    public final long SERVICE_RECONNECT_MAX_BACKOFF_SEC;
    public final long SERVICE_RECONNECT_MAX_BACKOFF_SEC;


    /**
     * If a connection lasts more than this duration, we reset the re-connect back-off time.
     */
    public final long SERVICE_STABLE_CONNECTION_THRESHOLD_SEC;

    /**
     * Extra binding flags for SMS service.
     */
    public final int SMS_APP_BIND_FLAGS;

    private AppBindingConstants(String settings) {
    private AppBindingConstants(String settings) {
        sourceSettings = settings;
        sourceSettings = settings;


@@ -67,13 +84,20 @@ class AppBindingConstants {
        }
        }


        long serviceReconnectBackoffSec = parser.getLong(
        long serviceReconnectBackoffSec = parser.getLong(
                SERVICE_RECONNECT_BACKOFF_SEC_KEY, TimeUnit.HOURS.toSeconds(1));
                SERVICE_RECONNECT_BACKOFF_SEC_KEY, 10);


        double serviceReconnectBackoffIncrease = parser.getFloat(
        double serviceReconnectBackoffIncrease = parser.getFloat(
                SERVICE_RECONNECT_BACKOFF_INCREASE_KEY, 2f);
                SERVICE_RECONNECT_BACKOFF_INCREASE_KEY, 2f);


        long serviceReconnectMaxBackoffSec = parser.getLong(
        long serviceReconnectMaxBackoffSec = parser.getLong(
                SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.DAYS.toSeconds(1));
                SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.HOURS.toSeconds(1));

        int smsAppBindFlags = parser.getInt(
                SMS_APP_BIND_FLAGS_KEY,
                Context.BIND_NOT_VISIBLE | Context.BIND_FOREGROUND_SERVICE);

        long serviceStableConnectionThresholdSec = parser.getLong(
                SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY, TimeUnit.MINUTES.toSeconds(2));


        // Set minimum: 5 seconds.
        // Set minimum: 5 seconds.
        serviceReconnectBackoffSec = Math.max(5, serviceReconnectBackoffSec);
        serviceReconnectBackoffSec = Math.max(5, serviceReconnectBackoffSec);
@@ -89,6 +113,8 @@ class AppBindingConstants {
        SERVICE_RECONNECT_BACKOFF_SEC = serviceReconnectBackoffSec;
        SERVICE_RECONNECT_BACKOFF_SEC = serviceReconnectBackoffSec;
        SERVICE_RECONNECT_BACKOFF_INCREASE = serviceReconnectBackoffIncrease;
        SERVICE_RECONNECT_BACKOFF_INCREASE = serviceReconnectBackoffIncrease;
        SERVICE_RECONNECT_MAX_BACKOFF_SEC = serviceReconnectMaxBackoffSec;
        SERVICE_RECONNECT_MAX_BACKOFF_SEC = serviceReconnectMaxBackoffSec;
        SERVICE_STABLE_CONNECTION_THRESHOLD_SEC = serviceStableConnectionThresholdSec;
        SMS_APP_BIND_FLAGS = smsAppBindFlags;
    }
    }


    /**
    /**
@@ -116,5 +142,13 @@ class AppBindingConstants {
        pw.print(prefix);
        pw.print(prefix);
        pw.print("  SERVICE_RECONNECT_MAX_BACKOFF_SEC: ");
        pw.print("  SERVICE_RECONNECT_MAX_BACKOFF_SEC: ");
        pw.println(SERVICE_RECONNECT_MAX_BACKOFF_SEC);
        pw.println(SERVICE_RECONNECT_MAX_BACKOFF_SEC);

        pw.print(prefix);
        pw.print("  SERVICE_STABLE_CONNECTION_THRESHOLD_SEC: ");
        pw.println(SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);

        pw.print(prefix);
        pw.print("  SMS_APP_BIND_FLAGS: 0x");
        pw.println(Integer.toHexString(SMS_APP_BIND_FLAGS));
    }
    }
}
}
+35 −10
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.app.AppGlobals;
import android.app.AppGlobals;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
@@ -58,10 +59,11 @@ import java.util.function.Consumer;
 *
 *
 * <p>As of android Q, we only use it for the default SMS app.
 * <p>As of android Q, we only use it for the default SMS app.
 *
 *
 * TODO Unit tests
 * Relevant tests:
 * TODO How do we handle force stop??
 * atest CtsAppBindingHostTestCases
 * TODO Change OOM adjustment to 200 or so
 *
 * TODO Only allow it when the service is associated with a secondary process.
 * TODO Maybe handle force-stop differently. Right now we just get "binder died" and re-bind
 * after a timeout. b/116813347
 */
 */
public class AppBindingService extends Binder {
public class AppBindingService extends Binder {
    public static final String TAG = "AppBindingService";
    public static final String TAG = "AppBindingService";
@@ -91,17 +93,25 @@ public class AppBindingService extends Binder {
        public IPackageManager getIPackageManager() {
        public IPackageManager getIPackageManager() {
            return AppGlobals.getPackageManager();
            return AppGlobals.getPackageManager();
        }
        }

        public String getGlobalSettingString(ContentResolver resolver, String key) {
            return Settings.Global.getString(resolver, key);
        }
    }
    }


    /**
    /**
     * {@link SystemService} for this service.
     * {@link SystemService} for this service.
     */
     */
    public static final class Lifecycle extends SystemService {
    public static class Lifecycle extends SystemService {
        final AppBindingService mService;
        final AppBindingService mService;


        public Lifecycle(Context context) {
        public Lifecycle(Context context) {
            this(context, new Injector());
        }

        Lifecycle(Context context, Injector injector) {
            super(context);
            super(context);
            mService = new AppBindingService(new Injector(), context);
            mService = new AppBindingService(injector, context);
        }
        }


        @Override
        @Override
@@ -171,7 +181,6 @@ public class AppBindingService extends Binder {
        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
        packageFilter.addDataScheme("package");
        packageFilter.addDataScheme("package");


        packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
        packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -197,7 +206,7 @@ public class AppBindingService extends Binder {
    };
    };


    private void refreshConstants() {
    private void refreshConstants() {
        final String newSetting = Settings.Global.getString(
        final String newSetting = mInjector.getGlobalSettingString(
                mContext.getContentResolver(), Global.APP_BINDING_CONSTANTS);
                mContext.getContentResolver(), Global.APP_BINDING_CONSTANTS);


        synchronized (mLock) {
        synchronized (mLock) {
@@ -215,6 +224,9 @@ public class AppBindingService extends Binder {
    final BroadcastReceiver mPackageUserMonitor = new BroadcastReceiver() {
    final BroadcastReceiver mPackageUserMonitor = new BroadcastReceiver() {
        @Override
        @Override
        public void onReceive(Context context, Intent intent) {
        public void onReceive(Context context, Intent intent) {
            if (DEBUG) {
                Slog.d(TAG, "Broadcast received: " + intent);
            }
            final int userId  = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
            final int userId  = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
            if (userId == UserHandle.USER_NULL) {
            if (userId == UserHandle.USER_NULL) {
                Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
                Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
@@ -454,14 +466,15 @@ public class AppBindingService extends Binder {
            super(TAG, context, handler, userId, componentName,
            super(TAG, context, handler, userId, componentName,
                    constants.SERVICE_RECONNECT_BACKOFF_SEC,
                    constants.SERVICE_RECONNECT_BACKOFF_SEC,
                    constants.SERVICE_RECONNECT_BACKOFF_INCREASE,
                    constants.SERVICE_RECONNECT_BACKOFF_INCREASE,
                    constants.SERVICE_RECONNECT_MAX_BACKOFF_SEC);
                    constants.SERVICE_RECONNECT_MAX_BACKOFF_SEC,
                    constants.SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
            mFinder = finder;
            mFinder = finder;
            mConstants = constants;
            mConstants = constants;
        }
        }


        @Override
        @Override
        protected int getBindFlags() {
        protected int getBindFlags() {
            return Context.BIND_FOREGROUND_SERVICE;
            return mFinder.getBindFlags(mConstants);
        }
        }


        @Override
        @Override
@@ -535,9 +548,21 @@ public class AppBindingService extends Binder {
                pw.print(conn.isBound() ? "bound" : "not-bound");
                pw.print(conn.isBound() ? "bound" : "not-bound");
                pw.print(",");
                pw.print(",");
                pw.print(conn.isConnected() ? "connected" : "not-connected");
                pw.print(conn.isConnected() ? "connected" : "not-connected");
                pw.print(",#con=");
                pw.print(conn.getNumConnected());
                pw.print(",#dis=");
                pw.print(conn.getNumDisconnected());
                pw.print(",#died=");
                pw.print(conn.getNumBindingDied());
                pw.print(",backoff=");
                pw.print(conn.getNextBackoffMs());
                pw.println();
                pw.println();
            }
            }
            forAllAppsLocked((app) -> app.dumpSimple(pw));
            forAllAppsLocked((app) -> app.dumpSimple(pw));
        }
        }
    }
    }

    AppBindingConstants getConstantsForTest() {
        return mConstants;
    }
}
}
+35 −11
Original line number Original line Diff line number Diff line
@@ -24,10 +24,12 @@ import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.IInterface;
import android.util.Log;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseArray;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.server.appbinding.AppBindingConstants;
import com.android.server.appbinding.AppBindingService;
import com.android.server.appbinding.AppBindingService;
import com.android.server.appbinding.AppBindingUtils;
import com.android.server.appbinding.AppBindingUtils;


@@ -51,13 +53,13 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
    private final Object mLock = new Object();
    private final Object mLock = new Object();


    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private final SparseArray<String> mTargetPackages = new SparseArray(1);
    private final SparseArray<String> mTargetPackages = new SparseArray(4);


    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private final SparseArray<ServiceInfo> mTargetServices = new SparseArray(1);
    private final SparseArray<ServiceInfo> mTargetServices = new SparseArray(4);


    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private final SparseArray<String> mLastMessages = new SparseArray(1);
    private final SparseArray<String> mLastMessages = new SparseArray(4);


    public AppServiceFinder(Context context,
    public AppServiceFinder(Context context,
            BiConsumer<AppServiceFinder, Integer> listener,
            BiConsumer<AppServiceFinder, Integer> listener,
@@ -80,6 +82,7 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
        synchronized (mLock) {
        synchronized (mLock) {
            mTargetPackages.delete(userId);
            mTargetPackages.delete(userId);
            mTargetServices.delete(userId);
            mTargetServices.delete(userId);
            mLastMessages.delete(userId);
        }
        }
    }
    }


@@ -91,6 +94,7 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
        synchronized (mLock) {
        synchronized (mLock) {
            mTargetPackages.put(userId, null);
            mTargetPackages.put(userId, null);
            mTargetServices.put(userId, null);
            mTargetServices.put(userId, null);
            mLastMessages.put(userId, null);


            final String targetPackage = getTargetPackage(userId);
            final String targetPackage = getTargetPackage(userId);
            if (DEBUG) {
            if (DEBUG) {
@@ -118,11 +122,18 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
                final String message = errorMessage.toString();
                final String message = errorMessage.toString();
                mLastMessages.put(userId, message);
                mLastMessages.put(userId, message);
                if (DEBUG) {
                if (DEBUG) {
                    // This log is optional because findService() already did Log.e().
                    Slog.w(TAG, getAppDescription() + " package " + targetPackage + " u" + userId
                    Slog.w(TAG, getAppDescription() + " package " + targetPackage + " u" + userId
                            + " " + message);
                            + " " + message);
                }
                }
                return null;
                return null;
            }
            }
            final String error = validateService(service);
            if (error != null) {
                mLastMessages.put(userId, error);
                Log.e(TAG, error);
                return null;
            }


            final String message = "Valid service found";
            final String message = "Valid service found";
            mLastMessages.put(userId, message);
            mLastMessages.put(userId, message);
@@ -156,6 +167,17 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
    @NonNull
    @NonNull
    protected abstract String getServicePermission();
    protected abstract String getServicePermission();


    /**
     * Subclass can implement it to decide whether to accept a service (by returning null) or not
     * (by returning an error message.)
     */
    protected String validateService(ServiceInfo service) {
        return null;
    }

    /** Return the bind flags for this service. */
    public abstract int getBindFlags(AppBindingConstants constants);

    /** Dumpsys support. */
    /** Dumpsys support. */
    public void dump(String prefix, PrintWriter pw) {
    public void dump(String prefix, PrintWriter pw) {
        pw.print(prefix);
        pw.print(prefix);
@@ -165,24 +187,25 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten


        synchronized (mLock) {
        synchronized (mLock) {
            for (int i = 0; i < mTargetPackages.size(); i++) {
            for (int i = 0; i < mTargetPackages.size(); i++) {
                final int userId = mTargetPackages.keyAt(i);
                pw.print(prefix);
                pw.print(prefix);
                pw.print("  User: ");
                pw.print("  User: ");
                pw.print(mTargetPackages.keyAt(i));
                pw.print(userId);
                pw.println();
                pw.println();


                pw.print(prefix);
                pw.print(prefix);
                pw.print("    Package: ");
                pw.print("    Package: ");
                pw.print(mTargetPackages.valueAt(i));
                pw.print(mTargetPackages.get(userId));
                pw.println();
                pw.println();


                pw.print(prefix);
                pw.print(prefix);
                pw.print("    Service: ");
                pw.print("    Service: ");
                pw.print(mTargetServices.valueAt(i));
                pw.print(mTargetServices.get(userId));
                pw.println();
                pw.println();


                pw.print(prefix);
                pw.print(prefix);
                pw.print("    Message: ");
                pw.print("    Message: ");
                pw.print(mLastMessages.valueAt(i));
                pw.print(mLastMessages.get(userId));
                pw.println();
                pw.println();
            }
            }
        }
        }
@@ -192,16 +215,17 @@ public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType exten
    public void dumpSimple(PrintWriter pw) {
    public void dumpSimple(PrintWriter pw) {
        synchronized (mLock) {
        synchronized (mLock) {
            for (int i = 0; i < mTargetPackages.size(); i++) {
            for (int i = 0; i < mTargetPackages.size(); i++) {
                final int userId = mTargetPackages.keyAt(i);
                pw.print("finder,");
                pw.print("finder,");
                pw.print(getAppDescription());
                pw.print(getAppDescription());
                pw.print(",");
                pw.print(",");
                pw.print(mTargetPackages.keyAt(i)); // User-id
                pw.print(userId);
                pw.print(",");
                pw.print(",");
                pw.print(mTargetPackages.valueAt(i));
                pw.print(mTargetPackages.get(userId));
                pw.print(",");
                pw.print(",");
                pw.print(mTargetServices.valueAt(i));
                pw.print(mTargetServices.get(userId));
                pw.print(",");
                pw.print(",");
                pw.print(mLastMessages.valueAt(i));
                pw.print(mLastMessages.get(userId));
                pw.println();
                pw.println();
            }
            }
        }
        }
Loading