> For the complete documentation index, see [llms.txt](https://notes.tejpratapsingh.com/android-tips/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://notes.tejpratapsingh.com/android-tips/interview/aosp/interview-aosp-2.md).

# Interview AOSP 2

### 1. Activity Internals

#### Activity Launch: From `startActivity()` to `onResume()`

**1. Client Request** The call enters `ActivityTaskManagerService` (ATMS) via Binder. In Android 10+, `ActivityManagerService` delegates task/stack management to ATMS.

```java
// frameworks/base/core/java/android/app/Instrumentation.java
public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    IApplicationThread whoThread = (IApplicationThread) contextThread;
    // Cross-process call into system_server
    int result = ActivityTaskManager.getService()
        .startActivity(whoThread, who.getBasePackageName(), intent, ...);
}
```

**2. ATMS Resolve & Intent Flags** `ActivityStarter` (inside ATMS) resolves the intent via `PackageManagerInternal`, computes the launch mode (`standard`, `singleTop`, `singleTask`, `singleInstance`), and determines if a new task is needed.

```java
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
        IVoiceInteractionSession voiceSession, ...) {
    // Compute launch flags
    computeLaunchingTaskFlags();
    // Find or create Task
    mTargetTask = mPreferredTaskDisplayArea.getOrCreateRootTask(...);
    // Place activity in task
    mTargetStack.startActivityLocked(mStartActivity, ...);
}
```

**3. Process Start via Zygote** If the target app is not running, ATMS calls `ProcessList.startProcessLocked()`, which builds the `ProcessRecord` and issues a `start()` to Zygote.

```java
// frameworks/base/services/core/java/com/android/server/am/ProcessList.java
boolean startProcessLocked(ProcessRecord app, ...) {
    final String entryPoint = "android.app.ActivityThread";
    return startProcessLocked(hostingRecord, entryPoint, app, uid, ...);
}

private boolean startProcessLocked(...) {
    // Open Zygote socket
    final ZygoteState zygoteState = ZygoteProcess.getZygoteState();
    // Send args: [entryPoint, niceName, uid, gid, ...]
    final Process.ProcessStartResult startResult = ZygoteProcess.start(...);
}
```

**4. ActivityThread Attach** The new process starts `ActivityThread.main()`, creates the main `Looper`, and attaches to ATMS. ATMS replies with `bindApplication()` containing `ApplicationInfo`, providers, and configuration.

```java
// frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq); // false = not system
    Looper.loop();
}

private void attach(boolean system, long startSeq) {
    final IActivityManager mgr = ActivityManager.getService();
    mgr.attachApplication(mAppThread, startSeq);
}
```

**5. Application & Activity Creation** ATMS schedules `bindApplication()` back to the app. `ActivityThread` creates the `LoadedApk`, `Instrumentation`, `Application`, and then schedules `Activity.onCreate()` via `H.BIND_APPLICATION` and `H.LAUNCH_ACTIVITY`.

```java
// ActivityThread.java
public final void bindApplication(...) {
    AppBindData data = new AppBindData();
    data.processName = processName;
    data.appInfo = appInfo;
    data.instrumentationName = instrumentationName;
    data.providers = providers;
    sendMessage(H.BIND_APPLICATION, data);
}

private void handleBindApplication(AppBindData data) {
    // Create Instrumentation
    mInstrumentation = new Instrumentation();
    // Create Application
    Application app = data.info.makeApplication(false, mInstrumentation);
    mInitialApplication = app;
    mInstrumentation.callApplicationOnCreate(app);
}

// LAUNCH_ACTIVITY path
public Activity handleLaunchActivity(ActivityClientRecord r, ...) {
    // Creates Window, decorView, WindowManager
    final Activity activity = mInstrumentation.newActivity(...);
    activity.attach(...); // Creates Window, PhoneWindow, WindowManager
    mInstrumentation.callActivityOnCreate(activity, r.state);
    return activity;
}
```

**6. Window & Surface Creation** `Activity.attach()` creates a `PhoneWindow`. `handleResumeActivity()` adds the decor view to `WindowManager`, which triggers `ViewRootImpl.setView()`. `ViewRootImpl` requests a `Surface` from `WindowManagerService` via `relayoutWindow()`, and `WMS` allocates a `SurfaceControl` from `SurfaceFlinger`.

```java
// Activity.java
final void attach(Context context, ActivityThread aThread, ...) {
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowManager(...);
    mWindowManager = mWindow.getWindowManager();
}

// ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    // IPC to WMS
    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, ...);
    // Create native Surface
    mSurface = new Surface();
    // Relayout to get real buffer
    relayoutResult = mWindowSession.relayout(mWindow, mSeq, mWindowAttributes,
            requestedWidth, requestedHeight, viewVisibility, ...);
    mSurface.copyFrom(mSurfaceControl); // Now backed by SurfaceFlinger buffer
}
```

***

### 2. Zygote Internals

#### The Fork Model & USAP (Unspecialized App Process)

**Zygote Startup** `init` starts Zygote via `app_process`. It loads the Android runtime, preloads classes/resources, and enters a `select()` loop on a Unix domain socket (`/dev/socket/zygote`).

```cpp
// frameworks/base/core/jni/AndroidRuntime.cpp
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) {
    JavaVMInitArgs initArgs;
    initArgs.version = JNI_VERSION_1_4;
    // ...
    JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs);
}

// app_main.cpp
int main(int argc, char* const argv[]) {
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    }
}
```

**Preloading** `ZygoteInit` preloads \~4000 classes, drawables, and shared libraries. This memory is marked read-only and shared across all children via Copy-on-Write (COW).

```java
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
static void preload() {
    preloadClasses();      // /system/etc/preloaded-classes
    preloadResources();    // com.android.internal.R.array, drawable
    preloadSharedLibraries(); // android, compiler_rt, jnigraphics
    // ...
    // Explicit GC to clean up preload garbage
    gcAndFinalize();
}
```

**USAP (Unspecialized App Process) — Android 10+** To reduce fork latency, Zygote maintains a pool of pre-forked, idle processes (USAP). When an app launch request arrives, Zygote **specializes** a USAP process instead of forking from the main Zygote.

```java
// ZygoteServer.java
Runnable runSelectLoop(String abiList) {
    while (true) {
        // Check for USAP pool refill
        if (mUsapPoolEnabled) {
            fillUsapPool(abiList); // Pre-fork processes
        }
        
        // Poll on Zygote socket and USAP reporting pipes
        StructPollfd[] pollFds = new StructPollfd[...];
        Os.poll(pollFds, -1);
        
        if (isUsapRequest(pollFds)) {
            return usapPoolServer.processCommand(...);
        } else {
            ZygoteConnection connection = peers.get(i);
            return connection.processCommand(this, multipleForksOK);
        }
    }
}
```

**Fork & Specialization** `Zygote.forkAndSpecialize()` calls native `nativeForkAndSpecialize()`, which uses `fork()`. Post-fork, the child calls `specializeAppProcess()` to drop privileges, set SELinux context, and apply `seccomp-bpf` filters.

```cpp
// frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(...) {
    pid_t pid = fork();
    if (pid == 0) {
        // Child process
        SpecializeCommon(env, uid, gid, gids, runtime_flags, ...);
    }
    return pid;
}

static void SpecializeCommon(JNIEnv* env, ...) {
    // Set UID/GID
    setgid(gid);
    setuid(uid);
    
    // SELinux transition
    SetSELinuxContext(...);
    
    // Enable seccomp
    SetSeccompFilter(...);
    
    // Unmount storage if not needed
    UnmountStorage(...);
}
```

***

### 3. Broadcast Internals

#### Broadcast Resolution, Scheduling, and ANR

**Registration** Receivers are registered either statically in `AndroidManifest.xml` (parsed by PMS at boot) or dynamically via `Context.registerReceiver()`. Dynamic receivers are stored in `AMS.mRegisteredReceivers` keyed by `IIntentReceiver` Binder.

```java
// ActivityManagerService.java
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
        IIntentReceiver receiver, IntentFilter filter, ...) {
    synchronized (this) {
        ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
        if (rl == null) {
            rl = new ReceiverList(callerApp, ...);
            mRegisteredReceivers.put(receiver.asBinder(), rl);
        }
        BroadcastFilter bf = new BroadcastFilter(filter, rl, ...);
        rl.add(bf);
        mReceiverResolver.addFilter(bf);
    }
}
```

**Broadcast Enqueue** `broadcastIntentLocked()` resolves the broadcast to a list of receivers (both manifest and registered). It creates a `BroadcastRecord` and places it in a queue.

* **Ordered**: Enqueued to `mOrderedBroadcasts`. Delivered sequentially.
* **Parallel**: Enqueued to `mParallelBroadcasts`. Delivered concurrently to registered receivers.

```java
// ActivityManagerService.java
final int broadcastIntentLocked(...) {
    // Resolve receivers
    List receivers = ...;
    List registeredReceivers = mReceiverResolver.queryIntent(intent, ...);
    
    // Split into parallel and ordered
    if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
        receivers = collectReceiverComponents(intent, ...);
    }
    
    if (ordered) {
        // Build single BroadcastRecord for all receivers
        BroadcastRecord r = new BroadcastRecord(intent, ...);
        r.receivers = receivers;
        mOrderedBroadcasts.add(r);
        scheduleBroadcastsLocked();
    } else {
        // Queue parallel broadcasts
        for (int i = registeredReceivers.size() - 1; i >= 0; i--) {
            BroadcastRecord r = new BroadcastRecord(intent, ...);
            r.receiver = registeredReceivers.get(i);
            mParallelBroadcasts.add(r);
        }
        scheduleBroadcastsLocked();
    }
}
```

**Delivery & ANR** `processNextBroadcast()` dequeues records. For each receiver, it calls `deliverToRegisteredReceiverLocked()`. If the target process is not running, it starts it. An **ANR** is posted via `mHandler.postDelayed(mBroadcastTimeout, timeout)` (default 10s for foreground, 60s for background). If the receiver does not finish in time, `broadcastTimeoutLocked()` triggers ANR.

```java
// ActivityManagerService.java
final void processNextBroadcast(boolean fromMsg) {
    synchronized (mBroadcastQueues) {
        BroadcastQueue queue = mBroadcastQueues[i];
        BroadcastRecord r = queue.mOrderedBroadcasts.get(0);
        
        // Check if current receiver is done
        if (r.receiverTime > 0) {
            long timeout = r.timeout;
            if (now > r.receiverTime + timeout) {
                broadcastTimeoutLocked(r); // ANR
                return;
            }
        }
        
        // Deliver to next receiver
        Object nextReceiver = r.receivers.get(r.nextReceiver++);
        if (nextReceiver instanceof ResolveInfo) {
            // Manifest receiver: start process if needed
            ProcessRecord app = getProcessRecordLocked(...);
            if (app == null) {
                app = startProcessLocked(...);
                r.curApp = app;
                return; // Will resume broadcast when process attaches
            }
        } else {
            // Registered receiver: direct IPC
            performReceiveLocked(r, (BroadcastFilter) nextReceiver, ...);
        }
    }
}

void broadcastTimeoutLocked(BroadcastRecord r) {
    // Log ANR
    mHandler.post(() -> {
        appNotResponding(r.curApp, "Broadcast of " + r.intent);
    });
}
```

**Background Restrictions (Android 8+)** Implicit broadcasts (without a specific target component) are dropped for manifest-registered receivers unless the app is in the foreground or on a whitelist. Explicit broadcasts and registered receivers are still allowed.

```java
// ActivityManagerService.java
if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.O
        && (r.intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0
        && !r.intent.getComponent() != null) {
    // Implicit broadcast to manifest receiver is blocked
    return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
}
```

***

### 4. Service Internals

#### `startService()` vs `bindService()`

**startService() Path** The client calls `Context.startService()`. This goes to `ActivityManagerService.startService()`. AMS creates a `ServiceRecord`, places it in `mServicesByName`, and if the service is not running, starts the process. The service is then created via `scheduleCreateService()` and `scheduleStartService()`.

```java
// ActivityManagerService.java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, ...) {
    ServiceLookupResult res = retrieveServiceLocked(service, ...);
    ServiceRecord r = res.record;
    
    // Start service if needed
    if (r.app == null) {
        r.app = startProcessLocked(r.processName, ...);
    }
    
    // Send CREATE_SERVICE message to app thread
    r.app.thread.scheduleCreateService(r, r.serviceInfo, ...);
    // Send START_SERVICE
    r.app.thread.scheduleServiceArgs(r, false, r.intent);
}
```

**bindService() Path** `bindService()` adds a `ConnectionRecord` linking the client `IServiceConnection` to the `ServiceRecord`. If the service is not created, it goes through `scheduleCreateService()`. Then `scheduleBindService()` is sent, which calls `Service.onBind()` in the app. The resulting `IBinder` is returned to the client via `publishService()`.

```java
// ActivityManagerService.java
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
        IServiceConnection connection, int flags, ...) {
    ServiceRecord s = retrieveServiceLocked(service, ...).record;
    
    // Create connection record
    ConnectionRecord c = new ConnectionRecord(b, activity, connection, ...);
    s.addConnection(c);
    
    if ((flags & Context.BIND_AUTO_CREATE) != 0) {
        s.app = startProcessLocked(s.processName, ...);
        s.app.thread.scheduleCreateService(s, ...);
    }
    
    // Request binding
    s.app.thread.scheduleBindService(s, service.getExtras(), ...);
}

// App side: Service publishes binder
private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);
    IBinder binder = s.onBind(data.intent);
    ActivityManager.getService().publishService(data.token, data.intent, binder);
}
```

**Service Lifecycle & Death** `ServiceRecord` tracks `startId` for each `startService()` call. `stopService()` removes the start request. When all starts are removed and no bindings exist, `AMS` schedules `scheduleStopService()`. If the hosting process dies, `AMS` calls `serviceDiedLocked()` and restarts the service if `START_STICKY`.

```java
// ActivityManagerService.java
void serviceDiedLocked(ServiceRecord r, ProcessRecord app) {
    if (r.startRequested && (r.startFlags & Service.START_STICKY) != 0) {
        // Reschedule start
        r.pendingStarts.add(new ServiceRecord.StartItem(...));
        startServiceLocked(null, r.intent, ...);
    }
}
```

***

### 5. Creating a System Service

A system service lives in `system_server` and is accessible to apps via `Context.getSystemService()`.

#### Step 1: Define the AIDL Interface

```aidl
// frameworks/base/core/java/android/app/IMySystemService.aidl
package android.app;

import android.os.Bundle;

interface IMySystemService {
    int doSomething(String arg);
    oneway void doSomethingAsync(String arg);
}
```

Add to `frameworks/base/Android.bp` in `srcs` for the `framework` module.

#### Step 2: Implement the Service

```java
// frameworks/base/services/core/java/com/android/server/MySystemService.java
package com.android.server;

import android.app.IMySystemService;
import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;

public class MySystemService extends IMySystemService.Stub {
    private final Context mContext;
    private final Object mLock = new Object();

    public MySystemService(Context context) {
        mContext = context;
    }

    @Override
    public int doSomething(String arg) throws RemoteException {
        mContext.enforceCallingPermission(
            "android.permission.MY_PERMISSION", "doSomething");
        synchronized (mLock) {
            return arg.length();
        }
    }

    @Override
    public void doSomethingAsync(String arg) throws RemoteException {
        // Async implementation
    }
}
```

#### Step 3: Register in SystemServer

```java
// frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    // ...
    t.traceBegin("StartMySystemService");
    ServiceManager.addService(Context.MY_SYSTEM_SERVICE, new MySystemService(mSystemContext));
    t.traceEnd();
}
```

#### Step 4: Add to Context and SystemServiceRegistry

```java
// frameworks/base/core/java/android/content/Context.java
public static final String MY_SYSTEM_SERVICE = "my_system_service";
```

```java
// frameworks/base/core/java/android/app/SystemServiceRegistry.java
static {
    // ...
    registerService(Context.MY_SYSTEM_SERVICE, MySystemManager.class,
            new CachedServiceFetcher<MySystemManager>() {
        @Override
        public MySystemManager createService(ContextImpl ctx) {
            IBinder b = ServiceManager.getService(Context.MY_SYSTEM_SERVICE);
            IMySystemService service = IMySystemService.Stub.asInterface(b);
            return new MySystemManager(ctx, service);
        }});
}
```

#### Step 5: Create the Manager Wrapper

```java
// frameworks/base/core/java/android/app/MySystemManager.java
package android.app;

public class MySystemManager {
    private final IMySystemService mService;

    public MySystemManager(Context context, IMySystemService service) {
        mService = service;
    }

    public int doSomething(String arg) {
        try {
            return mService.doSomething(arg);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
```

#### Step 6: SELinux & Permissions

Add the permission in `frameworks/base/core/res/AndroidManifest.xml`:

```xml
<<permission android:name="android.permission.MY_PERMISSION"
    android:protectionLevel="signature|privileged" />
```

Add SELinux rules in `system/sepolicy/private/service.te` and `service_contexts`:

```te
# service.te
type my_system_service, system_api_service, service_manager_type;

# service_contexts
my_system_service    u:object_r:my_system_service:s0
```

***

### 6. Creating a Vendor Service

Vendor services live in the vendor partition, run in vendor processes, and are accessed by framework or other vendor code. They use **hwbinder** (HIDL) or **vndbinder** (stable AIDL).

#### Option A: HIDL HAL (Legacy but still common)

**1. Define HIDL Interface**

```hidl
// hardware/interfaces/mydevice/1.0/IMyDevice.hal
package android.hardware.mydevice@1.0;

interface IMyDevice {
    doSomething(string arg) generates (int32_t result);
};
```

**2. Generate Boilerplate** Run `hidl-gen` to generate `Android.bp`, `IMyDevice.h`, `BpHwMyDevice`, `BnHwMyDevice`.

```bash
hidl-gen -Landroidbp -r android.hardware:hardware/interfaces android.hardware.mydevice@1.0
hidl-gen -Lhash -r android.hardware:hardware/interfaces android.hardware.mydevice@1.0
```

**3. Implement the HAL**

```cpp
// vendor/acme/mydevice/1.0/default/MyDevice.cpp
#include <android/hardware/mydevice/1.0/IMyDevice.h>

using namespace android::hardware::mydevice::V1_0;

class MyDevice : public IMyDevice {
public:
    Return<int32_t> doSomething(const hidl_string& arg) override {
        return arg.size();
    }
};

// Entry point
extern "C" IMyDevice* HIDL_FETCH_IMyDevice(const char* /* name */) {
    return new MyDevice();
}
```

**4. Build & Register**

```json
// vendor/acme/mydevice/1.0/default/Android.bp
cc_library_shared {
    name: "android.hardware.mydevice@1.0-impl",
    relative_install_path: "hw",
    vendor: true,
    srcs: ["MyDevice.cpp"],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "android.hardware.mydevice@1.0",
    ],
}
```

Register in `manifest.xml`:

```xml
<<hal format="hidl">
    <name>android.hardware.mydevice</name>
    <transport>hwbinder</transport>
    <version>1.0</version>
    <interface>
        <name>IMyDevice</name>
        <instance>default</instance>
    </interface>
</hal>
```

**5. Access from Framework**

```cpp
#include <android/hardware/mydevice/1.0/IMyDevice.h>

sp<IMyDevice> myDevice = IMyDevice::getService();
if (myDevice != nullptr) {
    auto ret = myDevice->doSomething("hello");
}
```

#### Option B: Stable AIDL HAL (Modern, Android 11+)

**1. Define Stable AIDL**

```aidl
// hardware/interfaces/mydevice/aidl/android/hardware/mydevice/IMyDevice.aidl
package android.hardware.mydevice;

@VintfStability
interface IMyDevice {
    int doSomething(String arg);
}
```

**2. Implement**

```java
// vendor/acme/mydevice/aidl/MyDevice.java
package vendor.acme.mydevice;

import android.hardware.mydevice.IMyDevice;
import android.os.RemoteException;

public class MyDevice extends IMyDevice.Stub {
    @Override
    public int doSomething(String arg) throws RemoteException {
        return arg.length();
    }
}
```

Or in C++:

```cpp
// vendor/acme/mydevice/aidl/MyDevice.cpp
#include <aidl/android/hardware/mydevice/BnMyDevice.h>

using aidl::android::hardware::mydevice::BnMyDevice;

class MyDevice : public BnMyDevice {
    ndk::ScopedAStatus doSomething(const std::string& arg, int32_t* result) override {
        *result = arg.size();
        return ndk::ScopedAStatus::ok();
    }
};
```

**3. Build**

```json
// vendor/acme/mydevice/aidl/Android.bp
cc_library_shared {
    name: "vendor.acme.mydevice-aidl",
    vendor: true,
    srcs: ["MyDevice.cpp"],
    shared_libs: [
        "libbinder_ndk",
        "android.hardware.mydevice-V1-ndk_platform",
    ],
}
```

**4. Register with APEX or vndservicemanager**

For a vendor native service exposed via `vndbinder`:

```cpp
// Main entry
int main() {
    ABinderProcess_setThreadPoolMaxThreadCount(1);
    std::shared_ptr<MyDevice> service = ndk::SharedRefBase::make<MyDevice>();
    
    const std::string instance = std::string() + MyDevice::descriptor + "/default";
    binder_status_t status = AServiceManager_addService(service->asBinder().get(), instance.c_str());
    
    ABinderProcess_joinThreadPool();
    return 1;
}
```

Add to `vendor/etc/vintf/manifest.xml`:

```xml
<<hal format="aidl">
    <name>android.hardware.mydevice</name>
    <version>1</version>
    <interface>
        <name>IMyDevice</name>
        <instance>default</instance>
    </interface>
</hal>
```

#### Option C: Vendor Native Service (vndservicemanager)

For vendor-specific services not defined in VINTF, use `vndservicemanager` directly.

```cpp
// vendor/acme/service/MyVendorService.cpp
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>

using namespace android;

int main(int argc, char** argv) {
    // Use /dev/vndbinder
    sp<IServiceManager> sm = defaultServiceManager();
    sp<MyVendorService> service = new MyVendorService();
    
    // Register with vndservicemanager
    sm->addService(String16("vendor.acme.my_service"), service);
    
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    return 0;
}
```

**SELinux for Vendor Services**

```te
# vendor/acme/sepolicy/vendor/service.te
type vendor_acme_service, vendor_service, service_manager_type;

# vendor/acme/sepolicy/vendor/service_contexts
vendor.acme.my_service    u:object_r:vendor_acme_service:s0

# vendor/acme/sepolicy/vendor/my_service.te
type my_service, domain;
type my_service_exec, exec_type, vendor_file_type, file_type;

init_daemon_domain(my_service)

# Allow my_service to register with vndservicemanager
allow my_service vndservicemanager:binder { call transfer };
allow my_service vendor_acme_service:service_manager { add find };
```

**Accessing Vendor Service from Framework**

Framework code must use `ServiceManager.getService("vendor.acme.my_service")` with the proper SELinux context (`system_server` must have `find` permission on `vendor_acme_service`). However, **Treble restrictions** prevent direct framework access to arbitrary vendor services; use a HAL interface or a **passthrough** HIDL interface instead.
