내부 이더넷 네트워크 구성

Android Auto OS 13 이상에는 이더넷 네트워크를 구성하고 관리할 수 있는 기능이 포함되어 있습니다. 그림 1은 자동차의 네트워크 다이어그램 예를 보여줍니다.

Android Auto 네트워킹

그림 1. Android Auto 네트워킹

이 그림은 OEM 네트워킹 앱이 EthernetManager 클래스의 메서드를 호출하여 온보드 이더넷 네트워크(eth0.1, eth0.2, eth0.3)를 구성하고 관리하는 것을 보여줍니다. 그림 1의 나머지 부분은 이 문서의 범위에 해당하지 않습니다.

기본 이더넷 네트워크 설정 지정

기본 네트워크 설정을 설정하려면 리소스 오버레이 config_ethernet_interfaces를 사용합니다.

<string-array translatable="false" name="config_ethernet_interfaces">
       
<!--
        <item>eth1;12,13,14,15;ip=192.168.0.10/24 gateway=192.168.0.1 dns=4.4.4.4,8.8.8.8</item>
        <item>eth2;;ip=192.168.0.11/24</item>
        <item>eth3;12,13,14,15;ip=192.168.0.12/24;1</item>
        -->

   
</string-array>

이 예는 config.xmlconfig_ethernet_interfaces 리소스 오버레이를 보여줍니다.

코드 관련 핵심 사항

  • eth1, eth2, eth3는 구성 중인 네트워크 인터페이스의 이름입니다.
  • 연속된 12, 13, 14, 15 숫자는 사용 설정된 네트워크 기능을 나타냅니다.
  • ip=, gateway=, dns는 네트워크의 초기 IP 주소, 게이트웨이, DNS를 설정하는 데 사용됩니다.

네트워크 인터페이스 사용 설정 또는 중지

네트워크 인터페이스를 사용 설정하려면 EthernetManager.enableInterface()를 호출합니다.

public final class InterfaceEnabler {
   
private final Context mApplicationContext;
   
private final EthernetManager mEthernetManager;
   
private final OutcomeReceiver<String, EthernetNetworkManagementException> mOutcomeReceiver;

   
public InterfaceEnabler(Context applicationContext,
           
OutcomeReceiver<String, EthernetNetworkManagementException> outcomeReceiver) {
        mApplicationContext
= applicationContext;
        mEthernetManager
= applicationContext.getSystemService(EthernetManager.class);
        mOutcomeReceiver
= outcomeReceiver;
   
}

   
public void enableInterface(String ifaceName) {
        mEthernetManager
.enableInterface(ifaceName,
                mApplicationContext
.getMainExecutor(),
                mOutcomeReceiver
);
   
}
}

코드 관련 핵심 사항

  • ifaceName은 사용 설정할 네트워크 인터페이스의 이름입니다.
  • getMainExecutor()는 앱 컨텍스트를 반환합니다.
  • OutcomeReceiver는 완료 여부를 전달하는 데 사용되는 콜백으로, 성공 시 업데이트된 네트워크 이름을 반환하고 오류 시 EthernetNetworkManagementException를 반환합니다.

네트워크 인터페이스가 사용 설정되면 EthernetManager.updateConfiguration()에서 설정한 구성을 사용합니다. EthernetManager.updateConfiguration()에서 구성을 설정하지 않은 경우 네트워크 인터페이스는 리소스 오버레이 config_ethernet_interfaces를 사용하거나 오버레이를 사용할 수 없는 경우 기본 이더넷 네트워크 구성을 사용합니다.

네트워크 인터페이스를 사용 중지하려면 EthernetManager.disableInterface()를 호출합니다.

public final class InterfaceEnabler {
   
private final Context mApplicationContext;
   
private final EthernetManager mEthernetManager;
   
private final OutcomeReceiver<String, EthernetNetworkManagementException> mOutcomeReceiver;

   
public InterfaceEnabler(Context applicationContext,
           
OutcomeReceiver<String, EthernetNetworkManagementException> outcomeReceiver) {
        mApplicationContext
= applicationContext;
        mEthernetManager
= applicationContext.getSystemService(EthernetManager.class);
        mOutcomeReceiver
= outcomeReceiver;
   
}

   
public void disableInterface(String ifaceName) {
        mEthernetManager
.disableInterface(ifaceName,
                mApplicationContext
.getMainExecutor(),
                mOutcomeReceiver
);
   
}
}

코드 관련 핵심 사항

  • ifaceName은 사용 중지할 네트워크 인터페이스의 이름입니다.
  • getMainExecutor()는 앱 컨텍스트를 반환합니다.
  • OutcomeReceiver는 완료 여부를 전달하는 데 사용되는 콜백으로, 성공 시 업데이트된 네트워크 이름을 반환하고 오류 시 EthernetNetworkManagementException를 반환합니다.

네트워크 구성 업데이트

이더넷 네트워크 구성을 업데이트하려면 EthernetManager.updateConfiguration()를 호출합니다.

