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

Commit 09831056 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implement client side framework for the new Serial API." into main

parents 491545d5 1d9cf534
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -11370,6 +11370,7 @@ package android.content {
    field public static final String SEARCH_SERVICE = "search";
    field @FlaggedApi("android.os.security_state_service") public static final String SECURITY_STATE_SERVICE = "security_state";
    field public static final String SENSOR_SERVICE = "sensor";
    field @FlaggedApi("android.hardware.serial.flags.enable_serial_api") public static final String SERIAL_SERVICE = "serial";
    field public static final String SHORTCUT_SERVICE = "shortcut";
    field public static final String STATUS_BAR_SERVICE = "statusbar";
    field public static final String STORAGE_SERVICE = "storage";
@@ -21316,6 +21317,34 @@ package android.hardware.lights {
}
package android.hardware.serial {
  @FlaggedApi("android.hardware.serial.flags.enable_serial_api") public final class SerialManager {
    method @NonNull public java.util.List<android.hardware.serial.SerialPort> getSerialPorts();
    method public void registerSerialPortListener(@NonNull android.hardware.serial.SerialPortListener, @NonNull java.util.concurrent.Executor);
    method public void unregisterSerialPortListener(@NonNull android.hardware.serial.SerialPortListener);
  }
  @FlaggedApi("android.hardware.serial.flags.enable_serial_api") public final class SerialPort {
    method @NonNull public String getName();
    method public int getProductId();
    method public int getVendorId();
    method public void requestOpen(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.hardware.serial.SerialPortResponse,java.lang.Exception>);
    field public static final int INVALID_ID = -1; // 0xffffffff
  }
  @FlaggedApi("android.hardware.serial.flags.enable_serial_api") public interface SerialPortListener {
    method public void onSerialPortConnected(@NonNull android.hardware.serial.SerialPort);
    method public void onSerialPortDisconnected(@NonNull android.hardware.serial.SerialPort);
  }
  @FlaggedApi("android.hardware.serial.flags.enable_serial_api") public final class SerialPortResponse {
    method @NonNull public android.os.ParcelFileDescriptor getFileDescriptor();
    method @NonNull public android.hardware.serial.SerialPort getPort();
  }
}
package android.hardware.usb {
  public class UsbAccessory implements android.os.Parcelable {
+24 −9
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.app;

import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
import static android.hardware.serial.flags.Flags.enableSerialApi;
import static android.provider.flags.Flags.newStoragePublicApi;
import static android.server.Flags.removeGameManagerServiceFromWear;
import static android.service.chooser.Flags.interactiveChooser;
@@ -107,10 +108,8 @@ import android.devicelock.DeviceLockFrameworkInitializer;
import android.graphics.fonts.FontManager;
import android.hardware.ConsumerIrManager;
import android.hardware.ISensorPrivacyManager;
import android.hardware.ISerialManager;
import android.hardware.SensorManager;
import android.hardware.SensorPrivacyManager;
import android.hardware.SerialManager;
import android.hardware.SystemSensorManager;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.IAuthService;
@@ -805,13 +804,29 @@ public final class SystemServiceRegistry {
                        return new AdbManager(ctx, IAdbManager.Stub.asInterface(b));
                    }});

        registerService(Context.SERIAL_SERVICE, SerialManager.class,
                new CachedServiceFetcher<SerialManager>() {
        if (enableSerialApi()) {
            registerService(Context.SERIAL_SERVICE, android.hardware.serial.SerialManager.class,
                    new CachedServiceFetcher<android.hardware.serial.SerialManager>() {
                        @Override
            public SerialManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                        public android.hardware.serial.SerialManager createService(ContextImpl ctx)
                                throws ServiceNotFoundException {
                            IBinder b = ServiceManager.getServiceOrThrow(Context.SERIAL_SERVICE);
                return new SerialManager(ctx, ISerialManager.Stub.asInterface(b));
            }});
                            return new android.hardware.serial.SerialManager(ctx,
                                    android.hardware.serial.ISerialManager.Stub.asInterface(b));
                        }
                    });
        } else {
            registerService(Context.SERIAL_SERVICE, android.hardware.SerialManager.class,
                    new CachedServiceFetcher<android.hardware.SerialManager>() {
                        @Override
                        public android.hardware.SerialManager createService(ContextImpl ctx)
                                throws ServiceNotFoundException {
                            IBinder b = ServiceManager.getServiceOrThrow(Context.SERIAL_SERVICE);
                            return new android.hardware.SerialManager(ctx,
                                    android.hardware.ISerialManager.Stub.asInterface(b));
                        }
                    });
        }

        registerService(Context.VIBRATOR_MANAGER_SERVICE, VibratorManager.class,
                new CachedServiceFetcher<VibratorManager>() {
+4 −5
Original line number Diff line number Diff line
@@ -4427,7 +4427,7 @@ public abstract class Context {
                // @hide: SIP_SERVICE,
                USB_SERVICE,
                LAUNCHER_APPS_SERVICE,
                // @hide: SERIAL_SERVICE,
                SERIAL_SERVICE,
                // @hide: HDMI_CONTROL_SERVICE,
                INPUT_SERVICE,
                DISPLAY_SERVICE,
@@ -5998,13 +5998,12 @@ public abstract class Context {

    /**
     * Use with {@link #getSystemService(String)} to retrieve a {@link
     * android.hardware.SerialManager} for access to serial ports.
     * android.hardware.serial.SerialManager} for access to serial ports.
     *
     * @see #getSystemService(String)
     * @see android.hardware.SerialManager
     *
     * @hide
     * @see android.hardware.serial.SerialManager
     */
    @FlaggedApi(android.hardware.serial.flags.Flags.FLAG_ENABLE_SERIAL_API)
    public static final String SERIAL_SERVICE = "serial";

    /**
+4 −1
Original line number Diff line number Diff line
@@ -22,3 +22,6 @@ per-file OverlayProperties* = file:/graphics/java/android/graphics/OWNERS

# Lut related files
per-file *Lut* = file:/graphics/java/android/graphics/OWNERS

# Serial
per-file *Serial* = file:/core/java/android/hardware/serial/OWNERS
+161 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 android.hardware.serial;

import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;

/**
 * This class allows you to communicate with Serial ports.
 */
@SystemService(Context.SERIAL_SERVICE)
@FlaggedApi(android.hardware.serial.flags.Flags.FLAG_ENABLE_SERIAL_API)
public final class SerialManager {
    private static final String TAG = "SerialManager";

    @SuppressWarnings("unused")
    private final @NonNull Context mContext;
    private final @NonNull ISerialManager mService;

    @GuardedBy("mLock")
    private SerialPortServiceListener mServiceListener;

    @GuardedBy("mLock")
    private ArrayMap<SerialPortListener, Executor> mListeners;

    private final Object mLock = new Object();

    /** @hide */
    public SerialManager(@NonNull Context context, @NonNull ISerialManager service) {
        mContext = context;
        mService = service;
    }

    /**
     * Enumerates serial ports.
     */
    @NonNull
    public List<SerialPort> getSerialPorts() {
        try {
            List<SerialPortInfo> infos = mService.getSerialPorts();
            List<SerialPort> ports = new ArrayList<>(infos.size());
            for (int i = 0; i < infos.size(); i++) {
                ports.add(new SerialPort(infos.get(i), mService));
            }
            return Collections.unmodifiableList(ports);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Register a listener to monitor serial port connections and disconnections.
     */
    public void registerSerialPortListener(@NonNull SerialPortListener listener,
            @NonNull Executor executor) {
        synchronized (mLock) {
            if (mServiceListener == null) {
                mServiceListener = new SerialPortServiceListener();
                try {
                    mService.registerSerialPortListener(mServiceListener);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            if (mListeners == null) {
                mListeners = new ArrayMap<>();
            }
            if (mListeners.containsKey(listener)) {
                throw new IllegalStateException("Listener has already been registered.");
            }
            mListeners.put(listener, executor);
        }
    }

    /**
     * Unregister a listener that monitored serial port connections and disconnections.
     */
    public void unregisterSerialPortListener(@NonNull SerialPortListener listener) {
        synchronized (mLock) {
            if (mListeners == null) {
                return;
            }
            mListeners.remove(listener);
            if (mListeners.isEmpty()) {
                if (mServiceListener != null) {
                    try {
                        mService.unregisterSerialPortListener(mServiceListener);
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    } finally {
                        // If there was a RemoteException, the system server may have died,
                        // and this listener probably became unregistered, so clear it for
                        // re-registration.
                        mServiceListener = null;
                    }
                }
            }
        }
    }

    private class SerialPortServiceListener extends ISerialPortListener.Stub {
        @Override
        public void onSerialPortConnected(SerialPortInfo info) {
            SerialPort port = new SerialPort(info, mService);
            synchronized (mLock) {
                for (Map.Entry<SerialPortListener, Executor> e : mListeners.entrySet()) {
                    Executor executor = e.getValue();
                    SerialPortListener listener = e.getKey();
                    try {
                        executor.execute(() -> listener.onSerialPortConnected(port));
                    } catch (RuntimeException e2) {
                        Slog.w(TAG, "Exception in listener", e2);
                    }
                }
            }
        }

        @Override
        public void onSerialPortDisconnected(SerialPortInfo info) {
            SerialPort port = new SerialPort(info, mService);
            synchronized (mLock) {
                for (Map.Entry<SerialPortListener, Executor> e : mListeners.entrySet()) {
                    Executor executor = e.getValue();
                    SerialPortListener listener = e.getKey();
                    try {
                        executor.execute(() -> listener.onSerialPortDisconnected(port));
                    } catch (RuntimeException e2) {
                        Slog.w(TAG, "Exception in listener", e2);
                    }
                }
            }
        }
    }
}
Loading