diff --git a/service/Android.bp b/service/Android.bp index 856f3b8533..f630ceac36 100644 --- a/service/Android.bp +++ b/service/Android.bp @@ -60,6 +60,7 @@ java_library { "services.core", "services.net", "unsupportedappusage", + "ServiceConnectivityResources", ], static_libs: [ "modules-utils-os", diff --git a/service/ServiceConnectivityResources/Android.bp b/service/ServiceConnectivityResources/Android.bp new file mode 100644 index 0000000000..f2446b7f7e --- /dev/null +++ b/service/ServiceConnectivityResources/Android.bp @@ -0,0 +1,35 @@ +// +// Copyright (C) 2021 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. +// + +// APK to hold all the wifi overlayable resources. +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_app { + name: "ServiceConnectivityResources", + sdk_version: "system_current", + resource_dirs: [ + "res", + ], + privileged: true, + export_package_resources: true, + apex_available: [ + "com.android.tethering", + ], + // TODO: use a dedicated cert once generated + certificate: "platform", +} diff --git a/service/ServiceConnectivityResources/AndroidManifest.xml b/service/ServiceConnectivityResources/AndroidManifest.xml new file mode 100644 index 0000000000..2c30302615 --- /dev/null +++ b/service/ServiceConnectivityResources/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml new file mode 100644 index 0000000000..7d98c76a40 --- /dev/null +++ b/service/ServiceConnectivityResources/res/values/config.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + 60000 + + + + + + + \ No newline at end of file diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml new file mode 100644 index 0000000000..00ec2df0e6 --- /dev/null +++ b/service/ServiceConnectivityResources/res/values/overlayable.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + diff --git a/service/ServiceConnectivityResources/res/values/strings.xml b/service/ServiceConnectivityResources/res/values/strings.xml new file mode 100644 index 0000000000..2c7b992650 --- /dev/null +++ b/service/ServiceConnectivityResources/res/values/strings.xml @@ -0,0 +1,22 @@ + + + + + System Connectivity Resources + \ No newline at end of file diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index eac9f4de5a..688b33e0f6 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -188,7 +188,6 @@ import android.util.SparseArray; import android.util.SparseIntArray; import com.android.connectivity.aidl.INetworkAgent; -import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; @@ -202,6 +201,7 @@ import com.android.net.module.util.LinkPropertiesUtils.CompareResult; import com.android.net.module.util.NetworkCapabilitiesUtils; import com.android.net.module.util.PermissionUtils; import com.android.server.connectivity.AutodestructReference; +import com.android.server.connectivity.ConnectivityResources; import com.android.server.connectivity.DnsManager; import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate; import com.android.server.connectivity.KeepaliveTracker; @@ -317,6 +317,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private boolean mRestrictBackground; private final Context mContext; + private final ConnectivityResources mResources; // The Context is created for UserHandle.ALL. private final Context mUserAllContext; private final Dependencies mDeps; @@ -604,7 +605,6 @@ public class ConnectivityService extends IConnectivityManager.Stub private Intent mInitialBroadcast; private PowerManager.WakeLock mNetTransitionWakeLock; - private int mNetTransitionWakeLockTimeout; private final PowerManager.WakeLock mPendingIntentWakeLock; // A helper object to track the current default HTTP proxy. ConnectivityService needs to tell @@ -1011,6 +1011,13 @@ public class ConnectivityService extends IConnectivityManager.Stub return new MockableSystemProperties(); } + /** + * Get the {@link ConnectivityResources} to use in ConnectivityService. + */ + public ConnectivityResources getResources(@NonNull Context ctx) { + return new ConnectivityResources(ctx); + } + /** * Create a HandlerThread to use in ConnectivityService. */ @@ -1093,6 +1100,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mSystemProperties = mDeps.getSystemProperties(); mNetIdManager = mDeps.makeNetIdManager(); mContext = Objects.requireNonNull(context, "missing Context"); + mResources = deps.getResources(mContext); mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID); mMetricsLog = logger; @@ -1154,8 +1162,6 @@ public class ConnectivityService extends IConnectivityManager.Stub final PowerManager powerManager = (PowerManager) context.getSystemService( Context.POWER_SERVICE); mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); - mNetTransitionWakeLockTimeout = mContext.getResources().getInteger( - com.android.internal.R.integer.config_networkTransitionTimeout); mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1]; @@ -1222,10 +1228,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - mWolSupportedInterfaces = new ArraySet( - mContext.getResources().getStringArray( - com.android.internal.R.array.config_wakeonlan_supported_interfaces)); - mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mPermissionMonitor = new PermissionMonitor(mContext, mNetd); @@ -4601,7 +4603,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } mWakelockLogs.log("ACQUIRE for " + forWhom); Message msg = mHandler.obtainMessage(EVENT_EXPIRE_NET_TRANSITION_WAKELOCK); - mHandler.sendMessageDelayed(msg, mNetTransitionWakeLockTimeout); + final int lockTimeout = mResources.get().getInteger( + com.android.connectivity.resources.R.integer.config_networkTransitionTimeout); + mHandler.sendMessageDelayed(msg, lockTimeout); } // Called when we gain a new default network to release the network transition wakelock in a @@ -6515,6 +6519,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void updateWakeOnLan(@NonNull LinkProperties lp) { + if (mWolSupportedInterfaces == null) { + mWolSupportedInterfaces = new ArraySet<>(mResources.get().getStringArray( + com.android.connectivity.resources.R.array + .config_wakeonlan_supported_interfaces)); + } lp.setWakeOnLanSupported(mWolSupportedInterfaces.contains(lp.getInterfaceName())); } @@ -8076,8 +8085,8 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public String getCaptivePortalServerUrl() { enforceNetworkStackOrSettingsPermission(); - String settingUrl = mContext.getResources().getString( - R.string.config_networkCaptivePortalServerUrl); + String settingUrl = mResources.get().getString( + com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl); if (!TextUtils.isEmpty(settingUrl)) { return settingUrl; diff --git a/services/core/java/com/android/server/connectivity/ConnectivityResources.java b/services/core/java/com/android/server/connectivity/ConnectivityResources.java new file mode 100644 index 0000000000..45cf21e035 --- /dev/null +++ b/services/core/java/com/android/server/connectivity/ConnectivityResources.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2021 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.connectivity; + +import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.util.Log; + +import com.android.server.ConnectivityService; + +import java.util.List; + +/** + * Utility to obtain the {@link ConnectivityService} {@link Resources}, in the + * ServiceConnectivityResources APK. + */ +public class ConnectivityResources { + private static final String RESOURCES_APK_INTENT = + "com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK"; + private static final String RES_PKG_DIR = "/apex/com.android.tethering/"; + + @NonNull + private final Context mContext; + + @Nullable + private Resources mResources = null; + + public ConnectivityResources(Context context) { + mContext = context; + } + + /** + * Get the {@link Resources} of the ServiceConnectivityResources APK. + */ + public synchronized Resources get() { + if (mResources != null) { + return mResources; + } + + final List pkgs = mContext.getPackageManager() + .queryIntentActivities(new Intent(RESOURCES_APK_INTENT), MATCH_SYSTEM_ONLY); + pkgs.removeIf(pkg -> !pkg.activityInfo.applicationInfo.sourceDir.startsWith(RES_PKG_DIR)); + if (pkgs.size() > 1) { + Log.wtf(ConnectivityResources.class.getSimpleName(), + "More than one package found: " + pkgs); + } + if (pkgs.isEmpty()) { + throw new IllegalStateException("No connectivity resource package found"); + } + + final Context pkgContext; + try { + pkgContext = mContext.createPackageContext( + pkgs.get(0).activityInfo.applicationInfo.packageName, 0 /* flags */); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalStateException("Resolved package not found", e); + } + + mResources = pkgContext.getResources(); + return mResources; + } +} diff --git a/tests/net/Android.bp b/tests/net/Android.bp index 7f0318a135..e1a424f214 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -79,6 +79,7 @@ android_test { "android.test.runner", "android.test.base", "android.test.mock", + "ServiceConnectivityResources", ], jni_libs: [ "libservice-connectivity", diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 2c8c8a6409..763995959b 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -275,6 +275,7 @@ import com.android.internal.util.test.FakeSettingsProvider; import com.android.net.module.util.ArrayTrackRecord; import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo; import com.android.server.connectivity.ConnectivityConstants; +import com.android.server.connectivity.ConnectivityResources; import com.android.server.connectivity.MockableSystemProperties; import com.android.server.connectivity.Nat464Xlat; import com.android.server.connectivity.NetworkAgentInfo; @@ -444,6 +445,7 @@ public class ConnectivityServiceTest { @Mock NetworkPolicyManager mNetworkPolicyManager; @Mock VpnProfileStore mVpnProfileStore; @Mock SystemConfigManager mSystemConfigManager; + @Mock Resources mResources; private ArgumentCaptor mResolverParamsParcelCaptor = ArgumentCaptor.forClass(ResolverParamsParcel.class); @@ -471,7 +473,7 @@ public class ConnectivityServiceTest { private class MockContext extends BroadcastInterceptingContext { private final MockContentResolver mContentResolver; - @Spy private Resources mResources; + @Spy private Resources mInternalResources; private final LinkedBlockingQueue mStartedActivities = new LinkedBlockingQueue<>(); // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant @@ -480,21 +482,15 @@ public class ConnectivityServiceTest { MockContext(Context base, ContentProvider settingsProvider) { super(base); - mResources = spy(base.getResources()); - when(mResources.getStringArray(com.android.internal.R.array.networkAttributes)). - thenReturn(new String[] { + mInternalResources = spy(base.getResources()); + when(mInternalResources.getStringArray(com.android.internal.R.array.networkAttributes)) + .thenReturn(new String[] { "wifi,1,1,1,-1,true", "mobile,0,0,0,-1,true", "mobile_mms,2,0,2,60000,true", "mobile_supl,3,0,2,60000,true", }); - when(mResources.getStringArray( - com.android.internal.R.array.config_wakeonlan_supported_interfaces)) - .thenReturn(new String[]{ - WIFI_WOL_IFNAME, - }); - mContentResolver = new MockContentResolver(); mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider); } @@ -559,7 +555,7 @@ public class ConnectivityServiceTest { @Override public Resources getResources() { - return mResources; + return mInternalResources; } @Override @@ -1534,6 +1530,17 @@ public class ConnectivityServiceTest { }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any()); doReturn(true).when(deps).getCellular464XlatEnabled(); + doReturn(60000).when(mResources).getInteger( + com.android.connectivity.resources.R.integer.config_networkTransitionTimeout); + doReturn("").when(mResources).getString( + com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl); + doReturn(new String[]{ WIFI_WOL_IFNAME }).when(mResources).getStringArray( + com.android.connectivity.resources.R.array.config_wakeonlan_supported_interfaces); + final com.android.server.connectivity.ConnectivityResources connRes = mock( + ConnectivityResources.class); + doReturn(mResources).when(connRes).get(); + doReturn(connRes).when(deps).getResources(any()); + return deps; }