public final class ConfigurationUpdater {
   
private final Context mApplicationContext;
   
private final EthernetManager mEthernetManager;
   
private final OutcomeReceiver<String, EthernetNetworkManagementException> mCallback;

   
public ConfigurationUpdater(Context applicationContext,
           
OutcomeReceiver<String, EthernetNetworkManagementException> callback) {
        mApplicationContext
= applicationContext;
        mEthernetManager
= applicationContext.getSystemService(EthernetManager.class);
        mCallback
= callback;
   
}

   
public void updateNetworkConfiguration(String packageNames,
           
String ipConfigurationText,
           
String networkCapabilitiesText,
           
String interfaceName)
           
throws IllegalArgumentException, PackageManager.NameNotFoundException {

       
EthernetNetworkUpdateRequest request = new EthernetNetworkUpdateRequest.Builder()
               
.setIpConfiguration(getIpConfiguration(ipConfigurationText))
               
.setNetworkCapabilities(getCapabilities(
                        interfaceName
, networkCapabilitiesText, packageNames))
               
.build();

        mEthernetManager
.updateConfiguration(interfaceName, request,
                mApplicationContext
.getMainExecutor(), mCallback);

   
}
}

코드 관련 핵심 사항

  • getCapabilities()는 현재 네트워크 기능을 가져오고 convertToUIDs()를 호출하여 사람이 읽을 수 있는 패키지 이름을 Linux 고유 식별자 (UID)로 변환하는 도우미 메서드입니다. 일반적으로 연결된 패키지의 UID는 사전에 알 수 없습니다. 따라서 EthernetManager.updateConfiguration()를 사용하여 앱의 하위 집합에 대한 액세스를 제한하려면 앱의 UID를 사용해야 합니다.
  • request는 내부 네트워크에 사용할 구성입니다. 요청에는 IP 구성 및 네트워크 기능에 관한 새 설정이 포함될 수 있습니다. 네트워크가 연결 스택에 등록되면 구성에 따라 업데이트됩니다. 이 구성은 재부팅 후 유지되지 않습니다.
  • getMainExecutor()는 리스너가 호출되는 실행자를 반환합니다.
  • mCallback는 완료 여부를 전달하는 데 사용되는 콜백으로, 성공 시 업데이트된 네트워크 이름을 반환하고 오류 시 EthernetNetworkManagementException를 반환합니다.

updateConfiguration()는 Android 연결 스택에서 변경 불가능한 것으로 간주되는 네트워크의 특성을 업데이트할 수 있습니다. 이러한 변경 불가능한 속성을 업데이트하기 위해 네트워크가 다운되고 업데이트된 후 다시 업됩니다.

네트워크를 앱 하위 집합으로 제한

EthernetManager#updateConfiguration를 사용하여 허용된 UID의 하위 집합에만 액세스하도록 제한할 수 있습니다. 소수의 OEM 앱에서만 사용할 수 있는 내부 차량 네트워크와 같이 이 메서드가 필요한 사용 사례를 처리하려면 이 메서드를 사용하세요.

Android는 주로 UID로 앱을 추적합니다. UIDToPackageNameConverter.java의 다음 코드는 패키지 이름 문자열에서 일련의 UID를 가져오는 방법을 보여줍니다.

public static Set<Integer> convertToUids(Context applicationContext, String packageNames)
           
throws PackageManager.NameNotFoundException {
       
final PackageManager packageManager = applicationContext.getPackageManager();
       
final UserManager userManager = applicationContext.getSystemService(UserManager.class);

       
final Set<Integer> uids = new ArraySet<>();
       
final List<UserHandle> users = userManager.getUserHandles(true);

       
String[] packageNamesArray = packageNames.split(",");
       
for (String packageName : packageNamesArray) {
           
boolean nameNotFound = true;
            packageName
= packageName.trim();
           
for (final UserHandle user : users) {
               
try {
                   
final int uid =
                            packageManager
.getApplicationInfoAsUser(packageName, 0, user).uid;
                    uids
.add(uid);
                    nameNotFound
= false;
               
} catch (PackageManager.NameNotFoundException e) {
                   
// Although this may seem like an error scenario, it is OK as all packages are
                   
// not expected to be installed for all users.
                   
continue;
               
}
           
}

           
if (nameNotFound) {
               
throw new PackageManager.NameNotFoundException("Not installed: " + packageName);
           
}
       
}
       
return uids;

코드 관련 핵심 사항

  • getApplicationInfoAsuser().uid는 패키지 이름에서 UID를 가져오는 데 사용됩니다.
  • uids는 생성된 정수 배열입니다.

EthernetManagerTest.kt의 다음 코드는 네트워크를 사용할 수 있는 앱의 UID로 네트워크 인터페이스 구성을 업데이트하는 방법을 보여줍니다.

val allowedUids = setOf(Process.myUid())
        val nc
= NetworkCapabilities.Builder(request.networkCapabilities)
               
.setAllowedUids(allowedUids).build()
        updateConfiguration
(iface, capabilities = nc).expectResult(iface.name)

코드 관련 핵심 사항

  • allowUids는 네트워크를 사용할 수 있는 앱 UID 집합입니다.
  • updateConfiguration()는 구성을 업데이트하여 네트워크를 제공된 UID 집합으로 제한합니다.