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

Commit fb518e00 authored by sandeepbandaru's avatar sandeepbandaru
Browse files

Adding shell command to use in CTS tests

This CL adds a command to set temporary services for using in a CTS
Test. Also introduces a LocalService for use by other system-server
components.

Bug: 316590022
Bug: 323147058
Test: cts
Change-Id: I3f825feffb010cb4c073e90adcd86efc0e9340db
parent 69b85244
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -67,4 +67,6 @@
      void processRequestStreaming(in Feature feature,
                    in Bundle requestBundle, int requestType, in  ICancellationSignal cancellationSignal, in  IProcessingSignal signal,
                    in IStreamingResponseCallback streamingCallback) = 8;

      String getRemoteServicePackageName() = 9;
 }
+6 −7
Original line number Diff line number Diff line
@@ -120,14 +120,13 @@ public final class OnDeviceIntelligenceManager {
    @Nullable
    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
    public String getRemoteServicePackageName() {
        String serviceConfigValue = mContext.getResources().getString(
                R.string.config_defaultOnDeviceSandboxedInferenceService);
        ComponentName componentName = ComponentName.unflattenFromString(serviceConfigValue);
        if (componentName != null) {
            return componentName.getPackageName();
        String result;
        try{
           result = mService.getRemoteServicePackageName();
        } catch (RemoteException e){
            throw e.rethrowFromSystemServer();
        }

        return null;
        return result;
    }

    /**
+21 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.ondeviceintelligence;

public interface OnDeviceIntelligenceManagerInternal {
    String getRemoteServicePackageName();
}
 No newline at end of file
+304 −176
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.server.ondeviceintelligence;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.AppGlobals;
import android.app.ondeviceintelligence.DownloadCallback;
import android.app.ondeviceintelligence.Feature;
@@ -36,12 +38,19 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
@@ -59,8 +68,10 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;

import java.io.FileDescriptor;
import java.util.Objects;
import java.util.Set;

@@ -81,6 +92,9 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
    private static final String TAG = OnDeviceIntelligenceManagerService.class.getSimpleName();
    private static final String KEY_SERVICE_ENABLED = "service_enabled";

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

    /** Default value in absence of {@link DeviceConfig} override. */
    private static final boolean DEFAULT_SERVICE_ENABLED = true;
    private static final String NAMESPACE_ON_DEVICE_INTELLIGENCE = "ondeviceintelligence";
@@ -93,19 +107,30 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
    private RemoteOnDeviceIntelligenceService mRemoteOnDeviceIntelligenceService;
    volatile boolean mIsServiceEnabled;

    @GuardedBy("mLock")
    private String[] mTemporaryServiceNames;

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

    public OnDeviceIntelligenceManagerService(Context context) {
        super(context);
        mContext = context;
        mTemporaryServiceNames = new String[0];
    }

    @Override
    public void onStart() {
        publishBinderService(
                Context.ON_DEVICE_INTELLIGENCE_SERVICE, new OnDeviceIntelligenceManagerInternal(),
                Context.ON_DEVICE_INTELLIGENCE_SERVICE, getOnDeviceIntelligenceManagerService(),
                /* allowIsolated = */true);
        LocalServices.addService(OnDeviceIntelligenceManagerInternal.class,
                OnDeviceIntelligenceManagerService.this::getRemoteConfiguredPackageName);
    }


    @Override
    public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
@@ -130,8 +155,13 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
                KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
    }

    private final class OnDeviceIntelligenceManagerInternal extends
            IOnDeviceIntelligenceManager.Stub {
    private IBinder getOnDeviceIntelligenceManagerService() {
        return new IOnDeviceIntelligenceManager.Stub() {
            @Override
            public String getRemoteServicePackageName() {
                return OnDeviceIntelligenceManagerService.this.getRemoteConfiguredPackageName();
            }

            @Override
            public void getVersion(RemoteCallback remoteCallback) throws RemoteException {
                Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getVersion");
@@ -144,12 +174,13 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
                    return;
                }
                ensureRemoteIntelligenceServiceInitialized();
            mRemoteOnDeviceIntelligenceService.post(
                mRemoteOnDeviceIntelligenceService.run(
                        service -> service.getVersion(remoteCallback));
            }

            @Override
        public void getFeature(int id, IFeatureCallback featureCallback) throws RemoteException {
            public void getFeature(int id, IFeatureCallback featureCallback)
                    throws RemoteException {
                Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
                Objects.requireNonNull(featureCallback);
                mContext.enforceCallingOrSelfPermission(
@@ -159,11 +190,11 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
                    featureCallback.onFailure(
                            OnDeviceIntelligenceException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
                            "OnDeviceIntelligenceManagerService is unavailable",
                        new PersistableBundle());
                            PersistableBundle.EMPTY);
                    return;
                }
                ensureRemoteIntelligenceServiceInitialized();
            mRemoteOnDeviceIntelligenceService.post(
                mRemoteOnDeviceIntelligenceService.run(
                        service -> service.getFeature(Binder.getCallingUid(), id, featureCallback));
            }

@@ -179,12 +210,13 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
                    listFeaturesCallback.onFailure(
                            OnDeviceIntelligenceException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
                            "OnDeviceIntelligenceManagerService is unavailable",
                        new PersistableBundle());
                            PersistableBundle.EMPTY);
                    return;
                }
                ensureRemoteIntelligenceServiceInitialized();
            mRemoteOnDeviceIntelligenceService.post(
                    service -> service.listFeatures(Binder.getCallingUid(), listFeaturesCallback));
                mRemoteOnDeviceIntelligenceService.run(
                        service -> service.listFeatures(Binder.getCallingUid(),
                                listFeaturesCallback));
            }

            @Override
@@ -201,17 +233,18 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
                    featureDetailsCallback.onFailure(
                            OnDeviceIntelligenceException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
                            "OnDeviceIntelligenceManagerService is unavailable",
                        new PersistableBundle());
                            PersistableBundle.EMPTY);
                    return;
                }
                ensureRemoteIntelligenceServiceInitialized();
            mRemoteOnDeviceIntelligenceService.post(
                mRemoteOnDeviceIntelligenceService.run(
                        service -> service.getFeatureDetails(Binder.getCallingUid(), feature,
                                featureDetailsCallback));
            }

            @Override
        public void requestFeatureDownload(Feature feature, ICancellationSignal cancellationSignal,
            public void requestFeatureDownload(Feature feature,
                    ICancellationSignal cancellationSignal,
                    IDownloadCallback downloadCallback) throws RemoteException {
                Slog.i(TAG, "OnDeviceIntelligenceManagerInternal requestFeatureDownload");
                Objects.requireNonNull(feature);
@@ -223,10 +256,10 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
                    downloadCallback.onDownloadFailed(
                            DownloadCallback.DOWNLOAD_FAILURE_STATUS_UNAVAILABLE,
                            "OnDeviceIntelligenceManagerService is unavailable",
                        new PersistableBundle());
                            PersistableBundle.EMPTY);
                }
                ensureRemoteIntelligenceServiceInitialized();
            mRemoteOnDeviceIntelligenceService.post(
                mRemoteOnDeviceIntelligenceService.run(
                        service -> service.requestFeatureDownload(Binder.getCallingUid(), feature,
                                cancellationSignal,
                                downloadCallback));
@@ -249,11 +282,12 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
                    tokenInfoCallback.onFailure(
                            OnDeviceIntelligenceException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
                            "OnDeviceIntelligenceManagerService is unavailable",
                        new PersistableBundle());
                            PersistableBundle.EMPTY);
                }
                ensureRemoteInferenceServiceInitialized();
            mRemoteInferenceService.post(
                    service -> service.requestTokenInfo(Binder.getCallingUid(), feature, request,
                mRemoteInferenceService.run(
                        service -> service.requestTokenInfo(Binder.getCallingUid(), feature,
                                request,
                                cancellationSignal,
                                tokenInfoCallback));
            }
@@ -276,10 +310,10 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
                    responseCallback.onFailure(
                            OnDeviceIntelligenceException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
                            "OnDeviceIntelligenceManagerService is unavailable",
                        new PersistableBundle());
                            PersistableBundle.EMPTY);
                }
                ensureRemoteInferenceServiceInitialized();
            mRemoteInferenceService.post(
                mRemoteInferenceService.run(
                        service -> service.processRequest(Binder.getCallingUid(), feature, request,
                                requestType,
                                cancellationSignal, processingSignal,
@@ -303,22 +337,29 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
                    streamingCallback.onFailure(
                            OnDeviceIntelligenceException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
                            "OnDeviceIntelligenceManagerService is unavailable",
                        new PersistableBundle());
                            PersistableBundle.EMPTY);
                }
                ensureRemoteInferenceServiceInitialized();
            mRemoteInferenceService.post(
                mRemoteInferenceService.run(
                        service -> service.processRequestStreaming(Binder.getCallingUid(), feature,
                                request, requestType,
                                cancellationSignal, processingSignal,
                                streamingCallback));
            }

            @Override
            public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                    String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
                new OnDeviceIntelligenceShellCommand(OnDeviceIntelligenceManagerService.this).exec(
                        this, in, out, err, args, callback, resultReceiver);
            }
        };
    }

    private void ensureRemoteIntelligenceServiceInitialized() throws RemoteException {
        synchronized (mLock) {
            if (mRemoteOnDeviceIntelligenceService == null) {
                String serviceName = mContext.getResources().getString(
                        R.string.config_defaultOnDeviceIntelligenceService);
                String serviceName = getServiceNames()[0];
                validateService(serviceName, false);
                mRemoteOnDeviceIntelligenceService = new RemoteOnDeviceIntelligenceService(mContext,
                        ComponentName.unflattenFromString(serviceName),
@@ -349,7 +390,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
                    IProcessingUpdateStatusCallback callback) {
                try {
                    ensureRemoteInferenceServiceInitialized();
                    mRemoteInferenceService.post(
                    mRemoteInferenceService.run(
                            service -> service.updateProcessingState(
                                    processingState, callback));
                } catch (RemoteException unused) {
@@ -368,8 +409,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
    private void ensureRemoteInferenceServiceInitialized() throws RemoteException {
        synchronized (mLock) {
            if (mRemoteInferenceService == null) {
                String serviceName = mContext.getResources().getString(
                        R.string.config_defaultOnDeviceSandboxedInferenceService);
                String serviceName = getServiceNames()[1];
                validateService(serviceName, true);
                mRemoteInferenceService = new RemoteOnDeviceSandboxedInferenceService(mContext,
                        ComponentName.unflattenFromString(serviceName),
@@ -381,7 +421,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
                                    @NonNull IOnDeviceSandboxedInferenceService service) {
                                try {
                                    ensureRemoteIntelligenceServiceInitialized();
                                    mRemoteOnDeviceIntelligenceService.post(
                                    mRemoteOnDeviceIntelligenceService.run(
                                            intelligenceService -> intelligenceService.notifyInferenceServiceConnected());
                                    service.registerRemoteStorageService(
                                            getIRemoteStorageService());
@@ -401,7 +441,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
            public void getReadOnlyFileDescriptor(
                    String filePath,
                    AndroidFuture<ParcelFileDescriptor> future) {
                mRemoteOnDeviceIntelligenceService.post(
                mRemoteOnDeviceIntelligenceService.run(
                        service -> service.getReadOnlyFileDescriptor(
                                filePath, future));
            }
@@ -410,7 +450,7 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
            public void getReadOnlyFeatureFileDescriptorMap(
                    Feature feature,
                    RemoteCallback remoteCallback) {
                mRemoteOnDeviceIntelligenceService.post(
                mRemoteOnDeviceIntelligenceService.run(
                        service -> service.getReadOnlyFeatureFileDescriptorMap(
                                feature, remoteCallback));
            }
@@ -466,4 +506,92 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
        return (serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
                && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
    }

    @Nullable
    public String getRemoteConfiguredPackageName() {
        try {
            String[] serviceNames = getServiceNames();
            ComponentName componentName = ComponentName.unflattenFromString(serviceNames[1]);
            if (componentName != null) {
                return componentName.getPackageName();
            }
        } catch (Resources.NotFoundException e) {
            Slog.e(TAG, "Could not find resource", e);
        }

        return null;
    }


    protected String[] getServiceNames() throws Resources.NotFoundException {
        // TODO 329240495 : Consider a small class with explicit field names for the two services
        synchronized (mLock) {
            if (mTemporaryServiceNames != null && mTemporaryServiceNames.length == 2) {
                return mTemporaryServiceNames;
            }
        }
        return new String[]{mContext.getResources().getString(
                R.string.config_defaultOnDeviceIntelligenceService),
                mContext.getResources().getString(
                        R.string.config_defaultOnDeviceSandboxedInferenceService)};
    }

    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
    public void setTemporaryServices(@NonNull String[] componentNames, int durationMs) {
        Objects.requireNonNull(componentNames);
        enforceShellOnly(Binder.getCallingUid(), "setTemporaryServices");
        mContext.enforceCallingPermission(
                Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
        synchronized (mLock) {
            mTemporaryServiceNames = componentNames;

            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) {
                                resetTemporaryServices();
                            }
                        } else {
                            Slog.wtf(TAG, "invalid handler msg: " + msg);
                        }
                    }
                };
            } else {
                mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
            }

            if (durationMs != -1) {
                mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
            }
        }
    }

    public void resetTemporaryServices() {
        synchronized (mLock) {
            if (mTemporaryHandler != null) {
                mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
                mTemporaryHandler = null;
            }

            mRemoteInferenceService = null;
            mRemoteOnDeviceIntelligenceService = null;
            mTemporaryServiceNames = new String[0];
        }
    }

    /**
     * Throws if the caller is not of a shell (or root) UID.
     *
     * @param callingUid pass Binder.callingUid().
     */
    public static void enforceShellOnly(int callingUid, String message) {
        if (callingUid == android.os.Process.SHELL_UID
                || callingUid == android.os.Process.ROOT_UID) {
            return; // okay
        }

        throw new SecurityException(message + ": Only shell user can call it");
    }
}
+96 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.ondeviceintelligence;

import android.annotation.NonNull;
import android.os.ShellCommand;

import java.io.PrintWriter;
import java.util.Objects;

final class OnDeviceIntelligenceShellCommand extends ShellCommand {
    private static final String TAG = OnDeviceIntelligenceShellCommand.class.getSimpleName();

    @NonNull
    private final OnDeviceIntelligenceManagerService mService;

    OnDeviceIntelligenceShellCommand(@NonNull OnDeviceIntelligenceManagerService service) {
        mService = service;
    }

    @Override
    public int onCommand(String cmd) {
        if (cmd == null) {
            return handleDefaultCommands(cmd);
        }

        switch (cmd) {
            case "set-temporary-services":
                return setTemporaryServices();
            case "get-services":
                return getConfiguredServices();
            default:
                return handleDefaultCommands(cmd);
        }
    }

    @Override
    public void onHelp() {
        PrintWriter pw = getOutPrintWriter();
        pw.println("OnDeviceIntelligenceShellCommand commands: ");
        pw.println("  help");
        pw.println("    Print this help text.");
        pw.println();
        pw.println(
                "  set-temporary-services [IntelligenceServiceComponentName] "
                        + "[InferenceServiceComponentName] [DURATION]");
        pw.println("    Temporarily (for DURATION ms) changes the service implementations.");
        pw.println("    To reset, call without any arguments.");

        pw.println("  get-services To get the names of services that are currently being used.");
    }

    private int setTemporaryServices() {
        final PrintWriter out = getOutPrintWriter();
        final String intelligenceServiceName = getNextArg();
        final String inferenceServiceName = getNextArg();
        if (getRemainingArgsCount() == 0 && intelligenceServiceName == null
                && inferenceServiceName == null) {
            mService.resetTemporaryServices();
            out.println("OnDeviceIntelligenceManagerService temporary reset. ");
            return 0;
        }

        Objects.requireNonNull(intelligenceServiceName);
        Objects.requireNonNull(inferenceServiceName);
        final int duration = Integer.parseInt(getNextArgRequired());
        mService.setTemporaryServices(
                new String[]{intelligenceServiceName, inferenceServiceName}, duration);
        out.println("OnDeviceIntelligenceService temporarily set to " + intelligenceServiceName
                + " \n and \n OnDeviceTrustedInferenceService set to " + inferenceServiceName
                + " for " + duration + "ms");
        return 0;
    }

    private int getConfiguredServices() {
        final PrintWriter out = getOutPrintWriter();
        String[] services = mService.getServiceNames();
        out.println("OnDeviceIntelligenceService set to :  " + services[0]
                + " \n and \n OnDeviceTrustedInferenceService set to : " + services[1]);
        return 0;
    }
}
 No newline at end of file