summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/ServiceUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/ServiceUtils.java')
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/ServiceUtils.java141
1 files changed, 141 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/ServiceUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/ServiceUtils.java
new file mode 100644
index 0000000000..695c69666b
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/ServiceUtils.java
@@ -0,0 +1,141 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.process;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import androidx.annotation.NonNull;
+
+/* package */ final class ServiceUtils {
+ private static final String DEFAULT_ISOLATED_CONTENT_SERVICE_NAME_SUFFIX = "0";
+
+ private ServiceUtils() {}
+
+ /**
+ * @return StringBuilder containing the name of a service class but not qualifed with any unique
+ * identifiers.
+ */
+ private static StringBuilder startSvcName(@NonNull final GeckoProcessType type) {
+ final StringBuilder builder = new StringBuilder(GeckoChildProcessServices.class.getName());
+ builder.append("$").append(type);
+ return builder;
+ }
+
+ /**
+ * Given a service's GeckoProcessType, obtain the name of its class, including any qualifiers that
+ * are needed to uniquely identify its manifest definition.
+ */
+ public static String buildSvcName(
+ @NonNull final GeckoProcessType type, final String... suffixes) {
+ final StringBuilder builder = startSvcName(type);
+
+ for (final String suffix : suffixes) {
+ builder.append(suffix);
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Given a service's GeckoProcessType, obtain the name of its class to be used for the purpose of
+ * binding as an isolated service.
+ *
+ * <p>Content services are defined in the manifest as "tab0" through "tabN" for some value of N.
+ * For the purposes of binding to an isolated content service, we simply need to repeatedly re-use
+ * the definition of "tab0", the "0" being stored as the
+ * DEFAULT_ISOLATED_CONTENT_SERVICE_NAME_SUFFIX constant.
+ */
+ public static String buildIsolatedSvcName(@NonNull final GeckoProcessType type) {
+ if (type == GeckoProcessType.CONTENT) {
+ return buildSvcName(type, DEFAULT_ISOLATED_CONTENT_SERVICE_NAME_SUFFIX);
+ }
+
+ // Non-content services do not require any unique IDs
+ return buildSvcName(type);
+ }
+
+ /**
+ * Given a service's GeckoProcessType, obtain the unqualified name of its class.
+ *
+ * @return The name of the class that hosts the implementation of the service corresponding to
+ * type, but without any unique identifiers that may be required to actually instantiate it.
+ */
+ private static String buildSvcNamePrefix(@NonNull final GeckoProcessType type) {
+ return startSvcName(type).toString();
+ }
+
+ /**
+ * Extracts flags from the manifest definition of a service.
+ *
+ * @param context Context to use for extraction
+ * @param type Service type
+ * @return flags that are specified in the service's definition in our manifest.
+ * @see android.content.pm.ServiceInfo for explanation of the various flags.
+ */
+ public static int getServiceFlags(
+ @NonNull final Context context, @NonNull final GeckoProcessType type) {
+ final ComponentName component = new ComponentName(context, buildIsolatedSvcName(type));
+ final PackageManager pkgMgr = context.getPackageManager();
+
+ try {
+ final ServiceInfo svcInfo = pkgMgr.getServiceInfo(component, 0);
+ // svcInfo is never null
+ return svcInfo.flags;
+ } catch (final PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Obtain the list of all services defined for |context|. */
+ private static ServiceInfo[] getServiceList(@NonNull final Context context) {
+ final PackageInfo packageInfo;
+ try {
+ packageInfo =
+ context
+ .getPackageManager()
+ .getPackageInfo(context.getPackageName(), PackageManager.GET_SERVICES);
+ } catch (final PackageManager.NameNotFoundException e) {
+ throw new AssertionError("Should not happen: Can't get package info of own package");
+ }
+ return packageInfo.services;
+ }
+
+ /**
+ * Count the number of service definitions in our manifest that satisfy bindings for a particular
+ * service type.
+ *
+ * @param context Context object to use for extracting the service definitions
+ * @param type The type of service to count
+ * @return The number of available service definitions.
+ */
+ public static int getServiceCount(
+ @NonNull final Context context, @NonNull final GeckoProcessType type) {
+ final ServiceInfo[] svcList = getServiceList(context);
+ final String serviceNamePrefix = buildSvcNamePrefix(type);
+
+ int result = 0;
+ for (final ServiceInfo svc : svcList) {
+ final String svcName = svc.name;
+ // If svcName starts with serviceNamePrefix, then both strings must either be equal
+ // or else the first subsequent character in svcName must be a digit.
+ // This guards against any future GeckoProcessType whose string representation shares
+ // a common prefix with another GeckoProcessType value.
+ if (svcName.startsWith(serviceNamePrefix)
+ && (svcName.length() == serviceNamePrefix.length()
+ || Character.isDigit(svcName.codePointAt(serviceNamePrefix.length())))) {
+ ++result;
+ }
+ }
+
+ if (result <= 0) {
+ throw new RuntimeException("Could not count " + serviceNamePrefix + " services in manifest");
+ }
+
+ return result;
+ }
+}